--- sbbs/src/sbbs3/services.c 2018/04/24 16:41:23 1.1.1.1 +++ sbbs/src/sbbs3/services.c 2018/04/24 16:42:58 1.1.1.2 @@ -2,13 +2,13 @@ /* Synchronet Services */ -/* $Id: services.c,v 1.1.1.1 2018/04/24 16:41:23 root Exp $ */ +/* $Id: services.c,v 1.1.1.2 2018/04/24 16:42:58 root Exp $ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * - * Copyright 2005 Rob Swindell - http://www.synchro.net/copyright.html * + * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * @@ -59,43 +59,42 @@ #include "services.h" #include "ident.h" /* identify() */ #include "sbbs_ini.h" +#include "js_rtpool.h" +#include "js_request.h" /* Constants */ -#define MAX_SERVICES 128 -#define TIMEOUT_THREAD_WAIT 60 /* Seconds */ #define MAX_UDP_BUF_LEN 8192 /* 8K */ #define DEFAULT_LISTEN_BACKLOG 5 static services_startup_t* startup=NULL; static scfg_t scfg; -static DWORD sockets=0; -static BOOL terminated=FALSE; +static volatile BOOL terminated=FALSE; static time_t uptime=0; -static DWORD served=0; +static ulong served=0; static char revision[16]; static str_list_t recycle_semfiles; static str_list_t shutdown_semfiles; typedef struct { /* These are sysop-configurable */ - DWORD interface_addr; - WORD port; - char protocol[34]; - char cmd[128]; - DWORD max_clients; - DWORD options; - int listen_backlog; - int log_level; - DWORD stack_size; + uint32_t interface_addr; + uint16_t port; + char protocol[34]; + char cmd[128]; + uint max_clients; + uint32_t options; + int listen_backlog; + int log_level; + uint32_t stack_size; js_startup_t js; js_server_props_t js_server_props; /* These are run-time state and stat vars */ - DWORD clients; - DWORD served; - SOCKET socket; - BOOL running; - BOOL terminated; + uint32_t clients; + ulong served; + SOCKET socket; + BOOL running; + BOOL terminated; } service_t; typedef struct { @@ -109,17 +108,29 @@ typedef struct { /* Initial UDP datagram */ BYTE* udp_buf; int udp_len; + subscan_t *subscan; } service_client_t; static service_t *service=NULL; -static DWORD services=0; +static uint32_t services=0; -static int lprintf(int level, char *fmt, ...) +static int lprintf(int level, const char *fmt, ...) { va_list argptr; char sbuf[1024]; - if(startup==NULL || startup->lputs==NULL) + va_start(argptr,fmt); + vsnprintf(sbuf,sizeof(sbuf),fmt,argptr); + sbuf[sizeof(sbuf)-1]=0; + va_end(argptr); + + if(level <= LOG_ERR) { + errorlog(&scfg,startup==NULL ? NULL:startup->host_name, sbuf); + if(startup!=NULL && startup->errormsg!=NULL) + startup->errormsg(startup->cbdata,level,sbuf); + } + + if(startup==NULL || startup->lputs==NULL || level > startup->log_level) return(0); #if defined(_WIN32) @@ -127,10 +138,6 @@ static int lprintf(int level, char *fmt, return(0); #endif - va_start(argptr,fmt); - vsnprintf(sbuf,sizeof(sbuf),fmt,argptr); - sbuf[sizeof(sbuf)-1]=0; - va_end(argptr); return(startup->lputs(startup->cbdata,level,sbuf)); } @@ -145,7 +152,7 @@ static BOOL winsock_startup(void) int status; /* Status Code */ if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) { - lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus); + lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus); WSAInitialized=TRUE; return (TRUE); } @@ -212,14 +219,9 @@ static SOCKET open_socket(int type, cons if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); if(sock!=INVALID_SOCKET) { - sockets++; SAFEPRINTF(section,"services|%s", protocol); if(set_socket_options(&scfg, sock, section, error, sizeof(error))) lprintf(LOG_ERR,"%04d !ERROR %s",sock, error); - -#if 0 /*def _DEBUG */ - lprintf(LOG_DEBUG,"%04d Socket opened (%d sockets in use)",sock,sockets); -#endif } return(sock); } @@ -235,13 +237,8 @@ static int close_socket(SOCKET sock) result=closesocket(sock); if(startup!=NULL && startup->socket_open!=NULL) startup->socket_open(startup->cbdata,FALSE); - sockets--; if(result!=0) lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",sock, ERROR_VALUE); -#if 0 /*def _DEBUG */ - else - lprintf(LOG_DEBUG,"%04d Socket closed (%d sockets in use)",sock,sockets); -#endif return(result); } @@ -252,16 +249,6 @@ static void status(char* str) startup->status(startup->cbdata,str); } -static time_t checktime(void) -{ - struct tm tm; - - memset(&tm,0,sizeof(tm)); - tm.tm_year=94; - tm.tm_mday=1; - return(mktime(&tm)-0x2D24BD00L); -} - /* Global JavaScript Methods */ static JSBool @@ -270,6 +257,7 @@ js_read(JSContext *cx, JSObject *obj, ui char* buf; int32 len=512; service_client_t* client; + jsrefcount rc; if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) return(JS_FALSE); @@ -280,7 +268,9 @@ js_read(JSContext *cx, JSObject *obj, ui if((buf=alloca(len))==NULL) return(JS_TRUE); + rc=JS_SUSPENDREQUEST(cx); len=recv(client->socket,buf,len,0); + JS_RESUMEREQUEST(cx, rc); if(len>0) *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx,buf,len)); @@ -300,6 +290,7 @@ js_readln(JSContext *cx, JSObject *obj, int32 timeout=30; /* seconds */ JSString* str; service_client_t* client; + jsrefcount rc; if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) return(JS_FALSE); @@ -315,6 +306,7 @@ js_readln(JSContext *cx, JSObject *obj, if(argc>1) JS_ValueToInt32(cx,argv[1],(int32*)&timeout); + rc=JS_SUSPENDREQUEST(cx); start=time(NULL); for(i=0;itimeout) { *rval = JSVAL_NULL; + JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); /* time-out */ } continue; /* no data */ @@ -341,6 +334,7 @@ js_readln(JSContext *cx, JSObject *obj, buf[i-1]=0; else buf[i]=0; + JS_RESUMEREQUEST(cx, rc); str = JS_NewStringCopyZ(cx, buf); if(str==NULL) @@ -358,6 +352,7 @@ js_write(JSContext *cx, JSObject *obj, u char* cp; JSString* str; service_client_t* client; + jsrefcount rc; if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) return(JS_FALSE); @@ -369,7 +364,9 @@ js_write(JSContext *cx, JSObject *obj, u continue; if((cp=JS_GetStringBytes(str))==NULL) continue; + rc=JS_SUSPENDREQUEST(cx); sendsocket(client->socket,cp,strlen(cp)); + JS_RESUMEREQUEST(cx, rc); } return(JS_TRUE); @@ -380,14 +377,17 @@ js_writeln(JSContext *cx, JSObject *obj, { char* cp; service_client_t* client; + jsrefcount rc; if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) return(JS_FALSE); js_write(cx,obj,argc,argv,rval); + rc=JS_SUSPENDREQUEST(cx); cp="\r\n"; sendsocket(client->socket,cp,2); + JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); } @@ -400,6 +400,7 @@ js_log(JSContext *cx, JSObject *obj, uin int32 level=LOG_INFO; JSString* js_str; service_client_t* client; + jsrefcount rc; if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) return(JS_FALSE); @@ -418,95 +419,127 @@ js_log(JSContext *cx, JSObject *obj, uin strcat(str," "); } + rc=JS_SUSPENDREQUEST(cx); if(service==NULL) lprintf(level,"%04d %s",client->socket,str); else if(level <= client->service->log_level) lprintf(level,"%04d %s %s",client->socket,client->service->protocol,str); + JS_RESUMEREQUEST(cx, rc); *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)); return(JS_TRUE); } +static void badlogin(SOCKET sock, char* prot, char* user, char* passwd, char* host, SOCKADDR_IN* addr) +{ + char reason[128]; + ulong count; + + SAFEPRINTF(reason,"%s LOGIN", prot); + count=loginFailure(startup->login_attempt_list, addr, prot, user, passwd); + if(startup->login_attempt_hack_threshold && count>=startup->login_attempt_hack_threshold) + hacklog(&scfg, reason, user, passwd, host, addr); + if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold) + filter_ip(&scfg, prot, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS" + ,host, inet_ntoa(addr->sin_addr), user, /* fname: */NULL); + + mswait(startup->login_attempt_delay); +} + static JSBool js_login(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - char* p; + char* user; + char* pass; JSBool inc_logons=JS_FALSE; - user_t user; jsval val; - JSString* js_str; service_client_t* client; + jsrefcount rc; *rval = BOOLEAN_TO_JSVAL(JS_FALSE); if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) return(JS_FALSE); - /* User name */ - if((js_str=JS_ValueToString(cx, argv[0]))==NULL) + /* User name or number */ + if((user=js_ValueToStringBytes(cx, argv[0], NULL))==NULL) return(JS_FALSE); - if((p=JS_GetStringBytes(js_str))==NULL) + /* Password */ + if((pass=js_ValueToStringBytes(cx, argv[1], NULL))==NULL) return(JS_FALSE); - memset(&user,0,sizeof(user)); + rc=JS_SUSPENDREQUEST(cx); + memset(&client->user,0,sizeof(user_t)); - if(isdigit(*p)) - user.number=atoi(p); - else if(*p) - user.number=matchuser(&scfg,p,FALSE); + /* ToDo Deuce: did you mean to do this *before* the above memset(0) ? */ + if(client->user.number) { + if(client->subscan!=NULL) + putmsgptrs(&scfg, client->user.number, client->subscan); + } + + if(isdigit(*user)) + client->user.number=atoi(user); + else if(*user) + client->user.number=matchuser(&scfg,user,FALSE); - if(getuserdat(&scfg,&user)!=0) { + if(getuserdat(&scfg,&client->user)!=0) { lprintf(LOG_NOTICE,"%04d %s !USER NOT FOUND: '%s'" - ,client->socket,client->service->protocol,p); + ,client->socket,client->service->protocol,user); + badlogin(client->socket, client->service->protocol, user, pass, client->client->host, &client->addr); + JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); } - if(user.misc&(DELETED|INACTIVE)) { + if(client->user.misc&(DELETED|INACTIVE)) { lprintf(LOG_WARNING,"%04d %s !DELETED OR INACTIVE USER #%d: %s" - ,client->socket,client->service->protocol,user.number,p); + ,client->socket,client->service->protocol,client->user.number,user); + JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); } /* Password */ - if(user.pass[0]) { - if((js_str=JS_ValueToString(cx, argv[1]))==NULL) - return(JS_FALSE); - - if((p=JS_GetStringBytes(js_str))==NULL) - return(JS_FALSE); - - if(stricmp(user.pass,p)) { /* Wrong password */ - lprintf(LOG_WARNING,"%04d %s !INVALID PASSWORD ATTEMPT FOR USER: %s" - ,client->socket,client->service->protocol,user.alias); - return(JS_TRUE); - } + if(client->user.pass[0] && stricmp(client->user.pass,pass)) { /* Wrong password */ + lprintf(LOG_WARNING,"%04d %s !INVALID PASSWORD ATTEMPT FOR USER: %s" + ,client->socket,client->service->protocol,client->user.alias); + badlogin(client->socket, client->service->protocol, user, pass, client->client->host, &client->addr); + JS_RESUMEREQUEST(cx, rc); + return(JS_TRUE); } + JS_RESUMEREQUEST(cx, rc); if(argc>2) JS_ValueToBoolean(cx,argv[2],&inc_logons); + rc=JS_SUSPENDREQUEST(cx); if(client->client!=NULL) { - SAFECOPY(user.note,client->client->addr); - SAFECOPY(user.comp,client->client->host); - SAFECOPY(user.modem,client->service->protocol); + SAFECOPY(client->user.note,client->client->addr); + SAFECOPY(client->user.comp,client->client->host); + SAFECOPY(client->user.modem,client->service->protocol); } if(inc_logons) { - user.logons++; - user.ltoday++; + client->user.logons++; + client->user.ltoday++; } - putuserdat(&scfg,&user); + putuserdat(&scfg,&client->user); + if(client->subscan==NULL) { + client->subscan=(subscan_t*)malloc(sizeof(subscan_t)*scfg.total_subs); + if(client->subscan==NULL) + lprintf(LOG_CRIT,"!MALLOC FAILURE"); + } + if(client->subscan!=NULL) { + getmsgptrs(&scfg,client->user.number,client->subscan); + } + + JS_RESUMEREQUEST(cx, rc); - /* user-specific objects */ - if(!js_CreateUserObjects(cx, obj, &scfg, &user, NULL, NULL)) + if(!js_CreateUserObjects(cx, obj, &scfg, &client->user, client->client, NULL, client->subscan)) lprintf(LOG_ERR,"%04d %s !JavaScript ERROR creating user objects" ,client->socket,client->service->protocol); - memcpy(&client->user,&user,sizeof(user)); - if(client->client!=NULL) { client->client->user=client->user.alias; client_on(client->socket,client->client,TRUE /* update */); @@ -520,6 +553,9 @@ js_login(JSContext *cx, JSObject *obj, u val = BOOLEAN_TO_JSVAL(JS_TRUE); JS_SetProperty(cx, obj, "logged_in", &val); + if(client->user.pass[0]) + loginSuccess(startup->login_attempt_list, &client->addr); + *rval=BOOLEAN_TO_JSVAL(JS_TRUE); return(JS_TRUE); @@ -530,6 +566,7 @@ js_logout(JSContext *cx, JSObject *obj, { jsval val; service_client_t* client; + jsrefcount rc; *rval = BOOLEAN_TO_JSVAL(JS_FALSE); @@ -539,12 +576,15 @@ js_logout(JSContext *cx, JSObject *obj, if(client->user.number<1) /* Not logged in */ return(JS_TRUE); - logoutuserdat(&scfg,&client->user,time(NULL),client->logintime); + rc=JS_SUSPENDREQUEST(cx); + if(!logoutuserdat(&scfg,&client->user,time(NULL),client->logintime)) + lprintf(LOG_ERR,"%04d !ERROR in logoutuserdat",client->socket); lprintf(LOG_INFO,"%04d %s Logging out %s" ,client->socket,client->service->protocol,client->user.alias); memset(&client->user,0,sizeof(client->user)); + JS_RESUMEREQUEST(cx, rc); val = BOOLEAN_TO_JSVAL(JS_FALSE); JS_SetProperty(cx, obj, "logged_in", &val); @@ -575,6 +615,8 @@ js_ErrorReporter(JSContext *cx, const ch SOCKET sock=0; char* warning; service_client_t* client; + jsrefcount rc; + int log_level; if((client=(service_client_t*)JS_GetContextPrivate(cx))!=NULL) { prot=client->service->protocol; @@ -601,72 +643,17 @@ js_ErrorReporter(JSContext *cx, const ch warning="strict warning"; else warning="warning"; - } else + log_level=LOG_WARNING; + } else { + log_level=LOG_ERR; warning=""; - - lprintf(LOG_ERR,"%04d %s !JavaScript %s%s%s: %s",sock,prot,warning,file,line,message); -} - -#if 0 - -/* Server Object Properites */ -enum { - SERVER_PROP_TERMINATED - ,SERVER_PROP_CLIENTS -}; - - -static JSBool js_server_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp) -{ - jsint tiny; - service_client_t* client; - - if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) - return(JS_FALSE); - - tiny = JSVAL_TO_INT(id); - - switch(tiny) { - case SERVER_PROP_TERMINATED: -#if 0 - lprintf(LOG_DEBUG,"%s client->service->terminated=%d" - ,client->service->protocol, client->service->terminated); -#endif - *vp = BOOLEAN_TO_JSVAL(client->service->terminated); - break; - case SERVER_PROP_CLIENTS: - *vp = INT_TO_JSVAL(active_clients()); - break; } - return(JS_TRUE); + rc=JS_SUSPENDREQUEST(cx); + lprintf(log_level,"%04d %s !JavaScript %s%s%s: %s",sock,prot,warning,file,line,message); + JS_RESUMEREQUEST(cx, rc); } -#define SERVER_PROP_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY - -static struct JSPropertySpec js_server_properties[] = { -/* name ,tinyid ,flags, getter, setter */ - - { "terminated" ,SERVER_PROP_TERMINATED ,SERVER_PROP_FLAGS, NULL,NULL}, - { "clients" ,SERVER_PROP_CLIENTS ,SERVER_PROP_FLAGS, NULL,NULL}, - {0} -}; - -static JSClass js_server_class = { - "Server" /* name */ - ,0 /* flags */ - ,JS_PropertyStub /* addProperty */ - ,JS_PropertyStub /* delProperty */ - ,js_server_get /* getProperty */ - ,JS_PropertyStub /* setProperty */ - ,JS_EnumerateStub /* enumerate */ - ,JS_ResolveStub /* resolve */ - ,JS_ConvertStub /* convert */ - ,JS_FinalizeStub /* finalize */ -}; - -#endif - /* Server Methods */ static JSBool @@ -677,6 +664,7 @@ js_client_add(JSContext *cx, JSObject *o socklen_t addr_len; SOCKADDR_IN addr; service_client_t* service_client; + jsrefcount rc; if((service_client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) return(JS_FALSE); @@ -706,12 +694,14 @@ js_client_add(JSContext *cx, JSObject *o if(argc>2) SAFECOPY(client.host,JS_GetStringBytes(JS_ValueToString(cx,argv[2]))); + rc=JS_SUSPENDREQUEST(cx); client_on(sock, &client, /* update? */ FALSE); #ifdef _DEBUG lprintf(LOG_DEBUG,"%04d %s client_add(%04u,%s,%s)" ,service_client->service->socket,service_client->service->protocol ,sock,client.user,client.host); #endif + JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); } @@ -723,6 +713,7 @@ js_client_update(JSContext *cx, JSObject socklen_t addr_len; SOCKADDR_IN addr; service_client_t* service_client; + jsrefcount rc; if((service_client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) return(JS_FALSE); @@ -747,12 +738,14 @@ js_client_update(JSContext *cx, JSObject if(argc>2) SAFECOPY(client.host,JS_GetStringBytes(JS_ValueToString(cx,argv[2]))); + rc=JS_SUSPENDREQUEST(cx); client_on(sock, &client, /* update? */ TRUE); #ifdef _DEBUG lprintf(LOG_DEBUG,"%04d %s client_update(%04u,%s,%s)" ,service_client->service->socket,service_client->service->protocol ,sock,client.user,client.host); #endif + JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); } @@ -762,6 +755,7 @@ js_client_remove(JSContext *cx, JSObject { SOCKET sock=INVALID_SOCKET; service_client_t* service_client; + jsrefcount rc; if((service_client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) return(JS_FALSE); @@ -770,6 +764,7 @@ js_client_remove(JSContext *cx, JSObject if(sock!=INVALID_SOCKET) { + rc=JS_SUSPENDREQUEST(cx); client_off(sock); if(service_client->service->clients==0) @@ -779,6 +774,7 @@ js_client_remove(JSContext *cx, JSObject service_client->service->clients--; update_clients(); } + JS_RESUMEREQUEST(cx, rc); } #ifdef _DEBUG @@ -799,21 +795,24 @@ js_initcx(JSRuntime* js_runtime, SOCKET if((js_cx = JS_NewContext(js_runtime, service_client->service->js.cx_stack))==NULL) return(NULL); + JS_BEGINREQUEST(js_cx); JS_SetErrorReporter(js_cx, js_ErrorReporter); + /* ToDo: call js_CreateCommonObjects() instead */ + do { JS_SetContextPrivate(js_cx, service_client); - if((js_glob=js_CreateGlobalObject(js_cx, &scfg, NULL))==NULL) + if((js_glob=js_CreateGlobalObject(js_cx, &scfg, NULL, &service_client->service->js))==NULL) break; if (!JS_DefineFunctions(js_cx, js_glob, js_global_functions)) break; /* Internal JS Object */ - if(js_CreateInternalJsObject(js_cx, js_glob, &service_client->branch)==NULL) + if(js_CreateInternalJsObject(js_cx, js_glob, &service_client->branch, &service_client->service->js)==NULL) break; /* Client Object */ @@ -837,8 +836,16 @@ js_initcx(JSRuntime* js_runtime, SOCKET if(js_CreateFileClass(js_cx, js_glob)==NULL) break; + /* Queue Class */ + if(js_CreateQueueClass(js_cx, js_glob)==NULL) + break; + + /* COM Class */ + if(js_CreateCOMClass(js_cx, js_glob)==NULL) + break; + /* user-specific objects */ - if(!js_CreateUserObjects(js_cx, js_glob, &scfg, NULL, NULL, NULL)) + if(!js_CreateUserObjects(js_cx, js_glob, &scfg, /*user: */NULL, service_client->client, NULL, service_client->subscan)) break; if(js_CreateSystemObject(js_cx, js_glob, &scfg, uptime, startup->host_name, SOCKLIB_DESC)==NULL) @@ -916,6 +923,7 @@ js_initcx(JSRuntime* js_runtime, SOCKET if(!success) { + JS_ENDREQUEST(js_cx); JS_DestroyContext(js_cx); return(NULL); } @@ -932,8 +940,8 @@ js_BranchCallback(JSContext *cx, JSScrip return(JS_FALSE); /* Terminated? */ - if(terminated) { - JS_ReportError(cx,"Terminated"); + if(client->branch.auto_terminate && terminated) { + JS_ReportWarning(cx,"Terminated"); client->branch.counter=0; return(JS_FALSE); } @@ -941,6 +949,19 @@ js_BranchCallback(JSContext *cx, JSScrip return js_CommonBranchCallback(cx,&client->branch); } +#ifdef USE_JS_OPERATION_CALLBACK +static JSBool +js_OperationCallback(JSContext *cx) +{ + JSBool ret; + + JS_SetOperationCallback(cx, NULL); + ret=js_BranchCallback(cx, NULL); + JS_SetOperationCallback(cx, js_OperationCallback); + return ret; +} +#endif + static void js_init_args(JSContext* js_cx, JSObject* js_obj, const char* cmdline) { char argbuf[MAX_PATH+1]; @@ -983,13 +1004,13 @@ static void js_init_args(JSContext* js_c static void js_service_thread(void* arg) { - int i; char* host_name; HOSTENT* host; SOCKET socket; client_t client; service_t* service; service_client_t service_client; + ulong login_attempts; /* JavaScript-specific */ char spath[MAX_PATH+1]; char fname[MAX_PATH+1]; @@ -1011,7 +1032,7 @@ static void js_service_thread(void* arg) lprintf(LOG_DEBUG,"%04d %s JavaScript service thread started", socket, service->protocol); - SetThreadName("JS Service Thread"); + SetThreadName("JS Service"); thread_up(TRUE /* setuid */); /* Host name lookup and filtering */ @@ -1031,10 +1052,13 @@ static void js_service_thread(void* arg) && !(startup->options&BBS_OPT_NO_HOST_LOOKUP)) { lprintf(LOG_INFO,"%04d %s Hostname: %s" ,socket, service->protocol, host_name); +#if 0 /* gethostbyaddr() is apparently not (always) thread-safe + and getnameinfo() doesn't return alias information */ for(i=0;host!=NULL && host->h_aliases!=NULL && host->h_aliases[i]!=NULL;i++) lprintf(LOG_INFO,"%04d %s HostAlias: %s" ,socket, service->protocol, host->h_aliases[i]); +#endif } if(trashcan(&scfg,host_name,"host")) { @@ -1075,7 +1099,7 @@ static void js_service_thread(void* arg) /* Initialize client display */ client_on(socket,&client,FALSE /* update */); - if((js_runtime=JS_NewRuntime(service->js.max_bytes))==NULL + if((js_runtime=jsrt_GetNew(service->js.max_bytes, 5000, __FILE__, __LINE__))==NULL || (js_cx=js_initcx(js_runtime,socket,&service_client,&js_glob))==NULL) { lprintf(LOG_ERR,"%04d !%s ERROR initializing JavaScript context" ,socket,service->protocol); @@ -1089,6 +1113,13 @@ static void js_service_thread(void* arg) update_clients(); + if(startup->login_attempt_throttle + && (login_attempts=loginAttempts(startup->login_attempt_list, &service_client.addr)) > 1) { + lprintf(LOG_DEBUG,"%04d %s Throttling suspicious connection from: %s (%u login attempts)" + ,socket, service->protocol, inet_ntoa(service_client.addr.sin_addr), login_attempts); + mswait(login_attempts*startup->login_attempt_throttle); + } + /* RUN SCRIPT */ SAFECOPY(fname,service->cmd); truncstr(fname," "); @@ -1121,20 +1152,29 @@ static void js_service_thread(void* arg) if(js_script==NULL) lprintf(LOG_ERR,"%04d !JavaScript FAILED to compile script (%s)",socket,spath); else { + js_PrepareToExecute(js_cx, js_glob, spath, /* startup_dir */NULL); +#ifdef USE_JS_OPERATION_CALLBACK + JS_SetOperationCallback(js_cx, js_OperationCallback); +#else JS_SetBranchCallback(js_cx, js_BranchCallback); +#endif JS_ExecuteScript(js_cx, js_glob, js_script, &rval); js_EvalOnExit(js_cx, js_glob, &service_client.branch); JS_DestroyScript(js_cx, js_script); } + JS_ENDREQUEST(js_cx); JS_DestroyContext(js_cx); /* Free Context */ - JS_DestroyRuntime(js_runtime); + jsrt_Release(js_runtime); if(service_client.user.number) { + if(service_client.subscan!=NULL) + putmsgptrs(&scfg, service_client.user.number, service_client.subscan); lprintf(LOG_INFO,"%04d %s Logging out %s" ,socket, service->protocol, service_client.user.alias); logoutuserdat(&scfg,&service_client.user,time(NULL),service_client.logintime); } + FREE_AND_NULL(service_client.subscan); if(service->clients) service->clients--; @@ -1147,8 +1187,8 @@ static void js_service_thread(void* arg) #endif thread_down(); - lprintf(LOG_DEBUG,"%04d %s JavaScript service thread terminated (%u clients remain, %d total, %lu served)" - , socket, service->protocol, service->clients, active_clients(), service->served); + lprintf(LOG_INFO,"%04d %s service thread terminated (%u clients remain, %d total, %lu served)" + ,socket, service->protocol, service->clients, active_clients(), service->served); client_off(socket); close_socket(socket); @@ -1177,7 +1217,7 @@ static void js_static_service_thread(voi lprintf(LOG_DEBUG,"%04d %s static JavaScript service thread started", service->socket, service->protocol); - SetThreadName("JS Static Service Thread"); + SetThreadName("JS Static Service"); thread_up(TRUE /* setuid */); memset(&service_client,0,sizeof(service_client)); @@ -1189,7 +1229,7 @@ static void js_static_service_thread(voi service_client.branch.terminated = &service->terminated; service_client.branch.auto_terminate = TRUE; - if((js_runtime=JS_NewRuntime(service->js.max_bytes))==NULL) { + if((js_runtime=jsrt_GetNew(service->js.max_bytes, 5000, __FILE__, __LINE__))==NULL) { lprintf(LOG_ERR,"%04d !%s ERROR initializing JavaScript runtime" ,service->socket,service->protocol); close_socket(service->socket); @@ -1216,25 +1256,33 @@ static void js_static_service_thread(voi val = BOOLEAN_TO_JSVAL(JS_FALSE); JS_SetProperty(js_cx, js_glob, "logged_in", &val); +#ifdef USE_JS_OPERATION_CALLBACK + JS_SetOperationCallback(js_cx, js_OperationCallback); +#else JS_SetBranchCallback(js_cx, js_BranchCallback); +#endif if((js_script=JS_CompileFile(js_cx, js_glob, spath))==NULL) { lprintf(LOG_ERR,"%04d !JavaScript FAILED to compile script (%s)",service->socket,spath); break; } + js_PrepareToExecute(js_cx, js_glob, spath, /* startup_dir */NULL); JS_ExecuteScript(js_cx, js_glob, js_script, &rval); js_EvalOnExit(js_cx, js_glob, &service_client.branch); JS_DestroyScript(js_cx, js_script); + JS_ENDREQUEST(js_cx); JS_DestroyContext(js_cx); /* Free Context */ js_cx=NULL; } while(!service->terminated && service->options&SERVICE_OPT_STATIC_LOOP); - if(js_cx!=NULL) + if(js_cx!=NULL) { + JS_ENDREQUEST(js_cx); JS_DestroyContext(js_cx); /* Free Context */ + } - JS_DestroyRuntime(js_runtime); + jsrt_Release(js_runtime); if(service->clients) { lprintf(LOG_WARNING,"%04d %s !service terminating with %u active clients" @@ -1243,7 +1291,7 @@ static void js_static_service_thread(voi } thread_down(); - lprintf(LOG_DEBUG,"%04d %s static JavaScript service thread terminated (%lu clients served)" + lprintf(LOG_INFO,"%04d %s service thread terminated (%lu clients served)" ,socket, service->protocol, service->served); close_socket(service->socket); @@ -1267,7 +1315,7 @@ static void native_static_service_thread lprintf(LOG_DEBUG,"%04d %s static service thread started", socket, service->protocol); - SetThreadName("Native Static Service Thread"); + SetThreadName("Static Service"); thread_up(TRUE /* setuid */); #ifdef _WIN32 @@ -1301,7 +1349,7 @@ static void native_static_service_thread } while(!service->terminated && service->options&SERVICE_OPT_STATIC_LOOP); thread_down(); - lprintf(LOG_DEBUG,"%04d %s static service thread terminated (%lu clients served)" + lprintf(LOG_INFO,"%04d %s service thread terminated (%lu clients served)" ,socket, service->protocol, service->served); close_socket(service->socket); @@ -1313,7 +1361,6 @@ static void native_static_service_thread static void native_service_thread(void* arg) { - int i; char cmd[MAX_PATH]; char fullcmd[MAX_PATH*2]; char* host_name; @@ -1323,6 +1370,7 @@ static void native_service_thread(void* client_t client; service_t* service; service_client_t service_client=*(service_client_t*)arg; + ulong login_attempts; free(arg); @@ -1331,7 +1379,7 @@ static void native_service_thread(void* lprintf(LOG_DEBUG,"%04d %s service thread started", socket, service->protocol); - SetThreadName("Native Service Thread"); + SetThreadName("Native Service"); thread_up(TRUE /* setuid */); /* Host name lookup and filtering */ @@ -1351,10 +1399,13 @@ static void native_service_thread(void* && !(startup->options&BBS_OPT_NO_HOST_LOOKUP)) { lprintf(LOG_INFO,"%04d %s Hostname: %s" ,socket, service->protocol, host_name); +#if 0 /* gethostbyaddr() is apparently not (always) thread-safe + and getnameinfo() doesn't return alias information */ for(i=0;host!=NULL && host->h_aliases!=NULL && host->h_aliases[i]!=NULL;i++) lprintf(LOG_INFO,"%04d %s HostAlias: %s" ,socket, service->protocol, host->h_aliases[i]); +#endif } if(trashcan(&scfg,host_name,"host")) { @@ -1414,6 +1465,13 @@ static void native_service_thread(void* /* Initialize client display */ client_on(socket,&client,FALSE /* update */); + if(startup->login_attempt_throttle + && (login_attempts=loginAttempts(startup->login_attempt_list, &service_client.addr)) > 1) { + lprintf(LOG_DEBUG,"%04d %s Throttling suspicious connection from: %s (%u login attempts)" + ,socket, service->protocol, inet_ntoa(service_client.addr.sin_addr), login_attempts); + mswait(login_attempts*startup->login_attempt_throttle); + } + /* RUN SCRIPT */ if(strpbrk(service->cmd,"/\\")==NULL) sprintf(cmd,"%s%s",scfg.exec_dir,service->cmd); @@ -1434,7 +1492,7 @@ static void native_service_thread(void* #endif thread_down(); - lprintf(LOG_DEBUG,"%04d %s service thread terminated (%u clients remain, %d total, %lu served)" + lprintf(LOG_INFO,"%04d %s service thread terminated (%u clients remain, %d total, %lu served)" ,socket, service->protocol, service->clients, active_clients(), service->served); client_off(socket); @@ -1445,7 +1503,7 @@ static void native_service_thread(void* void DLLCALL services_terminate(void) { - DWORD i; + uint32_t i; lprintf(LOG_INFO,"0000 Services terminate"); terminated=TRUE; @@ -1455,7 +1513,7 @@ void DLLCALL services_terminate(void) #define NEXT_FIELD(p) FIND_WHITESPACE(p); SKIP_WHITESPACE(p) -static service_t* read_services_ini(service_t* service, DWORD* services) +static service_t* read_services_ini(const char* services_ini, service_t* service, uint32_t* services) { uint i,j; FILE* fp; @@ -1463,39 +1521,67 @@ static service_t* read_services_ini(serv char cmd[INI_MAX_VALUE_LEN]; char host[INI_MAX_VALUE_LEN]; char prot[INI_MAX_VALUE_LEN]; - char services_ini[MAX_PATH+1]; + char portstr[INI_MAX_VALUE_LEN]; char** sec_list; str_list_t list; service_t* np; service_t serv; int log_level; - - iniFileName(services_ini,sizeof(services_ini),scfg.ctrl_dir,"services.ini"); + int listen_backlog; + uint max_clients; + uint32_t options; + uint32_t stack_size; if((fp=fopen(services_ini,"r"))==NULL) { - lprintf(LOG_ERR,"!ERROR %d opening %s", errno, services_ini); + lprintf(LOG_CRIT,"!ERROR %d opening %s", errno, services_ini); return(NULL); } - lprintf(LOG_INFO,"Reading %s",services_ini); + lprintf(LOG_DEBUG,"Reading %s",services_ini); list=iniReadFile(fp); fclose(fp); - log_level = iniGetLogLevel(list,ROOT_SECTION,"LogLevel",LOG_DEBUG); + /* Get default key values from "root" section */ + log_level = iniGetLogLevel(list,ROOT_SECTION,"LogLevel",startup->log_level); + stack_size = (uint32_t)iniGetBytes(list,ROOT_SECTION,"StackSize",1,0); + max_clients = iniGetInteger(list,ROOT_SECTION,"MaxClients",0); + listen_backlog = iniGetInteger(list,ROOT_SECTION,"ListenBacklog",DEFAULT_LISTEN_BACKLOG); + options = iniGetBitField(list,ROOT_SECTION,"Options",service_options,0); + + /* Enumerate and parse each service configuration */ sec_list = iniGetSectionList(list,""); for(i=0; sec_list!=NULL && sec_list[i]!=NULL; i++) { + if(!iniGetBool(list,sec_list[i],"Enabled",TRUE)) { + lprintf(LOG_WARNING,"Ignoring disabled service: %s",sec_list[i]); + continue; + } memset(&serv,0,sizeof(service_t)); SAFECOPY(serv.protocol,iniGetString(list,sec_list[i],"Protocol",sec_list[i],prot)); serv.socket=INVALID_SOCKET; serv.interface_addr=iniGetIpAddress(list,sec_list[i],"Interface",startup->interface_addr); - serv.port=iniGetShortInt(list,sec_list[i],"Port",0); - serv.max_clients=iniGetInteger(list,sec_list[i],"MaxClients",0); - serv.listen_backlog=iniGetInteger(list,sec_list[i],"ListenBacklog",DEFAULT_LISTEN_BACKLOG); - serv.stack_size=iniGetInteger(list,sec_list[i],"StackSize",0); - serv.options=iniGetBitField(list,sec_list[i],"Options",service_options,0); - serv.log_level = iniGetLogLevel(list,sec_list[i],"LogLevel",log_level); + serv.max_clients=iniGetInteger(list,sec_list[i],"MaxClients",max_clients); + serv.listen_backlog=iniGetInteger(list,sec_list[i],"ListenBacklog",listen_backlog); + serv.stack_size=(uint32_t)iniGetBytes(list,sec_list[i],"StackSize",1,stack_size); + serv.options=iniGetBitField(list,sec_list[i],"Options",service_options,options); + serv.log_level=iniGetLogLevel(list,sec_list[i],"LogLevel",log_level); SAFECOPY(serv.cmd,iniGetString(list,sec_list[i],"Command","",cmd)); + p=iniGetString(list,sec_list[i],"Port",serv.protocol,portstr); + if(isdigit(*p)) + serv.port=(ushort)strtol(p,NULL,0); + else { + struct servent* servent = getservbyname(p,serv.options&SERVICE_OPT_UDP ? "udp":"tcp"); + if(servent==NULL) + servent = getservbyname(p,serv.options&SERVICE_OPT_UDP ? "tcp":"udp"); + if(servent!=NULL) + serv.port = ntohs(servent->s_port); + } + + if(serv.port==0) { + lprintf(LOG_WARNING,"Ignoring service with invalid port (%s): %s",p, sec_list[i]); + continue; + } + if(serv.cmd[0]==0) { lprintf(LOG_WARNING,"Ignoring service with no command: %s",sec_list[i]); continue; @@ -1557,7 +1643,7 @@ static void cleanup(int code) thread_down(); if(terminated || code) - lprintf(LOG_DEBUG,"#### Services thread terminated (%lu clients served)",served); + lprintf(LOG_INFO,"#### Services thread terminated (%lu clients served)",served); status("Down"); if(startup!=NULL && startup->terminated!=NULL) startup->terminated(startup->cbdata,code); @@ -1570,7 +1656,7 @@ const char* DLLCALL services_ver(void) DESCRIBE_COMPILER(compiler); - sscanf("$Revision: 1.1.1.1 $", "%*s %s", revision); + sscanf("$Revision: 1.1.1.2 $", "%*s %s", revision); sprintf(ver,"Synchronet Services %s%s " "Compiled %s %s with %s" @@ -1594,6 +1680,7 @@ void DLLCALL services_thread(void* arg) char host_ip[32]; char compiler[32]; char str[128]; + char services_ini[MAX_PATH+1]; SOCKADDR_IN addr; SOCKADDR_IN client_addr; socklen_t client_addr_len; @@ -1646,7 +1733,7 @@ void DLLCALL services_thread(void* arg) startup->recycle_now=FALSE; startup->shutdown_now=FALSE; - SetThreadName("Services Thread"); + SetThreadName("Services"); do { @@ -1678,7 +1765,10 @@ void DLLCALL services_thread(void* arg) t=time(NULL); lprintf(LOG_INFO,"Initializing on %.24s with options: %lx" - ,CTIME_R(&t,str),startup->options); + ,ctime_r(&t,str),startup->options); + + if(chdir(startup->ctrl_dir)!=0) + lprintf(LOG_ERR,"!ERROR %d changing directory to: %s", errno, startup->ctrl_dir); /* Initial configuration and load from CNF files */ SAFECOPY(scfg.ctrl_dir, startup->ctrl_dir); @@ -1686,8 +1776,8 @@ void DLLCALL services_thread(void* arg) scfg.size=sizeof(scfg); SAFECOPY(error,UNKNOWN_LOAD_ERROR); if(!load_cfg(&scfg, NULL, TRUE, error)) { - lprintf(LOG_ERR,"!ERROR %s",error); - lprintf(LOG_ERR,"!Failed to load configuration files"); + lprintf(LOG_CRIT,"!ERROR %s",error); + lprintf(LOG_CRIT,"!Failed to load configuration files"); cleanup(1); return; } @@ -1700,7 +1790,7 @@ void DLLCALL services_thread(void* arg) MKDIR(scfg.temp_dir); lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir); if(!isdir(scfg.temp_dir)) { - lprintf(LOG_ERR,"!Invalid temp directory: %s", scfg.temp_dir); + lprintf(LOG_CRIT,"!Invalid temp directory: %s", scfg.temp_dir); cleanup(1); return; } @@ -1708,22 +1798,16 @@ void DLLCALL services_thread(void* arg) if(startup->host_name[0]==0) SAFECOPY(startup->host_name,scfg.sys_inetaddr); - if(!(scfg.sys_misc&SM_LOCAL_TZ) && !(startup->options&BBS_OPT_LOCAL_TIMEZONE)) { - if(putenv("TZ=UTC0")) - lprintf(LOG_ERR,"!putenv() FAILED"); - tzset(); - - if((t=checktime())!=0) { /* Check binary time */ - lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t); - cleanup(1); - return; - } + if((t=checktime())!=0) { /* Check binary time */ + lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t); } if(uptime==0) uptime=time(NULL); /* this must be done *after* setting the timezone */ - if((service=read_services_ini(service, &services))==NULL) { + iniFileName(services_ini,sizeof(services_ini),scfg.ctrl_dir,"services.ini"); + + if((service=read_services_ini(services_ini, service, &services))==NULL) { cleanup(1); return; } @@ -1740,7 +1824,7 @@ void DLLCALL services_thread(void* arg) (service[i].options&SERVICE_OPT_UDP) ? SOCK_DGRAM : SOCK_STREAM ,service[i].protocol)) ==INVALID_SOCKET) { - lprintf(LOG_ERR,"!ERROR %d opening %s socket" + lprintf(LOG_CRIT,"!ERROR %d opening %s socket" ,ERROR_VALUE, service[i].protocol); cleanup(1); return; @@ -1772,12 +1856,16 @@ void DLLCALL services_thread(void* arg) addr.sin_family = AF_INET; addr.sin_port = htons(service[i].port); - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); + if(service[i].port < IPPORT_RESERVED) { + if(startup->seteuid!=NULL) + startup->seteuid(FALSE); + } result=retry_bind(socket, (struct sockaddr *) &addr, sizeof(addr) ,startup->bind_retry_count, startup->bind_retry_delay, service[i].protocol, lprintf); - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); + if(service[i].port < IPPORT_RESERVED) { + if(startup->seteuid!=NULL) + startup->seteuid(TRUE); + } if(result!=0) { lprintf(LOG_ERR,"%04d %s",socket,BIND_FAILURE_HELP); close_socket(socket); @@ -1828,8 +1916,8 @@ void DLLCALL services_thread(void* arg) recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","services"); SAFEPRINTF(path,"%sservices.rec",scfg.ctrl_dir); /* legacy */ semfile_list_add(&recycle_semfiles,path); + semfile_list_add(&recycle_semfiles,services_ini); if(!initialized) { - initialized=time(NULL); semfile_list_check(&initialized,recycle_semfiles); semfile_list_check(&initialized,shutdown_semfiles); } @@ -1840,6 +1928,8 @@ void DLLCALL services_thread(void* arg) if(startup->started!=NULL) startup->started(startup->cbdata); + lprintf(LOG_INFO,"0000 Services thread started (%u service sockets bound)", total_sockets); + /* Main Server Loop */ while(!terminated) { @@ -1849,10 +1939,6 @@ void DLLCALL services_thread(void* arg) lprintf(LOG_INFO,"0000 Recycle semaphore file (%s) detected",p); break; } -#if 0 /* unused */ - if(startup->recycle_sem!=NULL && sem_trywait(&startup->recycle_sem)==0) - startup->recycle_now=TRUE; -#endif if(startup->recycle_now==TRUE) { lprintf(LOG_NOTICE,"0000 Recycle semaphore signaled"); startup->recycle_now=FALSE; @@ -1974,7 +2060,7 @@ void DLLCALL services_thread(void* arg) result=bind(client_socket, (struct sockaddr *) &addr, sizeof(addr)); if(result==SOCKET_ERROR) { /* Failed to re-bind to same port number, use user port */ - lprintf(LOG_ERR,"%04d %s ERROR %d re-binding socket to port %u failed, " + lprintf(LOG_NOTICE,"%04d %s ERROR %d re-binding socket to port %u failed, " "using user port" ,client_socket, service[i].protocol, ERROR_VALUE, service[i].port); addr.sin_port=0; @@ -2014,10 +2100,6 @@ void DLLCALL services_thread(void* arg) #endif continue; } - sockets++; -#if 0 /*def _DEBUG */ - lprintf(LOG_DEBUG,"%04d Socket opened (%d sockets in use)",client_socket,sockets); -#endif if(startup->socket_open!=NULL) /* Callback, increments socket counter */ startup->socket_open(startup->cbdata,TRUE); }