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

View file

@ -4,6 +4,8 @@
#include "socket.h" #include "socket.h"
#include <poll.h> #include <poll.h>
#include <netdb.h>
#include <sstream>
#ifdef __FreeBSD__ #ifdef __FreeBSD__
#include <netinet/in.h> #include <netinet/in.h>
@ -74,6 +76,45 @@ Socket::Connection::Connection(std::string address, bool nonblock){
} }
}//Socket::Connection Unix Contructor }//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. /// 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. /// This function may return true even if there is no data, but never returns false when there is.
bool Socket::Connection::canRead(){ bool Socket::Connection::canRead(){

View file

@ -26,6 +26,7 @@ namespace Socket{
public: public:
Connection(); ///< Create a new disconnected base socket. Connection(); ///< Create a new disconnected base socket.
Connection(int sockNo); ///< Create a new 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. 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 canRead(); ///< Calls poll() on the socket, checking if data is available.
bool canWrite(); ///< Calls poll() on the socket, checking if data can be written. bool canWrite(); ///< Calls poll() on the socket, checking if data can be written.