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>
16 #define MAX_CONNECTION_QUEUE 5
17 #define INPUT_BUFFER_SIZE 128
19 #define MSG_STR_TOO_LONG "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
22 typedef struct sClient
34 void Server_Start(void);
35 void Server_HandleClient(int Socket);
36 char *Server_ParseClientCommand(tClient *Client, char *CommandString);
37 char *Server_Cmd_USER(tClient *Client, char *Args);
40 int giServer_Port = 1020;
41 int giServer_NextClientID = 1;
43 struct sClientCommand {
45 char *(*Function)(tClient *Client, char *Arguments);
46 } gaServer_Commands[] = {
47 {"USER", Server_Cmd_USER}
49 #define NUM_COMMANDS (sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0]))
53 * \brief Open listenting socket and serve connections
55 void Server_Start(void)
61 * \brief Reads from a client socket and parses the command strings
63 void Server_HandleClient(int Socket)
65 char inbuf[INPUT_BUFFER_SIZE];
67 int remspace = INPUT_BUFFER_SIZE-1;
69 tClient clientInfo = {0};
71 // Initialise Client info
72 clientInfo.ID = giServer_NextClientID ++;
77 * - The `buf` and `remspace` variables allow a line to span several
78 * calls to recv(), if a line is not completed in one recv() call
79 * it is saved to the beginning of `inbuf` and `buf` is updated to
82 while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
85 buf[bytes] = '\0'; // Allow us to use stdlib string functions on it
89 while( (eol = strchr(start, '\n')) )
93 ret = Server_ParseClientCommand(&clientInfo, start);
94 // `ret` is a string on the heap
95 send(Socket, ret, strlen(ret), 0);
100 // Check if there was an incomplete line
101 if( *start != '\0' ) {
102 int tailBytes = bytes - (start-buf);
103 // Roll back in buffer
104 memcpy(inbuf, start, tailBytes);
105 remspace -= tailBytes;
107 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
109 remspace = INPUT_BUFFER_SIZE - 1;
114 remspace = INPUT_BUFFER_SIZE - 1;
120 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
126 * \brief Parses a client command and calls the required helper function
127 * \param Client Pointer to client state structure
129 char *Server_ParseClientCommand(tClient *Client, char *CommandString)
134 // Split at first space
135 space = strchr(CommandString, ' ');
145 for( i = 0; i < NUM_COMMANDS; i++ )
147 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0)
148 return gaServer_Commands[i].Function(Client, args);
151 return strdup("400 Unknown Command\n");
158 * \brief Set client username
160 char *Server_Cmd_USER(tClient *Client, char *Args)
166 printf("Client %i authenticating as '%s'\n", Client->ID, Args);
170 free(Client->Username);
171 Client->Username = strdup(Args);
173 // Create a salt (that changes if the username is changed)
174 Client->Salt[0] = 0x21 + (rand()&0x3F);
175 Client->Salt[1] = 0x21 + (rand()&0x3F);
176 Client->Salt[2] = 0x21 + (rand()&0x3F);
177 Client->Salt[3] = 0x21 + (rand()&0x3F);
178 Client->Salt[4] = 0x21 + (rand()&0x3F);
179 Client->Salt[5] = 0x21 + (rand()&0x3F);
180 Client->Salt[6] = 0x21 + (rand()&0x3F);
181 Client->Salt[7] = 0x21 + (rand()&0x3F);
183 // "100 Salt xxxxXXXX\n"
184 ret = strdup("100 SALT xxxxXXXX\n");
185 sprintf(ret, "100 SALT %s\n", Client->Salt);