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>
19 #define MAX_CONNECTION_QUEUE 5
20 #define INPUT_BUFFER_SIZE 256
22 #define HASH_TYPE SHA512
23 #define HASH_LENGTH 64
25 #define MSG_STR_TOO_LONG "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
28 typedef struct sClient
32 int bIsTrusted; // Is the connection from a trusted host/port
42 void Server_Start(void);
43 void Server_HandleClient(int Socket, int bTrusted);
44 char *Server_ParseClientCommand(tClient *Client, char *CommandString);
46 char *Server_Cmd_USER(tClient *Client, char *Args);
47 char *Server_Cmd_PASS(tClient *Client, char *Args);
48 char *Server_Cmd_AUTOAUTH(tClient *Client, char *Args);
50 void HexBin(uint8_t *Dest, char *Src, int BufSize);
53 int giServer_Port = 1020;
54 int giServer_NextClientID = 1;
56 struct sClientCommand {
58 char *(*Function)(tClient *Client, char *Arguments);
59 } gaServer_Commands[] = {
60 {"USER", Server_Cmd_USER},
61 {"PASS", Server_Cmd_PASS},
62 {"AUTOAUTH", Server_Cmd_AUTOAUTH}
64 #define NUM_COMMANDS (sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0]))
68 * \brief Open listenting socket and serve connections
70 void Server_Start(void)
72 int server_socket, client_socket;
73 struct sockaddr_in server_addr, client_addr;
76 server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
77 if( server_socket < 0 ) {
78 fprintf(stderr, "ERROR: Unable to create server socket\n");
82 // Make listen address
83 memset(&server_addr, 0, sizeof(server_addr));
84 server_addr.sin_family = AF_INET; // Internet Socket
85 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on all interfaces
86 server_addr.sin_port = htons(giServer_Port); // Port
89 if( bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0 ) {
90 fprintf(stderr, "ERROR: Unable to bind to 0.0.0.0:%i\n", giServer_Port);
95 if( listen(server_socket, MAX_CONNECTION_QUEUE) < 0 ) {
96 fprintf(stderr, "ERROR: Unable to listen to socket\n");
100 printf("Listening on 0.0.0.0:%i\n", giServer_Port);
104 uint len = sizeof(client_addr);
107 client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &len);
108 if(client_socket < 0) {
109 fprintf(stderr, "ERROR: Unable to accept client connection\n");
113 if(giDebugLevel >= 2) {
114 char ipstr[INET_ADDRSTRLEN];
115 inet_ntop(AF_INET, &client_addr.sin_addr, ipstr, INET_ADDRSTRLEN);
116 printf("Client connection from %s:%i\n",
117 ipstr, ntohs(client_addr.sin_port));
120 // Trusted Connections
121 if( ntohs(client_addr.sin_port) < 1024 )
123 // TODO: Make this runtime configurable
124 switch( ntohl( client_addr.sin_addr.s_addr ) )
126 case 0x7F000001: // 127.0.0.1 localhost
127 //case 0x825E0D00: // 130.95.13.0
128 case 0x825E0D12: // 130.95.13.18 mussel
129 case 0x825E0D17: // 130.95.13.23 martello
137 // TODO: Multithread this?
138 Server_HandleClient(client_socket, bTrusted);
140 close(client_socket);
145 * \brief Reads from a client socket and parses the command strings
146 * \param Socket Client socket number/handle
147 * \param bTrusted Is the client trusted?
149 void Server_HandleClient(int Socket, int bTrusted)
151 char inbuf[INPUT_BUFFER_SIZE];
153 int remspace = INPUT_BUFFER_SIZE-1;
155 tClient clientInfo = {0};
157 // Initialise Client info
158 clientInfo.ID = giServer_NextClientID ++;
159 clientInfo.bIsTrusted = bTrusted;
164 * - The `buf` and `remspace` variables allow a line to span several
165 * calls to recv(), if a line is not completed in one recv() call
166 * it is saved to the beginning of `inbuf` and `buf` is updated to
169 while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
172 buf[bytes] = '\0'; // Allow us to use stdlib string functions on it
176 while( (eol = strchr(start, '\n')) )
180 ret = Server_ParseClientCommand(&clientInfo, start);
181 // `ret` is a string on the heap
182 send(Socket, ret, strlen(ret), 0);
187 // Check if there was an incomplete line
188 if( *start != '\0' ) {
189 int tailBytes = bytes - (start-buf);
190 // Roll back in buffer
191 memcpy(inbuf, start, tailBytes);
192 remspace -= tailBytes;
194 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
196 remspace = INPUT_BUFFER_SIZE - 1;
201 remspace = INPUT_BUFFER_SIZE - 1;
207 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
211 if(giDebugLevel >= 2) {
212 printf("Client %i: Disconnected\n", clientInfo.ID);
217 * \brief Parses a client command and calls the required helper function
218 * \param Client Pointer to client state structure
219 * \param CommandString Command from client (single line of the command)
220 * \return Heap String to return to the client
222 char *Server_ParseClientCommand(tClient *Client, char *CommandString)
227 // Split at first space
228 space = strchr(CommandString, ' ');
238 for( i = 0; i < NUM_COMMANDS; i++ )
240 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0)
241 return gaServer_Commands[i].Function(Client, args);
244 return strdup("400 Unknown Command\n");
251 * \brief Set client username
253 * Usage: USER <username>
255 char *Server_Cmd_USER(tClient *Client, char *Args)
261 printf("Client %i authenticating as '%s'\n", Client->ID, Args);
265 free(Client->Username);
266 Client->Username = strdup(Args);
269 // Create a salt (that changes if the username is changed)
270 // Yes, I know, I'm a little paranoid, but who isn't?
271 Client->Salt[0] = 0x21 + (rand()&0x3F);
272 Client->Salt[1] = 0x21 + (rand()&0x3F);
273 Client->Salt[2] = 0x21 + (rand()&0x3F);
274 Client->Salt[3] = 0x21 + (rand()&0x3F);
275 Client->Salt[4] = 0x21 + (rand()&0x3F);
276 Client->Salt[5] = 0x21 + (rand()&0x3F);
277 Client->Salt[6] = 0x21 + (rand()&0x3F);
278 Client->Salt[7] = 0x21 + (rand()&0x3F);
280 // "100 Salt xxxxXXXX\n"
281 ret = strdup("100 SALT xxxxXXXX\n");
282 sprintf(ret, "100 SALT %s\n", Client->Salt);
284 ret = strdup("100 User Set\n");
290 * \brief Authenticate as a user
294 char *Server_Cmd_PASS(tClient *Client, char *Args)
296 uint8_t clienthash[HASH_LENGTH] = {0};
299 HexBin(clienthash, Args, HASH_LENGTH);
303 printf("Client %i: Password hash ", Client->ID);
304 for(i=0;i<HASH_LENGTH;i++)
305 printf("%02x", clienthash[i]&0xFF);
309 return strdup("401 Auth Failure\n");
313 * \brief Authenticate as a user without a password
315 * Usage: AUTOAUTH <user>
317 char *Server_Cmd_AUTOAUTH(tClient *Client, char *Args)
319 char *spos = strchr(Args, ' ');
320 if(spos) *spos = '\0'; // Remove characters after the ' '
323 if( !Client->bIsTrusted ) {
325 printf("Client %i: Untrusted client attempting to AUTOAUTH\n", Client->ID);
326 return strdup("401 Untrusted\n");
330 Client->UID = GetUserID( Args );
331 if( Client->UID < 0 ) {
333 printf("Client %i: Unknown user '%s'\n", Client->ID, Args);
334 return strdup("401 Auth Failure\n");
338 printf("Client %i: Authenticated as '%s' (%i)\n", Client->ID, Args, Client->UID);
340 return strdup("200 Auth OK\n");
343 // --- INTERNAL HELPERS ---
344 // TODO: Move to another file
345 void HexBin(uint8_t *Dest, char *Src, int BufSize)
348 for( i = 0; i < BufSize; i ++ )
352 if('0' <= *Src && *Src <= '9')
353 val |= (*Src-'0') << 4;
354 else if('A' <= *Src && *Src <= 'F')
355 val |= (*Src-'A'+10) << 4;
356 else if('a' <= *Src && *Src <= 'f')
357 val |= (*Src-'a'+10) << 4;
362 if('0' <= *Src && *Src <= '9')
364 else if('A' <= *Src && *Src <= 'F')
365 val |= (*Src-'A'+10);
366 else if('a' <= *Src && *Src <= 'f')
367 val |= (*Src-'a'+10);
374 for( ; i < BufSize; i++ )
379 * \brief Decode a Base64 value
381 int UnBase64(uint8_t *Dest, char *Src, int BufSize)
385 char *start_src = Src;
387 for( i = 0; i+2 < BufSize; i += 3 )
390 for( j = 0; j < 4; j++, Src ++ ) {
391 if('A' <= *Src && *Src <= 'Z')
392 val |= (*Src - 'A') << ((3-j)*6);
393 else if('a' <= *Src && *Src <= 'z')
394 val |= (*Src - 'a' + 26) << ((3-j)*6);
395 else if('0' <= *Src && *Src <= '9')
396 val |= (*Src - '0' + 52) << ((3-j)*6);
398 val |= 62 << ((3-j)*6);
400 val |= 63 << ((3-j)*6);
404 j --; // Ignore invalid characters
406 Dest[i ] = (val >> 16) & 0xFF;
407 Dest[i+1] = (val >> 8) & 0xFF;
408 Dest[i+2] = val & 0xFF;
414 Dest[i] = (val >> 16) & 0xFF;
416 Dest[i+1] = (val >> 8) & 0xFF;
418 return Src - start_src;