root/src/dps8/uvutil.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. accessMonotonicMs
  2. accessPeerEqual
  3. accessParsePeer
  4. accessGetPeer
  5. accessPeerText
  6. accessRateFind
  7. accessRatePermits
  8. accessRateFailure
  9. accessPendingFor
  10. accessRandomDelay
  11. accessForClient
  12. alloc_buffer
  13. accessWriteCallback
  14. accessReadCallback
  15. accessReadStart
  16. accessStartWriteActual
  17. accessStartWrite
  18. accessPutCharForce
  19. accessPutStrForce
  20. accessStartWriteStr
  21. accessLogon
  22. accessRelease
  23. accessTimerCloseCallback
  24. accessCloseCallback
  25. accessCloseConnection
  26. accessTimerCallback
  27. accessReject
  28. accessShutdownCallback
  29. accessDrainOld
  30. accessPromote
  31. accessCloseAll
  32. accessOpenSessions
  33. accessProcessInput
  34. accessTelnetReadCallback
  35. evHandler
  36. accessTelnetConnect
  37. accessListenerCloseCallback
  38. onNewAccess
  39. uv_open_access
  40. accessPutChar
  41. accessGetChar
  42. accessPutStr

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * scspell-id: 6315eaa3-f630-11ec-96fc-80ee73e9b8e7
   5  *
   6  * ---------------------------------------------------------------------------
   7  *
   8  * Copyright (c) 2013-2019 Charles Anthony
   9  * Copyright (c) 2021 Jeffrey H. Johnson
  10  * Copyright (c) 2021-2025 The DPS8M Development Team
  11  *
  12  * This software is made available under the terms of the ICU License.
  13  * See the LICENSE.md file at the top-level directory of this distribution.
  14  *
  15  * ---------------------------------------------------------------------------
  16  */
  17 
  18 //-V::595
  19 
  20 #include <uv.h>
  21 #include <ctype.h>
  22 #include <limits.h>
  23 #include <signal.h>
  24 #include "dps8.h"
  25 #include "dps8_sys.h"
  26 #include "dps8_cpu.h"
  27 #include "dps8_utils.h"
  28 #include "libtelnet.h"
  29 #include "uvutil.h"
  30 
  31 // OPCON reads continue while Multics may be stalled. Bound queued input so
  32 // an unauthenticated or authenticated TCP peer cannot exhaust host memory.
  33 #define OPCON_MAX_PENDING_INPUT (16U * 1024U * 1024U)
  34 #define OPCON_PENDING_PER_LISTENER 8U
  35 #define OPCON_PENDING_PER_ADDRESS 2U
  36 #define OPCON_PENDING_TOTAL 32U
  37 #define OPCON_AUTH_TIMEOUT_MS 30000U
  38 #define OPCON_FAILURE_WINDOW_MS 60000U
  39 #define OPCON_FAILURE_LIMIT 5U
  40 #define OPCON_RATE_SLOTS 128U
  41 #define OPCON_DRAIN_TIMEOUT_MS 1000U
  42 #define OPCON_MAX_DRAINING_PER_LISTENER 8U
  43 #define OPCON_MAX_QUEUED_OUTPUT (4U * 1024U * 1024U)
  44 
  45 #if defined(NO_LOCALE)
  46 # define xstrerror_l strerror
  47 #endif
  48 
  49 #if defined(FREE)
  50 # undef FREE
  51 #endif /* if defined(FREE) */
  52 #define FREE(p) do  \
  53   {                 \
  54     free((p));      \
  55     (p) = NULL;     \
  56   } while(0)
  57 
  58 #define USE_REQ_DATA
  59 
  60 static void accessTelnetReadCallback (uv_tcp_t * client,
  61                             ssize_t nread,
  62                             unsigned char * buf);
  63 
  64 // The listener survives reconnects.  In particular, the password buffer,
  65 // TELNET parser, and timers are properties of an accepted socket, never of
  66 // the currently active OPCON listener.
  67 typedef struct
  68   {
  69     int family;
  70     unsigned char address[16];
  71   } access_peer;
  72 
  73 typedef enum
  74   {
  75     access_authenticating,
  76     access_active,
  77     access_rejected,
  78     access_draining,
  79     access_closing
  80   } access_state;
  81 
  82 typedef struct access_client_s
  83   {
  84     uv_access * owner;
  85     uv_tcp_t * client;
  86     telnet_t * telnetp;
  87     uv_timer_t timer;
  88     bool timer_inited;
  89     unsigned int close_refs;
  90     bool pending;
  91     bool admitted;
  92     bool closing;
  93     bool promoted_in_read;
  94     access_state state;
  95     access_peer peer;
  96     char pwBuffer[PW_SIZE + 1];
  97     unsigned int pwPos;
  98     struct access_client_s * next;
  99   } access_client;
 100 
 101 typedef struct
 102   {
 103     access_peer peer;
 104     uint64_t last_seen_ms;
 105     uint64_t failures_ms[OPCON_FAILURE_LIMIT];
 106     unsigned int next_failure;
 107   } access_rate_slot;
 108 
 109 static access_client * access_sessions;
 110 static unsigned int access_pending_total;
 111 static access_rate_slot access_rates[OPCON_RATE_SLOTS];
 112 static uint64_t access_jitter_counter;
 113 
 114 static void accessPromote (access_client * session);
 115 static void accessReject (access_client * session);
 116 static void accessTimerCallback (uv_timer_t * timer);
 117 
 118 static uint64_t accessMonotonicMs (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 119   {
 120     return uv_hrtime () / UINT64_C(1000000);
 121   }
 122 
 123 static bool accessPeerEqual (const access_peer * a, const access_peer * b)
     /* [previous][next][first][last][top][bottom][index][help] */
 124   {
 125     if (a->family != b->family)
 126       return false;
 127     return memcmp (a->address, b->address,
 128                    a->family == AF_INET ? 4U : 16U) == 0;
 129   }
 130 
 131 static bool accessParsePeer (const struct sockaddr_storage * address,
     /* [previous][next][first][last][top][bottom][index][help] */
 132                              int len, access_peer * peer)
 133   {
 134     memset (peer, 0, sizeof (*peer));
 135     if (len < (int)sizeof (struct sockaddr) ||
 136         (size_t)len > sizeof (*address))
 137       return false;
 138     if (address->ss_family == AF_INET)
 139       {
 140         struct sockaddr_in ipv4;
 141         // Keep the IPv4-specific length guard for platforms whose sockaddr
 142         // and sockaddr_in sizes differ, and for future parsing changes.
 143         if ((size_t)len < sizeof (ipv4)) //-V547
 144           return false;
 145         memcpy (&ipv4, address, sizeof (ipv4));
 146         // The destination holds IPv4 or IPv6: copying only the four IPv4
 147         // bytes is intentional; memset above zeroed the other 12 bytes.
 148         memcpy (peer->address, &ipv4.sin_addr, sizeof (ipv4.sin_addr)); //-V1086
 149       }
 150     else if (address->ss_family == AF_INET6)
 151       {
 152         struct sockaddr_in6 ipv6;
 153         if ((size_t)len < sizeof (ipv6))
 154           return false;
 155         memcpy (&ipv6, address, sizeof (ipv6));
 156         memcpy (peer->address, &ipv6.sin6_addr, sizeof (ipv6.sin6_addr));
 157       }
 158     else
 159       return false;
 160     peer->family = address->ss_family;
 161     return true;
 162   }
 163 
 164 static bool accessGetPeer (uv_tcp_t * client, access_peer * peer)
     /* [previous][next][first][last][top][bottom][index][help] */
 165   {
 166     struct sockaddr_storage address;
 167     int len = sizeof (address);
 168     if (uv_tcp_getpeername (client, (struct sockaddr *)&address, &len) != 0)
 169       return false;
 170     return accessParsePeer (&address, len, peer);
 171   }
 172 
 173 static const char * accessPeerText (const access_peer * peer,
     /* [previous][next][first][last][top][bottom][index][help] */
 174                                     char * buf, size_t size)
 175   {
 176     if (peer->family != AF_INET && peer->family != AF_INET6)
 177       return NULL;
 178     if (size > UINT_MAX)
 179       return NULL;
 180     return inet_ntop (peer->family, peer->address, buf, (socklen_t)size);
 181   }
 182 
 183 static access_rate_slot * accessRateFind (const access_peer * peer)
     /* [previous][next][first][last][top][bottom][index][help] */
 184   {
 185     uint64_t now = accessMonotonicMs ();
 186     access_rate_slot * available = NULL;
 187     access_rate_slot * oldest = &access_rates[0];
 188     for (unsigned int i = 0; i < OPCON_RATE_SLOTS; i ++)
 189       {
 190         access_rate_slot * slot = &access_rates[i];
 191         if (slot->last_seen_ms < oldest->last_seen_ms)
 192           oldest = slot;
 193         if (slot->last_seen_ms &&
 194             now - slot->last_seen_ms < OPCON_FAILURE_WINDOW_MS &&
 195             accessPeerEqual (&slot->peer, peer))
 196           return slot;
 197         if (!available && (!slot->last_seen_ms ||
 198                            now - slot->last_seen_ms >=
 199                                                OPCON_FAILURE_WINDOW_MS))
 200           available = slot;
 201       }
 202     if (!available)
 203       available = oldest;
 204     memset (available, 0, sizeof (*available));
 205     available->peer = *peer;
 206     available->last_seen_ms = now;
 207     return available;
 208   }
 209 
 210 static bool accessRatePermits (const access_peer * peer)
     /* [previous][next][first][last][top][bottom][index][help] */
 211   {
 212     access_rate_slot * slot = accessRateFind (peer);
 213     uint64_t now = accessMonotonicMs ();
 214     unsigned int count = 0;
 215     for (unsigned int i = 0; i < OPCON_FAILURE_LIMIT; i ++)
 216       if (slot->failures_ms[i] &&
 217           now - slot->failures_ms[i] < OPCON_FAILURE_WINDOW_MS)
 218         ++count;
 219     return count < OPCON_FAILURE_LIMIT;
 220   }
 221 
 222 static void accessRateFailure (const access_peer * peer)
     /* [previous][next][first][last][top][bottom][index][help] */
 223   {
 224     access_rate_slot * slot = accessRateFind (peer);
 225     slot->last_seen_ms = accessMonotonicMs ();
 226     slot->failures_ms[slot->next_failure] = slot->last_seen_ms;
 227     slot->next_failure = (slot->next_failure + 1U) % OPCON_FAILURE_LIMIT;
 228   }
 229 
 230 static unsigned int accessPendingFor (uv_access * access,
     /* [previous][next][first][last][top][bottom][index][help] */
 231                                        const access_peer * peer)
 232   {
 233     unsigned int count = 0;
 234     for (access_client * s = access_sessions; s; s = s->next)
 235       if (s->pending && (!access || s->owner == access) &&
 236           (!peer || accessPeerEqual (&s->peer, peer)))
 237         count ++;
 238     return count;
 239   }
 240 
 241 static unsigned int accessRandomDelay (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 242   {
 243     uint64_t x = uv_hrtime () ^ (++access_jitter_counter *
 244                                  UINT64_C(0x9e3779b97f4a7c15));
 245     x ^= x >> 12;
 246     x ^= x << 25;
 247     x ^= x >> 27;
 248     return 1000U + (unsigned int)((x * UINT64_C(2685821657736338717))
 249                                     % UINT64_C(2001));
 250   }
 251 
 252 uv_access * accessForClient (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 253   {
 254     if (!client || !client->data)
 255       return NULL;
 256     access_client * session = (access_client *)client->data;
 257     return session->closing ? NULL : session->owner;
 258   }
 259 
 260 //
 261 // alloc_buffer: libuv callback handler to allocate buffers for incoming data.
 262 //
 263 
 264 static void alloc_buffer (UNUSED uv_handle_t * handle, size_t suggested_size,
     /* [previous][next][first][last][top][bottom][index][help] */
 265                           uv_buf_t * buf)
 266   {
 267 /* Suppress Clang Analyzer's possible memory leak warning */
 268 #if !defined(__clang_analyzer__)
 269     if (suggested_size > UINT_MAX)
 270       suggested_size = UINT_MAX;
 271     char * base = (char *)malloc (suggested_size);
 272     *buf = uv_buf_init (base, base ? (unsigned int)suggested_size : 0);
 273 #endif /* if !defined(__clang_analyzer__) */
 274   }
 275 
 276 static void accessWriteCallback (uv_write_t * req, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
 277   {
 278     if (status < 0)
 279       {
 280         if (status == -ECONNRESET || status == -ECANCELED ||
 281             status == -EPIPE)
 282           {
 283             // This occurs when the other end disconnects; not an "error"
 284           }
 285         else
 286           {
 287             sim_warn ("accessWriteCallback status %d (%s)\r\n", -status,
 288                       xstrerror_l(-status));
 289           }
 290 
 291         // connection reset by peer
 292         if (!uv_is_closing ((uv_handle_t *)req->handle))
 293           accessCloseConnection (req->handle);
 294       }
 295 
 296 #if defined(USE_REQ_DATA)
 297     FREE (req->data);
 298 #else
 299     unsigned int nbufs = req->nbufs;
 300     uv_buf_t * bufs = req->bufs;
 301     for (unsigned int i = 0; i < nbufs; i ++)
 302       {
 303         if (bufs && bufs[i].base)
 304           {
 305             FREE (bufs[i].base);
 306             //bufp->base = NULL;
 307           }
 308         if (req->bufsml[i].base)
 309           {
 310             FREE (req->bufsml[i].base);
 311           }
 312       }
 313 #endif /* if defined(USE_REQ_DATA) */
 314 
 315     // the buf structure is copied; do not free.
 316     //sim_printf ("freeing req %p\r\n", req);
 317     FREE (req);
 318   }
 319 
 320 //
 321 // accessReadCallback: libuv read complete callback
 322 //
 323 //   Cleanup on read error.
 324 //   Forward data to appropriate handler.
 325 
 326 static void accessReadCallback (uv_stream_t* stream,
     /* [previous][next][first][last][top][bottom][index][help] */
 327                              ssize_t nread,
 328                              const uv_buf_t* buf)
 329   {
 330     access_client * session = (access_client *)stream->data;
 331     uv_access * access = session ? session->owner : NULL;
 332     if (nread < 0)
 333       {
 334         if (session && !session->closing)
 335             accessCloseConnection (stream);
 336       }
 337     else if (nread > 0 && session && !session->closing && access)
 338       {
 339         if (session->telnetp)
 340           {
 341             telnet_recv (session->telnetp, buf->base, (size_t) nread);
 342           }
 343         else
 344           {
 345             accessTelnetReadCallback ((uv_tcp_t *) stream,
 346                                       (ssize_t) nread,
 347                                       (unsigned char *) buf->base);
 348           }
 349         session->promoted_in_read = false;
 350       }
 351     if (buf->base)
 352         free (buf->base); /* X-LINTED: FREE */
 353   }
 354 
 355 //
 356 // Enable reading on connection
 357 //
 358 
 359 static void accessReadStart (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 360   {
 361     if (! client || uv_is_closing ((uv_handle_t *) client))
 362       return;
 363     uv_read_start ((uv_stream_t *) client, alloc_buffer, accessReadCallback);
 364   }
 365 
 366 // Create and start a write request
 367 
 368 static void accessStartWriteActual (uv_tcp_t * client, char * data,
     /* [previous][next][first][last][top][bottom][index][help] */
 369                                         ssize_t datalen)
 370   {
 371     if (! client || uv_is_closing ((uv_handle_t *) client) ||
 372         datalen <= 0 || (uint64_t)datalen > UINT_MAX || !data)
 373       return;
 374     access_client * session = (access_client *)client->data;
 375     if (session && !session->closing && session->state != access_active)
 376       {
 377         size_t queued = uv_stream_get_write_queue_size ((uv_stream_t *)client);
 378         if ((uint64_t)datalen > OPCON_MAX_QUEUED_OUTPUT ||
 379             queued > OPCON_MAX_QUEUED_OUTPUT - (size_t)datalen)
 380           {
 381             sim_warn ("OPCON slow reader: write queue limit exceeded\r\n");
 382             accessCloseConnection ((uv_stream_t *)client);
 383             return;
 384           }
 385       }
 386 /* Suppress Clang Analyzer's possible memory leak warning */
 387 #if !defined(__clang_analyzer__)
 388     uv_write_t * req = (uv_write_t *) malloc (sizeof (uv_write_t));
 389     if (!req)
 390       {
 391         sim_warn ("OPCON write allocation failed; dropping output\r\n");
 392         return;
 393       }
 394     // This makes sure that bufs*.base and bufsml*.base are NULL
 395     (void)memset (req, 0, sizeof (uv_write_t));
 396     uv_buf_t buf = uv_buf_init ((char *) malloc ((unsigned long) datalen),
 397                                                  (uint) datalen);
 398     if (!buf.base)
 399       {
 400         FREE (req);
 401         return;
 402       }
 403 # if defined(USE_REQ_DATA)
 404     req->data = buf.base;
 405 # endif /* if defined(USE_REQ_DATA) */
 406     memcpy (buf.base, data, (unsigned long) datalen);
 407 //sim_printf ("write %u<%s>\r\n", datalen, data);
 408     int ret = uv_write (req, (uv_stream_t *) client, & buf, 1,
 409                         accessWriteCallback);
 410 // There seems to be a race condition when Multics signals a disconnect_line;
 411 // We close the socket, but Multics is still writing its goodbye text trailing
 412 // NULs.
 413 // If the socket has been closed, write will return BADF; just ignore it.
 414     if (ret < 0)
 415       {
 416         if (ret != UV_EBADF && !sim_quiet)
 417           sim_printf ("uv_write returns %d\r\n", ret);
 418         FREE (buf.base);
 419         FREE (req);
 420       }
 421 #endif /* if !defined(__clang_analyzer__) */
 422   }
 423 
 424 void accessStartWrite (uv_tcp_t * client, char * data, ssize_t datalen)
     /* [previous][next][first][last][top][bottom][index][help] */
 425   {
 426     if ((client == NULL) || uv_is_closing ((uv_handle_t *) client))
 427       return;
 428     access_client * session = (access_client *)client->data;
 429     if (!session || session->closing || !data || datalen <= 0 ||
 430         (uint64_t)datalen > UINT_MAX)
 431       return;
 432     if (session->telnetp)
 433       telnet_send (session->telnetp, data, (size_t) datalen);
 434     else
 435       accessStartWriteActual (client, data, (ssize_t) datalen);
 436   }
 437 
 438 static void accessPutCharForce (uv_tcp_t * client, char ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 439   {
 440     //sim_putchar (ch);
 441     accessStartWrite (client, & ch, 1);
 442   }
 443 
 444 static void accessPutStrForce (uv_tcp_t * client, char * str)
     /* [previous][next][first][last][top][bottom][index][help] */
 445   {
 446     size_t l = strlen (str);
 447     accessStartWrite (client, str, (ssize_t) l);
 448   }
 449 
 450 void accessStartWriteStr (uv_tcp_t * client, char * data)
     /* [previous][next][first][last][top][bottom][index][help] */
 451   {
 452     if (data)
 453       accessStartWrite (client, data, (ssize_t) strlen (data));
 454   }
 455 
 456 static void accessLogon (access_client * session, unsigned char * buf,
     /* [previous][next][first][last][top][bottom][index][help] */
 457                          ssize_t nread)
 458   {
 459     uv_access * access = session->owner;
 460     uv_tcp_t * client = session->client;
 461     for (ssize_t nchar = 0; nchar < nread; nchar ++)
 462       {
 463         unsigned char kar = buf[nchar];
 464         // buffer too full for anything more?
 465         if (session->pwPos >= sizeof (session->pwBuffer) - 1)
 466           {
 467             // yes. Only allow \n, \r, ^H
 468             switch (kar)
 469               {
 470                 case '\b':  // backspace
 471                 case 127:   // delete
 472                   {
 473                     // remove char from line
 474                     accessPutStrForce (client, "\b \b");
 475                     // This branch requires pwPos >= PW_SIZE (128).
 476                     session->pwPos -= 1;
 477                     session->pwBuffer[session->pwPos] = 0;
 478                   }
 479                   break;
 480 
 481                 case '\n':
 482                 case '\r':
 483                   {
 484                     session->pwBuffer[session->pwPos] = 0;
 485                     goto check;
 486                   }
 487 
 488                 default:
 489                   break;
 490               } // switch kar
 491             continue; // process next character in buffer
 492           } // if buffer full
 493 
 494         if (isprint (kar))   // printable?
 495           {
 496             accessPutCharForce (client, '*');
 497             session->pwBuffer[session->pwPos++] = (char) kar;
 498             session->pwBuffer[session->pwPos] = 0;
 499           }
 500         else
 501           {
 502             switch (kar)
 503               {
 504                 case '\b':  // backspace
 505                 case 127:   // delete
 506                   {
 507                     // remove char from line
 508                     accessPutStrForce (client, "\b \b");
 509                    // remove char from buffer
 510                     if (session->pwPos > 0)
 511                       session->pwPos -= 1;
 512                     session->pwBuffer[session->pwPos] = 0;
 513                   }
 514                   break;
 515 
 516                 case '\n':
 517                 case '\r':
 518                   {
 519                     session->pwBuffer[session->pwPos] = 0;
 520                     goto check;
 521                   }
 522 
 523                 default:
 524                   break;
 525               } // switch kar
 526           } // not printable
 527       } // for nchar
 528     return;
 529 
 530 check:;
 531     char cpy[PW_SIZE + 1];
 532     char *fname = NULL, tbuffer[256];
 533     struct tm *timeinfo;
 534     memcpy (cpy, session->pwBuffer, session->pwPos);
 535     cpy[session->pwPos] = 0;
 536     trim (cpy);
 537     //sim_printf ("<%s>", cpy);
 538     session->pwPos = 0;
 539     memset (session->pwBuffer, 0, sizeof (session->pwBuffer));
 540     accessPutStrForce (client, "\r\n");
 541 
 542     char ip[INET6_ADDRSTRLEN];
 543     fname = (char *)accessPeerText (&session->peer, ip, sizeof (ip));
 544     if (strcmp (cpy, access->pw) == 0)
 545       {
 546         if (fname == NULL)
 547           sim_printf ("\r[OPC emulation: ACCESS GRANTED]\r\n");
 548         else
 549           sim_printf ("\r[OPC emulation: ACCESS GRANTED to %s]\r\n", fname);
 550         accessPutStrForce (client, "\r[OPC emulation: ACCESS GRANTED]\r\n");
 551         if (access->atime)
 552           {
 553             timeinfo = localtime(&access->atime);
 554             strftime(tbuffer, sizeof(tbuffer),
 555                     "\r[OPC emulation: Last successful login %A, %B %d, %Y at %I:%M %p %Z]\r\n", timeinfo);
 556             accessPutStrForce (client, tbuffer);
 557           }
 558         if (access->rtime)
 559           {
 560             timeinfo = localtime(&access->rtime);
 561             strftime(tbuffer, sizeof(tbuffer),
 562                     "\r[OPC emulation: Last failed attempt %A, %B %d, %Y at %I:%M %p %Z]\r\n", timeinfo);
 563             accessPutStrForce (client, tbuffer);
 564           }
 565         if (access->rcount)
 566           {
 567             char buffer[64];
 568             snprintf(buffer, sizeof(buffer), "%lu", access->rcount);
 569             accessPutStrForce (client, "\r[OPC emulation: ");
 570             accessPutStrForce (client, buffer);
 571             accessPutStrForce (client, " failed attempt(s) since last successful login]\r\n");
 572           }
 573         access->atime = time(NULL);
 574         access->rcount = 0;
 575         if (session->closing)
 576           return;
 577         accessPromote (session);
 578         session->promoted_in_read = true;
 579         if (access->connected && !session->closing)
 580           access->connected (client);
 581       }
 582     else
 583       {
 584         //accessPutStrForce ("<");
 585         //accessPutStrForce (access->pwBuffer);
 586         //accessPutStrForce (">\r\n");
 587         accessPutStrForce (client, "[OPC emulation: ACCESS DENIED]\r\n");
 588         if (fname == NULL)
 589           sim_printf ("\r[OPC emulation: ACCESS DENIED]\r\n");
 590         else
 591           sim_printf ("\r[OPC emulation: ACCESS DENIED to %s]\r\n", fname);
 592         access->rtime = time(NULL);
 593         access->rcount++;
 594         accessPutStrForce (client, "\rMultics has disconnected you\r\n");
 595         accessRateFailure (&session->peer);
 596         accessReject (session);
 597         return;
 598       }
 599   }
 600 
 601 static void accessRelease (access_client * session)
     /* [previous][next][first][last][top][bottom][index][help] */
 602   {
 603     if (--session->close_refs != 0)
 604       return;
 605     access_client ** link = &access_sessions;
 606     while (*link && *link != session)
 607       link = &(*link)->next;
 608     if (*link == session)
 609       *link = session->next;
 610     if (session->pending)
 611       {
 612         --access_pending_total;
 613         session->pending = false;
 614       }
 615     memset (session->pwBuffer, 0, sizeof (session->pwBuffer));
 616     FREE (session);
 617   }
 618 
 619 static void accessTimerCloseCallback (uv_handle_t * handle)
     /* [previous][next][first][last][top][bottom][index][help] */
 620   {
 621     access_client * session = (access_client *)handle->data;
 622     accessRelease (session);
 623   }
 624 
 625 static void accessCloseCallback (uv_handle_t * stream)
     /* [previous][next][first][last][top][bottom][index][help] */
 626   {
 627     access_client * session = (access_client *)stream->data;
 628     if (session)
 629       {
 630         // The parser may still be on the call stack when disconnect is
 631         // requested by a Telnet event.  Only release it after uv_close.
 632         if (session->telnetp)
 633           telnet_free (session->telnetp);
 634         accessRelease (session);
 635       }
 636     FREE (stream);
 637   }
 638 
 639 void accessCloseConnection (uv_stream_t* stream)
     /* [previous][next][first][last][top][bottom][index][help] */
 640   {
 641     if (!stream || uv_is_closing ((uv_handle_t *)stream))
 642       return;
 643     access_client * session = (access_client *)stream->data;
 644     if (!session || session->closing)
 645       return;
 646     uv_access * access = session->owner;
 647     const char *pname;
 648     char ip[INET6_ADDRSTRLEN];
 649     pname = accessPeerText (&session->peer, ip, sizeof (ip));
 650 
 651     // Flooded/rejected sockets have never been advertised as connected;
 652     // avoid giving unauthenticated clients an unbounded log-amplification
 653     // path. Admitted connections retain their historical disconnect logs.
 654     if (session->admitted)
 655       {
 656         if (pname == NULL)
 657           sim_printf ("\r[OPC emulation: DISCONNECT]\r\n");
 658         else
 659           sim_printf ("\r[OPC emulation: DISCONNECT %s]\r\n", pname);
 660       }
 661     session->closing = true;
 662     session->state = access_closing;
 663     if (session->telnetp)
 664       libtelnet_set_invalid (session->telnetp);
 665     if (access && access->client == (uv_tcp_t *)stream)
 666       {
 667         access->client = NULL;
 668         access->loggedOn = false;
 669         FREE (access->inBuffer);
 670         access->inSize = access->inUsed = 0;
 671       }
 672     (void)uv_read_stop (stream);
 673     if (session->timer_inited)
 674       {
 675         uv_timer_stop (&session->timer);
 676         uv_close ((uv_handle_t *)&session->timer, accessTimerCloseCallback);
 677       }
 678     uv_close ((uv_handle_t *) stream, accessCloseCallback);
 679   }
 680 
 681 static void accessTimerCallback (uv_timer_t * timer)
     /* [previous][next][first][last][top][bottom][index][help] */
 682   {
 683     access_client * session = (access_client *)timer->data;
 684     if (session && !session->closing)
 685       accessCloseConnection ((uv_stream_t *)session->client);
 686   }
 687 
 688 static void accessReject (access_client * session)
     /* [previous][next][first][last][top][bottom][index][help] */
 689   {
 690     if (session->closing)
 691       return;
 692     session->state = access_rejected;
 693     (void)uv_read_stop ((uv_stream_t *)session->client);
 694 
 695     uv_timer_stop (&session->timer);
 696     if (uv_timer_start (&session->timer, accessTimerCallback,
 697                         accessRandomDelay (), 0) != 0)
 698       accessCloseConnection ((uv_stream_t *)session->client);
 699   }
 700 
 701 static void accessShutdownCallback (uv_shutdown_t * req, UNUSED int status)
     /* [previous][next][first][last][top][bottom][index][help] */
 702   {
 703     uv_stream_t * stream = req->handle;
 704     if (!uv_is_closing ((uv_handle_t *)stream))
 705       accessCloseConnection (stream);
 706     FREE (req);
 707   }
 708 
 709 static void accessDrainOld (access_client * old)
     /* [previous][next][first][last][top][bottom][index][help] */
 710   {
 711     uv_tcp_t * client = old->client;
 712     if (old->closing)
 713       return;
 714 
 715     unsigned int draining = 0;
 716     access_client * oldest = NULL;
 717     for (access_client * s = access_sessions; s; s = s->next)
 718       if (s->owner == old->owner && s->state == access_draining)
 719         {
 720           ++draining;
 721           oldest = s;
 722         }
 723     if (draining >= OPCON_MAX_DRAINING_PER_LISTENER && oldest)
 724       accessCloseConnection ((uv_stream_t *)oldest->client);
 725 
 726     old->state = access_draining;
 727     (void)uv_read_stop ((uv_stream_t *)client);
 728     char ip[INET6_ADDRSTRLEN];
 729     const char * pname = accessPeerText (&old->peer, ip, sizeof (ip));
 730     if (pname)
 731       sim_printf ("\r[OPC emulation: BUMPED %s]\r\n", pname);
 732     else
 733       sim_printf ("\r[OPC emulation: BUMPED]\r\n");
 734     accessPutStrForce (client, "\r[OPC emulation: BUMPED]\r\n");
 735     accessPutStrForce (client, "\rMultics has disconnected you\r\n");
 736     if (old->closing) //-V547
 737       return;
 738 
 739     uv_shutdown_t * req = (uv_shutdown_t *)malloc (sizeof (*req));
 740     if (!req)
 741       {
 742         accessCloseConnection ((uv_stream_t *)client);
 743         return;
 744       }
 745     if (uv_timer_start (&old->timer, accessTimerCallback,
 746                         OPCON_DRAIN_TIMEOUT_MS, 0) != 0)
 747       {
 748         FREE (req);
 749         accessCloseConnection ((uv_stream_t *)client);
 750         return;
 751       }
 752     if (uv_shutdown (req, (uv_stream_t *)client,
 753                      accessShutdownCallback) != 0)
 754       {
 755         FREE (req);
 756         accessCloseConnection ((uv_stream_t *)client);
 757       }
 758   }
 759 
 760 static void accessPromote (access_client * session)
     /* [previous][next][first][last][top][bottom][index][help] */
 761   {
 762     uv_access * access = session->owner;
 763     uv_tcp_t * old = access->client;
 764     uv_timer_stop (&session->timer);
 765     if (session->pending)
 766       {
 767         session->pending = false;
 768         --access_pending_total;
 769       }
 770     session->state = access_active;
 771 
 772     FREE (access->inBuffer);
 773     access->inSize = access->inUsed = 0;
 774     access->client = session->client;
 775     access->loggedOn = true;
 776     if (old && old != session->client)
 777       {
 778         access_client * previous = (access_client *)old->data;
 779         if (previous)
 780           accessDrainOld (previous);
 781       }
 782   }
 783 
 784 void accessCloseAll (uv_access * access)
     /* [previous][next][first][last][top][bottom][index][help] */
 785   {
 786     for (access_client * session = access_sessions; session; )
 787       {
 788         access_client * next = session->next;
 789         if (session->owner == access && !session->closing)
 790           accessCloseConnection ((uv_stream_t *)session->client);
 791         session = next;
 792       }
 793   }
 794 
 795 unsigned int accessOpenSessions (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 796   {
 797     unsigned int count = 0;
 798     for (access_client * session = access_sessions; session;
 799          session = session->next)
 800       ++count;
 801     return count;
 802   }
 803 
 804 static void accessProcessInput (uv_access * access, unsigned char * buf,
     /* [previous][next][first][last][top][bottom][index][help] */
 805                                  ssize_t nread)
 806   {
 807     if (nread <= 0 || (uint64_t)nread > UINT_MAX - access->inSize ||
 808         (uint64_t)access->inSize + (uint64_t)nread > OPCON_MAX_PENDING_INPUT)
 809       {
 810         // The consumer indexes this FIFO using unsigned int positions.
 811         // Never wrap its size or copy outside the allocation.
 812         accessCloseConnection ((uv_stream_t *)access->client);
 813         return;
 814       }
 815     if (access->inBuffer)
 816       {
 817         //sim_warn ("inBuffer overrun\r\n");
 818         unsigned char * new =
 819           realloc (access->inBuffer,
 820                    (unsigned long) (access->inSize + nread));
 821         if (! new)
 822           {
 823             sim_warn ("OPCON input allocation failed; disconnecting\r\n");
 824             accessCloseConnection ((uv_stream_t *)access->client);
 825             return;
 826           }
 827         memcpy (new + access->inSize, buf, (unsigned long) nread);
 828         access->inSize += nread;
 829         access->inBuffer = new;
 830       }
 831     else
 832       {
 833         access->inBuffer = malloc ((unsigned long) nread);
 834         if (! access->inBuffer)
 835           {
 836             sim_warn ("OPCON input allocation failed; disconnecting\r\n");
 837             accessCloseConnection ((uv_stream_t *)access->client);
 838             return;
 839           }
 840         memcpy (access->inBuffer, buf, (unsigned long) nread);
 841         access->inSize = (uint) nread;
 842         access->inUsed = 0;
 843       }
 844 
 845     // Prevent further reading until this buffer is consumed
 846     //fnpuv_read_stop (client);
 847     //if (! client || uv_is_closing ((uv_handle_t *) client))
 848       //return;
 849     //uv_read_stop ((uv_stream_t *) client);
 850   }
 851 
 852 static void accessTelnetReadCallback (uv_tcp_t * client,
     /* [previous][next][first][last][top][bottom][index][help] */
 853                             ssize_t nread,
 854                             unsigned char * buf)
 855   {
 856     access_client * session = (access_client *)client->data;
 857     if (!session || session->closing || session->promoted_in_read)
 858       return;
 859     if (session->state == access_active &&
 860         session->owner->client == client)
 861       accessProcessInput (session->owner, buf, nread);
 862     else if (session->state == access_authenticating)
 863       accessLogon (session, buf, nread);
 864   }
 865 
 866 static void evHandler (UNUSED telnet_t *telnet, telnet_event_t *event,
     /* [previous][next][first][last][top][bottom][index][help] */
 867                        void *user_data)
 868   {
 869     uv_tcp_t * client = (uv_tcp_t *) user_data;
 870 
 871     if (! telnet || ! libtelnet_is_valid(telnet)) {
 872       if (! sim_quiet) {
 873         sim_warn ("evHandler telnet status invalid\r\n");
 874       }
 875       return;
 876     }
 877 
 878     switch (event->type)
 879       {
 880         case TELNET_EV_DATA:
 881           {
 882             accessTelnetReadCallback (client, (ssize_t) event->data.size,
 883                             (unsigned char *)event->data.buffer);
 884           }
 885           break;
 886 
 887         case TELNET_EV_SEND:
 888           {
 889             accessStartWriteActual (client, (char *) event->data.buffer,
 890                                         (ssize_t) event->data.size);
 891           }
 892           break;
 893 
 894         case TELNET_EV_DO:
 895           {
 896             if (event->neg.telopt == TELNET_TELOPT_BINARY)
 897               {
 898                 // DO Binary
 899               }
 900             else if (event->neg.telopt == TELNET_TELOPT_SGA)
 901               {
 902                 // DO Suppress Go Ahead
 903               }
 904             else if (event->neg.telopt == TELNET_TELOPT_ECHO)
 905               {
 906                 // DO Suppress Echo
 907               }
 908             else
 909               {
 910                 if (! sim_quiet) {
 911                   sim_printf ("evHandler DO %d\r\n", event->neg.telopt);
 912                 }
 913               }
 914           }
 915           break;
 916 
 917         case TELNET_EV_DONT:
 918           {
 919             if (! sim_quiet) {
 920               sim_printf ("evHandler DONT %d\r\n", event->neg.telopt);
 921             }
 922           }
 923           break;
 924 
 925         case TELNET_EV_WILL:
 926           {
 927             if (! sim_quiet) {
 928               if (event->neg.telopt != 3) {
 929                 sim_printf ("evHandler WILL %d\r\n", event->neg.telopt);
 930               }
 931             }
 932           }
 933           break;
 934 
 935         case TELNET_EV_WONT:
 936           {
 937             if (! sim_quiet) {
 938               sim_printf ("evHandler WONT %d\r\n", event->neg.telopt);
 939             }
 940           }
 941           break;
 942 
 943         case TELNET_EV_ERROR:
 944           {
 945             if (! sim_quiet) {
 946               sim_warn ("libtelnet evHandler error <%s>\r\n", event->error.msg);
 947             }
 948           }
 949           break;
 950 
 951         case TELNET_EV_IAC:
 952           {
 953             if (event->iac.cmd == 243 || // BRK
 954                 event->iac.cmd == 244)   // IP
 955               {
 956                 if (! sim_quiet) {
 957                   sim_warn ("libtelnet dropping unassociated BRK or IP\r\n");
 958                 }
 959               }
 960             else
 961               if (! sim_quiet) {
 962                 if (event->iac.cmd != 241) {
 963                     sim_warn ("libtelnet unhandled IAC event %d\r\n", event->iac.cmd);
 964                 }
 965               }
 966           }
 967           break;
 968 
 969         default:
 970           if (! sim_quiet) {
 971             sim_printf ("evHandler: unhandled event %d\r\n", event->type);
 972           }
 973           break;
 974       }
 975 
 976   }
 977 
 978 static const telnet_telopt_t my_telopts[] =
 979   {
 980     { TELNET_TELOPT_SGA,       TELNET_WILL, TELNET_DO   },
 981     { TELNET_TELOPT_ECHO,      TELNET_WILL, TELNET_DONT },
 982   //{ TELNET_TELOPT_TTYPE,     TELNET_WONT, TELNET_DONT },
 983  //   { TELNET_TELOPT_BINARY,    TELNET_WILL, TELNET_DO   },
 984     { TELNET_TELOPT_BINARY,    TELNET_WONT, TELNET_DONT   },
 985   //{ TELNET_TELOPT_NAWS,      TELNET_WONT, TELNET_DONT },
 986     { -1, 0, 0 }
 987   };
 988 
 989 static void * accessTelnetConnect (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 990   {
 991     void * p = (void *) telnet_init (my_telopts, evHandler, 0, client);
 992     if (!p)
 993       {
 994         (void)fprintf(stderr, "\rtelnet_init failed at %s[%s:%d]\r\n",
 995                       __func__, __FILE__, __LINE__);
 996         return NULL;
 997       }
 998     const telnet_telopt_t * q = my_telopts;
 999     while (q->telopt != -1)
1000       {
1001         telnet_negotiate (p, q->us, (unsigned char) q->telopt);
1002         q ++;
1003       }
1004     return p;
1005   }
1006 
1007 //
1008 // Connection callback handler
1009 //
1010 
1011 static void accessListenerCloseCallback (uv_handle_t * handle)
     /* [previous][next][first][last][top][bottom][index][help] */
1012   {
1013     uv_access * access = (uv_access *)handle->data;
1014     if (access)
1015       access->open = false;
1016   }
1017 
1018 static void onNewAccess (uv_stream_t * server, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
1019   {
1020     if (status < 0)
1021       {
1022         (void)fprintf (stderr, "\r[OPC emulation: new connection error %s]\r\n", uv_strerror(status));
1023         // error!
1024         return;
1025       }
1026 
1027     uv_access * access = (uv_access *) server->data;
1028 
1029     uv_tcp_t * client = (uv_tcp_t *) malloc (sizeof (uv_tcp_t));
1030     if (!client)
1031       {
1032         sim_warn ("OPCON client allocation failed; rejecting connection\r\n");
1033         return;
1034       }
1035     int init_rc = uv_tcp_init (access->loop, client);
1036     if (init_rc < 0)
1037       {
1038         sim_warn ("OPCON TCP initialization failed: %s\r\n", uv_strerror (init_rc));
1039         FREE (client);
1040         return;
1041       }
1042     client->data = NULL;
1043     if (uv_accept (server, (uv_stream_t *) client) == 0)
1044       {
1045         access_client * session = (access_client *)calloc (1, sizeof (*session));
1046         if (!session)
1047           {
1048             uv_close ((uv_handle_t *)client, accessCloseCallback);
1049             return;
1050           }
1051         session->owner = access;
1052         session->client = client;
1053         session->close_refs = 1;
1054         session->state = access_authenticating;
1055         client->data = session;
1056         if (!accessGetPeer (client, &session->peer))
1057           {
1058             accessCloseConnection ((uv_stream_t *)client);
1059             return;
1060           }
1061 
1062         if (access->pw[0] &&
1063             (access_pending_total >= OPCON_PENDING_TOTAL ||
1064              accessPendingFor (access, NULL) >= OPCON_PENDING_PER_LISTENER ||
1065              accessPendingFor (NULL, &session->peer) >=
1066                                                     OPCON_PENDING_PER_ADDRESS ||
1067              !accessRatePermits (&session->peer)))
1068           {
1069             accessCloseConnection ((uv_stream_t *)client);
1070             return;
1071           }
1072 
1073         int timer_rc = uv_timer_init (access->loop, &session->timer);
1074         if (timer_rc != 0)
1075           {
1076             accessCloseConnection ((uv_stream_t *)client);
1077             return;
1078           }
1079         session->timer_inited = true;
1080         session->timer.data = session;
1081         session->close_refs ++;
1082         session->admitted = true;
1083         session->next = access_sessions;
1084         access_sessions = session;
1085         if (access->pw[0])
1086           {
1087             session->pending = true;
1088             access_pending_total ++;
1089             if (uv_timer_start (&session->timer, accessTimerCallback,
1090                                 OPCON_AUTH_TIMEOUT_MS, 0) != 0)
1091               {
1092                 accessCloseConnection ((uv_stream_t *)client);
1093                 return;
1094               }
1095           }
1096 
1097         (void)uv_tcp_nodelay (client, 1);
1098         char ip[INET6_ADDRSTRLEN];
1099         const char * pname = accessPeerText (&session->peer, ip, sizeof (ip));
1100         if (pname)
1101           sim_printf ("\r[OPC emulation: CONNECT %s]\r\n", pname);
1102         else
1103           sim_printf ("\r[OPC emulation: CONNECT]\r\n");
1104 
1105         if (access->useTelnet)
1106           {
1107             session->telnetp = accessTelnetConnect (client);
1108             if (!session->telnetp)
1109               {
1110                  sim_warn ("ltnConnect failed\r\n");
1111                  accessCloseConnection ((uv_stream_t *)client);
1112                  return;
1113               }
1114           }
1115         if (session->closing)
1116           return;
1117         if (!access->pw[0])
1118           {
1119             accessPromote (session);
1120             if (access->connected && !session->closing)
1121               access->connected (client);
1122           }
1123         else if (access->connectPrompt)
1124           access->connectPrompt (client);
1125         if (!session->closing)
1126           accessReadStart (client);
1127       }
1128     else
1129       {
1130         uv_close ((uv_handle_t *) client, accessCloseCallback);
1131       }
1132   }
1133 
1134 void uv_open_access (uv_access * access)
     /* [previous][next][first][last][top][bottom][index][help] */
1135   {
1136     if (access->open == true)
1137       {
1138         sim_printf ("\r[OPC emulation: OPC already initialized]\r\n");
1139       }
1140 
1141     if (! access->port)
1142       {
1143         //sim_printf ("access port disabled\r\n");
1144         return;
1145       }
1146 
1147     if (! access->loop)
1148       access->loop = uv_default_loop ();
1149 
1150     // Do we already have a listening port (ie not first time)?
1151     if (access->open)
1152       return;
1153 
1154     int r = uv_tcp_init (access->loop, & access->server);
1155     if (r < 0)
1156       return;
1157     access->open = true;
1158     access->server.data = (void *) access;
1159     struct sockaddr_in addr;
1160     r = uv_ip4_addr (access->address ? access->address : "0.0.0.0",
1161                      access->port, & addr);
1162     if (r == 0)
1163       r = uv_tcp_bind (& access->server, (const struct sockaddr *) & addr, 0);
1164 #define DEFAULT_BACKLOG 1024
1165     if (r == 0)
1166       r = uv_listen ((uv_stream_t *) & access->server,
1167                      DEFAULT_BACKLOG, onNewAccess);
1168     if (r)
1169      {
1170         (void)fprintf (stderr, "\r[OPC emulation: listen error: %s:%ld: %s]\r\n",
1171                        access->address ? access->address : "0.0.0.0",
1172                        (long) access->port, uv_strerror(r));
1173         // The embedded listener must not be reused before libuv has
1174         // completed its close. Keep 'open' set until that callback.
1175         uv_close ((uv_handle_t *)&access->server, accessListenerCloseCallback);
1176         return;
1177       }
1178     if (access->address != NULL)
1179       sim_printf ("\r[OPC emulation: TELNET server listening on %s:%ld]\r\n",
1180                   access->address, (long) access->port);
1181     else
1182       sim_printf ("\r[OPC emulation: TELNET server listening on 0.0.0.0:%ld]\r\n",
1183                   (long) access->port);
1184   }
1185 
1186 #if !defined(QUIET_UNUSED)
1187 void accessPutChar (uv_access * access, char ch)
     /* [previous][next][first][last][top][bottom][index][help] */
1188   {
1189     //sim_putchar (ch);
1190     if (access->loggedOn)
1191       accessStartWrite (access->client, & ch, 1);
1192   }
1193 #endif /* if !defined(QUIET_UNUSED) */
1194 
1195 int accessGetChar (uv_access * access)
     /* [previous][next][first][last][top][bottom][index][help] */
1196   {
1197     // The connection could have closed when we were not looking
1198     if (! access->client)
1199       {
1200         if (access->inBuffer)
1201           FREE (access->inBuffer);
1202         access->inBuffer = NULL;
1203         access->inSize = 0;
1204         access->inUsed = 0;
1205         return SCPE_OK;
1206       }
1207 
1208     if (access->inBuffer && access->inUsed < access->inSize)
1209        {
1210          unsigned char c = access->inBuffer[access->inUsed ++];
1211          if (access->inUsed >= access->inSize)
1212            {
1213              FREE (access->inBuffer);
1214              access->inBuffer = NULL;
1215              access->inSize = 0;
1216              access->inUsed = 0;
1217              // The connection could have been closed when we weren't looking
1218              //if (access->client)
1219                //fnpuv_read_start (access->client);
1220            }
1221          return (int) c + SCPE_KFLAG;
1222        }
1223     return SCPE_OK;
1224 
1225   }
1226 
1227 #if !defined(QUIET_UNUSED)
1228 void accessPutStr (uv_access * access, char * str)
     /* [previous][next][first][last][top][bottom][index][help] */
1229   {
1230     size_t l = strlen (str);
1231     //for (size_t i = 0; i < l; i ++)
1232       //sim_putchar (str[i]);
1233     if (access->loggedOn)
1234       accessStartWrite (access->client, str, (ssize_t) l);
1235   }
1236 #endif /* if !defined(QUIET_UNUSED) */

/* [previous][next][first][last][top][bottom][index][help] */