Added socket functions for connecting over TCP - fixed some stuff, added uplink support to server binary.

This commit is contained in:
Thulinma 2011-10-18 05:17:50 +02:00
commit 9526cdc995
3 changed files with 135 additions and 21 deletions

View file

@ -28,6 +28,8 @@
#include <openssl/rsa.h>
#include <openssl/x509.h>
#define UPLINK_INTERVAL 30
#define defstr(x) #x ///< converts a define name to string
/// Needed for base64_encode function
@ -122,21 +124,19 @@ unsigned char __gbv2keypub_der[] = {
}; ///< The GBv2 public key file.
unsigned int __gbv2keypub_der_len = 294; ///< Length of GBv2 public key data
RSA * pubkey = 0; ///< Holds the public key for encoding.
/// Attempts to load the public key for encoding.
RSA * pubkey = 0; ///< Holds the public key.
/// Attempts to load the GBv2 public key.
void RSA_Load(){
pubkey = d2i_RSAPublicKey(0, (const unsigned char **)(&__gbv2keypub_der), __gbv2keypub_der_len);
const unsigned char * key = __gbv2keypub_der;
pubkey = d2i_RSAPublicKey(0, &key, __gbv2keypub_der_len);
}
/// Attempts to encode the input data using the key loaded with RSA_Load().
/// Returns raw encoded data as std::string, or empty string on failure.
std::string RSA_enc(std::string & data){
std::string out = "";
char * encrypted = (char*)malloc(RSA_size(pubkey));
int len = RSA_public_encrypt(data.size(), (unsigned char *)data.c_str(), (unsigned char *)encrypted, pubkey, RSA_PKCS1_PADDING);
if (len > 0){out = std::string(encrypted, len);}
free(encrypted);
return out;
/// Attempts to verify RSA signature using the public key loaded with RSA_Load().
/// Assumes basesign argument is base64 encoded RSA signature for data.
/// Returns true if the data could be verified, false otherwise.
bool RSA_check(std::string & data, std::string basesign){
std::string sign = base64_decode(basesign);
return (RSA_verify(NID_md5, (const unsigned char*)data.c_str(), data.size(), (const unsigned char*)sign.c_str(), sign.size(), pubkey) == 1);
}
Json::Value Storage = Json::Value(Json::objectValue); ///< Global storage of data.
@ -163,10 +163,12 @@ class ConnectedUser{
HTTP::Parser H;
bool Authorized;
bool clientMode;
int logins;
std::string Username;
ConnectedUser(Socket::Connection c){
C = c;
H.Clean();
logins = 0;
Authorized = false;
clientMode = false;
}
@ -198,7 +200,19 @@ void Authorize( Json::Value & Request, Json::Value & Response, ConnectedUser & c
return;
}
}
Log("AUTH", "Failed login attempt "+UserID+" @ "+conn.C.getHost());
if (Storage["authorize"].isMember("key")){
UserID = "gearbox";
if (RSA_check(Challenge, Storage["authorize"]["key"].asString())){
Response["authorize"]["status"] = "OK";
conn.Username = UserID;
conn.Authorized = true;
return;
}
}
if (UserID != ""){
Log("AUTH", "Failed login attempt "+UserID+" @ "+conn.C.getHost());
}
conn.logins++;
}
conn.Username = "";
conn.Authorized = false;
@ -256,23 +270,68 @@ int main(int argc, char ** argv){
time_t lastuplink = 0;
Socket::Server API_Socket = Socket::Server(C.listen_port, C.interface, true);
Socket::Server Stats_Socket = Socket::Server("/tmp/ddv_statistics", true);
Util::setUser(C.username);
if (C.daemon_mode){
Util::Daemonize();
}
Socket::Connection Incoming;
std::vector< ConnectedUser > users;
Json::Value Request = Json::Value(Json::objectValue);
Json::Value Response = Json::Value(Json::objectValue);
Json::Reader JsonParse;
std::string jsonp;
ConnectedUser * uplink = 0;
JsonParse.parse(ReadFile("config.json"), Storage, false);
if (!Storage.isMember("config")){Storage["config"] = Json::Value(Json::objectValue);}
if (!Storage.isMember("log")){Storage["log"] = Json::Value(Json::arrayValue);}
if (!Storage.isMember("statistics")){Storage["statistics"] = Json::Value(Json::arrayValue);}
while (API_Socket.connected()){
usleep(100000); //sleep for 100 ms - prevents 100% CPU time
if (time(0) - lastuplink > UPLINK_INTERVAL){
lastuplink = time(0);
bool gotUplink = false;
if (users.size() > 0){
for( std::vector< ConnectedUser >::iterator it = users.end() - 1; it >= users.begin(); it--) {
if (!it->C.connected()){
it->C.close();
users.erase(it);
break;
}
if (it->clientMode){uplink = &*it; gotUplink = true;}
}
}
if (!gotUplink){
Incoming = Socket::Connection("gearbox.ddvtech.com", 4242, true);
if (Incoming.connected()){
users.push_back(Incoming);
users.back().clientMode = true;
uplink = &users.back();
gotUplink = true;
}
}
if (gotUplink){
Response.clear(); //make sure no data leaks from previous requests
Response["config"] = Storage["config"];
Response["streams"] = Storage["streams"];
Response["log"] = Storage["log"];
Response["statistics"] = Storage["statistics"];
uplink->H.Clean();
uplink->H.SetBody("command="+HTTP::Parser::urlencode(Response.toStyledString()));
uplink->H.BuildRequest();
uplink->C.write(uplink->H.BuildResponse("200", "OK"));
uplink->H.Clean();
Log("UPLK", "Sending server data to uplink.");
}else{
Log("UPLK", "Could not connect to uplink.");
}
}
Incoming = API_Socket.accept();
if (Incoming.connected()){users.push_back(Incoming);}
if (users.size() > 0){
for( std::vector< ConnectedUser >::iterator it = users.end() - 1; it >= users.begin(); it--) {
if (!it->C.connected()){
if (!it->C.connected() || it->logins > 3){
it->C.close();
users.erase(it);
break;
@ -289,17 +348,29 @@ int main(int argc, char ** argv){
}else{
if (Request["authorize"]["status"] != "OK"){
if (Request["authorize"].isMember("challenge")){
Response["authorize"]["username"] = defstr(COMPILED_USERNAME);
Response["authorize"]["password"] = md5(defstr(COMPILED_PASSWORD) + Request["authorize"]["challenge"].asString());
it->H.Clean();
it->H.SetBody("command="+HTTP::Parser::urlencode(Response.toStyledString()));
it->H.BuildRequest();
it->C.write(it->H.BuildResponse("200", "OK"));
it->H.Clean();
it->logins++;
if (it->logins > 2){
Log("UPLK", "Max login attempts passed - dropping connection to uplink.");
it->C.close();
}else{
Response["authorize"]["username"] = defstr(COMPILED_USERNAME);
Response["authorize"]["password"] = md5(defstr(COMPILED_PASSWORD) + Request["authorize"]["challenge"].asString());
it->H.Clean();
it->H.SetBody("command="+HTTP::Parser::urlencode(Response.toStyledString()));
it->H.BuildRequest();
it->C.write(it->H.BuildResponse("200", "OK"));
it->H.Clean();
Log("UPLK", "Attempting login to uplink.");
}
}
}else{
if (Request.isMember("config")){CheckConfig(Request["config"], Storage["config"]);}
if (Request.isMember("streams")){CheckStreams(Request["streams"], Storage["streams"]);}
if (Request.isMember("clearstatlogs")){
Storage["log"].clear();
Storage["statistics"].clear();
}
Log("UPLK", "Received data from uplink.");
}
}
}else{
@ -316,6 +387,7 @@ int main(int argc, char ** argv){
//sent current configuration, no matter if it was changed or not
//Response["streams"] = Storage["streams"];
Response["config"] = Storage["config"];
Response["streams"] = Storage["streams"];
//add required data to the current unix time to the config, for syncing reasons
Response["config"]["time"] = (Json::Value::UInt)time(0);
if (!Response["config"].isMember("serverid")){Response["config"]["serverid"] = "";}

View file

@ -4,6 +4,8 @@
#include "socket.h"
#include <poll.h>
#include <netdb.h>
#include <sstream>
#ifdef __FreeBSD__
#include <netinet/in.h>
@ -74,6 +76,45 @@ Socket::Connection::Connection(std::string address, bool nonblock){
}
}//Socket::Connection Unix Contructor
/// Create a new TCP Socket. This socket will (try to) connect to the given host/port right away.
/// \param host String containing the hostname to connect to.
/// \param port String containing the port to connect to.
/// \param nonblock Whether the socket should be nonblocking.
Socket::Connection::Connection(std::string host, int port, bool nonblock){
struct addrinfo *result, *rp;
Error = false;
Blocking = false;
std::stringstream ss;
ss << port;
if (getaddrinfo(host.c_str(), ss.str().c_str(), 0, &result) != 0){
#if DEBUG >= 1
fprintf(stderr, "Could not connect to %s:%i! Error: %s\n", host.c_str(), port, strerror(errno));
#endif
close();
return;
}
for (rp = result; rp != NULL; rp = rp->ai_next) {
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sock == -1){continue;}
if (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1){break;}
::close(sock);
}
if (rp == 0){
#if DEBUG >= 1
fprintf(stderr, "Could not connect to %s! Error: %s\n", host.c_str(), strerror(errno));
#endif
close();
}else{
if (nonblock){
int flags = fcntl(sock, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(sock, F_SETFL, flags);
}
}
}//Socket::Connection TCP Contructor
/// Calls poll() on the socket, checking if data is available.
/// This function may return true even if there is no data, but never returns false when there is.
bool Socket::Connection::canRead(){

View file

@ -26,6 +26,7 @@ namespace Socket{
public:
Connection(); ///< Create a new disconnected base socket.
Connection(int sockNo); ///< Create a new base socket.
Connection(std::string hostname, int port, bool nonblock); ///< Create a new TCP socket.
Connection(std::string adres, bool nonblock = false); ///< Create a new Unix Socket.
bool canRead(); ///< Calls poll() on the socket, checking if data is available.
bool canWrite(); ///< Calls poll() on the socket, checking if data can be written.