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