|
|
1.1 root 1: /* services.c */
2:
3: /* Synchronet Services */
4:
5: /* $Id: services.c,v 1.175 2004/12/12 22:46:41 rswindell Exp $ */
6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
11: * Copyright 2004 Rob Swindell - http://www.synchro.net/copyright.html *
12: * *
13: * This program is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU General Public License for more details: gpl.txt or *
18: * http://www.fsf.org/copyleft/gpl.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38: /* Platform-specific headers */
39: #ifdef _WIN32
40:
41: #include <io.h> /* open/close */
42: #include <share.h> /* share open flags */
43: #include <process.h> /* _beginthread */
44: #include <windows.h> /* required for mmsystem.h */
45: #include <mmsystem.h> /* SND_ASYNC */
46:
47: #endif
48:
49: #ifdef __unix__
50: #include <sys/param.h> /* BSD? */
51: #endif
52:
53: /* ANSI C Library headers */
54: #include <stdio.h>
55: #include <stdlib.h> /* ltoa in GNU C lib */
56: #include <stdarg.h> /* va_list */
57: #include <string.h> /* strrchr */
58: #include <ctype.h> /* isdigit */
59: #include <fcntl.h> /* Open flags */
60: #include <errno.h> /* errno */
61:
62: /* Synchronet-specific headers */
63: #ifndef JAVASCRIPT
64: #define JAVASCRIPT /* required to include JS API headers */
65: #endif
66: #define SERVICES_INI_BITDESC_TABLE /* required to defined service_options */
67: #undef SBBS /* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
68: #include "sbbs.h"
69: #include "services.h"
70: #include "ident.h" /* identify() */
71: #include "sbbs_ini.h"
72:
73: /* Constants */
74:
75: #define MAX_SERVICES 128
76: #define TIMEOUT_THREAD_WAIT 60 /* Seconds */
77: #define MAX_UDP_BUF_LEN 8192 /* 8K */
78: #define DEFAULT_LISTEN_BACKLOG 5
79:
80: static services_startup_t* startup=NULL;
81: static scfg_t scfg;
82: static DWORD sockets=0;
83: static BOOL terminated=FALSE;
84: static time_t uptime=0;
85: static DWORD served=0;
86: static char revision[16];
87: static link_list_t recycle_semfiles;
88: static link_list_t shutdown_semfiles;
89:
90: typedef struct {
91: /* These are sysop-configurable */
92: DWORD interface_addr;
93: WORD port;
94: char protocol[34];
95: char cmd[128];
96: DWORD max_clients;
97: DWORD options;
98: int listen_backlog;
99: DWORD stack_size;
100: DWORD js_max_bytes;
101: DWORD js_cx_stack;
102: DWORD js_branch_limit;
103: DWORD js_yield_interval;
104: DWORD js_gc_interval;
105: js_server_props_t js_server_props;
106: /* These are run-time state and stat vars */
107: DWORD clients;
108: DWORD served;
109: SOCKET socket;
110: BOOL running;
111: BOOL terminated;
112: } service_t;
113:
114: typedef struct {
115: SOCKET socket;
116: SOCKADDR_IN addr;
117: time_t logintime;
118: user_t user;
119: client_t* client;
120: service_t* service;
121: js_branch_t branch;
122: /* Initial UDP datagram */
123: BYTE* udp_buf;
124: int udp_len;
125: } service_client_t;
126:
127: static service_t *service=NULL;
128: static DWORD services=0;
129:
130: static int lprintf(int level, char *fmt, ...)
131: {
132: va_list argptr;
133: char sbuf[1024];
134:
135: if(startup==NULL || startup->lputs==NULL)
136: return(0);
137:
138: #if defined(_WIN32)
139: if(IsBadCodePtr((FARPROC)startup->lputs))
140: return(0);
141: #endif
142:
143: va_start(argptr,fmt);
144: vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
145: sbuf[sizeof(sbuf)-1]=0;
146: va_end(argptr);
147: return(startup->lputs(startup->cbdata,level,sbuf));
148: }
149:
150: #ifdef _WINSOCKAPI_
151:
152: static WSADATA WSAData;
153: #define SOCKLIB_DESC WSAData.szDescription
154: static BOOL WSAInitialized=FALSE;
155:
156: static BOOL winsock_startup(void)
157: {
158: int status; /* Status Code */
159:
160: if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
161: lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
162: WSAInitialized=TRUE;
163: return (TRUE);
164: }
165:
166: lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
167: return (FALSE);
168: }
169:
170: #else /* No WINSOCK */
171:
172: #define winsock_startup() (TRUE)
173: #define SOCKLIB_DESC NULL
174:
175: #endif
176:
177: static ulong active_clients(void)
178: {
179: ulong i;
180: ulong total_clients=0;
181:
182: for(i=0;i<services;i++)
183: total_clients+=service[i].clients;
184:
185: return(total_clients);
186: }
187:
188: static void update_clients(void)
189: {
190: if(startup!=NULL && startup->clients!=NULL)
191: startup->clients(startup->cbdata,active_clients());
192: }
193:
194: static void client_on(SOCKET sock, client_t* client, BOOL update)
195: {
196: if(startup!=NULL && startup->client_on!=NULL)
197: startup->client_on(startup->cbdata,TRUE,sock,client,update);
198: }
199:
200: static void client_off(SOCKET sock)
201: {
202: if(startup!=NULL && startup->client_on!=NULL)
203: startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
204: }
205:
206: static void thread_up(BOOL setuid)
207: {
208: if(startup!=NULL && startup->thread_up!=NULL)
209: startup->thread_up(startup->cbdata,TRUE,setuid);
210: }
211:
212: static void thread_down(void)
213: {
214: if(startup!=NULL && startup->thread_up!=NULL)
215: startup->thread_up(startup->cbdata,FALSE,FALSE);
216: }
217:
218: static SOCKET open_socket(int type)
219: {
220: char error[256];
221: SOCKET sock;
222:
223: sock=socket(AF_INET, type, IPPROTO_IP);
224: if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
225: startup->socket_open(startup->cbdata,TRUE);
226: if(sock!=INVALID_SOCKET) {
227: sockets++;
228: if(set_socket_options(&scfg, sock, error))
229: lprintf(LOG_ERR,"%04d !ERROR %s",sock, error);
230:
231: #if 0 /*def _DEBUG */
232: lprintf(LOG_DEBUG,"%04d Socket opened (%d sockets in use)",sock,sockets);
233: #endif
234: }
235: return(sock);
236: }
237:
238: static int close_socket(SOCKET sock)
239: {
240: int result;
241:
242: if(sock==INVALID_SOCKET)
243: return(-1);
244:
245: shutdown(sock,SHUT_RDWR); /* required on Unix */
246: result=closesocket(sock);
247: if(startup!=NULL && startup->socket_open!=NULL)
248: startup->socket_open(startup->cbdata,FALSE);
249: sockets--;
250: if(result!=0)
251: lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",sock, ERROR_VALUE);
252: #if 0 /*def _DEBUG */
253: else
254: lprintf(LOG_DEBUG,"%04d Socket closed (%d sockets in use)",sock,sockets);
255: #endif
256:
257: return(result);
258: }
259:
260: static void status(char* str)
261: {
262: if(startup!=NULL && startup->status!=NULL)
263: startup->status(startup->cbdata,str);
264: }
265:
266: static time_t checktime(void)
267: {
268: struct tm tm;
269:
270: memset(&tm,0,sizeof(tm));
271: tm.tm_year=94;
272: tm.tm_mday=1;
273: return(mktime(&tm)-0x2D24BD00L);
274: }
275:
276: /* Global JavaScript Methods */
277:
278: static JSBool
279: js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
280: {
281: char* buf;
282: int32 len=512;
283: service_client_t* client;
284:
285: if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
286: return(JS_FALSE);
287:
288: *rval = JSVAL_VOID;
289:
290: if(argc)
291: JS_ValueToInt32(cx,argv[0],&len);
292:
293: if((buf=malloc(len))==NULL)
294: return(JS_TRUE);
295:
296: len=recv(client->socket,buf,len,0);
297:
298: if(len>0)
299: *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx,buf,len));
300:
301: free(buf);
302:
303: return(JS_TRUE);
304: }
305:
306: static JSBool
307: js_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
308: {
309: char* buf;
310: int32 len=512;
311: service_client_t* client;
312:
313: if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
314: return(JS_FALSE);
315:
316: *rval = JSVAL_VOID;
317:
318: if(argc)
319: JS_ValueToInt32(cx,argv[0],&len);
320:
321: if((buf=malloc(len))==NULL)
322: return(JS_TRUE);
323:
324: len=recv(client->socket,buf,len,0); /* Need to switch to sockreadline */
325:
326: if(len>0)
327: *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx,buf,len));
328:
329: free(buf);
330:
331: return(JS_TRUE);
332: }
333:
334: static JSBool
335: js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
336: {
337: uintN i;
338: char* cp;
339: JSString* str;
340: service_client_t* client;
341:
342: if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
343: return(JS_FALSE);
344:
345: *rval = argv[0];
346:
347: for(i=0; i<argc; i++) {
348: if((str=JS_ValueToString(cx, argv[i]))==NULL)
349: continue;
350: if((cp=JS_GetStringBytes(str))==NULL)
351: continue;
352: sendsocket(client->socket,cp,strlen(cp));
353: }
354:
355: return(JS_TRUE);
356: }
357:
358: static JSBool
359: js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
360: {
361: char* cp;
362: service_client_t* client;
363:
364: if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
365: return(JS_FALSE);
366:
367: js_write(cx,obj,argc,argv,rval);
368:
369: cp="\r\n";
370: sendsocket(client->socket,cp,2);
371:
372: return(JS_TRUE);
373: }
374:
375: static JSBool
376: js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
377: {
378: char str[512];
379: uintN i=0;
380: int32 level=LOG_INFO;
381: JSString* js_str;
382: service_client_t* client;
383:
384: if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
385: return(JS_FALSE);
386:
387: if(startup==NULL || startup->lputs==NULL)
388: return(JS_FALSE);
389:
390: if(JSVAL_IS_NUMBER(argv[i]))
391: JS_ValueToInt32(cx,argv[i++],&level);
392:
393: str[0]=0;
394: for(;i<argc && strlen(str)<(sizeof(str)/2);i++) {
395: if((js_str=JS_ValueToString(cx, argv[i]))==NULL)
396: return(JS_FALSE);
397: strncat(str,JS_GetStringBytes(js_str),sizeof(str)/2);
398: strcat(str," ");
399: }
400:
401: if(service==NULL)
402: lprintf(level,"%04d %s",client->socket,str);
403: else
404: lprintf(level,"%04d %s %s",client->socket,client->service->protocol,str);
405:
406: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str));
407:
408: return(JS_TRUE);
409: }
410:
411: static JSBool
412: js_login(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
413: {
414: char* p;
415: JSBool inc_logons=JS_FALSE;
416: user_t user;
417: jsval val;
418: JSString* js_str;
419: service_client_t* client;
420:
421: *rval = BOOLEAN_TO_JSVAL(JS_FALSE);
422:
423: if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
424: return(JS_FALSE);
425:
426: /* User name */
427: if((js_str=JS_ValueToString(cx, argv[0]))==NULL)
428: return(JS_FALSE);
429:
430: if((p=JS_GetStringBytes(js_str))==NULL)
431: return(JS_FALSE);
432:
433: memset(&user,0,sizeof(user));
434:
435: if(isdigit(*p))
436: user.number=atoi(p);
437: else if(*p)
438: user.number=matchuser(&scfg,p,FALSE);
439:
440: if(getuserdat(&scfg,&user)!=0) {
441: lprintf(LOG_NOTICE,"%04d %s !USER NOT FOUND: '%s'"
442: ,client->socket,client->service->protocol,p);
443: return(JS_TRUE);
444: }
445:
446: if(user.misc&(DELETED|INACTIVE)) {
447: lprintf(LOG_WARNING,"%04d %s !DELETED OR INACTIVE USER #%d: %s"
448: ,client->socket,client->service->protocol,user.number,p);
449: return(JS_TRUE);
450: }
451:
452: /* Password */
453: if(user.pass[0]) {
454: if((js_str=JS_ValueToString(cx, argv[1]))==NULL)
455: return(JS_FALSE);
456:
457: if((p=JS_GetStringBytes(js_str))==NULL)
458: return(JS_FALSE);
459:
460: if(stricmp(user.pass,p)) { /* Wrong password */
461: lprintf(LOG_WARNING,"%04d %s !INVALID PASSWORD ATTEMPT FOR USER: %s"
462: ,client->socket,client->service->protocol,user.alias);
463: return(JS_TRUE);
464: }
465: }
466:
467: if(argc>2)
468: JS_ValueToBoolean(cx,argv[2],&inc_logons);
469:
470: if(client->client!=NULL) {
471: SAFECOPY(user.note,client->client->addr);
472: SAFECOPY(user.comp,client->client->host);
473: SAFECOPY(user.modem,client->service->protocol);
474: }
475:
476: if(inc_logons) {
477: user.logons++;
478: user.ltoday++;
479: }
480:
481: putuserdat(&scfg,&user);
482:
483: /* user-specific objects */
484: if(!js_CreateUserObjects(cx, obj, &scfg, &user, NULL, NULL))
485: lprintf(LOG_ERR,"%04d %s !JavaScript ERROR creating user objects"
486: ,client->socket,client->service->protocol);
487:
488: memcpy(&client->user,&user,sizeof(user));
489:
490: if(client->client!=NULL) {
491: client->client->user=client->user.alias;
492: client_on(client->socket,client->client,TRUE /* update */);
493: }
494:
495: client->logintime=time(NULL);
496:
497: lprintf(LOG_INFO,"%04d %s Logging in %s"
498: ,client->socket,client->service->protocol,client->user.alias);
499:
500: val = BOOLEAN_TO_JSVAL(JS_TRUE);
501: JS_SetProperty(cx, obj, "logged_in", &val);
502:
503: *rval=BOOLEAN_TO_JSVAL(JS_TRUE);
504:
505: return(JS_TRUE);
506: }
507:
508: static JSBool
509: js_logout(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
510: {
511: jsval val;
512: service_client_t* client;
513:
514: *rval = BOOLEAN_TO_JSVAL(JS_FALSE);
515:
516: if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
517: return(JS_FALSE);
518:
519: if(client->user.number<1) /* Not logged in */
520: return(JS_TRUE);
521:
522: logoutuserdat(&scfg,&client->user,time(NULL),client->logintime);
523:
524: lprintf(LOG_INFO,"%04d %s Logging out %s"
525: ,client->socket,client->service->protocol,client->user.alias);
526:
527: memset(&client->user,0,sizeof(client->user));
528:
529: val = BOOLEAN_TO_JSVAL(JS_FALSE);
530: JS_SetProperty(cx, obj, "logged_in", &val);
531:
532: *rval=BOOLEAN_TO_JSVAL(JS_TRUE);
533:
534: return(JS_TRUE);
535: }
536:
537: static JSFunctionSpec js_global_functions[] = {
538: {"read", js_read, 0}, /* read from client socket */
539: {"readln", js_readln, 0}, /* read line from client socket */
540: {"write", js_write, 0}, /* write to client socket */
541: {"writeln", js_writeln, 0}, /* write line to client socket */
542: {"print", js_writeln, 0}, /* write line to client socket */
543: {"log", js_log, 0}, /* Log a string */
544: {"login", js_login, 2}, /* Login specified username and password */
545: {"logout", js_logout, 0}, /* Logout user */
546: {0}
547: };
548:
549: static void
550: js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
551: {
552: char line[64];
553: char file[MAX_PATH+1];
554: char* prot="???";
555: SOCKET sock=0;
556: char* warning;
557: service_client_t* client;
558:
559: if((client=(service_client_t*)JS_GetContextPrivate(cx))!=NULL) {
560: prot=client->service->protocol;
561: sock=client->socket;
562: }
563:
564: if(report==NULL) {
565: lprintf(LOG_ERR,"%04d %s !JavaScript: %s", sock,prot,message);
566: return;
567: }
568:
569: if(report->filename)
570: sprintf(file," %s",report->filename);
571: else
572: file[0]=0;
573:
574: if(report->lineno)
575: sprintf(line," line %d",report->lineno);
576: else
577: line[0]=0;
578:
579: if(JSREPORT_IS_WARNING(report->flags)) {
580: if(JSREPORT_IS_STRICT(report->flags))
581: warning="strict warning";
582: else
583: warning="warning";
584: } else
585: warning="";
586:
587: lprintf(LOG_ERR,"%04d %s !JavaScript %s%s%s: %s",sock,prot,warning,file,line,message);
588: }
589:
590: #if 0
591:
592: /* Server Object Properites */
593: enum {
594: SERVER_PROP_TERMINATED
595: ,SERVER_PROP_CLIENTS
596: };
597:
598:
599: static JSBool js_server_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
600: {
601: jsint tiny;
602: service_client_t* client;
603:
604: if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
605: return(JS_FALSE);
606:
607: tiny = JSVAL_TO_INT(id);
608:
609: switch(tiny) {
610: case SERVER_PROP_TERMINATED:
611: #if 0
612: lprintf(LOG_DEBUG,"%s client->service->terminated=%d"
613: ,client->service->protocol, client->service->terminated);
614: #endif
615: *vp = BOOLEAN_TO_JSVAL(client->service->terminated);
616: break;
617: case SERVER_PROP_CLIENTS:
618: *vp = INT_TO_JSVAL(active_clients());
619: break;
620: }
621:
622: return(JS_TRUE);
623: }
624:
625: #define SERVER_PROP_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY
626:
627: static struct JSPropertySpec js_server_properties[] = {
628: /* name ,tinyid ,flags, getter, setter */
629:
630: { "terminated" ,SERVER_PROP_TERMINATED ,SERVER_PROP_FLAGS, NULL,NULL},
631: { "clients" ,SERVER_PROP_CLIENTS ,SERVER_PROP_FLAGS, NULL,NULL},
632: {0}
633: };
634:
635: static JSClass js_server_class = {
636: "Server" /* name */
637: ,0 /* flags */
638: ,JS_PropertyStub /* addProperty */
639: ,JS_PropertyStub /* delProperty */
640: ,js_server_get /* getProperty */
641: ,JS_PropertyStub /* setProperty */
642: ,JS_EnumerateStub /* enumerate */
643: ,JS_ResolveStub /* resolve */
644: ,JS_ConvertStub /* convert */
645: ,JS_FinalizeStub /* finalize */
646: };
647:
648: #endif
649:
650: /* Server Methods */
651:
652: static JSBool
653: js_client_add(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
654: {
655: client_t client;
656: SOCKET sock=INVALID_SOCKET;
657: socklen_t addr_len;
658: SOCKADDR_IN addr;
659: service_client_t* service_client;
660:
661: if((service_client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
662: return(JS_FALSE);
663:
664: service_client->service->clients++;
665: update_clients();
666: service_client->service->served++;
667: served++;
668: memset(&client,0,sizeof(client));
669: client.size=sizeof(client);
670: client.protocol=service_client->service->protocol;
671: client.time=time(NULL);
672: client.user="<unknown>";
673: SAFECOPY(client.host,client.user);
674:
675: sock=js_socket(cx,argv[0]);
676:
677: addr_len = sizeof(addr);
678: if(getpeername(sock, (struct sockaddr *)&addr, &addr_len)==0) {
679: SAFECOPY(client.addr,inet_ntoa(addr.sin_addr));
680: client.port=ntohs(addr.sin_port);
681: }
682:
683: if(argc>1)
684: client.user=JS_GetStringBytes(JS_ValueToString(cx,argv[1]));
685:
686: if(argc>2)
687: SAFECOPY(client.host,JS_GetStringBytes(JS_ValueToString(cx,argv[2])));
688:
689: client_on(sock, &client, /* update? */ FALSE);
690: #ifdef _DEBUG
691: lprintf(LOG_DEBUG,"%04d %s client_add(%04u,%s,%s)"
692: ,service_client->service->socket,service_client->service->protocol
693: ,sock,client.user,client.host);
694: #endif
695: return(JS_TRUE);
696: }
697:
698: static JSBool
699: js_client_update(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
700: {
701: client_t client;
702: SOCKET sock=INVALID_SOCKET;
703: socklen_t addr_len;
704: SOCKADDR_IN addr;
705: service_client_t* service_client;
706:
707: if((service_client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
708: return(JS_FALSE);
709:
710: memset(&client,0,sizeof(client));
711: client.size=sizeof(client);
712: client.protocol=service_client->service->protocol;
713: client.user="<unknown>";
714: SAFECOPY(client.host,client.user);
715:
716: sock=js_socket(cx,argv[0]);
717:
718: addr_len = sizeof(addr);
719: if(getpeername(sock, (struct sockaddr *)&addr, &addr_len)==0) {
720: SAFECOPY(client.addr,inet_ntoa(addr.sin_addr));
721: client.port=ntohs(addr.sin_port);
722: }
723:
724: if(argc>1)
725: client.user=JS_GetStringBytes(JS_ValueToString(cx,argv[1]));
726:
727: if(argc>2)
728: SAFECOPY(client.host,JS_GetStringBytes(JS_ValueToString(cx,argv[2])));
729:
730: client_on(sock, &client, /* update? */ TRUE);
731: #ifdef _DEBUG
732: lprintf(LOG_DEBUG,"%04d %s client_update(%04u,%s,%s)"
733: ,service_client->service->socket,service_client->service->protocol
734: ,sock,client.user,client.host);
735: #endif
736: return(JS_TRUE);
737: }
738:
739:
740: static JSBool
741: js_client_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
742: {
743: SOCKET sock=INVALID_SOCKET;
744: service_client_t* service_client;
745:
746: if((service_client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
747: return(JS_FALSE);
748:
749: sock=js_socket(cx,argv[0]);
750:
751: if(sock!=INVALID_SOCKET) {
752:
753: client_off(sock);
754:
755: if(service_client->service->clients==0)
756: lprintf(LOG_WARNING,"%04d %s !client_remove() called with 0 service clients"
757: ,service_client->service->socket, service_client->service->protocol);
758: else {
759: service_client->service->clients--;
760: update_clients();
761: }
762: }
763:
764: #ifdef _DEBUG
765: lprintf(LOG_DEBUG,"%04d %s client_remove(%04u)"
766: ,service_client->service->socket, service_client->service->protocol, sock);
767: #endif
768: return(JS_TRUE);
769: }
770:
771: static JSContext*
772: js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client, JSObject** glob)
773: {
774: JSContext* js_cx;
775: JSObject* js_glob;
776: JSObject* server;
777: BOOL success=FALSE;
778:
779: if((js_cx = JS_NewContext(js_runtime, service_client->service->js_cx_stack))==NULL)
780: return(NULL);
781:
782: JS_SetErrorReporter(js_cx, js_ErrorReporter);
783:
784: do {
785:
786: JS_SetContextPrivate(js_cx, service_client);
787:
788: if((js_glob=js_CreateGlobalObject(js_cx, &scfg, NULL))==NULL)
789: break;
790:
791: if (!JS_DefineFunctions(js_cx, js_glob, js_global_functions))
792: break;
793:
794: /* Internal JS Object */
795: if(js_CreateInternalJsObject(js_cx, js_glob, &service_client->branch)==NULL)
796: break;
797:
798: /* Client Object */
799: if(service_client->client!=NULL)
800: if(js_CreateClientObject(js_cx, js_glob, "client", service_client->client, sock)==NULL)
801: break;
802:
803: /* User Class */
804: if(js_CreateUserClass(js_cx, js_glob, &scfg)==NULL)
805: break;
806:
807: /* Socket Class */
808: if(js_CreateSocketClass(js_cx, js_glob)==NULL)
809: break;
810:
811: /* MsgBase Class */
812: if(js_CreateMsgBaseClass(js_cx, js_glob, &scfg)==NULL)
813: break;
814:
815: /* File Class */
816: if(js_CreateFileClass(js_cx, js_glob)==NULL)
817: break;
818:
819: /* user-specific objects */
820: if(!js_CreateUserObjects(js_cx, js_glob, &scfg, NULL, NULL, NULL))
821: break;
822:
823: if(js_CreateSystemObject(js_cx, js_glob, &scfg, uptime, startup->host_name, SOCKLIB_DESC)==NULL)
824: break;
825: #if 0
826: char ver[256];
827: JSString* js_str;
828: jsval val;
829:
830: /* server object */
831: if((server=JS_DefineObject(js_cx, js_glob, "server", &js_server_class
832: ,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))==NULL)
833: break;
834:
835: if(!JS_DefineProperties(js_cx, server, js_server_properties))
836: break;
837:
838: sprintf(ver,"Synchronet Services %s",revision);
839: if((js_str=JS_NewStringCopyZ(js_cx, ver))==NULL)
840: break;
841: val = STRING_TO_JSVAL(js_str);
842: if(!JS_SetProperty(js_cx, server, "version", &val))
843: break;
844:
845: if((js_str=JS_NewStringCopyZ(js_cx, services_ver()))==NULL)
846: break;
847: val = STRING_TO_JSVAL(js_str);
848: if(!JS_SetProperty(js_cx, server, "version_detail", &val))
849: break;
850:
851: #else
852:
853: if(service_client->service->js_server_props.version[0]==0) {
854: sprintf(service_client->service->js_server_props.version
855: ,"Synchronet Services %s",revision);
856: service_client->service->js_server_props.version_detail=
857: services_ver();
858: service_client->service->js_server_props.clients=
859: &service_client->service->clients;
860: service_client->service->js_server_props.interface_addr=
861: &service_client->service->interface_addr;
862: service_client->service->js_server_props.options=
863: &service_client->service->options;
864: }
865:
866: if((server=js_CreateServerObject(js_cx,js_glob
867: ,&service_client->service->js_server_props))==NULL)
868: break;
869: #endif
870:
871: if(service_client->client==NULL) /* static service */
872: if(js_CreateSocketObject(js_cx, server, "socket", service_client->socket)==NULL)
873: break;
874:
875: JS_DefineFunction(js_cx, server, "client_add" , js_client_add, 1, 0);
876: JS_DefineFunction(js_cx, server, "client_update", js_client_update, 1, 0);
877: JS_DefineFunction(js_cx, server, "client_remove", js_client_remove, 1, 0);
878:
879:
880: if(glob!=NULL)
881: *glob=js_glob;
882:
883: success=TRUE;
884:
885: } while(0);
886:
887:
888: if(!success) {
889: JS_DestroyContext(js_cx);
890: return(NULL);
891: }
892:
893: return(js_cx);
894: }
895:
896: static JSBool
897: js_BranchCallback(JSContext *cx, JSScript *script)
898: {
899: service_client_t* client;
900:
901: if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
902: return(JS_FALSE);
903:
904: /* Terminated? */
905: if(terminated) {
906: JS_ReportError(cx,"Terminated");
907: client->branch.counter=0;
908: return(JS_FALSE);
909: }
910:
911: return js_CommonBranchCallback(cx,&client->branch);
912: }
913:
914: static void js_init_args(JSContext* js_cx, JSObject* js_obj, const char* cmdline)
915: {
916: char argbuf[MAX_PATH+1];
917: char* p;
918: char* args;
919: int argc=0;
920: JSString* arg_str;
921: JSObject* argv;
922: jsval val;
923:
924: argv=JS_NewArrayObject(js_cx, 0, NULL);
925: JS_DefineProperty(js_cx, js_obj, "argv", OBJECT_TO_JSVAL(argv)
926: ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
927:
928: p=(char*)cmdline;
929: while(*p && *p>' ') p++; /* find end of filename */
930: while(*p && *p<=' ') p++; /* find first arg */
931: SAFECOPY(argbuf,p);
932:
933: args=argbuf;
934: while(*args && argv!=NULL) {
935: p=strchr(args,' ');
936: if(p!=NULL)
937: *p=0;
938: while(*args && *args<=' ') args++; /* Skip spaces */
939: arg_str = JS_NewStringCopyZ(js_cx, args);
940: if(arg_str==NULL)
941: break;
942: val=STRING_TO_JSVAL(arg_str);
943: if(!JS_SetElement(js_cx, argv, argc, &val))
944: break;
945: argc++;
946: if(p==NULL) /* last arg */
947: break;
948: args+=(strlen(args)+1);
949: }
950: JS_DefineProperty(js_cx, js_obj, "argc", INT_TO_JSVAL(argc)
951: ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
952: }
953:
954: static void js_service_thread(void* arg)
955: {
956: int i;
957: char* host_name;
958: HOSTENT* host;
959: SOCKET socket;
960: client_t client;
961: service_t* service;
962: service_client_t service_client;
963: /* JavaScript-specific */
964: char spath[MAX_PATH+1];
965: char fname[MAX_PATH+1];
966: JSString* datagram;
967: JSObject* js_glob;
968: JSScript* js_script;
969: JSRuntime* js_runtime;
970: JSContext* js_cx;
971: jsval val;
972: jsval rval;
973:
974: // Copy service_client arg
975: service_client=*(service_client_t*)arg;
976: // Free original
977: free(arg);
978:
979: socket=service_client.socket;
980: service=service_client.service;
981:
982: lprintf(LOG_DEBUG,"%04d %s JavaScript service thread started", socket, service->protocol);
983:
984: thread_up(TRUE /* setuid */);
985:
986: /* Host name lookup and filtering */
987: if(service->options&BBS_OPT_NO_HOST_LOOKUP
988: || startup->options&BBS_OPT_NO_HOST_LOOKUP)
989: host=NULL;
990: else
991: host=gethostbyaddr((char *)&service_client.addr.sin_addr
992: ,sizeof(service_client.addr.sin_addr),AF_INET);
993:
994: if(host!=NULL && host->h_name!=NULL)
995: host_name=host->h_name;
996: else
997: host_name="<no name>";
998:
999: if(!(service->options&BBS_OPT_NO_HOST_LOOKUP)
1000: && !(startup->options&BBS_OPT_NO_HOST_LOOKUP)) {
1001: lprintf(LOG_INFO,"%04d %s Hostname: %s"
1002: ,socket, service->protocol, host_name);
1003: for(i=0;host!=NULL && host->h_aliases!=NULL
1004: && host->h_aliases[i]!=NULL;i++)
1005: lprintf(LOG_INFO,"%04d %s HostAlias: %s"
1006: ,socket, service->protocol, host->h_aliases[i]);
1007: }
1008:
1009: if(trashcan(&scfg,host_name,"host")) {
1010: lprintf(LOG_NOTICE,"%04d !%s CLIENT BLOCKED in host.can: %s"
1011: ,socket, service->protocol, host_name);
1012: close_socket(socket);
1013: if(service->clients)
1014: service->clients--;
1015: thread_down();
1016: return;
1017: }
1018:
1019:
1020: #if 0 /* Need to export from SBBS.DLL */
1021: identity=NULL;
1022: if(service->options&BBS_OPT_GET_IDENT
1023: && startup->options&BBS_OPT_GET_IDENT) {
1024: identify(&service_client.addr, service->port, str, sizeof(str)-1);
1025: identity=strrchr(str,':');
1026: if(identity!=NULL) {
1027: identity++; /* skip colon */
1028: while(*identity && *identity<=SP) /* point to user name */
1029: identity++;
1030: lprintf(LOG_INFO,"%04d Identity: %s",socket, identity);
1031: }
1032: }
1033: #endif
1034:
1035: client.size=sizeof(client);
1036: client.time=time(NULL);
1037: SAFECOPY(client.addr,inet_ntoa(service_client.addr.sin_addr));
1038: SAFECOPY(client.host,host_name);
1039: client.port=ntohs(service_client.addr.sin_port);
1040: client.protocol=service->protocol;
1041: client.user="<unknown>";
1042: service_client.client=&client;
1043:
1044: /* Initialize client display */
1045: client_on(socket,&client,FALSE /* update */);
1046:
1047: if((js_runtime=JS_NewRuntime(service->js_max_bytes))==NULL
1048: || (js_cx=js_initcx(js_runtime,socket,&service_client,&js_glob))==NULL) {
1049: lprintf(LOG_ERR,"%04d !%s ERROR initializing JavaScript context"
1050: ,socket,service->protocol);
1051: client_off(socket);
1052: close_socket(socket);
1053: if(service->clients)
1054: service->clients--;
1055: thread_down();
1056: return;
1057: }
1058:
1059: update_clients();
1060:
1061: /* RUN SCRIPT */
1062: SAFECOPY(fname,service->cmd);
1063: truncstr(fname," ");
1064: sprintf(spath,"%s%s",scfg.mods_dir,fname);
1065: if(scfg.mods_dir[0]==0 || !fexist(spath))
1066: sprintf(spath,"%s%s",scfg.exec_dir,fname);
1067:
1068: js_init_args(js_cx, js_glob, service->cmd);
1069:
1070: val = BOOLEAN_TO_JSVAL(JS_FALSE);
1071: JS_SetProperty(js_cx, js_glob, "logged_in", &val);
1072:
1073: if(service->options&SERVICE_OPT_UDP
1074: && service_client.udp_buf != NULL
1075: && service_client.udp_len > 0) {
1076: datagram = JS_NewStringCopyN(js_cx, service_client.udp_buf, service_client.udp_len);
1077: if(datagram==NULL)
1078: val=JSVAL_VOID;
1079: else
1080: val = STRING_TO_JSVAL(datagram);
1081: } else
1082: val = JSVAL_VOID;
1083: JS_SetProperty(js_cx, js_glob, "datagram", &val);
1084: FREE_AND_NULL(service_client.udp_buf);
1085:
1086: JS_ClearPendingException(js_cx);
1087:
1088: js_script=JS_CompileFile(js_cx, js_glob, spath);
1089:
1090: if(js_script==NULL)
1091: lprintf(LOG_ERR,"%04d !JavaScript FAILED to compile script (%s)",socket,spath);
1092: else {
1093: JS_SetBranchCallback(js_cx, js_BranchCallback);
1094: JS_ExecuteScript(js_cx, js_glob, js_script, &rval);
1095: JS_DestroyScript(js_cx, js_script);
1096: }
1097: JS_DestroyContext(js_cx); /* Free Context */
1098:
1099: JS_DestroyRuntime(js_runtime);
1100:
1101: if(service_client.user.number) {
1102: lprintf(LOG_INFO,"%04d %s Logging out %s"
1103: ,socket, service->protocol, service_client.user.alias);
1104: logoutuserdat(&scfg,&service_client.user,time(NULL),service_client.logintime);
1105: }
1106:
1107: if(service->clients)
1108: service->clients--;
1109: update_clients();
1110:
1111: #ifdef _WIN32
1112: if(startup->hangup_sound[0] && !(startup->options&BBS_OPT_MUTE)
1113: && !(service->options&BBS_OPT_MUTE))
1114: PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
1115: #endif
1116:
1117: thread_down();
1118: lprintf(LOG_DEBUG,"%04d %s JavaScript service thread terminated (%u clients remain, %d total, %lu served)"
1119: , socket, service->protocol, service->clients, active_clients(), service->served);
1120:
1121: client_off(socket);
1122: close_socket(socket);
1123: }
1124:
1125: static void js_static_service_thread(void* arg)
1126: {
1127: char spath[MAX_PATH+1];
1128: char fname[MAX_PATH+1];
1129: service_t* service;
1130: service_client_t service_client;
1131: SOCKET socket;
1132: /* JavaScript-specific */
1133: JSObject* js_glob;
1134: JSScript* js_script;
1135: JSRuntime* js_runtime;
1136: JSContext* js_cx;
1137: jsval val;
1138: jsval rval;
1139:
1140: // Copy service_client arg
1141: service=(service_t*)arg;
1142:
1143: service->running=TRUE;
1144: socket = service->socket;
1145:
1146: lprintf(LOG_DEBUG,"%04d %s static JavaScript service thread started", service->socket, service->protocol);
1147:
1148: thread_up(TRUE /* setuid */);
1149:
1150: memset(&service_client,0,sizeof(service_client));
1151: service_client.socket = service->socket;
1152: service_client.service = service;
1153: service_client.branch.limit = service->js_branch_limit;
1154: service_client.branch.gc_interval = service->js_gc_interval;
1155: service_client.branch.yield_interval = service->js_yield_interval;
1156: service_client.branch.terminated = &service->terminated;
1157: service_client.branch.auto_terminate = TRUE;
1158:
1159: if((js_runtime=JS_NewRuntime(service->js_max_bytes))==NULL) {
1160: lprintf(LOG_ERR,"%04d !%s ERROR initializing JavaScript runtime"
1161: ,service->socket,service->protocol);
1162: close_socket(service->socket);
1163: service->socket=INVALID_SOCKET;
1164: thread_down();
1165: return;
1166: }
1167:
1168: SAFECOPY(fname,service->cmd);
1169: truncstr(fname," ");
1170: sprintf(spath,"%s%s",scfg.mods_dir,fname);
1171: if(scfg.mods_dir[0]==0 || !fexist(spath))
1172: sprintf(spath,"%s%s",scfg.exec_dir,fname);
1173:
1174: do {
1175: if((js_cx=js_initcx(js_runtime,service->socket,&service_client,&js_glob))==NULL) {
1176: lprintf(LOG_ERR,"%04d !%s ERROR initializing JavaScript context"
1177: ,service->socket,service->protocol);
1178: break;
1179: }
1180:
1181: js_init_args(js_cx, js_glob, service->cmd);
1182:
1183: val = BOOLEAN_TO_JSVAL(JS_FALSE);
1184: JS_SetProperty(js_cx, js_glob, "logged_in", &val);
1185:
1186: JS_SetBranchCallback(js_cx, js_BranchCallback);
1187:
1188: if((js_script=JS_CompileFile(js_cx, js_glob, spath))==NULL) {
1189: lprintf(LOG_ERR,"%04d !JavaScript FAILED to compile script (%s)",service->socket,spath);
1190: break;
1191: }
1192:
1193: JS_ExecuteScript(js_cx, js_glob, js_script, &rval);
1194: JS_DestroyScript(js_cx, js_script);
1195:
1196: JS_DestroyContext(js_cx); /* Free Context */
1197: js_cx=NULL;
1198: } while(!service->terminated && service->options&SERVICE_OPT_STATIC_LOOP);
1199:
1200: if(js_cx!=NULL)
1201: JS_DestroyContext(js_cx); /* Free Context */
1202:
1203: JS_DestroyRuntime(js_runtime);
1204:
1205: if(service->clients) {
1206: lprintf(LOG_WARNING,"%04d %s !service terminating with %u active clients"
1207: ,socket, service->protocol, service->clients);
1208: service->clients=0;
1209: }
1210:
1211: thread_down();
1212: lprintf(LOG_DEBUG,"%04d %s static JavaScript service thread terminated (%lu clients served)"
1213: ,socket, service->protocol, service->served);
1214:
1215: close_socket(service->socket);
1216: service->socket=INVALID_SOCKET;
1217:
1218: service->running=FALSE;
1219: }
1220:
1221: static void native_static_service_thread(void* arg)
1222: {
1223: char cmd[MAX_PATH];
1224: char fullcmd[MAX_PATH*2];
1225: SOCKET socket;
1226: SOCKET socket_dup;
1227: service_t* service;
1228:
1229: service = (service_t*)arg;
1230:
1231: service->running=TRUE;
1232: socket = service->socket;
1233:
1234: lprintf(LOG_DEBUG,"%04d %s static service thread started", socket, service->protocol);
1235:
1236: thread_up(TRUE /* setuid */);
1237:
1238: #ifdef _WIN32
1239: if(!DuplicateHandle(GetCurrentProcess(),
1240: (HANDLE)socket,
1241: GetCurrentProcess(),
1242: (HANDLE*)&socket_dup,
1243: 0,
1244: TRUE, // Inheritable
1245: DUPLICATE_SAME_ACCESS)) {
1246: lprintf(LOG_ERR,"%04d !%s ERROR %d duplicating socket descriptor"
1247: ,socket,service->protocol,GetLastError());
1248: close_socket(service->socket);
1249: service->socket=INVALID_SOCKET;
1250: thread_down();
1251: return;
1252: }
1253: #else
1254: socket_dup = dup(service->socket);
1255: #endif
1256:
1257: /* RUN SCRIPT */
1258: if(strpbrk(service->cmd,"/\\")==NULL)
1259: sprintf(cmd,"%s%s",scfg.exec_dir,service->cmd);
1260: else
1261: strcpy(cmd,service->cmd);
1262: sprintf(fullcmd,cmd,socket_dup);
1263:
1264: do {
1265: system(fullcmd);
1266: } while(!service->terminated && service->options&SERVICE_OPT_STATIC_LOOP);
1267:
1268: thread_down();
1269: lprintf(LOG_DEBUG,"%04d %s static service thread terminated (%lu clients served)"
1270: ,socket, service->protocol, service->served);
1271:
1272: close_socket(service->socket);
1273: service->socket=INVALID_SOCKET;
1274: closesocket(socket_dup); /* close duplicate handle */
1275:
1276: service->running=FALSE;
1277: }
1278:
1279: static void native_service_thread(void* arg)
1280: {
1281: int i;
1282: char cmd[MAX_PATH];
1283: char fullcmd[MAX_PATH*2];
1284: char* host_name;
1285: HOSTENT* host;
1286: SOCKET socket;
1287: SOCKET socket_dup;
1288: client_t client;
1289: service_t* service;
1290: service_client_t service_client=*(service_client_t*)arg;
1291:
1292: free(arg);
1293:
1294: socket=service_client.socket;
1295: service=service_client.service;
1296:
1297: lprintf(LOG_DEBUG,"%04d %s service thread started", socket, service->protocol);
1298:
1299: thread_up(TRUE /* setuid */);
1300:
1301: /* Host name lookup and filtering */
1302: if(service->options&BBS_OPT_NO_HOST_LOOKUP
1303: || startup->options&BBS_OPT_NO_HOST_LOOKUP)
1304: host=NULL;
1305: else
1306: host=gethostbyaddr((char *)&service_client.addr.sin_addr
1307: ,sizeof(service_client.addr.sin_addr),AF_INET);
1308:
1309: if(host!=NULL && host->h_name!=NULL)
1310: host_name=host->h_name;
1311: else
1312: host_name="<no name>";
1313:
1314: if(!(service->options&BBS_OPT_NO_HOST_LOOKUP)
1315: && !(startup->options&BBS_OPT_NO_HOST_LOOKUP)) {
1316: lprintf(LOG_INFO,"%04d %s Hostname: %s"
1317: ,socket, service->protocol, host_name);
1318: for(i=0;host!=NULL && host->h_aliases!=NULL
1319: && host->h_aliases[i]!=NULL;i++)
1320: lprintf(LOG_INFO,"%04d %s HostAlias: %s"
1321: ,socket, service->protocol, host->h_aliases[i]);
1322: }
1323:
1324: if(trashcan(&scfg,host_name,"host")) {
1325: lprintf(LOG_WARNING,"%04d !%s CLIENT BLOCKED in host.can: %s"
1326: ,socket, service->protocol, host_name);
1327: close_socket(socket);
1328: if(service->clients)
1329: service->clients--;
1330: thread_down();
1331: return;
1332: }
1333:
1334:
1335: #if 0 /* Need to export from SBBS.DLL */
1336: identity=NULL;
1337: if(service->options&BBS_OPT_GET_IDENT
1338: && startup->options&BBS_OPT_GET_IDENT) {
1339: identify(&service_client.addr, service->port, str, sizeof(str)-1);
1340: identity=strrchr(str,':');
1341: if(identity!=NULL) {
1342: identity++; /* skip colon */
1343: while(*identity && *identity<=SP) /* point to user name */
1344: identity++;
1345: lprintf(LOG_INFO,"%04d Identity: %s",socket, identity);
1346: }
1347: }
1348: #endif
1349:
1350: client.size=sizeof(client);
1351: client.time=time(NULL);
1352: SAFECOPY(client.addr,inet_ntoa(service_client.addr.sin_addr));
1353: SAFECOPY(client.host,host_name);
1354: client.port=ntohs(service_client.addr.sin_port);
1355: client.protocol=service->protocol;
1356: client.user="<unknown>";
1357:
1358: #ifdef _WIN32
1359: if(!DuplicateHandle(GetCurrentProcess(),
1360: (HANDLE)socket,
1361: GetCurrentProcess(),
1362: (HANDLE*)&socket_dup,
1363: 0,
1364: TRUE, // Inheritable
1365: DUPLICATE_SAME_ACCESS)) {
1366: lprintf(LOG_ERR,"%04d !%s ERROR %d duplicating socket descriptor"
1367: ,socket,service->protocol,GetLastError());
1368: close_socket(socket);
1369: thread_down();
1370: return;
1371: }
1372: #else
1373: socket_dup = dup(socket);
1374: #endif
1375:
1376: update_clients();
1377:
1378: /* Initialize client display */
1379: client_on(socket,&client,FALSE /* update */);
1380:
1381: /* RUN SCRIPT */
1382: if(strpbrk(service->cmd,"/\\")==NULL)
1383: sprintf(cmd,"%s%s",scfg.exec_dir,service->cmd);
1384: else
1385: strcpy(cmd,service->cmd);
1386: sprintf(fullcmd,cmd,socket_dup);
1387:
1388: system(fullcmd);
1389:
1390: if(service->clients)
1391: service->clients--;
1392: update_clients();
1393:
1394: #ifdef _WIN32
1395: if(startup->hangup_sound[0] && !(startup->options&BBS_OPT_MUTE)
1396: && !(service->options&BBS_OPT_MUTE))
1397: PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
1398: #endif
1399:
1400: thread_down();
1401: lprintf(LOG_DEBUG,"%04d %s service thread terminated (%u clients remain, %d total, %lu served)"
1402: ,socket, service->protocol, service->clients, active_clients(), service->served);
1403:
1404: client_off(socket);
1405: close_socket(socket);
1406: closesocket(socket_dup); /* close duplicate handle */
1407: }
1408:
1409:
1410: void DLLCALL services_terminate(void)
1411: {
1412: DWORD i;
1413:
1414: lprintf(LOG_INFO,"0000 Services terminate");
1415: terminated=TRUE;
1416: for(i=0;i<services;i++)
1417: service[i].terminated=TRUE;
1418: }
1419:
1420: #define NEXT_FIELD(p) FIND_WHITESPACE(p); SKIP_WHITESPACE(p)
1421:
1422: static service_t* read_services_ini(service_t* service, DWORD* services)
1423: {
1424: uint i,j;
1425: FILE* fp;
1426: char cmd[INI_MAX_VALUE_LEN];
1427: char host[INI_MAX_VALUE_LEN];
1428: char prot[INI_MAX_VALUE_LEN];
1429: char services_ini[MAX_PATH+1];
1430: char** sec_list;
1431: service_t* np;
1432: service_t serv;
1433:
1434: iniFileName(services_ini,sizeof(services_ini),scfg.ctrl_dir,"services.ini");
1435:
1436: if((fp=fopen(services_ini,"r"))==NULL) {
1437: lprintf(LOG_ERR,"!ERROR %d opening %s", errno, services_ini);
1438: return(NULL);
1439: }
1440:
1441: lprintf(LOG_INFO,"Reading %s",services_ini);
1442: sec_list = iniReadSectionList(fp,"");
1443: for(i=0; sec_list!=NULL && sec_list[i]!=NULL; i++) {
1444: memset(&serv,0,sizeof(service_t));
1445: SAFECOPY(serv.protocol,iniReadString(fp,sec_list[i],"Protocol",sec_list[i],prot));
1446: serv.socket=INVALID_SOCKET;
1447: serv.interface_addr=iniReadIpAddress(fp,sec_list[i],"Interface",startup->interface_addr);
1448: serv.port=iniReadShortInt(fp,sec_list[i],"Port",0);
1449: serv.max_clients=iniReadInteger(fp,sec_list[i],"MaxClients",0);
1450: serv.listen_backlog=iniReadInteger(fp,sec_list[i],"ListenBacklog",DEFAULT_LISTEN_BACKLOG);
1451: serv.stack_size=iniReadInteger(fp,sec_list[i],"StackSize",0);
1452: serv.options=iniReadBitField(fp,sec_list[i],"Options",service_options,0);
1453: SAFECOPY(serv.cmd,iniReadString(fp,sec_list[i],"Command","",cmd));
1454:
1455: /* JavaScript operating parameters */
1456: serv.js_max_bytes=iniReadInteger(fp,sec_list[i] ,strJavaScriptMaxBytes ,startup->js_max_bytes);
1457: serv.js_cx_stack=iniReadInteger(fp,sec_list[i] ,strJavaScriptContextStack ,startup->js_cx_stack);
1458: serv.js_branch_limit=iniReadInteger(fp,sec_list[i] ,strJavaScriptBranchLimit ,startup->js_branch_limit);
1459: serv.js_gc_interval=iniReadInteger(fp,sec_list[i] ,strJavaScriptGcInterval ,startup->js_gc_interval);
1460: serv.js_yield_interval=iniReadInteger(fp,sec_list[i] ,strJavaScriptYieldInterval ,startup->js_yield_interval);
1461:
1462: for(j=0;j<*services;j++)
1463: if(service[j].interface_addr==serv.interface_addr && service[j].port==serv.port
1464: && (service[j].options&SERVICE_OPT_UDP)==(serv.options&SERVICE_OPT_UDP))
1465: break;
1466: if(j<*services) { /* ignore duplicate services */
1467: lprintf(LOG_NOTICE,"Ignoring duplicate service: %s",sec_list[i]);
1468: continue;
1469: }
1470:
1471: if(stricmp(iniReadString(fp,sec_list[i],"Host",startup->host_name,host), startup->host_name)!=0) {
1472: lprintf(LOG_NOTICE,"Ignoring service (%s) for host: %s", sec_list[i], host);
1473: continue;
1474: }
1475: if(stricmp(iniReadString(fp,sec_list[i],"NotHost","",host), startup->host_name)==0) {
1476: lprintf(LOG_NOTICE,"Ignoring service (%s) not for host: %s", sec_list[i], host);
1477: continue;
1478: }
1479:
1480: if((np=(service_t*)realloc(service,sizeof(service_t)*((*services)+1)))==NULL) {
1481: fclose(fp);
1482: lprintf(LOG_CRIT,"!MALLOC FAILURE");
1483: return(service);
1484: }
1485: service=np;
1486: service[*services]=serv;
1487: (*services)++;
1488: }
1489: iniFreeStringList(sec_list);
1490:
1491: fclose(fp);
1492:
1493: return(service);
1494: }
1495:
1496: static void cleanup(int code)
1497: {
1498: FREE_AND_NULL(service);
1499: services=0;
1500:
1501: free_cfg(&scfg);
1502:
1503: semfile_list_free(&recycle_semfiles);
1504: semfile_list_free(&shutdown_semfiles);
1505:
1506: update_clients();
1507:
1508: #ifdef _WINSOCKAPI_
1509: if(WSAInitialized && WSACleanup()!=0)
1510: lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
1511: #endif
1512:
1513: thread_down();
1514: if(terminated || code)
1515: lprintf(LOG_DEBUG,"#### Services thread terminated (%lu clients served)",served);
1516: status("Down");
1517: if(startup!=NULL && startup->terminated!=NULL)
1518: startup->terminated(startup->cbdata,code);
1519: }
1520:
1521: const char* DLLCALL services_ver(void)
1522: {
1523: static char ver[256];
1524: char compiler[32];
1525:
1526: DESCRIBE_COMPILER(compiler);
1527:
1528: sscanf("$Revision: 1.175 $", "%*s %s", revision);
1529:
1530: sprintf(ver,"Synchronet Services %s%s "
1531: "Compiled %s %s with %s"
1532: ,revision
1533: #ifdef _DEBUG
1534: ," Debug"
1535: #else
1536: ,""
1537: #endif
1538: ,__DATE__, __TIME__, compiler
1539: );
1540:
1541: return(ver);
1542: }
1543:
1544: void DLLCALL services_thread(void* arg)
1545: {
1546: char* p;
1547: char path[MAX_PATH+1];
1548: char error[256];
1549: char host_ip[32];
1550: char compiler[32];
1551: char str[128];
1552: SOCKADDR_IN addr;
1553: SOCKADDR_IN client_addr;
1554: socklen_t client_addr_len;
1555: SOCKET socket;
1556: SOCKET client_socket;
1557: BYTE* udp_buf = NULL;
1558: int udp_len;
1559: int i;
1560: int result;
1561: int optval;
1562: ulong total_running;
1563: time_t t;
1564: time_t initialized=0;
1565: fd_set socket_set;
1566: SOCKET high_socket;
1567: ulong total_sockets;
1568: struct timeval tv;
1569: service_client_t* client;
1570:
1571: services_ver();
1572:
1573: startup=(services_startup_t*)arg;
1574:
1575: if(startup==NULL) {
1576: sbbs_beep(100,500);
1577: fprintf(stderr, "No startup structure passed!\n");
1578: return;
1579: }
1580:
1581: if(startup->size!=sizeof(services_startup_t)) { /* verify size */
1582: sbbs_beep(100,500);
1583: sbbs_beep(300,500);
1584: sbbs_beep(100,500);
1585: fprintf(stderr, "Invalid startup structure!\n");
1586: return;
1587: }
1588:
1589: #ifdef _THREAD_SUID_BROKEN
1590: startup->seteuid(TRUE);
1591: #endif
1592:
1593: /* Setup intelligent defaults */
1594: if(startup->sem_chk_freq==0) startup->sem_chk_freq=2;
1595: if(startup->js_max_bytes==0) startup->js_max_bytes=JAVASCRIPT_MAX_BYTES;
1596: if(startup->js_cx_stack==0) startup->js_cx_stack=JAVASCRIPT_CONTEXT_STACK;
1597:
1598: uptime=0;
1599: served=0;
1600: startup->recycle_now=FALSE;
1601: startup->shutdown_now=FALSE;
1602:
1603: do {
1604:
1605: thread_up(FALSE /* setuid */);
1606:
1607: status("Initializing");
1608:
1609: memset(&scfg, 0, sizeof(scfg));
1610:
1611: lprintf(LOG_INFO,"Synchronet Services Revision %s%s"
1612: ,revision
1613: #ifdef _DEBUG
1614: ," Debug"
1615: #else
1616: ,""
1617: #endif
1618: );
1619:
1620: DESCRIBE_COMPILER(compiler);
1621:
1622: lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler);
1623:
1624: srand(time(NULL)); /* Seed random number generator */
1625: sbbs_random(10); /* Throw away first number */
1626:
1627: if(!winsock_startup()) {
1628: cleanup(1);
1629: return;
1630: }
1631:
1632: t=time(NULL);
1633: lprintf(LOG_INFO,"Initializing on %.24s with options: %lx"
1634: ,CTIME_R(&t,str),startup->options);
1635:
1636: /* Initial configuration and load from CNF files */
1637: SAFECOPY(scfg.ctrl_dir, startup->ctrl_dir);
1638: lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir);
1639: scfg.size=sizeof(scfg);
1640: SAFECOPY(error,UNKNOWN_LOAD_ERROR);
1641: if(!load_cfg(&scfg, NULL, TRUE, error)) {
1642: lprintf(LOG_ERR,"!ERROR %s",error);
1643: lprintf(LOG_ERR,"!Failed to load configuration files");
1644: cleanup(1);
1645: return;
1646: }
1647:
1648: #if 0 /* ToDo */
1649: if(startup->temp_dir[0]) {
1650: SAFECOPY(scfg.temp_dir,startup->temp_dir);
1651: backslash(scfg.temp_dir);
1652: } else
1653: #endif
1654: prep_dir(scfg.data_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
1655:
1656: if(startup->host_name[0]==0)
1657: SAFECOPY(startup->host_name,scfg.sys_inetaddr);
1658:
1659: if(!(scfg.sys_misc&SM_LOCAL_TZ) && !(startup->options&BBS_OPT_LOCAL_TIMEZONE)) {
1660: if(putenv("TZ=UTC0"))
1661: lprintf(LOG_ERR,"!putenv() FAILED");
1662: tzset();
1663:
1664: if((t=checktime())!=0) { /* Check binary time */
1665: lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
1666: cleanup(1);
1667: return;
1668: }
1669: }
1670:
1671: if(uptime==0)
1672: uptime=time(NULL); /* this must be done *after* setting the timezone */
1673:
1674: if((service=read_services_ini(service, &services))==NULL) {
1675: cleanup(1);
1676: return;
1677: }
1678:
1679: update_clients();
1680:
1681: /* Open and Bind Listening Sockets */
1682: total_sockets=0;
1683: for(i=0;i<(int)services;i++) {
1684:
1685: service[i].socket=INVALID_SOCKET;
1686:
1687: if((socket = open_socket(
1688: (service[i].options&SERVICE_OPT_UDP) ? SOCK_DGRAM : SOCK_STREAM))
1689: ==INVALID_SOCKET) {
1690: lprintf(LOG_ERR,"!ERROR %d opening %s socket"
1691: ,ERROR_VALUE, service[i].protocol);
1692: cleanup(1);
1693: return;
1694: }
1695:
1696: if(service[i].options&SERVICE_OPT_UDP) {
1697: /* We need to set the REUSE ADDRESS socket option */
1698: optval=TRUE;
1699: if(setsockopt(socket,SOL_SOCKET,SO_REUSEADDR
1700: ,(char*)&optval,sizeof(optval))!=0) {
1701: lprintf(LOG_ERR,"%04d !ERROR %d setting %s socket option"
1702: ,socket, ERROR_VALUE, service[i].protocol);
1703: close_socket(socket);
1704: continue;
1705: }
1706: #ifdef BSD
1707: if(setsockopt(socket,SOL_SOCKET,SO_REUSEPORT
1708: ,(char*)&optval,sizeof(optval))!=0) {
1709: lprintf(LOG_ERR,"%04d !ERROR %d setting %s socket option"
1710: ,socket, ERROR_VALUE, service[i].protocol);
1711: close_socket(socket);
1712: continue;
1713: }
1714: #endif
1715: }
1716: memset(&addr, 0, sizeof(addr));
1717:
1718: addr.sin_addr.s_addr = htonl(service[i].interface_addr);
1719: addr.sin_family = AF_INET;
1720: addr.sin_port = htons(service[i].port);
1721:
1722: if(startup->seteuid!=NULL)
1723: startup->seteuid(FALSE);
1724: result=retry_bind(socket, (struct sockaddr *) &addr, sizeof(addr)
1725: ,startup->bind_retry_count, startup->bind_retry_delay, service[i].protocol, lprintf);
1726: if(startup->seteuid!=NULL)
1727: startup->seteuid(TRUE);
1728: if(result!=0) {
1729: lprintf(LOG_ERR,"%04d %s",socket,BIND_FAILURE_HELP);
1730: close_socket(socket);
1731: continue;
1732: }
1733:
1734: lprintf(LOG_INFO,"%04d %s socket bound to %s port %u"
1735: ,socket, service[i].protocol
1736: ,service[i].options&SERVICE_OPT_UDP ? "UDP" : "TCP"
1737: ,service[i].port);
1738:
1739: if(!(service[i].options&SERVICE_OPT_UDP)) {
1740: if(listen(socket,service[i].listen_backlog)!=0) {
1741: lprintf(LOG_ERR,"%04d !ERROR %d listening on %s socket"
1742: ,socket, ERROR_VALUE, service[i].protocol);
1743: close_socket(socket);
1744: continue;
1745: }
1746: }
1747: service[i].socket=socket;
1748: total_sockets++;
1749: }
1750:
1751: if(!total_sockets) {
1752: lprintf(LOG_WARNING,"0000 !No service sockets bound");
1753: cleanup(1);
1754: return;
1755: }
1756:
1757: /* Setup static service threads */
1758: for(i=0;i<(int)services;i++) {
1759: if(!(service[i].options&SERVICE_OPT_STATIC))
1760: continue;
1761: if(service[i].socket==INVALID_SOCKET) /* bind failure? */
1762: continue;
1763:
1764: /* start thread here */
1765: if(service[i].options&SERVICE_OPT_NATIVE) /* Native */
1766: _beginthread(native_static_service_thread, service[i].stack_size, &service[i]);
1767: else /* JavaScript */
1768: _beginthread(js_static_service_thread, service[i].stack_size, &service[i]);
1769: }
1770:
1771: status("Listening");
1772:
1773: /* Setup recycle/shutdown semaphore file lists */
1774: semfile_list_init(&shutdown_semfiles,scfg.ctrl_dir,"shutdown","services");
1775: semfile_list_init(&recycle_semfiles,scfg.ctrl_dir,"recycle","services");
1776: SAFEPRINTF(path,"%sservices.rec",scfg.ctrl_dir); /* legacy */
1777: semfile_list_add(&recycle_semfiles,path);
1778: if(!initialized) {
1779: initialized=time(NULL);
1780: semfile_list_check(&initialized,&recycle_semfiles);
1781: semfile_list_check(&initialized,&shutdown_semfiles);
1782: }
1783:
1784: terminated=FALSE;
1785:
1786: /* signal caller that we've started up successfully */
1787: if(startup->started!=NULL)
1788: startup->started(startup->cbdata);
1789:
1790: /* Main Server Loop */
1791: while(!terminated) {
1792:
1793: if(active_clients()==0) {
1794: if(!(startup->options&BBS_OPT_NO_RECYCLE)) {
1795: if((p=semfile_list_check(&initialized,&recycle_semfiles))!=NULL) {
1796: lprintf(LOG_INFO,"0000 Recycle semaphore file (%s) detected",p);
1797: break;
1798: }
1799: #if 0 /* unused */
1800: if(startup->recycle_sem!=NULL && sem_trywait(&startup->recycle_sem)==0)
1801: startup->recycle_now=TRUE;
1802: #endif
1803: if(startup->recycle_now==TRUE) {
1804: lprintf(LOG_NOTICE,"0000 Recycle semaphore signaled");
1805: startup->recycle_now=FALSE;
1806: break;
1807: }
1808: }
1809: if(((p=semfile_list_check(&initialized,&shutdown_semfiles))!=NULL
1810: && lprintf(LOG_INFO,"0000 Shutdown semaphore file (%s) detected",p))
1811: || (startup->shutdown_now==TRUE
1812: && lprintf(LOG_INFO,"0000 Shutdown semaphore signaled"))) {
1813: startup->shutdown_now=FALSE;
1814: terminated=TRUE;
1815: break;
1816: }
1817: }
1818:
1819: /* Setup select() parms */
1820: FD_ZERO(&socket_set);
1821: high_socket=0;
1822: for(i=0;i<(int)services;i++) {
1823: if(service[i].options&SERVICE_OPT_STATIC)
1824: continue;
1825: if(service[i].socket==INVALID_SOCKET)
1826: continue;
1827: if(!(service[i].options&SERVICE_OPT_FULL_ACCEPT)
1828: && service[i].max_clients && service[i].clients >= service[i].max_clients)
1829: continue;
1830: FD_SET(service[i].socket,&socket_set);
1831: if(service[i].socket>high_socket)
1832: high_socket=service[i].socket;
1833: }
1834: if(high_socket==0) { /* No dynamic services? */
1835: YIELD();
1836: continue;
1837: }
1838: tv.tv_sec=startup->sem_chk_freq;
1839: tv.tv_usec=0;
1840: if((result=select(high_socket+1,&socket_set,NULL,NULL,&tv))<1) {
1841: if(result==0)
1842: continue;
1843:
1844: if(ERROR_VALUE==EINTR)
1845: lprintf(LOG_NOTICE,"0000 Services listening interrupted");
1846: else if(ERROR_VALUE == ENOTSOCK)
1847: lprintf(LOG_NOTICE,"0000 Services sockets closed");
1848: else
1849: lprintf(LOG_WARNING,"0000 !ERROR %d selecting sockets",ERROR_VALUE);
1850: continue;
1851: }
1852:
1853: /* Determine who services this socket */
1854: for(i=0;i<(int)services;i++) {
1855:
1856: if(service[i].socket==INVALID_SOCKET)
1857: continue;
1858:
1859: if(!FD_ISSET(service[i].socket,&socket_set))
1860: continue;
1861:
1862: client_addr_len = sizeof(client_addr);
1863:
1864: udp_len=0;
1865:
1866: if(service[i].options&SERVICE_OPT_UDP) {
1867: /* UDP */
1868: if((udp_buf = (BYTE*)calloc(1, MAX_UDP_BUF_LEN)) == NULL) {
1869: lprintf(LOG_CRIT,"%04d %s !ERROR %d allocating UDP buffer"
1870: ,service[i].socket, service[i].protocol, errno);
1871: continue;
1872: }
1873:
1874: udp_len = recvfrom(service[i].socket
1875: ,udp_buf, MAX_UDP_BUF_LEN, 0 /* flags */
1876: ,(struct sockaddr *)&client_addr, &client_addr_len);
1877: if(udp_len<1) {
1878: FREE_AND_NULL(udp_buf);
1879: lprintf(LOG_ERR,"%04d %s !ERROR %d recvfrom failed"
1880: ,service[i].socket, service[i].protocol, ERROR_VALUE);
1881: continue;
1882: }
1883:
1884: if((client_socket = open_socket(SOCK_DGRAM))
1885: ==INVALID_SOCKET) {
1886: FREE_AND_NULL(udp_buf);
1887: lprintf(LOG_ERR,"%04d %s !ERROR %d opening socket"
1888: ,service[i].socket, service[i].protocol, ERROR_VALUE);
1889: continue;
1890: }
1891:
1892: lprintf(LOG_DEBUG,"%04d %s created client socket: %d"
1893: ,service[i].socket, service[i].protocol, client_socket);
1894:
1895: /* We need to set the REUSE ADDRESS socket option */
1896: optval=TRUE;
1897: if(setsockopt(client_socket,SOL_SOCKET,SO_REUSEADDR
1898: ,(char*)&optval,sizeof(optval))!=0) {
1899: FREE_AND_NULL(udp_buf);
1900: lprintf(LOG_ERR,"%04d %s !ERROR %d setting socket option"
1901: ,client_socket, service[i].protocol, ERROR_VALUE);
1902: close_socket(client_socket);
1903: continue;
1904: }
1905: #ifdef BSD
1906: if(setsockopt(client_socket,SOL_SOCKET,SO_REUSEPORT
1907: ,(char*)&optval,sizeof(optval))!=0) {
1908: FREE_AND_NULL(udp_buf);
1909: lprintf(LOG_ERR,"%04d %s !ERROR %d setting socket option"
1910: ,client_socket, service[i].protocol, ERROR_VALUE);
1911: close_socket(client_socket);
1912: continue;
1913: }
1914: #endif
1915:
1916: memset(&addr, 0, sizeof(addr));
1917: addr.sin_addr.s_addr = htonl(service[i].interface_addr);
1918: addr.sin_family = AF_INET;
1919: addr.sin_port = htons(service[i].port);
1920:
1921: result=bind(client_socket, (struct sockaddr *) &addr, sizeof(addr));
1922: if(result==SOCKET_ERROR) {
1923: /* Failed to re-bind to same port number, use user port */
1924: lprintf(LOG_ERR,"%04d %s ERROR %d re-binding socket to port %u failed, "
1925: "using user port"
1926: ,client_socket, service[i].protocol, ERROR_VALUE, service[i].port);
1927: addr.sin_port=0;
1928: result=bind(client_socket, (struct sockaddr *) &addr, sizeof(addr));
1929: }
1930: if(result!=0) {
1931: FREE_AND_NULL(udp_buf);
1932: lprintf(LOG_ERR,"%04d %s !ERROR %d re-binding socket to port %u"
1933: ,client_socket, service[i].protocol, ERROR_VALUE, service[i].port);
1934: close_socket(client_socket);
1935: continue;
1936: }
1937:
1938: /* Set client address as default addres for send/recv */
1939: if(connect(client_socket
1940: ,(struct sockaddr *)&client_addr, client_addr_len)!=0) {
1941: FREE_AND_NULL(udp_buf);
1942: lprintf(LOG_ERR,"%04d %s !ERROR %d connect failed"
1943: ,client_socket, service[i].protocol, ERROR_VALUE);
1944: close_socket(client_socket);
1945: continue;
1946: }
1947:
1948: } else {
1949: /* TCP */
1950: if((client_socket=accept(service[i].socket
1951: ,(struct sockaddr *)&client_addr, &client_addr_len))==INVALID_SOCKET) {
1952: if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINVAL)
1953: lprintf(LOG_NOTICE,"%04d %s socket closed while listening"
1954: ,service[i].socket, service[i].protocol);
1955: else
1956: lprintf(LOG_WARNING,"%04d %s !ERROR %d accepting connection"
1957: ,service[i].socket, service[i].protocol, ERROR_VALUE);
1958: #ifdef _WIN32
1959: if(WSAGetLastError()==WSAENOBUFS) /* recycle (re-init WinSock) on this error */
1960: break;
1961: #endif
1962: continue;
1963: }
1964: sockets++;
1965: #if 0 /*def _DEBUG */
1966: lprintf(LOG_DEBUG,"%04d Socket opened (%d sockets in use)",client_socket,sockets);
1967: #endif
1968: if(startup->socket_open!=NULL) /* Callback, increments socket counter */
1969: startup->socket_open(startup->cbdata,TRUE);
1970: }
1971: SAFECOPY(host_ip,inet_ntoa(client_addr.sin_addr));
1972:
1973: if(trashcan(&scfg,host_ip,"ip-silent")) {
1974: FREE_AND_NULL(udp_buf);
1975: close_socket(client_socket);
1976: continue;
1977: }
1978:
1979: lprintf(LOG_INFO,"%04d %s connection accepted from: %s port %u"
1980: ,client_socket
1981: ,service[i].protocol, host_ip, ntohs(client_addr.sin_port));
1982:
1983: if(service[i].max_clients && service[i].clients+1>service[i].max_clients) {
1984: lprintf(LOG_WARNING,"%04d !%s MAXMIMUM CLIENTS (%u) reached, access denied"
1985: ,client_socket, service[i].protocol, service[i].max_clients);
1986: mswait(3000);
1987: close_socket(client_socket);
1988: continue;
1989: }
1990:
1991: #ifdef _WIN32
1992: if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE)
1993: && !(service[i].options&BBS_OPT_MUTE))
1994: PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
1995: #endif
1996:
1997: if(trashcan(&scfg,host_ip,"ip")) {
1998: FREE_AND_NULL(udp_buf);
1999: lprintf(LOG_NOTICE,"%04d !%s CLIENT BLOCKED in ip.can: %s"
2000: ,client_socket, service[i].protocol, host_ip);
2001: mswait(3000);
2002: close_socket(client_socket);
2003: continue;
2004: }
2005:
2006: if((client=malloc(sizeof(service_client_t)))==NULL) {
2007: FREE_AND_NULL(udp_buf);
2008: lprintf(LOG_CRIT,"%04d !%s ERROR allocating %u bytes of memory for service_client"
2009: ,client_socket, service[i].protocol, sizeof(service_client_t));
2010: mswait(3000);
2011: close_socket(client_socket);
2012: continue;
2013: }
2014:
2015: memset(client,0,sizeof(service_client_t));
2016: client->socket=client_socket;
2017: client->addr=client_addr;
2018: client->service=&service[i];
2019: client->service->clients++; /* this should be mutually exclusive */
2020: client->udp_buf=udp_buf;
2021: client->udp_len=udp_len;
2022: client->branch.limit = service[i].js_branch_limit;
2023: client->branch.gc_interval = service[i].js_gc_interval;
2024: client->branch.yield_interval = service[i].js_yield_interval;
2025: client->branch.terminated = &client->service->terminated;
2026: client->branch.auto_terminate = TRUE;
2027:
2028: udp_buf = NULL;
2029:
2030: if(service[i].options&SERVICE_OPT_NATIVE) /* Native */
2031: _beginthread(native_service_thread, service[i].stack_size, client);
2032: else /* JavaScript */
2033: _beginthread(js_service_thread, service[i].stack_size, client);
2034: service[i].served++;
2035: served++;
2036: }
2037: }
2038:
2039: /* Close Service Sockets */
2040: lprintf(LOG_DEBUG,"0000 Closing service sockets");
2041: for(i=0;i<(int)services;i++) {
2042: service[i].terminated=TRUE;
2043: if(service[i].socket==INVALID_SOCKET)
2044: continue;
2045: if(service[i].options&SERVICE_OPT_STATIC)
2046: continue;
2047: close_socket(service[i].socket);
2048: service[i].socket=INVALID_SOCKET;
2049: }
2050:
2051: /* Wait for Dynamic Service Threads to terminate */
2052: if(active_clients()) {
2053: lprintf(LOG_DEBUG,"0000 Waiting for %d clients to disconnect",active_clients());
2054: while(active_clients()) {
2055: mswait(500);
2056: }
2057: lprintf(LOG_DEBUG,"0000 Done waiting");
2058: }
2059:
2060: /* Wait for Static Service Threads to terminate */
2061: total_running=0;
2062: for(i=0;i<(int)services;i++)
2063: total_running+=service[i].running;
2064: if(total_running) {
2065: lprintf(LOG_DEBUG,"0000 Waiting for %d static services to terminate",total_running);
2066: while(1) {
2067: total_running=0;
2068: for(i=0;i<(int)services;i++)
2069: total_running+=service[i].running;
2070: if(!total_running)
2071: break;
2072: mswait(500);
2073: }
2074: lprintf(LOG_DEBUG,"0000 Done waiting");
2075: }
2076:
2077: cleanup(0);
2078: if(!terminated) {
2079: lprintf(LOG_INFO,"Recycling server...");
2080: mswait(2000);
2081: if(startup->recycle!=NULL)
2082: startup->recycle(startup->cbdata);
2083: }
2084:
2085: } while(!terminated);
2086: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.