X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;f=src%2Fserver%2Fserver.c;h=2a10babd22f4601f0fd9e8e5742737b55bb7a8cd;hb=f75c35bdf5e2c6c1104c22c32bd0d55ca16b52c9;hp=d7466a9e27619a0e686051d1218ad8ae71ee9e3c;hpb=50c87bcb88b67cfa84ea213ca7b23f89a7e0de5f;p=tpg%2Fopendispense2.git diff --git a/src/server/server.c b/src/server/server.c index d7466a9..2a10bab 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -15,11 +15,15 @@ #include #include #include +#include +#include // HACKS #define HACK_TPG_NOAUTH 1 #define HACK_ROOT_NOAUTH 1 +#define DEBUG_TRACE_CLIENT 0 + // Statistics #define MAX_CONNECTION_QUEUE 5 #define INPUT_BUFFER_SIZE 256 @@ -32,6 +36,7 @@ // === TYPES === typedef struct sClient { + int Socket; // Client socket ID int ID; // Client ID int bIsTrusted; // Is the connection from a trusted host/port @@ -47,15 +52,23 @@ typedef struct sClient void Server_Start(void); void Server_Cleanup(void); void Server_HandleClient(int Socket, int bTrusted); -char *Server_ParseClientCommand(tClient *Client, char *CommandString); +void Server_ParseClientCommand(tClient *Client, char *CommandString); // --- Commands --- -char *Server_Cmd_USER(tClient *Client, char *Args); -char *Server_Cmd_PASS(tClient *Client, char *Args); -char *Server_Cmd_AUTOAUTH(tClient *Client, char *Args); -char *Server_Cmd_ENUMITEMS(tClient *Client, char *Args); -char *Server_Cmd_ITEMINFO(tClient *Client, char *Args); -char *Server_Cmd_DISPENSE(tClient *Client, char *Args); +void Server_Cmd_USER(tClient *Client, char *Args); +void Server_Cmd_PASS(tClient *Client, char *Args); +void Server_Cmd_AUTOAUTH(tClient *Client, char *Args); +void Server_Cmd_ENUMITEMS(tClient *Client, char *Args); +void Server_Cmd_ITEMINFO(tClient *Client, char *Args); +void Server_Cmd_DISPENSE(tClient *Client, char *Args); +void Server_Cmd_GIVE(tClient *Client, char *Args); +void Server_Cmd_ADD(tClient *Client, char *Args); +void Server_Cmd_ENUMUSERS(tClient *Client, char *Args); +void Server_Cmd_USERINFO(tClient *Client, char *Args); +void _SendUserInfo(tClient *Client, int UserID); +void Server_Cmd_USERADD(tClient *Client, char *Args); +void Server_Cmd_USERFLAGS(tClient *Client, char *Args); // --- Helpers --- + int sendf(int Socket, const char *Format, ...); int GetUserAuth(const char *Salt, const char *Username, const uint8_t *Hash); void HexBin(uint8_t *Dest, char *Src, int BufSize); @@ -65,14 +78,20 @@ void HexBin(uint8_t *Dest, char *Src, int BufSize); // - Commands struct sClientCommand { char *Name; - char *(*Function)(tClient *Client, char *Arguments); + void (*Function)(tClient *Client, char *Arguments); } gaServer_Commands[] = { {"USER", Server_Cmd_USER}, {"PASS", Server_Cmd_PASS}, {"AUTOAUTH", Server_Cmd_AUTOAUTH}, {"ENUM_ITEMS", Server_Cmd_ENUMITEMS}, {"ITEM_INFO", Server_Cmd_ITEMINFO}, - {"DISPENSE", Server_Cmd_DISPENSE} + {"DISPENSE", Server_Cmd_DISPENSE}, + {"GIVE", Server_Cmd_GIVE}, + {"ADD", Server_Cmd_ADD}, + {"ENUM_USERS", Server_Cmd_ENUMUSERS}, + {"USER_INFO", Server_Cmd_USERINFO}, + {"USER_ADD", Server_Cmd_USERADD}, + {"USER_FLAGS", Server_Cmd_USERFLAGS} }; #define NUM_COMMANDS (sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0])) int giServer_Socket; @@ -179,6 +198,7 @@ void Server_HandleClient(int Socket, int bTrusted) tClient clientInfo = {0}; // Initialise Client info + clientInfo.Socket = Socket; clientInfo.ID = giServer_NextClientID ++; clientInfo.bIsTrusted = bTrusted; @@ -199,12 +219,10 @@ void Server_HandleClient(int Socket, int bTrusted) start = inbuf; while( (eol = strchr(start, '\n')) ) { - char *ret; *eol = '\0'; - ret = Server_ParseClientCommand(&clientInfo, start); - // `ret` is a string on the heap - send(Socket, ret, strlen(ret), 0); - free(ret); + + Server_ParseClientCommand(&clientInfo, start); + start = eol + 1; } @@ -243,10 +261,14 @@ void Server_HandleClient(int Socket, int bTrusted) * \param CommandString Command from client (single line of the command) * \return Heap String to return to the client */ -char *Server_ParseClientCommand(tClient *Client, char *CommandString) +void Server_ParseClientCommand(tClient *Client, char *CommandString) { char *space, *args; int i; + #if 0 + char **argList; + int numArgs = 0; + #endif // Split at first space space = strchr(CommandString, ' '); @@ -256,16 +278,38 @@ char *Server_ParseClientCommand(tClient *Client, char *CommandString) else { *space = '\0'; args = space + 1; + while( *space == ' ' ) space ++; + + #if 0 + // Count arguments + numArgs = 1; + for( i = 0; args[i]; ) + { + while( CommandString[i] != ' ' ) { + if( CommandString[i] == '"' ) { + while( !(CommandString[i] != '\\' CommandString[i+1] == '"' ) ) + i ++; + i ++; + } + i ++; + } + numArgs ++; + while( CommandString[i] == ' ' ) i ++; + } + #endif } + // Find command for( i = 0; i < NUM_COMMANDS; i++ ) { - if(strcmp(CommandString, gaServer_Commands[i].Name) == 0) - return gaServer_Commands[i].Function(Client, args); + if(strcmp(CommandString, gaServer_Commands[i].Name) == 0) { + gaServer_Commands[i].Function(Client, args); + return ; + } } - return strdup("400 Unknown Command\n"); + sendf(Client->Socket, "400 Unknown Command\n"); } // --- @@ -276,10 +320,8 @@ char *Server_ParseClientCommand(tClient *Client, char *CommandString) * * Usage: USER */ -char *Server_Cmd_USER(tClient *Client, char *Args) -{ - char *ret; - +void Server_Cmd_USER(tClient *Client, char *Args) +{ // Debug! if( giDebugLevel ) printf("Client %i authenticating as '%s'\n", Client->ID, Args); @@ -302,13 +344,10 @@ char *Server_Cmd_USER(tClient *Client, char *Args) Client->Salt[7] = 0x21 + (rand()&0x3F); // TODO: Also send hash type to use, (SHA1 or crypt according to [DAA]) - // "100 Salt xxxxXXXX\n" - ret = strdup("100 SALT xxxxXXXX\n"); - sprintf(ret, "100 SALT %s\n", Client->Salt); + sendf(Client->Socket, "100 SALT %s\n", Client->Salt); #else - ret = strdup("100 User Set\n"); + sendf(Client->Socket, "100 User Set\n"); #endif - return ret; } /** @@ -316,7 +355,7 @@ char *Server_Cmd_USER(tClient *Client, char *Args) * * Usage: PASS */ -char *Server_Cmd_PASS(tClient *Client, char *Args) +void Server_Cmd_PASS(tClient *Client, char *Args) { uint8_t clienthash[HASH_LENGTH] = {0}; @@ -329,7 +368,8 @@ char *Server_Cmd_PASS(tClient *Client, char *Args) if( Client->UID != -1 ) { Client->bIsAuthed = 1; - return strdup("200 Auth OK\n"); + sendf(Client->Socket, "200 Auth OK\n"); + return ; } if( giDebugLevel ) { @@ -340,7 +380,7 @@ char *Server_Cmd_PASS(tClient *Client, char *Args) printf("\n"); } - return strdup("401 Auth Failure\n"); + sendf(Client->Socket, "401 Auth Failure\n"); } /** @@ -348,7 +388,7 @@ char *Server_Cmd_PASS(tClient *Client, char *Args) * * Usage: AUTOAUTH */ -char *Server_Cmd_AUTOAUTH(tClient *Client, char *Args) +void Server_Cmd_AUTOAUTH(tClient *Client, char *Args) { char *spos = strchr(Args, ' '); if(spos) *spos = '\0'; // Remove characters after the ' ' @@ -357,51 +397,42 @@ char *Server_Cmd_AUTOAUTH(tClient *Client, char *Args) if( !Client->bIsTrusted ) { if(giDebugLevel) printf("Client %i: Untrusted client attempting to AUTOAUTH\n", Client->ID); - return strdup("401 Untrusted\n"); + sendf(Client->Socket, "401 Untrusted\n"); + return ; } // Get UID - Client->UID = GetUserID( Args ); + Client->UID = GetUserID( Args ); if( Client->UID < 0 ) { if(giDebugLevel) printf("Client %i: Unknown user '%s'\n", Client->ID, Args); - return strdup("401 Auth Failure\n"); + sendf(Client->Socket, "401 Auth Failure\n"); + return ; } if(giDebugLevel) printf("Client %i: Authenticated as '%s' (%i)\n", Client->ID, Args, Client->UID); - return strdup("200 Auth OK\n"); + sendf(Client->Socket, "200 Auth OK\n"); } /** * \brief Enumerate the items that the server knows about */ -char *Server_Cmd_ENUMITEMS(tClient *Client, char *Args) +void Server_Cmd_ENUMITEMS(tClient *Client, char *Args) { -// int nItems = giNumItems; - int retLen; int i; - char *ret; - retLen = snprintf(NULL, 0, "201 Items %i", giNumItems); - - for( i = 0; i < giNumItems; i ++ ) - { - retLen += snprintf(NULL, 0, " %s:%i", gaItems[i].Handler->Name, gaItems[i].ID); - } - - ret = malloc(retLen+1); - retLen = 0; - retLen += sprintf(ret+retLen, "201 Items %i", giNumItems); + sendf(Client->Socket, "201 Items %i\n", giNumItems); for( i = 0; i < giNumItems; i ++ ) { - retLen += sprintf(ret+retLen, " %s:%i", gaItems[i].Handler->Name, gaItems[i].ID); + sendf(Client->Socket, + "202 Item %s:%i %i %s\n", + gaItems[i].Handler->Name, gaItems[i].ID, gaItems[i].Price, gaItems[i].Name + ); } - strcat(ret, "\n"); - - return ret; + sendf(Client->Socket, "200 List end\n"); } tItem *_GetItemFromString(char *String) @@ -444,84 +475,387 @@ tItem *_GetItemFromString(char *String) /** * \brief Fetch information on a specific item */ -char *Server_Cmd_ITEMINFO(tClient *Client, char *Args) +void Server_Cmd_ITEMINFO(tClient *Client, char *Args) { - int retLen = 0; - char *ret; tItem *item = _GetItemFromString(Args); if( !item ) { - return strdup("406 Bad Item ID\n"); + sendf(Client->Socket, "406 Bad Item ID\n"); + return ; } - - // Create return - retLen = snprintf(NULL, 0, "202 Item %s:%i %i %s\n", - item->Handler->Name, item->ID, item->Price, item->Name); - ret = malloc(retLen+1); - sprintf(ret, "202 Item %s:%i %i %s\n", - item->Handler->Name, item->ID, item->Price, item->Name); - - return ret; + + sendf(Client->Socket, + "202 Item %s:%i %i %s\n", + item->Handler->Name, item->ID, item->Price, item->Name + ); } -char *Server_Cmd_DISPENSE(tClient *Client, char *Args) +void Server_Cmd_DISPENSE(tClient *Client, char *Args) { tItem *item; int ret; - if( !Client->bIsAuthed ) return strdup("401 Not Authenticated\n"); + if( !Client->bIsAuthed ) { + sendf(Client->Socket, "401 Not Authenticated\n"); + return ; + } item = _GetItemFromString(Args); if( !item ) { - return strdup("406 Bad Item ID\n"); + sendf(Client->Socket, "406 Bad Item ID\n"); + return ; } switch( ret = DispenseItem( Client->UID, item ) ) { - case 0: return strdup("200 Dispense OK\n"); - case 1: return strdup("501 Unable to dispense\n"); - case 2: return strdup("402 Poor You\n"); + case 0: sendf(Client->Socket, "200 Dispense OK\n"); return ; + case 1: sendf(Client->Socket, "501 Unable to dispense\n"); return ; + case 2: sendf(Client->Socket, "402 Poor You\n"); return ; default: - return strdup("500 Dispense Error\n"); + sendf(Client->Socket, "500 Dispense Error\n"); + return ; } } -char *Server_Cmd_GIVE(tClient *Client, char *Args) +void Server_Cmd_GIVE(tClient *Client, char *Args) { char *recipient, *ammount, *reason; int uid, iAmmount; - if( !Client->bIsAuthed ) return strdup("401 Not Authenticated\n"); + if( !Client->bIsAuthed ) { + sendf(Client->Socket, "401 Not Authenticated\n"); + return ; + } recipient = Args; ammount = strchr(Args, ' '); - if( !ammount ) return strdup("407 Invalid Argument, expected 3 parameters, 1 encountered\n"); + if( !ammount ) { + sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n"); + return ; + } *ammount = '\0'; ammount ++; reason = strchr(ammount, ' '); - if( !reason ) return strdup("407 Invalid Argument, expected 3 parameters, 2 encountered\n"); + if( !reason ) { + sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n"); + return ; + } *reason = '\0'; reason ++; // Get recipient uid = GetUserID(recipient); - if( uid == -1 ) return strdup("404 Invalid target user"); + if( uid == -1 ) { + sendf(Client->Socket, "404 Invalid target user\n"); + return ; + } + + // Parse ammount + iAmmount = atoi(ammount); + if( iAmmount <= 0 ) { + sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n"); + return ; + } + + // Do give + switch( DispenseGive(Client->UID, uid, iAmmount, reason) ) + { + case 0: + sendf(Client->Socket, "200 Give OK\n"); + return ; + case 2: + sendf(Client->Socket, "402 Poor You\n"); + return ; + default: + sendf(Client->Socket, "500 Unknown error\n"); + return ; + } +} + +void Server_Cmd_ADD(tClient *Client, char *Args) +{ + char *user, *ammount, *reason; + int uid, iAmmount; + + if( !Client->bIsAuthed ) { + sendf(Client->Socket, "401 Not Authenticated\n"); + return ; + } + + user = Args; + + ammount = strchr(Args, ' '); + if( !ammount ) { + sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n"); + return ; + } + *ammount = '\0'; + ammount ++; + + reason = strchr(ammount, ' '); + if( !reason ) { + sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n"); + return ; + } + *reason = '\0'; + reason ++; + + // TODO: Check if the current user is in coke/higher + if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_COKE ) { + sendf(Client->Socket, "403 Not in coke\n"); + return ; + } + + // Get recipient + uid = GetUserID(user); + if( uid == -1 ) { + sendf(Client->Socket, "404 Invalid user\n"); + return ; + } // Parse ammount iAmmount = atoi(ammount); - if( iAmmount <= 0 ) return strdup("407 Invalid Argument, ammount must be > zero\n"); + if( iAmmount == 0 && ammount[0] != '0' ) { + sendf(Client->Socket, "407 Invalid Argument\n"); + return ; + } // Do give - switch( Transfer(Client->UID, uid, iAmmount, reason) ) + switch( DispenseAdd(uid, Client->UID, iAmmount, reason) ) { case 0: - return strdup("200 Give OK\n"); + sendf(Client->Socket, "200 Add OK\n"); + return ; + case 2: + sendf(Client->Socket, "402 Poor Guy\n"); + return ; default: - return strdup("402 Poor You\n"); + sendf(Client->Socket, "500 Unknown error\n"); + return ; } } +void Server_Cmd_ENUMUSERS(tClient *Client, char *Args) +{ + int i, numRet = 0; + int maxBal = INT_MAX, minBal = INT_MIN; + int numUsr = GetMaxID(); + + // Parse arguments + if( Args && strlen(Args) ) + { + char *min = Args, *max; + + max = strchr(Args, ' '); + if( max ) { + *max = '\0'; + max ++; + } + + // If != "-" + if( strcmp(min, "-") != 0 ) + minBal = atoi(min); + // If != "-" + if( max && strcmp(max, "-") != 0 ) + maxBal = atoi(max); + } + + // Get return number + for( i = 0; i < numUsr; i ++ ) + { + int bal = GetBalance(i); + + if( bal == INT_MIN ) continue; + + if( bal < minBal ) continue; + if( bal > maxBal ) continue; + + numRet ++; + } + + // Send count + sendf(Client->Socket, "201 Users %i\n", numRet); + + for( i = 0; i < numUsr; i ++ ) + { + int bal = GetBalance(i); + + if( bal == INT_MIN ) continue; + + if( bal < minBal ) continue; + if( bal > maxBal ) continue; + + _SendUserInfo(Client, i); + } + + sendf(Client->Socket, "200 List End\n"); +} + +void Server_Cmd_USERINFO(tClient *Client, char *Args) +{ + int uid; + char *user = Args; + char *space; + + space = strchr(user, ' '); + if(space) *space = '\0'; + + // Get recipient + uid = GetUserID(user); + if( uid == -1 ) { + sendf(Client->Socket, "404 Invalid user"); + return ; + } + + _SendUserInfo(Client, uid); +} + +void _SendUserInfo(tClient *Client, int UserID) +{ + char *type, *disabled=""; + int flags = GetFlags(UserID); + + switch( flags & USER_FLAG_TYPEMASK ) + { + default: + case USER_TYPE_NORMAL: type = "user"; break; + case USER_TYPE_COKE: type = "coke"; break; + case USER_TYPE_WHEEL: type = "wheel"; break; + case USER_TYPE_GOD: type = "meta"; break; + } + + if( flags & USER_FLAG_DISABLED ) + disabled = ",disabled"; + + // TODO: User flags/type + sendf( + Client->Socket, "202 User %s %i %s%s\n", + GetUserName(UserID), GetBalance(UserID), + type, disabled + ); +} + +void Server_Cmd_USERADD(tClient *Client, char *Args) +{ + char *username, *space; + + // Check permissions + if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_WHEEL ) { + sendf(Client->Socket, "403 Not Wheel\n"); + return ; + } + + // Read arguments + username = Args; + while( *username == ' ' ) username ++; + space = strchr(username, ' '); + if(space) *space = '\0'; + + // Try to create user + if( CreateUser(username) == -1 ) { + sendf(Client->Socket, "404 User exists\n"); + return ; + } + + sendf(Client->Socket, "200 User Added\n"); +} + +void Server_Cmd_USERFLAGS(tClient *Client, char *Args) +{ + char *username, *flags; + char *space; + int mask=0, value=0; + int uid; + + // Check permissions + if( (GetFlags(Client->UID) & USER_FLAG_TYPEMASK) < USER_TYPE_WHEEL ) { + sendf(Client->Socket, "403 Not Wheel\n"); + return ; + } + + // Read arguments + // - Username + username = Args; + while( *username == ' ' ) username ++; + space = strchr(username, ' '); + if(!space) { + sendf(Client->Socket, "407 USER_FLAGS requires 2 arguments, 1 given\n"); + return ; + } + *space = '\0'; + // - Flags + flags = space + 1; + while( *flags == ' ' ) flags ++; + space = strchr(flags, ' '); + if(space) *space = '\0'; + + // Get UID + uid = GetUserID(username); + if( uid == -1 ) { + sendf(Client->Socket, "404 User '%s' not found\n", username); + return ; + } + + // Parse flags + do { + int bRemove = 0; + int i; + struct { + const char *Name; + int Mask; + int Value; + } cFLAGS[] = { + {"disabled", USER_FLAG_DISABLED, USER_FLAG_DISABLED}, + {"door", USER_FLAG_DOORGROUP, USER_FLAG_DOORGROUP}, + {"user", USER_FLAG_TYPEMASK, USER_TYPE_NORMAL}, + {"coke", USER_FLAG_TYPEMASK, USER_TYPE_COKE}, + {"wheel", USER_FLAG_TYPEMASK, USER_TYPE_WHEEL}, + {"meta", USER_FLAG_TYPEMASK, USER_TYPE_GOD} + }; + const int ciNumFlags = sizeof(cFLAGS)/sizeof(cFLAGS[0]); + + while( *flags == ' ' ) flags ++; // Eat whitespace + space = strchr(flags, ','); // Find the end of the flag + if(space) *space = '\0'; + + // Check for inversion/removal + if( *flags == '!' || *flags == '-' ) { + bRemove = 1; + flags ++; + } + else if( *flags == '+' ) { + flags ++; + } + + // Check flag values + for( i = 0; i < ciNumFlags; i ++ ) + { + if( strcmp(flags, cFLAGS[i].Name) == 0 ) { + mask |= cFLAGS[i].Mask; + value &= ~cFLAGS[i].Mask; + if( !bRemove ) + value |= cFLAGS[i].Value; + break; + } + } + + // Error check + if( i == ciNumFlags ) { + sendf(Client->Socket, "407 Unknown flag value '%s'\n", flags); + return ; + } + + flags = space + 1; + } while(space); + + // Apply flags + SetFlags(uid, mask, value); + + // Return OK + sendf(Client->Socket, "200 User Updated\n"); +} + /** * \brief Authenticate a user * \return User ID, or -1 if authentication failed @@ -540,8 +874,12 @@ int GetUserAuth(const char *Salt, const char *Username, const uint8_t *ProvidedH return GetUserID("tpg"); #endif #if HACK_ROOT_NOAUTH - if( strcmp(Username, "root") == 0 ) - return GetUserID("root"); + if( strcmp(Username, "root") == 0 ) { + int ret = GetUserID("root"); + if( ret == -1 ) + return CreateUser("root"); + return ret; + } #endif #if 0 @@ -564,6 +902,29 @@ int GetUserAuth(const char *Salt, const char *Username, const uint8_t *ProvidedH } // --- INTERNAL HELPERS --- +int sendf(int Socket, const char *Format, ...) +{ + va_list args; + int len; + + va_start(args, Format); + len = vsnprintf(NULL, 0, Format, args); + va_end(args); + + { + char buf[len+1]; + va_start(args, Format); + vsnprintf(buf, len+1, Format, args); + va_end(args); + + #if DEBUG_TRACE_CLIENT + printf("sendf: %s", buf); + #endif + + return send(Socket, buf, len, 0); + } +} + // TODO: Move to another file void HexBin(uint8_t *Dest, char *Src, int BufSize) {