AcessNative - Set SO_REUSEADDR
[tpg/acess2.git] / AcessNative / acesskernel_src / server.c
1 /*
2  * Acess2 Native Kernel
3  * - Acess kernel emulation on another OS using SDL and UDP
4  *
5  * Syscall Server
6  */
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <stdbool.h>
10 #include <string.h>
11 #include <SDL/SDL.h>
12 #ifdef __WIN32__
13 # define _WIN32_WINNT 0x0501
14 # include <windows.h>
15 # include <winsock2.h>
16 # include <ws2tcpip.h>
17 # define close(fd)      closesocket(fd)
18 typedef int     socklen_t;
19 #else
20 # include <unistd.h>
21 # include <sys/socket.h>
22 # include <netinet/in.h>
23 # include <netdb.h>     // getaddrinfo
24 #endif
25 #define DONT_INCLUDE_SYSCALL_NAMES
26 #include "../syscalls.h"
27 #include <logdebug.h>   // acess but std
28 #include <errno.h>
29
30 #define USE_TCP 1
31 #define MAX_CLIENTS     16
32
33 // === TYPES ===
34 typedef struct {
35          int    ClientID;
36         SDL_Thread      *WorkerThread;
37         tRequestHeader  *CurrentRequest;
38         SDL_cond        *WaitFlag;
39         SDL_mutex       *Mutex;
40         #if USE_TCP
41          int    Socket;
42         #else
43         struct sockaddr_in      ClientAddr;
44         #endif
45 }       tClient;
46
47 // === IMPORTS ===
48 // TODO: Move these to headers
49 extern tRequestHeader *SyscallRecieve(tRequestHeader *Request, size_t *ReturnLength);
50 extern int      Threads_CreateRootProcess(void);
51 extern void     Threads_SetThread(int TID, void *ClientPtr);
52 extern void     *Threads_GetThread(int TID);
53 extern void     Threads_PostEvent(void *Thread, uint32_t Event);
54 extern void     Threads_int_Terminate(void *Thread);
55
56 // === PROTOTYPES ===
57 tClient *Server_GetClient(int ClientID);
58  int    Server_WorkerThread(void *ClientPtr);
59  int    SyscallServer(void);
60  int    Server_ListenThread(void *Unused);
61
62 // === GLOBALS ===
63 #ifdef __WIN32__
64 WSADATA gWinsock;
65 SOCKET  gSocket = INVALID_SOCKET;
66 #else
67 # define INVALID_SOCKET -1
68  int    gSocket = INVALID_SOCKET;
69 #endif
70 tClient gaServer_Clients[MAX_CLIENTS];
71 SDL_Thread      *gpServer_ListenThread;
72
73 // === CODE ===
74 int Server_GetClientID(void)
75 {
76         Uint32  thisId = SDL_ThreadID();
77         
78         for( int i = 0; i < MAX_CLIENTS; i ++ )
79         {
80                 if( SDL_GetThreadID(gaServer_Clients[i].WorkerThread) == thisId )
81                         return gaServer_Clients[i].ClientID;
82         }
83         
84         fprintf(stderr, "ERROR: Server_GetClientID - Thread is not allocated\n");
85         
86         return 0;
87 }
88
89 tClient *Server_GetClient(int ClientID)
90 {
91         tClient *ret = NULL;
92         
93         // Allocate an ID if needed
94         if(ClientID == 0)
95                 ClientID = Threads_CreateRootProcess();
96         
97         for( int i = 0; i < MAX_CLIENTS; i ++ )
98         {
99                 if( gaServer_Clients[i].ClientID == ClientID ) {
100                         return &gaServer_Clients[i];
101                 }
102                 if(!ret && gaServer_Clients[i].ClientID == 0)
103                         ret = &gaServer_Clients[i];
104         }
105         
106         // Uh oh, no free slots
107         // TODO: Dynamic allocation
108         if( !ret ) {
109                 Log_Error("Server", "Ran out of static client slots (%i)", MAX_CLIENTS);
110                 return NULL;
111         }
112         
113         // Allocate a thread for the process
114         ret->ClientID = ClientID;
115         ret->CurrentRequest = NULL;
116         #if USE_TCP
117         ret->Socket = 0;
118         #endif
119                 
120         if( !ret->WorkerThread ) {
121                 Log_Debug("Server", "Creating worker for %p", ret);
122                 ret->WaitFlag = SDL_CreateCond();
123                 ret->Mutex = SDL_CreateMutex();
124                 SDL_mutexP( ret->Mutex );
125                 ret->WorkerThread = SDL_CreateThread( Server_WorkerThread, ret );
126         }
127         
128         return ret;
129 }
130
131 int Server_WorkerThread(void *ClientPtr)
132 {
133         tClient *Client = ClientPtr;
134
135         Log_Debug("Server", "Worker %p active", ClientPtr);     
136
137         tRequestHeader  errorHeader;
138         size_t  retSize = 0;
139          int    cur_client_id = 0;
140         while( Client->ClientID != 0 )
141         {
142                 // Wait for something to do
143                 if( Client->CurrentRequest == NULL )
144                         SDL_CondWait(Client->WaitFlag, Client->Mutex);
145                 if( Client->CurrentRequest == NULL )
146                         continue ;
147                 
148                 if(Client->ClientID != cur_client_id) {
149                         Threads_SetThread( Client->ClientID, Client );
150                         cur_client_id = Client->ClientID;
151                 }
152                 
153                 // Get the response
154                 tRequestHeader  *retHeader = SyscallRecieve(Client->CurrentRequest, &retSize);
155
156                 if( !retHeader ) {
157                         // Return an error to the client
158                         printf("ERROR: SyscallRecieve failed\n");
159                         errorHeader.CallID = Client->CurrentRequest->CallID;
160                         errorHeader.NParams = 0;
161                         retHeader = &errorHeader;
162                         retSize = sizeof(errorHeader);
163                 }
164                 
165                 // Set ID
166                 retHeader->ClientID = Client->ClientID;
167                 
168                 // Mark the thread as ready for another job
169                 free(Client->CurrentRequest);
170                 Client->CurrentRequest = 0;
171
172                 // If the thread is being terminated, don't send reply
173                 if( Client->ClientID > 0 )
174                 {
175                         // Return the data
176                         #if USE_TCP
177                         size_t sentSize = send(Client->Socket, retHeader, retSize, 0); 
178                         #else
179                         size_t sentSize = sendto(gSocket, retHeader, retSize, 0,
180                                 (struct sockaddr*)&Client->ClientAddr, sizeof(Client->ClientAddr)
181                                 );
182                         #endif
183                         if( sentSize != retSize ) {
184                                 perror("Server_WorkerThread - send");
185                         }
186                 }
187                 
188                 // Free allocated header
189                 if( retHeader != &errorHeader )
190                         free( retHeader );
191         }
192         Log_Notice("Server", "Terminated Worker %p", ClientPtr);        
193         return 0;
194 }
195
196 int SyscallServer(void)
197 {
198         struct sockaddr_in      server;
199         
200         #ifdef __WIN32__
201         /* Open windows connection */
202         if (WSAStartup(0x0101, &gWinsock) != 0)
203         {
204                 fprintf(stderr, "Could not open Windows connection.\n");
205                 exit(0);
206         }
207         #endif
208         
209         #if USE_TCP
210         // Open TCP Connection
211         gSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
212         #else
213         // Open UDP Connection
214         gSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
215         #endif
216         if (gSocket == INVALID_SOCKET)
217         {
218                 fprintf(stderr, "Could not create socket.\n");
219                 #if __WIN32__
220                 WSACleanup();
221                 #endif
222                 exit(0);
223         }
224         
225         // Set server address
226         memset(&server, 0, sizeof(struct sockaddr_in));
227         server.sin_family = AF_INET;
228         server.sin_port = htons(SERVER_PORT);
229         server.sin_addr.s_addr = htonl(INADDR_ANY);
230         
231         #if USE_TCP
232         {
233                 int val = 1;
234                 setsockopt(gSocket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val);
235         }
236         #endif
237         
238         // Bind
239         if( bind(gSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1 )
240         {
241                 fprintf(stderr, "Cannot bind address to socket.\n");
242                 perror("SyscallServer - bind");
243                 #if __WIN32__
244                 closesocket(gSocket);
245                 WSACleanup();
246                 #else
247                 close(gSocket);
248                 #endif
249                 exit(0);
250         }
251         
252         #if USE_TCP
253         listen(gSocket, 5);
254         #endif
255         
256         Log_Notice("AcessSrv", "Listening on 0.0.0.0:%i", SERVER_PORT);
257         gpServer_ListenThread = SDL_CreateThread( Server_ListenThread, NULL );
258         return 0;
259 }
260
261 int Server_Shutdown(void)
262 {
263         close(gSocket);
264         for( int i = 0; i < MAX_CLIENTS; i ++ )
265         {
266                 if( gaServer_Clients[i].ClientID == 0 )
267                         continue ;
268                 Threads_PostEvent( Threads_GetThread(gaServer_Clients[i].ClientID), 0 );
269                 gaServer_Clients[i].ClientID = -1;
270                 #if USE_TCP
271                 close(gaServer_Clients[i].Socket);
272                 #else
273                 SDL_CondSignal(gaServer_Clients[i].WaitFlag);
274                 #endif
275         }
276         return 0;
277 }
278
279 #if USE_TCP
280 int Server_int_HandleRx(tClient *Client)
281 {
282         const int       ciMaxParamCount = 6;
283         char    lbuf[sizeof(tRequestHeader) + ciMaxParamCount*sizeof(tRequestValue)];
284         tRequestHeader  *hdr = (void*)lbuf;
285         size_t  len = recv(Client->Socket, (void*)hdr, sizeof(*hdr), 0);
286         if( len == 0 ) {
287                 Log_Notice("Server", "Zero RX on %i (worker %p)", Client->Socket, Client);
288                 return 1;
289         }
290         if( len == -1 ) {
291                 perror("recv header");
292                 return 2;
293         }
294         if( len != sizeof(*hdr) ) {
295                 // Oops?
296                 Log_Warning("Server", "FD%i bad sized (%i != exp %i)",
297                         Client->Socket, len, sizeof(*hdr));
298                 return 0;
299         }
300
301         if( hdr->NParams > ciMaxParamCount ) {
302                 // Oops.
303                 Log_Warning("Server", "FD%i too many params (%i > max %i)",
304                         Client->Socket, hdr->NParams, ciMaxParamCount);
305                 return 0;
306         }
307
308         if( hdr->NParams > 0 )
309         {
310                 len = recv(Client->Socket, (void*)hdr->Params, hdr->NParams*sizeof(tRequestValue), 0);
311                 if( len != hdr->NParams*sizeof(tRequestValue) ) {
312                         // Oops.
313                         perror("recv params");
314                         Log_Warning("Sever", "Recieving params failed");
315                         return 0;
316                 }
317         }
318         else
319         {
320                 //Log_Debug("Server", "No params?");
321         }
322
323         // Get buffer size
324         size_t  hdrsize = sizeof(tRequestHeader) + hdr->NParams*sizeof(tRequestValue);
325         size_t  bufsize = hdrsize;
326         for( int i = 0; i < hdr->NParams; i ++ )
327         {
328                 if( hdr->Params[i].Flags & ARG_FLAG_ZEROED )
329                         ;
330                 else {
331                         bufsize += hdr->Params[i].Length;
332                 }
333         }
334
335         // Allocate full buffer
336         hdr = malloc(bufsize);
337         memcpy(hdr, lbuf, hdrsize);
338         if( bufsize > hdrsize )
339         {
340                 size_t  rem = bufsize - hdrsize;
341                 char    *ptr = (void*)( hdr->Params + hdr->NParams );
342                 while( rem )
343                 {
344                         len = recv(Client->Socket, ptr, rem, 0);
345                         if( len == -1 ) {
346                                 // Oops?
347                                 perror("recv data");
348                                 Log_Warning("Sever", "Recieving data failed");
349                                 return 2;
350                         }
351                         rem -= len;
352                         ptr += len;
353                 }
354                 if( rem ) {
355                         // Extra data?
356                         return 0;
357                 }
358         }
359         else {
360                 //Log_Debug("Server", "no data");
361         }
362         
363         // Dispatch to worker
364         if( Client->CurrentRequest ) {
365                 printf("Worker thread for client ID %i is busy\n", Client->ClientID);
366                 return 1;
367         }
368
369         // Give to worker
370         Log_Debug("Server", "Message from Client %i (%p)", Client->ClientID, Client);
371         Client->CurrentRequest = hdr;
372         SDL_CondSignal(Client->WaitFlag);
373
374         return 0;
375 }
376
377 int Server_int_HandshakeClient(int Socket, struct sockaddr_in *addr, socklen_t addr_size)
378 {
379         ENTER("iSocket paddr iaddr_size",
380                 Socket, addr, addr_size);
381         unsigned short  port = ntohs(addr->sin_port);
382         char    addrstr[4*8+8+1];
383         getnameinfo((struct sockaddr*)addr, addr_size, addrstr, sizeof(addrstr), NULL, 0, NI_NUMERICHOST);
384         Log_Debug("Server", "Client connection %s:%i", addrstr, port);
385         
386         // Perform handshake
387         tRequestAuthHdr authhdr;
388         size_t  len  = recv(Socket, &authhdr, sizeof(authhdr), 0);
389         if( len != sizeof(authhdr) ) {
390                 // Some form of error?
391                 Log_Warning("Server", "Client auth block bad size (%i != exp %i)",
392                         len, sizeof(authhdr));
393                 LEAVE('i', 1);
394                 return 1;
395         }
396         
397         LOG("authhdr.pid = %i", authhdr.pid);
398         tClient *client = Server_GetClient(authhdr.pid);
399         if( authhdr.pid == 0 ) {
400                 // Allocate PID and client structure/thread
401                 client->Socket = Socket;
402                 authhdr.pid = client->ClientID;
403         }
404         else {
405                 Log_Debug("Server", "Client assumed PID %i", authhdr.pid);
406                 
407                 // Get client structure and make sure it's unused
408                 // - Auth token / verifcation?
409                 if( !client ) {
410                         Log_Warning("Server", "Can't allocate a client struct for %s:%i",
411                                 addrstr, port);
412                         LEAVE('i', 1);
413                         return 1;
414                 }
415                 if( client->Socket != 0 ) {
416                         Log_Warning("Server", "Client (%i)%p owned by FD%i but %s:%i tried to use it",
417                                 authhdr.pid, client, addrstr, port);
418                         LEAVE('i', 1);
419                         return 1;
420                 }
421                 
422                 client->Socket = Socket;
423         }
424
425         LOG("Sending auth reply");      
426         len = send(Socket, (void*)&authhdr, sizeof(authhdr), 0);
427         if( len != sizeof(authhdr) ) {
428                 // Ok, this is an error
429                 perror("Sending auth reply");
430                 LEAVE('i', 1);
431                 return 1;
432         }
433
434         // All done, client thread should be watching now               
435         
436         LEAVE('i', 0);
437         return 0;
438 }
439
440 void Server_int_RemoveClient(tClient *Client)
441 {
442         // Trigger the thread to kill itself
443         Threads_int_Terminate( Threads_GetThread(Client->ClientID) );
444         Client->ClientID = 0;
445         close(Client->Socket);
446 }
447
448 #endif
449
450 int Server_ListenThread(void *Unused)
451 {       
452         // Wait for something to do :)
453         for( ;; )
454         {
455                 #if USE_TCP
456                 fd_set  fds;
457                  int    maxfd = gSocket;
458                 FD_ZERO(&fds);
459                 FD_SET(gSocket, &fds);
460
461                 for( int i = 0; i < MAX_CLIENTS; i ++ ) {
462                         tClient *client = &gaServer_Clients[i];
463                         if( client->ClientID == 0 )
464                                 continue ;
465                         FD_SET(client->Socket, &fds);
466                         if(client->Socket > maxfd)
467                                 maxfd = client->Socket;
468                 }
469                 
470                 int rv = select(maxfd+1, &fds, NULL, NULL, NULL);
471                 Log_Debug("Server", "Select rv = %i", rv);
472                 if( rv <= 0 ) {
473                         perror("select");
474                         return 1;
475                 }
476                 
477                 // Incoming connection
478                 if( FD_ISSET(gSocket, &fds) )
479                 {
480                         struct sockaddr_in      clientaddr;
481                         socklen_t       clientSize = sizeof(clientaddr);
482                          int    clientSock = accept(gSocket, (struct sockaddr*)&clientaddr, &clientSize);
483                         if( clientSock < 0 ) {
484                                 perror("SyscallServer - accept");
485                                 break ;
486                         }
487                         if( Server_int_HandshakeClient(clientSock, &clientaddr, clientSize) ) {
488                                 Log_Warning("Server", "Client handshake failed :(");
489                                 close(clientSock);
490                         }
491                 }
492                 
493                 for( int i = 0; i < MAX_CLIENTS; i ++ )
494                 {
495                         tClient *client = &gaServer_Clients[i];
496                         if( client->ClientID == 0 )
497                                 continue ;
498                         //Debug("Server_ListenThread: Idx %i ID %i FD %i",
499                         //      i, client->ClientID, client->Socket);
500                         if( !FD_ISSET(client->Socket, &fds) )
501                                 continue ;
502                         
503                         if( Server_int_HandleRx( client ) )
504                         {
505                                 Log_Warning("Server", "Client %p dropped, TODO: clean up", client);
506                                 Server_int_RemoveClient(client);
507                         }
508                 }
509         
510                 #else
511                 char    data[BUFSIZ];
512                 tRequestHeader  *req = (void*)data;
513                 struct sockaddr_in      addr;
514                 uint    clientSize = sizeof(addr);
515                  int    length;
516                 
517                 length = recvfrom(gSocket, data, BUFSIZ, 0, (struct sockaddr*)&addr, &clientSize);
518                 
519                 if( length == -1 ) {
520                         perror("SyscallServer - recv");
521                         break;
522                 }
523                 
524                 // Recive data
525 //              Log_Debug("Server", "%i bytes from %x:%i", length,
526 //                      ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
527                 
528                 tClient *client = Server_GetClient(req->ClientID);
529                 // NOTE: I should really check if the sin_addr is zero, but meh
530                 // Shouldn't matter much
531                 if( req->ClientID == 0 || client->ClientAddr.sin_port == 0 )
532                 {
533                         memcpy(&client->ClientAddr, &addr, sizeof(addr));
534                 }
535                 else if( memcmp(&client->ClientAddr, &addr, sizeof(addr)) != 0 )
536                 {
537                         printf("ClientID %i used by %x:%i\n",
538                                 client->ClientID, ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
539                         printf(" actually owned by %x:%i\n",
540                                 ntohl(client->ClientAddr.sin_addr.s_addr), ntohs(client->ClientAddr.sin_port));
541                         continue;
542                 }
543                 
544 //              Log_Debug("AcessSrv", "Message from Client %i (%p)",
545 //                      client->ClientID, client);
546                 if( client->CurrentRequest ) {
547                         printf("Worker thread for %x:%i is busy\n",
548                                 ntohl(client->ClientAddr.sin_addr.s_addr), ntohs(client->ClientAddr.sin_port));
549                         continue;
550                 }
551                 
552                 // Duplicate the data currently on the stack, and dispatch to worker
553                 req = malloc(length);
554                 memcpy(req, data, length);
555                 client->CurrentRequest = req;
556                 SDL_CondSignal(client->WaitFlag);
557                 #endif
558         }
559         return -1;
560 }

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