#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
+#include <limits.h>
+#include <stdarg.h>
+// 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
-#define HASH_TYPE SHA512
-#define HASH_LENGTH 64
+#define HASH_TYPE SHA1
+#define HASH_LENGTH 20
#define MSG_STR_TOO_LONG "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
// === TYPES ===
typedef struct sClient
{
+ int Socket; // Client socket ID
int ID; // Client ID
int bIsTrusted; // Is the connection from a trusted host/port
// === PROTOTYPES ===
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);
// --- 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);
// - 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}
};
#define NUM_COMMANDS (sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0]))
+ int giServer_Socket;
// === CODE ===
/**
*/
void Server_Start(void)
{
- int server_socket, client_socket;
+ int client_socket;
struct sockaddr_in server_addr, client_addr;
+ atexit(Server_Cleanup);
+
// Create Server
- server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- if( server_socket < 0 ) {
+ giServer_Socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if( giServer_Socket < 0 ) {
fprintf(stderr, "ERROR: Unable to create server socket\n");
return ;
}
server_addr.sin_port = htons(giServer_Port); // Port
// Bind
- if( bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0 ) {
+ if( bind(giServer_Socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0 ) {
fprintf(stderr, "ERROR: Unable to bind to 0.0.0.0:%i\n", giServer_Port);
+ perror("Binding");
return ;
}
// Listen
- if( listen(server_socket, MAX_CONNECTION_QUEUE) < 0 ) {
+ if( listen(giServer_Socket, MAX_CONNECTION_QUEUE) < 0 ) {
fprintf(stderr, "ERROR: Unable to listen to socket\n");
+ perror("Listen");
return ;
}
uint len = sizeof(client_addr);
int bTrusted = 0;
- client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &len);
+ client_socket = accept(giServer_Socket, (struct sockaddr *) &client_addr, &len);
if(client_socket < 0) {
fprintf(stderr, "ERROR: Unable to accept client connection\n");
return ;
}
}
+void Server_Cleanup(void)
+{
+ printf("Close(%i)\n", giServer_Socket);
+ close(giServer_Socket);
+}
+
/**
* \brief Reads from a client socket and parses the command strings
* \param Socket Client socket number/handle
tClient clientInfo = {0};
// Initialise Client info
+ clientInfo.Socket = Socket;
clientInfo.ID = giServer_NextClientID ++;
clientInfo.bIsTrusted = 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;
}
* \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;
// 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");
}
// ---
*
* Usage: USER <username>
*/
-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);
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;
}
/**
*
* Usage: PASS <hash>
*/
-char *Server_Cmd_PASS(tClient *Client, char *Args)
+void Server_Cmd_PASS(tClient *Client, char *Args)
{
uint8_t clienthash[HASH_LENGTH] = {0};
if( Client->UID != -1 ) {
Client->bIsAuthed = 1;
- return strdup("200 Auth OK\n");
+ sendf(Client->Socket, "200 Auth OK\n");
+ return ;
}
if( giDebugLevel ) {
printf("\n");
}
- return strdup("401 Auth Failure\n");
+ sendf(Client->Socket, "401 Auth Failure\n");
}
/**
*
* Usage: AUTOAUTH <user>
*/
-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 ' '
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
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)
/**
* \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;
- if( !Client->bIsAuthed ) return strdup("401 Not Authenticated\n");
+ int ret;
+ 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( DispenseItem( Client->UID, item ) )
+ 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:
+ 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
+ //minBal = atoi(Args);
+
+ // 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;
+
+ // TODO: User flags
+ _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:
- return strdup("402 Poor You\n");
+ 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
+ );
}
/**
if( strcmp(Username, "tpg") == 0 )
return GetUserID("tpg");
#endif
+ #if HACK_ROOT_NOAUTH
+ if( strcmp(Username, "root") == 0 )
+ return GetUserID("root");
+ #endif
#if 0
//
}
// --- 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)
{