root/src/dps8/uvutil.c

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

DEFINITIONS

This source file includes following definitions.
  1. alloc_buffer
  2. accessWriteCallback
  3. accessReadCallback
  4. accessReadStart
  5. accessStartWriteActual
  6. accessStartWrite
  7. accessPutCharForce
  8. accessPutStrForce
  9. accessStartWriteStr
  10. accessLogon
  11. accessCloseCallback
  12. accessCloseConnection
  13. accessProcessInput
  14. accessTelnetReadCallback
  15. evHandler
  16. accessTelnetConnect
  17. onNewAccess
  18. uv_open_access
  19. accessPutChar
  20. accessGetChar
  21. 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 <signal.h>
  23 #include "dps8.h"
  24 #include "dps8_sys.h"
  25 #include "dps8_cpu.h"
  26 #include "dps8_utils.h"
  27 #include "libtelnet.h"
  28 #include "uvutil.h"
  29 
  30 #if defined(NO_LOCALE)
  31 # define xstrerror_l strerror
  32 #endif
  33 
  34 #if defined(FREE)
  35 # undef FREE
  36 #endif /* if defined(FREE) */
  37 #define FREE(p) do  \
  38   {                 \
  39     free((p));      \
  40     (p) = NULL;     \
  41   } while(0)
  42 
  43 #define USE_REQ_DATA
  44 
  45 static void accessTelnetReadCallback (uv_tcp_t * client,
  46                             ssize_t nread,
  47                             unsigned char * buf);
  48 
  49 //
  50 // alloc_buffer: libuv callback handler to allocate buffers for incoming data.
  51 //
  52 
  53 static void alloc_buffer (UNUSED uv_handle_t * handle, size_t suggested_size,
     /* [previous][next][first][last][top][bottom][index][help] */
  54                           uv_buf_t * buf)
  55   {
  56 /* Suppress Clang Analyzer's possible memory leak warning */
  57 #if !defined(__clang_analyzer__)
  58     * buf = uv_buf_init ((char *) malloc (suggested_size),
  59                          (uint) suggested_size);
  60 #endif /* if !defined(__clang_analyzer__) */
  61   }
  62 
  63 static void accessWriteCallback (uv_write_t * req, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
  64   {
  65     if (status < 0)
  66       {
  67         if (status == -ECONNRESET || status == -ECANCELED ||
  68             status == -EPIPE)
  69           {
  70             // This occurs when the other end disconnects; not an "error"
  71           }
  72         else
  73           {
  74             sim_warn ("accessWriteCallback status %d (%s)\r\n", -status,
  75                       xstrerror_l(-status));
  76           }
  77 
  78         // connection reset by peer
  79         accessCloseConnection (req->handle);
  80       }
  81 
  82 #if defined(USE_REQ_DATA)
  83     FREE (req->data);
  84 #else
  85     unsigned int nbufs = req->nbufs;
  86     uv_buf_t * bufs = req->bufs;
  87     for (unsigned int i = 0; i < nbufs; i ++)
  88       {
  89         if (bufs && bufs[i].base)
  90           {
  91             FREE (bufs[i].base);
  92             //bufp->base = NULL;
  93           }
  94         if (req->bufsml[i].base)
  95           {
  96             FREE (req->bufsml[i].base);
  97           }
  98       }
  99 #endif /* if defined(USE_REQ_DATA) */
 100 
 101     // the buf structure is copied; do not free.
 102     //sim_printf ("freeing req %p\r\n", req);
 103     FREE (req);
 104   }
 105 
 106 //
 107 // accessReadCallback: libuv read complete callback
 108 //
 109 //   Cleanup on read error.
 110 //   Forward data to appropriate handler.
 111 
 112 static void accessReadCallback (uv_stream_t* stream,
     /* [previous][next][first][last][top][bottom][index][help] */
 113                              ssize_t nread,
 114                              const uv_buf_t* buf)
 115   {
 116     uv_access * access = (uv_access *) stream->data;
 117     if (nread < 0)
 118       {
 119         //if (nread == UV_EOF)
 120           {
 121             accessCloseConnection (stream);
 122           }
 123       }
 124     else if (nread > 0)
 125       {
 126         if (access->telnetp)
 127           {
 128             telnet_recv (access->telnetp, buf->base, (size_t) nread);
 129           }
 130         else
 131           {
 132             accessTelnetReadCallback ((uv_tcp_t *) stream,
 133                                       (ssize_t) nread,
 134                                       (unsigned char *) buf->base);
 135           }
 136       }
 137     if (buf->base)
 138         free (buf->base); /* X-LINTED: FREE */
 139   }
 140 
 141 //
 142 // Enable reading on connection
 143 //
 144 
 145 static void accessReadStart (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 146   {
 147     if (! client || uv_is_closing ((uv_handle_t *) client))
 148       return;
 149     uv_read_start ((uv_stream_t *) client, alloc_buffer, accessReadCallback);
 150   }
 151 
 152 // Create and start a write request
 153 
 154 static void accessStartWriteActual (uv_tcp_t * client, char * data,
     /* [previous][next][first][last][top][bottom][index][help] */
 155                                         ssize_t datalen)
 156   {
 157     if (! client || uv_is_closing ((uv_handle_t *) client))
 158       return;
 159 /* Suppress Clang Analyzer's possible memory leak warning */
 160 #if !defined(__clang_analyzer__)
 161     uv_write_t * req = (uv_write_t *) malloc (sizeof (uv_write_t));
 162     if (!req)
 163       {
 164         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 165                        __func__, __FILE__, __LINE__);
 166 # if defined(USE_BACKTRACE)
 167 #  if defined(SIGUSR2)
 168         (void)raise(SIGUSR2);
 169         /*NOTREACHED*/ /* unreachable */
 170 #  endif /* if defined(SIGUSR2) */
 171 # endif /* if defined(USE_BACKTRACE) */
 172         abort();
 173       }
 174     // This makes sure that bufs*.base and bufsml*.base are NULL
 175     (void)memset (req, 0, sizeof (uv_write_t));
 176     uv_buf_t buf = uv_buf_init ((char *) malloc ((unsigned long) datalen),
 177                                                  (uint) datalen);
 178 # if defined(USE_REQ_DATA)
 179     req->data = buf.base;
 180 # endif /* if defined(USE_REQ_DATA) */
 181     memcpy (buf.base, data, (unsigned long) datalen);
 182 //sim_printf ("write %u<%s>\r\n", datalen, data);
 183     int ret = uv_write (req, (uv_stream_t *) client, & buf, 1,
 184                         accessWriteCallback);
 185 // There seems to be a race condition when Multics signals a disconnect_line;
 186 // We close the socket, but Multics is still writing its goodbye text trailing
 187 // NULs.
 188 // If the socket has been closed, write will return BADF; just ignore it.
 189     if (ret < 0 && ret != -EBADF)
 190       sim_printf ("uv_write returns %d\r\n", ret);
 191 #endif /* if !defined(__clang_analyzer__) */
 192   }
 193 
 194 void accessStartWrite (uv_tcp_t * client, char * data, ssize_t datalen)
     /* [previous][next][first][last][top][bottom][index][help] */
 195   {
 196     if ((client == NULL) || uv_is_closing ((uv_handle_t *) client))
 197       return;
 198     uv_access * access = (uv_access *) client->data;
 199     if (access->telnetp)
 200       telnet_send (access->telnetp, data, (size_t) datalen);
 201     else
 202       accessStartWriteActual (client, data, (ssize_t) datalen);
 203   }
 204 
 205 static void accessPutCharForce (uv_access * access, char ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 206   {
 207     //sim_putchar (ch);
 208     accessStartWrite (access->client, & ch, 1);
 209   }
 210 
 211 static void accessPutStrForce (uv_access * access, char * str)
     /* [previous][next][first][last][top][bottom][index][help] */
 212   {
 213     size_t l = strlen (str);
 214     accessStartWrite (access->client, str, (ssize_t) l);
 215   }
 216 
 217 void accessStartWriteStr (uv_tcp_t * client, char * data)
     /* [previous][next][first][last][top][bottom][index][help] */
 218   {
 219     accessStartWrite (client, data, (ssize_t) strlen (data));
 220   }
 221 
 222 static void accessLogon (uv_access * access, unsigned char * buf, ssize_t nread)
     /* [previous][next][first][last][top][bottom][index][help] */
 223   {
 224     for (ssize_t nchar = 0; nchar < nread; nchar ++)
 225       {
 226         unsigned char kar = buf[nchar];
 227         // buffer too full for anything more?
 228         if ((unsigned long) access->pwPos >= sizeof (access->pwBuffer) - 1)
 229           {
 230             // yes. Only allow \n, \r, ^H
 231             switch (kar)
 232               {
 233                 case '\b':  // backspace
 234                 case 127:   // delete
 235                   {
 236                     // remove char from line
 237                     accessPutStrForce (access, "\b \b");
 238                     access->pwBuffer[access->pwPos] = 0;     // remove char from buffer
 239                     if (access->pwPos > 0)
 240                       access->pwPos -= 1;             // back up buffer pointer
 241                   }
 242                   break;
 243 
 244                 case '\n':
 245                 case '\r':
 246                   {
 247                     access->pwBuffer[access->pwPos] = 0;
 248                     goto check;
 249                   }
 250 
 251                 default:
 252                   break;
 253               } // switch kar
 254             continue; // process next character in buffer
 255           } // if buffer full
 256 
 257         if (isprint (kar))   // printable?
 258           {
 259             accessPutCharForce (access, '*');
 260             access->pwBuffer[access->pwPos++] = (char) kar;
 261             access->pwBuffer[access->pwPos] = 0;
 262           }
 263         else
 264           {
 265             switch (kar)
 266               {
 267                 case '\b':  // backspace
 268                 case 127:   // delete
 269                   {
 270                     // remove char from line
 271                     accessPutStrForce (access, "\b \b");
 272                    // remove char from buffer
 273                     access->pwBuffer[access->pwPos] = 0;
 274                     if (access->pwPos > 0)
 275                       access->pwPos -= 1;   // back up buffer pointer
 276                   }
 277                   break;
 278 
 279                 case '\n':
 280                 case '\r':
 281                   {
 282                     access->pwBuffer[access->pwPos] = 0;
 283                     goto check;
 284                   }
 285 
 286                 default:
 287                   break;
 288               } // switch kar
 289           } // not printable
 290       } // for nchar
 291     return;
 292 
 293 check:;
 294     char cpy[access->pwPos + 1];
 295     char *fname = NULL, tbuffer[256];
 296     struct tm *timeinfo;
 297     memcpy (cpy, access->pwBuffer, (unsigned long) access->pwPos);
 298     cpy[access->pwPos] = 0;
 299     trim (cpy);
 300     //sim_printf ("<%s>", cpy);
 301     access->pwPos = 0;
 302     accessPutStrForce (access, "\r\n");
 303 
 304     struct sockaddr cname;
 305     int cnamelen = sizeof (cname);
 306     int ret = uv_tcp_getpeername (access->client, & cname, & cnamelen);
 307     if (ret > -1)
 308       {
 309         struct sockaddr_in * p = (struct sockaddr_in *) & cname;
 310         fname = inet_ntoa (p->sin_addr);
 311       }
 312     if (strcmp (cpy, access->pw) == 0)
 313       {
 314         if (fname == NULL)
 315           sim_printf ("\r[OPC emulation: ACCESS GRANTED]\r\n");
 316         else
 317           sim_printf ("\r[OPC emulation: ACCESS GRANTED to %s]\r\n", fname);
 318         accessPutStrForce (access, "\r[OPC emulation: ACCESS GRANTED]\r\n");
 319         access->loggedOn = true;
 320         if (access->atime)
 321           {
 322             timeinfo = localtime(&access->atime);
 323             strftime(tbuffer, sizeof(tbuffer),
 324                     "\r[OPC emulation: Last successful login %A, %B %d, %Y at %I:%M %p %Z]\r\n", timeinfo);
 325             accessPutStrForce (access, tbuffer);
 326           }
 327         if (access->rtime)
 328           {
 329             timeinfo = localtime(&access->rtime);
 330             strftime(tbuffer, sizeof(tbuffer),
 331                     "\r[OPC emulation: Last failed attempt %A, %B %d, %Y at %I:%M %p %Z]\r\n", timeinfo);
 332             accessPutStrForce (access, tbuffer);
 333           }
 334         if (access->rcount)
 335           {
 336             char buffer[64];
 337             snprintf(buffer, sizeof(buffer), "%lu", access->rcount);
 338             accessPutStrForce (access, "\r[OPC emulation: ");
 339             accessPutStrForce (access, buffer);
 340             accessPutStrForce (access, " failed attempt(s) since last successful login]\r\n");
 341           }
 342         access->atime = time(NULL);
 343         access->rcount = 0;
 344         if (access->connected)
 345             access->connected (access->client);
 346       }
 347     else
 348       {
 349         //accessPutStrForce ("<");
 350         //accessPutStrForce (access->pwBuffer);
 351         //accessPutStrForce (">\r\n");
 352         accessPutStrForce (access, "[OPC emulation: ACCESS DENIED]\r\n");
 353         if (fname == NULL)
 354           sim_printf ("\r[OPC emulation: ACCESS DENIED]\r\n");
 355         else
 356           sim_printf ("\r[OPC emulation: ACCESS DENIED to %s]\r\n", fname);
 357         access->rtime = time(NULL);
 358         access->rcount++;
 359         accessPutStrForce (access, "\rMultics has disconnected you\r\n");
 360         accessCloseConnection ((uv_stream_t *) access->client);
 361         return;
 362       }
 363   }
 364 
 365 static void accessCloseCallback (uv_handle_t * stream)
     /* [previous][next][first][last][top][bottom][index][help] */
 366   {
 367     FREE (stream);
 368     //access->client = NULL;
 369   }
 370 
 371 void accessCloseConnection (uv_stream_t* stream)
     /* [previous][next][first][last][top][bottom][index][help] */
 372   {
 373     uv_access * access = (uv_access *) stream->data;
 374     char *pname = NULL;
 375     struct sockaddr name;
 376     int namelen = sizeof (name);
 377     int ret = uv_tcp_getpeername (access->client, & name, & namelen);
 378     if (ret > -1)
 379       {
 380         struct sockaddr_in * p = (struct sockaddr_in *) & name;
 381         pname = inet_ntoa (p -> sin_addr);
 382       }
 383 
 384     if (pname == NULL)
 385       sim_printf ("\r[OPC emulation: DISCONNECT]\r\n");
 386     else
 387       sim_printf ("\r[OPC emulation: DISCONNECT %s]\r\n", pname);
 388     libtelnet_set_invalid(access->telnetp);
 389     // Clean up allocated data
 390     if (access->telnetp)
 391       {
 392         libtelnet_set_invalid(access->telnetp);
 393         telnet_free (access->telnetp);
 394         access->telnetp = NULL;
 395       }
 396     if (! uv_is_closing ((uv_handle_t *) stream))
 397       uv_close ((uv_handle_t *) stream, accessCloseCallback);
 398     access->client = NULL;
 399   }
 400 
 401 static void accessProcessInput (uv_access * access, unsigned char * buf,
     /* [previous][next][first][last][top][bottom][index][help] */
 402                                  ssize_t nread)
 403   {
 404     if (access->inBuffer)
 405       {
 406         //sim_warn ("inBuffer overrun\r\n");
 407         unsigned char * new =
 408           realloc (access->inBuffer,
 409                    (unsigned long) (access->inSize + nread));
 410         if (! new)
 411           {
 412             (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 413                            __func__, __FILE__, __LINE__);
 414 #if defined(USE_BACKTRACE)
 415 # if defined(SIGUSR2)
 416             (void)raise(SIGUSR2);
 417             /*NOTREACHED*/ /* unreachable */
 418 # endif /* if defined(SIGUSR2) */
 419 #endif /* if defined(USE_BACKTRACE) */
 420             abort();
 421           }
 422         memcpy (new + access->inSize, buf, (unsigned long) nread);
 423         access->inSize += nread;
 424         access->inBuffer = new;
 425       }
 426     else
 427       {
 428         access->inBuffer = malloc ((unsigned long) nread);
 429         if (! access->inBuffer)
 430           {
 431             (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 432                            __func__, __FILE__, __LINE__);
 433 #if defined(USE_BACKTRACE)
 434 # if defined(SIGUSR2)
 435             (void)raise(SIGUSR2);
 436             /*NOTREACHED*/ /* unreachable */
 437 # endif /* if defined(SIGUSR2) */
 438 #endif /* if defined(USE_BACKTRACE) */
 439             abort();
 440           }
 441         memcpy (access->inBuffer, buf, (unsigned long) nread);
 442         access->inSize = (uint) nread;
 443         access->inUsed = 0;
 444       }
 445 
 446     // Prevent further reading until this buffer is consumed
 447     //fnpuv_read_stop (client);
 448     //if (! client || uv_is_closing ((uv_handle_t *) client))
 449       //return;
 450     //uv_read_stop ((uv_stream_t *) client);
 451   }
 452 
 453 static void accessTelnetReadCallback (uv_tcp_t * client,
     /* [previous][next][first][last][top][bottom][index][help] */
 454                             ssize_t nread,
 455                             unsigned char * buf)
 456   {
 457     uv_access * access = (uv_access *) client->data;
 458     if (access)
 459       {
 460         if (access->loggedOn)
 461           accessProcessInput (access, buf, nread);
 462         else
 463           accessLogon (access, buf, nread);
 464       }
 465   }
 466 
 467 static void evHandler (UNUSED telnet_t *telnet, telnet_event_t *event,
     /* [previous][next][first][last][top][bottom][index][help] */
 468                        void *user_data)
 469   {
 470     uv_tcp_t * client = (uv_tcp_t *) user_data;
 471 
 472     if (! telnet || ! libtelnet_is_valid(telnet)) {
 473       if (! sim_quiet) {
 474         sim_warn ("evHandler telnet status invalid\r\n");
 475       }
 476       return;
 477     }
 478 
 479     switch (event->type)
 480       {
 481         case TELNET_EV_DATA:
 482           {
 483             accessTelnetReadCallback (client, (ssize_t) event->data.size,
 484                             (unsigned char *)event->data.buffer);
 485           }
 486           break;
 487 
 488         case TELNET_EV_SEND:
 489           {
 490             accessStartWriteActual (client, (char *) event->data.buffer,
 491                                         (ssize_t) event->data.size);
 492           }
 493           break;
 494 
 495         case TELNET_EV_DO:
 496           {
 497             if (event->neg.telopt == TELNET_TELOPT_BINARY)
 498               {
 499                 // DO Binary
 500               }
 501             else if (event->neg.telopt == TELNET_TELOPT_SGA)
 502               {
 503                 // DO Suppress Go Ahead
 504               }
 505             else if (event->neg.telopt == TELNET_TELOPT_ECHO)
 506               {
 507                 // DO Suppress Echo
 508               }
 509             else
 510               {
 511                 if (! sim_quiet) {
 512                   sim_printf ("evHandler DO %d\r\n", event->neg.telopt);
 513                 }
 514               }
 515           }
 516           break;
 517 
 518         case TELNET_EV_DONT:
 519           {
 520             if (! sim_quiet) {
 521               sim_printf ("evHandler DONT %d\r\n", event->neg.telopt);
 522             }
 523           }
 524           break;
 525 
 526         case TELNET_EV_WILL:
 527           {
 528             if (! sim_quiet) {
 529               if (event->neg.telopt != 3) {
 530                 sim_printf ("evHandler WILL %d\r\n", event->neg.telopt);
 531               }
 532             }
 533           }
 534           break;
 535 
 536         case TELNET_EV_WONT:
 537           {
 538             if (! sim_quiet) {
 539               sim_printf ("evHandler WONT %d\r\n", event->neg.telopt);
 540             }
 541           }
 542           break;
 543 
 544         case TELNET_EV_ERROR:
 545           {
 546             if (! sim_quiet) {
 547               sim_warn ("libtelnet evHandler error <%s>\r\n", event->error.msg);
 548             }
 549           }
 550           break;
 551 
 552         case TELNET_EV_IAC:
 553           {
 554             if (event->iac.cmd == 243 || // BRK
 555                 event->iac.cmd == 244)   // IP
 556               {
 557                 if (! sim_quiet) {
 558                   sim_warn ("libtelnet dropping unassociated BRK or IP\r\n");
 559                 }
 560               }
 561             else
 562               if (! sim_quiet) {
 563                 if (event->iac.cmd != 241) {
 564                     sim_warn ("libtelnet unhandled IAC event %d\r\n", event->iac.cmd);
 565                 }
 566               }
 567           }
 568           break;
 569 
 570         default:
 571           if (! sim_quiet) {
 572             sim_printf ("evHandler: unhandled event %d\r\n", event->type);
 573           }
 574           break;
 575       }
 576 
 577   }
 578 
 579 static const telnet_telopt_t my_telopts[] =
 580   {
 581     { TELNET_TELOPT_SGA,       TELNET_WILL, TELNET_DO   },
 582     { TELNET_TELOPT_ECHO,      TELNET_WILL, TELNET_DONT },
 583   //{ TELNET_TELOPT_TTYPE,     TELNET_WONT, TELNET_DONT },
 584  //   { TELNET_TELOPT_BINARY,    TELNET_WILL, TELNET_DO   },
 585     { TELNET_TELOPT_BINARY,    TELNET_WONT, TELNET_DONT   },
 586   //{ TELNET_TELOPT_NAWS,      TELNET_WONT, TELNET_DONT },
 587     { -1, 0, 0 }
 588   };
 589 
 590 static void * accessTelnetConnect (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 591   {
 592     void * p = (void *) telnet_init (my_telopts, evHandler, 0, client);
 593     if (!p)
 594       {
 595         (void)fprintf(stderr, "\rtelnet_init failed at %s[%s:%d]\r\n",
 596                       __func__, __FILE__, __LINE__);
 597         return NULL;
 598       }
 599     const telnet_telopt_t * q = my_telopts;
 600     while (q->telopt != -1)
 601       {
 602         telnet_negotiate (p, q->us, (unsigned char) q->telopt);
 603         q ++;
 604       }
 605     return p;
 606   }
 607 
 608 //
 609 // Connection callback handler
 610 //
 611 
 612 static void onNewAccess (uv_stream_t * server, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
 613   {
 614     if (status < 0)
 615       {
 616         (void)fprintf (stderr, "\r[OPC emulation: new connection error %s]\r\n", uv_strerror(status));
 617         // error!
 618         return;
 619       }
 620 
 621     uv_access * access = (uv_access *) server->data;
 622 
 623     uv_tcp_t * client = (uv_tcp_t *) malloc (sizeof (uv_tcp_t));
 624     if (!client)
 625       {
 626         (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 627                       __func__, __FILE__, __LINE__);
 628 #if defined(USE_BACKTRACE)
 629 # if defined(SIGUSR2)
 630         (void)raise(SIGUSR2);
 631         /*NOTREACHED*/ /* unreachable */
 632 # endif /* if defined(SIGUSR2) */
 633 #endif /* if defined(USE_BACKTRACE) */
 634         abort();
 635       }
 636     uv_tcp_init (access->loop, client);
 637     client->data = server->data;
 638     if (uv_accept (server, (uv_stream_t *) client) == 0)
 639       {
 640         // Only a single connection at a time
 641         if (access->client)
 642           {
 643             access->loggedOn = false;
 644 
 645             char *pname = NULL;
 646             struct sockaddr lname;
 647             int lnamelen = sizeof (lname);
 648             int ret = uv_tcp_getpeername (access->client, & lname, & lnamelen);
 649             if (ret > -1)
 650               {
 651                 struct sockaddr_in * p = (struct sockaddr_in *) & lname;
 652                 pname = inet_ntoa (p -> sin_addr);
 653               }
 654 
 655             if (pname == NULL)
 656               sim_printf ("\r[OPC emulation: BUMPED]\r\n");
 657             else
 658               sim_printf ("\r[OPC emulation: BUMPED %s]\r\n", pname);
 659             accessPutStrForce (access, "\r[OPC emulation: BUMPED]\r\n");
 660             accessPutStrForce (access, "\rMultics has disconnected you\r\n");
 661             libtelnet_set_invalid(access->telnetp);
 662             accessCloseConnection ((uv_stream_t *) access->client);
 663           }
 664         access->client = client;
 665         uv_tcp_nodelay (client, 1);
 666         struct sockaddr name;
 667         int namelen = sizeof (name);
 668         int ret = uv_tcp_getpeername (access->client, & name, & namelen);
 669         if (ret < 0)
 670           {
 671             sim_printf ("\r[OPC emulation: CONNECT; addr err %d]\r\n", ret);
 672           }
 673         else
 674           {
 675             struct sockaddr_in * p = (struct sockaddr_in *) & name;
 676             sim_printf ("\r[OPC emulation: CONNECT %s]\r\n", inet_ntoa (p -> sin_addr));
 677           }
 678 
 679         if (access->useTelnet)
 680           {
 681             access->telnetp = accessTelnetConnect (access->client);
 682             if (!access->telnetp)
 683               {
 684                  sim_warn ("ltnConnect failed\r\n");
 685                  return;
 686               }
 687           }
 688         else
 689           {
 690             access->telnetp = NULL;
 691           }
 692         access->loggedOn = ! strlen (access->pw);
 693         if (access->loggedOn)
 694           access->connected (access->client);
 695         else
 696           access->connectPrompt (access->client);
 697         accessReadStart (access->client);
 698       }
 699     else
 700       {
 701         uv_close ((uv_handle_t *) client, accessCloseCallback);
 702       }
 703   }
 704 
 705 void uv_open_access (uv_access * access)
     /* [previous][next][first][last][top][bottom][index][help] */
 706   {
 707     if (access->open == true)
 708       {
 709         sim_printf ("\r[OPC emulation: OPC already initialized]\r\n");
 710       }
 711 
 712     if (! access->port)
 713       {
 714         //sim_printf ("access port disabled\r\n");
 715         return;
 716       }
 717 
 718     if (! access->loop)
 719       access->loop = uv_default_loop ();
 720 
 721     // Do we already have a listening port (ie not first time)?
 722     if (access->open)
 723       return;
 724 
 725     uv_tcp_init (access->loop, & access->server);
 726     access->server.data = (void *) access;
 727     struct sockaddr_in addr;
 728     uv_ip4_addr (access->address, access->port, & addr);
 729     uv_tcp_bind (& access->server, (const struct sockaddr *) & addr, 0);
 730 #define DEFAULT_BACKLOG 1024
 731     int r = uv_listen ((uv_stream_t *) & access->server,
 732                        DEFAULT_BACKLOG,
 733                        onNewAccess);
 734     if (r)
 735      {
 736         (void)fprintf (stderr, "\r[OPC emulation: listen error: %s:%ld: %s]\r\n",
 737                        access->address, (long) access->port, uv_strerror(r));
 738       }
 739     access->open = true;
 740     if (access->address != NULL)
 741       sim_printf ("\r[OPC emulation: TELNET server listening on %s:%ld]\r\n",
 742                   access->address, (long) access->port);
 743     else
 744       sim_printf ("\r[OPC emulation: TELNET server listening on 0.0.0.0:%ld]\r\n",
 745                   (long) access->port);
 746   }
 747 
 748 #if !defined(QUIET_UNUSED)
 749 void accessPutChar (uv_access * access, char ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 750   {
 751     //sim_putchar (ch);
 752     if (access->loggedOn)
 753       accessStartWrite (access->client, & ch, 1);
 754   }
 755 #endif /* if !defined(QUIET_UNUSED) */
 756 
 757 int accessGetChar (uv_access * access)
     /* [previous][next][first][last][top][bottom][index][help] */
 758   {
 759     // The connection could have closed when we were not looking
 760     if (! access->client)
 761       {
 762         if (access->inBuffer)
 763           FREE (access->inBuffer);
 764         access->inBuffer = NULL;
 765         access->inSize = 0;
 766         access->inUsed = 0;
 767         return SCPE_OK;
 768       }
 769 
 770     if (access->inBuffer && access->inUsed < access->inSize)
 771        {
 772          unsigned char c = access->inBuffer[access->inUsed ++];
 773          if (access->inUsed >= access->inSize)
 774            {
 775              FREE (access->inBuffer);
 776              access->inBuffer = NULL;
 777              access->inSize = 0;
 778              access->inUsed = 0;
 779              // The connection could have been closed when we weren't looking
 780              //if (access->client)
 781                //fnpuv_read_start (access->client);
 782            }
 783          return (int) c + SCPE_KFLAG;
 784        }
 785     return SCPE_OK;
 786 
 787   }
 788 
 789 #if !defined(QUIET_UNUSED)
 790 void accessPutStr (uv_access * access, char * str)
     /* [previous][next][first][last][top][bottom][index][help] */
 791   {
 792     size_t l = strlen (str);
 793     //for (size_t i = 0; i < l; i ++)
 794       //sim_putchar (str[i]);
 795     if (access->loggedOn)
 796       accessStartWrite (access->client, str, (ssize_t) l);
 797   }
 798 #endif /* if !defined(QUIET_UNUSED) */

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