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/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,8 +200,20 @@ void Authorize( Json::Value & Request, Json::Value & Response, ConnectedUser & c
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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());
|
Log("AUTH", "Failed login attempt "+UserID+" @ "+conn.C.getHost());
|
||||||
}
|
}
|
||||||
|
conn.logins++;
|
||||||
|
}
|
||||||
conn.Username = "";
|
conn.Username = "";
|
||||||
conn.Authorized = false;
|
conn.Authorized = false;
|
||||||
Response["authorize"]["status"] = "CHALL";
|
Response["authorize"]["status"] = "CHALL";
|
||||||
|
@ -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,6 +348,11 @@ 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")){
|
||||||
|
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"]["username"] = defstr(COMPILED_USERNAME);
|
||||||
Response["authorize"]["password"] = md5(defstr(COMPILED_PASSWORD) + Request["authorize"]["challenge"].asString());
|
Response["authorize"]["password"] = md5(defstr(COMPILED_PASSWORD) + Request["authorize"]["challenge"].asString());
|
||||||
it->H.Clean();
|
it->H.Clean();
|
||||||
|
@ -296,10 +360,17 @@ int main(int argc, char ** argv){
|
||||||
it->H.BuildRequest();
|
it->H.BuildRequest();
|
||||||
it->C.write(it->H.BuildResponse("200", "OK"));
|
it->C.write(it->H.BuildResponse("200", "OK"));
|
||||||
it->H.Clean();
|
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"] = "";}
|
||||||
|
|
|
@ -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(){
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Add table
Reference in a new issue