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 MSG_STR_TOO_LONG "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
25 typedef struct sClient
37 void Server_Start(void);
38 void Server_HandleClient(int Socket);
39 char *Server_ParseClientCommand(tClient *Client, char *CommandString);
41 char *Server_Cmd_USER(tClient *Client, char *Args);
42 char *Server_Cmd_PASS(tClient *Client, char *Args);
44 void HexBin(uint8_t *Dest, char *Src, int BufSize);
47 int giServer_Port = 1020;
48 int giServer_NextClientID = 1;
50 struct sClientCommand {
52 char *(*Function)(tClient *Client, char *Arguments);
53 } gaServer_Commands[] = {
54 {"USER", Server_Cmd_USER},
55 {"PASS", Server_Cmd_PASS}
57 #define NUM_COMMANDS (sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0]))
61 * \brief Open listenting socket and serve connections
63 void Server_Start(void)
65 int server_socket, client_socket;
66 struct sockaddr_in server_addr, client_addr;
69 server_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
70 if( server_socket < 0 ) {
71 fprintf(stderr, "ERROR: Unable to create server socket\n");
75 // Make listen address
76 memset(&server_addr, 0, sizeof(server_addr));
77 server_addr.sin_family = AF_INET; // Internet Socket
78 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // Listen on all interfaces
79 server_addr.sin_port = htons(giServer_Port); // Port
82 if( bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0 ) {
83 fprintf(stderr, "ERROR: Unable to bind to 0.0.0.0:%i\n", giServer_Port);
88 if( listen(server_socket, MAX_CONNECTION_QUEUE) < 0 ) {
89 fprintf(stderr, "ERROR: Unable to listen to socket\n");
93 printf("Listening on 0.0.0.0:%i\n", giServer_Port);
97 uint len = sizeof(client_addr);
99 client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &len);
100 if(client_socket < 0) {
101 fprintf(stderr, "ERROR: Unable to accept client connection\n");
105 if(giDebugLevel >= 2) {
106 char ipstr[INET_ADDRSTRLEN];
107 inet_ntop(AF_INET, &client_addr.sin_addr, ipstr, INET_ADDRSTRLEN);
108 printf("Client connection from %s\n", ipstr);
111 // TODO: Multithread this?
112 Server_HandleClient(client_socket);
114 close(client_socket);
119 * \brief Reads from a client socket and parses the command strings
120 * \param Socket Client socket number/handle
122 void Server_HandleClient(int Socket)
124 char inbuf[INPUT_BUFFER_SIZE];
126 int remspace = INPUT_BUFFER_SIZE-1;
128 tClient clientInfo = {0};
130 // Initialise Client info
131 clientInfo.ID = giServer_NextClientID ++;
136 * - The `buf` and `remspace` variables allow a line to span several
137 * calls to recv(), if a line is not completed in one recv() call
138 * it is saved to the beginning of `inbuf` and `buf` is updated to
141 while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
144 buf[bytes] = '\0'; // Allow us to use stdlib string functions on it
148 while( (eol = strchr(start, '\n')) )
152 ret = Server_ParseClientCommand(&clientInfo, start);
153 // `ret` is a string on the heap
154 send(Socket, ret, strlen(ret), 0);
159 // Check if there was an incomplete line
160 if( *start != '\0' ) {
161 int tailBytes = bytes - (start-buf);
162 // Roll back in buffer
163 memcpy(inbuf, start, tailBytes);
164 remspace -= tailBytes;
166 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
168 remspace = INPUT_BUFFER_SIZE - 1;
173 remspace = INPUT_BUFFER_SIZE - 1;
179 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
183 if(giDebugLevel >= 2) {
184 printf("Client %i disconnected\n", clientInfo.ID);
189 * \brief Parses a client command and calls the required helper function
190 * \param Client Pointer to client state structure
191 * \param CommandString Command from client (single line of the command)
192 * \return Heap String to return to the client
194 char *Server_ParseClientCommand(tClient *Client, char *CommandString)
199 // Split at first space
200 space = strchr(CommandString, ' ');
210 for( i = 0; i < NUM_COMMANDS; i++ )
212 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0)
213 return gaServer_Commands[i].Function(Client, args);
216 return strdup("400 Unknown Command\n");
223 * \brief Set client username
225 * Usage: USER <username>
227 char *Server_Cmd_USER(tClient *Client, char *Args)
233 printf("Client %i authenticating as '%s'\n", Client->ID, Args);
237 free(Client->Username);
238 Client->Username = strdup(Args);
241 // Create a salt (that changes if the username is changed)
242 // Yes, I know, I'm a little paranoid, but who isn't?
243 Client->Salt[0] = 0x21 + (rand()&0x3F);
244 Client->Salt[1] = 0x21 + (rand()&0x3F);
245 Client->Salt[2] = 0x21 + (rand()&0x3F);
246 Client->Salt[3] = 0x21 + (rand()&0x3F);
247 Client->Salt[4] = 0x21 + (rand()&0x3F);
248 Client->Salt[5] = 0x21 + (rand()&0x3F);
249 Client->Salt[6] = 0x21 + (rand()&0x3F);
250 Client->Salt[7] = 0x21 + (rand()&0x3F);
252 // "100 Salt xxxxXXXX\n"
253 ret = strdup("100 SALT xxxxXXXX\n");
254 sprintf(ret, "100 SALT %s\n", Client->Salt);
256 ret = strdup("100 User Set\n");
261 * \brief Authenticate as a user
265 char *Server_Cmd_PASS(tClient *Client, char *Args)
267 uint8_t clienthash[64] = {0};
270 HexBin(clienthash, Args, 64);
274 printf("Client %i: Password hash ", Client->ID);
276 printf("%02x", clienthash[i]&0xFF);
280 return strdup("401 Auth Failure\n");
283 // --- INTERNAL HELPERS ---
284 // TODO: Move to another file
285 void HexBin(uint8_t *Dest, char *Src, int BufSize)
288 for( i = 0; i < BufSize; i ++ )
292 if('0' <= *Src && *Src <= '9')
293 val |= (*Src-'0') << 4;
294 else if('A' <= *Src && *Src <= 'B')
295 val |= (*Src-'A'+10) << 4;
296 else if('a' <= *Src && *Src <= 'b')
297 val |= (*Src-'a'+10) << 4;
302 if('0' <= *Src && *Src <= '9')
304 else if('A' <= *Src && *Src <= 'B')
305 val |= (*Src-'A'+10);
306 else if('a' <= *Src && *Src <= 'b')
307 val |= (*Src-'a'+10);
314 for( ; i < BufSize; i++ )