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