mux/src/bsd.cpp

Go to the documentation of this file.
00001 
00008 #include "copyright.h"
00009 #include "autoconf.h"
00010 #include "config.h"
00011 #include "externs.h"
00012 
00013 #ifndef WIN32
00014 #include <sys/file.h>
00015 #include <sys/ioctl.h>
00016 #include <sys/stat.h>
00017 #include <sys/wait.h>
00018 #endif // !WIN32
00019 
00020 #include <signal.h>
00021 
00022 #include "attrs.h"
00023 #include "command.h"
00024 #include "file_c.h"
00025 #include "slave.h"
00026 
00027 #ifdef SOLARIS
00028 extern const int _sys_nsig;
00029 #define NSIG _sys_nsig
00030 #endif // SOLARIS
00031 
00032 PortInfo aMainGamePorts[MAX_LISTEN_PORTS];
00033 int      nMainGamePorts = 0;
00034 
00035 unsigned int ndescriptors = 0;
00036 DESC *descriptor_list = NULL;
00037 
00038 static void TelnetSetup(DESC *d);
00039 static void SiteMonSend(int, const char *, DESC *, const char *);
00040 static DESC *initializesock(SOCKET, struct sockaddr_in *);
00041 static DESC *new_connection(PortInfo *Port, int *piError);
00042 static bool process_input(DESC *);
00043 static int make_nonblocking(SOCKET s);
00044 
00045 #ifdef WIN32
00046 static bool bDescriptorListInit = false;
00047 int game_pid;
00048 #else // WIN32
00049 int maxd = 0;
00050 pid_t slave_pid = 0;
00051 int slave_socket = INVALID_SOCKET;
00052 pid_t game_pid;
00053 #ifdef QUERY_SLAVE
00054 pid_t sqlslave_pid = 0;
00055 int sqlslave_socket = INVALID_SOCKET;
00056 #endif // QUERY_SLAVE
00057 #endif // WIN32
00058 
00059 #ifdef WIN32
00060 
00061 // First version of Windows NT TCP/IP routines written by Nick Gammon
00062 // <nick@gammon.com.au>, and were throughly reviewed, re-written and debugged
00063 // by Stephen Dennis <brazilofmux@gmail.com>.
00064 //
00065 HANDLE hGameProcess = INVALID_HANDLE_VALUE;
00066 FCANCELIO *fpCancelIo = NULL;
00067 FGETPROCESSTIMES *fpGetProcessTimes = NULL;
00068 HANDLE CompletionPort;    // IOs are queued up on this port
00069 DWORD platform;   // which version of Windows are we using?
00070 static OVERLAPPED lpo_aborted; // special to indicate a player has finished TCP IOs
00071 static OVERLAPPED lpo_aborted_final; // Finally free the descriptor.
00072 static OVERLAPPED lpo_shutdown; // special to indicate a player should do a shutdown
00073 static OVERLAPPED lpo_welcome; // special to indicate a player has -just- connected.
00074 static OVERLAPPED lpo_wakeup;  // special to indicate that the loop should wakeup and return.
00075 CRITICAL_SECTION csDescriptorList;      // for thread synchronization
00076 static void __cdecl MUDListenThread(void * pVoid);  // the listening thread
00077 static void ProcessWindowsTCP(DWORD dwTimeout);  // handle NT-style IOs
00078 
00079 typedef struct
00080 {
00081     int                port_in;
00082     struct sockaddr_in sa_in;
00083 } SLAVE_REQUEST;
00084 
00085 static HANDLE hSlaveRequestStackSemaphore;
00086 #define SLAVE_REQUEST_STACK_SIZE 50
00087 static SLAVE_REQUEST SlaveRequests[SLAVE_REQUEST_STACK_SIZE];
00088 static int iSlaveRequest = 0;
00089 #define MAX_STRING 514
00090 typedef struct
00091 {
00092     char host[MAX_STRING];
00093     char token[MAX_STRING];
00094     char ident[MAX_STRING];
00095 } SLAVE_RESULT;
00096 
00097 static HANDLE hSlaveResultStackSemaphore;
00098 #define SLAVE_RESULT_STACK_SIZE 50
00099 static SLAVE_RESULT SlaveResults[SLAVE_RESULT_STACK_SIZE];
00100 static volatile int iSlaveResult = 0;
00101 
00102 #define NUM_SLAVE_THREADS 5
00103 typedef struct tagSlaveThreadsInfo
00104 {
00105     DWORD iDoing;
00106     DWORD iError;
00107     DWORD hThreadId;
00108 } SLAVETHREADINFO;
00109 static SLAVETHREADINFO SlaveThreadInfo[NUM_SLAVE_THREADS];
00110 static HANDLE hSlaveThreadsSemaphore;
00111 
00112 static DWORD WINAPI SlaveProc(LPVOID lpParameter)
00113 {
00114     SLAVE_REQUEST req;
00115     unsigned long addr;
00116     struct hostent *hp;
00117     DWORD iSlave = (DWORD)lpParameter;
00118 
00119     if (NUM_SLAVE_THREADS <= iSlave) return 1;
00120 
00121     SlaveThreadInfo[iSlave].iDoing = __LINE__;
00122     for (;;)
00123     {
00124         // Go to sleep until there's something useful to do.
00125         //
00126         SlaveThreadInfo[iSlave].iDoing = __LINE__;
00127         DWORD dwReason = WaitForSingleObject(hSlaveThreadsSemaphore,
00128             30000UL*NUM_SLAVE_THREADS);
00129         switch (dwReason)
00130         {
00131         case WAIT_TIMEOUT:
00132         case WAIT_OBJECT_0:
00133 
00134             // Either the main game thread rang, or 60 seconds has past,
00135             // and it's probably a good idea to check the stack anyway.
00136             //
00137             break;
00138 
00139         default:
00140 
00141             // Either the main game thread has terminated, in which case
00142             // we want to, too, or the function itself has failed, in which
00143             // case: calling it again won't do much good.
00144             //
00145             SlaveThreadInfo[iSlave].iError = __LINE__;
00146             return 1;
00147         }
00148 
00149         SlaveThreadInfo[iSlave].iDoing = __LINE__;
00150         for (;;)
00151         {
00152             // Go take the request off the stack, but not if it takes more
00153             // than 5 seconds to do it. Go back to sleep if we time out. The
00154             // request can wait: either another thread will pick it up, or
00155             // we'll wakeup in 60 seconds anyway.
00156             //
00157             SlaveThreadInfo[iSlave].iDoing = __LINE__;
00158             if (WAIT_OBJECT_0 != WaitForSingleObject(hSlaveRequestStackSemaphore, 5000))
00159             {
00160                 SlaveThreadInfo[iSlave].iError = __LINE__;
00161                 break;
00162             }
00163 
00164             SlaveThreadInfo[iSlave].iDoing = __LINE__;
00165 
00166             // We have control of the stack.
00167             //
00168             if (iSlaveRequest <= 0)
00169             {
00170                 // The stack is empty. Release control and go back to sleep.
00171                 //
00172                 SlaveThreadInfo[iSlave].iDoing = __LINE__;
00173                 ReleaseSemaphore(hSlaveRequestStackSemaphore, 1, NULL);
00174                 SlaveThreadInfo[iSlave].iDoing = __LINE__;
00175                 break;
00176             }
00177 
00178             // Remove the request from the stack.
00179             //
00180             iSlaveRequest--;
00181             req = SlaveRequests[iSlaveRequest];
00182 
00183             SlaveThreadInfo[iSlave].iDoing = __LINE__;
00184             ReleaseSemaphore(hSlaveRequestStackSemaphore, 1, NULL);
00185             SlaveThreadInfo[iSlave].iDoing = __LINE__;
00186 
00187             // Ok, we have complete control of this address, now, so let's
00188             // do the host/ident thing.
00189             //
00190 
00191             // Take note of what time it is.
00192             //
00193 #define IDENT_PROTOCOL_TIMEOUT 5*60 // 5 minutes expressed in seconds.
00194             CLinearTimeAbsolute ltaTimeoutOrigin;
00195             ltaTimeoutOrigin.GetUTC();
00196             CLinearTimeDelta ltdTimeout;
00197             ltdTimeout.SetSeconds(IDENT_PROTOCOL_TIMEOUT);
00198             CLinearTimeAbsolute ltaTimeoutForward(ltaTimeoutOrigin, ltdTimeout);
00199             ltdTimeout.SetSeconds(-IDENT_PROTOCOL_TIMEOUT);
00200             CLinearTimeAbsolute ltaTimeoutBackward(ltaTimeoutOrigin, ltdTimeout);
00201 
00202             addr = req.sa_in.sin_addr.S_un.S_addr;
00203             hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
00204 
00205             if (  hp
00206                && strlen(hp->h_name) < MAX_STRING)
00207             {
00208                 SlaveThreadInfo[iSlave].iDoing = __LINE__;
00209 
00210                 char host[MAX_STRING];
00211                 char token[MAX_STRING];
00212                 char szIdent[MAX_STRING];
00213                 struct sockaddr_in sin;
00214                 memset(&sin, 0, sizeof(sin));
00215                 SOCKET s;
00216 
00217                 // We have a host name.
00218                 //
00219                 strcpy(host, inet_ntoa(req.sa_in.sin_addr));
00220                 strcpy(token, hp->h_name);
00221 
00222                 // Setup ident port.
00223                 //
00224                 sin.sin_family = hp->h_addrtype;
00225                 memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
00226                 sin.sin_port = htons(113);
00227 
00228                 szIdent[0] = 0;
00229                 s = socket(hp->h_addrtype, SOCK_STREAM, 0);
00230                 SlaveThreadInfo[iSlave].iDoing = __LINE__;
00231                 if (s != INVALID_SOCKET)
00232                 {
00233                     SlaveThreadInfo[iSlave].iDoing = __LINE__;
00234 
00235                     DebugTotalSockets++;
00236                     if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
00237                     {
00238                         SlaveThreadInfo[iSlave].iDoing = __LINE__;
00239                         shutdown(s, SD_BOTH);
00240                         SlaveThreadInfo[iSlave].iDoing = __LINE__;
00241                         if (closesocket(s) == 0)
00242                         {
00243                             DebugTotalSockets--;
00244                         }
00245                         s = INVALID_SOCKET;
00246                     }
00247                     else
00248                     {
00249                         int TurnOn = 1;
00250                         setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *)&TurnOn, sizeof(TurnOn));
00251 
00252                         SlaveThreadInfo[iSlave].iDoing = __LINE__;
00253                         char szPortPair[128];
00254                         sprintf(szPortPair, "%d, %d\r\n",
00255                             ntohs(req.sa_in.sin_port), req.port_in);
00256                         SlaveThreadInfo[iSlave].iDoing = __LINE__;
00257                         int nPortPair = strlen(szPortPair);
00258 
00259                         CLinearTimeAbsolute ltaCurrent;
00260                         ltaCurrent.GetUTC();
00261                         if (  ltaTimeoutBackward < ltaCurrent
00262                            &&  ltaCurrent < ltaTimeoutForward
00263                            && send(s, szPortPair, nPortPair, 0) != SOCKET_ERROR)
00264                         {
00265                             SlaveThreadInfo[iSlave].iDoing = __LINE__;
00266                             int nIdent = 0;
00267                             int cc;
00268 
00269                             char szIdentBuffer[MAX_STRING];
00270                             szIdentBuffer[0] = 0;
00271                             bool bAllDone = false;
00272 
00273                             ltaCurrent.GetUTC();
00274                             while (  !bAllDone
00275                                   && nIdent < sizeof(szIdent)-1
00276                                   && ltaTimeoutBackward < ltaCurrent
00277                                   && ltaCurrent < ltaTimeoutForward
00278                                   && (cc = recv(s, szIdentBuffer, sizeof(szIdentBuffer)-1, 0)) != SOCKET_ERROR
00279                                   && cc != 0)
00280                             {
00281                                 SlaveThreadInfo[iSlave].iDoing = __LINE__;
00282 
00283                                 int nIdentBuffer = cc;
00284                                 szIdentBuffer[nIdentBuffer] = 0;
00285 
00286                                 char *p = szIdentBuffer;
00287                                 for (; nIdent < sizeof(szIdent)-1;)
00288                                 {
00289                                     if (  *p == '\0'
00290                                        || *p == '\r'
00291                                        || *p == '\n')
00292                                     {
00293                                         bAllDone = true;
00294                                         break;
00295                                     }
00296                                     if (mux_isprint(*p))
00297                                     {
00298                                         szIdent[nIdent++] = *p;
00299                                     }
00300                                     p++;
00301                                 }
00302                                 szIdent[nIdent] = '\0';
00303 
00304                                 ltaCurrent.GetUTC();
00305                             }
00306                         }
00307                         SlaveThreadInfo[iSlave].iDoing = __LINE__;
00308                         shutdown(s, SD_BOTH);
00309                         SlaveThreadInfo[iSlave].iDoing = __LINE__;
00310                         if (closesocket(s) == 0)
00311                         {
00312                             DebugTotalSockets--;
00313                         }
00314                         s = INVALID_SOCKET;
00315                     }
00316                 }
00317 
00318                 SlaveThreadInfo[iSlave].iDoing = __LINE__;
00319                 if (WAIT_OBJECT_0 == WaitForSingleObject(hSlaveResultStackSemaphore, INFINITE))
00320                 {
00321                     SlaveThreadInfo[iSlave].iDoing = __LINE__;
00322                     if (iSlaveResult < SLAVE_RESULT_STACK_SIZE)
00323                     {
00324                         SlaveThreadInfo[iSlave].iDoing = __LINE__;
00325                         strcpy(SlaveResults[iSlaveResult].host, host);
00326                         strcpy(SlaveResults[iSlaveResult].token, token);
00327                         strcpy(SlaveResults[iSlaveResult].ident, szIdent);
00328                         iSlaveResult++;
00329                     }
00330                     else
00331                     {
00332                         // The result stack is full, so we just toss
00333                         // the info and act like it never happened.
00334                         //
00335                         SlaveThreadInfo[iSlave].iError = __LINE__;
00336                     }
00337                     SlaveThreadInfo[iSlave].iDoing = __LINE__;
00338                     ReleaseSemaphore(hSlaveResultStackSemaphore, 1, NULL);
00339                     SlaveThreadInfo[iSlave].iDoing = __LINE__;
00340                 }
00341                 else
00342                 {
00343                     // The main game thread terminated or the function itself failed,
00344                     // There isn't much left to do except terminate ourselves.
00345                     //
00346                     SlaveThreadInfo[iSlave].iError = __LINE__;
00347                     return 1;
00348                 }
00349             }
00350         }
00351     }
00352     //SlaveThreadInfo[iSlave].iDoing = __LINE__;
00353     //return 1;
00354 }
00355 
00356 static bool bSlaveBooted = false;
00357 void boot_slave(dbref executor, dbref caller, dbref enactor, int)
00358 {
00359     UNUSED_PARAMETER(executor);
00360     UNUSED_PARAMETER(caller);
00361     UNUSED_PARAMETER(enactor);
00362 
00363     int iSlave;
00364 
00365     if (bSlaveBooted) return;
00366 
00367     hSlaveThreadsSemaphore = CreateSemaphore(NULL, 0, NUM_SLAVE_THREADS, NULL);
00368     hSlaveRequestStackSemaphore = CreateSemaphore(NULL, 1, 1, NULL);
00369     hSlaveResultStackSemaphore = CreateSemaphore(NULL, 1, 1, NULL);
00370     DebugTotalSemaphores += 3;
00371     for (iSlave = 0; iSlave < NUM_SLAVE_THREADS; iSlave++)
00372     {
00373         SlaveThreadInfo[iSlave].iDoing = 0;
00374         SlaveThreadInfo[iSlave].iError = 0;
00375         CreateThread(NULL, 0, SlaveProc, (LPVOID)iSlave, 0, &SlaveThreadInfo[iSlave].hThreadId);
00376         DebugTotalThreads++;
00377     }
00378     bSlaveBooted = true;
00379 }
00380 
00381 
00382 static int get_slave_result(void)
00383 {
00384     char host[MAX_STRING];
00385     char token[MAX_STRING];
00386     char ident[MAX_STRING];
00387     char os[MAX_STRING];
00388     char userid[MAX_STRING];
00389     DESC *d;
00390     int local_port, remote_port;
00391 
00392     // Go take the result off the stack, but not if it takes more
00393     // than 5 seconds to do it. Skip it if we time out.
00394     //
00395     if (WAIT_OBJECT_0 != WaitForSingleObject(hSlaveResultStackSemaphore, 5000))
00396     {
00397         return 1;
00398     }
00399 
00400     // We have control of the stack. Go back to sleep if the stack is empty.
00401     //
00402     if (iSlaveResult <= 0)
00403     {
00404         ReleaseSemaphore(hSlaveResultStackSemaphore, 1, NULL);
00405         return 1;
00406     }
00407     iSlaveResult--;
00408     strcpy(host, SlaveResults[iSlaveResult].host);
00409     strcpy(token, SlaveResults[iSlaveResult].token);
00410     strcpy(ident, SlaveResults[iSlaveResult].ident);
00411     ReleaseSemaphore(hSlaveResultStackSemaphore, 1, NULL);
00412 
00413     // At this point, we have a host name on our own stack.
00414     //
00415     if (!mudconf.use_hostname)
00416     {
00417         return 1;
00418     }
00419     for (d = descriptor_list; d; d = d->next)
00420     {
00421         if (strcmp(d->addr, host))
00422         {
00423             continue;
00424         }
00425 
00426         strncpy(d->addr, token, 50);
00427         d->addr[50] = '\0';
00428         if (d->player != 0)
00429         {
00430             if (d->username[0])
00431             {
00432                 atr_add_raw(d->player, A_LASTSITE, tprintf("%s@%s", d->username, d->addr));
00433             }
00434             else
00435             {
00436                 atr_add_raw(d->player, A_LASTSITE, d->addr);
00437             }
00438             atr_add_raw(d->player, A_LASTIP, inet_ntoa((d->address).sin_addr));
00439         }
00440     }
00441 
00442     if (sscanf(ident, "%d , %d : %s : %s : %s", &remote_port, &local_port, token, os, userid) != 5)
00443     {
00444         return 1;
00445     }
00446     for (d = descriptor_list; d; d = d->next)
00447     {
00448         if (ntohs((d->address).sin_port) != remote_port)
00449         {
00450             continue;
00451         }
00452 
00453         strncpy(d->username, userid, 10);
00454         d->username[10] = '\0';
00455         if (d->player != 0)
00456         {
00457             atr_add_raw(d->player, A_LASTSITE, tprintf("%s@%s", d->username, d->addr));
00458         }
00459     }
00460     return 1;
00461 }
00462 
00463 #else // WIN32
00464 
00465 void CleanUpSlaveSocket(void)
00466 {
00467     if (!IS_INVALID_SOCKET(slave_socket))
00468     {
00469         shutdown(slave_socket, SD_BOTH);
00470         if (close(slave_socket) == 0)
00471         {
00472             DebugTotalSockets--;
00473         }
00474         slave_socket = INVALID_SOCKET;
00475     }
00476 }
00477 
00478 void CleanUpSlaveProcess(void)
00479 {
00480     if (slave_pid > 0)
00481     {
00482         kill(slave_pid, SIGKILL);
00483         waitpid(slave_pid, NULL, 0);
00484     }
00485     slave_pid = 0;
00486 }
00487 
00488 #ifdef QUERY_SLAVE
00489 void CleanUpSQLSlaveSocket(void)
00490 {
00491     if (!IS_INVALID_SOCKET(sqlslave_socket))
00492     {
00493         shutdown(sqlslave_socket, SD_BOTH);
00494         if (close(sqlslave_socket) == 0)
00495         {
00496             DebugTotalSockets--;
00497         }
00498         sqlslave_socket = INVALID_SOCKET;
00499     }
00500 }
00501 
00502 void CleanUpSQLSlaveProcess(void)
00503 {
00504     if (sqlslave_pid > 0)
00505     {
00506         kill(sqlslave_pid, SIGKILL);
00507         waitpid(sqlslave_pid, NULL, 0);
00508     }
00509     sqlslave_pid = 0;
00510 }
00511 
00524 void boot_sqlslave(dbref executor, dbref caller, dbref enactor, int)
00525 {
00526     char *pFailedFunc = 0;
00527     int sv[2];
00528     int i;
00529     int maxfds;
00530 
00531 #ifdef HAVE_GETDTABLESIZE
00532     maxfds = getdtablesize();
00533 #else // HAVE_GETDTABLESIZE
00534     maxfds = sysconf(_SC_OPEN_MAX);
00535 #endif // HAVE_GETDTABLESIZE
00536 
00537     CleanUpSQLSlaveSocket();
00538     CleanUpSQLSlaveProcess();
00539 
00540     if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0)
00541     {
00542         pFailedFunc = "socketpair() error: ";
00543         goto failure;
00544     }
00545 
00546     // Set to nonblocking.
00547     //
00548     if (make_nonblocking(sv[0]) < 0)
00549     {
00550         pFailedFunc = "make_nonblocking() error: ";
00551         close(sv[0]);
00552         close(sv[1]);
00553         goto failure;
00554     }
00555 
00556     sqlslave_pid = fork();
00557     switch (sqlslave_pid)
00558     {
00559     case -1:
00560 
00561         pFailedFunc = "fork() error: ";
00562         close(sv[0]);
00563         close(sv[1]);
00564         goto failure;
00565 
00566     case 0:
00567 
00568         // If we don't clear this alarm, the child will eventually receive a
00569         // SIG_PROF.
00570         //
00571         MuxAlarm.Clear();
00572 
00573         // Child.  The following calls to dup2() assume only the minimal
00574         // dup2() functionality.  That is, the destination descriptor is
00575         // always available for it, and sv[1] is never that descriptor.
00576         // It is likely that the standard defined behavior of dup2()
00577         // would handle the job by itself more directly, but a little
00578         // extra code is low-cost insurance.
00579         //
00580         close(sv[0]);
00581         if (sv[1] != 0)
00582         {
00583             close(0);
00584             if (dup2(sv[1], 0) == -1)
00585             {
00586                 _exit(1);
00587             }
00588         }
00589         if (sv[1] != 1)
00590         {
00591             close(1);
00592             if (dup2(sv[1], 1) == -1)
00593             {
00594                 _exit(1);
00595             }
00596         }
00597         for (i = 3; i < maxfds; i++)
00598         {
00599             close(i);
00600         }
00601         execlp("bin/sqlslave", "sqlslave", NULL);
00602         _exit(1);
00603     }
00604     close(sv[1]);
00605 
00606     sqlslave_socket = sv[0];
00607     DebugTotalSockets++;
00608     if (make_nonblocking(sqlslave_socket) < 0)
00609     {
00610         pFailedFunc = "make_nonblocking() error: ";
00611         CleanUpSQLSlaveSocket();
00612         goto failure;
00613     }
00614     if (maxd <= sqlslave_socket)
00615     {
00616         maxd = sqlslave_socket + 1;
00617     }
00618 
00619     STARTLOG(LOG_ALWAYS, "NET", "QUERY");
00620     log_text("SQL slave started on fd ");
00621     log_number(sqlslave_socket);
00622     ENDLOG;
00623 
00624     write(sqlslave_socket, "PING", 4);
00625     return;
00626 
00627 failure:
00628 
00629     CleanUpSQLSlaveProcess();
00630     STARTLOG(LOG_ALWAYS, "NET", "SQL");
00631     log_text(pFailedFunc);
00632     log_number(errno);
00633     ENDLOG;
00634 }
00635 #endif // QUERY_SLAVE
00636 
00649 void boot_slave(dbref executor, dbref caller, dbref enactor, int)
00650 {
00651     char *pFailedFunc = 0;
00652     int sv[2];
00653     int i;
00654     int maxfds;
00655 
00656 #ifdef HAVE_GETDTABLESIZE
00657     maxfds = getdtablesize();
00658 #else // HAVE_GETDTABLESIZE
00659     maxfds = sysconf(_SC_OPEN_MAX);
00660 #endif // HAVE_GETDTABLESIZE
00661 
00662     CleanUpSlaveSocket();
00663     CleanUpSlaveProcess();
00664 
00665     if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0)
00666     {
00667         pFailedFunc = "socketpair() error: ";
00668         goto failure;
00669     }
00670 
00671     // Set to nonblocking.
00672     //
00673     if (make_nonblocking(sv[0]) < 0)
00674     {
00675         pFailedFunc = "make_nonblocking() error: ";
00676         close(sv[0]);
00677         close(sv[1]);
00678         goto failure;
00679     }
00680     slave_pid = fork();
00681     switch (slave_pid)
00682     {
00683     case -1:
00684 
00685         pFailedFunc = "fork() error: ";
00686         close(sv[0]);
00687         close(sv[1]);
00688         goto failure;
00689 
00690     case 0:
00691 
00692         // If we don't clear this alarm, the child will eventually receive a
00693         // SIG_PROF.
00694         //
00695         MuxAlarm.Clear();
00696 
00697         // Child.  The following calls to dup2() assume only the minimal
00698         // dup2() functionality.  That is, the destination descriptor is
00699         // always available for it, and sv[1] is never that descriptor.
00700         // It is likely that the standard defined behavior of dup2()
00701         // would handle the job by itself more directly, but a little
00702         // extra code is low-cost insurance.
00703         //
00704         close(sv[0]);
00705         if (sv[1] != 0)
00706         {
00707             close(0);
00708             if (dup2(sv[1], 0) == -1)
00709             {
00710                 _exit(1);
00711             }
00712         }
00713         if (sv[1] != 1)
00714         {
00715             close(1);
00716             if (dup2(sv[1], 1) == -1)
00717             {
00718                 _exit(1);
00719             }
00720         }
00721         for (i = 3; i < maxfds; i++)
00722         {
00723             close(i);
00724         }
00725         execlp("bin/slave", "slave", NULL);
00726         _exit(1);
00727     }
00728     close(sv[1]);
00729 
00730     slave_socket = sv[0];
00731     DebugTotalSockets++;
00732     if (make_nonblocking(slave_socket) < 0)
00733     {
00734         pFailedFunc = "make_nonblocking() error: ";
00735         CleanUpSlaveSocket();
00736         goto failure;
00737     }
00738     if (maxd <= slave_socket)
00739     {
00740         maxd = slave_socket + 1;
00741     }
00742 
00743     STARTLOG(LOG_ALWAYS, "NET", "SLAVE");
00744     log_text("DNS lookup slave started on fd ");
00745     log_number(slave_socket);
00746     ENDLOG;
00747     return;
00748 
00749 failure:
00750 
00751     CleanUpSlaveProcess();
00752     STARTLOG(LOG_ALWAYS, "NET", "SLAVE");
00753     log_text(pFailedFunc);
00754     log_number(errno);
00755     ENDLOG;
00756 }
00757 
00758 #ifdef QUERY_SLAVE
00759 
00767 static int get_sqlslave_result(void)
00768 {
00769     char buf[LBUF_SIZE];
00770 
00771     int len = read(sqlslave_socket, buf, sizeof(buf)-1);
00772     if (len < 0)
00773     {
00774         int iSocketError = SOCKET_LAST_ERROR;
00775         if (  iSocketError == SOCKET_EAGAIN
00776            || iSocketError == SOCKET_EWOULDBLOCK)
00777         {
00778             return -1;
00779         }
00780         CleanUpSQLSlaveSocket();
00781         CleanUpSQLSlaveProcess();
00782 
00783         STARTLOG(LOG_ALWAYS, "NET", "QUERY");
00784         log_text("read() of query slave failed. Query Slave stopped.");
00785         ENDLOG;
00786 
00787         return -1;
00788     }
00789     else if (len == 0)
00790     {
00791         return -1;
00792     }
00793     buf[len] = '\0';
00794 
00795     STARTLOG(LOG_ALWAYS, "NET", "QUERY");
00796     log_text(buf);
00797     ENDLOG;
00798 
00799     return 0;
00800 }
00801 
00802 #endif // QUERY_SLAVE
00803 
00804 // Get a result from the slave
00805 //
00806 static int get_slave_result(void)
00807 {
00808     int local_port, remote_port;
00809     DESC *d;
00810 
00811     char *buf = alloc_lbuf("slave_buf");
00812 
00813     int len = read(slave_socket, buf, LBUF_SIZE-1);
00814     if (len < 0)
00815     {
00816         int iSocketError = SOCKET_LAST_ERROR;
00817         if (  iSocketError == SOCKET_EAGAIN
00818            || iSocketError == SOCKET_EWOULDBLOCK)
00819         {
00820             free_lbuf(buf);
00821             return -1;
00822         }
00823         CleanUpSlaveSocket();
00824         CleanUpSlaveProcess();
00825         free_lbuf(buf);
00826 
00827         STARTLOG(LOG_ALWAYS, "NET", "SLAVE");
00828         log_text("read() of slave result failed. Slave stopped.");
00829         ENDLOG;
00830 
00831         return -1;
00832     }
00833     else if (len == 0)
00834     {
00835         free_lbuf(buf);
00836         return -1;
00837     }
00838     buf[len] = '\0';
00839 
00840     char *token = alloc_lbuf("slave_token");
00841     char *os = alloc_lbuf("slave_os");
00842     char *userid = alloc_lbuf("slave_userid");
00843     char *host = alloc_lbuf("slave_host");
00844     char *p;
00845     if (sscanf(buf, "%s %s", host, token) != 2)
00846     {
00847         goto Done;
00848     }
00849     p = strchr(buf, '\n');
00850     *p = '\0';
00851     if (mudconf.use_hostname)
00852     {
00853         for (d = descriptor_list; d; d = d->next)
00854         {
00855             if (strcmp(d->addr, host) != 0)
00856             {
00857                 continue;
00858             }
00859 
00860             strncpy(d->addr, token, 50);
00861             d->addr[50] = '\0';
00862             if (d->player != 0)
00863             {
00864                 if (d->username[0])
00865                 {
00866                     atr_add_raw(d->player, A_LASTSITE, tprintf("%s@%s",
00867                         d->username, d->addr));
00868                 }
00869                 else
00870                 {
00871                     atr_add_raw(d->player, A_LASTSITE, d->addr);
00872                 }
00873                 atr_add_raw(d->player, A_LASTIP, inet_ntoa((d->address).sin_addr));
00874             }
00875         }
00876     }
00877 
00878     if (sscanf(p + 1, "%s %d , %d : %s : %s : %s",
00879            host,
00880            &remote_port, &local_port,
00881            token, os, userid) != 6)
00882     {
00883         goto Done;
00884     }
00885     for (d = descriptor_list; d; d = d->next)
00886     {
00887         if (ntohs((d->address).sin_port) != remote_port)
00888             continue;
00889         strncpy(d->username, userid, 10);
00890         d->username[10] = '\0';
00891         if (d->player != 0)
00892         {
00893             atr_add_raw(d->player, A_LASTSITE, tprintf("%s@%s",
00894                              d->username, d->addr));
00895         }
00896     }
00897 Done:
00898     free_lbuf(buf);
00899     free_lbuf(token);
00900     free_lbuf(os);
00901     free_lbuf(userid);
00902     free_lbuf(host);
00903     return 0;
00904 }
00905 #endif // WIN32
00906 
00907 static void make_socket(PortInfo *Port)
00908 {
00909     SOCKET s;
00910     struct sockaddr_in server;
00911     int opt = 1;
00912 
00913 #ifdef WIN32
00914 
00915     // If we are running Windows NT we must create a completion port,
00916     // and start up a listening thread for new connections
00917     //
00918     if (platform == VER_PLATFORM_WIN32_NT)
00919     {
00920         int nRet;
00921 
00922         // create initial IO completion port, so threads have something to wait on
00923         //
00924         CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
00925 
00926         if (!CompletionPort)
00927         {
00928             Log.tinyprintf("Error %ld on CreateIoCompletionPort" ENDLINE,  GetLastError());
00929             WSACleanup();     // clean up
00930             exit(1);
00931         }
00932 
00933         // Initialize the critical section
00934         //
00935         if (!bDescriptorListInit)
00936         {
00937             InitializeCriticalSection(&csDescriptorList);
00938             bDescriptorListInit = true;
00939         }
00940 
00941         // Create a TCP/IP stream socket
00942         //
00943         s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
00944         if (s == INVALID_SOCKET)
00945         {
00946             log_perror("NET", "FAIL", NULL, "creating master socket");
00947             exit(3);
00948         }
00949         DebugTotalSockets++;
00950         if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0)
00951         {
00952             log_perror("NET", "FAIL", NULL, "setsockopt");
00953         }
00954 
00955         // Fill in the the address structure
00956         //
00957         server.sin_port = htons((unsigned short)(Port->port));
00958         server.sin_family = AF_INET;
00959         server.sin_addr.s_addr = INADDR_ANY;
00960 
00961         // bind our name to the socket
00962         //
00963         nRet = bind(s, (LPSOCKADDR) &server, sizeof server);
00964 
00965         if (nRet == SOCKET_ERROR)
00966         {
00967             Log.tinyprintf("Error %ld on Win32: bind" ENDLINE, SOCKET_LAST_ERROR);
00968             if (closesocket(s) == 0)
00969             {
00970                 DebugTotalSockets--;
00971             }
00972             s = INVALID_SOCKET;
00973             WSACleanup();     // clean up
00974             exit(1);
00975         }
00976 
00977         // Set the socket to listen
00978         //
00979         nRet = listen(s, SOMAXCONN);
00980 
00981         if (nRet)
00982         {
00983             Log.tinyprintf("Error %ld on Win32: listen" ENDLINE, SOCKET_LAST_ERROR);
00984             WSACleanup();
00985             exit(1);
00986         }
00987 
00988         // Create the MUD listening thread
00989         //
00990         if (_beginthread(MUDListenThread, 0, (void *) Port) == (unsigned)(-1))
00991         {
00992             log_perror("NET", "FAIL", "_beginthread", "setsockopt");
00993             WSACleanup();
00994             exit(1);
00995         }
00996 
00997         Port->socket = s;
00998         Log.tinyprintf("Listening (NT-style) on port %d" ENDLINE, Port->port);
00999         return;
01000     }
01001 #endif // WIN32
01002 
01003     s = socket(AF_INET, SOCK_STREAM, 0);
01004     if (IS_INVALID_SOCKET(s))
01005     {
01006         log_perror("NET", "FAIL", NULL, "creating master socket");
01007 #ifdef WIN32
01008         WSACleanup();
01009 #endif // WIN32
01010         exit(3);
01011     }
01012     DebugTotalSockets++;
01013     if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0)
01014     {
01015         log_perror("NET", "FAIL", NULL, "setsockopt");
01016     }
01017     server.sin_family = AF_INET;
01018     server.sin_addr.s_addr = INADDR_ANY;
01019     server.sin_port = htons((unsigned short)(Port->port));
01020     int cc  = bind(s, (struct sockaddr *)&server, sizeof(server));
01021     if (IS_SOCKET_ERROR(cc))
01022     {
01023         log_perror("NET", "FAIL", NULL, "bind");
01024         if (SOCKET_CLOSE(s) == 0)
01025         {
01026             DebugTotalSockets--;
01027         }
01028         s = INVALID_SOCKET;
01029 #ifdef WIN32
01030         WSACleanup();
01031 #endif // WIN32
01032         exit(4);
01033     }
01034     listen(s, SOMAXCONN);
01035     Port->socket = s;
01036     Log.tinyprintf("Listening on port %d" ENDLINE, Port->port);
01037 }
01038 
01039 void SetupPorts(int *pnPorts, PortInfo aPorts[], IntArray *pia)
01040 {
01041     // Any existing open port which does not appear in the requested set
01042     // should be closed.
01043     //
01044     int i, j, k;
01045     bool bFound;
01046     for (i = 0; i < *pnPorts; i++)
01047     {
01048         bFound = false;
01049         for (j = 0; j < pia->n; j++)
01050         {
01051             if (aPorts[i].port == pia->pi[j])
01052             {
01053                 bFound = true;
01054                 break;
01055             }
01056         }
01057         if (!bFound)
01058         {
01059             if (SOCKET_CLOSE(aPorts[i].socket) == 0)
01060             {
01061                 DebugTotalSockets--;
01062                 (*pnPorts)--;
01063                 k = *pnPorts;
01064                 if (i != k)
01065                 {
01066                     aPorts[i] = aPorts[k];
01067                 }
01068                 aPorts[k].port = 0;
01069                 aPorts[k].socket = INVALID_SOCKET;
01070             }
01071         }
01072     }
01073 
01074     // Any requested port which does not appear in the existing open set
01075     // of ports should be opened.
01076     //
01077     for (j = 0; j < pia->n; j++)
01078     {
01079         bFound = false;
01080         for (i = 0; i < *pnPorts; i++)
01081         {
01082             if (aPorts[i].port == pia->pi[j])
01083             {
01084                 bFound = true;
01085                 break;
01086             }
01087         }
01088         if (!bFound)
01089         {
01090             k = *pnPorts;
01091             (*pnPorts)++;
01092             aPorts[k].port = pia->pi[j];
01093             make_socket(aPorts+k);
01094         }
01095     }
01096 
01097 #ifndef WIN32
01098     for (i = 0; i < *pnPorts; i++)
01099     {
01100         if (maxd <= aPorts[i].socket)
01101         {
01102             maxd = aPorts[i].socket + 1;
01103         }
01104     }
01105 #endif
01106 }
01107 
01108 #ifdef WIN32
01109 // Private version of FD_ISSET:
01110 //
01111 // The following routine is only used on Win9x. Ordinarily, FD_ISSET
01112 // maps to a __WSAFDIsSet call, however, the Intel compiler encounters
01113 // an internal error at link time when some of the higher-order
01114 // optimizations are requested (-Qipo). Including this function is a
01115 // workaround.
01116 //
01117 DCL_INLINE bool FD_ISSET_priv(SOCKET fd, fd_set *set)
01118 {
01119     unsigned int i;
01120     for (i = 0; i < set->fd_count; i++)
01121     {
01122         if (set->fd_array[i] == fd)
01123         {
01124             return true;
01125         }
01126     }
01127     return false;
01128 }
01129 
01130 void shovechars9x(int nPorts, PortInfo aPorts[])
01131 {
01132     fd_set input_set, output_set;
01133     int found;
01134     DESC *d, *dnext, *newd;
01135 
01136 #define CheckInput(x)   FD_ISSET_priv(x, &input_set)
01137 #define CheckOutput(x)  FD_ISSET_priv(x, &output_set)
01138 
01139     mudstate.debug_cmd = "< shovechars >";
01140 
01141     CLinearTimeAbsolute ltaLastSlice;
01142     ltaLastSlice.GetUTC();
01143 
01144     while (mudstate.shutdown_flag == 0)
01145     {
01146         CLinearTimeAbsolute ltaCurrent;
01147         ltaCurrent.GetUTC();
01148         update_quotas(ltaLastSlice, ltaCurrent);
01149 
01150         // Before processing a possible QUIT command, be sure to give the slave
01151         // a chance to report it's findings.
01152         //
01153         if (iSlaveResult) get_slave_result();
01154 
01155         // Check the scheduler. Run a little ahead into the future so that
01156         // we tend to sleep longer.
01157         //
01158         scheduler.RunTasks(ltaCurrent);
01159         CLinearTimeAbsolute ltaWakeUp;
01160         if (!scheduler.WhenNext(&ltaWakeUp))
01161         {
01162             CLinearTimeDelta ltd = time_30m;
01163             ltaWakeUp = ltaCurrent + ltd;
01164         }
01165         else if (ltaWakeUp < ltaCurrent)
01166         {
01167             ltaWakeUp = ltaCurrent;
01168         }
01169 
01170         if (mudstate.shutdown_flag)
01171         {
01172             break;
01173         }
01174 
01175         FD_ZERO(&input_set);
01176         FD_ZERO(&output_set);
01177 
01178         // Listen for new connections.
01179         //
01180         int i;
01181         for (i = 0; i < nPorts; i++)
01182         {
01183             FD_SET(aPorts[i].socket, &input_set);
01184         }
01185 
01186         // Mark sockets that we want to test for change in status.
01187         //
01188         DESC_ITER_ALL(d)
01189         {
01190             if (!d->input_head)
01191                 FD_SET(d->descriptor, &input_set);
01192             if (d->output_head)
01193                 FD_SET(d->descriptor, &output_set);
01194         }
01195 
01196         // Wait for something to happen
01197         //
01198         struct timeval timeout;
01199         CLinearTimeDelta ltdTimeout = ltaWakeUp - ltaCurrent;
01200         ltdTimeout.ReturnTimeValueStruct(&timeout);
01201         found = select(0, &input_set, &output_set, (fd_set *) NULL, &timeout);
01202 
01203         switch (found)
01204         {
01205         case SOCKET_ERROR:
01206             {
01207                 STARTLOG(LOG_NET, "NET", "CONN");
01208                 log_text("shovechars: Socket error.");
01209                 ENDLOG;
01210             }
01211 
01212         case 0:
01213             continue;
01214         }
01215 
01216         // Check for new connection requests.
01217         //
01218         for (i = 0; i < nPorts; i++)
01219         {
01220             if (CheckInput(aPorts[i].socket))
01221             {
01222                 int iSocketError;
01223                 newd = new_connection(aPorts+i, &iSocketError);
01224                 if (!newd)
01225                 {
01226                     if (  iSocketError
01227                        && iSocketError != SOCKET_EINTR)
01228                     {
01229                         log_perror("NET", "FAIL", NULL, "new_connection");
01230                     }
01231                 }
01232             }
01233         }
01234 
01235         // Check for activity on user sockets
01236         //
01237         DESC_SAFEITER_ALL(d, dnext)
01238         {
01239             // Process input from sockets with pending input
01240             //
01241             if (CheckInput(d->descriptor))
01242             {
01243                 // Undo autodark
01244                 //
01245                 if (d->flags & DS_AUTODARK)
01246                 {
01247                     // Clear the DS_AUTODARK on every related session.
01248                     //
01249                     DESC *d1;
01250                     DESC_ITER_PLAYER(d->player, d1)
01251                     {
01252                         d1->flags &= ~DS_AUTODARK;
01253                     }
01254                     db[d->player].fs.word[FLAG_WORD1] &= ~DARK;
01255                 }
01256 
01257                 // Process received data
01258                 //
01259                 if (!process_input(d))
01260                 {
01261                     shutdownsock(d, R_SOCKDIED);
01262                     continue;
01263                 }
01264             }
01265 
01266             // Process output for sockets with pending output
01267             //
01268             if (CheckOutput(d->descriptor))
01269             {
01270                 process_output9x(d, true);
01271             }
01272         }
01273     }
01274 }
01275 
01276 static LRESULT WINAPI mux_WindowProc
01277 (
01278     HWND   hWin,
01279     UINT   msg,
01280     WPARAM wParam,
01281     LPARAM lParam
01282 )
01283 {
01284     switch (msg)
01285     {
01286     case WM_CLOSE:
01287         mudstate.shutdown_flag = true;
01288         PostQueuedCompletionStatus(CompletionPort, 0, 0, &lpo_wakeup);
01289         return 0;
01290 
01291     case WM_DESTROY:
01292         PostQuitMessage(0);
01293         return 0;
01294     }
01295 
01296     return DefWindowProc(hWin, msg, wParam, lParam);
01297 }
01298 
01299 const char szApp[] = "MUX2";
01300 
01301 static DWORD WINAPI ListenForCloseProc(LPVOID lpParameter)
01302 {
01303     UNUSED_PARAMETER(lpParameter);
01304 
01305     WNDCLASS wc;
01306 
01307     wc.style         = CS_HREDRAW | CS_VREDRAW;
01308     wc.lpfnWndProc   = mux_WindowProc;
01309     wc.cbClsExtra    = 0;
01310     wc.cbWndExtra    = 0;
01311     wc.hInstance     = 0;
01312     wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
01313     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
01314     wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
01315     wc.lpszMenuName  = NULL;
01316     wc.lpszClassName = szApp;
01317 
01318     RegisterClass(&wc);
01319 
01320     HWND hWnd = CreateWindow(szApp, szApp, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
01321         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, 0, NULL);
01322 
01323     ShowWindow(hWnd, SW_HIDE);
01324     UpdateWindow(hWnd);
01325 
01326     MSG msg;
01327     while (GetMessage(&msg, NULL, 0, 0))
01328     {
01329         DispatchMessage(&msg);
01330     }
01331     mudstate.shutdown_flag = true;
01332     PostQueuedCompletionStatus(CompletionPort, 0, 0, &lpo_wakeup);
01333     return 1;
01334 }
01335 
01336 void shovecharsNT(int nPorts, PortInfo aPorts[])
01337 {
01338     UNUSED_PARAMETER(nPorts);
01339     UNUSED_PARAMETER(aPorts);
01340 
01341     mudstate.debug_cmd = "< shovechars >";
01342 
01343     CreateThread(NULL, 0, ListenForCloseProc, NULL, 0, NULL);
01344 
01345     CLinearTimeAbsolute ltaLastSlice;
01346     ltaLastSlice.GetUTC();
01347 
01348     for (;;)
01349     {
01350         CLinearTimeAbsolute ltaCurrent;
01351         ltaCurrent.GetUTC();
01352         update_quotas(ltaLastSlice, ltaCurrent);
01353 
01354         // Before processing a possible QUIT command, be sure to give the slave
01355         // a chance to report it's findings.
01356         //
01357         if (iSlaveResult) get_slave_result();
01358 
01359         // Check the scheduler. Run a little ahead into the future so that
01360         // we tend to sleep longer.
01361         //
01362         scheduler.RunTasks(ltaCurrent);
01363         CLinearTimeAbsolute ltaWakeUp;
01364         if (!scheduler.WhenNext(&ltaWakeUp))
01365         {
01366             CLinearTimeDelta ltd = time_30m;
01367             ltaWakeUp = ltaCurrent + ltd;
01368         }
01369         else if (ltaWakeUp < ltaCurrent)
01370         {
01371             ltaWakeUp = ltaCurrent;
01372         }
01373 
01374         // The following gets Asyncronous writes to the sockets going
01375         // if they are not already going. Doing it this way is better
01376         // than:
01377         //
01378         //   1) starting an asyncronous write after a single addition
01379         //      to the socket's output queue,
01380         //
01381         //   2) scheduling a task to do it (because we would need to
01382         //      either maintain the task's uniqueness in the
01383         //      scheduler's queue, or endure many redudant calls to
01384         //      process_output for the same descriptor.
01385         //
01386         DESC *d, *dnext;
01387         DESC_SAFEITER_ALL(d, dnext)
01388         {
01389             if (d->bCallProcessOutputLater)
01390             {
01391                 d->bCallProcessOutputLater = false;
01392                 process_outputNT(d, false);
01393             }
01394         }
01395 
01396         if (mudstate.shutdown_flag)
01397             break;
01398 
01399         CLinearTimeDelta ltdTimeOut = ltaWakeUp - ltaCurrent;
01400         unsigned int iTimeout = ltdTimeOut.ReturnMilliseconds();
01401         ProcessWindowsTCP(iTimeout);
01402     }
01403 }
01404 
01405 #else // WIN32
01406 
01407 bool ValidSocket(SOCKET s)
01408 {
01409     struct stat fstatbuf;
01410     if (fstat(s, &fstatbuf) < 0)
01411     {
01412         return false;
01413     }
01414     return true;
01415 }
01416 
01417 void shovechars(int nPorts, PortInfo aPorts[])
01418 {
01419     fd_set input_set, output_set;
01420     int found;
01421     DESC *d, *dnext, *newd;
01422     unsigned int avail_descriptors;
01423     int maxfds;
01424     int i;
01425 
01426 #define CheckInput(x)     FD_ISSET(x, &input_set)
01427 #define CheckOutput(x)    FD_ISSET(x, &output_set)
01428 
01429     mudstate.debug_cmd = "< shovechars >";
01430 
01431     CLinearTimeAbsolute ltaLastSlice;
01432     ltaLastSlice.GetUTC();
01433 
01434 #ifdef HAVE_GETDTABLESIZE
01435     maxfds = getdtablesize();
01436 #else // HAVE_GETDTABLESIZE
01437     maxfds = sysconf(_SC_OPEN_MAX);
01438 #endif // HAVE_GETDTABLESIZE
01439 
01440     avail_descriptors = maxfds - 7;
01441 
01442     while (mudstate.shutdown_flag == 0)
01443     {
01444         CLinearTimeAbsolute ltaCurrent;
01445         ltaCurrent.GetUTC();
01446         update_quotas(ltaLastSlice, ltaCurrent);
01447 
01448         // Check the scheduler.
01449         //
01450         scheduler.RunTasks(ltaCurrent);
01451         CLinearTimeAbsolute ltaWakeUp;
01452         if (scheduler.WhenNext(&ltaWakeUp))
01453         {
01454             if (ltaWakeUp < ltaCurrent)
01455             {
01456                 ltaWakeUp = ltaCurrent;
01457             }
01458         }
01459         else
01460         {
01461             CLinearTimeDelta ltd = time_30m;
01462             ltaWakeUp = ltaCurrent + ltd;
01463         }
01464 
01465         if (mudstate.shutdown_flag)
01466         {
01467             break;
01468         }
01469 
01470         FD_ZERO(&input_set);
01471         FD_ZERO(&output_set);
01472 
01473         // Listen for new connections if there are free descriptors.
01474         //
01475         if (ndescriptors < avail_descriptors)
01476         {
01477             for (i = 0; i < nPorts; i++)
01478             {
01479                 FD_SET(aPorts[i].socket, &input_set);
01480             }
01481         }
01482 
01483         // Listen for replies from the slave socket.
01484         //
01485         if (!IS_INVALID_SOCKET(slave_socket))
01486         {
01487             FD_SET(slave_socket, &input_set);
01488         }
01489 
01490 #ifdef QUERY_SLAVE
01491         // Listen for replies from the sqlslave socket.
01492         //
01493         if (!IS_INVALID_SOCKET(sqlslave_socket))
01494         {
01495             FD_SET(sqlslave_socket, &input_set);
01496         }
01497 #endif // QUERY_SLAVE
01498 
01499         // Mark sockets that we want to test for change in status.
01500         //
01501         DESC_ITER_ALL(d)
01502         {
01503             if (!d->input_head)
01504             {
01505                 FD_SET(d->descriptor, &input_set);
01506             }
01507             if (d->output_head)
01508             {
01509                 FD_SET(d->descriptor, &output_set);
01510             }
01511         }
01512 
01513         // Wait for something to happen.
01514         //
01515         struct timeval timeout;
01516         CLinearTimeDelta ltdTimeout = ltaWakeUp - ltaCurrent;
01517         ltdTimeout.ReturnTimeValueStruct(&timeout);
01518         found = select(maxd, &input_set, &output_set, (fd_set *) NULL,
01519                    &timeout);
01520 
01521         if (IS_SOCKET_ERROR(found))
01522         {
01523             int iSocketError = SOCKET_LAST_ERROR;
01524             if (iSocketError == SOCKET_EBADF)
01525             {
01526                 // This one is bad, as it results in a spiral of
01527                 // doom, unless we can figure out what the bad file
01528                 // descriptor is and get rid of it.
01529                 //
01530                 log_perror("NET", "FAIL", "checking for activity", "select");
01531 
01532                 // Search for a bad socket amoungst the players.
01533                 //
01534                 DESC_ITER_ALL(d)
01535                 {
01536                     if (!ValidSocket(d->descriptor))
01537                     {
01538                         STARTLOG(LOG_PROBLEMS, "ERR", "EBADF");
01539                         log_text("Bad descriptor ");
01540                         log_number(d->descriptor);
01541                         ENDLOG;
01542                         shutdownsock(d, R_SOCKDIED);
01543                     }
01544                 }
01545                 if (  !IS_INVALID_SOCKET(slave_socket)
01546                    && !ValidSocket(slave_socket))
01547                 {
01548                     // Try to restart the slave, since it presumably
01549                     // died.
01550                     //
01551                     STARTLOG(LOG_PROBLEMS, "ERR", "EBADF");
01552                     log_text("Bad slave descriptor ");
01553                     log_number(slave_socket);
01554                     ENDLOG;
01555                     boot_slave(GOD, GOD, GOD, 0);
01556                 }
01557 
01558 #ifdef QUERY_SLAVE
01559                 if (  !IS_INVALID_SOCKET(sqlslave_socket)
01560                    && !ValidSocket(sqlslave_socket))
01561                 {
01562                     CleanUpSQLSlaveSocket();
01563                 }
01564 #endif // QUERY_SLAVE
01565 
01566                 for (i = 0; i < nPorts; i++)
01567                 {
01568                     if (!ValidSocket(aPorts[i].socket))
01569                     {
01570                         // That's it. Game over.
01571                         //
01572                         STARTLOG(LOG_PROBLEMS, "ERR", "EBADF");
01573                         log_text("Bad game port descriptor ");
01574                         log_number(aPorts[i].socket);
01575                         ENDLOG;
01576                         return;
01577                     }
01578                 }
01579             }
01580             else if (iSocketError != SOCKET_EINTR)
01581             {
01582                 log_perror("NET", "FAIL", "checking for activity", "select");
01583             }
01584             continue;
01585         }
01586 
01587         // Get usernames and hostnames.
01588         //
01589         if (  !IS_INVALID_SOCKET(slave_socket)
01590            && CheckInput(slave_socket))
01591         {
01592             while (get_slave_result() == 0)
01593             {
01594                 ; // Nothing.
01595             }
01596         }
01597 
01598 #ifdef QUERY_SLAVE
01599         // Get result sets from sqlslave.
01600         //
01601         if (  !IS_INVALID_SOCKET(sqlslave_socket)
01602            && CheckInput(sqlslave_socket))
01603         {
01604             while (get_sqlslave_result() == 0)
01605             {
01606                 ; // Nothing.
01607             }
01608         }
01609 #endif // QUERY_SLAVE
01610 
01611         // Check for new connection requests.
01612         //
01613         for (i = 0; i < nPorts; i++)
01614         {
01615             if (CheckInput(aPorts[i].socket))
01616             {
01617                 int iSocketError;
01618                 newd = new_connection(aPorts+i, &iSocketError);
01619                 if (!newd)
01620                 {
01621                     if (  iSocketError
01622                        && iSocketError != SOCKET_EINTR)
01623                     {
01624                         log_perror("NET", "FAIL", NULL, "new_connection");
01625                     }
01626                 }
01627                 else if (maxd <= newd->descriptor)
01628                 {
01629                     maxd = newd->descriptor + 1;
01630                 }
01631             }
01632         }
01633 
01634         // Check for activity on user sockets.
01635         //
01636         DESC_SAFEITER_ALL(d, dnext)
01637         {
01638             // Process input from sockets with pending input.
01639             //
01640             if (CheckInput(d->descriptor))
01641             {
01642                 // Undo autodark
01643                 //
01644                 if (d->flags & DS_AUTODARK)
01645                 {
01646                     // Clear the DS_AUTODARK on every related session.
01647                     //
01648                     DESC *d1;
01649                     DESC_ITER_PLAYER(d->player, d1)
01650                     {
01651                         d1->flags &= ~DS_AUTODARK;
01652                     }
01653                     db[d->player].fs.word[FLAG_WORD1] &= ~DARK;
01654                 }
01655 
01656                 // Process received data.
01657                 //
01658                 if (!process_input(d))
01659                 {
01660                     shutdownsock(d, R_SOCKDIED);
01661                     continue;
01662                 }
01663             }
01664 
01665             // Process output for sockets with pending output.
01666             //
01667             if (CheckOutput(d->descriptor))
01668             {
01669                 process_output(d, true);
01670             }
01671         }
01672     }
01673 }
01674 
01675 #endif // WIN32
01676 
01677 DESC *new_connection(PortInfo *Port, int *piSocketError)
01678 {
01679     DESC *d;
01680     struct sockaddr_in addr;
01681 #ifdef SOCKLEN_T_DCL
01682     socklen_t addr_len;
01683 #else // SOCKLEN_T_DCL
01684     int addr_len;
01685 #endif // SOCKLEN_T_DCL
01686 #ifndef WIN32
01687     int len;
01688 #endif // !WIN32
01689 
01690     char *cmdsave = mudstate.debug_cmd;
01691     mudstate.debug_cmd = "< new_connection >";
01692     addr_len = sizeof(struct sockaddr);
01693 
01694     SOCKET newsock = accept(Port->socket, (struct sockaddr *)&addr, &addr_len);
01695 
01696     if (IS_INVALID_SOCKET(newsock))
01697     {
01698         *piSocketError = SOCKET_LAST_ERROR;
01699         return 0;
01700     }
01701 
01702     char *pBuffM2 = alloc_mbuf("new_connection.address");
01703     strcpy(pBuffM2, inet_ntoa(addr.sin_addr));
01704     unsigned short usPort = ntohs(addr.sin_port);
01705 
01706     DebugTotalSockets++;
01707     if (site_check(addr.sin_addr, mudstate.access_list) == H_FORBIDDEN)
01708     {
01709         STARTLOG(LOG_NET | LOG_SECURITY, "NET", "SITE");
01710         char *pBuffM1  = alloc_mbuf("new_connection.LOG.badsite");
01711         sprintf(pBuffM1, "[%u/%s] Connection refused.  (Remote port %d)",
01712             newsock, pBuffM2, usPort);
01713         log_text(pBuffM1);
01714         free_mbuf(pBuffM1);
01715         ENDLOG;
01716 
01717         // Report site monitor information.
01718         //
01719         SiteMonSend(newsock, pBuffM2, NULL, "Connection refused");
01720 
01721         fcache_rawdump(newsock, FC_CONN_SITE);
01722         shutdown(newsock, SD_BOTH);
01723         if (SOCKET_CLOSE(newsock) == 0)
01724         {
01725             DebugTotalSockets--;
01726         }
01727         newsock = INVALID_SOCKET;
01728         errno = 0;
01729         d = NULL;
01730     }
01731     else
01732     {
01733 #ifdef WIN32
01734         // Make slave request
01735         //
01736         // Go take control of the stack, but don't bother if it takes
01737         // longer than 5 seconds to do it.
01738         //
01739         if (  bSlaveBooted
01740            && WAIT_OBJECT_0 == WaitForSingleObject(hSlaveRequestStackSemaphore, 5000))
01741         {
01742             // We have control of the stack. Skip the request if the stack is full.
01743             //
01744             if (iSlaveRequest < SLAVE_REQUEST_STACK_SIZE)
01745             {
01746                 // There is room on the stack, so make the request.
01747                 //
01748                 SlaveRequests[iSlaveRequest].sa_in = addr;
01749                 SlaveRequests[iSlaveRequest].port_in = Port->port;
01750                 iSlaveRequest++;
01751                 ReleaseSemaphore(hSlaveRequestStackSemaphore, 1, NULL);
01752 
01753                 // Wake up a single slave thread. Event automatically resets itself.
01754                 //
01755                 ReleaseSemaphore(hSlaveThreadsSemaphore, 1, NULL);
01756             }
01757             else
01758             {
01759                 // No room on the stack, so skip it.
01760                 //
01761                 ReleaseSemaphore(hSlaveRequestStackSemaphore, 1, NULL);
01762             }
01763         }
01764 #else // WIN32
01765         // Make slave request
01766         //
01767         if (  !IS_INVALID_SOCKET(slave_socket)
01768            && mudconf.use_hostname)
01769         {
01770             char *pBuffL1 = alloc_lbuf("new_connection.write");
01771             sprintf(pBuffL1, "%s\n%s,%d,%d\n", pBuffM2, pBuffM2, usPort,
01772                 Port->port);
01773             len = strlen(pBuffL1);
01774             if (write(slave_socket, pBuffL1, len) < 0)
01775             {
01776                 CleanUpSlaveSocket();
01777                 CleanUpSlaveProcess();
01778 
01779                 STARTLOG(LOG_ALWAYS, "NET", "SLAVE");
01780                 log_text("write() of slave request failed. Slave stopped.");
01781                 ENDLOG;
01782             }
01783             free_lbuf(pBuffL1);
01784         }
01785 #endif // WIN32
01786 
01787         STARTLOG(LOG_NET, "NET", "CONN");
01788         char *pBuffM3 = alloc_mbuf("new_connection.LOG.open");
01789         sprintf(pBuffM3, "[%u/%s] Connection opened (remote port %d)", newsock,
01790             pBuffM2, usPort);
01791         log_text(pBuffM3);
01792         free_mbuf(pBuffM3);
01793         ENDLOG;
01794 
01795         d = initializesock(newsock, &addr);
01796         TelnetSetup(d);
01797 
01798         // Initalize everything before sending the sitemon info, so that we
01799         // can pass the descriptor, d.
01800         //
01801         SiteMonSend(newsock, pBuffM2, d, "Connection");
01802 
01803         welcome_user(d);
01804         mudstate.debug_cmd = cmdsave;
01805     }
01806     free_mbuf(pBuffM2);
01807     mudstate.debug_cmd = cmdsave;
01808     *piSocketError = SOCKET_LAST_ERROR;
01809     return d;
01810 }
01811 
01812 // Disconnect reasons that get written to the logfile
01813 //
01814 static const char *disc_reasons[] =
01815 {
01816     "Unspecified",
01817     "Quit",
01818     "Inactivity Timeout",
01819     "Booted",
01820     "Remote Close or Net Failure",
01821     "Game Shutdown",
01822     "Login Retry Limit",
01823     "Logins Disabled",
01824     "Logout (Connection Not Dropped)",
01825     "Too Many Connected Players"
01826 };
01827 
01828 // Disconnect reasons that get fed to A_ADISCONNECT via announce_disconnect
01829 //
01830 static const char *disc_messages[] =
01831 {
01832     "Unknown",
01833     "Quit",
01834     "Timeout",
01835     "Boot",
01836     "Netfailure",
01837     "Shutdown",
01838     "BadLogin",
01839     "NoLogins",
01840     "Logout"
01841 };
01842 
01843 void shutdownsock(DESC *d, int reason)
01844 {
01845     char *buff, *buff2;
01846     int i, num;
01847     DESC *dtemp;
01848 
01849     if (  (reason == R_LOGOUT)
01850        && (site_check((d->address).sin_addr, mudstate.access_list) == H_FORBIDDEN))
01851     {
01852         reason = R_QUIT;
01853     }
01854 
01855     CLinearTimeAbsolute ltaNow;
01856     ltaNow.GetUTC();
01857 
01858     if (d->flags & DS_CONNECTED)
01859     {
01860         // Added by D.Piper (del@doofer.org) 1997 & 2000-APR
01861         //
01862 
01863         // Reason: attribute (disconnect reason)
01864         //
01865         atr_add_raw(d->player, A_REASON, (char *)disc_messages[reason]);
01866 
01867         // Update the A_CONNINFO attribute.
01868         //
01869         long anFields[4];
01870         fetch_ConnectionInfoFields(d->player, anFields);
01871 
01872         // One of the active sessions is going away. It doesn't matter which
01873         // one.
01874         //
01875         anFields[CIF_NUMCONNECTS]++;
01876 
01877         // What are the two longest sessions?
01878         //
01879         DESC *dOldest[2];
01880         find_oldest(d->player, dOldest);
01881 
01882         CLinearTimeDelta ltdFull;
01883         ltdFull = ltaNow - dOldest[0]->connected_at;
01884         long tFull = ltdFull.ReturnSeconds();
01885         if (dOldest[0] == d)
01886         {
01887             // We are dropping the oldest connection.
01888             //
01889             CLinearTimeDelta ltdPart;
01890             if (dOldest[1])
01891             {
01892                 // There is another (more recently made) connection.
01893                 //
01894                 ltdPart = dOldest[1]->connected_at - dOldest[0]->connected_at;
01895             }
01896             else
01897             {
01898                 // There is only one connection.
01899                 //
01900                 ltdPart = ltdFull;
01901             }
01902             long tPart = ltdPart.ReturnSeconds();
01903 
01904             anFields[CIF_TOTALTIME] += tPart;
01905             if (anFields[CIF_LONGESTCONNECT] < tFull)
01906             {
01907                 anFields[CIF_LONGESTCONNECT] = tFull;
01908             }
01909         }
01910         anFields[CIF_LASTCONNECT] = tFull;
01911 
01912         put_ConnectionInfoFields(d->player, anFields, ltaNow);
01913 
01914         // If we are doing a LOGOUT, keep the connection open so that the
01915         // player can connect to a different character. Otherwise, we
01916         // do the normal disconnect stuff.
01917         //
01918         if (reason == R_LOGOUT)
01919         {
01920             STARTLOG(LOG_NET | LOG_LOGIN, "NET", "LOGO")
01921             buff = alloc_mbuf("shutdownsock.LOG.logout");
01922             sprintf(buff, "[%u/%s] Logout by ", d->descriptor, d->addr);
01923             log_text(buff);
01924             log_name(d->player);
01925             sprintf(buff, " <Reason: %s>", disc_reasons[reason]);
01926             log_text(buff);
01927             free_mbuf(buff);
01928             ENDLOG;
01929         }
01930         else
01931         {
01932             fcache_dump(d, FC_QUIT);
01933             STARTLOG(LOG_NET | LOG_LOGIN, "NET", "DISC")
01934             buff = alloc_mbuf("shutdownsock.LOG.disconn");
01935             sprintf(buff, "[%u/%s] Logout by ", d->descriptor, d->addr);
01936             log_text(buff);
01937             log_name(d->player);
01938             sprintf(buff, " <Reason: %s>", disc_reasons[reason]);
01939             log_text(buff);
01940             free_mbuf(buff);
01941             ENDLOG;
01942             SiteMonSend(d->descriptor, d->addr, d, "Disconnection");
01943         }
01944 
01945         // If requested, write an accounting record of the form:
01946         // Plyr# Flags Cmds ConnTime Loc Money [Site] <DiscRsn> Name
01947         //
01948         STARTLOG(LOG_ACCOUNTING, "DIS", "ACCT");
01949         CLinearTimeDelta ltd = ltaNow - d->connected_at;
01950         int Seconds = ltd.ReturnSeconds();
01951         buff = alloc_lbuf("shutdownsock.LOG.accnt");
01952         buff2 = decode_flags(GOD, &(db[d->player].fs));
01953         sprintf(buff, "%d %s %d %d %d %d [%s] <%s> %s", d->player, buff2, d->command_count,
01954                 Seconds, Location(d->player), Pennies(d->player), d->addr, disc_reasons[reason],
01955                 Name(d->player));
01956         log_text(buff);
01957         free_lbuf(buff);
01958         free_sbuf(buff2);
01959         ENDLOG;
01960         announce_disconnect(d->player, d, disc_messages[reason]);
01961     }
01962     else
01963     {
01964         if (reason == R_LOGOUT)
01965         {
01966             reason = R_QUIT;
01967         }
01968         STARTLOG(LOG_SECURITY | LOG_NET, "NET", "DISC");
01969         buff = alloc_mbuf("shutdownsock.LOG.neverconn");
01970         sprintf(buff, "[%u/%s] Connection closed, never connected. <Reason: %s>", d->descriptor, d->addr, disc_reasons[reason]);
01971         log_text(buff);
01972         free_mbuf(buff);
01973         ENDLOG;
01974         SiteMonSend(d->descriptor, d->addr, d, "N/C Connection Closed");
01975     }
01976 
01977     process_output(d, false);
01978     clearstrings(d);
01979 
01980     d->flags &= ~DS_CONNECTED;
01981 
01982     // Is this desc still in interactive mode?
01983     //
01984     if (d->program_data != NULL)
01985     {
01986         num = 0;
01987         DESC_ITER_PLAYER(d->player, dtemp) num++;
01988 
01989         if (num == 0)
01990         {
01991             for (i = 0; i < MAX_GLOBAL_REGS; i++)
01992             {
01993                 if (d->program_data->wait_regs[i])
01994                 {
01995                     free_lbuf(d->program_data->wait_regs[i]);
01996                     d->program_data->wait_regs[i] = NULL;
01997                 }
01998             }
01999             MEMFREE(d->program_data);
02000             atr_clr(d->player, A_PROGCMD);
02001         }
02002         d->program_data = NULL;
02003     }
02004     if (reason == R_LOGOUT)
02005     {
02006         d->connected_at.GetUTC();
02007         d->retries_left = mudconf.retry_limit;
02008         d->command_count = 0;
02009         d->timeout = mudconf.idle_timeout;
02010         d->player = 0;
02011         d->doing[0] = '\0';
02012         d->quota = mudconf.cmd_quota_max;
02013         d->last_time = d->connected_at;
02014         d->host_info = site_check((d->address).sin_addr, mudstate.access_list)
02015                      | site_check((d->address).sin_addr, mudstate.suspect_list);
02016         d->input_tot = d->input_size;
02017         d->output_tot = 0;
02018         welcome_user(d);
02019     }
02020     else
02021     {
02022         // Cancel any scheduled processing on this descriptor.
02023         //
02024         scheduler.CancelTask(Task_ProcessCommand, d, 0);
02025 
02026 #ifdef WIN32
02027         if (platform == VER_PLATFORM_WIN32_NT)
02028         {
02029             // Don't close down the socket twice.
02030             //
02031             if (!d->bConnectionShutdown)
02032             {
02033                 // Make sure we don't try to initiate or process any
02034                 // outstanding IOs
02035                 //
02036                 d->bConnectionShutdown = true;
02037 
02038                 // Protect removing the descriptor from our linked list from
02039                 // any interference from the listening thread.
02040                 //
02041                 EnterCriticalSection(&csDescriptorList);
02042                 *d->prev = d->next;
02043                 if (d->next)
02044                 {
02045                     d->next->prev = d->prev;
02046                 }
02047                 LeaveCriticalSection(&csDescriptorList);
02048 
02049                 // This descriptor may hang around awhile, clear out the links.
02050                 //
02051                 d->next = 0;
02052                 d->prev = 0;
02053 
02054                 // Close the connection in 5 seconds.
02055                 //
02056                 scheduler.DeferTask(ltaNow + FACTOR_100NS_PER_SECOND*5,
02057                     PRIORITY_SYSTEM, Task_DeferredClose, d, 0);
02058             }
02059             return;
02060         }
02061 #endif
02062 
02063         shutdown(d->descriptor, SD_BOTH);
02064         if (SOCKET_CLOSE(d->descriptor) == 0)
02065         {
02066             DebugTotalSockets--;
02067         }
02068         d->descriptor = INVALID_SOCKET;
02069 
02070         *d->prev = d->next;
02071         if (d->next)
02072         {
02073             d->next->prev = d->prev;
02074         }
02075 
02076         // This descriptor may hang around awhile, clear out the links.
02077         //
02078         d->next = 0;
02079         d->prev = 0;
02080 
02081         // If we don't have queued IOs, then we can free these, now.
02082         //
02083         freeqs(d);
02084         free_desc(d);
02085         ndescriptors--;
02086     }
02087 }
02088 
02089 #ifdef WIN32
02090 static void shutdownsock_brief(DESC *d)
02091 {
02092     // don't close down the socket twice
02093     //
02094     if (d->bConnectionShutdown)
02095     {
02096         return;
02097     }
02098 
02099     // make sure we don't try to initiate or process any outstanding IOs
02100     //
02101     d->bConnectionShutdown = true;
02102     d->bConnectionDropped = true;
02103 
02104 
02105     // cancel any pending reads or writes on this socket
02106     //
02107     if (!fpCancelIo((HANDLE) d->descriptor))
02108     {
02109         Log.tinyprintf("Error %ld on CancelIo" ENDLINE, GetLastError());
02110     }
02111 
02112     shutdown(d->descriptor, SD_BOTH);
02113     if (closesocket(d->descriptor) == 0)
02114     {
02115         DebugTotalSockets--;
02116     }
02117     d->descriptor = INVALID_SOCKET;
02118 
02119     // protect removing the descriptor from our linked list from
02120     // any interference from the listening thread
02121     //
02122     EnterCriticalSection(&csDescriptorList);
02123 
02124     *d->prev = d->next;
02125     if (d->next)
02126     {
02127         d->next->prev = d->prev;
02128     }
02129 
02130     d->next = 0;
02131     d->prev = 0;
02132 
02133     // safe to allow the listening thread to continue now
02134     LeaveCriticalSection(&csDescriptorList);
02135 
02136     // post a notification that it is safe to free the descriptor
02137     // we can't free the descriptor here (below) as there may be some
02138     // queued completed IOs that will crash when they refer to a descriptor
02139     // (d) that has been freed.
02140     //
02141     if (!PostQueuedCompletionStatus(CompletionPort, 0, (DWORD) d, &lpo_aborted))
02142     {
02143         Log.tinyprintf("Error %ld on PostQueuedCompletionStatus in shutdownsock" ENDLINE, GetLastError());
02144     }
02145 
02146 }
02147 #endif // WIN32
02148 
02149 int make_nonblocking(SOCKET s)
02150 {
02151 #ifdef WIN32
02152     unsigned long on = 1;
02153     if (IS_SOCKET_ERROR(ioctlsocket(s, FIONBIO, &on)))
02154     {
02155         log_perror("NET", "FAIL", "make_nonblocking", "ioctlsocket");
02156         return -1;
02157     }
02158 #else // WIN32
02159 #if defined(O_NONBLOCK)
02160     if (fcntl(s, F_SETFL, O_NONBLOCK) < 0)
02161     {
02162         log_perror("NET", "FAIL", "make_nonblocking", "fcntl");
02163         return -1;
02164     }
02165 #elif defined(FNDELAY)
02166     if (fcntl(s, F_SETFL, FNDELAY) < 0)
02167     {
02168         log_perror("NET", "FAIL", "make_nonblocking", "fcntl");
02169         return -1;
02170     }
02171 #elif defined(O_NDELAY)
02172     if (fcntl(s, F_SETFL, O_NDELAY) < 0)
02173     {
02174         log_perror("NET", "FAIL", "make_nonblocking", "fcntl");
02175         return -1;
02176     }
02177 #elif defined(FIONBIO)
02178     unsigned long on = 1;
02179     if (ioctl(s, FIONBIO, &on) < 0)
02180     {
02181         log_perror("NET", "FAIL", "make_nonblocking", "ioctl");
02182         return -1;
02183     }
02184 #endif // O_NONBLOCK, FNDELAY, O_NDELAY, FIONBIO
02185 #endif // WIN32
02186     return 0;
02187 }
02188 
02189 static void make_nolinger(SOCKET s)
02190 {
02191 #if defined(HAVE_LINGER)
02192     struct linger ling;
02193     ling.l_onoff = 0;
02194     ling.l_linger = 0;
02195     if (IS_SOCKET_ERROR(setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling))))
02196     {
02197         log_perror("NET", "FAIL", "linger", "setsockopt");
02198     }
02199 #endif // HAVE_LINGER
02200 }
02201 
02202 static void config_socket(SOCKET s)
02203 {
02204     make_nonblocking(s);
02205     make_nolinger(s);
02206 }
02207 
02208 // This function must be thread safe WinNT
02209 //
02210 DESC *initializesock(SOCKET s, struct sockaddr_in *a)
02211 {
02212     DESC *d;
02213 
02214 #ifdef WIN32
02215     // protect adding the descriptor from the linked list from
02216     // any interference from socket shutdowns
02217     //
02218     if (platform == VER_PLATFORM_WIN32_NT)
02219     {
02220         EnterCriticalSection(&csDescriptorList);
02221     }
02222 #endif // WIN32
02223 
02224     d = alloc_desc("init_sock");
02225 
02226 #ifdef WIN32
02227     if (platform == VER_PLATFORM_WIN32_NT)
02228     {
02229         LeaveCriticalSection(&csDescriptorList);
02230     }
02231 #endif // WIN32
02232 
02233     d->descriptor = s;
02234     d->flags = 0;
02235     d->connected_at.GetUTC();
02236     d->last_time = d->connected_at;
02237     d->retries_left = mudconf.retry_limit;
02238     d->command_count = 0;
02239     d->timeout = mudconf.idle_timeout;
02240     d->host_info = site_check((*a).sin_addr, mudstate.access_list)
02241                  | site_check((*a).sin_addr, mudstate.suspect_list);
02242 
02243     // Be sure #0 isn't wizard. Shouldn't be.
02244     //
02245     d->player = 0;
02246 
02247     d->addr[0] = '\0';
02248     d->doing[0] = '\0';
02249     d->username[0] = '\0';
02250     config_socket(s);
02251     d->output_prefix = NULL;
02252     d->output_suffix = NULL;
02253     d->output_size = 0;
02254     d->output_tot = 0;
02255     d->output_lost = 0;
02256     d->output_head = NULL;
02257     d->output_tail = NULL;
02258     d->input_head = NULL;
02259     d->input_tail = NULL;
02260     d->input_size = 0;
02261     d->input_tot = 0;
02262     d->input_lost = 0;
02263     d->raw_input = NULL;
02264     d->raw_input_at = NULL;
02265     d->nOption = 0;
02266     d->raw_input_state = NVT_IS_NORMAL;
02267     d->nvt_sga_him_state = OPTION_NO;
02268     d->nvt_sga_us_state = OPTION_NO;
02269     d->nvt_eor_him_state = OPTION_NO;
02270     d->nvt_eor_us_state = OPTION_NO;
02271     d->nvt_naws_him_state = OPTION_NO;
02272     d->nvt_naws_us_state = OPTION_NO;
02273     d->height = 24;
02274     d->width = 78;
02275     d->quota = mudconf.cmd_quota_max;
02276     d->program_data = NULL;
02277     d->address = *a;
02278     strncpy(d->addr, inet_ntoa(a->sin_addr), 50);
02279     d->addr[50] = '\0';
02280 
02281 #ifdef WIN32
02282     // protect adding the descriptor from the linked list from
02283     // any interference from socket shutdowns
02284     //
02285     if (platform == VER_PLATFORM_WIN32_NT)
02286     {
02287         EnterCriticalSection (&csDescriptorList);
02288     }
02289 #endif // WIN32
02290 
02291     ndescriptors++;
02292 
02293     if (descriptor_list)
02294     {
02295         descriptor_list->prev = &d->next;
02296     }
02297     d->hashnext = NULL;
02298     d->next = descriptor_list;
02299     d->prev = &descriptor_list;
02300     descriptor_list = d;
02301 
02302 #ifdef WIN32
02303     // ok to continue now
02304     //
02305     if (platform == VER_PLATFORM_WIN32_NT)
02306     {
02307         LeaveCriticalSection (&csDescriptorList);
02308 
02309         d->OutboundOverlapped.hEvent = NULL;
02310         d->InboundOverlapped.hEvent = NULL;
02311         d->InboundOverlapped.Offset = 0;
02312         d->InboundOverlapped.OffsetHigh = 0;
02313         d->bWritePending = false;   // no write pending yet
02314         d->bConnectionShutdown = false; // not shutdown yet
02315         d->bConnectionDropped = false; // not dropped yet
02316         d->bCallProcessOutputLater = false;
02317     }
02318 #endif // WIN32
02319     return d;
02320 }
02321 
02322 #ifdef WIN32
02323 FTASK *process_output = 0;
02324 
02325 void process_output9x(void *dvoid, int bHandleShutdown)
02326 {
02327     DESC *d = (DESC *)dvoid;
02328     int cnt;
02329 
02330     char *cmdsave = mudstate.debug_cmd;
02331     mudstate.debug_cmd = "< process_output >";
02332 
02333     TBLOCK *tb = d->output_head;
02334 
02335     while (tb != NULL)
02336     {
02337         while (tb->hdr.nchars > 0)
02338         {
02339             cnt = SOCKET_WRITE(d->descriptor, tb->hdr.start, tb->hdr.nchars, 0);
02340             if (IS_SOCKET_ERROR(cnt))
02341             {
02342                 int iSocketError = SOCKET_LAST_ERROR;
02343                 if (  iSocketError != SOCKET_EWOULDBLOCK
02344 #ifdef SOCKET_EAGAIN
02345                    && iSocketError != SOCKET_EAGAIN
02346 #endif // SOCKET_EAGAIN
02347                    && bHandleShutdown)
02348                 {
02349                     shutdownsock(d, R_SOCKDIED);
02350                 }
02351                 mudstate.debug_cmd = cmdsave;
02352                 return;
02353             }
02354             d->output_size -= cnt;
02355             tb->hdr.nchars -= cnt;
02356             tb->hdr.start += cnt;
02357         }
02358         TBLOCK *save = tb;
02359         tb = tb->hdr.nxt;
02360         MEMFREE(save);
02361         save = NULL;
02362         d->output_head = tb;
02363         if (tb == NULL)
02364         {
02365             d->output_tail = NULL;
02366         }
02367     }
02368     mudstate.debug_cmd = cmdsave;
02369 }
02370 
02371 static int AsyncSend(DESC *d, char *buf, int len)
02372 {
02373     DWORD nBytes;
02374 
02375     // Move data from one buffer to another.
02376     //
02377     if (len <= SIZEOF_OVERLAPPED_BUFFERS)
02378     {
02379         // We can consume this buffer.
02380         //
02381         nBytes = len;
02382     }
02383     else
02384     {
02385         // Use the entire bufer and leave the remaining data in the queue.
02386         //
02387         nBytes = SIZEOF_OVERLAPPED_BUFFERS;
02388     }
02389     memcpy(d->output_buffer, buf, nBytes);
02390 
02391     d->OutboundOverlapped.Offset = 0;
02392     d->OutboundOverlapped.OffsetHigh = 0;
02393 
02394     BOOL bResult = WriteFile((HANDLE) d->descriptor, d->output_buffer, nBytes, NULL, &d->OutboundOverlapped);
02395 
02396     d->bWritePending = false;
02397 
02398     if (!bResult)
02399     {
02400         DWORD dwLastError = GetLastError();
02401         if (dwLastError == ERROR_IO_PENDING)
02402         {
02403             d->bWritePending = true;
02404         }
02405         else
02406         {
02407             if (!(d->bConnectionDropped))
02408             {
02409                 // Do no more writes and post a notification that the descriptor should be shutdown.
02410                 //
02411                 d->bConnectionDropped = true;
02412                 Log.tinyprintf("AsyncSend(%d) failed with error %ld. Requesting port shutdown." ENDLINE, d->descriptor, dwLastError);
02413                 if (!PostQueuedCompletionStatus(CompletionPort, 0, (DWORD) d, &lpo_shutdown))
02414                 {
02415                     Log.tinyprintf("Error %ld on PostQueuedCompletionStatus in AsyncSend" ENDLINE, GetLastError());
02416                 }
02417             }
02418             return 0;
02419         }
02420     }
02421     return nBytes;
02422 }
02423 
02424 void process_outputNT(void *dvoid, int bHandleShutdown)
02425 {
02426     UNUSED_PARAMETER(bHandleShutdown);
02427 
02428     DESC *d = (DESC *)dvoid;
02429 
02430     // Don't write if connection dropped or a write is pending.
02431     //
02432     if (d->bConnectionDropped || d->bWritePending)
02433     {
02434         return;
02435     }
02436 
02437     char *cmdsave = mudstate.debug_cmd;
02438     mudstate.debug_cmd = "< process_output >";
02439 
02440     TBLOCK *tb = d->output_head;
02441     TBLOCK *save;
02442     int cnt;
02443 
02444     if (tb != NULL)
02445     {
02446         while (tb->hdr.nchars == 0)
02447         {
02448             save = tb;
02449             tb = tb->hdr.nxt;
02450             MEMFREE(save);
02451             save = NULL;
02452             d->output_head = tb;
02453             if (tb == NULL)
02454             {
02455                 d->output_tail = NULL;
02456                 break;
02457             }
02458         }
02459 
02460         if (tb != NULL)
02461         {
02462             if (tb->hdr.nchars > 0)
02463             {
02464                 cnt = AsyncSend(d, tb->hdr.start, tb->hdr.nchars);
02465                 if (cnt <= 0)
02466                 {
02467                     mudstate.debug_cmd = cmdsave;
02468                     return;
02469                 }
02470                 d->output_size -= cnt;
02471                 tb->hdr.nchars -= cnt;
02472                 tb->hdr.start += cnt;
02473             }
02474             if (tb->hdr.nchars <= 0)
02475             {
02476                 save = tb;
02477                 tb = tb->hdr.nxt;
02478                 MEMFREE(save);
02479                 save = NULL;
02480                 d->output_head = tb;
02481                 if (tb == NULL)
02482                 {
02483                     d->output_tail = NULL;
02484                 }
02485             }
02486         }
02487     }
02488     mudstate.debug_cmd = cmdsave;
02489 }
02490 
02491 #else // WIN32
02492 
02493 void process_output(void *dvoid, int bHandleShutdown)
02494 {
02495     DESC *d = (DESC *)dvoid;
02496 
02497     char *cmdsave = mudstate.debug_cmd;
02498     mudstate.debug_cmd = "< process_output >";
02499 
02500     TBLOCK *tb = d->output_head;
02501     while (tb != NULL)
02502     {
02503         while (tb->hdr.nchars > 0)
02504         {
02505             int cnt = SOCKET_WRITE(d->descriptor, tb->hdr.start, tb->hdr.nchars, 0);
02506             if (IS_SOCKET_ERROR(cnt))
02507             {
02508                 int iSocketError = SOCKET_LAST_ERROR;
02509                 mudstate.debug_cmd = cmdsave;
02510                 if (  iSocketError != SOCKET_EWOULDBLOCK
02511 #ifdef SOCKET_EAGAIN
02512                    && iSocketError != SOCKET_EAGAIN
02513 #endif // SOCKET_EAGAIN
02514                    && bHandleShutdown)
02515                 {
02516                     shutdownsock(d, R_SOCKDIED);
02517                 }
02518                 return;
02519             }
02520             d->output_size -= cnt;
02521             tb->hdr.nchars -= cnt;
02522             tb->hdr.start += cnt;
02523         }
02524         TBLOCK *save = tb;
02525         tb = tb->hdr.nxt;
02526         MEMFREE(save);
02527         save = NULL;
02528         d->output_head = tb;
02529         if (tb == NULL)
02530         {
02531             d->output_tail = NULL;
02532         }
02533     }
02534 
02535     mudstate.debug_cmd = cmdsave;
02536 }
02537 #endif // WIN32
02538 
02555 static const unsigned char nvt_input_xlat_table[256] =
02556 {
02557 //  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
02558 //
02559     0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  2,  0,  0,  3,  0,  0,  // 0
02560     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 1
02561     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 2
02562     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 3
02563     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 4
02564     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 5
02565     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 6
02566     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  // 7
02567 
02568     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 8
02569     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 9
02570     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // A
02571     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // B
02572     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // C
02573     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // D
02574     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  5,  // E
02575     4,  5,  5,  5,  5,  5,  6,  7,  5,  5,  8,  9, 10, 11, 12, 13   // F
02576 };
02577 
02602 static const int nvt_input_action_table[8][14] =
02603 {
02604 //    Any   BS   LF   CR   SE  NOP  AYT   EC   SB WILL DONT   DO WONT  IAC
02605     {   1,   2,   3,   0,   1,   1,   1,   1,   1,   1,   1,   1,   1,   5  }, // Normal
02606     {   4,   4,   4,   4,   4,   4,  12,   2,  10,   6,   7,   8,   9,   1  }, // Have_IAC
02607     {  13,  13,  13,  13,  13,  13,  13,  13,  13,  13,  13,  13,  13,   4  }, // Have_IAC_WILL
02608     {  14,  14,  14,  14,  14,  14,  14,  14,  14,  14,  14,  14,  14,   4  }, // Have_IAC_DONT
02609     {  15,  15,  15,  15,  15,  15,  15,  15,  15,  15,  15,  15,  15,   4  }, // Have_IAC_DO
02610     {  16,  16,  16,  16,  16,  16,  16,  16,  16,  16,  16,  16,  16,   4  }, // Have_IAC_WONT
02611     {  17,  17,  17,  17,  17,  17,  17,  17,  17,  17,  17,  17,  17,  11  }, // Have_IAC_SB
02612     {   0,   0,   0,   0,  18,   0,   0,   0,   0,   0,   0,   0,   0,  17  }, // Have_IAC_SB_IAC
02613 };
02614 
02629 int HimState(DESC *d, unsigned char chOption)
02630 {
02631     if (TELNET_NAWS == chOption)
02632     {
02633         return d->nvt_naws_him_state;
02634     }
02635     else if (TELNET_EOR == chOption)
02636     {
02637         return d->nvt_eor_him_state;
02638     }
02639     else if (TELNET_SGA == chOption)
02640     {
02641         return d->nvt_sga_him_state;
02642     }
02643     return OPTION_NO;
02644 }
02645 
02660 int UsState(DESC *d, unsigned char chOption)
02661 {
02662     if (TELNET_NAWS == chOption)
02663     {
02664         return d->nvt_naws_us_state;
02665     }
02666     else if (TELNET_EOR == chOption)
02667     {
02668         return d->nvt_eor_us_state;
02669     }
02670     else if (TELNET_SGA == chOption)
02671     {
02672         return d->nvt_sga_us_state;
02673     }
02674     return OPTION_NO;
02675 }
02676 
02685 static void SetHimState(DESC *d, unsigned char chOption, int iHimState)
02686 {
02687     if (TELNET_NAWS == chOption)
02688     {
02689         d->nvt_naws_him_state = iHimState;
02690     }
02691     else if (TELNET_EOR == chOption)
02692     {
02693         d->nvt_eor_him_state = iHimState;
02694     }
02695     else if (TELNET_SGA == chOption)
02696     {
02697         d->nvt_sga_him_state = iHimState;
02698     }
02699 }
02700 
02709 static void SetUsState(DESC *d, unsigned char chOption, int iUsState)
02710 {
02711     if (TELNET_NAWS == chOption)
02712     {
02713         d->nvt_naws_us_state = iUsState;
02714     }
02715     else if (TELNET_EOR == chOption)
02716     {
02717         d->nvt_eor_us_state = iUsState;
02718         if (OPTION_YES == iUsState)
02719         {
02720             EnableUs(d, TELNET_SGA);
02721         }
02722         else if (OPTION_NO == iUsState)
02723         {
02724             DisableUs(d, TELNET_SGA);
02725         }
02726     }
02727     else if (TELNET_SGA == chOption)
02728     {
02729         d->nvt_sga_us_state = iUsState;
02730     }
02731 }
02732 
02740 static void SendWill(DESC *d, unsigned char chOption)
02741 {
02742     char aWill[3] = { NVT_IAC, NVT_WILL, 0 };
02743     aWill[2] = chOption;
02744     queue_write_LEN(d, aWill, sizeof(aWill));
02745 }
02746 
02754 static void SendDont(DESC *d, unsigned char chOption)
02755 {
02756     char aDont[3] = { NVT_IAC, NVT_DONT, 0 };
02757     aDont[2] = chOption;
02758     queue_write_LEN(d, aDont, sizeof(aDont));
02759 }
02760 
02768 static void SendDo(DESC *d, unsigned char chOption)
02769 {
02770     char aDo[3]   = { NVT_IAC, NVT_DO,   0 };
02771     aDo[2] = chOption;
02772     queue_write_LEN(d, aDo, sizeof(aDo));
02773 }
02774 
02782 static void SendWont(DESC *d, unsigned char chOption)
02783 {
02784     char aWont[3] = { NVT_IAC, NVT_WONT, 0 };
02785     aWont[2] = chOption;
02786     queue_write_LEN(d, aWont, sizeof(aWont));
02787 }
02788 
02797 static bool DesiredHimOption(DESC *d, unsigned char chOption)
02798 {
02799     UNUSED_PARAMETER(d);
02800 
02801     if (  TELNET_NAWS == chOption
02802        || TELNET_EOR  == chOption
02803        || TELNET_SGA  == chOption)
02804     {
02805         return true;
02806     }
02807     return false;
02808 }
02809 
02822 static bool DesiredUsOption(DESC *d, unsigned char chOption)
02823 {
02824     if (  TELNET_EOR  == chOption
02825        || (  TELNET_SGA == chOption
02826           && OPTION_YES == UsState(d, TELNET_EOR)))
02827     {
02828         return true;
02829     }
02830     return false;
02831 }
02832 
02844 void EnableHim(DESC *d, unsigned char chOption)
02845 {
02846     switch (HimState(d, chOption))
02847     {
02848     case OPTION_NO:
02849         SetHimState(d, chOption, OPTION_WANTYES_EMPTY);
02850         SendDo(d, chOption);
02851         break;
02852 
02853     case OPTION_WANTNO_EMPTY:
02854         SetHimState(d, chOption, OPTION_WANTNO_OPPOSITE);
02855         break;
02856 
02857     case OPTION_WANTYES_OPPOSITE:
02858         SetHimState(d, chOption, OPTION_WANTYES_EMPTY);
02859         break;
02860     }
02861 }
02862 
02874 void DisableHim(DESC *d, unsigned char chOption)
02875 {
02876     switch (HimState(d, chOption))
02877     {
02878     case OPTION_YES:
02879         SetHimState(d, chOption, OPTION_WANTNO_EMPTY);
02880         SendDont(d, chOption);
02881         break;
02882 
02883     case OPTION_WANTNO_OPPOSITE:
02884         SetHimState(d, chOption, OPTION_WANTNO_EMPTY);
02885         break;
02886 
02887     case OPTION_WANTYES_EMPTY:
02888         SetHimState(d, chOption, OPTION_WANTYES_OPPOSITE);
02889         break;
02890     }
02891 }
02892 
02904 void EnableUs(DESC *d, unsigned char chOption)
02905 {
02906     switch (HimState(d, chOption))
02907     {
02908     case OPTION_NO:
02909         SetUsState(d, chOption, OPTION_WANTYES_EMPTY);
02910         SendWill(d, chOption);
02911         break;
02912 
02913     case OPTION_WANTNO_EMPTY:
02914         SetUsState(d, chOption, OPTION_WANTNO_OPPOSITE);
02915         break;
02916 
02917     case OPTION_WANTYES_OPPOSITE:
02918         SetUsState(d, chOption, OPTION_WANTYES_EMPTY);
02919         break;
02920     }
02921 }
02922 
02934 void DisableUs(DESC *d, unsigned char chOption)
02935 {
02936     switch (HimState(d, chOption))
02937     {
02938     case OPTION_YES:
02939         SetUsState(d, chOption, OPTION_WANTNO_EMPTY);
02940         SendWont(d, chOption);
02941         break;
02942 
02943     case OPTION_WANTNO_OPPOSITE:
02944         SetUsState(d, chOption, OPTION_WANTNO_EMPTY);
02945         break;
02946 
02947     case OPTION_WANTYES_EMPTY:
02948         SetUsState(d, chOption, OPTION_WANTYES_OPPOSITE);
02949         break;
02950     }
02951 }
02952 
02965 void TelnetSetup(DESC *d)
02966 {
02967     // Attempt negotation of EOR so we can use that, and if that succeeds,
02968     // code elsewhere will attempt the negotation of SGA for our side as well.
02969     //
02970     EnableUs(d, TELNET_EOR);
02971     EnableHim(d, TELNET_EOR);
02972     EnableHim(d, TELNET_SGA);
02973     EnableHim(d, TELNET_NAWS);
02974 }
02975 
02993 static void process_input_helper(DESC *d, char *pBytes, int nBytes)
02994 {
02995     if (!d->raw_input)
02996     {
02997         d->raw_input = (CBLK *) alloc_lbuf("process_input.raw");
02998         d->raw_input_at = d->raw_input->cmd;
02999     }
03000 
03001     int nInputBytes = 0;
03002     int nLostBytes  = 0;
03003 
03004     char *p    = d->raw_input_at;
03005     char *pend = d->raw_input->cmd + (LBUF_SIZE - sizeof(CBLKHDR) - 1);
03006 
03007     unsigned char *q    = d->aOption + d->nOption;
03008     unsigned char *qend = d->aOption + SBUF_SIZE - 1;
03009 
03010     int n = nBytes;
03011     while (n--)
03012     {
03013         unsigned char ch = (unsigned char)*pBytes;
03014         int iAction = nvt_input_action_table[d->raw_input_state][nvt_input_xlat_table[ch]];
03015         switch (iAction)
03016         {
03017         case 1:
03018             // Action 1 - Accept CHR(X).
03019             //
03020             if (mux_isprint(ch))
03021             {
03022                 if (p < pend)
03023                 {
03024                     *p++ = ch;
03025                     nInputBytes++;
03026                 }
03027                 else
03028                 {
03029                     nLostBytes++;
03030                 }
03031             }
03032             d->raw_input_state = NVT_IS_NORMAL;
03033             break;
03034 
03035         case 0:
03036             // Action 0 - Nothing.
03037             //
03038             break;
03039 
03040         case 2:
03041             // Action 2 - Erase Character.
03042             //
03043             if (NVT_DEL == ch)
03044             {
03045                 queue_string(d, "\b \b");
03046             }
03047             else
03048             {
03049                 queue_string(d, " \b");
03050             }
03051 
03052             if (p > d->raw_input->cmd)
03053             {
03054                 // The character we took back.
03055                 //
03056                 nInputBytes -= 1;
03057                 p--;
03058             }
03059             d->raw_input_state = NVT_IS_NORMAL;
03060             break;
03061 
03062         case 3:
03063             // Action  3 - Accept Line.
03064             //
03065             *p = '\0';
03066             if (d->raw_input->cmd < p)
03067             {
03068                 save_command(d, d->raw_input);
03069                 d->raw_input = (CBLK *) alloc_lbuf("process_input.raw");
03070 
03071                 p = d->raw_input_at = d->raw_input->cmd;
03072                 pend = d->raw_input->cmd + (LBUF_SIZE - sizeof(CBLKHDR) - 1);
03073             }
03074             break;
03075 
03076         case 4:
03077             // Action 4 - Transition to the Normal state.
03078             //
03079             d->raw_input_state = NVT_IS_NORMAL;
03080             break;
03081 
03082         case 5:
03083             // Action  5 - Transition to Have_IAC state.
03084             //
03085             d->raw_input_state = NVT_IS_HAVE_IAC;
03086             break;
03087 
03088         case 6:
03089             // Action 6 - Transition to the Have_IAC_WILL state.
03090             //
03091             d->raw_input_state = NVT_IS_HAVE_IAC_WILL;
03092             break;
03093 
03094         case 7:
03095             // Action  7 - Transition to the Have_IAC_DONT state.
03096             //
03097             d->raw_input_state = NVT_IS_HAVE_IAC_DONT;
03098             break;
03099 
03100         case 8:
03101             // Action  8 - Transition to the Have_IAC_DO state.
03102             //
03103             d->raw_input_state = NVT_IS_HAVE_IAC_DO;
03104             break;
03105 
03106         case 9:
03107             // Action  9 - Transition to the Have_IAC_WONT state.
03108             //
03109             d->raw_input_state = NVT_IS_HAVE_IAC_WONT;
03110             break;
03111 
03112         case 10:
03113             // Action 10 - Transition to the Have_IAC_SB state.
03114             //
03115             q = d->aOption;
03116             d->raw_input_state = NVT_IS_HAVE_IAC_SB;
03117             break;
03118 
03119         case 11:
03120             // Action 11 - Transition to the Have_IAC_SB_IAC state.
03121             //
03122             d->raw_input_state = NVT_IS_HAVE_IAC_SB_IAC;
03123             break;
03124 
03125         case 12:
03126             // Action 12 - Respond to IAC AYT and return to the Normal state.
03127             //
03128             queue_string(d, "\r\n[Yes]\r\n");
03129             d->raw_input_state = NVT_IS_NORMAL;
03130             break;
03131 
03132         case 13:
03133             // Action 13 - Respond to IAC WILL X
03134             //
03135             switch (HimState(d, ch))
03136             {
03137             case OPTION_NO:
03138                 if (DesiredHimOption(d, ch))
03139                 {
03140                     SetHimState(d, ch, OPTION_YES);
03141                     SendDo(d, ch);
03142                 }
03143                 else
03144                 {
03145                     SendDont(d, ch);
03146                 }
03147                 break;
03148 
03149             case OPTION_WANTNO_EMPTY:
03150                 SetHimState(d, ch, OPTION_NO);
03151                 break;
03152 
03153             case OPTION_WANTYES_OPPOSITE:
03154                 SetHimState(d, ch, OPTION_WANTNO_EMPTY);
03155                 SendDont(d, ch);
03156                 break;
03157 
03158             default:
03159                 SetHimState(d, ch, OPTION_YES);
03160                 break;
03161             }
03162             d->raw_input_state = NVT_IS_NORMAL;
03163             break;
03164 
03165         case 14:
03166             // Action 14 - Respond to IAC DONT X
03167             //
03168             switch (UsState(d, ch))
03169             {
03170             case OPTION_YES:
03171                 SetUsState(d, ch, OPTION_NO);
03172                 SendWont(d, ch);
03173                 break;
03174 
03175             case OPTION_WANTNO_OPPOSITE:
03176                 SetUsState(d, ch, OPTION_WANTYES_EMPTY);
03177                 SendWill(d, ch);
03178                 break;
03179 
03180             default:
03181                 SetUsState(d, ch, OPTION_NO);
03182                 break;
03183             }
03184             d->raw_input_state = NVT_IS_NORMAL;
03185             break;
03186 
03187         case 15:
03188             // Action 15 - Respond to IAC DO X
03189             //
03190             switch (UsState(d, ch))
03191             {
03192             case OPTION_NO:
03193                 if (DesiredUsOption(d, ch))
03194                 {
03195                     SetUsState(d, ch, OPTION_YES);
03196                     SendWill(d, ch);
03197                 }
03198                 else
03199                 {
03200                     SendWont(d, ch);
03201                 }
03202                 break;
03203 
03204             case OPTION_WANTNO_EMPTY:
03205                 SetUsState(d, ch, OPTION_NO);
03206                 break;
03207 
03208             case OPTION_WANTYES_OPPOSITE:
03209                 SetUsState(d, ch, OPTION_WANTNO_EMPTY);
03210                 SendWont(d, ch);
03211                 break;
03212 
03213             default:
03214                 SetUsState(d, ch, OPTION_YES);
03215                 break;
03216             }
03217             d->raw_input_state = NVT_IS_NORMAL;
03218             break;
03219 
03220         case 16:
03221             // Action 16 - Respond to IAC WONT X
03222             //
03223             // Ignore.
03224             //
03225             switch (HimState(d, ch))
03226             {
03227             case OPTION_NO:
03228                 break;
03229 
03230             case OPTION_YES:
03231                 SetHimState(d, ch, OPTION_NO);
03232                 SendDont(d, ch);
03233                 break;
03234 
03235             case OPTION_WANTNO_OPPOSITE:
03236                 SetHimState(d, ch, OPTION_WANTYES_EMPTY);
03237                 SendDo(d, ch);
03238                 break;
03239 
03240             default:
03241                 SetHimState(d, ch, OPTION_NO);
03242                 break;
03243             }
03244             d->raw_input_state = NVT_IS_NORMAL;
03245             break;
03246 
03247         case 17:
03248             // Action 17 - Accept CHR(X) for Sub-Option (and transition to Have_IAC_SB state).
03249             //
03250             d->raw_input_state = NVT_IS_HAVE_IAC_SB;
03251             if (  d->aOption <= q
03252                && q < qend)
03253             {
03254                 *q++ = ch;
03255             }
03256             break;
03257 
03258         case 18:
03259             // Action 18 - Accept Completed Sub-option and transition to Normal state.
03260             //
03261             if (  d->aOption < q
03262                && q < qend)
03263             {
03264                 size_t m = q - d->aOption;
03265                 switch (d->aOption[0])
03266                 {
03267                 case TELNET_NAWS:
03268                     if (m == 5)
03269                     {
03270                         d->width  = (d->aOption[1] << 8 ) | d->aOption[2];
03271                         d->height = (d->aOption[3] << 8 ) | d->aOption[4];
03272                     }
03273                     break;
03274                 }
03275             }
03276             q = d->aOption;
03277             d->raw_input_state = NVT_IS_NORMAL;
03278             break;
03279         }
03280         pBytes++;
03281     }
03282 
03283     if (  d->raw_input->cmd < p
03284        && p <= pend)
03285     {
03286         d->raw_input_at = p;
03287     }
03288     else
03289     {
03290         free_lbuf(d->raw_input);
03291         d->raw_input = NULL;
03292         d->raw_input_at = NULL;
03293     }
03294 
03295     if ( d->aOption <= q
03296        && q < qend)
03297     {
03298         d->nOption = q - d->aOption;
03299     }
03300     else
03301     {
03302         d->nOption = 0;
03303     }
03304     d->input_tot  += nBytes;
03305     d->input_size += nInputBytes;
03306     d->input_lost += nLostBytes;
03307 }
03308 
03309 bool process_input(DESC *d)
03310 {
03311     char *cmdsave = mudstate.debug_cmd;
03312     mudstate.debug_cmd = "< process_input >";
03313 
03314     char buf[LBUF_SIZE];
03315     int got = SOCKET_READ(d->descriptor, buf, sizeof(buf), 0);
03316     if (IS_SOCKET_ERROR(got) || got == 0)
03317     {
03318         int iSocketError = SOCKET_LAST_ERROR;
03319         mudstate.debug_cmd = cmdsave;
03320         if (  IS_SOCKET_ERROR(got)
03321            && (  iSocketError == SOCKET_EWOULDBLOCK
03322 #ifdef SOCKET_EAGAIN
03323               || iSocketError == SOCKET_EAGAIN
03324 #endif // SOCKET_EAGAIN
03325               || iSocketError == SOCKET_EINTR))
03326         {
03327             return true;
03328         }
03329         return false;
03330     }
03331     process_input_helper(d, buf, got);
03332     mudstate.debug_cmd = cmdsave;
03333     return true;
03334 }
03335 
03336 void close_sockets(bool emergency, char *message)
03337 {
03338     DESC *d, *dnext;
03339 
03340     DESC_SAFEITER_ALL(d, dnext)
03341     {
03342         if (emergency)
03343         {
03344             SOCKET_WRITE(d->descriptor, message, strlen(message), 0);
03345             if (IS_SOCKET_ERROR(shutdown(d->descriptor, SD_BOTH)))
03346             {
03347                 log_perror("NET", "FAIL", NULL, "shutdown");
03348             }
03349             if (SOCKET_CLOSE(d->descriptor) == 0)
03350             {
03351                 DebugTotalSockets--;
03352             }
03353         }
03354         else
03355         {
03356             queue_string(d, message);
03357             queue_write_LEN(d, "\r\n", 2);
03358             shutdownsock(d, R_GOING_DOWN);
03359         }
03360     }
03361     for (int i = 0; i < nMainGamePorts; i++)
03362     {
03363         if (SOCKET_CLOSE(aMainGamePorts[i].socket) == 0)
03364         {
03365             DebugTotalSockets--;
03366         }
03367         aMainGamePorts[i].socket = INVALID_SOCKET;
03368     }
03369 }
03370 
03371 void emergency_shutdown(void)
03372 {
03373     close_sockets(true, "Going down - Bye");
03374 }
03375 
03376 
03377 // ---------------------------------------------------------------------------
03378 // Signal handling routines.
03379 //
03380 static void check_panicking(int sig)
03381 {
03382     // If we are panicking, turn off signal catching and resignal.
03383     //
03384     if (mudstate.panicking)
03385     {
03386         for (int i = 0; i < NSIG; i++)
03387         {
03388             signal(i, SIG_DFL);
03389         }
03390 #ifdef WIN32
03391         UNUSED_PARAMETER(sig);
03392         abort();
03393 #else // WIN32
03394         kill(game_pid, sig);
03395 #endif // WIN32
03396     }
03397     mudstate.panicking = true;
03398 }
03399 
03400 static void unset_signals(void)
03401 {
03402     int i;
03403 
03404     for (i = 0; i < NSIG; i++)
03405     {
03406         signal(i, SIG_DFL);
03407     }
03408 }
03409 
03410 #ifdef _SGI_SOURCE
03411 #define CAST_SIGNAL_FUNC (SIG_PF)
03412 #else // _SGI_SOURCE
03413 #define CAST_SIGNAL_FUNC
03414 #endif // _SGI_SOURCE
03415 
03416 // The purpose of the following code is support the case where sys_siglist is
03417 // is not part of the environment. This is the case for some Unix platforms
03418 // and also for Win32.
03419 //
03420 typedef struct
03421 {
03422     int         iSignal;
03423     const char *szSignal;
03424 } SIGNALTYPE, *PSIGNALTYPE;
03425 
03426 const SIGNALTYPE aSigTypes[] =
03427 {
03428 #ifdef SIGHUP
03429     // Hangup detected on controlling terminal or death of controlling process.
03430     //
03431     { SIGHUP,   "SIGHUP"},
03432 #endif // SIGHUP
03433 #ifdef SIGINT
03434     // Interrupt from keyboard.
03435     //
03436     { SIGINT,   "SIGINT"},
03437 #endif // SIGINT
03438 #ifdef SIGQUIT
03439     // Quit from keyboard.
03440     //
03441     { SIGQUIT,  "SIGQUIT"},
03442 #endif // SIGQUIT
03443 #ifdef SIGILL
03444     // Illegal Instruction.
03445     //
03446     { SIGILL,   "SIGILL"},
03447 #endif // SIGILL
03448 #ifdef SIGTRAP
03449     // Trace/breakpoint trap.
03450     //
03451     { SIGTRAP,  "SIGTRAP"},
03452 #endif // SIGTRAP
03453 #if defined(SIGABRT)
03454     // Abort signal from abort(3).
03455     //
03456     { SIGABRT,  "SIGABRT"},
03457 #elif defined(SIGIOT)
03458 #define SIGABRT SIGIOT
03459     // Abort signal from abort(3).
03460     //
03461     { SIGIOT,   "SIGIOT"},
03462 #endif // SIGABRT
03463 #ifdef SIGEMT
03464     { SIGEMT,   "SIGEMT"},
03465 #endif // SIGEMT
03466 #ifdef SIGFPE
03467     // Floating-point exception.
03468     //
03469     { SIGFPE,   "SIGFPE"},
03470 #endif // SIGFPE
03471 #ifdef SIGKILL
03472     // Kill signal. Not catchable.
03473     //
03474     { SIGKILL,  "SIGKILL"},
03475 #endif // SIGKILL
03476 #ifdef SIGSEGV
03477     // Invalid memory reference.
03478     //
03479     { SIGSEGV,  "SIGSEGV"},
03480 #endif // SIGSEGV
03481 #ifdef SIGPIPE
03482     // Broken pipe: write to pipe with no readers.
03483     //
03484     { SIGPIPE,  "SIGPIPE"},
03485 #endif // SIGPIPE
03486 #ifdef SIGALRM
03487     // Timer signal from alarm(2).
03488     //
03489     { SIGALRM,  "SIGALRM"},
03490 #endif // SIGALRM
03491 #ifdef SIGTERM
03492     // Termination signal.
03493     //
03494     { SIGTERM,  "SIGTERM"},
03495 #endif // SIGTERM
03496 #ifdef SIGBREAK
03497     // Ctrl-Break.
03498     //
03499     { SIGBREAK, "SIGBREAK"},
03500 #endif // SIGBREAK
03501 #ifdef SIGUSR1
03502     // User-defined signal 1.
03503     //
03504     { SIGUSR1,  "SIGUSR1"},
03505 #endif // SIGUSR1
03506 #ifdef SIGUSR2
03507     // User-defined signal 2.
03508     //
03509     { SIGUSR2,  "SIGUSR2"},
03510 #endif // SIGUSR2
03511 #if defined(SIGCHLD)
03512     // Child stopped or terminated.
03513     //
03514     { SIGCHLD,  "SIGCHLD"},
03515 #elif defined(SIGCLD)
03516 #define SIGCHLD SIGCLD
03517     // Child stopped or terminated.
03518     //
03519     { SIGCLD,   "SIGCLD"},
03520 #endif // SIGCHLD
03521 #ifdef SIGCONT
03522     // Continue if stopped.
03523     //
03524     { SIGCONT,  "SIGCONT"},
03525 #endif // SIGCONT
03526 #ifdef SIGSTOP
03527     // Stop process. Not catchable.
03528     //
03529     { SIGSTOP,  "SIGSTOP"},
03530 #endif // SIGSTOP
03531 #ifdef SIGTSTP
03532     // Stop typed at tty
03533     //
03534     { SIGTSTP,  "SIGTSTP"},
03535 #endif // SIGTSTP
03536 #ifdef SIGTTIN
03537     // tty input for background process.
03538     //
03539     { SIGTTIN,  "SIGTTIN"},
03540 #endif // SIGTTIN
03541 #ifdef SIGTTOU
03542     // tty output for background process.
03543     //
03544     { SIGTTOU,  "SIGTTOU"},
03545 #endif // SIGTTOU
03546 #ifdef SIGBUS
03547     // Bus error (bad memory access).
03548     //
03549     { SIGBUS,   "SIGBUS"},
03550 #endif // SIGBUS
03551 #ifdef SIGPROF
03552     // Profiling timer expired.
03553     //
03554     { SIGPROF,  "SIGPROF"},
03555 #endif // SIGPROF
03556 #ifdef SIGSYS
03557     // Bad argument to routine (SVID).
03558     //
03559     { SIGSYS,   "SIGSYS"},
03560 #endif // SIGSYS
03561 #ifdef SIGURG
03562     // Urgent condition on socket (4.2 BSD).
03563     //
03564     { SIGURG,   "SIGURG"},
03565 #endif // SIGURG
03566 #ifdef SIGVTALRM
03567     // Virtual alarm clock (4.2 BSD).
03568     //
03569     { SIGVTALRM, "SIGVTALRM"},
03570 #endif // SIGVTALRM
03571 #ifdef SIGXCPU
03572     // CPU time limit exceeded (4.2 BSD).
03573     //
03574     { SIGXCPU,  "SIGXCPU"},
03575 #endif // SIGXCPU
03576 #ifdef SIGXFSZ
03577     // File size limit exceeded (4.2 BSD).
03578     //
03579     { SIGXFSZ,  "SIGXFSZ"},
03580 #endif // SIGXFSZ
03581 #ifdef SIGSTKFLT
03582     // Stack fault on coprocessor.
03583     //
03584     { SIGSTKFLT, "SIGSTKFLT"},
03585 #endif // SIGSTKFLT
03586 #if defined(SIGIO)
03587     // I/O now possible (4.2 BSD). File lock lost.
03588     //
03589     { SIGIO,    "SIGIO"},
03590 #elif defined(SIGPOLL)
03591 #define SIGIO SIGPOLL
03592     // Pollable event (Sys V).
03593     //
03594     { SIGPOLL,  "SIGPOLL"},
03595 #endif // SIGIO
03596 #ifdef SIGLOST
03597     { SIGLOST,  "SIGLOST"},
03598 #endif // SIGLOST
03599 #if defined(SIGPWR)
03600     // Power failure (System V).
03601     //
03602     { SIGPWR,   "SIGPWR"},
03603 #elif defined(SIGINFO)
03604 #define SIGPWR SIGINFO
03605     // Power failure (System V).
03606     //
03607     { SIGINFO,  "SIGINFO"},
03608 #endif // SIGPWR
03609 #ifdef SIGWINCH
03610     // Window resize signal (4.3 BSD, Sun).
03611     //
03612     { SIGWINCH, "SIGWINCH"},
03613 #endif // SIGWINCH
03614     { 0,        "SIGZERO" },
03615     { -1, NULL }
03616 };
03617 
03618 typedef struct
03619 {
03620     const char *pShortName;
03621     const char *pLongName;
03622 } MUX_SIGNAMES;
03623 
03624 static MUX_SIGNAMES signames[NSIG];
03625 
03626 #if defined(HAVE_SYS_SIGNAME)
03627 #define SysSigNames sys_signame
03628 #elif defined(SYS_SIGLIST_DECLARED)
03629 #define SysSigNames sys_siglist
03630 #endif // HAVE_SYS_SIGNAME
03631 
03632 void BuildSignalNamesTable(void)
03633 {
03634     int i;
03635     for (i = 0; i < NSIG; i++)
03636     {
03637         signames[i].pShortName = NULL;
03638         signames[i].pLongName  = NULL;
03639     }
03640 
03641     const SIGNALTYPE *pst = aSigTypes;
03642     while (pst->szSignal)
03643     {
03644         int sig = pst->iSignal;
03645         if (  0 <= sig
03646            && sig < NSIG)
03647         {
03648             MUX_SIGNAMES *tsn = &signames[sig];
03649             if (tsn->pShortName == NULL)
03650             {
03651                 tsn->pShortName = pst->szSignal;
03652 #ifndef WIN32
03653                 if (sig == SIGUSR1)
03654                 {
03655                     tsn->pLongName = "Restart server";
03656                 }
03657                 else if (sig == SIGUSR2)
03658                 {
03659                     tsn->pLongName = "Drop flatfile";
03660                 }
03661 #endif // WIN32
03662 #ifdef SysSigNames
03663                 if (  tsn->pLongName == NULL
03664                    && SysSigNames[sig]
03665                    && strcmp(tsn->pShortName, SysSigNames[sig]) != 0)
03666                 {
03667                     tsn->pLongName = SysSigNames[sig];
03668                 }
03669 #endif // SysSigNames
03670             }
03671         }
03672         pst++;
03673     }
03674     for (i = 0; i < NSIG; i++)
03675     {
03676         MUX_SIGNAMES *tsn = &signames[i];
03677         if (tsn->pShortName == NULL)
03678         {
03679 #ifdef SysSigNames
03680             if (SysSigNames[i])
03681             {
03682                 tsn->pLongName = SysSigNames[i];
03683             }
03684 #endif // SysSigNames
03685 
03686             // This is the only non-const memory case.
03687             //
03688             tsn->pShortName = StringClone(tprintf("SIG%03d", i));
03689         }
03690     }
03691 }
03692 
03693 static char *SignalDesc(int iSignal)
03694 {
03695     static char buff[LBUF_SIZE];
03696     char *bufc = buff;
03697     safe_str(signames[iSignal].pShortName, buff, &bufc);
03698     if (signames[iSignal].pLongName)
03699     {
03700         safe_str(" (", buff, &bufc);
03701         safe_str(signames[iSignal].pLongName, buff, &bufc);
03702         safe_chr(')', buff, &bufc);
03703     }
03704     *bufc = '\0';
03705     return buff;
03706 }
03707 
03708 static void log_signal(int iSignal)
03709 {
03710     STARTLOG(LOG_PROBLEMS, "SIG", "CATCH");
03711     log_text("Caught signal ");
03712     log_text(SignalDesc(iSignal));
03713     ENDLOG;
03714 }
03715 
03716 #ifndef WIN32
03717 static void log_signal_ignore(int iSignal)
03718 {
03719     STARTLOG(LOG_PROBLEMS, "SIG", "CATCH");
03720     log_text("Caught signal and ignored signal ");
03721     log_text(SignalDesc(iSignal));
03722     log_text(" because server just came up.");
03723     ENDLOG;
03724 }
03725 
03726 void LogStatBuf(int stat_buf, const char *Name)
03727 {
03728     STARTLOG(LOG_ALWAYS, "NET", Name);
03729     if (WIFEXITED(stat_buf))
03730     {
03731         Log.tinyprintf("process exited unexpectedly with exit status %d.", WEXITSTATUS(stat_buf));
03732     }
03733     else if (WIFSIGNALED(stat_buf))
03734     {
03735         Log.tinyprintf("process was terminated with signal %s.", SignalDesc(WTERMSIG(stat_buf)));
03736     }
03737     else
03738     {
03739         log_text("process ended unexpectedly.");
03740     }
03741     ENDLOG;
03742 }
03743 #endif
03744 
03745 static RETSIGTYPE DCL_CDECL sighandler(int sig)
03746 {
03747 #ifndef WIN32
03748     int stat_buf;
03749     pid_t child;
03750 #endif // !WIN32
03751 
03752     switch (sig)
03753     {
03754 #ifndef WIN32
03755     case SIGUSR1:
03756         if (mudstate.bCanRestart)
03757         {
03758             log_signal(sig);
03759             do_restart(GOD, GOD, GOD, 0);
03760         }
03761         else
03762         {
03763             log_signal_ignore(sig);
03764         }
03765         break;
03766 
03767     case SIGUSR2:
03768 
03769         // Drop a flatfile.
03770         //
03771         log_signal(sig);
03772         raw_broadcast(0, "Caught signal %s requesting a flatfile @dump. Please wait.", SignalDesc(sig));
03773         dump_database_internal(DUMP_I_SIGNAL);
03774         break;
03775 
03776     case SIGCHLD:
03777 
03778         // Change in child status.
03779         //
03780 #ifndef SIGNAL_SIGCHLD_BRAINDAMAGE
03781         signal(SIGCHLD, CAST_SIGNAL_FUNC sighandler);
03782 #endif // !SIGNAL_SIGCHLD_BRAINDAMAGE
03783 
03784         while ((child = waitpid(0, &stat_buf, WNOHANG)) > 0)
03785         {
03786             if (  WIFEXITED(stat_buf)
03787                || WIFSIGNALED(stat_buf))
03788             {
03789                 if (child == slave_pid)
03790                 {
03791                     // The reverse-DNS slave process ended unexpectedly.
03792                     //
03793                     CleanUpSlaveSocket();
03794                     slave_pid = 0;
03795 
03796                     LogStatBuf(stat_buf, "SLAVE");
03797 
03798                     continue;
03799                 }
03800 #ifdef QUERY_SLAVE
03801                 else if (child == sqlslave_pid)
03802                 {
03803                     // The SQL slave process ended unexpectedly.
03804                     //
03805                     CleanUpSQLSlaveSocket();
03806                     sqlslave_pid = 0;
03807 
03808                     LogStatBuf(stat_buf, "QUERY");
03809 
03810                     continue;
03811                 }
03812 #endif // QUERY_SLAVE
03813                 else if (  mudconf.fork_dump
03814                         && mudstate.dumping)
03815                 {
03816                     mudstate.dumped = child;
03817                     if (mudstate.dumper == mudstate.dumped)
03818                     {
03819                         // The dumping process finished.
03820                         //
03821                         mudstate.dumper  = 0;
03822                         mudstate.dumped  = 0;
03823                     }
03824                     else
03825                     {
03826                         // The dumping process finished before we could
03827                         // obtain its process id from fork().
03828                         //
03829                     }
03830                     mudstate.dumping = false;
03831                     local_dump_complete_signal();
03832 
03833                     continue;
03834                 }
03835             }
03836 
03837             log_signal(sig);
03838             LogStatBuf(stat_buf, "UKNWN");
03839 
03840             STARTLOG(LOG_PROBLEMS, "SIG", "DEBUG");
03841 #ifdef QUERY_SLAVE
03842             Log.tinyprintf("mudstate.dumper=%d, child=%d, slave_pid=%d, sqlslave_pid=%d" ENDLINE,
03843                 mudstate.dumper, child, slave_pid, sqlslave_pid);
03844 #else
03845             Log.tinyprintf("mudstate.dumper=%d, child=%d, slave_pid=%d" ENDLINE,
03846                 mudstate.dumper, child, slave_pid);
03847 #endif // QUERY_SLAVE
03848             ENDLOG;
03849         }
03850         break;
03851 
03852     case SIGHUP:
03853 
03854         // Perform a database dump.
03855         //
03856         log_signal(sig);
03857         extern void dispatch_DatabaseDump(void *pUnused, int iUnused);
03858         scheduler.CancelTask(dispatch_DatabaseDump, 0, 0);
03859         mudstate.dump_counter.GetUTC();
03860         scheduler.DeferTask(mudstate.dump_counter, PRIORITY_SYSTEM, dispatch_DatabaseDump, 0, 0);
03861         break;
03862 
03863 #ifdef HAVE_SETITIMER
03864     case SIGPROF:
03865 
03866         // Softcode is running longer than is reasonable.  Apply the brakes.
03867         //
03868         log_signal(sig);
03869         MuxAlarm.Signal();
03870         break;
03871 #endif
03872 
03873 #endif // !WIN32
03874 
03875     case SIGINT:
03876 
03877         // Log + ignore
03878         //
03879         log_signal(sig);
03880         break;
03881 
03882 #ifndef WIN32
03883     case SIGQUIT:
03884 #endif // !WIN32
03885     case SIGTERM:
03886 #ifdef SIGXCPU
03887     case SIGXCPU:
03888 #endif // SIGXCPU
03889         // Time for a normal and short-winded shutdown.
03890         //
03891         check_panicking(sig);
03892         log_signal(sig);
03893         raw_broadcast(0, "GAME: Caught signal %s, exiting.", SignalDesc(sig));
03894         mudstate.shutdown_flag = true;
03895         break;
03896 
03897     case SIGILL:
03898     case SIGFPE:
03899     case SIGSEGV:
03900 #ifndef WIN32
03901     case SIGTRAP:
03902 #ifdef SIGXFSZ
03903     case SIGXFSZ:
03904 #endif // SIGXFSZ
03905 #ifdef SIGEMT
03906     case SIGEMT:
03907 #endif // SIGEMT
03908 #ifdef SIGBUS
03909     case SIGBUS:
03910 #endif // SIGBUS
03911 #ifdef SIGSYS
03912     case SIGSYS:
03913 #endif // SIGSYS
03914 #endif // !WIN32
03915 
03916         // Panic save + restart.
03917         //
03918         Log.Flush();
03919         check_panicking(sig);
03920         log_signal(sig);
03921         report();
03922 
03923         local_presync_database_sigsegv();
03924 #ifndef MEMORY_BASED
03925         al_store();
03926 #endif
03927         pcache_sync();
03928         SYNC;
03929 
03930         if (  mudconf.sig_action != SA_EXIT
03931            && mudstate.bCanRestart)
03932         {
03933             raw_broadcast
03934             (  0,
03935                "GAME: Fatal signal %s caught, restarting.",
03936                SignalDesc(sig)
03937             );
03938 
03939             // There is no older DB. It's a fiction. Our only choice is
03940             // between unamed attributes and named ones. We go with what we
03941             // got.
03942             //
03943             dump_database_internal(DUMP_I_RESTART);
03944             SYNC;
03945             CLOSE;
03946 #ifdef WIN32
03947             unset_signals();
03948             signal(sig, SIG_DFL);
03949             WSACleanup();
03950             exit(12345678);
03951 #else // WIN32
03952             CleanUpSlaveSocket();
03953             CleanUpSlaveProcess();
03954 
03955             // Try our best to dump a core first
03956             //
03957             if (!fork())
03958             {
03959                 // We are the broken parent. Die.
03960                 //
03961                 unset_signals();
03962                 exit(1);
03963             }
03964 
03965             // We are the reproduced child with a slightly better chance.
03966             //
03967             dump_restart_db();
03968 #ifdef GAME_DOOFERMUX
03969             execl("bin/netmux", mudconf.mud_name, "-c", mudconf.config_file, "-p", mudconf.pid_file, NULL);
03970 #else // GAME_DOOFERMUX
03971             execl("bin/netmux", "netmux", "-c", mudconf.config_file, "-p", mudconf.pid_file, NULL);
03972 #endif // GAME_DOOFERMUX
03973             break;
03974 #endif // WIN32
03975         }
03976         else
03977         {
03978 #ifdef WIN32
03979             WSACleanup();
03980 #endif // WIN32
03981 
03982             unset_signals();
03983             signal(sig, SIG_DFL);
03984             exit(1);
03985         }
03986         break;
03987 
03988     case SIGABRT:
03989 
03990         // Coredump.
03991         //
03992         check_panicking(sig);
03993         log_signal(sig);
03994         report();
03995 
03996 #ifdef WIN32
03997         WSACleanup();
03998 #endif // WIN32
03999 
04000         unset_signals();
04001         signal(sig, SIG_DFL);
04002         exit(1);
04003     }
04004     signal(sig, CAST_SIGNAL_FUNC sighandler);
04005     mudstate.panicking = 0;
04006 }
04007 
04008 NAMETAB sigactions_nametab[] =
04009 {
04010     {"exit",        3,  0,  SA_EXIT},
04011     {"default",     1,  0,  SA_DFLT},
04012     { NULL,         0,  0,  0}
04013 };
04014 
04015 void set_signals(void)
04016 {
04017 #ifndef WIN32
04018     sigset_t sigs;
04019 
04020     // We have to reset our signal mask, because of the possibility
04021     // that we triggered a restart on a SIGUSR1. If we did so, then
04022     // the signal became blocked, and stays blocked, since control
04023     // never returns to the caller; i.e., further attempts to send
04024     // a SIGUSR1 would fail.
04025     //
04026 #undef sigfillset
04027 #undef sigprocmask
04028     sigfillset(&sigs);
04029     sigprocmask(SIG_UNBLOCK, &sigs, NULL);
04030 #endif // !WIN32
04031 
04032     signal(SIGINT,  CAST_SIGNAL_FUNC sighandler);
04033     signal(SIGTERM, CAST_SIGNAL_FUNC sighandler);
04034     signal(SIGILL,  CAST_SIGNAL_FUNC sighandler);
04035     signal(SIGSEGV, CAST_SIGNAL_FUNC sighandler);
04036     signal(SIGABRT, CAST_SIGNAL_FUNC sighandler);
04037     signal(SIGFPE,  SIG_IGN);
04038 
04039 #ifndef WIN32
04040     signal(SIGCHLD, CAST_SIGNAL_FUNC sighandler);
04041     signal(SIGHUP,  CAST_SIGNAL_FUNC sighandler);
04042     signal(SIGQUIT, CAST_SIGNAL_FUNC sighandler);
04043     signal(SIGPIPE, SIG_IGN);
04044     signal(SIGUSR1, CAST_SIGNAL_FUNC sighandler);
04045     signal(SIGUSR2, CAST_SIGNAL_FUNC sighandler);
04046     signal(SIGTRAP, CAST_SIGNAL_FUNC sighandler);
04047     signal(SIGILL,  CAST_SIGNAL_FUNC sighandler);
04048 #ifdef HAVE_SETITIMER
04049     signal(SIGPROF,  CAST_SIGNAL_FUNC sighandler);
04050 #endif
04051 
04052 #ifdef SIGXCPU
04053     signal(SIGXCPU, CAST_SIGNAL_FUNC sighandler);
04054 #endif // SIGXCPU
04055 #ifdef SIGFSZ
04056     signal(SIGXFSZ, CAST_SIGNAL_FUNC sighandler);
04057 #endif // SIGFSZ
04058 #ifdef SIGEMT
04059     signal(SIGEMT, CAST_SIGNAL_FUNC sighandler);
04060 #endif // SIGEMT
04061 #ifdef SIGBUS
04062     signal(SIGBUS, CAST_SIGNAL_FUNC sighandler);
04063 #endif // SIGBUS
04064 #ifdef SIGSYS
04065     signal(SIGSYS, CAST_SIGNAL_FUNC sighandler);
04066 #endif // SIGSYS
04067 #endif // !WIN32
04068 }
04069 
04070 void list_system_resources(dbref player)
04071 {
04072     char buffer[80];
04073 
04074     int nTotal = 0;
04075     notify(player, "System Resources");
04076 
04077     sprintf(buffer, "Total Open Files: %ld", DebugTotalFiles);
04078     notify(player, buffer);
04079     nTotal += DebugTotalFiles;
04080 
04081     sprintf(buffer, "Total Sockets: %ld", DebugTotalSockets);
04082     notify(player, buffer);
04083     nTotal += DebugTotalSockets;
04084 
04085 #ifdef WIN32
04086     sprintf(buffer, "Total Threads: %ld", DebugTotalThreads);
04087     notify(player, buffer);
04088     nTotal += DebugTotalThreads;
04089 
04090     sprintf(buffer, "Total Semaphores: %ld", DebugTotalSemaphores);
04091     notify(player, buffer);
04092     nTotal += DebugTotalSemaphores;
04093 #endif // WIN32
04094 
04095     sprintf(buffer, "Total Handles (sum of above): %d", nTotal);
04096     notify(player, buffer);
04097 
04098 #ifdef WIN32
04099     for (int i = 0; i < NUM_SLAVE_THREADS; i++)
04100     {
04101         sprintf(buffer, "Thread %d at line %u", i+1, SlaveThreadInfo[i].iDoing);
04102         notify(player, buffer);
04103     }
04104 #endif // WIN32
04105 }
04106 
04107 #ifdef WIN32
04108 
04109 // ---------------------------------------------------------------------------
04110 // Thread to listen on MUD port - for Windows NT
04111 // ---------------------------------------------------------------------------
04112 //
04113 void __cdecl MUDListenThread(void * pVoid)
04114 {
04115     PortInfo *Port = (PortInfo *)pVoid;
04116 
04117     SOCKADDR_IN SockAddr;
04118     int         nLen;
04119     BOOL        b;
04120 
04121     struct descriptor_data * d;
04122 
04123     Log.tinyprintf("Starting NT-style listening on port %d" ENDLINE, Port->port);
04124 
04125     //
04126     // Loop forever accepting connections
04127     //
04128     for (;;)
04129     {
04130         //
04131         // Block on accept()
04132         //
04133         nLen = sizeof(SOCKADDR_IN);
04134         SOCKET socketClient = accept(Port->socket, (LPSOCKADDR) &SockAddr,
04135             &nLen);
04136 
04137         if (socketClient == INVALID_SOCKET)
04138         {
04139             // parent thread closes the listening socket
04140             // when it wants this thread to stop.
04141             //
04142             break;
04143         }
04144 
04145         DebugTotalSockets++;
04146         if (site_check(SockAddr.sin_addr, mudstate.access_list) == H_FORBIDDEN)
04147         {
04148             STARTLOG(LOG_NET | LOG_SECURITY, "NET", "SITE");
04149             Log.tinyprintf("[%d/%s] Connection refused.  (Remote port %d)",
04150                 socketClient, inet_ntoa(SockAddr.sin_addr), ntohs(SockAddr.sin_port));
04151             ENDLOG;
04152 
04153             // The following are commented out for thread-safety, but
04154             // ordinarily, they would occur at this time.
04155             //
04156             //SiteMonSend(socketClient, inet_ntoa(SockAddr.sin_addr), NULL,
04157             //            "Connection refused");
04158             //fcache_rawdump(socketClient, FC_CONN_SITE);
04159 
04160             shutdown(socketClient, SD_BOTH);
04161             if (closesocket(socketClient) == 0)
04162             {
04163                 DebugTotalSockets--;
04164             }
04165             continue;
04166         }
04167 
04168         // Make slave request
04169         //
04170         // Go take control of the stack, but don't bother if it takes
04171         // longer than 5 seconds to do it.
04172         //
04173         if (bSlaveBooted && (WAIT_OBJECT_0 == WaitForSingleObject(hSlaveRequestStackSemaphore, 5000)))
04174         {
04175             // We have control of the stack. Skip the request if the stack is full.
04176             //
04177             if (iSlaveRequest < SLAVE_REQUEST_STACK_SIZE)
04178             {
04179                 // There is room on the stack, so make the request.
04180                 //
04181                 SlaveRequests[iSlaveRequest].sa_in = SockAddr;
04182                 SlaveRequests[iSlaveRequest].port_in = mudconf.ports.pi[0];
04183                 iSlaveRequest++;
04184                 ReleaseSemaphore(hSlaveRequestStackSemaphore, 1, NULL);
04185 
04186                 // Wake up a single slave thread. Event automatically resets itself.
04187                 //
04188                 ReleaseSemaphore(hSlaveThreadsSemaphore, 1, NULL);
04189             }
04190             else
04191             {
04192                 // No room on the stack, so skip it.
04193                 //
04194                 ReleaseSemaphore(hSlaveRequestStackSemaphore, 1, NULL);
04195             }
04196         }
04197         d = initializesock(socketClient, &SockAddr);
04198 
04199         // Add this socket to the IO completion port.
04200         //
04201         CompletionPort = CreateIoCompletionPort((HANDLE)socketClient, CompletionPort, (DWORD) d, 1);
04202 
04203         if (!CompletionPort)
04204         {
04205             Log.tinyprintf("Error %ld on CreateIoCompletionPort for socket %ld" ENDLINE, GetLastError(), socketClient);
04206             shutdownsock_brief(d);
04207             continue;
04208         }
04209 
04210         TelnetSetup(d);
04211 
04212         if (!PostQueuedCompletionStatus(CompletionPort, 0, (DWORD) d, &lpo_welcome))
04213         {
04214             Log.tinyprintf("Error %ld on PostQueuedCompletionStatus in ProcessWindowsTCP (read)" ENDLINE, GetLastError());
04215             shutdownsock_brief(d);
04216             continue;
04217         }
04218 
04219         // Do the first read
04220         //
04221         b = ReadFile((HANDLE) socketClient, d->input_buffer, sizeof(d->input_buffer), NULL, &d->InboundOverlapped);
04222 
04223         if (!b && GetLastError() != ERROR_IO_PENDING)
04224         {
04225             // Post a notification that the descriptor should be shutdown, and do no more IO.
04226             //
04227             d->bConnectionDropped = true;
04228             Log.tinyprintf("ProcessWindowsTCP(%d) cannot queue read request with error %ld. Requesting port shutdown." ENDLINE, d->descriptor, GetLastError());
04229             if (!PostQueuedCompletionStatus(CompletionPort, 0, (DWORD) d, &lpo_shutdown))
04230             {
04231                 Log.tinyprintf("Error %ld on PostQueuedCompletionStatus in ProcessWindowsTCP (initial read)" ENDLINE, GetLastError());
04232             }
04233         }
04234     }
04235     Log.tinyprintf("End of NT-style listening on port %d" ENDLINE, Port->port);
04236 }
04237 
04238 
04239 void Task_FreeDescriptor(void *arg_voidptr, int arg_Integer)
04240 {
04241     UNUSED_PARAMETER(arg_Integer);
04242 
04243     DESC *d = (DESC *)arg_voidptr;
04244     if (d)
04245     {
04246         EnterCriticalSection(&csDescriptorList);
04247         ndescriptors--;
04248         freeqs(d);
04249         free_desc(d);
04250         LeaveCriticalSection(&csDescriptorList);
04251     }
04252 }
04253 
04254 void Task_DeferredClose(void *arg_voidptr, int arg_Integer)
04255 {
04256     UNUSED_PARAMETER(arg_Integer);
04257 
04258     DESC *d = (DESC *)arg_voidptr;
04259     if (d)
04260     {
04261         d->bConnectionDropped = true;
04262 
04263         // Cancel any pending reads or writes on this socket
04264         //
04265         if (!fpCancelIo((HANDLE) d->descriptor))
04266         {
04267             Log.tinyprintf("Error %ld on CancelIo" ENDLINE, GetLastError());
04268         }
04269 
04270         shutdown(d->descriptor, SD_BOTH);
04271         if (SOCKET_CLOSE(d->descriptor) == 0)
04272         {
04273             DebugTotalSockets--;
04274         }
04275         d->descriptor = INVALID_SOCKET;
04276 
04277         // Post a notification that it is safe to free the descriptor
04278         // we can't free the descriptor here (below) as there may be some
04279         // queued completed IOs that will crash when they refer to a descriptor
04280         // (d) that has been freed.
04281         //
04282         if (!PostQueuedCompletionStatus(CompletionPort, 0, (DWORD) d, &lpo_aborted))
04283         {
04284             Log.tinyprintf("Error %ld on PostQueuedCompletionStatus in shutdownsock" ENDLINE, GetLastError());
04285         }
04286     }
04287 }
04288 
04289 
04290 /*
04291 This is called from within shovechars when it needs to see if any IOs have
04292 completed for the Windows NT version.
04293 
04294 The 4 sorts of IO completions are:
04295 
04296 1. Outstanding read completing (there should always be an outstanding read)
04297 2. Outstanding write completing
04298 3. A special "shutdown" message to tell us to shutdown the socket
04299 4. A special "aborted" message to tell us the socket has shut down, and we
04300 can now free the descriptor.
04301 
04302 The latter 2 are posted by the application by PostQueuedCompletionStatus
04303 when it is necessary to signal these "events".
04304 
04305 The reason for posting the special messages is to shut down sockets in an
04306 orderly way.
04307 
04308 */
04309 
04310 void ProcessWindowsTCP(DWORD dwTimeout)
04311 {
04312     LPOVERLAPPED lpo;
04313     DWORD nbytes;
04314     DESC *d;
04315 
04316     for ( ; ; dwTimeout = 0)
04317     {
04318         // pull out the next completed IO
04319         //
04320         BOOL b = GetQueuedCompletionStatus(CompletionPort, &nbytes, (LPDWORD) &d, &lpo, dwTimeout);
04321 
04322         if (!b)
04323         {
04324             DWORD dwLastError = GetLastError();
04325 
04326             // Ignore timeouts and cancelled IOs
04327             //
04328             switch (dwLastError)
04329             {
04330             case WAIT_TIMEOUT:
04331                 //Log.WriteString("Timeout." ENDLINE);
04332                 return;
04333 
04334             case ERROR_OPERATION_ABORTED:
04335                 //Log.WriteString("Operation Aborted." ENDLINE);
04336                 continue;
04337 
04338             default:
04339                 if (!(d->bConnectionDropped))
04340                 {
04341                     // bad IO - shut down this client
04342                     //
04343                     d->bConnectionDropped = true;
04344 
04345                     // Post a notification that the descriptor should be shutdown
04346                     //
04347                     Log.tinyprintf("ProcessWindowsTCP(%d) failed IO with error %ld. Requesting port shutdown." ENDLINE, d->descriptor, dwLastError);
04348                     if (!PostQueuedCompletionStatus(CompletionPort, 0, (DWORD) d, &lpo_shutdown))
04349                     {
04350                         Log.tinyprintf("Error %ld on PostQueuedCompletionStatus in ProcessWindowsTCP (write)" ENDLINE, GetLastError());
04351                     }
04352                 }
04353             }
04354         }
04355         else if (lpo == &d->OutboundOverlapped && !d->bConnectionDropped)
04356         {
04357             //Log.tinyprintf("Write(%d bytes)." ENDLINE, nbytes);
04358 
04359             // Write completed
04360             //
04361             TBLOCK *tp;
04362             DWORD nBytes;
04363 
04364             bool bNothingToWrite;
04365             do
04366             {
04367                 bNothingToWrite = true;
04368                 tp = d->output_head;
04369                 if (tp == NULL)
04370                 {
04371                     d->bWritePending = false;
04372                     break;
04373                 }
04374                 bNothingToWrite = true;
04375 
04376                 // Move data from one buffer to another.
04377                 //
04378                 if (tp->hdr.nchars <= SIZEOF_OVERLAPPED_BUFFERS)
04379                 {
04380                     // We can consume this buffer.
04381                     //
04382                     nBytes = tp->hdr.nchars;
04383                     memcpy(d->output_buffer, tp->hdr.start, nBytes);
04384                     TBLOCK *save = tp;
04385                     tp = tp->hdr.nxt;
04386                     MEMFREE(save);
04387                     save = NULL;
04388                     d->output_head = tp;
04389                     if (tp == NULL)
04390                     {
04391                         //Log.tinyprintf("Write...%d bytes taken from a queue of %d bytes...Empty Queue, now." ENDLINE, nBytes, d->output_size);
04392                         d->output_tail = NULL;
04393                     }
04394                     else
04395                     {
04396                         //Log.tinyprintf("Write...%d bytes taken from a queue of %d bytes...more buffers in Queue" ENDLINE, nBytes, d->output_size);
04397                     }
04398                 }
04399                 else
04400                 {
04401                     // Use the entire bufer and leave the remaining data in the queue.
04402                     //
04403                     nBytes = SIZEOF_OVERLAPPED_BUFFERS;
04404                     memcpy(d->output_buffer, tp->hdr.start, nBytes);
04405                     tp->hdr.nchars -= nBytes;
04406                     tp->hdr.start += nBytes;
04407                     //Log.tinyprintf("Write...%d bytes taken from a queue of %d bytes...buffer still has bytes" ENDLINE, nBytes, d->output_size);
04408                 }
04409                 d->output_size -= nBytes;
04410 
04411                 d->OutboundOverlapped.Offset = 0;
04412                 d->OutboundOverlapped.OffsetHigh = 0;
04413 
04414                 // We do allow more than one complete write request in the IO
04415                 // completion port queue. The reason is that if WriteFile
04416                 // returns true, we -can- re-used the output_buffer -and-
04417                 // redundant queue entries just cause us to try to write more
04418                 // often. There is no possibility of corruption.
04419                 //
04420                 // It then becomes a trade off between the costs. I find that
04421                 // keeping the TCP/IP full of data is more important.
04422                 //
04423                 DWORD nWritten;
04424                 b = WriteFile((HANDLE) d->descriptor, d->output_buffer,
04425                     nBytes, &nWritten, &d->OutboundOverlapped);
04426 
04427             } while (b);
04428 
04429             if (bNothingToWrite)
04430             {
04431                 if (d->bConnectionShutdown)
04432                 {
04433                     scheduler.CancelTask(Task_DeferredClose, d, 0);
04434                     scheduler.DeferImmediateTask(PRIORITY_SYSTEM, Task_DeferredClose, d, 0);
04435                 }
04436                 continue;
04437             }
04438 
04439             d->bWritePending = true;
04440             DWORD dwLastError = GetLastError();
04441             if (dwLastError != ERROR_IO_PENDING)
04442             {
04443                 // Post a notification that the descriptor should be shutdown
04444                 //
04445                 d->bWritePending = false;
04446                 d->bConnectionDropped = true;
04447                 Log.tinyprintf("ProcessWindowsTCP(%d) cannot queue write request with error %ld. Requesting port shutdown." ENDLINE, d->descriptor, dwLastError);
04448                 if (!PostQueuedCompletionStatus(CompletionPort, 0, (DWORD) d, &lpo_shutdown))
04449                 {
04450                     Log.tinyprintf("Error %ld on PostQueuedCompletionStatus in ProcessWindowsTCP (write)" ENDLINE, GetLastError());
04451                 }
04452             }
04453         }
04454         else if (lpo == &d->InboundOverlapped && !d->bConnectionDropped)
04455         {
04456             //Log.tinyprintf("Read(%d bytes)." ENDLINE, nbytes);
04457             // The read operation completed
04458             //
04459             if (nbytes == 0)
04460             {
04461                 // A zero-length IO completion means that the connection was dropped by the client.
04462                 //
04463 
04464                 // Post a notification that the descriptor should be shutdown
04465                 //
04466                 d->bConnectionDropped = true;
04467                 Log.tinyprintf("ProcessWindowsTCP(%d) zero-length read. Requesting port shutdown." ENDLINE, d->descriptor);
04468                 if (!PostQueuedCompletionStatus(CompletionPort, 0, (DWORD) d, &lpo_shutdown))
04469                 {
04470                     Log.tinyprintf("Error %ld on PostQueuedCompletionStatus in ProcessWindowsTCP (read)" ENDLINE, GetLastError());
04471                 }
04472                 continue;
04473             }
04474 
04475             d->last_time.GetUTC();
04476 
04477             // Undo autodark
04478             //
04479             if (d->flags & DS_AUTODARK)
04480             {
04481                 // Clear the DS_AUTODARK on every related session.
04482                 //
04483                 DESC *d1;
04484                 DESC_ITER_PLAYER(d->player, d1)
04485                 {
04486                     d1->flags &= ~DS_AUTODARK;
04487                 }
04488                 db[d->player].fs.word[FLAG_WORD1] &= ~DARK;
04489             }
04490 
04491             // process the player's input
04492             //
04493             process_input_helper(d, d->input_buffer, nbytes);
04494 
04495             // now fire off another read
04496             //
04497             b = ReadFile((HANDLE) d->descriptor, d->input_buffer, sizeof(d->input_buffer), &nbytes, &d->InboundOverlapped);
04498 
04499             // if ReadFile returns true, then the read completed successfully already, but it was also added to the IO
04500             // completion port queue, so in order to avoid having two requests in the queue for the same buffer
04501             // (corruption problems), we act as if the IO is still pending.
04502             //
04503 
04504             if (!b)
04505             {
04506                 // ERROR_IO_PENDING is a normal way of saying, 'not done yet'. All other errors are serious errors.
04507                 //
04508                 DWORD dwLastError = GetLastError();
04509                 if (dwLastError != ERROR_IO_PENDING)
04510                 {
04511                     // Post a notification that the descriptor should be shutdown, and do no more IO.
04512                     //
04513                     d->bConnectionDropped = true;
04514                     Log.tinyprintf("ProcessWindowsTCP(%d) cannot queue read request with error %ld. Requesting port shutdown." ENDLINE, d->descriptor, dwLastError);
04515                     if (!PostQueuedCompletionStatus(CompletionPort, 0, (DWORD) d, &lpo_shutdown))
04516                     {
04517                         Log.tinyprintf("Error %ld on PostQueuedCompletionStatus in ProcessWindowsTCP (read)" ENDLINE, GetLastError());
04518                     }
04519                 }
04520             }
04521         }
04522         else if (lpo == &lpo_welcome)
04523         {
04524             char *buff = alloc_mbuf("ProcessWindowsTCP.Premature");
04525             strcpy(buff, inet_ntoa(d->address.sin_addr));
04526 
04527             // If the socket is invalid, the we were unable to queue a read
04528             // request, and the port was shutdown while this packet was in
04529             // the completion port queue.
04530             //
04531             bool bInvalidSocket = IS_INVALID_SOCKET(d->descriptor);
04532 
04533             // Log connection.
04534             //
04535             STARTLOG(LOG_NET | LOG_LOGIN, "NET", "CONN");
04536             Log.tinyprintf("[%s/%s] Connection opened (remote port %d)",
04537                 bInvalidSocket ? "UNKNOWN" : mux_ltoa_t(d->descriptor), buff,
04538                 ntohs(d->address.sin_port));
04539             ENDLOG;
04540 
04541             SiteMonSend(d->descriptor, buff, d, "Connection");
04542 
04543             if (bInvalidSocket)
04544             {
04545                 // Log premature disconnection.
04546                 //
04547                 STARTLOG(LOG_NET | LOG_LOGIN, "NET", "DISC");
04548                 Log.tinyprintf("[UNKNOWN/%s] Connection closed prematurely (remote port %d)",
04549                     buff, ntohs(d->address.sin_port));
04550                 ENDLOG;
04551 
04552                 SiteMonSend(d->descriptor, buff, d, "Connection closed prematurely");
04553             }
04554             else
04555             {
04556                 // Welcome the user.
04557                 //
04558                 welcome_user(d);
04559             }
04560             free_mbuf(buff);
04561         }
04562         else if (lpo == &lpo_shutdown)
04563         {
04564             //Log.WriteString("Shutdown." ENDLINE);
04565             // Shut this descriptor down.
04566             //
04567             shutdownsock(d, R_SOCKDIED);   // shut him down
04568         }
04569         else if (lpo == &lpo_aborted)
04570         {
04571             // Instead of freeing the descriptor immediately, we are going to put it back at the
04572             // end of the queue. CancelIo will still generate aborted packets. We don't want the descriptor
04573             // be be re-used and have a new connection be stepped on by a dead one.
04574             //
04575             if (!PostQueuedCompletionStatus(CompletionPort, 0, (DWORD) d, &lpo_aborted_final))
04576             {
04577                 Log.tinyprintf("Error %ld on PostQueuedCompletionStatus in ProcessWindowsTCP (aborted)" ENDLINE, GetLastError());
04578             }
04579         }
04580         else if (lpo == &lpo_aborted_final)
04581         {
04582             // Now that we are fairly certain that all IO packets refering to this descriptor have been processed
04583             // and no further packets remain in the IO queue, schedule a task to free the descriptor. This allows
04584             // any tasks which might potentially refer to this descriptor to be handled before we free the
04585             // descriptor.
04586             //
04587             scheduler.DeferImmediateTask(PRIORITY_SYSTEM, Task_FreeDescriptor, d, 0);
04588         }
04589         else if (lpo == &lpo_wakeup)
04590         {
04591             // Just waking up is good enough.
04592             //
04593         }
04594     }
04595 }
04596 
04597 #endif // WIN32
04598 
04599 void SiteMonSend(int port, const char *address, DESC *d, const char *msg)
04600 {
04601     // Don't do sitemon for blocked sites.
04602     //
04603     if (  d != NULL
04604        && (d->host_info & H_NOSITEMON))
04605     {
04606         return;
04607     }
04608 
04609     // Build the msg.
04610     //
04611     char *sendMsg;
04612     bool bSuspect = (d != NULL) && (d->host_info & H_SUSPECT);
04613     if (IS_INVALID_SOCKET(port))
04614     {
04615         sendMsg = tprintf("SITEMON: [UNKNOWN] %s from %s.%s", msg, address,
04616             bSuspect ? " (SUSPECT)": "");
04617     }
04618     else
04619     {
04620         sendMsg = tprintf("SITEMON: [%d] %s from %s.%s", port, msg,
04621             address, bSuspect ? " (SUSPECT)": "");
04622     }
04623 
04624     DESC *nd;
04625     DESC_ITER_CONN(nd)
04626     {
04627         if (SiteMon(nd->player))
04628         {
04629             queue_string(nd, sendMsg);
04630             queue_write_LEN(nd, "\r\n", 2);
04631             process_output(nd, false);
04632         }
04633     }
04634 }

Generated on Mon May 28 04:40:08 2007 for MUX by  doxygen 1.4.7