|
|
1.1 root 1: /* main.cpp */
2:
1.1.1.2 ! root 3: /* Synchronet terminal server thread and related functions */
1.1 root 4:
1.1.1.2 ! root 5: /* $Id: main.cpp,v 1.557-patched 2011/10/17 03:10:53 rswindell Exp $ */
1.1 root 6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
1.1.1.2 ! root 11: * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html *
1.1 root 12: * *
13: * This program is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU General Public License for more details: gpl.txt or *
18: * http://www.fsf.org/copyleft/gpl.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38: #include "sbbs.h"
39: #include "ident.h"
40: #include "telnet.h"
1.1.1.2 ! root 41: #include "netwrap.h"
! 42: #include "js_rtpool.h"
! 43: #include "js_request.h"
1.1 root 44:
45: #ifdef __unix__
46: #include <sys/un.h>
47: #ifndef SUN_LEN
48: #define SUN_LEN(su) \
49: (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
50: #endif
51: #endif
52:
53: //---------------------------------------------------------------------------
54:
1.1.1.2 ! root 55: #define TELNET_SERVER "Synchronet Terminal Server"
1.1 root 56: #define STATUS_WFC "Listening"
57:
58: #define TIMEOUT_THREAD_WAIT 60 // Seconds (was 15)
59: #define IO_THREAD_BUF_SIZE 20000 // Bytes
60:
61: // Globals
62: #ifdef _WIN32
63: HANDLE exec_mutex=NULL;
64: HINSTANCE hK32=NULL;
65:
66: #if defined(_DEBUG) && defined(_MSC_VER)
67: HANDLE debug_log=INVALID_HANDLE_VALUE;
68: _CrtMemState mem_chkpoint;
69: #endif // _DEBUG && _MSC_VER
70:
71: #endif // _WIN32
72:
73: #ifdef USE_CRYPTLIB
74: #define SSH_END() if(ssh) cryptDestroySession(sbbs->ssh_session);
75: #else
76: #define SSH_END()
77: #endif
78:
1.1.1.2 ! root 79: volatile time_t uptime=0;
! 80: volatile ulong served=0;
1.1 root 81:
1.1.1.2 ! root 82: static protected_uint32_t node_threads_running;
1.1 root 83:
84: char lastuseron[LEN_ALIAS+1]; /* Name of user last online */
85: RingBuf* node_inbuf[MAX_NODES];
86: SOCKET spy_socket[MAX_NODES];
87: #ifdef __unix__
88: SOCKET uspy_socket[MAX_NODES]; /* UNIX domain spy sockets */
89: #endif
90: SOCKET node_socket[MAX_NODES];
91: static SOCKET telnet_socket=INVALID_SOCKET;
92: static SOCKET rlogin_socket=INVALID_SOCKET;
93: #ifdef USE_CRYPTLIB
94: static SOCKET ssh_socket=INVALID_SOCKET;
95: #endif
96: static sbbs_t* sbbs=NULL;
97: static scfg_t scfg;
98: static char * text[TOTAL_TEXT];
99: static WORD first_node;
100: static WORD last_node;
101: static bool terminate_server=false;
102: static str_list_t recycle_semfiles;
103: static str_list_t shutdown_semfiles;
104: #ifdef _THREAD_SUID_BROKEN
105: int thread_suid_broken=TRUE; /* NPTL is no longer broken */
106: #endif
107:
108: extern "C" {
109:
110: static bbs_startup_t* startup=NULL;
111:
1.1.1.2 ! root 112: static const char* status(const char* str)
1.1 root 113: {
114: if(startup!=NULL && startup->status!=NULL)
115: startup->status(startup->cbdata,str);
1.1.1.2 ! root 116: return str;
1.1 root 117: }
118:
119: static void update_clients()
120: {
121: if(startup!=NULL && startup->clients!=NULL)
1.1.1.2 ! root 122: startup->clients(startup->cbdata,node_threads_running.value);
1.1 root 123: }
124:
125: void client_on(SOCKET sock, client_t* client, BOOL update)
126: {
127: if(startup!=NULL && startup->client_on!=NULL)
128: startup->client_on(startup->cbdata,TRUE,sock,client,update);
129: }
130:
131: static void client_off(SOCKET sock)
132: {
133: if(startup!=NULL && startup->client_on!=NULL)
134: startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
135: }
136:
137: static void thread_up(BOOL setuid)
138: {
139: if(startup!=NULL && startup->thread_up!=NULL)
140: startup->thread_up(startup->cbdata,TRUE,setuid);
141: }
142:
143: static void thread_down()
144: {
145: if(startup!=NULL && startup->thread_up!=NULL)
146: startup->thread_up(startup->cbdata,FALSE,FALSE);
147: }
148:
1.1.1.2 ! root 149: int lputs(int level, const char* str)
1.1 root 150: {
1.1.1.2 ! root 151: if(level <= LOG_ERR) {
! 152: errorlog(&scfg,startup==NULL ? NULL:startup->host_name, str);
! 153: if(startup!=NULL && startup->errormsg!=NULL)
! 154: startup->errormsg(startup->cbdata,level,str);
! 155: }
! 156:
! 157: if(startup==NULL || startup->lputs==NULL || str==NULL || level > startup->log_level)
1.1 root 158: return(0);
159:
1.1.1.2 ! root 160: #if defined(_WIN32)
! 161: if(IsBadCodePtr((FARPROC)startup->lputs))
! 162: return(0);
! 163: #endif
! 164:
1.1 root 165: return(startup->lputs(startup->cbdata,level,str));
166: }
167:
1.1.1.2 ! root 168: int lprintf(int level, const char *fmt, ...)
1.1 root 169: {
170: va_list argptr;
171: char sbuf[1024];
172:
173: va_start(argptr,fmt);
174: vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
175: sbuf[sizeof(sbuf)-1]=0;
176: va_end(argptr);
177: return(lputs(level,sbuf));
178: }
179:
1.1.1.2 ! root 180: int eprintf(int level, const char *fmt, ...)
1.1 root 181: {
182: va_list argptr;
183: char sbuf[1024];
184:
185: va_start(argptr,fmt);
186: vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
187: sbuf[sizeof(sbuf)-1]=0;
188: va_end(argptr);
1.1.1.2 ! root 189:
! 190: if(level <= LOG_ERR) {
! 191: errorlog(&scfg,startup==NULL ? NULL:startup->host_name, sbuf);
! 192: if(startup!=NULL && startup->errormsg!=NULL)
! 193: startup->errormsg(startup->cbdata,level,sbuf);
! 194: }
! 195:
! 196: if(startup==NULL || startup->event_lputs==NULL || level > startup->log_level)
! 197: return(0);
! 198:
! 199: strip_ctrl(sbuf, sbuf);
! 200: return(startup->event_lputs(startup->event_cbdata,level,sbuf));
1.1 root 201: }
202:
203: SOCKET open_socket(int type, const char* protocol)
204: {
205: SOCKET sock;
206: char error[256];
207:
208: sock=socket(AF_INET, type, IPPROTO_IP);
209: if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
210: startup->socket_open(startup->cbdata,TRUE);
211: if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
212: lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
213:
214: return(sock);
215: }
216:
217: SOCKET accept_socket(SOCKET s, SOCKADDR* addr, socklen_t* addrlen)
218: {
219: SOCKET sock;
220:
221: sock=accept(s,addr,addrlen);
222: if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
223: startup->socket_open(startup->cbdata,TRUE);
224:
225: return(sock);
226: }
227:
228: int close_socket(SOCKET sock)
229: {
230: int result;
231:
232: if(sock==INVALID_SOCKET || sock==0)
233: return(0);
234:
235: shutdown(sock,SHUT_RDWR); /* required on Unix */
236: result=closesocket(sock);
237: if(startup!=NULL && startup->socket_open!=NULL)
238: startup->socket_open(startup->cbdata,FALSE);
239: if(result!=0 && ERROR_VALUE!=ENOTSOCK)
1.1.1.2 ! root 240: lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
1.1 root 241: return(result);
242: }
243:
244:
245: u_long resolve_ip(char *addr)
246: {
247: HOSTENT* host;
248: char* p;
249:
250: if(*addr==0)
251: return((u_long)INADDR_NONE);
252:
253: for(p=addr;*p;p++)
254: if(*p!='.' && !isdigit(*p))
255: break;
256: if(!(*p))
257: return(inet_addr(addr));
258: if((host=gethostbyname(addr))==NULL)
259: return((u_long)INADDR_NONE);
260: return(*((ulong*)host->h_addr_list[0]));
261: }
262:
263: } /* extern "C" */
264:
265: #ifdef _WINSOCKAPI_
266:
267: WSADATA WSAData;
268: #define SOCKLIB_DESC WSAData.szDescription
269: static BOOL WSAInitialized=FALSE;
270:
271: static BOOL winsock_startup(void)
272: {
273: int status; /* Status Code */
274:
275: if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
1.1.1.2 ! root 276: lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
1.1 root 277: WSAInitialized=TRUE;
278: return(TRUE);
279: }
280:
1.1.1.2 ! root 281: lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
1.1 root 282: return(FALSE);
283: }
284:
285: #else /* No WINSOCK */
286:
287: #define winsock_startup() (TRUE)
288: #define SOCKLIB_DESC NULL
289:
290: #endif
291:
292: DLLEXPORT void DLLCALL sbbs_srand()
293: {
1.1.1.2 ! root 294: DWORD seed;
1.1 root 295:
1.1.1.2 ! root 296: xp_randomize();
1.1 root 297: #if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
298: int rf;
299:
300: if((rf=open(RANDOM_DEV, O_RDONLY))!=-1) {
301: read(rf, &seed, sizeof(seed));
302: close(rf);
303: }
1.1.1.2 ! root 304: #else
! 305: seed = time(NULL) ^ (DWORD)GetCurrentThreadId();
1.1 root 306: #endif
307:
308: srand(seed);
309: sbbs_random(10); /* Throw away first number */
310: }
311:
312: int DLLCALL sbbs_random(int n)
313: {
314: return(xp_random(n));
315: }
316:
317: #ifdef JAVASCRIPT
318:
319: static js_server_props_t js_server_props;
320:
321: JSBool
322: DLLCALL js_CreateArrayOfStrings(JSContext* cx, JSObject* parent, const char* name, char* str[],uintN flags)
323: {
324: JSObject* array;
325: JSString* js_str;
326: jsval val;
327: size_t i;
328: jsuint len=0;
329:
330: if(JS_GetProperty(cx,parent,name,&val) && val!=JSVAL_VOID)
331: array=JSVAL_TO_OBJECT(val);
332: else
333: if((array=JS_NewArrayObject(cx, 0, NULL))==NULL) /* Assertion here, in _heap_alloc_dbg, June-21-2004 */
334: return(JS_FALSE); /* Caused by nntpservice.js? */
335:
336: if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
337: ,NULL,NULL,flags))
338: return(JS_FALSE);
339:
340: if(!JS_GetArrayLength(cx, array, &len))
341: return(JS_FALSE);
342:
343: for(i=0;str[i]!=NULL;i++) {
344: if((js_str = JS_NewStringCopyZ(cx, str[i]))==NULL)
345: break;
346: val = STRING_TO_JSVAL(js_str);
347: if(!JS_SetElement(cx, array, len+i, &val))
348: break;
349: }
350:
351: return(JS_TRUE);
352: }
353:
354: /* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
355:
356: JSBool
357: DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
358: {
359: JSString* js_str = JS_NewStringCopyZ(cx, str);
360:
361: if(js_str==NULL)
362: return(JS_FALSE);
363:
364: if(ver < 10000) /* auto convert 313 to 31300 */
365: ver*=100;
366:
367: return(JS_DefineProperty(cx,obj,"_description"
368: ,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
369: && JS_DefineProperty(cx,obj,"_ver"
370: ,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
371: }
372:
373: JSBool
374: DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
375: {
376: JSString* js_str = JS_NewStringCopyZ(cx, str);
377:
378: if(js_str==NULL)
379: return(JS_FALSE);
380:
381: return(JS_DefineProperty(cx,obj,"_constructor"
382: ,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY));
383: }
384:
385: #ifdef BUILD_JSDOCS
386:
387: static const char* method_array_name = "_method_list";
388: static const char* propver_array_name = "_property_ver_list";
389:
390: /*
391: * from jsatom.c:
392: * Keep this in sync with jspubtd.h -- an assertion below will insist that
393: * its length match the JSType enum's JSTYPE_LIMIT limit value.
394: */
395: static const char *js_type_str[] = {
396: "void", // changed from "undefined"
397: "object",
398: "function",
399: "string",
400: "number",
401: "boolean",
402: "array",
403: "alias",
404: "undefined",
405: "null"
406: };
407:
408: JSBool
409: DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
410: {
411: uint i;
412: long ver;
413: jsval val;
414: jsuint len=0;
415: JSObject* array;
416:
417: if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)
418: return(JS_FALSE);
419:
420: if(!JS_DefineProperty(cx, obj, propver_array_name, OBJECT_TO_JSVAL(array)
421: ,NULL,NULL,JSPROP_READONLY))
422: return(JS_FALSE);
423:
424: for(i=0;props[i].name;i++) {
425: if(!JS_DefinePropertyWithTinyId(cx, obj, /* Never reserve any "slots" for properties */
426: props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
427: return(JS_FALSE);
428: if(props[i].flags&JSPROP_ENUMERATE) { /* No need to version invisible props */
429: if((ver=props[i].ver) < 10000) /* auto convert 313 to 31300 */
430: ver*=100;
431: val = INT_TO_JSVAL(ver);
432: if(!JS_SetElement(cx, array, len++, &val))
433: return(JS_FALSE);
434: }
435: }
436:
437: return(JS_TRUE);
438: }
439:
440: JSBool
441: DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
442: {
443: int i;
444: jsuint len=0;
445: long ver;
446: jsval val;
447: JSObject* method;
448: JSObject* method_array;
449: JSString* js_str;
450:
451: /* Return existing method_list array if it's already been created */
452: if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID)
453: method_array=JSVAL_TO_OBJECT(val);
454: else
455: if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL)
456: return(JS_FALSE);
457:
458: if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
459: , NULL, NULL, 0))
460: return(JS_FALSE);
461:
462: if(append)
463: if(!JS_GetArrayLength(cx, method_array, &len))
464: return(JS_FALSE);
465:
466: for(i=0;funcs[i].name;i++) {
467:
468: if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
469: return(JS_FALSE);
470:
471: if(funcs[i].type==JSTYPE_ALIAS)
472: continue;
473:
474: method = JS_NewObject(cx, NULL, NULL, method_array); /* exception here June-7-2003 */
475:
476: if(method==NULL)
477: return(JS_FALSE);
478:
479: if(funcs[i].name!=NULL) {
480: if((js_str=JS_NewStringCopyZ(cx,funcs[i].name))==NULL)
481: return(JS_FALSE);
482: val = STRING_TO_JSVAL(js_str);
483: JS_SetProperty(cx, method, "name", &val);
484: }
485:
486: val = INT_TO_JSVAL(funcs[i].nargs);
487: if(!JS_SetProperty(cx, method, "nargs", &val))
488: return(JS_FALSE);
489:
490: if((js_str=JS_NewStringCopyZ(cx,js_type_str[funcs[i].type]))==NULL)
491: return(JS_FALSE);
492: val = STRING_TO_JSVAL(js_str);
493: JS_SetProperty(cx, method, "type", &val);
494:
495: if(funcs[i].args!=NULL) {
496: if((js_str=JS_NewStringCopyZ(cx,funcs[i].args))==NULL)
497: return(JS_FALSE);
498: val = STRING_TO_JSVAL(js_str);
499: JS_SetProperty(cx, method, "args", &val);
500: }
501:
502: if(funcs[i].desc!=NULL) {
503: if((js_str=JS_NewStringCopyZ(cx,funcs[i].desc))==NULL)
504: return(JS_FALSE);
505: val = STRING_TO_JSVAL(js_str);
506: JS_SetProperty(cx, method, "desc", &val);
507: }
508:
509: if(funcs[i].ver) {
510: if((ver=funcs[i].ver) < 10000) /* auto convert 313 to 31300 */
511: ver*=100;
512: val = INT_TO_JSVAL(ver);
513: JS_SetProperty(cx,method, "ver", &val);
514: }
515:
516: val=OBJECT_TO_JSVAL(method);
517: if(!JS_SetElement(cx, method_array, len+i, &val))
518: return(JS_FALSE);
519: }
520:
521: return(JS_TRUE);
522: }
523:
1.1.1.2 ! root 524: /*
! 525: * Always resolve all here since
! 526: * 1) We'll always be enumerating anyways
! 527: * 2) The speed penalty won't be seen in production code anyways
! 528: */
! 529: JSBool
! 530: DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
! 531: {
! 532: JSBool ret=JS_TRUE;
! 533:
! 534: if(props) {
! 535: if(!js_DefineSyncProperties(cx, obj, props))
! 536: ret=JS_FALSE;
! 537: }
! 538:
! 539: if(funcs) {
! 540: if(!js_DefineSyncMethods(cx, obj, funcs, 0))
! 541: ret=JS_FALSE;
! 542: }
! 543:
! 544: if(consts) {
! 545: if(!js_DefineConstIntegers(cx, obj, consts, flags))
! 546: ret=JS_FALSE;
! 547: }
! 548:
! 549: return(ret);
! 550: }
! 551:
1.1 root 552: #else // NON-JSDOCS
553:
554: JSBool
555: DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
556: {
557: uint i;
558:
559: for(i=0;props[i].name;i++)
560: if(!JS_DefinePropertyWithTinyId(cx, obj,
561: props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
562: return(JS_FALSE);
563:
564: return(JS_TRUE);
565: }
566:
567:
568: JSBool
569: DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
570: {
571: uint i;
572:
573: for(i=0;funcs[i].name;i++)
574: if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
575: return(JS_FALSE);
576: return(JS_TRUE);
577: }
578:
1.1.1.2 ! root 579: JSBool
! 580: DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
! 581: {
! 582: uint i;
! 583: jsval val;
! 584:
! 585: if(props) {
! 586: for(i=0;props[i].name;i++) {
! 587: if(name==NULL || strcmp(name, props[i].name)==0) {
! 588: if(!JS_DefinePropertyWithTinyId(cx, obj,
! 589: props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
! 590: return(JS_FALSE);
! 591: if(name)
! 592: return(JS_TRUE);
! 593: }
! 594: }
! 595: }
! 596: if(funcs) {
! 597: for(i=0;funcs[i].name;i++) {
! 598: if(name==NULL || strcmp(name, funcs[i].name)==0) {
! 599: if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
! 600: return(JS_FALSE);
! 601: if(name)
! 602: return(JS_TRUE);
! 603: }
! 604: }
! 605: }
! 606: if(consts) {
! 607: for(i=0;consts[i].name;i++) {
! 608: if(name==NULL || strcmp(name, consts[i].name)==0) {
! 609: if(!JS_NewNumberValue(cx, consts[i].val, &val))
! 610: return(JS_FALSE);
! 611:
! 612: if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
! 613: return(JS_FALSE);
! 614:
! 615: if(name)
! 616: return(JS_TRUE);
! 617: }
! 618: }
! 619: }
! 620:
! 621: return(JS_TRUE);
! 622: }
! 623:
1.1 root 624: #endif
625:
626: /* This is a stream-lined version of JS_DefineConstDoubles */
627: JSBool
628: DLLCALL js_DefineConstIntegers(JSContext* cx, JSObject* obj, jsConstIntSpec* ints, int flags)
629: {
630: uint i;
631: jsval val;
632:
633: for(i=0;ints[i].name;i++) {
634: if(!JS_NewNumberValue(cx, ints[i].val, &val))
635: return(JS_FALSE);
636:
637: if(!JS_DefineProperty(cx, obj, ints[i].name, val ,NULL, NULL, flags))
638: return(JS_FALSE);
639: }
1.1.1.2 ! root 640:
1.1 root 641: return(JS_TRUE);
642: }
643:
644: char*
645: DLLCALL js_ValueToStringBytes(JSContext* cx, jsval val, size_t* len)
646: {
647: JSString* str;
648:
649: if((str=JS_ValueToString(cx, val))==NULL)
650: return(NULL);
651:
652: if(len!=NULL)
653: *len = JS_GetStringLength(str);
654:
655: return(JS_GetStringBytes(str));
656: }
657:
658: static JSBool
659: js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
660: {
661: uintN i=0;
662: int32 level=LOG_INFO;
663: JSString* str=NULL;
664: sbbs_t* sbbs;
1.1.1.2 ! root 665: jsrefcount rc;
1.1 root 666:
667: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
668: return(JS_FALSE);
669:
670: if(argc > 1 && JSVAL_IS_NUMBER(argv[i]))
671: JS_ValueToInt32(cx,argv[i++],&level);
672:
673: for(; i<argc; i++) {
1.1.1.2 ! root 674: if((str=JS_ValueToString(cx, argv[i]))==NULL) {
! 675: JS_RESUMEREQUEST(cx, rc);
1.1 root 676: return(JS_FALSE);
1.1.1.2 ! root 677: }
! 678: rc=JS_SUSPENDREQUEST(cx);
1.1 root 679: if(sbbs->online==ON_LOCAL) {
1.1.1.2 ! root 680: if(startup!=NULL && startup->event_lputs!=NULL && level <= startup->log_level)
! 681: startup->event_lputs(startup->event_cbdata,level,JS_GetStringBytes(str));
1.1 root 682: } else
1.1.1.2 ! root 683: lprintf(level,"Node %d %s", sbbs->cfg.node_num, JS_GetStringBytes(str));
! 684: JS_RESUMEREQUEST(cx, rc);
1.1 root 685: }
686:
687: if(str==NULL)
688: *rval = JSVAL_VOID;
689: else
690: *rval = STRING_TO_JSVAL(str);
691: return(JS_TRUE);
692: }
693:
694: static JSBool
695: js_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
696: {
697: uchar* buf;
698: int32 len=128;
699: sbbs_t* sbbs;
1.1.1.2 ! root 700: jsrefcount rc;
1.1 root 701:
702: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
703: return(JS_FALSE);
704:
705: if(argc)
706: JS_ValueToInt32(cx,argv[0],&len);
707:
708: if((buf=(uchar*)malloc(len))==NULL)
709: return(JS_TRUE);
710:
1.1.1.2 ! root 711: rc=JS_SUSPENDREQUEST(cx);
1.1 root 712: len=RingBufRead(&sbbs->inbuf,buf,len);
1.1.1.2 ! root 713: JS_RESUMEREQUEST(cx, rc);
1.1 root 714:
715: if(len>0)
716: *rval = STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len));
717:
718: free(buf);
719: return(JS_TRUE);
720: }
721:
722: static JSBool
723: js_readln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
724: {
725: char* buf;
726: int32 len=128;
727: sbbs_t* sbbs;
1.1.1.2 ! root 728: jsrefcount rc;
1.1 root 729:
730: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
731: return(JS_FALSE);
732:
733: if(argc)
734: JS_ValueToInt32(cx,argv[0],&len);
735:
736: if((buf=(char*)malloc(len))==NULL)
737: return(JS_TRUE);
738:
1.1.1.2 ! root 739: rc=JS_SUSPENDREQUEST(cx);
1.1 root 740: len=sbbs->getstr(buf,len,K_NONE);
1.1.1.2 ! root 741: JS_RESUMEREQUEST(cx, rc);
1.1 root 742:
743: if(len>0)
744: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf));
745:
746: free(buf);
747: return(JS_TRUE);
748: }
749:
750: static JSBool
751: js_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
752: {
753: uintN i;
754: JSString* str=NULL;
755: sbbs_t* sbbs;
1.1.1.2 ! root 756: jsrefcount rc;
1.1 root 757:
758: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
759: return(JS_FALSE);
760:
761: for (i = 0; i < argc; i++) {
762: if((str=JS_ValueToString(cx, argv[i]))==NULL)
763: return(JS_FALSE);
1.1.1.2 ! root 764: rc=JS_SUSPENDREQUEST(cx);
1.1 root 765: if(sbbs->online==ON_LOCAL)
766: eprintf(LOG_INFO,"%s",JS_GetStringBytes(str));
767: else
768: sbbs->bputs(JS_GetStringBytes(str));
1.1.1.2 ! root 769: JS_RESUMEREQUEST(cx, rc);
1.1 root 770: }
771:
772: if(str==NULL)
773: *rval = JSVAL_VOID;
774: else
775: *rval = STRING_TO_JSVAL(str);
776: return(JS_TRUE);
777: }
778:
779: static JSBool
780: js_write_raw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
781: {
782: uintN i;
783: char* str=NULL;
784: size_t len;
785: sbbs_t* sbbs;
1.1.1.2 ! root 786: jsrefcount rc;
1.1 root 787:
788: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
789: return(JS_FALSE);
790:
791: for (i = 0; i < argc; i++) {
792: if((str=js_ValueToStringBytes(cx, argv[i], &len))==NULL)
793: return(JS_FALSE);
1.1.1.2 ! root 794: rc=JS_SUSPENDREQUEST(cx);
1.1 root 795: sbbs->putcom(str, len);
1.1.1.2 ! root 796: JS_RESUMEREQUEST(cx, rc);
1.1 root 797: }
798:
799: return(JS_TRUE);
800: }
801:
802: static JSBool
803: js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
804: {
805: sbbs_t* sbbs;
1.1.1.2 ! root 806: jsrefcount rc;
1.1 root 807:
808: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
809: return(JS_FALSE);
810:
811: js_write(cx,obj,argc,argv,rval);
1.1.1.2 ! root 812: rc=JS_SUSPENDREQUEST(cx);
1.1 root 813: if(sbbs->online==ON_REMOTE)
814: sbbs->bputs(crlf);
1.1.1.2 ! root 815: JS_RESUMEREQUEST(cx, rc);
1.1 root 816:
817: return(JS_TRUE);
818: }
819:
820: static JSBool
821: js_printf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
822: {
823: char* p;
824: sbbs_t* sbbs;
1.1.1.2 ! root 825: jsrefcount rc;
1.1 root 826:
827: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
828: return(JS_FALSE);
829:
830: if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
831: JS_ReportError(cx,"js_sprintf failed");
832: return(JS_FALSE);
833: }
834:
1.1.1.2 ! root 835: rc=JS_SUSPENDREQUEST(cx);
1.1 root 836: if(sbbs->online==ON_LOCAL)
837: eprintf(LOG_INFO,"%s",p);
838: else
839: sbbs->bputs(p);
1.1.1.2 ! root 840: JS_RESUMEREQUEST(cx, rc);
1.1 root 841:
842: *rval = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p));
843:
844: js_sprintf_free(p);
845:
846: return(JS_TRUE);
847: }
848:
849: static JSBool
850: js_alert(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
851: {
852: JSString * str;
853: sbbs_t* sbbs;
1.1.1.2 ! root 854: jsrefcount rc;
1.1 root 855:
856: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
857: return(JS_FALSE);
858:
859: if((str=JS_ValueToString(cx, argv[0]))==NULL)
860: return(JS_FALSE);
861:
1.1.1.2 ! root 862: rc=JS_SUSPENDREQUEST(cx);
1.1 root 863: sbbs->attr(sbbs->cfg.color[clr_err]);
864: sbbs->bputs(JS_GetStringBytes(str));
865: sbbs->attr(LIGHTGRAY);
866: sbbs->bputs(crlf);
1.1.1.2 ! root 867: JS_RESUMEREQUEST(cx, rc);
1.1 root 868:
869: return(JS_TRUE);
870: }
871:
872: static JSBool
873: js_confirm(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
874: {
875: JSString * str;
876: sbbs_t* sbbs;
1.1.1.2 ! root 877: jsrefcount rc;
1.1 root 878:
879: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
880: return(JS_FALSE);
881:
882: if((str=JS_ValueToString(cx, argv[0]))==NULL)
883: return(JS_FALSE);
884:
1.1.1.2 ! root 885: rc=JS_SUSPENDREQUEST(cx);
1.1 root 886: *rval = BOOLEAN_TO_JSVAL(sbbs->yesno(JS_GetStringBytes(str)));
1.1.1.2 ! root 887: JS_RESUMEREQUEST(cx, rc);
1.1 root 888: return(JS_TRUE);
889: }
890:
891: static JSBool
1.1.1.2 ! root 892: js_deny(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
! 893: {
! 894: JSString * str;
! 895: sbbs_t* sbbs;
! 896: jsrefcount rc;
! 897:
! 898: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
! 899: return(JS_FALSE);
! 900:
! 901: if((str=JS_ValueToString(cx, argv[0]))==NULL)
! 902: return(JS_FALSE);
! 903:
! 904: rc=JS_SUSPENDREQUEST(cx);
! 905: *rval = BOOLEAN_TO_JSVAL(sbbs->noyes(JS_GetStringBytes(str)));
! 906: JS_RESUMEREQUEST(cx, rc);
! 907: return(JS_TRUE);
! 908: }
! 909:
! 910:
! 911: static JSBool
1.1 root 912: js_prompt(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
913: {
914: char instr[81];
915: JSString * prompt;
916: JSString * str;
917: sbbs_t* sbbs;
1.1.1.2 ! root 918: jsrefcount rc;
1.1 root 919:
920: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
921: return(JS_FALSE);
922:
923: if((prompt=JS_ValueToString(cx, argv[0]))==NULL)
924: return(JS_FALSE);
925:
926: if(argc>1) {
927: if((str=JS_ValueToString(cx, argv[1]))==NULL)
928: return(JS_FALSE);
929: SAFECOPY(instr,JS_GetStringBytes(str));
930: } else
931: instr[0]=0;
932:
1.1.1.2 ! root 933: rc=JS_SUSPENDREQUEST(cx);
1.1 root 934: sbbs->bprintf("\1n\1y\1h%s\1w: ",JS_GetStringBytes(prompt));
935:
936: if(!sbbs->getstr(instr,sizeof(instr)-1,K_EDIT)) {
937: *rval = JSVAL_NULL;
1.1.1.2 ! root 938: JS_RESUMEREQUEST(cx, rc);
1.1 root 939: return(JS_TRUE);
940: }
1.1.1.2 ! root 941: JS_RESUMEREQUEST(cx, rc);
1.1 root 942:
943: if((str=JS_NewStringCopyZ(cx, instr))==NULL)
944: return(JS_FALSE);
945:
946: *rval = STRING_TO_JSVAL(str);
947: return(JS_TRUE);
948: }
949:
950: static jsSyncMethodSpec js_global_functions[] = {
951: {"log", js_log, 1, JSTYPE_STRING, JSDOCSTR("[level,] value [,value]")
952: ,JSDOCSTR("add a line of text to the server and/or system log, "
953: "<i>values</i> are typically string constants or variables, "
954: "<i>level</i> is the debug level/priority (default: <tt>LOG_INFO</tt>)")
955: ,311
956: },
957: {"read", js_read, 0, JSTYPE_STRING, JSDOCSTR("[count]")
958: ,JSDOCSTR("read up to count characters from input stream")
959: ,311
960: },
961: {"readln", js_readln, 0, JSTYPE_STRING, JSDOCSTR("[count]")
962: ,JSDOCSTR("read a single line, up to count characters, from input stream")
963: ,311
964: },
965: {"write", js_write, 0, JSTYPE_VOID, JSDOCSTR("value [,value]")
966: ,JSDOCSTR("send one or more values (typically strings) to the server output")
967: ,311
968: },
969: {"write_raw", js_write_raw, 0, JSTYPE_VOID, JSDOCSTR("value [,value]")
970: ,JSDOCSTR("send a stream of bytes (possibly containing NULLs or special control code sequences) to the server output")
971: ,314
972: },
973: {"print", js_writeln, 0, JSTYPE_ALIAS },
974: {"writeln", js_writeln, 0, JSTYPE_VOID, JSDOCSTR("value [,value]")
975: ,JSDOCSTR("send a line of text to the console or event log with automatic line termination (CRLF), "
976: "<i>values</i> are typically string constants or variables (AKA print)")
977: ,311
978: },
979: {"printf", js_printf, 1, JSTYPE_STRING, JSDOCSTR("string format [,value][,value]")
980: ,JSDOCSTR("print a formatted string - <small>CAUTION: for experienced C programmers ONLY</small>")
981: ,310
982: },
983: {"alert", js_alert, 1, JSTYPE_VOID, JSDOCSTR("value")
984: ,JSDOCSTR("print an alert message (ala client-side JS)")
985: ,310
986: },
987: {"prompt", js_prompt, 1, JSTYPE_STRING, JSDOCSTR("[value]")
988: ,JSDOCSTR("displays a prompt (<i>value</i>) and returns a string of user input (ala clent-side JS)")
989: ,310
990: },
991: {"confirm", js_confirm, 1, JSTYPE_BOOLEAN, JSDOCSTR("value")
992: ,JSDOCSTR("displays a Yes/No prompt and returns <i>true</i> or <i>false</i> "
1.1.1.2 ! root 993: "based on user's confirmation (ala client-side JS, <i>true</i> = yes)")
1.1 root 994: ,310
995: },
1.1.1.2 ! root 996: {"deny", js_deny, 1, JSTYPE_BOOLEAN, JSDOCSTR("value")
! 997: ,JSDOCSTR("displays a No/Yes prompt and returns <i>true</i> or <i>false</i> "
! 998: "based on user's denial (<i>true</i> = no)")
! 999: ,31501
! 1000: },
1.1 root 1001: {0}
1002: };
1003:
1004: static void
1005: js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
1006: {
1007: char line[64];
1008: char file[MAX_PATH+1];
1009: sbbs_t* sbbs;
1010: const char* warning;
1.1.1.2 ! root 1011: jsrefcount rc;
! 1012: int log_level;
! 1013: char nodestr[128];
1.1 root 1014:
1015: if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
1016: return;
1.1.1.2 ! root 1017:
! 1018: if(sbbs->cfg.node_num)
! 1019: SAFEPRINTF(nodestr,"Node %d",sbbs->cfg.node_num);
! 1020: else
! 1021: SAFECOPY(nodestr,sbbs->client_name);
1.1 root 1022:
1023: if(report==NULL) {
1.1.1.2 ! root 1024: lprintf(LOG_ERR,"%s !JavaScript: %s", nodestr, message);
1.1 root 1025: return;
1026: }
1027:
1028: if(report->filename)
1029: SAFEPRINTF(file," %s",report->filename);
1030: else
1031: file[0]=0;
1032:
1033: if(report->lineno)
1034: SAFEPRINTF(line," line %d",report->lineno);
1035: else
1036: line[0]=0;
1037:
1038: if(JSREPORT_IS_WARNING(report->flags)) {
1039: if(JSREPORT_IS_STRICT(report->flags))
1040: warning="strict warning";
1041: else
1042: warning="warning";
1.1.1.2 ! root 1043: log_level = LOG_WARNING;
! 1044: } else {
1.1 root 1045: warning=nulstr;
1.1.1.2 ! root 1046: log_level = LOG_ERR;
! 1047: }
1.1 root 1048:
1.1.1.2 ! root 1049: rc=JS_SUSPENDREQUEST(cx);
1.1 root 1050: if(sbbs->online==ON_LOCAL)
1.1.1.2 ! root 1051: eprintf(log_level,"!JavaScript %s%s%s: %s",warning,file,line,message);
1.1 root 1052: else {
1.1.1.2 ! root 1053: lprintf(log_level,"%s !JavaScript %s%s%s: %s",nodestr,warning,file,line,message);
1.1 root 1054: sbbs->bprintf("!JavaScript %s%s%s: %s\r\n",warning,file,line,message);
1055: }
1.1.1.2 ! root 1056: JS_RESUMEREQUEST(cx, rc);
1.1 root 1057: }
1058:
1059: bool sbbs_t::js_init(ulong* stack_frame)
1060: {
1061: char node[128];
1062:
1063: if(cfg.node_num)
1064: SAFEPRINTF(node,"Node %d",cfg.node_num);
1065: else
1066: SAFECOPY(node,client_name);
1067:
1068: if(startup->js.max_bytes==0) startup->js.max_bytes=JAVASCRIPT_MAX_BYTES;
1069: if(startup->js.cx_stack==0) startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK;
1070:
1071: lprintf(LOG_DEBUG,"%s JavaScript: Creating runtime: %lu bytes"
1072: ,node,startup->js.max_bytes);
1073:
1.1.1.2 ! root 1074: if((js_runtime = jsrt_GetNew(startup->js.max_bytes, 1000, __FILE__, __LINE__))==NULL)
1.1 root 1075: return(false);
1076:
1077: lprintf(LOG_DEBUG,"%s JavaScript: Initializing context (stack: %lu bytes)"
1078: ,node,startup->js.cx_stack);
1079:
1080: if((js_cx = JS_NewContext(js_runtime, startup->js.cx_stack))==NULL)
1081: return(false);
1.1.1.2 ! root 1082: JS_BEGINREQUEST(js_cx);
1.1 root 1083:
1084: memset(&js_branch,0,sizeof(js_branch));
1085: js_branch.limit = startup->js.branch_limit;
1086: js_branch.gc_interval = startup->js.gc_interval;
1087: js_branch.yield_interval = startup->js.yield_interval;
1088: js_branch.terminated = &terminated;
1089: js_branch.auto_terminate = TRUE;
1090:
1091: bool success=false;
1092:
1093: do {
1094:
1095: JS_SetErrorReporter(js_cx, js_ErrorReporter);
1096:
1097: JS_SetContextPrivate(js_cx, this); /* Store a pointer to sbbs_t instance */
1098:
1099: /* Global Objects (including system, js, client, Socket, MsgBase, File, User, etc. */
1100: if((js_glob=js_CreateCommonObjects(js_cx, &scfg, &cfg, js_global_functions
1101: ,uptime, startup->host_name, SOCKLIB_DESC /* system */
1102: ,&js_branch /* js */
1.1.1.2 ! root 1103: ,&startup->js
1.1 root 1104: ,&client, client_socket /* client */
1105: ,&js_server_props /* server */
1106: ))==NULL)
1107: break;
1108:
1109: /* BBS Object */
1110: if(js_CreateBbsObject(js_cx, js_glob)==NULL)
1111: break;
1112:
1113: /* Console Object */
1114: if(js_CreateConsoleObject(js_cx, js_glob)==NULL)
1115: break;
1116:
1117: if(startup->js.thread_stack) {
1118: ulong stack_limit;
1119:
1120: #if JS_STACK_GROWTH_DIRECTION > 0
1121: stack_limit=((ulong)stack_frame)+startup->js.thread_stack;
1122: #else
1123: stack_limit=((ulong)stack_frame)-startup->js.thread_stack;
1124: #endif
1125: JS_SetThreadStackLimit(js_cx, stack_limit);
1126:
1127: lprintf(LOG_DEBUG,"%s JavaScript: Thread stack limit: %lu bytes"
1128: ,node, startup->js.thread_stack);
1129: }
1130:
1131: success=true;
1132:
1133: } while(0);
1134:
1.1.1.2 ! root 1135: JS_ENDREQUEST(js_cx);
1.1 root 1136: if(!success) {
1137: JS_DestroyContext(js_cx);
1138: js_cx=NULL;
1139: return(false);
1140: }
1141:
1142: return(true);
1143: }
1144:
1.1.1.2 ! root 1145: void sbbs_t::js_cleanup(const char* node)
! 1146: {
! 1147: /* Free Context */
! 1148: if(js_cx!=NULL) {
! 1149: lprintf(LOG_DEBUG,"%s JavaScript: Destroying context",node);
! 1150: JS_DestroyContext(js_cx);
! 1151: js_cx=NULL;
! 1152: }
! 1153:
! 1154: if(js_runtime!=NULL) {
! 1155: lprintf(LOG_DEBUG,"%s JavaScript: Destroying runtime",node);
! 1156: jsrt_Release(js_runtime);
! 1157: js_runtime=NULL;
! 1158: }
! 1159: }
! 1160:
1.1 root 1161: void sbbs_t::js_create_user_objects(void)
1162: {
1163: if(js_cx==NULL)
1164: return;
1.1.1.2 ! root 1165:
! 1166: JS_BEGINREQUEST(js_cx);
! 1167: if(!js_CreateUserObjects(js_cx, js_glob, &cfg, &useron, &client, NULL, subscan))
1.1 root 1168: lprintf(LOG_ERR,"!JavaScript ERROR creating user objects");
1.1.1.2 ! root 1169: JS_ENDREQUEST(js_cx);
1.1 root 1170: }
1171:
1172: #endif /* JAVASCRIPT */
1173:
1174: static BYTE* telnet_interpret(sbbs_t* sbbs, BYTE* inbuf, int inlen,
1175: BYTE* outbuf, int& outlen)
1176: {
1177: BYTE* first_iac=NULL;
1178: BYTE* first_cr=NULL;
1179: int i;
1180:
1181: if(inlen<1) {
1182: outlen=0;
1183: return(inbuf); // no length? No interpretation
1184: }
1185:
1186: first_iac=(BYTE*)memchr(inbuf, TELNET_IAC, inlen);
1187:
1188: if(!(sbbs->telnet_mode&TELNET_MODE_GATE)
1189: && sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
1190: && !(sbbs->console&CON_RAW_IN)) {
1191: if(sbbs->telnet_last_rxch==CR)
1192: first_cr=inbuf;
1193: else
1194: first_cr=(BYTE*)memchr(inbuf, CR, inlen);
1195: }
1196:
1197: if(!sbbs->telnet_cmdlen && first_iac==NULL && first_cr==NULL) {
1198: outlen=inlen;
1199: return(inbuf); // no interpretation needed
1200: }
1201:
1202: if(first_iac!=NULL || first_cr!=NULL) {
1203: if(first_iac!=NULL && (first_cr==NULL || first_iac<first_cr))
1204: outlen=first_iac-inbuf;
1205: else
1206: outlen=first_cr-inbuf;
1207: memcpy(outbuf, inbuf, outlen);
1208: } else
1209: outlen=0;
1210:
1211: for(i=outlen;i<inlen;i++) {
1212: if(!(sbbs->telnet_mode&TELNET_MODE_GATE)
1213: && sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
1214: && !(sbbs->console&CON_RAW_IN)) {
1215: if(sbbs->telnet_last_rxch==CR
1216: && (inbuf[i]==LF || inbuf[i]==0)) { // CR/LF or CR/NUL, ignore 2nd char
1217: #if 0 /* Debug CR/LF problems */
1218: lprintf(LOG_INFO,"Node %d CR/%02Xh detected and ignored"
1219: ,sbbs->cfg.node_num, inbuf[i]);
1220: #endif
1221: sbbs->telnet_last_rxch=inbuf[i];
1222: continue;
1223: }
1224: sbbs->telnet_last_rxch=inbuf[i];
1225: }
1226:
1227: if(inbuf[i]==TELNET_IAC && sbbs->telnet_cmdlen==1) { /* escaped 255 */
1228: sbbs->telnet_cmdlen=0;
1229: outbuf[outlen++]=TELNET_IAC;
1230: continue;
1231: }
1232: if(inbuf[i]==TELNET_IAC || sbbs->telnet_cmdlen) {
1233:
1234: if(sbbs->telnet_cmdlen<sizeof(sbbs->telnet_cmd))
1235: sbbs->telnet_cmd[sbbs->telnet_cmdlen++]=inbuf[i];
1236:
1237: uchar command = sbbs->telnet_cmd[1];
1238: uchar option = sbbs->telnet_cmd[2];
1239:
1240: if(sbbs->telnet_cmdlen>=2 && command==TELNET_SB) {
1241: if(inbuf[i]==TELNET_SE
1242: && sbbs->telnet_cmd[sbbs->telnet_cmdlen-2]==TELNET_IAC) {
1.1.1.2 ! root 1243:
! 1244: if(startup->options&BBS_OPT_DEBUG_TELNET)
! 1245: lprintf(LOG_DEBUG,"Node %d %s telnet sub-negotiation command: %s"
! 1246: ,sbbs->cfg.node_num
! 1247: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
! 1248: ,telnet_opt_desc(option));
! 1249:
1.1 root 1250: /* sub-option terminated */
1251: if(option==TELNET_TERM_TYPE
1252: && sbbs->telnet_cmd[3]==TELNET_TERM_IS) {
1253: sprintf(sbbs->terminal,"%.*s",(int)sbbs->telnet_cmdlen-6,sbbs->telnet_cmd+4);
1254: lprintf(LOG_DEBUG,"Node %d %s telnet terminal type: %s"
1255: ,sbbs->cfg.node_num
1256: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1257: ,sbbs->terminal);
1258:
1259: } else if(option==TELNET_TERM_SPEED
1260: && sbbs->telnet_cmd[3]==TELNET_TERM_IS) {
1261: char speed[128];
1262: sprintf(speed,"%.*s",(int)sbbs->telnet_cmdlen-6,sbbs->telnet_cmd+4);
1263: lprintf(LOG_DEBUG,"Node %d %s telnet terminal speed: %s"
1264: ,sbbs->cfg.node_num
1265: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1266: ,speed);
1.1.1.2 ! root 1267: sbbs->cur_rate=atoi(speed);
! 1268: sbbs->cur_cps=sbbs->cur_rate/10;
! 1269: #if 0
! 1270: } else if(option==TELNET_NEW_ENVIRON
! 1271: && sbbs->telnet_cmd[3]==TELNET_ENVIRON_IS) {
! 1272: BYTE* p;
! 1273: BYTE* end=sbbs->telnet_cmd+(sbbs->telnet_cmdlen-2);
! 1274: for(p=sbbs->telnet_cmd+4; p < end; ) {
! 1275: if(*p==TELNET_ENVIRON_VAR || *p==TELNET_ENVIRON_USERVAR) {
! 1276: p++;
! 1277: lprintf(LOG_DEBUG,"Node %d %s telnet environment var/val: %.*s"
! 1278: ,sbbs->cfg.node_num
! 1279: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
! 1280: ,end-p
! 1281: ,p);
! 1282: p+=strlen((char*)p);
! 1283: } else
! 1284: p++;
! 1285: }
! 1286: #endif
! 1287: } else if(option==TELNET_SEND_LOCATION) {
! 1288: safe_snprintf(sbbs->telnet_location
! 1289: ,sizeof(sbbs->telnet_location)
! 1290: ,"%.*s",(int)sbbs->telnet_cmdlen-5,sbbs->telnet_cmd+3);
! 1291: lprintf(LOG_DEBUG,"Node %d %s telnet location: %s"
! 1292: ,sbbs->cfg.node_num
! 1293: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
! 1294: ,sbbs->telnet_location);
1.1 root 1295:
1296: } else if(option==TELNET_NEGOTIATE_WINDOW_SIZE) {
1297: long cols = (sbbs->telnet_cmd[3]<<8) | sbbs->telnet_cmd[4];
1298: long rows = (sbbs->telnet_cmd[5]<<8) | sbbs->telnet_cmd[6];
1299: lprintf(LOG_DEBUG,"Node %d %s telnet window size: %ux%u"
1300: ,sbbs->cfg.node_num
1301: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1302: ,cols
1303: ,rows);
1304: if(rows && !sbbs->useron.rows) /* auto-detect rows */
1305: sbbs->rows=rows;
1306: if(cols)
1307: sbbs->cols=cols;
1308:
1309: } else if(startup->options&BBS_OPT_DEBUG_TELNET)
1310: lprintf(LOG_DEBUG,"Node %d %s unsupported telnet sub-negotiation cmd: %s"
1311: ,sbbs->cfg.node_num
1312: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1313: ,telnet_opt_desc(option));
1314: sbbs->telnet_cmdlen=0;
1315: }
1316: }
1317: else if(sbbs->telnet_cmdlen==2 && inbuf[i]<TELNET_WILL) {
1318: if(startup->options&BBS_OPT_DEBUG_TELNET)
1319: lprintf(LOG_DEBUG,"Node %d %s telnet cmd: %s"
1320: ,sbbs->cfg.node_num
1321: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1322: ,telnet_cmd_desc(option));
1323: sbbs->telnet_cmdlen=0;
1324: }
1325: else if(sbbs->telnet_cmdlen>=3) { /* telnet option negotiation */
1326:
1327: if(startup->options&BBS_OPT_DEBUG_TELNET)
1328: lprintf(LOG_DEBUG,"Node %d %s telnet cmd: %s %s"
1329: ,sbbs->cfg.node_num
1330: ,sbbs->telnet_mode&TELNET_MODE_GATE ? "passed-through" : "received"
1331: ,telnet_cmd_desc(command)
1332: ,telnet_opt_desc(option));
1333:
1334: if(!(sbbs->telnet_mode&TELNET_MODE_GATE)) {
1335: if(command==TELNET_DO || command==TELNET_DONT) { /* local options */
1.1.1.2 ! root 1336: if(sbbs->telnet_local_option[option]==command)
! 1337: SetEvent(sbbs->telnet_ack_event);
! 1338: else {
1.1 root 1339: sbbs->telnet_local_option[option]=command;
1340: sbbs->send_telnet_cmd(telnet_opt_ack(command),option);
1341: }
1342: } else { /* WILL/WONT (remote options) */
1.1.1.2 ! root 1343: if(sbbs->telnet_remote_option[option]==command)
! 1344: SetEvent(sbbs->telnet_ack_event);
! 1345: else {
1.1 root 1346: switch(option) {
1347: case TELNET_BINARY_TX:
1348: case TELNET_ECHO:
1349: case TELNET_TERM_TYPE:
1350: case TELNET_TERM_SPEED:
1351: case TELNET_SUP_GA:
1352: case TELNET_NEGOTIATE_WINDOW_SIZE:
1.1.1.2 ! root 1353: case TELNET_SEND_LOCATION:
1.1 root 1354: sbbs->telnet_remote_option[option]=command;
1355: sbbs->send_telnet_cmd(telnet_opt_ack(command),option);
1356: break;
1357: default: /* unsupported remote options */
1358: if(command==TELNET_WILL) /* NAK */
1359: sbbs->send_telnet_cmd(telnet_opt_nak(command),option);
1360: break;
1361: }
1362: }
1363:
1364: if(command==TELNET_WILL && option==TELNET_TERM_TYPE) {
1365: if(startup->options&BBS_OPT_DEBUG_TELNET)
1366: lprintf(LOG_DEBUG,"Node %d requesting telnet terminal type"
1367: ,sbbs->cfg.node_num);
1368:
1369: char buf[64];
1370: sprintf(buf,"%c%c%c%c%c%c"
1371: ,TELNET_IAC,TELNET_SB
1372: ,TELNET_TERM_TYPE,TELNET_TERM_SEND
1373: ,TELNET_IAC,TELNET_SE);
1374: sbbs->putcom(buf,6);
1375: }
1376: else if(command==TELNET_WILL && option==TELNET_TERM_SPEED) {
1377: if(startup->options&BBS_OPT_DEBUG_TELNET)
1378: lprintf(LOG_DEBUG,"Node %d requesting telnet terminal speed"
1379: ,sbbs->cfg.node_num);
1380:
1381: char buf[64];
1382: sprintf(buf,"%c%c%c%c%c%c"
1383: ,TELNET_IAC,TELNET_SB
1384: ,TELNET_TERM_SPEED,TELNET_TERM_SEND
1385: ,TELNET_IAC,TELNET_SE);
1386: sbbs->putcom(buf,6);
1387: }
1.1.1.2 ! root 1388: #if 0
! 1389: else if(command==TELNET_WILL && option==TELNET_NEW_ENVIRON) {
! 1390: if(startup->options&BBS_OPT_DEBUG_TELNET)
! 1391: lprintf(LOG_DEBUG,"Node %d requesting USER environment variable value"
! 1392: ,sbbs->cfg.node_num);
! 1393:
! 1394: char buf[64];
! 1395: int len=sprintf(buf,"%c%c%c%c%cUSER%c%c"
! 1396: ,TELNET_IAC,TELNET_SB
! 1397: ,TELNET_NEW_ENVIRON,TELNET_ENVIRON_SEND,TELNET_ENVIRON_VAR
! 1398: ,TELNET_IAC,TELNET_SE);
! 1399: sbbs->putcom(buf,len);
! 1400: }
! 1401: #endif
1.1 root 1402: }
1403: }
1404:
1405: sbbs->telnet_cmdlen=0;
1406:
1407: }
1408: if(sbbs->telnet_mode&TELNET_MODE_GATE) // Pass-through commads
1409: outbuf[outlen++]=inbuf[i];
1410: } else
1411: outbuf[outlen++]=inbuf[i];
1412: }
1413: return(outbuf);
1414: }
1415:
1416: void sbbs_t::send_telnet_cmd(uchar cmd, uchar opt)
1417: {
1418: char buf[16];
1419:
1420: if(telnet_mode&TELNET_MODE_OFF)
1421: return;
1422:
1423: if(cmd<TELNET_WILL) {
1424: if(startup->options&BBS_OPT_DEBUG_TELNET)
1425: lprintf(LOG_DEBUG,"Node %d sending telnet cmd: %s"
1426: ,cfg.node_num
1427: ,telnet_cmd_desc(cmd));
1428: sprintf(buf,"%c%c",TELNET_IAC,cmd);
1429: putcom(buf,2);
1430: } else {
1431: if(startup->options&BBS_OPT_DEBUG_TELNET)
1432: lprintf(LOG_DEBUG,"Node %d sending telnet cmd: %s %s"
1433: ,cfg.node_num
1434: ,telnet_cmd_desc(cmd)
1435: ,telnet_opt_desc(opt));
1436: sprintf(buf,"%c%c%c",TELNET_IAC,cmd,opt);
1437: putcom(buf,3);
1438: }
1439: }
1440:
1.1.1.2 ! root 1441: bool sbbs_t::request_telnet_opt(uchar cmd, uchar opt, unsigned waitforack)
1.1 root 1442: {
1443: if(cmd==TELNET_DO || cmd==TELNET_DONT) { /* remote option */
1444: if(telnet_remote_option[opt]==telnet_opt_ack(cmd))
1.1.1.2 ! root 1445: return true; /* already set in this mode, do nothing */
1.1 root 1446: telnet_remote_option[opt]=telnet_opt_ack(cmd);
1447: } else { /* local option */
1448: if(telnet_local_option[opt]==telnet_opt_ack(cmd))
1.1.1.2 ! root 1449: return true; /* already set in this mode, do nothing */
1.1 root 1450: telnet_local_option[opt]=telnet_opt_ack(cmd);
1451: }
1.1.1.2 ! root 1452: if(waitforack)
! 1453: ResetEvent(telnet_ack_event);
1.1 root 1454: send_telnet_cmd(cmd,opt);
1.1.1.2 ! root 1455: if(waitforack)
! 1456: return WaitForEvent(telnet_ack_event, waitforack)==WAIT_OBJECT_0;
! 1457: return true;
1.1 root 1458: }
1459:
1460: void input_thread(void *arg)
1461: {
1462: BYTE inbuf[4000];
1463: BYTE telbuf[sizeof(inbuf)];
1464: BYTE *wrbuf;
1465: int i,rd,wr,avail;
1466: ulong total_recv=0;
1467: ulong total_pkts=0;
1468: fd_set socket_set;
1469: sbbs_t* sbbs = (sbbs_t*) arg;
1470: struct timeval tv;
1471: SOCKET high_socket;
1472: SOCKET sock;
1473:
1.1.1.2 ! root 1474: SetThreadName("Node Input");
1.1 root 1475: thread_up(TRUE /* setuid */);
1476:
1477: #ifdef _DEBUG
1478: lprintf(LOG_DEBUG,"Node %d input thread started",sbbs->cfg.node_num);
1479: #endif
1480:
1481: pthread_mutex_init(&sbbs->input_thread_mutex,NULL);
1482: sbbs->input_thread_running = true;
1483: sbbs->console|=CON_R_INPUT;
1484:
1485: while(sbbs->online && sbbs->client_socket!=INVALID_SOCKET
1486: && node_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {
1487:
1488: if(pthread_mutex_lock(&sbbs->input_thread_mutex)!=0)
1489: sbbs->errormsg(WHERE,ERR_LOCK,"input_thread_mutex",0);
1490:
1491: FD_ZERO(&socket_set);
1492: FD_SET(sbbs->client_socket,&socket_set);
1493: high_socket=sbbs->client_socket;
1494: #ifdef __unix__
1495: if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {
1496: FD_SET(uspy_socket[sbbs->cfg.node_num-1],&socket_set);
1497: if(uspy_socket[sbbs->cfg.node_num-1] > high_socket)
1498: high_socket=uspy_socket[sbbs->cfg.node_num-1];
1499: }
1500: #endif
1501:
1502: tv.tv_sec=1;
1503: tv.tv_usec=0;
1504:
1505: if((i=select(high_socket+1,&socket_set,NULL,NULL,&tv))<1) {
1506: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1507: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1508: if(i==0) {
1509: YIELD(); /* This kludge is necessary on some Linux distros */
1510: continue; /* to allow other threads to lock the input_thread_mutex */
1511: }
1512:
1513: if(sbbs->client_socket==INVALID_SOCKET)
1514: break;
1515: #ifdef __unix__
1516: if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET) {
1517: if(!socket_check(uspy_socket[sbbs->cfg.node_num-1],NULL,NULL,0)) {
1518: close_socket(uspy_socket[sbbs->cfg.node_num-1]);
1519: lprintf(LOG_NOTICE,"Closing local spy socket: %d",uspy_socket[sbbs->cfg.node_num-1]);
1520: uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
1521: continue;
1522: }
1523: }
1524: #endif
1525: if(ERROR_VALUE == ENOTSOCK)
1526: lprintf(LOG_NOTICE,"Node %d socket closed by peer on input->select", sbbs->cfg.node_num);
1527: else if(ERROR_VALUE==ESHUTDOWN)
1528: lprintf(LOG_NOTICE,"Node %d socket shutdown on input->select", sbbs->cfg.node_num);
1529: else if(ERROR_VALUE==EINTR)
1530: lprintf(LOG_DEBUG,"Node %d input thread interrupted",sbbs->cfg.node_num);
1531: else if(ERROR_VALUE==ECONNRESET)
1532: lprintf(LOG_NOTICE,"Node %d connection reset by peer on input->select", sbbs->cfg.node_num);
1533: else if(ERROR_VALUE==ECONNABORTED)
1534: lprintf(LOG_NOTICE,"Node %d connection aborted by peer on input->select", sbbs->cfg.node_num);
1535: else
1536: lprintf(LOG_WARNING,"Node %d !ERROR %d input->select socket %d"
1537: ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->client_socket);
1538: break;
1539: }
1540:
1541: if(sbbs->client_socket==INVALID_SOCKET) {
1542: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1543: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1544: break;
1545: }
1546:
1547: /* ^ ^
1548: * \______ ______/
1549: * \ * \ / * /
1550: * ----- ------ /----\
1551: * || -< Boo! |
1552: * /__\ \----/
1553: * \______________/
1554: * \/\/\/\/\/\/\/
1555: * ------------
1556: */
1557:
1558: if(FD_ISSET(sbbs->client_socket,&socket_set))
1559: sock=sbbs->client_socket;
1560: #ifdef __unix__
1561: else if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET
1562: && FD_ISSET(uspy_socket[sbbs->cfg.node_num-1],&socket_set)) {
1563: if(!socket_check(uspy_socket[sbbs->cfg.node_num-1],NULL,NULL,0)) {
1564: close_socket(uspy_socket[sbbs->cfg.node_num-1]);
1565: lprintf(LOG_NOTICE,"Closing local spy socket: %d",uspy_socket[sbbs->cfg.node_num-1]);
1566: uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
1567: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1568: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1569: continue;
1570: }
1571: sock=uspy_socket[sbbs->cfg.node_num-1];
1572: }
1573: #endif
1574: else {
1575: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1576: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1577: continue;
1578: }
1579:
1580: rd=RingBufFree(&sbbs->inbuf);
1581:
1582: if(!rd) { // input buffer full
1583: lprintf(LOG_WARNING,"Node %d !WARNING input buffer full", sbbs->cfg.node_num);
1584: // wait up to 5 seconds to empty (1 byte min)
1585: time_t start=time(NULL);
1586: while((rd=RingBufFree(&sbbs->inbuf))==0) {
1587: if(time(NULL)-start>=5) {
1588: rd=1;
1589: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1590: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1591: break;
1592: }
1593: YIELD();
1594: }
1595: }
1596:
1597: if(rd > (int)sizeof(inbuf))
1598: rd=sizeof(inbuf);
1599:
1600: #ifdef USE_CRYPTLIB
1601: if(sbbs->ssh_mode && sock==sbbs->client_socket) {
1.1.1.2 ! root 1602: int err;
! 1603: if(!cryptStatusOK((err=cryptPopData(sbbs->ssh_session, (char*)inbuf, rd, &i)))) {
! 1604: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
! 1605: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
! 1606: if(err==CRYPT_ERROR_TIMEOUT)
! 1607: continue;
! 1608: /* Handle the SSH error here... */
! 1609: lprintf(LOG_WARNING,"Node %d !ERROR %d receiving on Cryptlib session", sbbs->cfg.node_num, err);
! 1610: break;
! 1611: }
1.1 root 1612: else {
1613: if(!i) {
1614: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1615: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1616: continue;
1617: }
1618: rd=i;
1619: }
1620: }
1621: else
1622: #endif
1623: rd = recv(sock, (char*)inbuf, rd, 0);
1624:
1625: if(pthread_mutex_unlock(&sbbs->input_thread_mutex)!=0)
1626: sbbs->errormsg(WHERE,ERR_UNLOCK,"input_thread_mutex",0);
1627:
1628: if(rd == SOCKET_ERROR)
1629: {
1630: #ifdef __unix__
1631: if(sock==sbbs->client_socket) {
1632: #endif
1.1.1.2 ! root 1633: if(!sbbs->online) // sbbs_t::hangup() called?
! 1634: break;
1.1 root 1635: if(ERROR_VALUE == ENOTSOCK)
1636: lprintf(LOG_NOTICE,"Node %d socket closed by peer on receive", sbbs->cfg.node_num);
1637: else if(ERROR_VALUE==ECONNRESET)
1638: lprintf(LOG_NOTICE,"Node %d connection reset by peer on receive", sbbs->cfg.node_num);
1639: else if(ERROR_VALUE==ESHUTDOWN)
1640: lprintf(LOG_NOTICE,"Node %d socket shutdown on receive", sbbs->cfg.node_num);
1641: else if(ERROR_VALUE==ECONNABORTED)
1642: lprintf(LOG_NOTICE,"Node %d connection aborted by peer on receive", sbbs->cfg.node_num);
1643: else
1644: lprintf(LOG_WARNING,"Node %d !ERROR %d receiving from socket %d"
1645: ,sbbs->cfg.node_num, ERROR_VALUE, sock);
1646: break;
1647: #ifdef __unix__
1648: } else {
1649: if(ERROR_VALUE != EAGAIN) {
1650: lprintf(LOG_ERR,"Node %d !ERROR %d on local spy socket %d receive"
1651: , sbbs->cfg.node_num, errno, sock);
1652: close_socket(uspy_socket[sbbs->cfg.node_num-1]);
1653: uspy_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
1654: }
1655: continue;
1656: }
1657: #endif
1658: }
1659:
1660: if(rd == 0 && sock==sbbs->client_socket)
1661: {
1662: lprintf(LOG_NOTICE,"Node %d disconnected", sbbs->cfg.node_num);
1663: break;
1664: }
1665:
1666: total_recv+=rd;
1667: total_pkts++;
1668:
1669: // telbuf and wr are modified to reflect telnet escaped data
1670: wr=rd;
1671: #ifdef __unix__
1672: if(sock!=sbbs->client_socket)
1673: wrbuf=inbuf;
1674: else
1675: #endif
1676: if(sbbs->telnet_mode&TELNET_MODE_OFF)
1677: wrbuf=inbuf;
1678: else
1679: wrbuf=telnet_interpret(sbbs, inbuf, rd, telbuf, wr);
1680: if(wr > (int)sizeof(telbuf))
1681: lprintf(LOG_ERR,"!TELBUF OVERFLOW (%d>%d)",wr,sizeof(telbuf));
1682:
1683: /* First level Ctrl-C checking */
1684: if(!(sbbs->cfg.ctrlkey_passthru&(1<<CTRL_C))
1685: && sbbs->rio_abortable
1686: && !(sbbs->telnet_mode&TELNET_MODE_GATE)
1687: && sbbs->telnet_remote_option[TELNET_BINARY_TX]!=TELNET_WILL
1688: && memchr(wrbuf, CTRL_C, wr)) {
1689: if(RingBufFull(&sbbs->inbuf))
1690: lprintf(LOG_DEBUG,"Node %d Ctrl-C hit with %lu bytes in input buffer"
1691: ,sbbs->cfg.node_num,RingBufFull(&sbbs->inbuf));
1692: if(RingBufFull(&sbbs->outbuf))
1693: lprintf(LOG_DEBUG,"Node %d Ctrl-C hit with %lu bytes in output buffer"
1694: ,sbbs->cfg.node_num,RingBufFull(&sbbs->outbuf));
1695: sbbs->sys_status|=SS_ABORT;
1696: RingBufReInit(&sbbs->inbuf); /* Purge input buffer */
1697: RingBufReInit(&sbbs->outbuf); /* Purge output buffer */
1698: sem_post(&sbbs->inbuf.sem);
1699: continue; // Ignore the entire buffer
1700: }
1701:
1702: avail=RingBufFree(&sbbs->inbuf);
1703:
1704: if(avail<wr)
1705: lprintf(LOG_ERR,"!INPUT BUFFER FULL (%d free)", avail);
1706: else
1707: RingBufWrite(&sbbs->inbuf, wrbuf, wr);
1708: // if(wr>100)
1709: // mswait(500); // Throttle sender
1710: }
1711: sbbs->online=FALSE;
1712: sbbs->sys_status|=SS_ABORT; /* as though Ctrl-C were hit */
1713:
1714: sbbs->input_thread_running = false;
1715: if(node_socket[sbbs->cfg.node_num-1]==INVALID_SOCKET) // Shutdown locally
1716: sbbs->terminated = true; // Signal JS to stop execution
1717:
1718: while(pthread_mutex_destroy(&sbbs->input_thread_mutex)==EBUSY)
1719: mswait(1);
1720:
1721: thread_down();
1722: lprintf(LOG_DEBUG,"Node %d input thread terminated (received %lu bytes in %lu blocks)"
1723: ,sbbs->cfg.node_num, total_recv, total_pkts);
1724: }
1725:
1726: #ifdef USE_CRYPTLIB
1727: /*
1.1.1.2 ! root 1728: * This thread copies anything received from the client to the passthru_socket
1.1 root 1729: * It can only do that when the input thread is locked.
1730: * Luckily, the input thread is currently locked exactly when we want it to be.
1731: * Since the passthru socket is 8-bit clean and does NOT use a protocol,
1732: * we must handle telnet stuff HERE.
1733: * However, for JS stuff, direct operations on client_socket should generally
1734: * be done to the passthru_socket instead... THIS is the biggest problem here.
1735: */
1736: void passthru_output_thread(void* arg)
1737: {
1738: fd_set socket_set;
1739: sbbs_t *sbbs = (sbbs_t*) arg;
1740: struct timeval tv;
1741: int i;
1742: BYTE inbuf[4000];
1743: BYTE telbuf[sizeof(inbuf)];
1744: BYTE *wrbuf;
1745: int rd;
1746: int wr;
1747:
1.1.1.2 ! root 1748: SetThreadName("Passthrough Output");
1.1 root 1749: thread_up(FALSE /* setuid */);
1750:
1751: sbbs->passthru_output_thread_running = true;
1752:
1753: while(sbbs->client_socket!=INVALID_SOCKET && sbbs->passthru_socket!=INVALID_SOCKET && !terminate_server) {
1754: if(!sbbs->input_thread_mutex_locked) {
1755: SLEEP(1);
1756: continue;
1757: }
1758:
1759: FD_ZERO(&socket_set);
1760: FD_SET(sbbs->client_socket,&socket_set);
1761:
1762: tv.tv_sec=1;
1763: tv.tv_usec=0;
1764:
1765: if((i=select(sbbs->client_socket+1,&socket_set,NULL,NULL,&tv))<1) {
1766: if(i==0) {
1767: YIELD(); /* This kludge is necessary on some Linux distros */
1768: continue; /* to allow other threads to lock the input_thread_mutex */
1769: }
1770:
1771: if(sbbs->client_socket==INVALID_SOCKET)
1772: break;
1773: if(ERROR_VALUE == ENOTSOCK)
1774: lprintf(LOG_NOTICE,"Node %d socket closed by peer on input->select", sbbs->cfg.node_num);
1775: else if(ERROR_VALUE==ESHUTDOWN)
1776: lprintf(LOG_NOTICE,"Node %d socket shutdown on input->select", sbbs->cfg.node_num);
1777: else if(ERROR_VALUE==EINTR)
1778: lprintf(LOG_DEBUG,"Node %d passthru output thread interrupted",sbbs->cfg.node_num);
1779: else if(ERROR_VALUE==ECONNRESET)
1780: lprintf(LOG_NOTICE,"Node %d connection reset by peer on input->select", sbbs->cfg.node_num);
1781: else if(ERROR_VALUE==ECONNABORTED)
1782: lprintf(LOG_NOTICE,"Node %d connection aborted by peer on input->select", sbbs->cfg.node_num);
1783: else
1784: lprintf(LOG_WARNING,"Node %d !ERROR %d ->select socket %d"
1785: ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->client_socket);
1786: break;
1787: }
1788:
1789: if(sbbs->client_socket==INVALID_SOCKET)
1790: break;
1791:
1792: rd=sizeof(inbuf);
1793:
1794: #ifdef USE_CRYPTLIB
1795: if(sbbs->ssh_mode) {
1796: if(!cryptStatusOK(cryptPopData(sbbs->ssh_session, (char*)inbuf, rd, &i)))
1797: rd=0;
1798: else {
1799: if(!i)
1800: continue;
1801: rd=i;
1802: }
1803: }
1804: else
1805: #endif
1806: rd = recv(sbbs->client_socket, (char*)inbuf, rd, 0);
1807:
1808: if(rd == SOCKET_ERROR)
1809: {
1810: if(ERROR_VALUE == ENOTSOCK)
1811: lprintf(LOG_NOTICE,"Node %d socket closed by peer on receive", sbbs->cfg.node_num);
1812: else if(ERROR_VALUE==ECONNRESET)
1813: lprintf(LOG_NOTICE,"Node %d connection reset by peer on receive", sbbs->cfg.node_num);
1814: else if(ERROR_VALUE==ESHUTDOWN)
1815: lprintf(LOG_NOTICE,"Node %d socket shutdown on receive", sbbs->cfg.node_num);
1816: else if(ERROR_VALUE==ECONNABORTED)
1817: lprintf(LOG_NOTICE,"Node %d connection aborted by peer on receive", sbbs->cfg.node_num);
1818: else
1819: lprintf(LOG_WARNING,"Node %d !ERROR %d receiving from socket %d"
1820: ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->client_socket);
1821: break;
1822: }
1823:
1824: if(rd == 0)
1825: {
1.1.1.2 ! root 1826: lprintf(LOG_DEBUG,"Node %d passthru input socket disconnected", sbbs->cfg.node_num);
1.1 root 1827: break;
1828: }
1829:
1830: // telbuf and wr are modified to reflect telnet escaped data
1831: wr=rd;
1832: if(sbbs->telnet_mode&TELNET_MODE_OFF)
1833: wrbuf=inbuf;
1834: else
1835: wrbuf=telnet_interpret(sbbs, inbuf, rd, telbuf, wr);
1836: if(wr > (int)sizeof(telbuf))
1837: lprintf(LOG_ERR,"!TELBUF OVERFLOW (%d>%d)",wr,sizeof(telbuf));
1838:
1839: /*
1840: * TODO: This should check for writability etc.
1841: */
1842: sendsocket(sbbs->passthru_socket, (char*)wrbuf, wr);
1843: }
1844:
1845: sbbs->passthru_output_thread_running = false;
1846: }
1847:
1848: /*
1849: * This thread simply copies anything it manages to read from the
1850: * passthru_socket into the output ringbuffer.
1851: */
1852: void passthru_input_thread(void* arg)
1853: {
1854: fd_set r_set;
1855: sbbs_t *sbbs = (sbbs_t*) arg;
1856: struct timeval tv;
1857: BYTE ch;
1858: int i;
1859:
1.1.1.2 ! root 1860: SetThreadName("Passthrough Input");
1.1 root 1861: thread_up(FALSE /* setuid */);
1862:
1863: sbbs->passthru_input_thread_running = true;
1864:
1865: while(sbbs->passthru_socket!=INVALID_SOCKET && !terminate_server) {
1866: tv.tv_sec=1;
1867: tv.tv_usec=0;
1868:
1869: FD_ZERO(&r_set);
1870: FD_SET(sbbs->passthru_socket,&r_set);
1871: if((i=select(sbbs->passthru_socket+1,&r_set,NULL,NULL,&tv))<1) {
1872: if(i==0) {
1873: YIELD(); /* This kludge is necessary on some Linux distros */
1874: continue; /* to allow other threads to lock the input_thread_mutex */
1875: }
1876:
1877: if(sbbs->passthru_socket==INVALID_SOCKET)
1878: break;
1879: if(ERROR_VALUE == ENOTSOCK)
1880: lprintf(LOG_NOTICE,"Node %d socket closed by peer on passthru->select", sbbs->cfg.node_num);
1881: else if(ERROR_VALUE==ESHUTDOWN)
1882: lprintf(LOG_NOTICE,"Node %d socket shutdown on passthru->select", sbbs->cfg.node_num);
1883: else if(ERROR_VALUE==EINTR)
1884: lprintf(LOG_DEBUG,"Node %d passthru thread interrupted",sbbs->cfg.node_num);
1885: else if(ERROR_VALUE==ECONNRESET)
1886: lprintf(LOG_NOTICE,"Node %d connection reset by peer on passthru->select", sbbs->cfg.node_num);
1887: else if(ERROR_VALUE==ECONNABORTED)
1888: lprintf(LOG_NOTICE,"Node %d connection aborted by peer on passthru->select", sbbs->cfg.node_num);
1889: else
1890: lprintf(LOG_WARNING,"Node %d !ERROR %d passthru->select socket %d"
1891: ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->passthru_socket);
1892: break;
1893: }
1894: if(!RingBufFree(&sbbs->outbuf))
1895: continue;
1896:
1897: i = recv(sbbs->passthru_socket, (char*)(&ch), 1, 0);
1898:
1899: if(i == SOCKET_ERROR)
1900: {
1901: if(ERROR_VALUE == ENOTSOCK)
1902: lprintf(LOG_NOTICE,"Node %d passthru socket closed by peer on receive", sbbs->cfg.node_num);
1903: else if(ERROR_VALUE==ECONNRESET)
1904: lprintf(LOG_NOTICE,"Node %d passthru connection reset by peer on receive", sbbs->cfg.node_num);
1905: else if(ERROR_VALUE==ESHUTDOWN)
1906: lprintf(LOG_NOTICE,"Node %d passthru socket shutdown on receive", sbbs->cfg.node_num);
1907: else if(ERROR_VALUE==ECONNABORTED)
1908: lprintf(LOG_NOTICE,"Node %d passthru connection aborted by peer on receive", sbbs->cfg.node_num);
1909: else
1910: lprintf(LOG_WARNING,"Node %d !ERROR %d receiving from passthru socket %d"
1911: ,sbbs->cfg.node_num, ERROR_VALUE, sbbs->passthru_socket);
1912: break;
1913: }
1914:
1915: if(i == 0)
1916: {
1.1.1.2 ! root 1917: lprintf(LOG_NOTICE,"Node %d SSH passthru disconnected", sbbs->cfg.node_num);
1.1 root 1918: break;
1919: }
1920:
1921: if(!RingBufWrite(&sbbs->outbuf, &ch, 1)) {
1922: lprintf(LOG_ERR,"Cannot pass from passthru socket to outbuf");
1923: break;
1924: }
1925: }
1926: if(sbbs->passthru_socket!=INVALID_SOCKET) {
1927: close_socket(sbbs->passthru_socket);
1928: sbbs->passthru_socket=INVALID_SOCKET;
1929: }
1930: thread_down();
1931:
1932: sbbs->passthru_input_thread_running = false;
1933: }
1934: #endif
1935:
1936: void output_thread(void* arg)
1937: {
1938: char node[128];
1939: char stats[128];
1940: BYTE buf[IO_THREAD_BUF_SIZE];
1941: int i;
1942: ulong avail;
1943: ulong total_sent=0;
1944: ulong total_pkts=0;
1945: ulong short_sends=0;
1946: ulong bufbot=0;
1947: ulong buftop=0;
1948: sbbs_t* sbbs = (sbbs_t*) arg;
1949: fd_set socket_set;
1950: struct timeval tv;
1951: ulong mss=IO_THREAD_BUF_SIZE;
1952:
1.1.1.2 ! root 1953: SetThreadName("Node Output");
1.1 root 1954: thread_up(TRUE /* setuid */);
1955:
1956: if(sbbs->cfg.node_num)
1957: SAFEPRINTF(node,"Node %d",sbbs->cfg.node_num);
1958: else
1959: SAFECOPY(node,sbbs->client_name);
1960: #ifdef _DEBUG
1961: lprintf(LOG_DEBUG,"%s output thread started",node);
1962: #endif
1963:
1964: sbbs->output_thread_running = true;
1965: sbbs->console|=CON_R_ECHO;
1966:
1967: #ifdef TCP_MAXSEG
1968: /*
1969: * Auto-tune the highwater mark to be the negotiated MSS for the
1970: * socket (when possible)
1971: */
1972: if(!sbbs->outbuf.highwater_mark) {
1973: socklen_t sl;
1974: sl=sizeof(i);
1975: if(!getsockopt(sbbs->client_socket, IPPROTO_TCP, TCP_MAXSEG, &i, &sl)) {
1976: /* Check for sanity... */
1977: if(i>100) {
1978: sbbs->outbuf.highwater_mark=i;
1979: lprintf(LOG_DEBUG,"Autotuning outbuf highwater mark to %d based on MSS",i);
1980: mss=sbbs->outbuf.highwater_mark;
1981: if(mss>IO_THREAD_BUF_SIZE) {
1982: mss=IO_THREAD_BUF_SIZE;
1983: lprintf(LOG_DEBUG,"MSS (%d) is higher than IO_THREAD_BUF_SIZE (%d)",i,IO_THREAD_BUF_SIZE);
1984: }
1985: }
1986: }
1987: }
1988: #endif
1989:
1990: while(sbbs->client_socket!=INVALID_SOCKET && !terminate_server) {
1991: /*
1992: * I'd like to check the linear buffer against the highwater
1993: * at this point, but it would get too clumsy imho - Deuce
1994: *
1995: * Actually, another option would just be to have the size
1996: * of the linear buffer equal to the MSS... any larger and
1997: * you could have small sends off the end. this would
1998: * probobly be even clumbsier
1999: */
2000: if(bufbot == buftop) {
2001: /* Wait for something to output in the RingBuffer */
2002: if((avail=RingBufFull(&sbbs->outbuf))==0) { /* empty */
2003: if(sem_trywait_block(&sbbs->outbuf.sem,1000))
2004: continue;
2005: /* Check for spurious sem post... */
2006: if((avail=RingBufFull(&sbbs->outbuf))==0)
2007: continue;
2008: }
2009: else
2010: sem_trywait(&sbbs->outbuf.sem);
2011:
2012: /* Wait for full buffer or drain timeout */
2013: if(sbbs->outbuf.highwater_mark) {
2014: if(avail<sbbs->outbuf.highwater_mark) {
2015: sem_trywait_block(&sbbs->outbuf.highwater_sem,startup->outbuf_drain_timeout);
2016: /* We (potentially) blocked, so get fill level again */
2017: avail=RingBufFull(&sbbs->outbuf);
2018: } else
2019: sem_trywait(&sbbs->outbuf.highwater_sem);
2020: }
2021:
2022: /*
2023: * At this point, there's something to send and,
2024: * if the highwater mark is set, the timeout has
2025: * passed or we've hit highwater. Read ring buffer
2026: * into linear buffer.
2027: */
2028: if(avail>sizeof(buf)) {
1.1.1.2 ! root 2029: lprintf(LOG_WARNING,"%s !Insufficient linear output buffer (%lu > %lu)"
1.1 root 2030: ,node, avail, sizeof(buf));
2031: avail=sizeof(buf);
2032: }
2033: /* If we know the MSS, use it as the max send() size. */
2034: if(avail>mss)
2035: avail=mss;
2036: buftop=RingBufRead(&sbbs->outbuf, buf, avail);
2037: bufbot=0;
2038: }
2039:
2040: /* Check socket for writability (using select) */
2041: tv.tv_sec=0;
2042: tv.tv_usec=1000;
2043:
2044: FD_ZERO(&socket_set);
1.1.1.2 ! root 2045: if(sbbs->client_socket==INVALID_SOCKET) // Make the race condition less likely to actually happen... TODO: Fix race
! 2046: continue;
1.1 root 2047: FD_SET(sbbs->client_socket,&socket_set);
2048:
2049: i=select(sbbs->client_socket+1,NULL,&socket_set,NULL,&tv);
2050: if(i==SOCKET_ERROR) {
2051: if(sbbs->client_socket!=INVALID_SOCKET)
1.1.1.2 ! root 2052: lprintf(LOG_ERR,"%s !ERROR %d selecting socket %u for send"
1.1 root 2053: ,node,ERROR_VALUE,sbbs->client_socket);
2054: if(sbbs->cfg.node_num) /* Only break if node output (not server) */
2055: break;
1.1.1.2 ! root 2056: RingBufReInit(&sbbs->outbuf); /* Purge output ring buffer */
! 2057: bufbot=buftop=0; /* Purge linear buffer */
1.1 root 2058: continue;
2059: }
2060: if(i<1) {
2061: continue;
2062: }
2063:
2064: #ifdef USE_CRYPTLIB
2065: if(sbbs->ssh_mode) {
1.1.1.2 ! root 2066: int err;
! 2067: if(!cryptStatusOK((err=cryptPushData(sbbs->ssh_session, (char*)buf+bufbot, buftop-bufbot, &i)))) {
! 2068: /* Handle the SSH error here... */
! 2069: lprintf(LOG_WARNING,"%s !ERROR %d sending on Cryptlib session", node, err);
1.1 root 2070: i=-1;
1.1.1.2 ! root 2071: sbbs->online=FALSE;
! 2072: i=buftop-bufbot; // Pretend we sent it all
! 2073: }
1.1 root 2074: else
2075: cryptFlushData(sbbs->ssh_session);
2076: }
2077: else
2078: #endif
2079: i=sendsocket(sbbs->client_socket, (char*)buf+bufbot, buftop-bufbot);
2080: if(i==SOCKET_ERROR) {
2081: if(ERROR_VALUE == ENOTSOCK)
2082: lprintf(LOG_NOTICE,"%s client socket closed on send", node);
2083: else if(ERROR_VALUE==ECONNRESET)
2084: lprintf(LOG_NOTICE,"%s connection reset by peer on send", node);
2085: else if(ERROR_VALUE==ECONNABORTED)
2086: lprintf(LOG_NOTICE,"%s connection aborted by peer on send", node);
2087: else
1.1.1.2 ! root 2088: lprintf(LOG_WARNING,"%s !ERROR %d sending on socket %d"
1.1 root 2089: ,node, ERROR_VALUE, sbbs->client_socket);
2090: sbbs->online=FALSE;
2091: /* was break; on 4/7/00 */
2092: i=buftop-bufbot; // Pretend we sent it all
2093: }
2094:
2095: if(sbbs->cfg.node_num>0 && !(sbbs->sys_status&SS_FILEXFER)) {
2096: /* Spy on the user locally */
2097: if(startup->node_spybuf!=NULL
2098: && startup->node_spybuf[sbbs->cfg.node_num-1]!=NULL) {
2099: RingBufWrite(startup->node_spybuf[sbbs->cfg.node_num-1],buf+bufbot,i);
2100: /* Signal spy output semaphore? */
2101: if(startup->node_spysem!=NULL
2102: && startup->node_spysem[sbbs->cfg.node_num-1]!=NULL)
2103: sem_post(startup->node_spysem[sbbs->cfg.node_num-1]);
2104: }
2105: /* Spy on the user remotely */
2106: if(spy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET)
2107: sendsocket(spy_socket[sbbs->cfg.node_num-1],(char*)buf+bufbot,i);
2108: #ifdef __unix__
2109: if(uspy_socket[sbbs->cfg.node_num-1]!=INVALID_SOCKET)
2110: sendsocket(uspy_socket[sbbs->cfg.node_num-1],(char*)buf+bufbot,i);
2111: #endif
2112: }
2113:
2114: if(i!=(int)(buftop-bufbot)) {
1.1.1.2 ! root 2115: lprintf(LOG_WARNING,"%s !Short socket send (%u instead of %u)"
1.1 root 2116: ,node, i ,buftop-bufbot);
2117: short_sends++;
2118: }
2119: bufbot+=i;
2120: total_sent+=i;
2121: total_pkts++;
2122: }
2123:
2124: sbbs->spymsg("Disconnected");
2125:
2126: sbbs->output_thread_running = false;
2127:
2128: if(total_sent)
2129: sprintf(stats,"(sent %lu bytes in %lu blocks, %lu average, %lu short)"
2130: ,total_sent, total_pkts, total_sent/total_pkts, short_sends);
2131: else
2132: stats[0]=0;
2133:
2134: thread_down();
2135: lprintf(LOG_DEBUG,"%s output thread terminated %s", node, stats);
2136: }
2137:
2138: void event_thread(void* arg)
2139: {
2140: ulong stack_frame;
2141: char str[MAX_PATH+1];
2142: char bat_list[MAX_PATH+1];
2143: char semfile[MAX_PATH+1];
2144: int i,j,k;
2145: int file;
2146: int offset;
2147: bool check_semaphores;
2148: bool packed_rep;
1.1.1.2 ! root 2149: ulong l;
! 2150: /* TODO: This is a silly hack... */
! 2151: uint32_t l32;
1.1 root 2152: time_t now;
2153: time_t start;
2154: time_t lastsemchk=0;
2155: time_t lastnodechk=0;
1.1.1.2 ! root 2156: time32_t lastprepack=0;
! 2157: time_t tmptime;
1.1 root 2158: node_t node;
2159: glob_t g;
2160: sbbs_t* sbbs = (sbbs_t*) arg;
2161: struct tm now_tm;
2162: struct tm tm;
2163:
1.1.1.2 ! root 2164: eprintf(LOG_INFO,"BBS Events thread started");
1.1 root 2165:
2166: sbbs->event_thread_running = true;
2167:
2168: sbbs_srand(); /* Seed random number generator */
2169:
1.1.1.2 ! root 2170: SetThreadName("BBS Events");
1.1 root 2171: thread_up(TRUE /* setuid */);
2172:
2173: #ifdef JAVASCRIPT
2174: if(!(startup->options&BBS_OPT_NO_JAVASCRIPT)) {
1.1.1.2 ! root 2175: if(!sbbs->js_init(&stack_frame)) /* This must be done in the context of the events thread */
1.1 root 2176: lprintf(LOG_ERR,"!JavaScript Initialization FAILURE");
2177: }
2178: #endif
2179:
2180: // Read TIME.DAB
2181: SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
2182: if((file=sbbs->nopen(str,O_RDWR|O_CREAT))==-1)
2183: sbbs->errormsg(WHERE,ERR_OPEN,str,0);
2184: else {
2185: for(i=0;i<sbbs->cfg.total_events;i++) {
2186: sbbs->cfg.event[i]->last=0;
1.1.1.2 ! root 2187: if(filelength(file)<(long)(sizeof(time32_t)*(i+1))) {
1.1 root 2188: eprintf(LOG_WARNING,"Initializing last run time for event: %s"
2189: ,sbbs->cfg.event[i]->code);
1.1.1.2 ! root 2190: write(file,&sbbs->cfg.event[i]->last,sizeof(sbbs->cfg.event[i]->last));
1.1 root 2191: } else {
1.1.1.2 ! root 2192: if(read(file,&sbbs->cfg.event[i]->last,sizeof(sbbs->cfg.event[i]->last))!=sizeof(sbbs->cfg.event[i]->last))
! 2193: sbbs->errormsg(WHERE,ERR_READ,str,sizeof(time32_t));
1.1 root 2194: }
2195: /* Event always runs after initialization? */
2196: if(sbbs->cfg.event[i]->misc&EVENT_INIT)
2197: sbbs->cfg.event[i]->last=-1;
2198: }
2199: lastprepack=0;
1.1.1.2 ! root 2200: read(file,&lastprepack,sizeof(lastprepack)); /* expected to fail first time */
1.1 root 2201: close(file);
2202: }
2203:
2204: // Read QNET.DAB
2205: SAFEPRINTF(str,"%sqnet.dab",sbbs->cfg.ctrl_dir);
2206: if((file=sbbs->nopen(str,O_RDWR|O_CREAT))==-1)
2207: sbbs->errormsg(WHERE,ERR_OPEN,str,0);
2208: else {
2209: for(i=0;i<sbbs->cfg.total_qhubs;i++) {
2210: sbbs->cfg.qhub[i]->last=0;
1.1.1.2 ! root 2211: if(filelength(file)<(long)(sizeof(time32_t)*(i+1))) {
1.1 root 2212: eprintf(LOG_WARNING,"Initializing last call-out time for QWKnet hub: %s"
2213: ,sbbs->cfg.qhub[i]->id);
1.1.1.2 ! root 2214: write(file,&sbbs->cfg.qhub[i]->last,sizeof(sbbs->cfg.qhub[i]->last));
1.1 root 2215: } else {
1.1.1.2 ! root 2216: if(read(file,&sbbs->cfg.qhub[i]->last,sizeof(sbbs->cfg.qhub[i]->last))!=sizeof(sbbs->cfg.qhub[i]->last))
! 2217: sbbs->errormsg(WHERE,ERR_READ,str,sizeof(sbbs->cfg.qhub[i]->last));
1.1 root 2218: }
2219: }
2220: close(file);
2221: }
2222:
2223: // Read PNET.DAB
2224: SAFEPRINTF(str,"%spnet.dab",sbbs->cfg.ctrl_dir);
2225: if((file=sbbs->nopen(str,O_RDWR|O_CREAT))==-1)
2226: sbbs->errormsg(WHERE,ERR_OPEN,str,0);
2227: else {
2228: for(i=0;i<sbbs->cfg.total_phubs;i++) {
2229: sbbs->cfg.phub[i]->last=0;
1.1.1.2 ! root 2230: if(filelength(file)<(long)(sizeof(time32_t)*(i+1)))
! 2231: write(file,&sbbs->cfg.phub[i]->last,sizeof(sbbs->cfg.phub[i]->last));
1.1 root 2232: else
1.1.1.2 ! root 2233: read(file,&sbbs->cfg.phub[i]->last,sizeof(sbbs->cfg.phub[i]->last));
1.1 root 2234: }
2235: close(file);
2236: }
2237:
2238: while(!sbbs->terminated && !terminate_server) {
2239:
2240: if(startup->options&BBS_OPT_NO_EVENTS) {
2241: SLEEP(1000);
2242: continue;
2243: }
2244:
2245: now=time(NULL);
2246: localtime_r(&now,&now_tm);
2247:
2248: if(now-lastsemchk>=sbbs->cfg.node_sem_check) {
2249: check_semaphores=true;
2250: lastsemchk=now;
2251: } else
2252: check_semaphores=false;
2253:
2254: sbbs->online=FALSE; /* reset this from ON_LOCAL */
2255:
2256: /* QWK events */
2257: if(check_semaphores && !(startup->options&BBS_OPT_NO_QWK_EVENTS)) {
2258: /* Import any REP files that have magically appeared (via FTP perhaps) */
2259: SAFEPRINTF(str,"%sfile/",sbbs->cfg.data_dir);
2260: offset=strlen(str);
2261: strcat(str,"*.rep");
2262: glob(str,0,NULL,&g);
2263: for(i=0;i<(int)g.gl_pathc;i++) {
2264: sbbs->useron.number=atoi(g.gl_pathv[i]+offset);
2265: getuserdat(&sbbs->cfg,&sbbs->useron);
2266: if(sbbs->useron.number && flength(g.gl_pathv[i])>0) {
2267: SAFEPRINTF(semfile,"%s.lock",g.gl_pathv[i]);
1.1.1.2 ! root 2268: if(!fmutex(semfile,startup->host_name,24*60*60)) {
! 2269: eprintf(LOG_INFO,"%s exists (unpack in progress?)", semfile);
1.1 root 2270: continue;
1.1.1.2 ! root 2271: }
1.1 root 2272: sbbs->online=ON_LOCAL;
2273: eprintf(LOG_INFO,"Un-packing QWK Reply packet from %s",sbbs->useron.alias);
2274: sbbs->getusrsubs();
2275: sbbs->unpack_rep(g.gl_pathv[i]);
1.1.1.2 ! root 2276: delfiles(sbbs->cfg.temp_dir,ALLFILES); /* clean-up temp_dir after unpacking */
1.1 root 2277: sbbs->batch_create_list(); /* FREQs? */
2278: sbbs->batdn_total=0;
2279:
2280: /* putuserdat? */
2281: remove(g.gl_pathv[i]);
2282: remove(semfile);
2283: }
2284: }
2285: globfree(&g);
2286:
2287: /* Create any QWK files that have magically appeared (via FTP perhaps) */
2288: SAFEPRINTF(str,"%spack*.now",sbbs->cfg.data_dir);
2289: offset=strlen(sbbs->cfg.data_dir)+4;
2290: glob(str,0,NULL,&g);
2291: for(i=0;i<(int)g.gl_pathc;i++) {
1.1.1.2 ! root 2292: eprintf(LOG_INFO,"QWK pack semaphore signaled: %s", g.gl_pathv[i]);
1.1 root 2293: sbbs->useron.number=atoi(g.gl_pathv[i]+offset);
2294: SAFEPRINTF2(semfile,"%spack%04u.lock",sbbs->cfg.data_dir,sbbs->useron.number);
2295: if(!fmutex(semfile,startup->host_name,24*60*60)) {
1.1.1.2 ! root 2296: eprintf(LOG_INFO,"%s exists (pack in progress?)", semfile);
1.1 root 2297: continue;
2298: }
2299: getuserdat(&sbbs->cfg,&sbbs->useron);
2300: if(sbbs->useron.number && !(sbbs->useron.misc&(DELETED|INACTIVE))) {
2301: eprintf(LOG_INFO,"Packing QWK Message Packet for %s",sbbs->useron.alias);
2302: sbbs->online=ON_LOCAL;
2303: sbbs->getmsgptrs();
2304: sbbs->getusrsubs();
2305: sbbs->batdn_total=0;
2306:
2307: sbbs->last_ns_time=sbbs->ns_time=sbbs->useron.ns_time;
2308: SAFEPRINTF2(bat_list,"%sfile/%04u.dwn",sbbs->cfg.data_dir,sbbs->useron.number);
2309: sbbs->batch_add_list(bat_list);
2310:
2311: SAFEPRINTF3(str,"%sfile%c%04u.qwk"
2312: ,sbbs->cfg.data_dir,PATH_DELIM,sbbs->useron.number);
2313: if(sbbs->pack_qwk(str,&l,true /* pre-pack/off-line */)) {
2314: eprintf(LOG_INFO,"Packing completed");
2315: sbbs->qwk_success(l,0,1);
2316: sbbs->putmsgptrs();
2317: remove(bat_list);
2318: } else
2319: eprintf(LOG_INFO,"No packet created (no new messages)");
2320: delfiles(sbbs->cfg.temp_dir,ALLFILES);
2321: sbbs->online=FALSE;
2322: }
2323: remove(g.gl_pathv[i]);
2324: remove(semfile);
2325: }
2326: globfree(&g);
2327:
2328: /* Create (pre-pack) QWK files for users configured as such */
2329: SAFEPRINTF(semfile,"%sprepack.now",sbbs->cfg.data_dir);
2330: if(sbbs->cfg.preqwk_ar[0]
2331: && (fexistcase(semfile) || (now-lastprepack)/60>(60*24))) {
2332: j=lastuser(&sbbs->cfg);
2333: eprintf(LOG_INFO,"Pre-packing QWK Message packets...");
2334: for(i=1;i<=j;i++) {
2335:
2336: SAFEPRINTF2(str,"%5u of %-5u",i,j);
2337: //status(str);
2338: sbbs->useron.number=i;
2339: getuserdat(&sbbs->cfg,&sbbs->useron);
2340:
2341: if(sbbs->useron.number
2342: && !(sbbs->useron.misc&(DELETED|INACTIVE)) /* Pre-QWK */
1.1.1.2 ! root 2343: && sbbs->chk_ar(sbbs->cfg.preqwk_ar,&sbbs->useron,/* client: */NULL)) {
1.1 root 2344: for(k=1;k<=sbbs->cfg.sys_nodes;k++) {
2345: if(sbbs->getnodedat(k,&node,0)!=0)
2346: continue;
2347: if((node.status==NODE_INUSE || node.status==NODE_QUIET
2348: || node.status==NODE_LOGON) && node.useron==i)
2349: break;
2350: }
2351: if(k<=sbbs->cfg.sys_nodes) /* Don't pre-pack with user online */
2352: continue;
2353: eprintf(LOG_INFO,"Pre-packing QWK for %s",sbbs->useron.alias);
2354: sbbs->online=ON_LOCAL;
2355: sbbs->getmsgptrs();
2356: sbbs->getusrsubs();
2357: sbbs->batdn_total=0;
2358: SAFEPRINTF3(str,"%sfile%c%04u.qwk"
2359: ,sbbs->cfg.data_dir,PATH_DELIM,sbbs->useron.number);
2360: if(sbbs->pack_qwk(str,&l,true /* pre-pack */)) {
2361: sbbs->qwk_success(l,0,1);
2362: sbbs->putmsgptrs();
2363: }
2364: delfiles(sbbs->cfg.temp_dir,ALLFILES);
2365: sbbs->online=FALSE;
2366: }
2367: }
2368: lastprepack=now;
2369: SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
2370: if((file=sbbs->nopen(str,O_WRONLY))==-1) {
2371: sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY);
2372: break;
2373: }
2374: lseek(file,(long)sbbs->cfg.total_events*4L,SEEK_SET);
1.1.1.2 ! root 2375: write(file,&lastprepack,sizeof(lastprepack));
1.1 root 2376: close(file);
2377:
2378: remove(semfile);
2379: //status(STATUS_WFC);
2380: }
2381: }
2382:
2383: if(check_semaphores) {
2384:
2385: /* Run daily maintenance? */
2386: sbbs->cfg.node_num=0;
2387: sbbs->logonstats();
2388: if(sbbs->sys_status&SS_DAILY)
2389: sbbs->daily_maint();
2390:
2391: /* Node Daily Events */
2392: for(i=first_node;i<=last_node;i++) {
2393: // Node Daily Event
2394: node.status=NODE_INVALID_STATUS;
2395: if(sbbs->getnodedat(i,&node,0)!=0)
2396: continue;
2397: if(node.misc&NODE_EVENT && node.status==NODE_WFC) {
2398: sbbs->getnodedat(i,&node,1);
2399: node.status=NODE_EVENT_RUNNING;
2400: sbbs->putnodedat(i,&node);
2401: if(sbbs->cfg.node_daily[0]) {
2402: sbbs->cfg.node_num=i;
2403: strcpy(sbbs->cfg.node_dir, sbbs->cfg.node_path[i-1]);
2404:
2405: eprintf(LOG_INFO,"Running node %d daily event",i);
2406: sbbs->online=ON_LOCAL;
2407: sbbs->logentry("!:","Run node daily event");
2408: sbbs->external(
2409: sbbs->cmdstr(sbbs->cfg.node_daily,nulstr,nulstr,NULL)
2410: ,EX_OFFLINE);
2411: }
2412: sbbs->getnodedat(i,&node,1);
2413: node.misc&=~NODE_EVENT;
2414: node.status=NODE_WFC;
2415: node.useron=0;
2416: sbbs->putnodedat(i,&node);
2417: }
2418: }
2419:
2420: /* QWK Networking Call-out sempahores */
2421: for(i=0;i<sbbs->cfg.total_qhubs;i++) {
2422: if(sbbs->cfg.qhub[i]->node<first_node
2423: || sbbs->cfg.qhub[i]->node>last_node)
2424: continue;
2425: if(sbbs->cfg.qhub[i]->last==-1) // already signaled
2426: continue;
2427: SAFEPRINTF2(str,"%sqnet/%s.now",sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id);
2428: if(fexistcase(str)) {
2429: strcpy(str,sbbs->cfg.qhub[i]->id);
2430: eprintf(LOG_INFO,"Semaphore signaled for QWK Network Hub: %s",strupr(str));
2431: sbbs->cfg.qhub[i]->last=-1;
2432: }
2433: }
2434:
2435: /* Timed Event sempahores */
2436: for(i=0;i<sbbs->cfg.total_events;i++) {
2437: if((sbbs->cfg.event[i]->node<first_node
2438: || sbbs->cfg.event[i]->node>last_node)
2439: && !(sbbs->cfg.event[i]->misc&EVENT_EXCL))
2440: continue; // ignore non-exclusive events for other instances
2441: if(sbbs->cfg.event[i]->misc&EVENT_DISABLED)
2442: continue;
2443: if(sbbs->cfg.event[i]->last==-1) // already signaled
2444: continue;
2445: SAFEPRINTF2(str,"%s%s.now",sbbs->cfg.data_dir,sbbs->cfg.event[i]->code);
2446: if(fexistcase(str)) {
2447: strcpy(str,sbbs->cfg.event[i]->code);
2448: eprintf(LOG_INFO,"Semaphore signaled for Timed Event: %s",strupr(str));
2449: sbbs->cfg.event[i]->last=-1;
2450: }
2451: }
2452: }
2453:
2454: /* QWK Networking Call-out Events */
2455: for(i=0;i<sbbs->cfg.total_qhubs;i++) {
2456: if(sbbs->cfg.qhub[i]->node<first_node ||
2457: sbbs->cfg.qhub[i]->node>last_node)
2458: continue;
2459:
2460: if(check_semaphores) {
2461: // See if any packets have come in
1.1.1.2 ! root 2462: SAFEPRINTF2(str,"%s%s.q??",sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id);
! 2463: glob(str,GLOB_NOSORT,NULL,&g);
! 2464: for(j=0;j<(int)g.gl_pathc;j++) {
! 2465: SAFECOPY(str,g.gl_pathv[j]);
! 2466: if(flength(str)>0) { /* silently ignore 0-byte QWK packets */
1.1 root 2467: eprintf(LOG_DEBUG,"Inbound QWK Packet detected: %s", str);
2468: sbbs->online=ON_LOCAL;
2469: sbbs->console|=CON_L_ECHO;
2470: if(sbbs->unpack_qwk(str,i)==false) {
2471: char newname[MAX_PATH+1];
2472: SAFEPRINTF2(newname,"%s.%lx.bad",str,(long)now);
2473: remove(newname);
2474: if(rename(str,newname)==0) {
2475: char logmsg[MAX_PATH*3];
2476: SAFEPRINTF2(logmsg,"%s renamed to %s",str,newname);
1.1.1.2 ! root 2477: sbbs->logline(LOG_NOTICE,"Q!",logmsg);
1.1 root 2478: }
2479: }
1.1.1.2 ! root 2480: delfiles(sbbs->cfg.temp_dir,ALLFILES);
1.1 root 2481: sbbs->console&=~CON_L_ECHO;
2482: sbbs->online=FALSE;
2483: remove(str);
2484: }
2485: }
1.1.1.2 ! root 2486: globfree(&g);
1.1 root 2487: }
2488:
2489: /* Qnet call out based on time */
1.1.1.2 ! root 2490: tmptime=sbbs->cfg.qhub[i]->last;
! 2491: if(localtime_r(&tmptime,&tm)==NULL)
1.1 root 2492: memset(&tm,0,sizeof(tm));
2493: if((sbbs->cfg.qhub[i]->last==-1L /* or frequency */
2494: || ((sbbs->cfg.qhub[i]->freq
2495: && (now-sbbs->cfg.qhub[i]->last)/60>sbbs->cfg.qhub[i]->freq)
2496: || (sbbs->cfg.qhub[i]->time
2497: && (now_tm.tm_hour*60)+now_tm.tm_min>=sbbs->cfg.qhub[i]->time
2498: && (now_tm.tm_mday!=tm.tm_mday || now_tm.tm_mon!=tm.tm_mon)))
2499: && sbbs->cfg.qhub[i]->days&(1<<now_tm.tm_wday))) {
2500: SAFEPRINTF2(str,"%sqnet/%s.now"
2501: ,sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id);
2502: if(fexistcase(str))
2503: remove(str); /* Remove semaphore file */
2504: SAFEPRINTF2(str,"%sqnet/%s.ptr"
2505: ,sbbs->cfg.data_dir,sbbs->cfg.qhub[i]->id);
2506: file=sbbs->nopen(str,O_RDONLY);
2507: for(j=0;j<sbbs->cfg.qhub[i]->subs;j++) {
2508: sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr=0;
2509: if(file!=-1) {
1.1.1.2 ! root 2510: lseek(file,sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]]->ptridx*sizeof(int32_t),SEEK_SET);
! 2511: read(file,&sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr,sizeof(sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr));
1.1 root 2512: }
2513: }
2514: if(file!=-1)
2515: close(file);
2516: sbbs->console|=CON_L_ECHO;
2517: packed_rep=sbbs->pack_rep(i);
2518: sbbs->console&=~CON_L_ECHO;
2519: if(packed_rep) {
2520: if((file=sbbs->nopen(str,O_WRONLY|O_CREAT))==-1)
2521: sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_CREAT);
2522: else {
2523: for(j=l=0;j<sbbs->cfg.qhub[i]->subs;j++) {
2524: while(filelength(file)<
1.1.1.2 ! root 2525: sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]]->ptridx*4L) {
! 2526: l32=l;
! 2527: write(file,&l32,4); /* initialize ptrs to null */
! 2528: }
1.1 root 2529: lseek(file
1.1.1.2 ! root 2530: ,sbbs->cfg.sub[sbbs->cfg.qhub[i]->sub[j]]->ptridx*sizeof(int32_t)
1.1 root 2531: ,SEEK_SET);
1.1.1.2 ! root 2532: write(file,&sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr,sizeof(sbbs->subscan[sbbs->cfg.qhub[i]->sub[j]].ptr));
1.1 root 2533: }
2534: close(file);
2535: }
2536: }
2537: delfiles(sbbs->cfg.temp_dir,ALLFILES);
2538:
2539: sbbs->cfg.qhub[i]->last=time(NULL);
2540: SAFEPRINTF(str,"%sqnet.dab",sbbs->cfg.ctrl_dir);
2541: if((file=sbbs->nopen(str,O_WRONLY))==-1) {
2542: sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY);
2543: break;
2544: }
1.1.1.2 ! root 2545: lseek(file,sizeof(time32_t)*i,SEEK_SET);
! 2546: write(file,&sbbs->cfg.qhub[i]->last,sizeof(sbbs->cfg.qhub[i]->last));
1.1 root 2547: close(file);
2548:
2549: if(sbbs->cfg.qhub[i]->call[0]) {
2550: sbbs->cfg.node_num=sbbs->cfg.qhub[i]->node;
2551: if(sbbs->cfg.node_num<1)
2552: sbbs->cfg.node_num=1;
2553: strcpy(sbbs->cfg.node_dir, sbbs->cfg.node_path[sbbs->cfg.node_num-1]);
2554: eprintf(LOG_INFO,"QWK Network call-out: %s",sbbs->cfg.qhub[i]->id);
2555: sbbs->online=ON_LOCAL;
2556: sbbs->external(
2557: sbbs->cmdstr(sbbs->cfg.qhub[i]->call
2558: ,sbbs->cfg.qhub[i]->id,sbbs->cfg.qhub[i]->id,NULL)
2559: ,EX_OFFLINE|EX_SH); /* sh for Unix perl scripts */
2560: }
2561: }
2562: }
2563:
2564: /* PostLink Networking Call-out Events */
2565: for(i=0;i<sbbs->cfg.total_phubs;i++) {
2566: if(sbbs->cfg.phub[i]->node<first_node
2567: || sbbs->cfg.phub[i]->node>last_node)
2568: continue;
2569: /* PostLink call out based on time */
1.1.1.2 ! root 2570: tmptime=sbbs->cfg.phub[i]->last;
! 2571: if(localtime_r(&tmptime,&tm)==NULL)
1.1 root 2572: memset(&tm,0,sizeof(tm));
2573: if(sbbs->cfg.phub[i]->last==-1
2574: || (((sbbs->cfg.phub[i]->freq /* or frequency */
2575: && (now-sbbs->cfg.phub[i]->last)/60>sbbs->cfg.phub[i]->freq)
2576: || (sbbs->cfg.phub[i]->time
2577: && (now_tm.tm_hour*60)+now_tm.tm_min>=sbbs->cfg.phub[i]->time
2578: && (now_tm.tm_mday!=tm.tm_mday || now_tm.tm_mon!=tm.tm_mon)))
2579: && sbbs->cfg.phub[i]->days&(1<<now_tm.tm_wday))) {
2580:
2581: sbbs->cfg.phub[i]->last=time(NULL);
2582: SAFEPRINTF(str,"%spnet.dab",sbbs->cfg.ctrl_dir);
2583: if((file=sbbs->nopen(str,O_WRONLY))==-1) {
2584: sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY);
2585: break;
2586: }
1.1.1.2 ! root 2587: lseek(file,sizeof(time32_t)*i,SEEK_SET);
! 2588: write(file,&sbbs->cfg.phub[i]->last,sizeof(sbbs->cfg.phub[i]->last));
1.1 root 2589: close(file);
2590:
2591: if(sbbs->cfg.phub[i]->call[0]) {
2592: sbbs->cfg.node_num=sbbs->cfg.phub[i]->node;
2593: if(sbbs->cfg.node_num<1)
2594: sbbs->cfg.node_num=1;
2595: strcpy(sbbs->cfg.node_dir, sbbs->cfg.node_path[sbbs->cfg.node_num-1]);
2596: eprintf(LOG_INFO,"PostLink Network call-out: %s",sbbs->cfg.phub[i]->name);
2597: sbbs->online=ON_LOCAL;
2598: sbbs->external(
2599: sbbs->cmdstr(sbbs->cfg.phub[i]->call,nulstr,nulstr,NULL)
2600: ,EX_OFFLINE|EX_SH); /* sh for Unix perl scripts */
2601: }
2602: }
2603: }
2604:
2605: /* Timed Events */
2606: for(i=0;i<sbbs->cfg.total_events;i++) {
2607: if(!sbbs->cfg.event[i]->node
2608: || sbbs->cfg.event[i]->node>sbbs->cfg.sys_nodes)
2609: continue; // ignore events for invalid nodes
2610:
2611: if(sbbs->cfg.event[i]->misc&EVENT_DISABLED)
2612: continue;
2613:
2614: if((sbbs->cfg.event[i]->node<first_node
2615: || sbbs->cfg.event[i]->node>last_node)
2616: && !(sbbs->cfg.event[i]->misc&EVENT_EXCL))
2617: continue; // ignore non-exclusive events for other instances
2618:
1.1.1.2 ! root 2619: tmptime=sbbs->cfg.event[i]->last;
! 2620: if(localtime_r(&tmptime,&tm)==NULL)
1.1 root 2621: memset(&tm,0,sizeof(tm));
2622: if(sbbs->cfg.event[i]->last==-1 ||
2623: (((sbbs->cfg.event[i]->freq
2624: && (now-sbbs->cfg.event[i]->last)/60>sbbs->cfg.event[i]->freq)
2625: || (!sbbs->cfg.event[i]->freq
2626: && (now_tm.tm_hour*60)+now_tm.tm_min>=sbbs->cfg.event[i]->time
2627: && (now_tm.tm_mday!=tm.tm_mday || now_tm.tm_mon!=tm.tm_mon)))
2628: && sbbs->cfg.event[i]->days&(1<<now_tm.tm_wday)
2629: && (sbbs->cfg.event[i]->mdays==0
1.1.1.2 ! root 2630: || sbbs->cfg.event[i]->mdays&(1<<now_tm.tm_mday))
! 2631: && (sbbs->cfg.event[i]->months==0
! 2632: || sbbs->cfg.event[i]->months&(1<<now_tm.tm_mon))))
1.1 root 2633: {
2634: if(sbbs->cfg.event[i]->misc&EVENT_EXCL) { /* exclusive event */
2635:
2636: if(sbbs->cfg.event[i]->node<first_node
2637: || sbbs->cfg.event[i]->node>last_node) {
2638: eprintf(LOG_INFO,"Waiting for node %d to run timed event: %s"
2639: ,sbbs->cfg.event[i]->node,sbbs->cfg.event[i]->code);
2640: eprintf(LOG_DEBUG,"%s event last run: %s (0x%08lx)"
2641: ,sbbs->cfg.event[i]->code
1.1.1.2 ! root 2642: ,timestr(&sbbs->cfg, sbbs->cfg.event[i]->last, str)
1.1 root 2643: ,sbbs->cfg.event[i]->last);
2644: lastnodechk=0; /* really last event time check */
2645: start=time(NULL);
2646: while(!sbbs->terminated) {
2647: mswait(1000);
2648: now=time(NULL);
2649: if(now-start>10 && now-lastnodechk<10)
2650: continue;
2651: for(j=first_node;j<=last_node;j++) {
2652: if(sbbs->getnodedat(j,&node,1)!=0)
2653: continue;
2654: if(node.status==NODE_WFC)
2655: node.status=NODE_EVENT_LIMBO;
2656: node.aux=sbbs->cfg.event[i]->node;
2657: sbbs->putnodedat(j,&node);
2658: }
2659:
2660: lastnodechk=now;
2661: SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
2662: if((file=sbbs->nopen(str,O_RDONLY))==-1) {
2663: sbbs->errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
2664: sbbs->cfg.event[i]->last=now;
2665: continue;
2666: }
2667: lseek(file,(long)i*4L,SEEK_SET);
1.1.1.2 ! root 2668: read(file,&sbbs->cfg.event[i]->last,sizeof(sbbs->cfg.event[i]->last));
1.1 root 2669: close(file);
2670: if(now-sbbs->cfg.event[i]->last<(60*60)) /* event is done */
2671: break;
2672: if(now-start>(90*60)) {
2673: eprintf(LOG_WARNING,"!TIMEOUT waiting for event to complete");
2674: break;
2675: }
2676: }
2677: SAFEPRINTF2(str,"%s%s.now",sbbs->cfg.data_dir,sbbs->cfg.event[i]->code);
2678: if(fexistcase(str))
2679: remove(str);
2680: sbbs->cfg.event[i]->last=now;
2681: } else { // Exclusive event to run on a node under our control
2682: eprintf(LOG_INFO,"Waiting for all nodes to become inactive before "
2683: "running timed event: %s",sbbs->cfg.event[i]->code);
2684: lastnodechk=0;
2685: start=time(NULL);
2686: while(!sbbs->terminated) {
2687: mswait(1000);
2688: now=time(NULL);
2689: if(now-start>10 && now-lastnodechk<10)
2690: continue;
2691: lastnodechk=now;
2692: // Check/change the status of the nodes that we're in control of
2693: for(j=first_node;j<=last_node;j++) {
2694: if(sbbs->getnodedat(j,&node,1)!=0)
2695: continue;
2696: if(node.status==NODE_WFC) {
2697: if(j==sbbs->cfg.event[i]->node)
2698: node.status=NODE_EVENT_WAITING;
2699: else
2700: node.status=NODE_EVENT_LIMBO;
2701: node.aux=sbbs->cfg.event[i]->node;
2702: }
2703: sbbs->putnodedat(j,&node);
2704: }
2705:
2706: for(j=1;j<=sbbs->cfg.sys_nodes;j++) {
2707: if(sbbs->getnodedat(j,&node,0)!=0)
2708: continue;
2709: if(j==sbbs->cfg.event[i]->node) {
2710: if(node.status!=NODE_EVENT_WAITING)
2711: break;
2712: } else {
2713: if(node.status!=NODE_OFFLINE
2714: && node.status!=NODE_EVENT_LIMBO)
2715: break;
2716: }
2717: }
2718: if(j>sbbs->cfg.sys_nodes) /* all nodes either offline or in limbo */
2719: break;
2720: eprintf(LOG_DEBUG,"Waiting for node %d (status=%d)",j,node.status);
2721: if(now-start>(90*60)) {
2722: eprintf(LOG_WARNING,"!TIMEOUT waiting for node %d to become inactive",j);
2723: break;
2724: }
2725: }
2726: }
2727: }
2728: #if 0 // removed Jun-23-2002
2729: else { /* non-exclusive */
2730: sbbs->getnodedat(sbbs->cfg.event[i]->node,&node,0);
2731: if(node.status!=NODE_WFC)
2732: continue;
2733: }
2734: #endif
2735: if(sbbs->cfg.event[i]->node<first_node
2736: || sbbs->cfg.event[i]->node>last_node) {
2737: eprintf(LOG_NOTICE,"Changing node status for nodes %d through %d to WFC"
2738: ,first_node,last_node);
2739: sbbs->cfg.event[i]->last=now;
2740: for(j=first_node;j<=last_node;j++) {
2741: node.status=NODE_INVALID_STATUS;
2742: if(sbbs->getnodedat(j,&node,1)!=0)
2743: continue;
2744: node.status=NODE_WFC;
2745: sbbs->putnodedat(j,&node);
2746: }
2747: }
2748: else {
2749: sbbs->cfg.node_num=sbbs->cfg.event[i]->node;
2750: if(sbbs->cfg.node_num<1)
2751: sbbs->cfg.node_num=1;
2752: strcpy(sbbs->cfg.node_dir, sbbs->cfg.node_path[sbbs->cfg.node_num-1]);
2753:
2754: SAFEPRINTF2(str,"%s%s.now",sbbs->cfg.data_dir,sbbs->cfg.event[i]->code);
2755: if(fexistcase(str))
2756: remove(str);
2757: if(sbbs->cfg.event[i]->misc&EVENT_EXCL) {
2758: sbbs->getnodedat(sbbs->cfg.event[i]->node,&node,1);
2759: node.status=NODE_EVENT_RUNNING;
2760: sbbs->putnodedat(sbbs->cfg.event[i]->node,&node);
2761: }
2762: strcpy(str,sbbs->cfg.event[i]->code);
2763: eprintf(LOG_INFO,"Running timed event: %s",strupr(str));
2764: int ex_mode = EX_OFFLINE;
2765: if(!(sbbs->cfg.event[i]->misc&EVENT_EXCL)
2766: && sbbs->cfg.event[i]->misc&EX_BG)
2767: ex_mode |= EX_BG;
2768: if(sbbs->cfg.event[i]->misc&XTRN_SH)
2769: ex_mode |= EX_SH;
2770: ex_mode|=(sbbs->cfg.event[i]->misc&EX_NATIVE);
2771: sbbs->online=ON_LOCAL;
1.1.1.2 ! root 2772: {
! 2773: int result=
! 2774: sbbs->external(
! 2775: sbbs->cmdstr(sbbs->cfg.event[i]->cmd,nulstr,sbbs->cfg.event[i]->dir,NULL)
! 2776: ,ex_mode
! 2777: ,sbbs->cfg.event[i]->dir);
! 2778: if(!(ex_mode&EX_BG))
! 2779: eprintf(LOG_INFO,"Timed event: %s returned %d",strupr(str), result);
! 2780: }
1.1 root 2781: sbbs->cfg.event[i]->last=time(NULL);
2782: SAFEPRINTF(str,"%stime.dab",sbbs->cfg.ctrl_dir);
2783: if((file=sbbs->nopen(str,O_WRONLY))==-1) {
2784: sbbs->errormsg(WHERE,ERR_OPEN,str,O_WRONLY);
2785: break;
2786: }
2787: lseek(file,(long)i*4L,SEEK_SET);
1.1.1.2 ! root 2788: write(file,&sbbs->cfg.event[i]->last,sizeof(sbbs->cfg.event[i]->last));
1.1 root 2789: close(file);
2790:
2791: if(sbbs->cfg.event[i]->misc&EVENT_EXCL) { /* exclusive event */
2792: // Check/change the status of the nodes that we're in control of
2793: for(j=first_node;j<=last_node;j++) {
2794: node.status=NODE_INVALID_STATUS;
2795: if(sbbs->getnodedat(j,&node,1)!=0)
2796: continue;
2797: node.status=NODE_WFC;
2798: sbbs->putnodedat(j,&node);
2799: }
2800: }
2801: }
2802: }
2803: }
2804: mswait(1000);
2805: }
2806: sbbs->cfg.node_num=0;
1.1.1.2 ! root 2807: sbbs->js_cleanup(sbbs->client_name);
! 2808:
1.1 root 2809: sbbs->event_thread_running = false;
2810:
2811: thread_down();
1.1.1.2 ! root 2812: eprintf(LOG_INFO,"BBS Events thread terminated");
1.1 root 2813: }
2814:
2815:
2816: //****************************************************************************
1.1.1.2 ! root 2817: sbbs_t::sbbs_t(ushort node_num, SOCKADDR_IN addr, const char* name, SOCKET sd,
1.1 root 2818: scfg_t* global_cfg, char* global_text[], client_t* client_info)
2819: {
2820: char nodestr[32];
2821: char path[MAX_PATH+1];
2822: uint i;
2823:
2824: if(node_num)
2825: SAFEPRINTF(nodestr,"Node %d",node_num);
2826: else
1.1.1.2 ! root 2827: SAFECOPY(nodestr,name);
1.1 root 2828:
2829: lprintf(LOG_DEBUG,"%s constructor using socket %d (settings=%lx)"
2830: ,nodestr, sd, global_cfg->node_misc);
2831:
2832: startup = ::startup; // Convert from global to class member
2833:
2834: memcpy(&cfg, global_cfg, sizeof(cfg));
2835:
2836: cfg.node_num=node_num;
2837: if(node_num>0) {
2838: strcpy(cfg.node_dir, cfg.node_path[node_num-1]);
2839: prep_dir(cfg.node_dir, cfg.temp_dir, sizeof(cfg.temp_dir));
2840: } else { /* event thread needs exclusive-use temp_dir */
2841: if(startup->temp_dir[0])
2842: SAFECOPY(cfg.temp_dir,startup->temp_dir);
2843: else
2844: SAFECOPY(cfg.temp_dir,"../temp");
2845: prep_dir(cfg.ctrl_dir, cfg.temp_dir, sizeof(cfg.temp_dir));
2846: md(cfg.temp_dir);
2847: if(sd==INVALID_SOCKET) { /* events thread */
2848: if(startup->first_node==1)
2849: SAFEPRINTF(path,"%sevent",cfg.temp_dir);
2850: else
2851: SAFEPRINTF2(path,"%sevent%u",cfg.temp_dir,startup->first_node);
2852: backslash(path);
2853: SAFECOPY(cfg.temp_dir,path);
2854: }
2855: }
2856: lprintf(LOG_DEBUG,"%s temporary file directory: %s", nodestr, cfg.temp_dir);
2857:
2858: terminated = false;
2859: event_thread_running = false;
2860: input_thread_running = false;
2861: output_thread_running = false;
2862: input_thread_mutex_locked = false;
2863:
2864: if(client_info==NULL)
2865: memset(&client,0,sizeof(client));
2866: else
2867: memcpy(&client,client_info,sizeof(client));
2868: client_addr = addr;
2869: client_socket = sd;
2870: SAFECOPY(client_name, name);
2871: client_socket_dup=INVALID_SOCKET;
2872: client_ident[0]=0;
2873:
1.1.1.2 ! root 2874: telnet_location[0]=0;
1.1 root 2875: terminal[0]=0;
2876: rlogin_name[0]=0;
2877: rlogin_pass[0]=0;
2878:
2879: /* Init some important variables */
2880:
2881: #ifdef USE_CRYPTLIB
2882: ssh_mode=false;
2883: passthru_input_thread_running = false;
2884: passthru_output_thread_running = false;
2885: #endif
2886:
2887: rio_abortable=false;
2888:
2889: console = 0;
2890: online = 0;
2891: outchar_esc = 0;
2892: nodemsg_inside = 0; /* allows single nest */
2893: hotkey_inside = 0; /* allows single nest */
2894: event_time = 0;
2895: event_code = nulstr;
2896: nodesync_inside = false;
2897: errormsg_inside = false;
2898: gettimeleft_inside = false;
2899: timeleft = 60*10; /* just incase this is being used for calling gettimeleft() */
2900: uselect_total = 0;
2901: lbuflen = 0;
2902: keybufbot=keybuftop=0; /* initialize [unget]keybuf pointers */
1.1.1.2 ! root 2903: SAFECOPY(connection,"Telnet");
! 2904: node_connection=NODE_CONNECTION_TELNET;
1.1 root 2905:
2906: ZERO_VAR(telnet_local_option);
2907: ZERO_VAR(telnet_remote_option);
2908:
2909: telnet_cmdlen=0;
2910: telnet_mode=0;
2911: telnet_last_rxch=0;
1.1.1.2 ! root 2912: telnet_ack_event=CreateEvent(NULL, /* Manual Reset: */FALSE,/* InitialState */FALSE,NULL);
1.1 root 2913:
2914: sys_status=lncntr=tos=criterrs=slcnt=0L;
1.1.1.2 ! root 2915: column=0;
1.1 root 2916: curatr=LIGHTGRAY;
2917: attr_sp=0; /* attribute stack pointer */
2918: errorlevel=0;
2919: logcol=1;
2920: logfile_fp=NULL;
2921: nodefile=-1;
2922: node_ext=-1;
2923: nodefile_fp=NULL;
2924: node_ext_fp=NULL;
2925: current_msg=NULL;
2926: mnestr=NULL;
2927:
2928: #ifdef JAVASCRIPT
2929: js_runtime=NULL; /* runtime */
2930: js_cx=NULL; /* context */
2931: #endif
2932:
2933: for(i=0;i<TOTAL_TEXT;i++)
2934: text[i]=text_sav[i]=global_text[i];
2935:
2936: ZERO_VAR(main_csi);
2937: ZERO_VAR(thisnode);
2938: ZERO_VAR(useron);
2939: ZERO_VAR(inbuf);
2940: ZERO_VAR(outbuf);
2941: ZERO_VAR(smb);
2942: ZERO_VAR(nodesync_user);
2943:
2944: action=NODE_MAIN;
2945: global_str_vars=0;
2946: global_str_var=NULL;
2947: global_str_var_name=NULL;
2948: global_int_vars=0;
2949: global_int_var=NULL;
2950: global_int_var_name=NULL;
2951: sysvar_li=0;
2952: sysvar_pi=0;
2953:
2954: cursub=NULL;
2955: usrgrp=NULL;
2956: usrsubs=NULL;
2957: usrsub=NULL;
2958: usrgrp_total=0;
2959:
2960: subscan=NULL;
2961:
2962: curdir=NULL;
2963: usrlib=NULL;
2964: usrdirs=NULL;
2965: usrdir=NULL;
2966: usrlib_total=0;
2967:
2968: batup_desc=NULL;
2969: batup_name=NULL;
2970: batup_misc=NULL;
2971: batup_dir=NULL;
2972: batup_alt=NULL;
2973:
2974: batdn_name=NULL;
2975: batdn_dir=NULL;
2976: batdn_offset=NULL;
2977: batdn_size=NULL;
2978: batdn_alt=NULL;
2979: batdn_cdt=NULL;
2980:
2981: spymsg("Connected");
2982: }
2983:
2984: //****************************************************************************
2985: bool sbbs_t::init()
2986: {
2987: char str[MAX_PATH+1];
2988: char tmp[128];
2989: int result;
2990: uint i,j,k,l;
2991: node_t node;
2992: socklen_t addr_len;
2993: SOCKADDR_IN addr;
2994:
1.1.1.2 ! root 2995: RingBufInit(&inbuf, IO_THREAD_BUF_SIZE);
! 2996: if(cfg.node_num>0)
1.1 root 2997: node_inbuf[cfg.node_num-1]=&inbuf;
2998:
2999: RingBufInit(&outbuf, IO_THREAD_BUF_SIZE);
3000: outbuf.highwater_mark=startup->outbuf_highwater_mark;
3001:
3002: if(cfg.node_num && client_socket!=INVALID_SOCKET) {
3003:
3004: #ifdef _WIN32
3005: if(!DuplicateHandle(GetCurrentProcess(),
3006: (HANDLE)client_socket,
3007: GetCurrentProcess(),
3008: (HANDLE*)&client_socket_dup,
3009: 0,
3010: TRUE, // Inheritable
3011: DUPLICATE_SAME_ACCESS)) {
3012: errormsg(WHERE,ERR_CREATE,"duplicate socket handle",client_socket);
3013: return(false);
3014: }
3015: #else
3016: client_socket_dup = client_socket;
3017: #endif
3018:
3019: addr_len=sizeof(addr);
3020: if((result=getsockname(client_socket, (struct sockaddr *)&addr,&addr_len))!=0) {
3021: lprintf(LOG_ERR,"Node %d !ERROR %d (%d) getting address/port"
3022: ,cfg.node_num, result, ERROR_VALUE);
3023: return(false);
3024: }
1.1.1.2 ! root 3025: lprintf(LOG_INFO,"Node %d attached to local interface %s port %u"
1.1 root 3026: ,cfg.node_num, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
3027:
3028: local_addr=addr.sin_addr.s_addr;
3029: }
3030:
3031: if((comspec=os_cmdshell())==NULL) {
3032: errormsg(WHERE, ERR_CHK, OS_CMD_SHELL_ENV_VAR" environment variable", 0);
3033: return(false);
3034: }
3035:
3036: md(cfg.temp_dir);
3037:
3038: /* Shared NODE files */
3039: SAFEPRINTF2(str,"%s%s",cfg.ctrl_dir,"node.dab");
3040: if((nodefile=nopen(str,O_DENYNONE|O_RDWR|O_CREAT))==-1) {
3041: errormsg(WHERE, ERR_OPEN, str, cfg.node_num);
3042: return(false);
3043: }
3044: memset(&node,0,sizeof(node_t)); /* write NULL to node struct */
3045: node.status=NODE_OFFLINE;
3046: while(filelength(nodefile)<(long)(cfg.sys_nodes*sizeof(node_t))) {
3047: lseek(nodefile,0L,SEEK_END);
3048: if(write(nodefile,&node,sizeof(node_t))!=sizeof(node_t)) {
3049: errormsg(WHERE,ERR_WRITE,str,sizeof(node_t));
3050: break;
3051: }
3052: }
3053: for(i=0; cfg.node_num>0 && i<LOOP_NODEDAB; i++) {
3054: if(lock(nodefile,(cfg.node_num-1)*sizeof(node_t),sizeof(node_t))==0) {
3055: unlock(nodefile,(cfg.node_num-1)*sizeof(node_t),sizeof(node_t));
3056: break;
3057: }
3058: mswait(100);
3059: }
3060: if(cfg.node_misc&NM_CLOSENODEDAB) {
3061: close(nodefile);
3062: nodefile=-1;
3063: }
3064:
3065: if(i>=LOOP_NODEDAB) {
3066: errormsg(WHERE, ERR_LOCK, str, cfg.node_num);
3067: return(false);
3068: }
3069:
3070: if(cfg.node_num) {
3071: SAFEPRINTF(str,"%snode.log",cfg.node_dir);
3072: if((logfile_fp=fopen(str,"a+b"))==NULL) {
3073: errormsg(WHERE, ERR_OPEN, str, 0);
3074: lprintf(LOG_ERR,"Perhaps this node is already running");
3075: return(false);
3076: }
3077:
3078: if(filelength(fileno(logfile_fp))) {
3079: log(crlf);
3080: now=time(NULL);
3081: struct tm tm;
3082: localtime_r(&now,&tm);
3083: sprintf(str,"%s %s %s %02d %u "
3084: "End of preexisting log entry (possible crash)"
3085: ,hhmmtostr(&cfg,&tm,tmp)
3086: ,wday[tm.tm_wday]
3087: ,mon[tm.tm_mon],tm.tm_mday,tm.tm_year+1900);
1.1.1.2 ! root 3088: logline(LOG_NOTICE,"L!",str);
1.1 root 3089: log(crlf);
3090: catsyslog(1);
3091: }
3092:
3093: getnodedat(cfg.node_num,&thisnode,1);
3094: /* thisnode.status=0; */
3095: thisnode.action=0;
3096: thisnode.useron=0;
3097: thisnode.aux=0;
3098: thisnode.misc&=(NODE_EVENT|NODE_LOCK|NODE_RRUN);
3099: criterrs=thisnode.errors;
3100: putnodedat(cfg.node_num,&thisnode);
3101: }
3102:
3103: /** Put in if(cfg.node_num) ? (not needed for server and event threads) */
3104: backout();
3105:
3106: /* Reset COMMAND SHELL */
3107:
3108: main_csi.str=(char *)malloc(1024);
3109: if(main_csi.str==NULL) {
3110: errormsg(WHERE,ERR_ALLOC,"main_csi.str",1024);
3111: return(false);
3112: }
3113: memset(main_csi.str,0,1024);
3114: /***/
3115:
3116: if(cfg.total_grps) {
3117:
3118: usrgrp_total = cfg.total_grps;
3119:
3120: if((cursub=(uint *)malloc(sizeof(uint)*usrgrp_total))==NULL) {
3121: errormsg(WHERE, ERR_ALLOC, "cursub", sizeof(uint)*usrgrp_total);
3122: return(false);
3123: }
3124:
3125: if((usrgrp=(uint *)malloc(sizeof(uint)*usrgrp_total))==NULL) {
3126: errormsg(WHERE, ERR_ALLOC, "usrgrp", sizeof(uint)*usrgrp_total);
3127: return(false);
3128: }
3129:
3130: if((usrsubs=(uint *)malloc(sizeof(uint)*usrgrp_total))==NULL) {
3131: errormsg(WHERE, ERR_ALLOC, "usrsubs", sizeof(uint)*usrgrp_total);
3132: return(false);
3133: }
3134:
3135: if((usrsub=(uint **)calloc(usrgrp_total,sizeof(uint *)))==NULL) {
3136: errormsg(WHERE, ERR_ALLOC, "usrsub", sizeof(uint)*usrgrp_total);
3137: return(false);
3138: }
3139:
3140: if((subscan=(subscan_t *)malloc(sizeof(subscan_t)*cfg.total_subs))==NULL) {
3141: errormsg(WHERE, ERR_ALLOC, "subscan", sizeof(subscan_t)*cfg.total_subs);
3142: return(false);
3143: }
3144: }
3145:
3146: for(i=l=0;i<(uint)cfg.total_grps;i++) {
3147: for(j=k=0;j<cfg.total_subs;j++)
3148: if(cfg.sub[j]->grp==i)
3149: k++; /* k = number of subs per grp[i] */
3150: if(k>l) l=k; /* l = the largest number of subs per grp */
3151: }
3152: if(l)
3153: for(i=0;i<cfg.total_grps;i++)
3154: if((usrsub[i]=(uint *)malloc(sizeof(uint)*l))==NULL) {
3155: errormsg(WHERE, ERR_ALLOC, "usrsub[x]", sizeof(uint)*l);
3156: return(false);
3157: }
3158:
3159: if(cfg.total_libs) {
3160:
3161: usrlib_total = cfg.total_libs;
3162:
3163: if((curdir=(uint *)malloc(sizeof(uint)*usrlib_total))==NULL) {
3164: errormsg(WHERE, ERR_ALLOC, "curdir", sizeof(uint)*usrlib_total);
3165: return(false);
3166: }
3167:
3168: if((usrlib=(uint *)malloc(sizeof(uint)*usrlib_total))==NULL) {
3169: errormsg(WHERE, ERR_ALLOC, "usrlib", sizeof(uint)*usrlib_total);
3170: return(false);
3171: }
3172:
3173: if((usrdirs=(uint *)malloc(sizeof(uint)*usrlib_total))==NULL) {
3174: errormsg(WHERE, ERR_ALLOC, "usrdirs", sizeof(uint)*usrlib_total);
3175: return(false);
3176: }
3177:
3178: if((usrdir=(uint **)calloc(usrlib_total,sizeof(uint *)))==NULL) {
3179: errormsg(WHERE, ERR_ALLOC, "usrdir", sizeof(uint)*usrlib_total);
3180: return(false);
3181: }
3182: }
3183:
3184: for(i=l=0;i<cfg.total_libs;i++) {
3185: for(j=k=0;j<cfg.total_dirs;j++)
3186: if(cfg.dir[j]->lib==i)
3187: k++;
3188: if(k>l) l=k; /* l = largest number of dirs in a lib */
3189: }
3190: if(l) {
3191: l++; /* for temp dir */
3192: for(i=0;i<cfg.total_libs;i++)
3193: if((usrdir[i]=(uint *)malloc(sizeof(uint)*l))==NULL) {
3194: errormsg(WHERE, ERR_ALLOC, "usrdir[x]", sizeof(uint)*l);
3195: return(false);
3196: }
3197: }
3198:
3199: if(cfg.max_batup) {
3200:
3201: if((batup_desc=(char **)malloc(sizeof(char *)*cfg.max_batup))==NULL) {
3202: errormsg(WHERE, ERR_ALLOC, "batup_desc", sizeof(char *)*cfg.max_batup);
3203: return(false);
3204: }
3205: if((batup_name=(char **)malloc(sizeof(char *)*cfg.max_batup))==NULL) {
3206: errormsg(WHERE, ERR_ALLOC, "batup_name", sizeof(char *)*cfg.max_batup);
3207: return(false);
3208: }
3209: if((batup_misc=(long *)malloc(sizeof(long)*cfg.max_batup))==NULL) {
3210: errormsg(WHERE, ERR_ALLOC, "batup_misc", sizeof(char *)*cfg.max_batup);
3211: return(false);
3212: }
3213: if((batup_dir=(uint *)malloc(sizeof(uint)*cfg.max_batup))==NULL) {
3214: errormsg(WHERE, ERR_ALLOC, "batup_dir", sizeof(char *)*cfg.max_batup);
3215: return(false);
3216: }
3217: if((batup_alt=(ushort *)malloc(sizeof(ushort)*cfg.max_batup))==NULL) {
3218: errormsg(WHERE, ERR_ALLOC, "batup_alt", sizeof(char *)*cfg.max_batup);
3219: return(false);
3220: }
3221: for(i=0;i<cfg.max_batup;i++) {
3222: if((batup_desc[i]=(char *)malloc(59))==NULL) {
3223: errormsg(WHERE, ERR_ALLOC, "batup_desc[x]", 59);
3224: return(false);
3225: }
3226: if((batup_name[i]=(char *)malloc(13))==NULL) {
3227: errormsg(WHERE, ERR_ALLOC, "batup_name[x]", 13);
3228: return(false);
3229: }
3230: }
3231: }
3232:
3233: if(cfg.max_batdn) {
3234:
3235: if((batdn_name=(char **)malloc(sizeof(char *)*cfg.max_batdn))==NULL) {
3236: errormsg(WHERE, ERR_ALLOC, "batdn_name", sizeof(char *)*cfg.max_batdn);
3237: return(false);
3238: }
3239: if((batdn_dir=(uint *)malloc(sizeof(uint)*cfg.max_batdn))==NULL) {
3240: errormsg(WHERE, ERR_ALLOC, "batdn_dir", sizeof(uint)*cfg.max_batdn);
3241: return(false);
3242: }
3243: if((batdn_offset=(long *)malloc(sizeof(long)*cfg.max_batdn))==NULL) {
3244: errormsg(WHERE, ERR_ALLOC, "batdn_offset", sizeof(long)*cfg.max_batdn);
3245: return(false);
3246: }
3247: if((batdn_size=(ulong *)malloc(sizeof(ulong)*cfg.max_batdn))==NULL) {
3248: errormsg(WHERE, ERR_ALLOC, "batdn_size", sizeof(ulong)*cfg.max_batdn);
3249: return(false);
3250: }
3251: if((batdn_cdt=(ulong *)malloc(sizeof(ulong)*cfg.max_batdn))==NULL) {
3252: errormsg(WHERE, ERR_ALLOC, "batdn_cdt", sizeof(long)*cfg.max_batdn);
3253: return(false);
3254: }
3255: if((batdn_alt=(ushort *)malloc(sizeof(ushort)*cfg.max_batdn))==NULL) {
3256: errormsg(WHERE, ERR_ALLOC, "batdn_alt", sizeof(ushort)*cfg.max_batdn);
3257: return(false);
3258: }
3259: for(i=0;i<cfg.max_batdn;i++)
3260: if((batdn_name[i]=(char *)malloc(13))==NULL) {
3261: errormsg(WHERE, ERR_ALLOC, "batdn_name[x]", 13);
3262: return(false);
3263: }
3264: }
3265:
3266: reset_logon_vars();
3267:
3268: online=ON_REMOTE;
3269:
3270: return(true);
3271: }
3272:
3273: //****************************************************************************
3274: sbbs_t::~sbbs_t()
3275: {
3276: uint i;
3277: char node[32];
3278:
3279: if(cfg.node_num)
3280: SAFEPRINTF(node,"Node %d", cfg.node_num);
3281: else
3282: SAFECOPY(node,client_name);
3283: #ifdef _DEBUG
3284: lprintf(LOG_DEBUG,"%s destructor begin", node);
3285: #endif
3286:
3287: // if(!cfg.node_num)
3288: // rmdir(cfg.temp_dir);
3289:
3290: if(client_socket_dup!=INVALID_SOCKET && client_socket_dup!=client_socket)
3291: closesocket(client_socket_dup); /* close duplicate handle */
3292:
3293: if(cfg.node_num>0)
3294: node_inbuf[cfg.node_num-1]=NULL;
1.1.1.2 ! root 3295: if(!input_thread_running)
1.1 root 3296: RingBufDispose(&inbuf);
3297: if(!output_thread_running)
3298: RingBufDispose(&outbuf);
3299:
1.1.1.2 ! root 3300: if(telnet_ack_event!=NULL)
! 3301: CloseEvent(telnet_ack_event);
! 3302:
1.1 root 3303: /* Close all open files */
3304: if(nodefile!=-1) {
3305: close(nodefile);
3306: nodefile=-1;
3307: }
3308: if(node_ext!=-1) {
3309: close(node_ext);
3310: node_ext=-1;
3311: }
3312: if(logfile_fp!=NULL) {
3313: fclose(logfile_fp);
3314: logfile_fp=NULL;
3315: }
3316:
3317: /********************************/
3318: /* Free allocated class members */
3319: /********************************/
3320:
1.1.1.2 ! root 3321: js_cleanup(node);
1.1 root 3322:
3323: /* Reset text.dat */
3324:
3325: for(i=0;i<TOTAL_TEXT && text!=NULL;i++)
3326: if(text[i]!=text_sav[i]) {
3327: if(text[i]!=nulstr)
3328: free(text[i]);
3329: }
3330:
3331: /* Global command shell vars */
3332:
3333: freevars(&main_csi);
3334: clearvars(&main_csi);
3335: FREE_AND_NULL(main_csi.str); /* crash */
3336: FREE_AND_NULL(main_csi.cs);
3337:
3338: for(i=0;i<global_str_vars && global_str_var!=NULL;i++)
3339: FREE_AND_NULL(global_str_var[i]);
3340:
3341: FREE_AND_NULL(global_str_var);
3342: FREE_AND_NULL(global_str_var_name);
3343: global_str_vars=0;
3344:
3345: FREE_AND_NULL(global_int_var);
3346: FREE_AND_NULL(global_int_var_name);
3347: global_int_vars=0;
3348:
3349: /* Sub-board variables */
3350: for(i=0;i<usrgrp_total && usrsub!=NULL;i++)
3351: FREE_AND_NULL(usrsub[i]); /* exception here (ptr=0xfdfdfdfd) on exit July-10-2002 */
3352:
3353: FREE_AND_NULL(cursub);
3354: FREE_AND_NULL(usrgrp);
3355: FREE_AND_NULL(usrsubs);
3356: FREE_AND_NULL(usrsub);
3357: FREE_AND_NULL(subscan);
3358:
3359: /* File Directory variables */
3360: for(i=0;i<usrlib_total && usrdir!=NULL;i++)
3361: FREE_AND_NULL(usrdir[i]);
3362:
3363: FREE_AND_NULL(curdir);
3364: FREE_AND_NULL(usrlib);
3365: FREE_AND_NULL(usrdirs);
3366: FREE_AND_NULL(usrdir);
3367:
3368: /* Batch upload vars */
3369: for(i=0;i<cfg.max_batup && batup_desc!=NULL && batup_name!=NULL;i++) {
3370: FREE_AND_NULL(batup_desc[i]);
3371: FREE_AND_NULL(batup_name[i]);
3372: }
3373:
3374: FREE_AND_NULL(batup_desc);
3375: FREE_AND_NULL(batup_name);
3376: FREE_AND_NULL(batup_misc);
3377: FREE_AND_NULL(batup_dir);
3378: FREE_AND_NULL(batup_alt);
3379:
3380: /* Batch download vars */
3381: for(i=0;i<cfg.max_batdn && batdn_name!=NULL;i++)
3382: FREE_AND_NULL(batdn_name[i]);
3383:
3384: FREE_AND_NULL(batdn_name);
3385: FREE_AND_NULL(batdn_dir);
3386: FREE_AND_NULL(batdn_offset);
3387: FREE_AND_NULL(batdn_size);
3388: FREE_AND_NULL(batdn_cdt);
3389: FREE_AND_NULL(batdn_alt);
3390:
3391: #if 0 && defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER)
3392: if(!_CrtCheckMemory())
3393: lprintf(LOG_ERR,"!MEMORY ERRORS REPORTED IN DATA/DEBUG.LOG!");
3394: #endif
3395:
3396: #ifdef _DEBUG
3397: lprintf(LOG_DEBUG,"%s destructor end", node);
3398: #endif
3399: }
3400:
3401: /****************************************************************************/
3402: /* Network open function. Opens all files DENYALL and retries LOOP_NOPEN */
3403: /* number of times if the attempted file is already open or denying access */
3404: /* for some other reason. */
3405: /* All files are opened in BINARY mode, unless O_TEXT access bit is set. */
3406: /****************************************************************************/
3407: int sbbs_t::nopen(char *str, int access)
3408: {
3409: char logstr[256];
3410: int file,share,count=0;
3411:
3412: if(access&O_DENYNONE) {
3413: share=SH_DENYNO;
3414: access&=~O_DENYNONE;
3415: }
3416: else if(access==O_RDONLY) share=SH_DENYWR;
3417: else share=SH_DENYRW;
3418: if(!(access&O_TEXT))
3419: access|=O_BINARY;
1.1.1.2 ! root 3420: while(((file=sopen(str,access,share,DEFFILEMODE))==-1)
1.1 root 3421: && (errno==EACCES || errno==EAGAIN) && count++<LOOP_NOPEN)
3422: mswait(100);
3423: if(count>(LOOP_NOPEN/2) && count<=LOOP_NOPEN) {
3424: SAFEPRINTF2(logstr,"NOPEN COLLISION - File: \"%s\" Count: %d"
3425: ,str,count);
1.1.1.2 ! root 3426: logline(LOG_WARNING,"!!",logstr);
1.1 root 3427: }
3428: if(file==-1 && (errno==EACCES || errno==EAGAIN)) {
3429: SAFEPRINTF2(logstr,"NOPEN ACCESS DENIED - File: \"%s\" errno: %d"
3430: ,str,errno);
1.1.1.2 ! root 3431: logline(LOG_WARNING,"!!",logstr);
1.1 root 3432: bputs("\7\r\nNOPEN: ACCESS DENIED\r\n\7");
3433: }
3434: return(file);
3435: }
3436:
1.1.1.2 ! root 3437: void sbbs_t::spymsg(const char* msg)
1.1 root 3438: {
3439: char str[512];
3440:
3441: if(cfg.node_num<1)
3442: return;
3443:
3444: SAFEPRINTF4(str,"\r\n\r\n*** Spy Message ***\r\nNode %d: %s [%s]\r\n*** %s ***\r\n\r\n"
1.1.1.2 ! root 3445: ,cfg.node_num,client_name,inet_ntoa(client_addr.sin_addr),msg);
1.1 root 3446: if(startup->node_spybuf!=NULL
3447: && startup->node_spybuf[cfg.node_num-1]!=NULL) {
3448: RingBufWrite(startup->node_spybuf[cfg.node_num-1],(uchar*)str,strlen(str));
3449: /* Signal spy output semaphore? */
3450: if(startup->node_spysem!=NULL
3451: && startup->node_spysem[sbbs->cfg.node_num-1]!=NULL)
3452: sem_post(startup->node_spysem[sbbs->cfg.node_num-1]);
3453: }
3454:
3455: if(cfg.node_num && spy_socket[cfg.node_num-1]!=INVALID_SOCKET)
3456: sendsocket(spy_socket[cfg.node_num-1],str,strlen(str));
3457: #ifdef __unix__
3458: if(cfg.node_num && uspy_socket[cfg.node_num-1]!=INVALID_SOCKET)
3459: sendsocket(uspy_socket[cfg.node_num-1],str,strlen(str));
3460: #endif
3461: }
3462:
3463: #define MV_BUFLEN 4096
3464:
3465: /****************************************************************************/
3466: /* Moves or copies a file from one dir to another */
3467: /* both 'src' and 'dest' must contain full path and filename */
3468: /* returns 0 if successful, -1 if error */
3469: /****************************************************************************/
3470: int sbbs_t::mv(char *src, char *dest, char copy)
3471: {
3472: char str[MAX_PATH+1],*buf,atr=curatr;
3473: int ind,outd;
3474: uint chunk=MV_BUFLEN;
3475: ulong length,l;
3476: time_t ftime;
3477: FILE *inp,*outp;
3478:
3479: if(!stricmp(src,dest)) /* source and destination are the same! */
3480: return(0);
3481: if(!fexistcase(src)) {
3482: bprintf("\r\n\7MV ERROR: Source doesn't exist\r\n'%s'\r\n"
3483: ,src);
3484: return(-1);
3485: }
3486: if(!copy && fexistcase(dest)) {
3487: bprintf("\r\n\7MV ERROR: Destination already exists\r\n'%s'\r\n"
3488: ,dest);
3489: return(-1);
3490: }
3491: #ifndef __unix__ /* need to determine if on same mount device */
3492: if(!copy && ((src[1]!=':' && dest[1]!=':')
3493: || (src[1]==':' && dest[1]==':' && toupper(src[0])==toupper(dest[0])))) {
3494: if(rename(src,dest)) { /* same drive, so move */
3495: bprintf("\r\nMV ERROR: Error renaming '%s'"
3496: "\r\n to '%s'\r\n\7",src,dest);
3497: return(-1);
3498: }
3499: return(0);
3500: }
3501: #endif
3502: attr(WHITE);
3503: if((ind=nopen(src,O_RDONLY))==-1) {
3504: errormsg(WHERE,ERR_OPEN,src,O_RDONLY);
3505: return(-1);
3506: }
3507: if((inp=fdopen(ind,"rb"))==NULL) {
3508: close(ind);
3509: errormsg(WHERE,ERR_FDOPEN,str,O_RDONLY);
3510: return(-1);
3511: }
3512: setvbuf(inp,NULL,_IOFBF,32*1024);
3513: if((outd=nopen(dest,O_WRONLY|O_CREAT|O_TRUNC))==-1) {
3514: fclose(inp);
3515: errormsg(WHERE,ERR_OPEN,dest,O_WRONLY|O_CREAT|O_TRUNC);
3516: return(-1);
3517: }
3518: if((outp=fdopen(outd,"wb"))==NULL) {
3519: close(outd);
3520: fclose(inp);
3521: errormsg(WHERE,ERR_FDOPEN,dest,O_WRONLY|O_CREAT|O_TRUNC);
3522: return(-1);
3523: }
3524: setvbuf(outp,NULL,_IOFBF,8*1024);
3525: ftime=filetime(ind);
1.1.1.2 ! root 3526: length=(long)filelength(ind);
1.1 root 3527: if(length) { /* Something to copy */
3528: if((buf=(char *)malloc(MV_BUFLEN))==NULL) {
3529: fclose(inp);
3530: fclose(outp);
3531: errormsg(WHERE,ERR_ALLOC,nulstr,MV_BUFLEN);
3532: return(-1);
3533: }
3534: l=0L;
3535: while(l<length) {
3536: bprintf("%2lu%%",l ? (long)(100.0/((float)length/l)) : 0L);
3537: if(l+chunk>length)
3538: chunk=length-l;
3539: if(fread(buf,1,chunk,inp)!=chunk) {
3540: free(buf);
3541: fclose(inp);
3542: fclose(outp);
3543: errormsg(WHERE,ERR_READ,src,chunk);
3544: return(-1);
3545: }
3546: if(fwrite(buf,1,chunk,outp)!=chunk) {
3547: free(buf);
3548: fclose(inp);
3549: fclose(outp);
3550: errormsg(WHERE,ERR_WRITE,dest,chunk);
3551: return(-1);
3552: }
3553: l+=chunk;
3554: bputs("\b\b\b");
3555: }
3556: bputs(" \b\b\b"); /* erase it */
3557: attr(atr);
3558: free(buf);
3559: }
3560: fclose(inp);
3561: fclose(outp);
3562: setfdate(dest,ftime); /* Would be nice if we could use futime() instead */
3563: if(!copy && remove(src)) {
3564: errormsg(WHERE,ERR_REMOVE,src,0);
3565: return(-1);
3566: }
3567: return(0);
3568: }
3569:
3570: void sbbs_t::hangup(void)
3571: {
1.1.1.2 ! root 3572: if(online) {
! 3573: lprintf(LOG_DEBUG,"Node %d disconnecting client", cfg.node_num);
! 3574: online=FALSE; // moved from the bottom of this function on Jan-25-2009
! 3575: }
1.1 root 3576: if(client_socket_dup!=INVALID_SOCKET && client_socket_dup!=client_socket)
3577: closesocket(client_socket_dup);
3578: client_socket_dup=INVALID_SOCKET;
3579:
3580: if(client_socket!=INVALID_SOCKET) {
3581: mswait(1000); /* Give socket output buffer time to flush */
3582: client_off(client_socket);
3583: close_socket(client_socket);
3584: client_socket=INVALID_SOCKET;
3585: }
3586: sem_post(&outbuf.sem);
3587: }
3588:
3589: int sbbs_t::incom(unsigned long timeout)
3590: {
3591: uchar ch;
3592:
3593: #if 0 /* looping version */
3594: while(!RingBufRead(&inbuf, &ch, 1))
3595: if(sem_trywait_block(&inbuf.sem,timeout)!=0 || sys_status&SS_ABORT)
3596: return(NOINP);
3597: #else
3598: if(!RingBufRead(&inbuf, &ch, 1)) {
3599: if(sem_trywait_block(&inbuf.sem,timeout)!=0)
3600: return(NOINP);
3601: if(!RingBufRead(&inbuf, &ch, 1))
3602: return(NOINP);
3603: }
3604: #endif
3605: return(ch);
3606: }
3607:
3608: int sbbs_t::outcom(uchar ch)
3609: {
3610: if(!RingBufFree(&outbuf))
3611: return(TXBOF);
3612: if(!RingBufWrite(&outbuf, &ch, 1))
3613: return(TXBOF);
3614: return(0);
3615: }
3616:
1.1.1.2 ! root 3617: int sbbs_t::putcom(const char *str, size_t len)
1.1 root 3618: {
1.1.1.2 ! root 3619: size_t i;
1.1 root 3620:
1.1.1.2 ! root 3621: if(!len)
! 3622: len=strlen(str);
! 3623: for(i=0;i<len && online;i++)
! 3624: if(outcom(str[i])!=0)
! 3625: break;
! 3626: return i;
1.1 root 3627: }
3628:
3629: /* Legacy Remote I/O Control Interface */
3630: int sbbs_t::rioctl(ushort action)
3631: {
3632: int mode;
3633: int state;
3634:
3635: switch(action) {
3636: case GVERS: /* Get version */
3637: return(0x200);
3638: case GUART: /* Get UART I/O address, not available */
3639: return(0xffff);
3640: case GIRQN: /* Get IRQ number, not available */
3641: return((int)client_socket);
3642: case GBAUD: /* Get current bit rate */
3643: return(0xffff);
3644: case RXBC: /* Get receive buffer count */
3645: // ulong cnt;
3646: // ioctlsocket (client_socket,FIONREAD,&cnt);
3647: return(/* cnt+ */RingBufFull(&inbuf));
3648: case RXBS: /* Get receive buffer size */
3649: return(inbuf.size);
3650: case TXBC: /* Get transmit buffer count */
3651: return(RingBufFull(&outbuf));
3652: case TXBS: /* Get transmit buffer size */
3653: return(outbuf.size);
3654: case TXBF: /* Get transmit buffer free space */
3655: return(RingBufFree(&outbuf));
3656: case IOMODE:
3657: mode=0;
3658: if(rio_abortable)
3659: mode|=ABORT;
3660: return(mode);
3661: case IOSTATE:
3662: state=0;
3663: if(sys_status&SS_ABORT)
3664: state|=ABORT;
3665: return(state);
3666: case IOFI: /* Flush input buffer */
3667: RingBufReInit(&inbuf);
3668: break;
3669: case IOFO: /* Flush output buffer */
3670: RingBufReInit(&outbuf);
3671: break;
3672: case IOFB: /* Flush both buffers */
3673: RingBufReInit(&inbuf);
3674: RingBufReInit(&outbuf);
3675: break;
3676: case LFN81:
3677: case LFE71:
3678: case FIFOCTL:
3679: return(0);
3680: }
3681:
3682: if((action&0xff)==IOSM) { /* Get/Set/Clear mode */
3683: if(action&ABORT)
3684: rio_abortable=true;
3685: return(0);
3686: }
3687:
3688: if((action&0xff)==IOCM) { /* Get/Set/Clear mode */
3689: if(action&ABORT)
3690: rio_abortable=false;
3691: return(0);
3692: }
3693:
3694: if((action&0xff)==IOSS) { /* Set state */
3695: if(action&ABORT)
3696: sys_status|=SS_ABORT;
3697: return(0);
3698: }
3699:
3700: if((action&0xff)==IOCS) { /* Clear state */
3701: if(action&ABORT)
3702: sys_status&=~SS_ABORT;
3703: return(0);
3704: }
3705:
3706: return(0);
3707: }
3708:
3709: void sbbs_t::reset_logon_vars(void)
3710: {
3711: int i;
3712:
3713: /* bools */
3714: qwklogon=false;
3715:
3716: sys_status&=~(SS_USERON|SS_TMPSYSOP|SS_LCHAT|SS_ABORT
3717: |SS_PAUSEON|SS_PAUSEOFF|SS_EVENT|SS_NEWUSER|SS_NEWDAY);
3718: cid[0]=0;
3719: wordwrap[0]=0;
3720: question[0]=0;
3721: menu_dir[0]=0;
3722: menu_file[0]=0;
3723: rows=24;
3724: cols=80;
3725: lncntr=0;
3726: autoterm=0;
3727: lbuflen=0;
3728: slcnt=0;
3729: altul=0;
3730: timeleft_warn=0;
3731: keybufbot=keybuftop=0;
3732: logon_uls=logon_ulb=logon_dls=logon_dlb=0;
3733: logon_posts=logon_emails=logon_fbacks=0;
3734: batdn_total=batup_total=0;
3735: usrgrps=usrlibs=0;
3736: curgrp=curlib=0;
3737: for(i=0;i<cfg.total_libs;i++)
3738: curdir[i]=0;
3739: for(i=0;i<cfg.total_grps;i++)
3740: cursub[i]=0;
3741: cur_cps=3000;
3742: cur_rate=30000;
3743: dte_rate=38400;
3744: main_cmds=xfer_cmds=posts_read=0;
3745: lastnodemsg=0;
3746: lastnodemsguser[0]=0;
3747: }
3748:
3749: /****************************************************************************/
3750: /* Writes NODE.LOG at end of SYSTEM.LOG */
3751: /****************************************************************************/
3752: void sbbs_t::catsyslog(int crash)
3753: {
3754: char str[MAX_PATH+1];
3755: char *buf;
3756: int i,file;
3757: long length;
3758: struct tm tm;
3759:
3760: if(logfile_fp==NULL) {
3761: SAFEPRINTF(str,"%snode.log",cfg.node_dir);
3762: if((logfile_fp=fopen(str,"rb"))==NULL) {
3763: errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
3764: return;
3765: }
3766: }
1.1.1.2 ! root 3767: length=(long)ftell(logfile_fp);
1.1 root 3768: if(length) {
3769: if((buf=(char *)malloc(length))==NULL) {
3770: errormsg(WHERE,ERR_ALLOC,str,length);
3771: return;
3772: }
3773: rewind(logfile_fp);
3774: if(fread(buf,1,length,logfile_fp)!=(size_t)length) {
3775: errormsg(WHERE,ERR_READ,"log file",length);
3776: free((char *)buf);
3777: return;
3778: }
3779: now=time(NULL);
3780: localtime_r(&now,&tm);
3781: SAFEPRINTF4(str,"%slogs/%2.2d%2.2d%2.2d.log",cfg.logs_dir,tm.tm_mon+1,tm.tm_mday
3782: ,TM_YEAR(tm.tm_year));
3783: if((file=nopen(str,O_WRONLY|O_APPEND|O_CREAT))==-1) {
3784: errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_APPEND|O_CREAT);
3785: free((char *)buf);
3786: return;
3787: }
3788: if(lwrite(file,buf,length)!=length) {
3789: close(file);
3790: errormsg(WHERE,ERR_WRITE,str,length);
3791: free((char *)buf);
3792: return;
3793: }
3794: close(file);
3795: if(crash) {
3796: for(i=0;i<2;i++) {
3797: SAFEPRINTF(str,"%scrash.log",i ? cfg.logs_dir : cfg.node_dir);
3798: if((file=nopen(str,O_WRONLY|O_APPEND|O_CREAT))==-1) {
3799: errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_APPEND|O_CREAT);
3800: free((char *)buf);
3801: return;
3802: }
3803: if(lwrite(file,buf,length)!=length) {
3804: close(file);
3805: errormsg(WHERE,ERR_WRITE,str,length);
3806: free((char *)buf);
3807: return;
3808: }
3809: close(file);
3810: }
3811: }
3812: free((char *)buf);
3813: }
3814:
3815: fclose(logfile_fp);
3816:
3817: SAFEPRINTF(str,"%snode.log",cfg.node_dir);
3818: if((logfile_fp=fopen(str,"w+b"))==NULL) /* Truncate NODE.LOG */
3819: errormsg(WHERE,ERR_OPEN,str,O_WRONLY|O_TRUNC);
3820: }
3821:
3822:
3823: void sbbs_t::logoffstats()
3824: {
3825: char str[MAX_PATH+1];
3826: int i,file;
3827: stats_t stats;
3828:
3829: if(REALSYSOP && !(cfg.sys_misc&SM_SYSSTAT))
3830: return;
3831:
3832: for(i=0;i<2;i++) {
3833: SAFEPRINTF(str,"%sdsts.dab",i ? cfg.ctrl_dir : cfg.node_dir);
3834: if((file=nopen(str,O_RDWR))==-1) {
3835: errormsg(WHERE,ERR_OPEN,str,O_RDWR);
3836: return;
3837: }
3838: memset(&stats,0,sizeof(stats));
3839: lseek(file,4L,SEEK_SET); /* Skip timestamp, logons and logons today */
3840: read(file,&stats,sizeof(stats));
3841:
3842: if(!(useron.rest&FLAG('Q'))) { /* Don't count QWKnet nodes */
3843: stats.timeon+=(now-logontime)/60;
3844: stats.ttoday+=(now-logontime)/60;
3845: stats.ptoday+=logon_posts;
3846: }
3847: stats.uls+=logon_uls;
3848: stats.ulb+=logon_ulb;
3849: stats.dls+=logon_dls;
3850: stats.dlb+=logon_dlb;
3851: stats.etoday+=logon_emails;
3852: stats.ftoday+=logon_fbacks;
3853:
3854: #if 0 // This is now handled in newuserdat()
3855: if(sys_status&SS_NEWUSER)
3856: stats.nusers++;
3857: #endif
3858:
3859: lseek(file,4L,SEEK_SET);
3860: write(file,&stats,sizeof(stats));
3861: close(file);
3862: }
3863: }
3864:
3865: void node_thread(void* arg)
3866: {
3867: ulong stack_frame;
3868: char str[128];
3869: int file;
3870: uint curshell=0;
3871: node_t node;
1.1.1.2 ! root 3872: ulong login_attempts;
1.1 root 3873: sbbs_t* sbbs = (sbbs_t*) arg;
3874:
3875: update_clients();
1.1.1.2 ! root 3876: SetThreadName("Node");
1.1 root 3877: thread_up(TRUE /* setuid */);
3878:
3879: #ifdef _DEBUG
3880: lprintf(LOG_DEBUG,"Node %d thread started",sbbs->cfg.node_num);
3881: #endif
3882:
3883: sbbs_srand(); /* Seed random number generator */
3884:
3885: #ifdef JAVASCRIPT
3886: if(!(startup->options&BBS_OPT_NO_JAVASCRIPT)) {
3887: if(!sbbs->js_init(&stack_frame)) /* This must be done in the context of the node thread */
3888: lprintf(LOG_ERR,"Node %d !JavaScript Initialization FAILURE",sbbs->cfg.node_num);
3889: }
3890: #endif
3891:
1.1.1.2 ! root 3892: if(startup->login_attempt_throttle
! 3893: && (login_attempts=loginAttempts(startup->login_attempt_list, &sbbs->client_addr)) > 1) {
! 3894: lprintf(LOG_DEBUG,"Node %d Throttling suspicious connection from: %s (%u login attempts)"
! 3895: ,sbbs->cfg.node_num, inet_ntoa(sbbs->client_addr.sin_addr), login_attempts);
! 3896: mswait(login_attempts*startup->login_attempt_throttle);
! 3897: }
! 3898:
1.1 root 3899: if(sbbs->answer()) {
3900:
3901: if(sbbs->qwklogon) {
3902: sbbs->getsmsg(sbbs->useron.number);
3903: sbbs->qwk_sec();
3904: } else while(sbbs->useron.number
3905: && (sbbs->main_csi.misc&CS_OFFLINE_EXEC || sbbs->online)) {
3906:
3907: if(!sbbs->main_csi.cs || curshell!=sbbs->useron.shell) {
3908: if(sbbs->useron.shell>=sbbs->cfg.total_shells)
3909: sbbs->useron.shell=0;
3910: SAFEPRINTF2(str,"%s%s.bin",sbbs->cfg.mods_dir
3911: ,sbbs->cfg.shell[sbbs->useron.shell]->code);
3912: if(sbbs->cfg.mods_dir[0]==0 || !fexistcase(str))
3913: SAFEPRINTF2(str,"%s%s.bin",sbbs->cfg.exec_dir
3914: ,sbbs->cfg.shell[sbbs->useron.shell]->code);
3915: if((file=sbbs->nopen(str,O_RDONLY))==-1) {
3916: sbbs->errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
3917: sbbs->hangup();
3918: break;
3919: }
3920: FREE_AND_NULL(sbbs->main_csi.cs);
3921: sbbs->freevars(&sbbs->main_csi);
3922: sbbs->clearvars(&sbbs->main_csi);
3923:
1.1.1.2 ! root 3924: sbbs->main_csi.length=(long)filelength(file);
1.1 root 3925: if((sbbs->main_csi.cs=(uchar *)malloc(sbbs->main_csi.length))==NULL) {
3926: close(file);
3927: sbbs->errormsg(WHERE,ERR_ALLOC,str,sbbs->main_csi.length);
3928: sbbs->hangup();
3929: break;
3930: }
3931:
3932: if(lread(file,sbbs->main_csi.cs,sbbs->main_csi.length)
3933: !=(int)sbbs->main_csi.length) {
3934: sbbs->errormsg(WHERE,ERR_READ,str,sbbs->main_csi.length);
3935: close(file);
3936: free(sbbs->main_csi.cs);
3937: sbbs->main_csi.cs=NULL;
3938: sbbs->hangup();
3939: break;
3940: }
3941: close(file);
3942:
3943: curshell=sbbs->useron.shell;
3944: sbbs->main_csi.ip=sbbs->main_csi.cs;
3945: sbbs->menu_dir[0]=0;
3946: sbbs->menu_file[0]=0;
3947: }
3948: if(sbbs->exec(&sbbs->main_csi))
3949: break;
3950: }
3951: }
3952:
3953: #ifdef _WIN32
3954: if(startup->hangup_sound[0] && !(startup->options&BBS_OPT_MUTE))
3955: PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
3956: #endif
3957:
3958: sbbs->hangup(); /* closes sockets, calls client_off, and shuts down the output_thread */
3959: node_socket[sbbs->cfg.node_num-1]=INVALID_SOCKET;
3960:
3961: sbbs->logout();
3962: sbbs->logoffstats(); /* Updates both system and node dsts.dab files */
3963:
3964: if(sbbs->sys_status&SS_DAILY) { // New day, run daily events/maintenance
3965: sbbs->daily_maint();
3966: }
3967:
3968: #if 0 /* this is handled in the event_thread now */
3969: // Node Daily Event
3970: sbbs->getnodedat(sbbs->cfg.node_num,&node,0);
3971: if(node.misc&NODE_EVENT) {
3972: sbbs->getnodedat(sbbs->cfg.node_num,&node,1);
3973: node.status=NODE_EVENT_RUNNING;
3974: sbbs->putnodedat(sbbs->cfg.node_num,&node);
3975: if(sbbs->cfg.node_daily[0]) {
3976: sbbs->logentry("!:","Run node daily event");
3977: sbbs->external(
3978: sbbs->cmdstr(sbbs->cfg.node_daily,nulstr,nulstr,NULL)
3979: ,EX_OFFLINE);
3980: }
3981: sbbs->getnodedat(sbbs->cfg.node_num,&node,1);
3982: node.misc&=~NODE_EVENT;
3983: sbbs->putnodedat(sbbs->cfg.node_num,&node);
3984: }
3985: #endif
3986:
3987: // Wait for all node threads to terminate
3988: if(sbbs->input_thread_running || sbbs->output_thread_running
3989: #ifdef USE_CRYPTLIB
3990: || sbbs->passthru_input_thread_running || sbbs->passthru_output_thread_running
3991: #endif
3992: ) {
1.1.1.2 ! root 3993: lprintf(LOG_DEBUG,"Node %d Waiting for %s to terminate..."
1.1 root 3994: ,sbbs->cfg.node_num
3995: ,(sbbs->input_thread_running && sbbs->output_thread_running) ?
3996: "I/O threads" : sbbs->input_thread_running
3997: ? "input thread" : "output thread");
3998: time_t start=time(NULL);
3999: while(sbbs->input_thread_running
4000: || sbbs->output_thread_running
4001: #ifdef USE_CRYPTLIB
4002: || sbbs->passthru_input_thread_running || sbbs->passthru_output_thread_running
4003: #endif
4004: ) {
4005: if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
4006: lprintf(LOG_NOTICE,"Node %d !TIMEOUT waiting for %s to terminate"
4007: , sbbs->cfg.node_num
4008: ,(sbbs->input_thread_running && sbbs->output_thread_running) ?
4009: "I/O threads"
4010: : sbbs->input_thread_running
4011: ? "input thread" : "output thread");
4012: break;
4013: }
4014: mswait(100);
4015: }
4016: }
4017:
4018: sbbs->catsyslog(0);
4019:
4020: status(STATUS_WFC);
4021:
4022: sbbs->getnodedat(sbbs->cfg.node_num,&node,1);
4023: if(node.misc&NODE_DOWN)
4024: node.status=NODE_OFFLINE;
4025: else
4026: node.status=NODE_WFC;
4027: node.misc&=~(NODE_DOWN|NODE_INTR|NODE_MSGW|NODE_NMSG
4028: |NODE_UDAT|NODE_POFF|NODE_AOFF|NODE_EXT);
4029: /* node.useron=0; needed for hang-ups while in multinode chat */
4030: sbbs->putnodedat(sbbs->cfg.node_num,&node);
4031:
1.1.1.2 ! root 4032: {
! 4033: int32_t remain = protected_uint32_adjust(&node_threads_running, -1);
! 4034: lprintf(LOG_INFO,"Node %d thread terminated (%u node threads remain, %lu clients served)"
! 4035: ,sbbs->cfg.node_num, remain, served);
! 4036: }
1.1 root 4037: if(!sbbs->input_thread_running && !sbbs->output_thread_running)
4038: delete sbbs;
4039: else
4040: lprintf(LOG_WARNING,"Node %d !ORPHANED I/O THREAD(s)",sbbs->cfg.node_num);
4041:
4042: update_clients();
4043: thread_down();
4044: }
4045:
4046: void sbbs_t::daily_maint(void)
4047: {
4048: char str[128];
4049: char uname[LEN_ALIAS+1];
4050: uint i;
4051: uint usernum;
4052: uint lastusernum;
4053: node_t node;
4054: user_t user;
4055:
4056: now=time(NULL);
4057:
4058: sbbs->getnodedat(sbbs->cfg.node_num,&node,1);
4059: node.status=NODE_EVENT_RUNNING;
4060: sbbs->putnodedat(sbbs->cfg.node_num,&node);
4061:
4062: sbbs->logentry("!:","Ran system daily maintenance");
4063:
4064: if(sbbs->cfg.user_backup_level) {
4065: lputs(LOG_INFO,"Backing-up user data...");
4066: SAFEPRINTF(str,"%suser/user.dat",sbbs->cfg.data_dir);
4067: backup(str,sbbs->cfg.user_backup_level,FALSE);
4068: SAFEPRINTF(str,"%suser/name.dat",sbbs->cfg.data_dir);
4069: backup(str,sbbs->cfg.user_backup_level,FALSE);
4070: }
4071:
4072: if(sbbs->cfg.mail_backup_level) {
4073: lputs(LOG_INFO,"Backing-up mail data...");
4074: SAFEPRINTF(str,"%smail.shd",sbbs->cfg.data_dir);
4075: backup(str,sbbs->cfg.mail_backup_level,FALSE);
4076: SAFEPRINTF(str,"%smail.sha",sbbs->cfg.data_dir);
4077: backup(str,sbbs->cfg.mail_backup_level,FALSE);
4078: SAFEPRINTF(str,"%smail.sdt",sbbs->cfg.data_dir);
4079: backup(str,sbbs->cfg.mail_backup_level,FALSE);
4080: SAFEPRINTF(str,"%smail.sda",sbbs->cfg.data_dir);
4081: backup(str,sbbs->cfg.mail_backup_level,FALSE);
4082: SAFEPRINTF(str,"%smail.sid",sbbs->cfg.data_dir);
4083: backup(str,sbbs->cfg.mail_backup_level,FALSE);
4084: SAFEPRINTF(str,"%smail.sch",sbbs->cfg.data_dir);
4085: backup(str,sbbs->cfg.mail_backup_level,FALSE);
4086: }
4087:
1.1.1.2 ! root 4088: lputs(LOG_INFO,status("Checking for inactive/expired user records..."));
1.1 root 4089: lastusernum=lastuser(&sbbs->cfg);
4090: for(usernum=1;usernum<=lastusernum;usernum++) {
4091:
4092: SAFEPRINTF2(str,"%5u of %-5u",usernum,lastusernum);
4093: status(str);
4094: user.number=usernum;
4095: if((i=getuserdat(&sbbs->cfg,&user))!=0) {
4096: SAFEPRINTF(str,"user record %u",usernum);
4097: sbbs->errormsg(WHERE,ERR_READ,str,i);
4098: continue;
4099: }
4100:
4101: /***********************************************/
4102: /* Fix name (name.dat and user.dat) mismatches */
4103: /***********************************************/
4104: username(&sbbs->cfg,user.number,uname);
4105: if(user.misc&DELETED) {
4106: if(strcmp(uname,"DELETED USER"))
4107: putusername(&sbbs->cfg,user.number,nulstr);
4108: continue;
4109: }
4110:
4111: if(strcmp(user.alias,uname))
4112: putusername(&sbbs->cfg,user.number,user.alias);
4113:
4114: if(user.number==1)
4115: continue; /* skip expiration/inactivity checks for user #1 */
4116:
4117: if(!(user.misc&(DELETED|INACTIVE))
4118: && user.expire && (ulong)user.expire<=(ulong)now) {
4119: putsmsg(&sbbs->cfg,user.number,sbbs->text[AccountHasExpired]);
4120: SAFEPRINTF2(str,"%s #%u Expired",user.alias,user.number);
4121: sbbs->logentry("!%",str);
4122: if(sbbs->cfg.level_misc[user.level]&LEVEL_EXPTOVAL
4123: && sbbs->cfg.level_expireto[user.level]<10) {
4124: user.flags1=sbbs->cfg.val_flags1[sbbs->cfg.level_expireto[user.level]];
4125: user.flags2=sbbs->cfg.val_flags2[sbbs->cfg.level_expireto[user.level]];
4126: user.flags3=sbbs->cfg.val_flags3[sbbs->cfg.level_expireto[user.level]];
4127: user.flags4=sbbs->cfg.val_flags4[sbbs->cfg.level_expireto[user.level]];
4128: user.exempt=sbbs->cfg.val_exempt[sbbs->cfg.level_expireto[user.level]];
4129: user.rest=sbbs->cfg.val_rest[sbbs->cfg.level_expireto[user.level]];
4130: if(sbbs->cfg.val_expire[sbbs->cfg.level_expireto[user.level]])
4131: user.expire=now
4132: +(sbbs->cfg.val_expire[sbbs->cfg.level_expireto[user.level]]*24*60*60);
4133: else
4134: user.expire=0;
4135: user.level=sbbs->cfg.val_level[sbbs->cfg.level_expireto[user.level]];
4136: }
4137: else {
4138: if(sbbs->cfg.level_misc[user.level]&LEVEL_EXPTOLVL)
4139: user.level=sbbs->cfg.level_expireto[user.level];
4140: else
4141: user.level=sbbs->cfg.expired_level;
4142: user.flags1&=~sbbs->cfg.expired_flags1; /* expired status */
4143: user.flags2&=~sbbs->cfg.expired_flags2; /* expired status */
4144: user.flags3&=~sbbs->cfg.expired_flags3; /* expired status */
4145: user.flags4&=~sbbs->cfg.expired_flags4; /* expired status */
4146: user.exempt&=~sbbs->cfg.expired_exempt;
4147: user.rest|=sbbs->cfg.expired_rest;
4148: user.expire=0;
4149: }
4150: putuserrec(&sbbs->cfg,user.number,U_LEVEL,2,ultoa(user.level,str,10));
4151: putuserrec(&sbbs->cfg,user.number,U_FLAGS1,8,ultoa(user.flags1,str,16));
4152: putuserrec(&sbbs->cfg,user.number,U_FLAGS2,8,ultoa(user.flags2,str,16));
4153: putuserrec(&sbbs->cfg,user.number,U_FLAGS3,8,ultoa(user.flags3,str,16));
4154: putuserrec(&sbbs->cfg,user.number,U_FLAGS4,8,ultoa(user.flags4,str,16));
4155: putuserrec(&sbbs->cfg,user.number,U_EXPIRE,8,ultoa(user.expire,str,16));
4156: putuserrec(&sbbs->cfg,user.number,U_EXEMPT,8,ultoa(user.exempt,str,16));
4157: putuserrec(&sbbs->cfg,user.number,U_REST,8,ultoa(user.rest,str,16));
4158: if(sbbs->cfg.expire_mod[0]) {
4159: sbbs->useron=user;
4160: sbbs->online=ON_LOCAL;
4161: sbbs->exec_bin(sbbs->cfg.expire_mod,&sbbs->main_csi);
4162: sbbs->online=FALSE;
4163: }
4164: }
4165:
4166: /***********************************************************/
4167: /* Auto deletion based on expiration date or days inactive */
4168: /***********************************************************/
4169: if(!(user.exempt&FLAG('P')) /* Not a permanent account */
4170: && !(user.misc&(DELETED|INACTIVE)) /* alive */
4171: && (sbbs->cfg.sys_autodel && (now-user.laston)/(long)(24L*60L*60L)
4172: > sbbs->cfg.sys_autodel)) { /* Inactive too long */
4173: SAFEPRINTF2(str,"Auto-Deleted %s #%u",user.alias,user.number);
4174: sbbs->logentry("!*",str);
1.1.1.2 ! root 4175: sbbs->delallmail(user.number, MAIL_ANY);
1.1 root 4176: putusername(&sbbs->cfg,user.number,nulstr);
4177: putuserrec(&sbbs->cfg,user.number,U_MISC,8,ultoa(user.misc|DELETED,str,16));
4178: }
4179: }
4180:
1.1.1.2 ! root 4181: lputs(LOG_INFO,status("Purging deleted/expired e-mail"));
1.1 root 4182: SAFEPRINTF(sbbs->smb.file,"%smail",sbbs->cfg.data_dir);
4183: sbbs->smb.retry_time=sbbs->cfg.smb_retry_time;
4184: sbbs->smb.subnum=INVALID_SUB;
4185: if((i=smb_open(&sbbs->smb))!=0)
4186: sbbs->errormsg(WHERE,ERR_OPEN,sbbs->smb.file,i,sbbs->smb.last_error);
4187: else {
4188: if(filelength(fileno(sbbs->smb.shd_fp))>0) {
4189: if((i=smb_locksmbhdr(&sbbs->smb))!=0)
4190: sbbs->errormsg(WHERE,ERR_LOCK,sbbs->smb.file,i,sbbs->smb.last_error);
4191: else
4192: sbbs->delmail(0,MAIL_ALL);
4193: }
4194: smb_close(&sbbs->smb);
4195: }
4196:
4197: sbbs->sys_status&=~SS_DAILY;
4198: if(sbbs->cfg.sys_daily[0]) {
4199: // status("Running system daily event");
4200: sbbs->logentry("!:","Ran system daily event");
4201: sbbs->external(sbbs->cmdstr(sbbs->cfg.sys_daily,nulstr,nulstr,NULL)
4202: ,EX_OFFLINE);
4203: }
1.1.1.2 ! root 4204: status(STATUS_WFC);
1.1 root 4205: }
4206:
4207: const char* DLLCALL js_ver(void)
4208: {
4209: #ifdef JAVASCRIPT
4210: return(JS_GetImplementationVersion());
4211: #else
4212: return("");
4213: #endif
4214: }
4215:
4216: /* Returns char string of version and revision */
4217: const char* DLLCALL bbs_ver(void)
4218: {
4219: static char ver[256];
4220: char compiler[32];
4221:
4222: if(ver[0]==0) { /* uninitialized */
4223: DESCRIBE_COMPILER(compiler);
4224:
4225: sprintf(ver,"%s %s%c%s SMBLIB %s Compiled %s %s with %s"
4226: ,TELNET_SERVER
4227: ,VERSION, REVISION
4228: #ifdef _DEBUG
4229: ," Debug"
4230: #else
4231: ,""
4232: #endif
4233: ,smb_lib_ver()
4234: ,__DATE__, __TIME__, compiler
4235: );
4236: }
4237: return(ver);
4238: }
4239:
4240: /* Returns binary-coded version and revision (e.g. 0x31000 == 3.10a) */
4241: long DLLCALL bbs_ver_num(void)
4242: {
4243: return(VERSION_HEX);
4244: }
4245:
4246: void DLLCALL bbs_terminate(void)
4247: {
1.1.1.2 ! root 4248: lprintf(LOG_INFO,"BBS Server terminate");
1.1 root 4249: terminate_server=true;
4250: }
4251:
4252: static void cleanup(int code)
4253: {
1.1.1.2 ! root 4254: lputs(LOG_INFO,"Terminal Server thread terminating");
1.1 root 4255:
4256: if(telnet_socket!=INVALID_SOCKET) {
4257: close_socket(telnet_socket);
4258: telnet_socket=INVALID_SOCKET;
4259: }
4260: if(rlogin_socket!=INVALID_SOCKET) {
4261: close_socket(rlogin_socket);
4262: rlogin_socket=INVALID_SOCKET;
4263: }
4264: #ifdef USE_CRYPTLIB
4265: if(ssh_socket!=INVALID_SOCKET) {
4266: close_socket(ssh_socket);
4267: ssh_socket=INVALID_SOCKET;
4268: }
4269: #endif
4270:
4271:
4272: #ifdef _WINSOCKAPI_
4273: if(WSAInitialized && WSACleanup()!=0)
4274: lprintf(LOG_ERR,"!WSACleanup ERROR %d",ERROR_VALUE);
4275: #endif
4276:
4277: free_cfg(&scfg);
4278: free_text(text);
4279:
4280: semfile_list_free(&recycle_semfiles);
4281: semfile_list_free(&shutdown_semfiles);
4282:
1.1.1.2 ! root 4283: protected_uint32_destroy(node_threads_running);
! 4284:
1.1 root 4285: #ifdef _WIN32
4286: if(exec_mutex!=NULL) {
4287: CloseHandle(exec_mutex);
4288: exec_mutex=NULL;
4289: }
4290:
4291: if(hK32!=NULL) {
4292: FreeLibrary(hK32);
4293: hK32=NULL;
4294: }
4295:
4296: #if 0 && defined(_DEBUG) && defined(_MSC_VER)
4297: _CrtMemDumpAllObjectsSince(&mem_chkpoint);
4298:
4299: if(debug_log!=INVALID_HANDLE_VALUE) {
4300: CloseHandle(debug_log);
4301: debug_log=INVALID_HANDLE_VALUE;
4302: }
4303: #endif // _DEBUG && _MSC_VER
4304: #endif // _WIN32
4305:
4306: status("Down");
4307: thread_down();
4308: if(terminate_server || code)
1.1.1.2 ! root 4309: lprintf(LOG_INFO,"Terminal Server thread terminated (%lu clients served)", served);
1.1 root 4310: if(startup->terminated!=NULL)
4311: startup->terminated(startup->cbdata,code);
4312: }
4313:
4314: void DLLCALL bbs_thread(void* arg)
4315: {
1.1.1.2 ! root 4316: const char* host_name;
1.1 root 4317: char* identity;
4318: char* p;
4319: char str[MAX_PATH+1];
4320: char logstr[256];
4321: SOCKADDR_IN server_addr={0};
4322: SOCKADDR_IN client_addr;
4323: socklen_t client_addr_len;
4324: SOCKET client_socket=INVALID_SOCKET;
4325: fd_set socket_set;
4326: SOCKET high_socket_set;
4327: int i;
4328: int file;
4329: int result;
4330: time_t t;
4331: time_t start;
4332: time_t initialized=0;
4333: node_t node;
4334: sbbs_t* events=NULL;
4335: client_t client;
4336: startup=(bbs_startup_t*)arg;
4337: BOOL is_client=FALSE;
4338: #ifdef __unix__
4339: SOCKET uspy_listen_socket[MAX_NODES];
4340: struct sockaddr_un uspy_addr;
4341: socklen_t uspy_addr_len;
4342: #endif
4343: #ifdef USE_CRYPTLIB
4344: CRYPT_CONTEXT ssh_context;
4345: #endif
4346:
4347: if(startup==NULL) {
4348: sbbs_beep(100,500);
4349: fprintf(stderr, "No startup structure passed!\n");
4350: return;
4351: }
4352:
4353: if(startup->size!=sizeof(bbs_startup_t)) { // verify size
4354: sbbs_beep(100,500);
4355: sbbs_beep(300,500);
4356: sbbs_beep(100,500);
4357: fprintf(stderr, "Invalid startup structure!\n");
4358: return;
4359: }
4360:
4361: #ifdef _THREAD_SUID_BROKEN
4362: if(thread_suid_broken)
4363: startup->seteuid(TRUE);
4364: #endif
4365:
4366: /* Setup intelligent defaults */
4367: if(startup->telnet_port==0) startup->telnet_port=IPPORT_TELNET;
4368: if(startup->rlogin_port==0) startup->rlogin_port=513;
4369: #ifdef USE_CRYPTLIB
4370: if(startup->ssh_port==0) startup->ssh_port=22;
4371: #endif
4372: if(startup->outbuf_drain_timeout==0) startup->outbuf_drain_timeout=10;
4373: if(startup->sem_chk_freq==0) startup->sem_chk_freq=2;
4374: if(startup->temp_dir[0]) backslash(startup->temp_dir);
4375:
4376: ZERO_VAR(js_server_props);
4377: SAFEPRINTF3(js_server_props.version,"%s %s%c",TELNET_SERVER,VERSION,REVISION);
4378: js_server_props.version_detail=bbs_ver();
1.1.1.2 ! root 4379: js_server_props.clients=&node_threads_running.value;
1.1 root 4380: js_server_props.options=&startup->options;
4381: js_server_props.interface_addr=&startup->telnet_interface;
4382:
4383: uptime=0;
4384: served=0;
1.1.1.2 ! root 4385:
1.1 root 4386: startup->recycle_now=FALSE;
4387: startup->shutdown_now=FALSE;
4388: terminate_server=false;
4389:
1.1.1.2 ! root 4390: SetThreadName("BBS");
! 4391:
1.1 root 4392: do {
4393:
1.1.1.2 ! root 4394: protected_uint32_init(&node_threads_running,0);
! 4395:
1.1 root 4396: thread_up(FALSE /* setuid */);
4397:
4398: status("Initializing");
4399:
4400: /* Defeat the lameo hex0rs - the name and copyright must remain intact */
4401: if(crc32(COPYRIGHT_NOTICE,0)!=COPYRIGHT_CRC
4402: || crc32(VERSION_NOTICE,10)!=SYNCHRONET_CRC) {
1.1.1.2 ! root 4403: lprintf(LOG_CRIT,"!CORRUPTED LIBRARY FILE");
1.1 root 4404: cleanup(1);
4405: return;
4406: }
4407:
4408: memset(text, 0, sizeof(text));
4409: memset(&scfg, 0, sizeof(scfg));
4410:
4411: lastuseron[0]=0;
4412:
4413: char compiler[32];
4414: DESCRIBE_COMPILER(compiler);
4415:
4416: lprintf(LOG_INFO,"%s Version %s Revision %c%s"
4417: ,TELNET_SERVER
4418: ,VERSION
4419: ,toupper(REVISION)
4420: #ifdef _DEBUG
4421: ," Debug"
4422: #else
4423: ,""
4424: #endif
4425: );
4426: lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler);
1.1.1.2 ! root 4427: lprintf(LOG_DEBUG,"SMBLIB %s (format %x.%02x)",smb_lib_ver(),smb_ver()>>8,smb_ver()&0xff);
1.1 root 4428:
4429: if(startup->first_node<1 || startup->first_node>startup->last_node) {
1.1.1.2 ! root 4430: lprintf(LOG_CRIT,"!ILLEGAL node configuration (first: %d, last: %d)"
1.1 root 4431: ,startup->first_node, startup->last_node);
4432: cleanup(1);
4433: return;
4434: }
4435:
4436: #ifdef __BORLANDC__
4437: #pragma warn -8008 /* Disable "Condition always false" warning */
4438: #pragma warn -8066 /* Disable "Unreachable code" warning */
4439: #endif
4440: if(sizeof(node_t)!=SIZEOF_NODE_T) {
1.1.1.2 ! root 4441: lprintf(LOG_CRIT,"!COMPILER ERROR: sizeof(node_t)=%d instead of %d"
1.1 root 4442: ,sizeof(node_t),SIZEOF_NODE_T);
4443: cleanup(1);
4444: return;
4445: }
4446:
4447: #ifdef _WIN32
4448: if((exec_mutex=CreateMutex(NULL,false,NULL))==NULL) {
1.1.1.2 ! root 4449: lprintf(LOG_CRIT,"!ERROR %d creating exec_mutex", GetLastError());
1.1 root 4450: cleanup(1);
4451: return;
4452: }
4453: hK32 = LoadLibrary("KERNEL32");
4454: #endif // _WIN32
4455:
4456: if(!winsock_startup()) {
4457: cleanup(1);
4458: return;
4459: }
4460:
4461: t=time(NULL);
4462: lprintf(LOG_INFO,"Initializing on %.24s with options: %lx"
1.1.1.2 ! root 4463: ,ctime_r(&t,str),startup->options);
1.1 root 4464:
4465: if(chdir(startup->ctrl_dir)!=0)
4466: lprintf(LOG_ERR,"!ERROR %d changing directory to: %s", errno, startup->ctrl_dir);
4467:
4468: /* Initial configuration and load from CNF files */
4469: SAFECOPY(scfg.ctrl_dir,startup->ctrl_dir);
4470: lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir);
4471: scfg.size=sizeof(scfg);
4472: scfg.node_num=startup->first_node;
4473: SAFECOPY(logstr,UNKNOWN_LOAD_ERROR);
4474: if(!load_cfg(&scfg, text, TRUE, logstr)) {
1.1.1.2 ! root 4475: lprintf(LOG_CRIT,"!ERROR %s",logstr);
! 4476: lprintf(LOG_CRIT,"!FAILED to load configuration files");
1.1 root 4477: cleanup(1);
4478: return;
4479: }
4480:
4481: if(startup->host_name[0]==0)
4482: SAFECOPY(startup->host_name,scfg.sys_inetaddr);
4483:
1.1.1.2 ! root 4484: if((t=checktime())!=0) { /* Check binary time */
! 4485: lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
1.1 root 4486: }
4487:
4488: if(uptime==0)
4489: uptime=time(NULL); /* this must be done *after* setting the timezone */
4490:
4491: if(startup->last_node>scfg.sys_nodes) {
4492: lprintf(LOG_NOTICE,"Specified last_node (%d) > sys_nodes (%d), auto-corrected"
4493: ,startup->last_node, scfg.sys_nodes);
4494: startup->last_node=scfg.sys_nodes;
4495: }
4496:
4497: /* Create missing directories */
4498: lprintf(LOG_INFO,"Verifying/creating data directories");
4499: make_data_dirs(&scfg);
4500:
4501: /* Create missing node directories and dsts.dab files */
4502: lprintf(LOG_INFO,"Verifying/creating node directories");
4503: for(i=0;i<=scfg.sys_nodes;i++) {
4504: if(i)
4505: md(scfg.node_path[i-1]);
4506: SAFEPRINTF(str,"%sdsts.dab",i ? scfg.node_path[i-1] : scfg.ctrl_dir);
4507: if(flength(str)<DSTSDABLEN) {
1.1.1.2 ! root 4508: if((file=sopen(str,O_WRONLY|O_CREAT|O_APPEND, SH_DENYNO, DEFFILEMODE))==-1) {
! 4509: lprintf(LOG_CRIT,"!ERROR %d creating %s",errno, str);
1.1 root 4510: cleanup(1);
4511: return;
4512: }
4513: while(filelength(file)<DSTSDABLEN)
4514: if(write(file,"\0",1)!=1)
4515: break; /* Create NULL system dsts.dab */
4516: close(file);
4517: }
4518: }
4519:
4520: /* Initial global node variables */
4521: for(i=0;i<MAX_NODES;i++) {
4522: node_inbuf[i]=NULL;
4523: node_socket[i]=INVALID_SOCKET;
4524: spy_socket[i]=INVALID_SOCKET;
4525: #ifdef __unix__
4526: uspy_socket[i]=INVALID_SOCKET;
4527: uspy_listen_socket[i]=INVALID_SOCKET;
4528: #endif
4529: }
4530:
4531: startup->node_inbuf=node_inbuf;
4532:
4533: /* open a socket and wait for a client */
4534:
4535: telnet_socket = open_socket(SOCK_STREAM, "telnet");
4536:
4537: if(telnet_socket == INVALID_SOCKET) {
1.1.1.2 ! root 4538: lprintf(LOG_CRIT,"!ERROR %d creating Telnet socket", ERROR_VALUE);
1.1 root 4539: cleanup(1);
4540: return;
4541: }
4542:
1.1.1.2 ! root 4543: lprintf(LOG_DEBUG,"Telnet socket %d opened",telnet_socket);
1.1 root 4544:
4545: /*****************************/
4546: /* Listen for incoming calls */
4547: /*****************************/
4548: memset(&server_addr, 0, sizeof(server_addr));
4549:
4550: server_addr.sin_addr.s_addr = htonl(startup->telnet_interface);
4551: server_addr.sin_family = AF_INET;
4552: server_addr.sin_port = htons(startup->telnet_port);
4553:
1.1.1.2 ! root 4554: if(startup->telnet_port < IPPORT_RESERVED) {
! 4555: if(startup->seteuid!=NULL)
! 4556: startup->seteuid(FALSE);
! 4557: }
1.1 root 4558: result = retry_bind(telnet_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
4559: ,startup->bind_retry_count,startup->bind_retry_delay,"Telnet Server",lprintf);
1.1.1.2 ! root 4560: if(startup->telnet_port < IPPORT_RESERVED) {
! 4561: if(startup->seteuid!=NULL)
! 4562: startup->seteuid(TRUE);
! 4563: }
1.1 root 4564: if(result != 0) {
1.1.1.2 ! root 4565: lprintf(LOG_CRIT,"%s",BIND_FAILURE_HELP);
1.1 root 4566: cleanup(1);
4567: return;
4568: }
4569:
4570: result = listen(telnet_socket, 1);
4571:
4572: if(result != 0) {
1.1.1.2 ! root 4573: lprintf(LOG_CRIT,"!ERROR %d (%d) listening on Telnet socket", result, ERROR_VALUE);
1.1 root 4574: cleanup(1);
4575: return;
4576: }
1.1.1.2 ! root 4577: lprintf(LOG_INFO,"Telnet Server listening on port %u",startup->telnet_port);
1.1 root 4578:
4579: if(startup->options&BBS_OPT_ALLOW_RLOGIN) {
4580:
4581: /* open a socket and wait for a client */
4582:
4583: rlogin_socket = open_socket(SOCK_STREAM, "rlogin");
4584:
4585: if(rlogin_socket == INVALID_SOCKET) {
1.1.1.2 ! root 4586: lprintf(LOG_CRIT,"!ERROR %d creating RLogin socket", ERROR_VALUE);
1.1 root 4587: cleanup(1);
4588: return;
4589: }
4590:
1.1.1.2 ! root 4591: lprintf(LOG_DEBUG,"RLogin socket %d opened",rlogin_socket);
1.1 root 4592:
4593: /*****************************/
4594: /* Listen for incoming calls */
4595: /*****************************/
4596: memset(&server_addr, 0, sizeof(server_addr));
4597:
4598: server_addr.sin_addr.s_addr = htonl(startup->rlogin_interface);
4599: server_addr.sin_family = AF_INET;
4600: server_addr.sin_port = htons(startup->rlogin_port);
4601:
1.1.1.2 ! root 4602: if(startup->rlogin_port < IPPORT_RESERVED) {
! 4603: if(startup->seteuid!=NULL)
! 4604: startup->seteuid(FALSE);
! 4605: }
1.1 root 4606: result = retry_bind(rlogin_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
4607: ,startup->bind_retry_count,startup->bind_retry_delay,"RLogin Server",lprintf);
1.1.1.2 ! root 4608: if(startup->rlogin_port < IPPORT_RESERVED) {
! 4609: if(startup->seteuid!=NULL)
! 4610: startup->seteuid(TRUE);
! 4611: }
1.1 root 4612: if(result != 0) {
1.1.1.2 ! root 4613: lprintf(LOG_CRIT,"%s",BIND_FAILURE_HELP);
1.1 root 4614: cleanup(1);
4615: return;
4616: }
4617:
4618: result = listen(rlogin_socket, 1);
4619:
4620: if(result != 0) {
1.1.1.2 ! root 4621: lprintf(LOG_CRIT,"!ERROR %d (%d) listening on RLogin socket", result, ERROR_VALUE);
1.1 root 4622: cleanup(1);
4623: return;
4624: }
1.1.1.2 ! root 4625: lprintf(LOG_INFO,"RLogin Server listening on port %u",startup->rlogin_port);
1.1 root 4626: }
4627:
4628: #ifdef USE_CRYPTLIB
4629: #if CRYPTLIB_VERSION < 3300
4630: #warning This version of Cryptlib is known to crash Synchronet. Upgrade to at least version 3.3 or do not build with Cryptlib support.
4631: #endif
4632: if(startup->options&BBS_OPT_ALLOW_SSH) {
4633: bool loaded_key=false;
4634:
4635: CRYPT_KEYSET ssh_keyset;
4636:
4637: cryptInit();
4638: cryptAddRandom(NULL,CRYPT_RANDOM_SLOWPOLL);
4639: /* Get the private key... first try loading it from a file... */
4640: SAFEPRINTF2(str,"%s%s",scfg.ctrl_dir,"cryptlib.key");
4641: if(cryptStatusOK(cryptKeysetOpen(&ssh_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, str, CRYPT_KEYOPT_NONE))) {
4642: if(cryptStatusOK(cryptGetPrivateKey(ssh_keyset, &ssh_context, CRYPT_KEYID_NAME, "ssh_server", scfg.sys_pass)))
4643: loaded_key=true;
4644: cryptKeysetClose(ssh_keyset);
4645: /* Failed to load the key... delete the keyfile and create a new one */
4646: if(!loaded_key)
4647: remove(str);
4648: }
4649:
4650: if(!loaded_key) {
4651: /* Couldn't do that... create a new context and use the key from there... */
4652:
4653: if(!cryptStatusOK(i=cryptCreateContext(&ssh_context, CRYPT_UNUSED, CRYPT_ALGO_RSA))) {
1.1.1.2 ! root 4654: lprintf(LOG_ERR,"SSH Cryptlib error %d creating context",i);
1.1 root 4655: goto NO_SSH;
4656: }
4657: if(!cryptStatusOK(i=cryptSetAttributeString(ssh_context, CRYPT_CTXINFO_LABEL, "ssh_server", 10))) {
1.1.1.2 ! root 4658: lprintf(LOG_ERR,"SSH Cryptlib error %d setting key label",i);
1.1 root 4659: goto NO_SSH;
4660: }
4661: if(!cryptStatusOK(i=cryptGenerateKey(ssh_context))) {
1.1.1.2 ! root 4662: lprintf(LOG_ERR,"SSH Cryptlib error %d generating key",i);
1.1 root 4663: goto NO_SSH;
4664: }
4665:
4666: /* Ok, now try saving this one... use the syspass to enctrpy it. */
4667: if(cryptStatusOK(cryptKeysetOpen(&ssh_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, str, CRYPT_KEYOPT_CREATE))) {
4668: cryptAddPrivateKey(ssh_keyset, ssh_context, scfg.sys_pass);
4669: cryptKeysetClose(ssh_keyset);
4670: }
4671: }
4672:
4673: /* open a socket and wait for a client */
4674:
4675: ssh_socket = open_socket(SOCK_STREAM, "ssh");
4676:
4677: if(ssh_socket == INVALID_SOCKET) {
1.1.1.2 ! root 4678: lprintf(LOG_CRIT,"!ERROR %d creating SSH socket", ERROR_VALUE);
1.1 root 4679: cleanup(1);
4680: return;
4681: }
4682:
1.1.1.2 ! root 4683: lprintf(LOG_DEBUG,"SSH socket %d opened",ssh_socket);
1.1 root 4684:
4685: /*****************************/
4686: /* Listen for incoming calls */
4687: /*****************************/
4688: memset(&server_addr, 0, sizeof(server_addr));
4689:
4690: server_addr.sin_addr.s_addr = htonl(startup->ssh_interface);
4691: server_addr.sin_family = AF_INET;
4692: server_addr.sin_port = htons(startup->ssh_port);
4693:
1.1.1.2 ! root 4694: if(startup->ssh_port < IPPORT_RESERVED) {
! 4695: if(startup->seteuid!=NULL)
! 4696: startup->seteuid(FALSE);
! 4697: }
1.1 root 4698: result = retry_bind(ssh_socket,(struct sockaddr *)&server_addr,sizeof(server_addr)
4699: ,startup->bind_retry_count,startup->bind_retry_delay,"SSH Server",lprintf);
1.1.1.2 ! root 4700: if(startup->ssh_port < IPPORT_RESERVED) {
! 4701: if(startup->seteuid!=NULL)
! 4702: startup->seteuid(TRUE);
! 4703: }
1.1 root 4704: if(result != 0) {
1.1.1.2 ! root 4705: lprintf(LOG_CRIT,"%s",BIND_FAILURE_HELP);
1.1 root 4706: cleanup(1);
4707: return;
4708: }
4709:
4710: result = listen(ssh_socket, 1);
4711:
4712: if(result != 0) {
1.1.1.2 ! root 4713: lprintf(LOG_CRIT,"!ERROR %d (%d) listening on SSH socket", result, ERROR_VALUE);
1.1 root 4714: cleanup(1);
4715: return;
4716: }
1.1.1.2 ! root 4717: lprintf(LOG_INFO,"SSH Server listening on port %u",startup->ssh_port);
1.1 root 4718: }
4719: NO_SSH:
4720: #endif
4721:
1.1.1.2 ! root 4722: sbbs = new sbbs_t(0, server_addr
! 4723: ,"Terminal Server", telnet_socket, &scfg, text, NULL);
1.1 root 4724: sbbs->online = 0;
4725: if(sbbs->init()==false) {
1.1.1.2 ! root 4726: lputs(LOG_CRIT,"!BBS initialization failed");
1.1 root 4727: cleanup(1);
4728: return;
4729: }
4730: _beginthread(output_thread, 0, sbbs);
4731:
4732: if(!(startup->options&BBS_OPT_NO_EVENTS)) {
1.1.1.2 ! root 4733: events = new sbbs_t(0, server_addr
1.1 root 4734: ,"BBS Events", INVALID_SOCKET, &scfg, text, NULL);
4735: events->online = 0;
4736: if(events->init()==false) {
1.1.1.2 ! root 4737: lputs(LOG_CRIT,"!Events initialization failed");
1.1 root 4738: cleanup(1);
4739: return;
4740: }
4741: _beginthread(event_thread, 0, events);
4742: }
4743:
4744: /* Save these values incase they're changed dynamically */
4745: first_node=startup->first_node;
4746: last_node=startup->last_node;
4747:
4748: for(i=first_node;i<=last_node;i++) {
4749: sbbs->getnodedat(i,&node,1);
4750: node.status=NODE_WFC;
1.1.1.2 ! root 4751: node.misc&=NODE_EVENT; /* Note: Turns-off NODE_RRUN flag (and others) */
1.1 root 4752: node.action=0;
4753: sbbs->putnodedat(i,&node);
4754: }
4755:
4756: status(STATUS_WFC);
4757:
4758: #if defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER)
4759:
4760: SAFEPRINTF(str,"%sDEBUG.LOG",scfg.logs_dir);
4761: if((debug_log=CreateFile(
4762: str, // pointer to name of the file
4763: GENERIC_READ|GENERIC_WRITE,
4764: FILE_SHARE_READ|FILE_SHARE_WRITE,
4765: NULL, // pointer to security attributes
4766: OPEN_ALWAYS, // how to create
4767: FILE_ATTRIBUTE_NORMAL, // file attributes
4768: NULL // handle to file with attributes to
4769: ))==INVALID_HANDLE_VALUE) {
1.1.1.2 ! root 4770: lprintf(LOG_CRIT,"!ERROR %ld creating %s",GetLastError(),str);
1.1 root 4771: cleanup(1);
4772: return;
4773: }
4774:
4775: _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
4776: _CrtSetReportFile(_CRT_WARN, debug_log);
4777: _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE|_CRTDBG_MODE_WNDW);
4778: _CrtSetReportFile(_CRT_ERROR, debug_log);
4779: _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE|_CRTDBG_MODE_WNDW);
4780: _CrtSetReportFile(_CRT_ASSERT, debug_log);
4781:
4782: /* Turns on memory leak checking during program termination */
4783: // _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
4784:
4785: /* Save this allocation point for comparison */
4786: _CrtMemCheckpoint(&mem_chkpoint);
4787:
4788: #endif // _WIN32 && _DEBUG && _MSC_VER
4789:
4790: /* Setup recycle/shutdown semaphore file lists */
4791: shutdown_semfiles=semfile_list_init(scfg.ctrl_dir,"shutdown","telnet");
4792: recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","telnet");
4793: SAFEPRINTF(str,"%stelnet.rec",scfg.ctrl_dir); /* legacy */
4794: semfile_list_add(&recycle_semfiles,str);
1.1.1.2 ! root 4795: SAFEPRINTF(str,"%stext.dat",scfg.ctrl_dir);
! 4796: semfile_list_add(&recycle_semfiles,str);
! 4797: if(!initialized)
1.1 root 4798: semfile_list_check(&initialized,shutdown_semfiles);
1.1.1.2 ! root 4799: semfile_list_check(&initialized,recycle_semfiles);
1.1 root 4800:
4801: #ifdef __unix__ // unix-domain spy sockets
4802: for(i=first_node;i<=last_node && !(startup->options&BBS_OPT_NO_SPY_SOCKETS);i++) {
4803: if((uspy_listen_socket[i-1]=socket(PF_UNIX,SOCK_STREAM,0))==INVALID_SOCKET)
4804: lprintf(LOG_ERR,"Node %d !ERROR %d creating local spy socket"
4805: , i, errno);
4806: else {
4807: lprintf(LOG_INFO,"Node %d local spy using socket %d", i, uspy_listen_socket[i-1]);
4808: if(startup!=NULL && startup->socket_open!=NULL)
4809: startup->socket_open(startup->cbdata,TRUE);
4810: }
4811:
4812: uspy_addr.sun_family=AF_UNIX;
4813: if((unsigned int)snprintf(str,sizeof(uspy_addr.sun_path),
4814: "%slocalspy%d.sock", startup->temp_dir, i)
4815: >=sizeof(uspy_addr.sun_path))
4816: uspy_listen_socket[i-1]=INVALID_SOCKET;
4817: else {
4818: strcpy(uspy_addr.sun_path,str);
4819: if(fexist(str))
4820: unlink(str);
4821: }
4822: if(uspy_listen_socket[i-1]!=INVALID_SOCKET) {
4823: uspy_addr_len=SUN_LEN(&uspy_addr);
4824: if(bind(uspy_listen_socket[i-1], (struct sockaddr *) &uspy_addr, uspy_addr_len)) {
4825: lprintf(LOG_ERR,"Node %d !ERROR %d binding local spy socket %d to %s"
4826: , i, errno, uspy_listen_socket[i-1], uspy_addr.sun_path);
4827: close_socket(uspy_listen_socket[i-1]);
4828: uspy_listen_socket[i-1]=INVALID_SOCKET;
4829: continue;
4830: }
4831: lprintf(LOG_INFO,"Node %d local spy socket %d bound to %s"
4832: , i, uspy_listen_socket[i-1], uspy_addr.sun_path);
4833: if(listen(uspy_listen_socket[i-1],1)) {
4834: lprintf(LOG_ERR,"Node %d !ERROR %d listening local spy socket %d"
4835: ,i, errno, uspy_listen_socket[i-1]);
4836: close_socket(uspy_listen_socket[i-1]);
4837: uspy_listen_socket[i-1]=INVALID_SOCKET;
4838: continue;
4839: }
4840: uspy_addr_len=sizeof(uspy_addr);
4841: }
4842: }
4843: #endif // __unix__ (unix-domain spy sockets)
4844:
4845: /* signal caller that we've started up successfully */
4846: if(startup->started!=NULL)
4847: startup->started(startup->cbdata);
4848:
1.1.1.2 ! root 4849: lprintf(LOG_INFO,"Terminal Server thread started for nodes %d through %d", first_node, last_node);
1.1 root 4850:
4851: while(!terminate_server) {
4852:
1.1.1.2 ! root 4853: if(node_threads_running.value==0) { /* check for re-run flags and recycle/shutdown sem files */
1.1 root 4854: if(!(startup->options&BBS_OPT_NO_RECYCLE)) {
1.1.1.2 ! root 4855:
! 4856: bool rerun=false;
! 4857: for(i=first_node;i<=last_node;i++) {
! 4858: if(sbbs->getnodedat(i,&node,0)!=0)
! 4859: continue;
! 4860: if(node.misc&NODE_RRUN) {
! 4861: sbbs->getnodedat(i,&node,1);
! 4862: if(!rerun)
! 4863: lprintf(LOG_INFO,"Node %d flagged for re-run",i);
! 4864: rerun=true;
! 4865: node.misc&=~NODE_RRUN;
! 4866: sbbs->putnodedat(i,&node);
! 4867: }
! 4868: }
! 4869: if(rerun)
! 4870: break;
! 4871:
1.1 root 4872: if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) {
4873: lprintf(LOG_INFO,"%04d Recycle semaphore file (%s) detected"
4874: ,telnet_socket,p);
4875: break;
4876: }
4877: if(startup->recycle_now==TRUE) {
4878: lprintf(LOG_INFO,"%04d Recycle semaphore signaled",telnet_socket);
4879: startup->recycle_now=FALSE;
4880: break;
4881: }
4882: }
4883: if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL
4884: && lprintf(LOG_INFO,"%04d Shutdown semaphore file (%s) detected"
4885: ,telnet_socket,p))
4886: || (startup->shutdown_now==TRUE
4887: && lprintf(LOG_INFO,"%04d Shutdown semaphore signaled"
4888: ,telnet_socket))) {
4889: startup->shutdown_now=FALSE;
4890: terminate_server=TRUE;
4891: break;
4892: }
4893: }
4894:
4895: sbbs->online=FALSE;
1.1.1.2 ! root 4896: // sbbs->client_socket=INVALID_SOCKET;
1.1 root 4897: #ifdef USE_CRYPTLIB
4898: sbbs->ssh_mode=false;
4899: #endif
4900:
4901: /* now wait for connection */
4902:
4903: FD_ZERO(&socket_set);
4904: high_socket_set=0;
4905: if(telnet_socket!=INVALID_SOCKET) {
4906: FD_SET(telnet_socket,&socket_set);
4907: high_socket_set=telnet_socket+1;
4908: }
4909: if(startup->options&BBS_OPT_ALLOW_RLOGIN
4910: && rlogin_socket!=INVALID_SOCKET) {
4911: FD_SET(rlogin_socket,&socket_set);
4912: if(rlogin_socket+1>high_socket_set)
4913: high_socket_set=rlogin_socket+1;
4914: }
4915: #ifdef USE_CRYPTLIB
4916: if(startup->options&BBS_OPT_ALLOW_SSH
4917: && ssh_socket!=INVALID_SOCKET) {
4918: FD_SET(ssh_socket,&socket_set);
4919: if(ssh_socket+1>high_socket_set)
4920: high_socket_set=ssh_socket+1;
4921: }
4922: #endif
4923: #ifdef __unix__
4924: for(i=first_node;i<=last_node;i++) {
4925: if(uspy_listen_socket[i-1]!=INVALID_SOCKET) {
4926: FD_SET(uspy_listen_socket[i-1],&socket_set);
4927: if(uspy_listen_socket[i-1]+1>high_socket_set)
4928: high_socket_set=uspy_listen_socket[i-1]+1;
4929: }
4930: if(uspy_socket[i-1]!=INVALID_SOCKET) {
4931: FD_SET(uspy_socket[i-1],&socket_set);
4932: if(uspy_socket[i-1]+1>high_socket_set)
4933: high_socket_set=uspy_listen_socket[i-1]+1;
4934: }
4935: }
4936: #endif
4937:
4938: struct timeval tv;
4939: tv.tv_sec=startup->sem_chk_freq;
4940: tv.tv_usec=0;
4941:
4942: if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) {
4943: if(i==0)
4944: continue;
4945: if(ERROR_VALUE==EINTR)
1.1.1.2 ! root 4946: lprintf(LOG_DEBUG,"Terminal Server listening interrupted");
1.1 root 4947: else if(ERROR_VALUE == ENOTSOCK)
1.1.1.2 ! root 4948: lprintf(LOG_NOTICE,"Terminal Server sockets closed");
1.1 root 4949: else
4950: lprintf(LOG_WARNING,"!ERROR %d selecting sockets",ERROR_VALUE);
4951: continue;
4952: }
4953:
4954: if(terminate_server) /* terminated */
4955: break;
4956:
4957: client_addr_len = sizeof(client_addr);
4958:
4959: bool rlogin = false;
4960: #ifdef USE_CRYPTLIB
4961: bool ssh = false;
4962: #endif
4963:
4964: is_client=FALSE;
4965: if(telnet_socket!=INVALID_SOCKET
4966: && FD_ISSET(telnet_socket,&socket_set)) {
4967: client_socket = accept_socket(telnet_socket, (struct sockaddr *)&client_addr
4968: ,&client_addr_len);
4969: is_client=TRUE;
4970: } else if(rlogin_socket!=INVALID_SOCKET
4971: && FD_ISSET(rlogin_socket,&socket_set)) {
4972: client_socket = accept_socket(rlogin_socket, (struct sockaddr *)&client_addr
4973: ,&client_addr_len);
4974: rlogin = true;
4975: is_client=TRUE;
4976: #ifdef USE_CRYPTLIB
4977: } else if(ssh_socket!=INVALID_SOCKET
4978: && FD_ISSET(ssh_socket,&socket_set)) {
4979:
4980: client_socket = accept_socket(ssh_socket, (struct sockaddr *)&client_addr
4981: ,&client_addr_len);
4982: ssh = true;
4983: is_client=TRUE;
4984: sbbs->ssh_mode=true;
4985: #endif
4986: } else {
4987: #ifdef __unix__
4988: for(i=first_node;i<=last_node;i++) {
4989: if(uspy_socket[i-1]!=INVALID_SOCKET
4990: && FD_ISSET(uspy_socket[i-1],&socket_set)) {
4991: if(node_socket[i-1]==INVALID_SOCKET)
4992: read(uspy_socket[i-1],str,sizeof(str));
4993: if(!socket_check(uspy_socket[i-1],NULL,NULL,0)) {
4994: lprintf(LOG_NOTICE,"Spy socket for node %d disconnected",i);
4995: close_socket(uspy_socket[i-1]);
4996: uspy_socket[i-1]=INVALID_SOCKET;
4997: }
4998: }
4999: if(uspy_listen_socket[i-1]!=INVALID_SOCKET
5000: && FD_ISSET(uspy_listen_socket[i-1],&socket_set)) {
5001: BOOL already_connected=(uspy_socket[i-1]!=INVALID_SOCKET);
5002: SOCKET new_socket=INVALID_SOCKET;
5003: new_socket = accept(uspy_listen_socket[i-1], (struct sockaddr *)&uspy_addr
5004: ,&uspy_addr_len);
5005: if(new_socket < 0) {
5006: lprintf(LOG_ERR,"!ERROR Spy socket for node %d unable to accept()",i);
5007: close_socket(uspy_listen_socket[i-1]);
5008: uspy_listen_socket[i-1]=INVALID_SOCKET;
5009: }
5010: fcntl(new_socket,F_SETFL,fcntl(new_socket,F_GETFL)|O_NONBLOCK);
5011: if(already_connected) {
5012: lprintf(LOG_ERR,"!ERROR Spy socket %s already in use",uspy_addr.sun_path);
5013: send(new_socket,"Spy socket already in use.\r\n",27,0);
5014: close_socket(new_socket);
5015: }
5016: else {
5017: lprintf(LOG_ERR,"!Spy socket %s (%d) connected",uspy_addr.sun_path,new_socket);
5018: uspy_socket[i-1]=new_socket;
5019: SAFEPRINTF(str,"Spy connection established to node %d\r\n",i);
5020: send(uspy_socket[i-1],str,strlen(str),0);
5021: }
5022: }
5023: }
5024: #else
5025: lprintf(LOG_ERR,"!NO SOCKETS set by select");
5026: #endif
5027: }
5028:
5029: if(!is_client) {
5030: /* Do not need to close_socket(client_socket) here */
5031: continue;
5032: }
5033:
5034: if(client_socket == INVALID_SOCKET) {
5035: #if 0 /* is this necessary still? */
5036: if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINTR || ERROR_VALUE == EINVAL) {
5037: lputs(LOG_NOTICE,"BBS socket closed");
5038: break;
5039: }
5040: #endif
5041: lprintf(LOG_ERR,"!ERROR %d accepting connection", ERROR_VALUE);
5042: #ifdef _WIN32
5043: if(WSAGetLastError()==WSAENOBUFS) /* recycle (re-init WinSock) on this error */
5044: break;
5045: #endif
5046: SSH_END();
5047: continue;
5048: }
5049: char host_ip[32];
5050:
5051: strcpy(host_ip,inet_ntoa(client_addr.sin_addr));
5052:
5053: if(trashcan(&scfg,host_ip,"ip-silent")) {
5054: SSH_END();
5055: close_socket(client_socket);
5056: continue;
5057: }
5058:
5059: lprintf(LOG_INFO,"%04d %s connection accepted from: %s port %u"
5060: ,client_socket
5061: #ifdef USE_CRYPTLIB
5062: ,rlogin ? "RLogin" : (ssh ? "SSH" : "Telnet")
5063: #else
5064: ,rlogin ? "RLogin" : "Telnet"
5065: #endif
5066: , host_ip, ntohs(client_addr.sin_port));
5067:
5068: #ifdef _WIN32
5069: if(startup->answer_sound[0] && !(startup->options&BBS_OPT_MUTE))
5070: PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
5071: #endif
5072:
5073: /* Do SSH stuff here */
5074:
5075: if(ssh) {
1.1.1.2 ! root 5076: int ssh_failed=0;
1.1 root 5077: if(!cryptStatusOK(i=cryptCreateSession(&sbbs->ssh_session, CRYPT_UNUSED, CRYPT_SESSION_SSH_SERVER))) {
1.1.1.2 ! root 5078: lprintf(LOG_WARNING,"%04d SSH Cryptlib error %d creating session", client_socket, i);
1.1 root 5079: close_socket(client_socket);
5080: continue;
5081: }
5082: if(!cryptStatusOK(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_PRIVATEKEY, ssh_context))) {
1.1.1.2 ! root 5083: lprintf(LOG_WARNING,"%04d SSH Cryptlib error %d setting private key",client_socket, i);
1.1 root 5084: cryptDestroySession(sbbs->ssh_session);
5085: close_socket(client_socket);
5086: continue;
5087: }
5088: if(!cryptStatusOK(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_NETWORKSOCKET, client_socket))) {
1.1.1.2 ! root 5089: lprintf(LOG_WARNING,"%04d SSH Cryptlib error %d setting socket",client_socket, i);
1.1 root 5090: cryptDestroySession(sbbs->ssh_session);
5091: close_socket(client_socket);
5092: continue;
5093: }
1.1.1.2 ! root 5094: for(ssh_failed=0; ssh_failed < 2; ssh_failed++) {
! 5095: /* Accept any credentials */
! 5096: if(!cryptStatusOK(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_AUTHRESPONSE, 1))) {
! 5097: ssh_failed=1;
! 5098: break;
! 5099: }
! 5100: if(!cryptStatusOK(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_ACTIVE, 1))) {
! 5101: if(i != CRYPT_ENVELOPE_RESOURCE) {
! 5102: ssh_failed=2;
! 5103: break;
! 5104: }
! 5105: }
! 5106: else {
! 5107: ssh_failed=0;
! 5108: break;
! 5109: }
! 5110: }
! 5111: switch(ssh_failed) {
! 5112: case 1:
! 5113: lprintf(LOG_WARNING,"%04d SSH Cryptlib error %d setting AUTHRESPONSE",client_socket, i);
! 5114: break;
! 5115: case 2:
! 5116: switch(i) {
! 5117: case CRYPT_ERROR_BADDATA:
! 5118: lprintf(LOG_NOTICE,"%04d SSH Bad/unrecognized data format", client_socket);
! 5119: break;
! 5120: case CRYPT_ERROR_READ:
! 5121: lprintf(LOG_WARNING,"%04d SSH Read failure", client_socket);
! 5122: break;
! 5123: case CRYPT_ERROR_WRITE:
! 5124: lprintf(LOG_WARNING,"%04d SSH Write failure", client_socket);
! 5125: break;
! 5126: default:
! 5127: lprintf(LOG_WARNING,"%04d SSH Cryptlib error %d setting session active",client_socket, i);
! 5128: break;
! 5129: }
! 5130: break;
! 5131: }
! 5132: if(ssh_failed) {
1.1 root 5133: cryptDestroySession(sbbs->ssh_session);
5134: close_socket(client_socket);
5135: continue;
5136: }
5137: cryptPopData(sbbs->ssh_session, str, sizeof(str), &i);
5138: }
5139:
5140: sbbs->client_socket=client_socket; // required for output to the user
5141: sbbs->online=ON_REMOTE;
5142:
5143: if(sbbs->trashcan(host_ip,"ip")) {
5144: SSH_END();
5145: close_socket(client_socket);
1.1.1.2 ! root 5146: lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s"
! 5147: ,client_socket, host_ip);
1.1 root 5148: SAFEPRINTF(logstr, "Blocked IP: %s",host_ip);
5149: sbbs->syslog("@!",logstr);
5150: continue;
5151: }
5152:
5153: if(rlogin)
5154: sbbs->outcom(0); /* acknowledge RLogin per RFC 1282 */
5155:
5156: sbbs->putcom(crlf);
5157: sbbs->putcom(VERSION_NOTICE);
5158: sbbs->putcom(crlf);
5159:
5160: sbbs->bprintf("Connection from: %s\r\n", host_ip);
5161:
5162: struct hostent* h;
5163: if(startup->options&BBS_OPT_NO_HOST_LOOKUP)
5164: h=NULL;
5165: else {
5166: sbbs->bprintf("Resolving hostname...");
5167: h=gethostbyaddr((char *)&client_addr.sin_addr
5168: ,sizeof(client_addr.sin_addr),AF_INET);
5169: sbbs->putcom(crlf);
5170: }
5171: if(h!=NULL && h->h_name!=NULL)
5172: host_name=h->h_name;
5173: else
5174: host_name="<no name>";
5175:
1.1.1.2 ! root 5176: if(!(startup->options&BBS_OPT_NO_HOST_LOOKUP))
1.1 root 5177: lprintf(LOG_INFO,"%04d Hostname: %s", client_socket, host_name);
5178:
5179: if(sbbs->trashcan(host_name,"host")) {
5180: SSH_END();
5181: close_socket(client_socket);
1.1.1.2 ! root 5182: lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in host.can: %s"
! 5183: ,client_socket, host_name);
1.1 root 5184: SAFEPRINTF(logstr, "Blocked Hostname: %s",host_name);
5185: sbbs->syslog("@!",logstr);
5186: continue;
5187: }
5188:
5189: identity=NULL;
5190: if(startup->options&BBS_OPT_GET_IDENT) {
5191: sbbs->bprintf("Resolving identity...");
1.1.1.2 ! root 5192: /* ToDo: Make ident timeout configurable */
! 5193: if(identify(&client_addr, startup->telnet_port, str, sizeof(str)-1, /* timeout: */1)) {
! 5194: lprintf(LOG_DEBUG,"%04d Ident Response: %s",client_socket, str);
! 5195: identity=strrchr(str,':');
! 5196: if(identity!=NULL) {
! 5197: identity++; /* skip colon */
! 5198: SKIP_WHITESPACE(identity);
! 5199: if(*identity)
! 5200: lprintf(LOG_INFO,"%04d Identity: %s",client_socket, identity);
! 5201: }
1.1 root 5202: }
5203: sbbs->putcom(crlf);
5204: }
5205: /* Initialize client display */
5206: client.size=sizeof(client);
5207: client.time=time(NULL);
5208: SAFECOPY(client.addr,host_ip);
5209: SAFECOPY(client.host,host_name);
5210: client.port=ntohs(client_addr.sin_port);
5211: #ifdef USE_CRYPTLIB
5212: client.protocol=rlogin ? "RLogin":(ssh ? "SSH" : "Telnet");
5213: #else
5214: client.protocol=rlogin ? "RLogin":"Telnet";
5215: #endif
5216: client.user="<unknown>";
5217: client_on(client_socket,&client,FALSE /* update */);
5218:
5219: for(i=first_node;i<=last_node;i++) {
5220: /* paranoia: make sure node.status!=NODE_WFC by default */
5221: node.status=NODE_INVALID_STATUS;
5222: if(sbbs->getnodedat(i,&node,1)!=0)
5223: continue;
5224: if(node.status==NODE_WFC) {
5225: node.status=NODE_LOGON;
1.1.1.2 ! root 5226: #ifdef USE_CRYPTLIB
! 5227: if(ssh)
! 5228: node.connection=NODE_CONNECTION_SSH;
! 5229: else
! 5230: #endif
! 5231: if(rlogin)
! 5232: node.connection=NODE_CONNECTION_RLOGIN;
! 5233: else
! 5234: node.connection=NODE_CONNECTION_TELNET;
! 5235:
1.1 root 5236: sbbs->putnodedat(i,&node);
5237: break;
5238: }
5239: sbbs->putnodedat(i,&node);
5240: }
5241:
5242: if(i>last_node) {
5243: lprintf(LOG_WARNING,"%04d !No nodes available for login.",client_socket);
5244: SAFEPRINTF(str,"%snonodes.txt",scfg.text_dir);
5245: if(fexist(str))
5246: sbbs->printfile(str,P_NOABORT);
5247: else {
5248: sbbs->putcom("\r\nSorry, all telnet nodes are in use or otherwise unavailable.\r\n");
5249: sbbs->putcom("Please try again later.\r\n");
5250: }
5251: mswait(3000);
5252: client_off(client_socket);
5253: SSH_END();
5254: close_socket(client_socket);
5255: continue;
5256: }
5257:
5258: node_socket[i-1]=client_socket;
5259:
1.1.1.2 ! root 5260: sbbs_t* new_node = new sbbs_t(i, client_addr, host_name
1.1 root 5261: ,client_socket
5262: ,&scfg, text, &client);
5263:
5264: new_node->client=client;
5265: #ifdef USE_CRYPTLIB
5266: if(ssh) {
5267: new_node->ssh_session=sbbs->ssh_session;
5268: new_node->ssh_mode=true;
5269: }
5270: #endif
5271:
5272: /* copy the IDENT response, if any */
5273: if(identity!=NULL)
5274: SAFECOPY(new_node->client_ident,identity);
5275:
5276: if(new_node->init()==false) {
1.1.1.2 ! root 5277: lprintf(LOG_INFO,"%04d Node %d !Initialization failure"
1.1 root 5278: ,client_socket,new_node->cfg.node_num);
5279: SAFEPRINTF(str,"%snonodes.txt",scfg.text_dir);
5280: if(fexist(str))
5281: sbbs->printfile(str,P_NOABORT);
5282: else
5283: sbbs->putcom("\r\nSorry, initialization failed. Try again later.\r\n");
5284: mswait(3000);
5285: sbbs->getnodedat(new_node->cfg.node_num,&node,1);
5286: node.status=NODE_WFC;
5287: sbbs->putnodedat(new_node->cfg.node_num,&node);
5288: delete new_node;
5289: node_socket[i-1]=INVALID_SOCKET;
5290: client_off(client_socket);
5291: SSH_END();
5292: close_socket(client_socket);
5293: continue;
5294: }
5295:
5296: if(rlogin==true) {
1.1.1.2 ! root 5297: SAFECOPY(new_node->connection,"RLogin");
! 5298: new_node->node_connection=NODE_CONNECTION_RLOGIN;
1.1 root 5299: new_node->sys_status|=SS_RLOGIN;
5300: new_node->telnet_mode|=TELNET_MODE_OFF; // RLogin does not use Telnet commands
5301: }
5302: #ifdef USE_CRYPTLIB
5303: if(ssh) {
5304: SOCKET tmp_sock;
5305: SOCKADDR_IN tmp_addr={0};
5306: socklen_t tmp_addr_len;
5307:
5308: /* open a socket and connect to yourself */
5309:
5310: tmp_sock = open_socket(SOCK_STREAM, "passthru");
5311:
5312: if(tmp_sock == INVALID_SOCKET) {
5313: lprintf(LOG_ERR,"!ERROR %d creating passthru listen socket", ERROR_VALUE);
5314: goto NO_PASSTHRU;
5315: }
5316:
1.1.1.2 ! root 5317: lprintf(LOG_DEBUG,"passthru listen socket %d opened",tmp_sock);
1.1 root 5318:
5319: /*****************************/
5320: /* Listen for incoming calls */
5321: /*****************************/
5322: memset(&tmp_addr, 0, sizeof(tmp_addr));
5323:
1.1.1.2 ! root 5324: tmp_addr.sin_addr.s_addr = htonl(IPv4_LOCALHOST);
1.1 root 5325: tmp_addr.sin_family = AF_INET;
5326: tmp_addr.sin_port = 0;
5327:
5328: result = bind(tmp_sock,(struct sockaddr *)&tmp_addr,sizeof(tmp_addr));
5329: if(result != 0) {
5330: lprintf(LOG_NOTICE,"%s",BIND_FAILURE_HELP);
5331: close_socket(tmp_sock);
5332: goto NO_PASSTHRU;
5333: }
5334:
5335: result = listen(tmp_sock, 1);
5336:
5337: if(result != 0) {
5338: lprintf(LOG_ERR,"!ERROR %d (%d) listening on passthru socket", result, ERROR_VALUE);
5339: close_socket(tmp_sock);
5340: goto NO_PASSTHRU;
5341: }
1.1.1.2 ! root 5342: lprintf(LOG_INFO,"Listening passthru socket listening on port %u",htons(tmp_addr.sin_port));
1.1 root 5343:
5344: new_node->passthru_socket = open_socket(SOCK_STREAM, "passthru");
5345:
5346: if(new_node->passthru_socket == INVALID_SOCKET) {
5347: lprintf(LOG_ERR,"!ERROR %d creating passthru connecting socket", ERROR_VALUE);
5348: close_socket(tmp_sock);
5349: goto NO_PASSTHRU;
5350: }
5351:
1.1.1.2 ! root 5352: lprintf(LOG_DEBUG,"passthru connect socket %d opened",new_node->passthru_socket);
1.1 root 5353:
5354: tmp_addr_len=sizeof(tmp_addr);
5355: if(getsockname(tmp_sock, (struct sockaddr *)&tmp_addr, &tmp_addr_len)) {
5356: lprintf(LOG_ERR,"!ERROR %d getting passthru listener address", ERROR_VALUE);
5357: close_socket(tmp_sock);
5358: close_socket(new_node->passthru_socket);
5359: new_node->passthru_socket=INVALID_SOCKET;
5360: goto NO_PASSTHRU;
5361: }
5362:
5363: result = connect(new_node->passthru_socket, (struct sockaddr *)&tmp_addr, tmp_addr_len);
5364:
5365: if(result != 0) {
5366: lprintf(LOG_ERR,"!ERROR %d (%d) connecting to passthru socket", result, ERROR_VALUE);
5367: close_socket(new_node->passthru_socket);
5368: new_node->passthru_socket=INVALID_SOCKET;
5369: close_socket(tmp_sock);
5370: goto NO_PASSTHRU;
5371: }
5372:
5373: new_node->client_socket_dup=accept(tmp_sock, (struct sockaddr *)&tmp_addr, &tmp_addr_len);
5374:
5375: if(new_node->client_socket_dup == INVALID_SOCKET) {
5376: lprintf(LOG_ERR,"!ERROR (%d) connecting accept()ing on passthru socket", ERROR_VALUE);
5377: lprintf(LOG_WARNING,"!WARNING native doors which use sockets will not function");
5378: close_socket(new_node->passthru_socket);
5379: new_node->passthru_socket=INVALID_SOCKET;
5380: close_socket(tmp_sock);
5381: goto NO_PASSTHRU;
5382: }
5383: close_socket(tmp_sock);
5384: _beginthread(passthru_output_thread, 0, new_node);
5385: _beginthread(passthru_input_thread, 0, new_node);
5386:
5387: NO_PASSTHRU:
1.1.1.2 ! root 5388: SAFECOPY(new_node->connection,"SSH");
! 5389: new_node->node_connection=NODE_CONNECTION_SSH;
1.1 root 5390: new_node->sys_status|=SS_SSH;
5391: new_node->telnet_mode|=TELNET_MODE_OFF; // SSH does not use Telnet commands
5392: new_node->ssh_session=sbbs->ssh_session;
5393: /* Wait for pending data to be sent then turn off ssh_mode for uber-output */
5394: while(RingBufFull(&sbbs->outbuf))
5395: SLEEP(1);
1.1.1.2 ! root 5396: cryptPopData(sbbs->ssh_session, str, sizeof(str), &i);
1.1 root 5397: sbbs->ssh_mode=false;
5398: }
5399: #endif
5400:
1.1.1.2 ! root 5401: protected_uint32_adjust(&node_threads_running, 1);
1.1 root 5402: new_node->input_thread=(HANDLE)_beginthread(input_thread,0, new_node);
5403: _beginthread(output_thread, 0, new_node);
5404: _beginthread(node_thread, 0, new_node);
5405: served++;
5406: }
5407:
5408: // Close all open sockets
5409: for(i=0;i<MAX_NODES;i++) {
5410: if(node_socket[i]!=INVALID_SOCKET) {
5411: lprintf(LOG_INFO,"Closing node %d socket %d", i+1, node_socket[i]);
5412: close_socket(node_socket[i]);
5413: node_socket[i]=INVALID_SOCKET;
5414: }
5415: #ifdef __unix__
5416: if(uspy_listen_socket[i]!=INVALID_SOCKET) {
5417: close_socket(uspy_listen_socket[i]);
5418: uspy_listen_socket[i]=INVALID_SOCKET;
5419: snprintf(str,sizeof(uspy_addr.sun_path),"%slocalspy%d.sock", startup->temp_dir, i+1);
5420: if(fexist(str))
5421: unlink(str);
5422: }
5423: if(uspy_socket[i]!=INVALID_SOCKET) {
5424: close_socket(uspy_socket[i]);
5425: uspy_socket[i]=INVALID_SOCKET;
5426: }
5427: #endif
5428: }
5429:
5430: sbbs->client_socket=INVALID_SOCKET;
5431: if(events!=NULL)
5432: events->terminated=true;
5433: // Wake-up BBS output thread so it can terminate
5434: sem_post(&sbbs->outbuf.sem);
5435:
5436: // Wait for all node threads to terminate
1.1.1.2 ! root 5437: if(node_threads_running.value) {
! 5438: lprintf(LOG_INFO,"Waiting for %d node threads to terminate...", node_threads_running.value);
1.1 root 5439: start=time(NULL);
1.1.1.2 ! root 5440: while(node_threads_running.value) {
1.1 root 5441: if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
5442: lprintf(LOG_ERR,"!TIMEOUT waiting for %d node thread(s) to "
1.1.1.2 ! root 5443: "terminate", node_threads_running.value);
1.1 root 5444: break;
5445: }
5446: mswait(100);
5447: }
5448: }
5449:
5450: // Wait for Events thread to terminate
5451: if(events!=NULL && events->event_thread_running) {
1.1.1.2 ! root 5452: lprintf(LOG_INFO,"Waiting for events thread to terminate...");
1.1 root 5453: start=time(NULL);
5454: while(events->event_thread_running) {
1.1.1.2 ! root 5455: #if 0 /* the events thread can/will segfault if it continues to run and dereference sbbs->cfg */
1.1 root 5456: if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
1.1.1.2 ! root 5457: lprintf(LOG_ERR,"!TIMEOUT waiting for BBS events thread to "
1.1 root 5458: "terminate");
5459: break;
5460: }
5461: #endif
5462: mswait(100);
5463: }
5464: }
5465:
5466: // Wait for BBS output thread to terminate
5467: if(sbbs->output_thread_running) {
5468: lprintf(LOG_INFO,"Waiting for system output thread to terminate...");
5469: start=time(NULL);
5470: while(sbbs->output_thread_running) {
5471: if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
5472: lprintf(LOG_ERR,"!TIMEOUT waiting for BBS output thread to "
5473: "terminate");
5474: break;
5475: }
5476: mswait(100);
5477: }
5478: }
5479:
5480: // Set all nodes' status to OFFLINE
5481: for(i=first_node;i<=last_node;i++) {
5482: sbbs->getnodedat(i,&node,1);
5483: node.status=NODE_OFFLINE;
5484: sbbs->putnodedat(i,&node);
5485: }
5486:
1.1.1.2 ! root 5487: if(events!=NULL) {
! 5488: if(events->event_thread_running)
! 5489: lprintf(LOG_ERR,"!Events thread still running, can't delete");
! 5490: else
! 5491: delete events;
! 5492: }
1.1 root 5493:
1.1.1.2 ! root 5494: if(sbbs->output_thread_running)
! 5495: lprintf(LOG_ERR,"!Output thread still running, can't delete");
! 5496: else
1.1 root 5497: delete sbbs;
5498:
5499: cleanup(0);
5500:
5501: if(!terminate_server) {
5502: lprintf(LOG_INFO,"Recycling server...");
5503: mswait(2000);
5504: if(startup->recycle!=NULL)
5505: startup->recycle(startup->cbdata);
5506: }
5507:
5508: } while(!terminate_server);
5509:
5510: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.