3 * UCC (University [of WA] Computer Club) Electronic Accounting System
5 * server.c - Client Server Code
7 * This file is licenced under the 3-clause BSD Licence. See the file
8 * COPYING for full details.
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
22 #define HACK_TPG_NOAUTH 1
23 #define HACK_ROOT_NOAUTH 1
25 #define DEBUG_TRACE_CLIENT 1
28 #define MAX_CONNECTION_QUEUE 5
29 #define INPUT_BUFFER_SIZE 256
31 #define HASH_TYPE SHA1
32 #define HASH_LENGTH 20
34 #define MSG_STR_TOO_LONG "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
37 typedef struct sClient
39 int Socket; // Client socket ID
42 int bIsTrusted; // Is the connection from a trusted host/port
52 void Server_Start(void);
53 void Server_Cleanup(void);
54 void Server_HandleClient(int Socket, int bTrusted);
55 void Server_ParseClientCommand(tClient *Client, char *CommandString);
57 void Server_Cmd_USER(tClient *Client, char *Args);
58 void Server_Cmd_PASS(tClient *Client, char *Args);
59 void Server_Cmd_AUTOAUTH(tClient *Client, char *Args);
60 void Server_Cmd_ENUMITEMS(tClient *Client, char *Args);
61 void Server_Cmd_ITEMINFO(tClient *Client, char *Args);
62 void Server_Cmd_DISPENSE(tClient *Client, char *Args);
63 void Server_Cmd_GIVE(tClient *Client, char *Args);
64 void Server_Cmd_ADD(tClient *Client, char *Args);
65 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args);
66 void Server_Cmd_USERINFO(tClient *Client, char *Args);
68 int sendf(int Socket, const char *Format, ...);
69 int GetUserAuth(const char *Salt, const char *Username, const uint8_t *Hash);
70 void HexBin(uint8_t *Dest, char *Src, int BufSize);
73 int giServer_Port = 1020;
74 int giServer_NextClientID = 1;
76 struct sClientCommand {
78 void (*Function)(tClient *Client, char *Arguments);
79 } gaServer_Commands[] = {
80 {"USER", Server_Cmd_USER},
81 {"PASS", Server_Cmd_PASS},
82 {"AUTOAUTH", Server_Cmd_AUTOAUTH},
83 {"ENUM_ITEMS", Server_Cmd_ENUMITEMS},
84 {"ITEM_INFO", Server_Cmd_ITEMINFO},
85 {"DISPENSE", Server_Cmd_DISPENSE},
86 {"GIVE", Server_Cmd_GIVE},
87 {"ADD", Server_Cmd_ADD},
88 {"ENUM_USERS", Server_Cmd_ENUMUSERS},
89 {"USER_INFO", Server_Cmd_USERINFO}
91 #define NUM_COMMANDS (sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0]))
96 * \brief Open listenting socket and serve connections
98 void Server_Start(void)
101 struct sockaddr_in server_addr, client_addr;
103 atexit(Server_Cleanup);
106 giServer_Socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
107 if( giServer_Socket < 0 ) {
108 fprintf(stderr, "ERROR: Unable to create server socket\n");
112 // Make listen address
113 memset(&server_addr, 0, sizeof(server_addr));
114 server_addr.sin_family = AF_INET; // Internet Socket
115 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on all interfaces
116 server_addr.sin_port = htons(giServer_Port); // Port
119 if( bind(giServer_Socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0 ) {
120 fprintf(stderr, "ERROR: Unable to bind to 0.0.0.0:%i\n", giServer_Port);
126 if( listen(giServer_Socket, MAX_CONNECTION_QUEUE) < 0 ) {
127 fprintf(stderr, "ERROR: Unable to listen to socket\n");
132 printf("Listening on 0.0.0.0:%i\n", giServer_Port);
136 uint len = sizeof(client_addr);
139 client_socket = accept(giServer_Socket, (struct sockaddr *) &client_addr, &len);
140 if(client_socket < 0) {
141 fprintf(stderr, "ERROR: Unable to accept client connection\n");
145 if(giDebugLevel >= 2) {
146 char ipstr[INET_ADDRSTRLEN];
147 inet_ntop(AF_INET, &client_addr.sin_addr, ipstr, INET_ADDRSTRLEN);
148 printf("Client connection from %s:%i\n",
149 ipstr, ntohs(client_addr.sin_port));
152 // Trusted Connections
153 if( ntohs(client_addr.sin_port) < 1024 )
155 // TODO: Make this runtime configurable
156 switch( ntohl( client_addr.sin_addr.s_addr ) )
158 case 0x7F000001: // 127.0.0.1 localhost
159 //case 0x825E0D00: // 130.95.13.0
160 case 0x825E0D12: // 130.95.13.18 mussel
161 case 0x825E0D17: // 130.95.13.23 martello
169 // TODO: Multithread this?
170 Server_HandleClient(client_socket, bTrusted);
172 close(client_socket);
176 void Server_Cleanup(void)
178 printf("Close(%i)\n", giServer_Socket);
179 close(giServer_Socket);
183 * \brief Reads from a client socket and parses the command strings
184 * \param Socket Client socket number/handle
185 * \param bTrusted Is the client trusted?
187 void Server_HandleClient(int Socket, int bTrusted)
189 char inbuf[INPUT_BUFFER_SIZE];
191 int remspace = INPUT_BUFFER_SIZE-1;
193 tClient clientInfo = {0};
195 // Initialise Client info
196 clientInfo.Socket = Socket;
197 clientInfo.ID = giServer_NextClientID ++;
198 clientInfo.bIsTrusted = bTrusted;
203 * - The `buf` and `remspace` variables allow a line to span several
204 * calls to recv(), if a line is not completed in one recv() call
205 * it is saved to the beginning of `inbuf` and `buf` is updated to
208 while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
211 buf[bytes] = '\0'; // Allow us to use stdlib string functions on it
215 while( (eol = strchr(start, '\n')) )
219 Server_ParseClientCommand(&clientInfo, start);
224 // Check if there was an incomplete line
225 if( *start != '\0' ) {
226 int tailBytes = bytes - (start-buf);
227 // Roll back in buffer
228 memcpy(inbuf, start, tailBytes);
229 remspace -= tailBytes;
231 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
233 remspace = INPUT_BUFFER_SIZE - 1;
238 remspace = INPUT_BUFFER_SIZE - 1;
244 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
248 if(giDebugLevel >= 2) {
249 printf("Client %i: Disconnected\n", clientInfo.ID);
254 * \brief Parses a client command and calls the required helper function
255 * \param Client Pointer to client state structure
256 * \param CommandString Command from client (single line of the command)
257 * \return Heap String to return to the client
259 void Server_ParseClientCommand(tClient *Client, char *CommandString)
264 // Split at first space
265 space = strchr(CommandString, ' ');
275 for( i = 0; i < NUM_COMMANDS; i++ )
277 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0) {
278 gaServer_Commands[i].Function(Client, args);
283 sendf(Client->Socket, "400 Unknown Command\n");
290 * \brief Set client username
292 * Usage: USER <username>
294 void Server_Cmd_USER(tClient *Client, char *Args)
298 printf("Client %i authenticating as '%s'\n", Client->ID, Args);
302 free(Client->Username);
303 Client->Username = strdup(Args);
306 // Create a salt (that changes if the username is changed)
307 // Yes, I know, I'm a little paranoid, but who isn't?
308 Client->Salt[0] = 0x21 + (rand()&0x3F);
309 Client->Salt[1] = 0x21 + (rand()&0x3F);
310 Client->Salt[2] = 0x21 + (rand()&0x3F);
311 Client->Salt[3] = 0x21 + (rand()&0x3F);
312 Client->Salt[4] = 0x21 + (rand()&0x3F);
313 Client->Salt[5] = 0x21 + (rand()&0x3F);
314 Client->Salt[6] = 0x21 + (rand()&0x3F);
315 Client->Salt[7] = 0x21 + (rand()&0x3F);
317 // TODO: Also send hash type to use, (SHA1 or crypt according to [DAA])
318 sendf(Client->Socket, "100 SALT %s\n", Client->Salt);
320 sendf(Client->Socket, "100 User Set\n");
325 * \brief Authenticate as a user
329 void Server_Cmd_PASS(tClient *Client, char *Args)
331 uint8_t clienthash[HASH_LENGTH] = {0};
334 HexBin(clienthash, Args, HASH_LENGTH);
336 // TODO: Decrypt password passed
338 Client->UID = GetUserAuth(Client->Salt, Client->Username, clienthash);
340 if( Client->UID != -1 ) {
341 Client->bIsAuthed = 1;
342 sendf(Client->Socket, "200 Auth OK\n");
348 printf("Client %i: Password hash ", Client->ID);
349 for(i=0;i<HASH_LENGTH;i++)
350 printf("%02x", clienthash[i]&0xFF);
354 sendf(Client->Socket, "401 Auth Failure\n");
358 * \brief Authenticate as a user without a password
360 * Usage: AUTOAUTH <user>
362 void Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
364 char *spos = strchr(Args, ' ');
365 if(spos) *spos = '\0'; // Remove characters after the ' '
368 if( !Client->bIsTrusted ) {
370 printf("Client %i: Untrusted client attempting to AUTOAUTH\n", Client->ID);
371 sendf(Client->Socket, "401 Untrusted\n");
376 Client->UID = GetUserID( Args );
377 if( Client->UID < 0 ) {
379 printf("Client %i: Unknown user '%s'\n", Client->ID, Args);
380 sendf(Client->Socket, "401 Auth Failure\n");
385 printf("Client %i: Authenticated as '%s' (%i)\n", Client->ID, Args, Client->UID);
387 sendf(Client->Socket, "200 Auth OK\n");
391 * \brief Enumerate the items that the server knows about
393 void Server_Cmd_ENUMITEMS(tClient *Client, char *Args)
397 sendf(Client->Socket, "201 Items %i", giNumItems);
399 for( i = 0; i < giNumItems; i ++ ) {
400 sendf(Client->Socket, " %s:%i", gaItems[i].Handler->Name, gaItems[i].ID);
403 sendf(Client->Socket, "\n");
406 tItem *_GetItemFromString(char *String)
410 char *colon = strchr(String, ':');
422 for( i = 0; i < giNumHandlers; i ++ )
424 if( strcmp(gaHandlers[i]->Name, type) == 0) {
425 handler = gaHandlers[i];
434 for( i = 0; i < giNumItems; i ++ )
436 if( gaItems[i].Handler != handler ) continue;
437 if( gaItems[i].ID != num ) continue;
444 * \brief Fetch information on a specific item
446 void Server_Cmd_ITEMINFO(tClient *Client, char *Args)
448 tItem *item = _GetItemFromString(Args);
451 sendf(Client->Socket, "406 Bad Item ID\n");
455 sendf(Client->Socket,
456 "202 Item %s:%i %i %s\n",
457 item->Handler->Name, item->ID, item->Price, item->Name
461 void Server_Cmd_DISPENSE(tClient *Client, char *Args)
465 if( !Client->bIsAuthed ) {
466 sendf(Client->Socket, "401 Not Authenticated\n");
470 item = _GetItemFromString(Args);
472 sendf(Client->Socket, "406 Bad Item ID\n");
476 switch( ret = DispenseItem( Client->UID, item ) )
478 case 0: sendf(Client->Socket, "200 Dispense OK\n"); return ;
479 case 1: sendf(Client->Socket, "501 Unable to dispense\n"); return ;
480 case 2: sendf(Client->Socket, "402 Poor You\n"); return ;
482 sendf(Client->Socket, "500 Dispense Error\n");
487 void Server_Cmd_GIVE(tClient *Client, char *Args)
489 char *recipient, *ammount, *reason;
492 if( !Client->bIsAuthed ) {
493 sendf(Client->Socket, "401 Not Authenticated\n");
499 ammount = strchr(Args, ' ');
501 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
507 reason = strchr(ammount, ' ');
509 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
516 uid = GetUserID(recipient);
518 sendf(Client->Socket, "404 Invalid target user");
523 iAmmount = atoi(ammount);
524 if( iAmmount <= 0 ) {
525 sendf(Client->Socket, "407 Invalid Argument, ammount must be > zero\n");
530 switch( DispenseGive(Client->UID, uid, iAmmount, reason) )
533 sendf(Client->Socket, "200 Give OK\n");
536 sendf(Client->Socket, "402 Poor You\n");
539 sendf(Client->Socket, "500 Unknown error\n");
544 void Server_Cmd_ADD(tClient *Client, char *Args)
546 char *user, *ammount, *reason;
549 if( !Client->bIsAuthed ) {
550 sendf(Client->Socket, "401 Not Authenticated\n");
556 ammount = strchr(Args, ' ');
558 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 1 encountered\n");
564 reason = strchr(ammount, ' ');
566 sendf(Client->Socket, "407 Invalid Argument, expected 3 parameters, 2 encountered\n");
572 // TODO: Check if the current user is in coke/higher
575 uid = GetUserID(user);
577 sendf(Client->Socket, "404 Invalid user");
582 iAmmount = atoi(ammount);
583 if( iAmmount == 0 && ammount[0] != '0' ) {
584 sendf(Client->Socket, "407 Invalid Argument\n");
589 switch( DispenseAdd(uid, Client->UID, iAmmount, reason) )
592 sendf(Client->Socket, "200 Add OK\n");
595 sendf(Client->Socket, "402 Poor Guy\n");
598 sendf(Client->Socket, "500 Unknown error\n");
603 void Server_Cmd_ENUMUSERS(tClient *Client, char *Args)
606 int maxBal = INT_MAX, minBal = INT_MIN;
607 int numUsr = GetMaxID();
610 //minBal = atoi(Args);
613 for( i = 0; i < numUsr; i ++ )
615 int bal = GetBalance(i);
617 if( bal == INT_MIN ) continue;
619 if( bal < minBal ) continue;
620 if( bal > maxBal ) continue;
626 sendf(Client->Socket, "201 Users %i\n", numRet);
628 for( i = 0; i < numUsr; i ++ )
630 int bal = GetBalance(i);
632 if( bal == INT_MIN ) continue;
634 if( bal < minBal ) continue;
635 if( bal > maxBal ) continue;
638 sendf(Client->Socket, "202 User %s %i user\n", GetUserName(i), GetBalance(i));
641 sendf(Client->Socket, "200 List End\n");
644 void Server_Cmd_USERINFO(tClient *Client, char *Args)
650 space = strchr(user, ' ');
651 if(space) *space = '\0';
654 uid = GetUserID(user);
656 sendf(Client->Socket, "404 Invalid user");
660 // TODO: User flags/type
661 sendf(Client->Socket, "202 User %s %i user\n", user, GetBalance(uid));
665 * \brief Authenticate a user
666 * \return User ID, or -1 if authentication failed
668 int GetUserAuth(const char *Salt, const char *Username, const uint8_t *ProvidedHash)
672 int ofs = strlen(Username) + strlen(Salt);
673 char input[ ofs + 40 + 1];
674 char tmp[4 + strlen(Username) + 1]; // uid=%s
678 if( strcmp(Username, "tpg") == 0 )
679 return GetUserID("tpg");
682 if( strcmp(Username, "root") == 0 )
683 return GetUserID("root");
688 strcpy(input, Username);
690 // TODO: Get user's SHA-1 hash
691 sprintf(tmp, "uid=%s", Username);
692 ldap_search_s(ld, "", LDAP_SCOPE_BASE, tmp, "userPassword", 0, res);
694 sprintf(input+ofs, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
695 h[ 0], h[ 1], h[ 2], h[ 3], h[ 4], h[ 5], h[ 6], h[ 7], h[ 8], h[ 9],
696 h[10], h[11], h[12], h[13], h[14], h[15], h[16], h[17], h[18], h[19]
698 // Then create the hash from the provided salt
699 // Compare that with the provided hash
705 // --- INTERNAL HELPERS ---
706 int sendf(int Socket, const char *Format, ...)
711 va_start(args, Format);
712 len = vsnprintf(NULL, 0, Format, args);
717 va_start(args, Format);
718 vsnprintf(buf, len+1, Format, args);
721 #if DEBUG_TRACE_CLIENT
722 printf("sendf: %s", buf);
725 return send(Socket, buf, len, 0);
729 // TODO: Move to another file
730 void HexBin(uint8_t *Dest, char *Src, int BufSize)
733 for( i = 0; i < BufSize; i ++ )
737 if('0' <= *Src && *Src <= '9')
738 val |= (*Src-'0') << 4;
739 else if('A' <= *Src && *Src <= 'F')
740 val |= (*Src-'A'+10) << 4;
741 else if('a' <= *Src && *Src <= 'f')
742 val |= (*Src-'a'+10) << 4;
747 if('0' <= *Src && *Src <= '9')
749 else if('A' <= *Src && *Src <= 'F')
750 val |= (*Src-'A'+10);
751 else if('a' <= *Src && *Src <= 'f')
752 val |= (*Src-'a'+10);
759 for( ; i < BufSize; i++ )
764 * \brief Decode a Base64 value
766 int UnBase64(uint8_t *Dest, char *Src, int BufSize)
770 char *start_src = Src;
772 for( i = 0; i+2 < BufSize; i += 3 )
775 for( j = 0; j < 4; j++, Src ++ ) {
776 if('A' <= *Src && *Src <= 'Z')
777 val |= (*Src - 'A') << ((3-j)*6);
778 else if('a' <= *Src && *Src <= 'z')
779 val |= (*Src - 'a' + 26) << ((3-j)*6);
780 else if('0' <= *Src && *Src <= '9')
781 val |= (*Src - '0' + 52) << ((3-j)*6);
783 val |= 62 << ((3-j)*6);
785 val |= 63 << ((3-j)*6);
789 j --; // Ignore invalid characters
791 Dest[i ] = (val >> 16) & 0xFF;
792 Dest[i+1] = (val >> 8) & 0xFF;
793 Dest[i+2] = val & 0xFF;
799 Dest[i] = (val >> 16) & 0xFF;
801 Dest[i+1] = (val >> 8) & 0xFF;
803 return Src - start_src;