Usermode/libaxwin4 - Handle demarshal failure
[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         // Bind
232         if( bind(gSocket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1 )
233         {
234                 fprintf(stderr, "Cannot bind address to socket.\n");
235                 perror("SyscallServer - bind");
236                 #if __WIN32__
237                 closesocket(gSocket);
238                 WSACleanup();
239                 #else
240                 close(gSocket);
241                 #endif
242                 exit(0);
243         }
244         
245         #if USE_TCP
246         listen(gSocket, 5);
247         #endif
248         
249         Log_Notice("AcessSrv", "Listening on 0.0.0.0:%i", SERVER_PORT);
250         gpServer_ListenThread = SDL_CreateThread( Server_ListenThread, NULL );
251         return 0;
252 }
253
254 int Server_Shutdown(void)
255 {
256         close(gSocket);
257         for( int i = 0; i < MAX_CLIENTS; i ++ )
258         {
259                 if( gaServer_Clients[i].ClientID == 0 )
260                         continue ;
261                 Threads_PostEvent( Threads_GetThread(gaServer_Clients[i].ClientID), 0 );
262                 gaServer_Clients[i].ClientID = -1;
263                 #if USE_TCP
264                 close(gaServer_Clients[i].Socket);
265                 #else
266                 SDL_CondSignal(gaServer_Clients[i].WaitFlag);
267                 #endif
268         }
269         return 0;
270 }
271
272 #if USE_TCP
273 int Server_int_HandleRx(tClient *Client)
274 {
275         const int       ciMaxParamCount = 6;
276         char    lbuf[sizeof(tRequestHeader) + ciMaxParamCount*sizeof(tRequestValue)];
277         tRequestHeader  *hdr = (void*)lbuf;
278         size_t  len = recv(Client->Socket, (void*)hdr, sizeof(*hdr), 0);
279         if( len == 0 ) {
280                 Log_Notice("Server", "Zero RX on %i (worker %p)", Client->Socket, Client);
281                 return 1;
282         }
283         if( len == -1 ) {
284                 perror("recv header");
285                 return 2;
286         }
287         if( len != sizeof(*hdr) ) {
288                 // Oops?
289                 Log_Warning("Server", "FD%i bad sized (%i != exp %i)",
290                         Client->Socket, len, sizeof(*hdr));
291                 return 0;
292         }
293
294         if( hdr->NParams > ciMaxParamCount ) {
295                 // Oops.
296                 Log_Warning("Server", "FD%i too many params (%i > max %i)",
297                         Client->Socket, hdr->NParams, ciMaxParamCount);
298                 return 0;
299         }
300
301         if( hdr->NParams > 0 )
302         {
303                 len = recv(Client->Socket, (void*)hdr->Params, hdr->NParams*sizeof(tRequestValue), 0);
304                 if( len != hdr->NParams*sizeof(tRequestValue) ) {
305                         // Oops.
306                         perror("recv params");
307                         Log_Warning("Sever", "Recieving params failed");
308                         return 0;
309                 }
310         }
311         else
312         {
313                 //Log_Debug("Server", "No params?");
314         }
315
316         // Get buffer size
317         size_t  hdrsize = sizeof(tRequestHeader) + hdr->NParams*sizeof(tRequestValue);
318         size_t  bufsize = hdrsize;
319         for( int i = 0; i < hdr->NParams; i ++ )
320         {
321                 if( hdr->Params[i].Flags & ARG_FLAG_ZEROED )
322                         ;
323                 else {
324                         bufsize += hdr->Params[i].Length;
325                 }
326         }
327
328         // Allocate full buffer
329         hdr = malloc(bufsize);
330         memcpy(hdr, lbuf, hdrsize);
331         if( bufsize > hdrsize )
332         {
333                 size_t  rem = bufsize - hdrsize;
334                 char    *ptr = (void*)( hdr->Params + hdr->NParams );
335                 while( rem )
336                 {
337                         len = recv(Client->Socket, ptr, rem, 0);
338                         if( len == -1 ) {
339                                 // Oops?
340                                 perror("recv data");
341                                 Log_Warning("Sever", "Recieving data failed");
342                                 return 2;
343                         }
344                         rem -= len;
345                         ptr += len;
346                 }
347                 if( rem ) {
348                         // Extra data?
349                         return 0;
350                 }
351         }
352         else {
353                 //Log_Debug("Server", "no data");
354         }
355         
356         // Dispatch to worker
357         if( Client->CurrentRequest ) {
358                 printf("Worker thread for client ID %i is busy\n", Client->ClientID);
359                 return 1;
360         }
361
362         // Give to worker
363         Log_Debug("Server", "Message from Client %i (%p)", Client->ClientID, Client);
364         Client->CurrentRequest = hdr;
365         SDL_CondSignal(Client->WaitFlag);
366
367         return 0;
368 }
369
370 int Server_int_HandshakeClient(int Socket, struct sockaddr_in *addr, socklen_t addr_size)
371 {
372         ENTER("iSocket paddr iaddr_size",
373                 Socket, addr, addr_size);
374         unsigned short  port = ntohs(addr->sin_port);
375         char    addrstr[4*8+8+1];
376         getnameinfo((struct sockaddr*)addr, addr_size, addrstr, sizeof(addrstr), NULL, 0, NI_NUMERICHOST);
377         Log_Debug("Server", "Client connection %s:%i", addrstr, port);
378         
379         // Perform handshake
380         tRequestAuthHdr authhdr;
381         size_t  len  = recv(Socket, &authhdr, sizeof(authhdr), 0);
382         if( len != sizeof(authhdr) ) {
383                 // Some form of error?
384                 Log_Warning("Server", "Client auth block bad size (%i != exp %i)",
385                         len, sizeof(authhdr));
386                 LEAVE('i', 1);
387                 return 1;
388         }
389         
390         LOG("authhdr.pid = %i", authhdr.pid);
391         tClient *client = Server_GetClient(authhdr.pid);
392         if( authhdr.pid == 0 ) {
393                 // Allocate PID and client structure/thread
394                 client->Socket = Socket;
395                 authhdr.pid = client->ClientID;
396         }
397         else {
398                 Log_Debug("Server", "Client assumed PID %i", authhdr.pid);
399                 
400                 // Get client structure and make sure it's unused
401                 // - Auth token / verifcation?
402                 if( !client ) {
403                         Log_Warning("Server", "Can't allocate a client struct for %s:%i",
404                                 addrstr, port);
405                         LEAVE('i', 1);
406                         return 1;
407                 }
408                 if( client->Socket != 0 ) {
409                         Log_Warning("Server", "Client (%i)%p owned by FD%i but %s:%i tried to use it",
410                                 authhdr.pid, client, addrstr, port);
411                         LEAVE('i', 1);
412                         return 1;
413                 }
414                 
415                 client->Socket = Socket;
416         }
417
418         LOG("Sending auth reply");      
419         len = send(Socket, (void*)&authhdr, sizeof(authhdr), 0);
420         if( len != sizeof(authhdr) ) {
421                 // Ok, this is an error
422                 perror("Sending auth reply");
423                 LEAVE('i', 1);
424                 return 1;
425         }
426
427         // All done, client thread should be watching now               
428         
429         LEAVE('i', 0);
430         return 0;
431 }
432
433 void Server_int_RemoveClient(tClient *Client)
434 {
435         // Trigger the thread to kill itself
436         Threads_int_Terminate( Threads_GetThread(Client->ClientID) );
437         Client->ClientID = 0;
438         close(Client->Socket);
439 }
440
441 #endif
442
443 int Server_ListenThread(void *Unused)
444 {       
445         // Wait for something to do :)
446         for( ;; )
447         {
448                 #if USE_TCP
449                 fd_set  fds;
450                  int    maxfd = gSocket;
451                 FD_ZERO(&fds);
452                 FD_SET(gSocket, &fds);
453
454                 for( int i = 0; i < MAX_CLIENTS; i ++ ) {
455                         tClient *client = &gaServer_Clients[i];
456                         if( client->ClientID == 0 )
457                                 continue ;
458                         FD_SET(client->Socket, &fds);
459                         if(client->Socket > maxfd)
460                                 maxfd = client->Socket;
461                 }
462                 
463                 int rv = select(maxfd+1, &fds, NULL, NULL, NULL);
464                 Log_Debug("Server", "Select rv = %i", rv);
465                 if( rv <= 0 ) {
466                         perror("select");
467                         return 1;
468                 }
469                 
470                 // Incoming connection
471                 if( FD_ISSET(gSocket, &fds) )
472                 {
473                         struct sockaddr_in      clientaddr;
474                         socklen_t       clientSize = sizeof(clientaddr);
475                          int    clientSock = accept(gSocket, (struct sockaddr*)&clientaddr, &clientSize);
476                         if( clientSock < 0 ) {
477                                 perror("SyscallServer - accept");
478                                 break ;
479                         }
480                         if( Server_int_HandshakeClient(clientSock, &clientaddr, clientSize) ) {
481                                 Log_Warning("Server", "Client handshake failed :(");
482                                 close(clientSock);
483                         }
484                 }
485                 
486                 for( int i = 0; i < MAX_CLIENTS; i ++ )
487                 {
488                         tClient *client = &gaServer_Clients[i];
489                         if( client->ClientID == 0 )
490                                 continue ;
491                         //Debug("Server_ListenThread: Idx %i ID %i FD %i",
492                         //      i, client->ClientID, client->Socket);
493                         if( !FD_ISSET(client->Socket, &fds) )
494                                 continue ;
495                         
496                         if( Server_int_HandleRx( client ) )
497                         {
498                                 Log_Warning("Server", "Client %p dropped, TODO: clean up", client);
499                                 Server_int_RemoveClient(client);
500                         }
501                 }
502         
503                 #else
504                 char    data[BUFSIZ];
505                 tRequestHeader  *req = (void*)data;
506                 struct sockaddr_in      addr;
507                 uint    clientSize = sizeof(addr);
508                  int    length;
509                 
510                 length = recvfrom(gSocket, data, BUFSIZ, 0, (struct sockaddr*)&addr, &clientSize);
511                 
512                 if( length == -1 ) {
513                         perror("SyscallServer - recv");
514                         break;
515                 }
516                 
517                 // Recive data
518 //              Log_Debug("Server", "%i bytes from %x:%i", length,
519 //                      ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
520                 
521                 tClient *client = Server_GetClient(req->ClientID);
522                 // NOTE: I should really check if the sin_addr is zero, but meh
523                 // Shouldn't matter much
524                 if( req->ClientID == 0 || client->ClientAddr.sin_port == 0 )
525                 {
526                         memcpy(&client->ClientAddr, &addr, sizeof(addr));
527                 }
528                 else if( memcmp(&client->ClientAddr, &addr, sizeof(addr)) != 0 )
529                 {
530                         printf("ClientID %i used by %x:%i\n",
531                                 client->ClientID, ntohl(addr.sin_addr.s_addr), ntohs(addr.sin_port));
532                         printf(" actually owned by %x:%i\n",
533                                 ntohl(client->ClientAddr.sin_addr.s_addr), ntohs(client->ClientAddr.sin_port));
534                         continue;
535                 }
536                 
537 //              Log_Debug("AcessSrv", "Message from Client %i (%p)",
538 //                      client->ClientID, client);
539                 if( client->CurrentRequest ) {
540                         printf("Worker thread for %x:%i is busy\n",
541                                 ntohl(client->ClientAddr.sin_addr.s_addr), ntohs(client->ClientAddr.sin_port));
542                         continue;
543                 }
544                 
545                 // Duplicate the data currently on the stack, and dispatch to worker
546                 req = malloc(length);
547                 memcpy(req, data, length);
548                 client->CurrentRequest = req;
549                 SDL_CondSignal(client->WaitFlag);
550                 #endif
551         }
552         return -1;
553 }

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