Added socket functions for connecting over TCP - fixed some stuff, added uplink support to server binary.
This commit is contained in:
commit
9526cdc995
3 changed files with 135 additions and 21 deletions
|
@ -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"] = "";}
|
||||
|
|
|
@ -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(){
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue