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