f97af758b22b07d5e5d103a829cbc2d2a95543a0
[tpg/opendispense2.git] / server / src / server.c
1 /*
2  * OpenDispense 2 
3  * UCC (University [of WA] Computer Club) Electronic Accounting System
4  *
5  * server.c - Client Server Code
6  *
7  * This file is licenced under the 3-clause BSD Licence. See the file
8  * COPYING for full details.
9  */
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include "common.h"
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
16 #include <unistd.h>
17 #include <string.h>
18
19 #define MAX_CONNECTION_QUEUE    5
20 #define INPUT_BUFFER_SIZE       256
21
22 #define MSG_STR_TOO_LONG        "499 Command too long (limit "EXPSTR(INPUT_BUFFER_SIZE)")\n"
23
24 // === TYPES ===
25 typedef struct sClient
26 {
27          int    ID;     // Client ID
28         
29         char    *Username;
30         char    Salt[9];
31         
32          int    UID;
33          int    bIsAuthed;
34 }       tClient;
35
36 // === PROTOTYPES ===
37 void    Server_Start(void);
38 void    Server_HandleClient(int Socket);
39 char    *Server_ParseClientCommand(tClient *Client, char *CommandString);
40 // --- Commands ---
41 char    *Server_Cmd_USER(tClient *Client, char *Args);
42 char    *Server_Cmd_PASS(tClient *Client, char *Args);
43 // --- Helpers ---
44 void    HexBin(uint8_t *Dest, char *Src, int BufSize);
45
46 // === GLOBALS ===
47  int    giServer_Port = 1020;
48  int    giServer_NextClientID = 1;
49 // - Commands
50 struct sClientCommand {
51         char    *Name;
52         char    *(*Function)(tClient *Client, char *Arguments);
53 }       gaServer_Commands[] = {
54         {"USER", Server_Cmd_USER},
55         {"PASS", Server_Cmd_PASS}
56 };
57 #define NUM_COMMANDS    (sizeof(gaServer_Commands)/sizeof(gaServer_Commands[0]))
58
59 // === CODE ===
60 /**
61  * \brief Open listenting socket and serve connections
62  */
63 void Server_Start(void)
64 {
65          int    server_socket, client_socket;
66         struct sockaddr_in      server_addr, client_addr;
67
68         // Create Server
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");
72                 return ;
73         }
74         
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
80
81         // Bind
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);
84                 return ;
85         }
86         
87         // Listen
88         if( listen(server_socket, MAX_CONNECTION_QUEUE) < 0 ) {
89                 fprintf(stderr, "ERROR: Unable to listen to socket\n");
90                 return ;
91         }
92         
93         printf("Listening on 0.0.0.0:%i\n", giServer_Port);
94         
95         for(;;)
96         {
97                 uint    len = sizeof(client_addr);
98                 
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");
102                         return ;
103                 }
104                 
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);
109                 }
110                 
111                 // TODO: Multithread this?
112                 Server_HandleClient(client_socket);
113                 
114                 close(client_socket);
115         }
116 }
117
118 /**
119  * \brief Reads from a client socket and parses the command strings
120  * \param Socket        Client socket number/handle
121  */
122 void Server_HandleClient(int Socket)
123 {
124         char    inbuf[INPUT_BUFFER_SIZE];
125         char    *buf = inbuf;
126          int    remspace = INPUT_BUFFER_SIZE-1;
127          int    bytes = -1;
128         tClient clientInfo = {0};
129         
130         // Initialise Client info
131         clientInfo.ID = giServer_NextClientID ++;
132                 
133         // Read from client
134         /*
135          * Notes:
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
139          *   the end of it.
140          */
141         while( (bytes = recv(Socket, buf, remspace, 0)) > 0 )
142         {
143                 char    *eol, *start;
144                 buf[bytes] = '\0';      // Allow us to use stdlib string functions on it
145                 
146                 // Split by lines
147                 start = inbuf;
148                 while( (eol = strchr(start, '\n')) )
149                 {
150                         char    *ret;
151                         *eol = '\0';
152                         ret = Server_ParseClientCommand(&clientInfo, start);
153                         // `ret` is a string on the heap
154                         send(Socket, ret, strlen(ret), 0);
155                         free(ret);
156                         start = eol + 1;
157                 }
158                 
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;
165                         if(remspace == 0) {
166                                 send(Socket, MSG_STR_TOO_LONG, sizeof(MSG_STR_TOO_LONG), 0);
167                                 buf = inbuf;
168                                 remspace = INPUT_BUFFER_SIZE - 1;
169                         }
170                 }
171                 else {
172                         buf = inbuf;
173                         remspace = INPUT_BUFFER_SIZE - 1;
174                 }
175         }
176         
177         // Check for errors
178         if( bytes < 0 ) {
179                 fprintf(stderr, "ERROR: Unable to recieve from client on socket %i\n", Socket);
180                 return ;
181         }
182         
183         if(giDebugLevel >= 2) {
184                 printf("Client %i disconnected\n", clientInfo.ID);
185         }
186 }
187
188 /**
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
193  */
194 char *Server_ParseClientCommand(tClient *Client, char *CommandString)
195 {
196         char    *space, *args;
197          int    i;
198         
199         // Split at first space
200         space = strchr(CommandString, ' ');
201         if(space == NULL) {
202                 args = NULL;
203         }
204         else {
205                 *space = '\0';
206                 args = space + 1;
207         }
208         
209         // Find command
210         for( i = 0; i < NUM_COMMANDS; i++ )
211         {
212                 if(strcmp(CommandString, gaServer_Commands[i].Name) == 0)
213                         return gaServer_Commands[i].Function(Client, args);
214         }
215         
216         return strdup("400      Unknown Command\n");
217 }
218
219 // ---
220 // Commands
221 // ---
222 /**
223  * \brief Set client username
224  * 
225  * Usage: USER <username>
226  */
227 char *Server_Cmd_USER(tClient *Client, char *Args)
228 {
229         char    *ret;
230         
231         // Debug!
232         if( giDebugLevel )
233                 printf("Client %i authenticating as '%s'\n", Client->ID, Args);
234         
235         // Save username
236         if(Client->Username)
237                 free(Client->Username);
238         Client->Username = strdup(Args);
239         
240         #if USE_SALT
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);
251         
252         // "100 Salt xxxxXXXX\n"
253         ret = strdup("100 SALT xxxxXXXX\n");
254         sprintf(ret, "100 SALT %s\n", Client->Salt);
255         #else
256         ret = strdup("100 User Set\n");
257         #endif
258         return ret;
259 }
260 /**
261  * \brief Authenticate as a user
262  * 
263  * Usage: PASS <hash>
264  */
265 char *Server_Cmd_PASS(tClient *Client, char *Args)
266 {
267         uint8_t clienthash[64] = {0};
268         
269         // Read user's hash
270         HexBin(clienthash, Args, 64);
271         
272         if( giDebugLevel ) {
273                  int    i;
274                 printf("Client %i: Password hash ", Client->ID);
275                 for(i=0;i<64;i++)
276                         printf("%02x", clienthash[i]&0xFF);
277                 printf("\n");
278         }
279         
280         return strdup("401 Auth Failure\n");
281 }
282
283 // --- INTERNAL HELPERS ---
284 // TODO: Move to another file
285 void HexBin(uint8_t *Dest, char *Src, int BufSize)
286 {
287          int    i;
288         for( i = 0; i < BufSize; i ++ )
289         {
290                 uint8_t val = 0;
291                 
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;
298                 else
299                         break;
300                 Src ++;
301                 
302                 if('0' <= *Src && *Src <= '9')
303                         val |= (*Src-'0');
304                 else if('A' <= *Src && *Src <= 'B')
305                         val |= (*Src-'A'+10);
306                 else if('a' <= *Src && *Src <= 'b')
307                         val |= (*Src-'a'+10);
308                 else
309                         break;
310                 Src ++;
311                 
312                 Dest[i] = val;
313         }
314         for( ; i < BufSize; i++ )
315                 Dest[i] = 0;
316 }

UCC git Repository :: git.ucc.asn.au