|
|
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.