root/src/dps8/fnpuv.c

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

DEFINITIONS

This source file includes following definitions.
  1. tun_alloc
  2. alloc_buffer
  3. fnpuv_associated_brk
  4. fnpuv_associated_readcb
  5. fnpuv_3270_readcb
  6. fnpuv_unassociated_readcb
  7. fuv_close_cb
  8. close_connection
  9. fuv_read_cb
  10. fuv_write_cb
  11. fuv_write_3270_cb
  12. fnpuv_start_write_3270_actual
  13. fnpuv_start_write_actual
  14. fnpuv_start_write
  15. fnpuv_start_3270_write
  16. fnpuv_start_writestr
  17. fnpuv_send_eor
  18. fnpuv_recv_eor
  19. fnpuv_read_start
  20. fnpuv_read_stop
  21. on_new_connection
  22. fnpuvInit
  23. fnpuvProcessEvent
  24. on_dialout_connect
  25. fnpuv_dial_out
  26. fnpuv_open_slave
  27. processPacketInput
  28. fnoTUNProcessLine
  29. fnpTUNProcessEvent
  30. fnpuv3270Poll
  31. on_new_3270_connection
  32. fnpuv3270Init

   1 /*
   2  * vim: filetype=c:tabstop=4:ai:expandtab
   3  * SPDX-License-Identifier: ICU
   4  * scspell-id: 2fdee814-f62f-11ec-8f3d-80ee73e9b8e7
   5  *
   6  * ---------------------------------------------------------------------------
   7  *
   8  * Copyright (c) 2016 Charles Anthony
   9  * Copyright (c) 2016 Michal Tomek
  10  * Copyright (c) 2021-2024 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 // The FNP <--> libuv interface
  19 //
  20 // Every libuv TCP connection has a uv_tcp_t object.
  21 //
  22 // The uv_tcp_t object has a 'user data' field defined as "void * data".
  23 //
  24 // This code stores a pointer to a "struct uvClientData" in 'data'; this
  25 // structure allows this code to determine which HSLA line the connection
  26 // has been made with.
  27 //
  28 // The uvClientData structure contains:
  29 //
  30 //    bool assoc
  31 //    uint fnpno;
  32 //    uint lineno;
  33 //
  34 //       If 'assoc' is true, the TCP connection is associated with a particular
  35 //       HSLA line as specified by 'fnpno' and 'lineno'. A dialup line is
  36 //       associated by the user entering the FNP name and line number in the
  37 //       multiplexor dial in dialog. For slave and dialout lines, 'assoc'
  38 //       is always true as the association is predetermined by the FNP
  39 //       configuration.
  40 //
  41 //    char buffer [1024];
  42 //    size_t nPos;
  43 //
  44 //      Line canonicalization buffer used by the multiplexor dial in dialog.
  45 //
  46 // Each HSLA line is identified by a 'fnpno', 'lineno' pair. 'fnpno' indexes
  47 // into the 'fnpUnitData' data structure and 'lineno' into
  48 // 'fnpUnitData[fnpno].MState.line[]'.
  49 //
  50 // The 'line' structure contains:
  51 //
  52 //   enum service_types service;
  53 //
  54 //     This tracks the Multics' line configuration of 'dialup', 'dialout', and
  55 //     'slave'.
  56 //
  57 //   uv_tcp_t * client;
  58 //
  59 //     If this is non-null, the HSLA has a TCP connection.
  60 //
  61 //   telnet_t * telnetp;
  62 //
  63 //     If this is non-null, Telnet is enabled on the connection.
  64 //
  65 //   uv_tcp_t server;
  66 //
  67 //     This is used by the 'slave' logic and holds the handle of the
  68 //     TCP server port for the slave line.
  69 //
  70 //     The data field of 'server' points to a uvClientData structure; this
  71 //     field is used by the incoming connection handler to distinguish the
  72 //     slave and dialup server ports. (If null, dialup; else slave.)
  73 //
  74 //   uv_connect_t doConnect;
  75 //
  76 //     libuv uses a uv_connect_t object to create outbound connections;
  77 //     the dialout code uses 'doConnect' for this.
  78 //
  79 //   int port;
  80 //
  81 //     The port number that a slave line listens on.
  82 //
  83 
  84 // Dialup logic.
  85 //
  86 // The code assumes a single port number for all dialup lines, even for the
  87 // case of multiple FNPs.
  88 //
  89 // 'fnpuvInit()' is called when by the 'fnpserverport' command logic. It
  90 // creates a TCP server listen port, assigning 'on_new_connection()' as
  91 // the connection callback handler.
  92 //
  93 // When a connection is made to the listen port, libuv invokes the
  94 // 'on_new_connection()' callback handler.
  95 //
  96 // The handler creates a 'uv_tcp_t' object 'client' and accepts the
  97 // connection.
  98 //
  99 // It then checks the 'data' field on the listener object to see if this
 100 // is a dialup or slave listener. For the case of dialup, a 'uvClientData'
 101 // object is created and its 'assoc' field is set to false to flag the
 102 // the connection as not yet being associated with an HSLA line; the
 103 // libtelnet data structure is initialized and the initial Telnet negotiations
 104 // are started. 'client->data' is set to the 'uvClientData' object,
 105 // reading is enabled on 'client', and the HSLA line selection dialog
 106 // prompt is sent down the dialup connection.
 107 //
 108 // When input data is ready on the connection (user input), libuv allocates
 109 // a buffer, fills it with the data calls the 'fuv_read_cb()' callback handler.
 110 //
 111 // The handler looks at the 'data' field of the connection to access the
 112 // 'uvClientData' structure; if the structure indicates that Telnet
 113 // processing is to be done, it passes the data to libtelent handler.
 114 // Otherwise, it checks the 'assoc' field; if true the data is passed to the
 115 // FNP incoming data handler; if false, to the multiplexor line selection
 116 // dialog handler. The libtelnet handler, after stripping and processing
 117 // Telnet overhead passes any remaining data on in the same manner.
 118 // The data buffer is then freed, and the callback returns.
 119 //
 120 // Since the FNP incoming data handler is dependent on Multics accepting
 121 // the input data before it can safely dispose of the data, it disables
 122 // reading on the connection, and reenables it when Multics has accepted the
 123 // data.
 124 
 125 // Data are written to the TCP connections with 'fnpuv_start_write()'. The
 126 // 'uvClientData' is inspected for telnet usage; if so, the data is passed
 127 // to libtelnet for telnet packaging and is sent on to
 128 // 'fnpuv_start_write_actual'. If telnet is not in play, the data is sent
 129 // directly on to 'fnpuv_start_write_actual()' There, a buffer is allocated
 130 // and the data copied and a 'uv_write_t' request object is created and
 131 // initialized data the buffer address and length, and a write request
 132 // is send to libuv.
 133 //
 134 // When the write is complete, libuv calls the write callback 'fuv_write_cb()',
 135 // which frees the data buffers and destroys the write request object.
 136 
 137 // Dialout logic
 138 //
 139 // When the FNP receives a 'dial_out' command, it calls 'fnpuv_dial_out()'.
 140 // The dial string is parsed for IP address, port number and telnet flag.
 141 // An 'uv_tcp_t' object constructed and its address stored in the HSLA
 142 // 'client' field. An 'uvClientData' object is created, associated with
 143 // the dialout line HSLA, and if the Telnet flag is set, the Telnet processor
 144 // is initialized. The TCP connect is initiated and the procedure returns.
 145 //
 146 // When the connection succeeds or times out, the 'on_dialout_connect()' callback
 147 // is called. on_dialout_connect retrieves the 'uvClientData'. If the connection
 148 // succeeded, the connection data field is set to the 'uvClientData'; read is
 149 // enabled on the connection, and the 'accept_new_terminal' flag is set which
 150 // will cause an 'accept_new_terminal' command to be send to Multics.
 151 //
 152 
 153 #if defined(TUN)
 154 // TUN interface
 155 
 156 // If makes more sense to me to have the emulator on an endpoint, but
 157 // that's not really the way ethernet works; for the nonce, we will
 158 // create a subnet and route to it with a tun device; the emulator
 159 // will listen on the device and do the whole ARP rigamorole.
 160 //
 161 // It's not clear to me what happens if multiple emulators are listening
 162 // on the same tun device, do they each get a copy of the message (i.e.
 163 // are we wired as point-to-point)?
 164 //
 165 // Create device node:
 166 //   mkdir /dev/net (if it doesn't exist already)
 167 //   mknod /dev/net/tun c 10 200
 168 //   chmod 0666 /dev/net/tun
 169 //   modprobe tun
 170 //
 171 // The subnet gateway device is 'dps8m' at address 192.168.8.0
 172 //
 173 //   sudo ip tuntap add mode tun dps8m
 174 //   sudo ip link set dps8m up
 175 //   sudo ip addr add 192.168.8.1/24 dev dps8m
 176 #endif /* if defined(TUN) */
 177 
 178 #define ASSUME0 0
 179 
 180 #include <stdio.h>
 181 #include <stdlib.h>
 182 #include <unistd.h>
 183 #include <ctype.h>
 184 #include <signal.h>
 185 #if defined(TUN)
 186 # include <string.h>
 187 # include <fcntl.h>
 188 # include <errno.h>
 189 # include <sys/ioctl.h>
 190 # include <sys/types.h>
 191 # include <sys/stat.h>
 192 # include <netinet/in.h>
 193 # include <sys/socket.h>
 194 # include <linux/if.h>
 195 # include <linux/if_tun.h>
 196 #endif /* if defined(TUN) */
 197 
 198 #include "dps8.h"
 199 #include "dps8_sys.h"
 200 #include "dps8_iom.h"
 201 #include "dps8_cable.h"
 202 #include "dps8_cpu.h"
 203 #include "dps8_scu.h"
 204 #include "dps8_fnp2.h"
 205 #include "dps8_utils.h"
 206 #include "fnpuv.h"
 207 #include "fnptelnet.h"
 208 
 209 #if defined(FREE)
 210 # undef FREE
 211 #endif /* if defined(FREE) */
 212 #define FREE(p) do  \
 213   {                 \
 214     free((p));      \
 215     (p) = NULL;     \
 216   } while(0)
 217 
 218 #define USE_REQ_DATA
 219 
 220 // Making it up...
 221 #define DEFAULT_BACKLOG 1024
 222 
 223 #if defined(TUN)
 224 static int tun_alloc (char * dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 225   {
 226     struct ifreq ifr;
 227     int fd, err;
 228 
 229     if ((fd = open ("/dev/net/tun", O_RDWR)) < 0)
 230       //return tun_alloc_old (dev);
 231       return fd;
 232 
 233     (void)memset (& ifr, 0,  sizeof (ifr));
 234 
 235     /*
 236      *  Flags: IFF_TUN   - TUN device (no Ethernet headers)
 237      *        IFF_TAP   - TAP device
 238      *
 239      *        IFF_NO_PI - Do not provide packet information
 240      */
 241 
 242     ifr.ifr_flags = IFF_TUN;
 243     if (* dev)
 244       strncpy (ifr.ifr_name, dev, IFNAMSIZ);
 245 
 246     if ((err = ioctl (fd, TUNSETIFF, (void *) & ifr)) < 0)
 247       {
 248         close (fd);
 249         return err;
 250       }
 251     strcpy (dev, ifr.ifr_name);
 252     return fd;
 253   }
 254 #endif /* if defined(TUN) */
 255 
 256 //
 257 // alloc_buffer: libuv callback handler to allocate buffers for incoming data.
 258 //
 259 
 260 static void alloc_buffer (UNUSED uv_handle_t * handle, size_t suggested_size,
     /* [previous][next][first][last][top][bottom][index][help] */
 261                           uv_buf_t * buf)
 262  {
 263 /* Suppress Clang Analyzer's possible memory leak warning */
 264 #if !defined(__clang_analyzer__)
 265     * buf = uv_buf_init ((char *) malloc (suggested_size), (uint) suggested_size);
 266 #endif /* if !defined(__clang_analyzer__) */
 267   }
 268 
 269 void fnpuv_associated_brk (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 270   {
 271     if (! client || uv_is_closing ((uv_handle_t *) client))
 272       return;
 273     if (! client->data)
 274       {
 275         sim_warn ("fnpuv_associated_brk bad client data\r\n");
 276         return;
 277       }
 278     uvClientData * p = (uvClientData *) client->data;
 279     uint fnpno = p -> fnpno;
 280     uint lineno = p -> lineno;
 281     struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
 282     // The break key forces an input buffer flush.
 283     // XXX is this a race condition? Is it possible for processFnpMbx to see
 284     // the line_break before the accept input?
 285     linep->accept_input = 1;
 286     linep->line_break=true;
 287   }
 288 
 289 // read callback for connections that are associated with an HSLA line;
 290 // forward the data to the FNP input handler
 291 
 292 void fnpuv_associated_readcb (uv_tcp_t * client,
     /* [previous][next][first][last][top][bottom][index][help] */
 293                            ssize_t nread,
 294                            unsigned char * buf)
 295   {
 296     processLineInput (client, buf, nread);
 297   }
 298 
 299 void fnpuv_3270_readcb (uv_tcp_t * client,
     /* [previous][next][first][last][top][bottom][index][help] */
 300                            ssize_t nread,
 301                            unsigned char * buf)
 302   {
 303     process3270Input (client, buf, nread);
 304   }
 305 
 306 // read callback for connections that are no associated with an HSLA line;
 307 // forward the data to the line selection dialog handler
 308 
 309 void fnpuv_unassociated_readcb (uv_tcp_t * client,
     /* [previous][next][first][last][top][bottom][index][help] */
 310                            ssize_t nread,
 311                            unsigned char * buf)
 312   {
 313     //(void)printf ("unassoc. <%*s>\n", (int) nread, buf->base);
 314     processUserInput (client, buf, nread);
 315   }
 316 
 317 static void fuv_close_cb (uv_handle_t * stream)
     /* [previous][next][first][last][top][bottom][index][help] */
 318   {
 319     FREE (stream);
 320   }
 321 
 322 // teardown a connection
 323 
 324 void close_connection (uv_stream_t* stream)
     /* [previous][next][first][last][top][bottom][index][help] */
 325   {
 326     if (! stream)
 327       {
 328         sim_warn ("close_connection bad client data\r\n");
 329         return;
 330       }
 331     uvClientData * p = (uvClientData *) stream->data;
 332 
 333     // If stream->data, the stream is associated with a Multics line.
 334     // Tear down that association
 335     //if (p && fnpData.fnpUnitData[p->fnpno].MState.line[p->lineno].service != service_3270)
 336     if (p)
 337       {
 338         // If assoc is false, then the disconnect happened before the actual
 339         // association took place
 340         if (p->assoc)
 341           {
 342             struct t_line * linep = & fnpData.fnpUnitData[p->fnpno].MState.line[p->lineno];
 343             if (linep->service  == service_3270)
 344              {
 345                // On the 3270, the station closing does not close the controller
 346                sim_printf ("\r[FNP emulation: TN3270 %d.%d DISCONNECT]\r\n", ASSUME0, p->stationNo);
 347              }
 348             else
 349               {
 350                 sim_printf ("\r[FNP emulation: DISCONNECT %c.d%03d]\r\n", p->fnpno+'a', p->lineno);
 351 #if defined(DISC_DELAY)
 352                 linep -> line_disconnected = DISC_DELAY;
 353 #else
 354                 linep -> line_disconnected = true;
 355 #endif /* if defined(DISC_DELAY) */
 356                 linep -> listen = false;
 357                 if (linep->inBuffer)
 358                   FREE (linep->inBuffer);
 359                 linep->inBuffer = NULL;
 360                 linep->inSize   = 0;
 361                 linep->inUsed   = 0;
 362                 linep->nPos     = 0;
 363               }
 364             if (linep->line_client)
 365               {
 366                 // linep->line_client is a copied stream->data; will be freed
 367                 // below
 368                 linep->line_client = NULL;
 369               }
 370           }
 371         else // ! p-assoc
 372           {
 373             sim_printf ("\r[FNP emulation: DISCONNECT]\r\n");
 374           }
 375         // Clean up allocated data
 376         if (p->telnetp)
 377           {
 378             telnet_free (p->telnetp);
 379             // telnet_free frees self
 380             //free (p->telnetp);
 381             p->telnetp = NULL;
 382           }
 383         if (((uvClientData *) stream->data)->ttype)
 384           FREE (((uvClientData *) stream->data)->ttype);
 385         FREE (stream->data);
 386         stream->data = NULL;
 387       } // if (p)
 388     if (! uv_is_closing ((uv_handle_t *) stream))
 389       uv_close ((uv_handle_t *) stream, fuv_close_cb);
 390   }
 391 
 392 //
 393 // fuv_read_cb: libuv read complete callback
 394 //
 395 //   Cleanup on read error.
 396 //   Forward data to appropriate handler.
 397 
 398 static void fuv_read_cb (uv_stream_t* stream,
     /* [previous][next][first][last][top][bottom][index][help] */
 399                          ssize_t nread,
 400                          const uv_buf_t* buf)
 401   {
 402     if (nread < 0)
 403       {
 404         //if (nread == UV_EOF)
 405           {
 406             close_connection (stream);
 407           }
 408       }
 409     else if (nread > 0)
 410       {
 411         if (! stream)
 412           {
 413             sim_warn ("fuv_read_cb bad client data\r\n");
 414             return;
 415           }
 416         uvClientData * p = (uvClientData *) stream->data;
 417         if (p)
 418           {
 419             if (p->telnetp)
 420               {
 421                 telnet_recv (p->telnetp, buf->base, (size_t) nread);
 422               }
 423             else
 424               {
 425 
 426                 (* p->read_cb) ((uv_tcp_t *) stream, nread, (unsigned char *) buf->base);
 427 
 428 
 429 
 430 
 431 
 432 
 433 
 434 
 435 
 436 
 437 
 438               }
 439           }
 440       }
 441 
 442     if (buf->base)
 443         free (buf->base); /* X-LINTED: FREE */
 444   }
 445 
 446 //
 447 // fuv_write_cb: libuv write complete callback
 448 //
 449 //   Cleanup on error
 450 //   Free buffers
 451 //
 452 
 453 static void fuv_write_cb (uv_write_t * req, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
 454   {
 455     if (status < 0)
 456       {
 457         if (status == -ECONNRESET || status == -ECANCELED ||
 458             status == -EPIPE)
 459           {
 460             // This occurs when the other end disconnects; not an "error"
 461           }
 462         else
 463           {
 464             sim_warn ("fuv_write_cb status %d (%s)\n", -status, xstrerror_l(-status));
 465           }
 466 
 467         // connection reset by peer
 468         close_connection (req->handle);
 469       }
 470 
 471 #if defined(USE_REQ_DATA)
 472     //sim_printf ("freeing bufs %p\n", req->data);
 473     FREE (req->data);
 474 #else
 475     unsigned int nbufs = req->nbufs;
 476     uv_buf_t * bufs = req->bufs;
 477     for (unsigned int i = 0; i < nbufs; i ++)
 478       {
 479         if (bufs && bufs[i].base)
 480           {
 481             //sim_printf ("freeing bufs%d %p\n", i, bufs[i].base);
 482             FREE (bufs[i].base);
 483           }
 484         if (req->bufsml[i].base)
 485           {
 486             //sim_printf ("freeing bufsml%d %p@%p\n", i, req->bufsml[i].base, & req->bufsml[i].base);
 487             FREE (req->bufsml[i].base);
 488           }
 489       }
 490 #endif /* if defined(USE_REQ_DATA) */
 491 
 492     // the buf structure is copied; do not free.
 493     //sim_printf ("freeing req %p\n", req);
 494     FREE (req);
 495   }
 496 
 497 //
 498 // fuv_write_3270_cb: libuv write complete callback
 499 //
 500 //   Cleanup on error
 501 //   Free buffers
 502 //
 503 
 504 static void fuv_write_3270_cb (uv_write_t * req, int status) {
     /* [previous][next][first][last][top][bottom][index][help] */
 505     uv_tcp_t * client = (uv_tcp_t *) req->handle;
 506     fuv_write_cb (req, status);
 507     set_3270_write_complete (client);
 508   }
 509 
 510 // Create and start a write request
 511 
 512 static void fnpuv_start_write_3270_actual (UNUSED uv_tcp_t * client, unsigned char * data, ssize_t datalen)
     /* [previous][next][first][last][top][bottom][index][help] */
 513   {
 514 #if defined(TESTING)
 515       sim_printf ("fnpuv_start_write_3270_actual\r\n");
 516 #endif /* if defined(TESTING) */
 517 
 518     // Find the client from the device selection call
 519 
 520     uint stn_no;
 521     for (stn_no = 0; stn_no < ADDR_MAP_ENTRIES; stn_no ++)
 522       if (addr_map [stn_no] == fnpData.ibm3270ctlr[ASSUME0].selDevChar)
 523         break;
 524     if (stn_no >= ADDR_MAP_ENTRIES)
 525       {
 526         sim_printf ("fnpuv_start_write_3270_actual couldn't find selDevChar %02x\r\n",
 527                     (unsigned int) fnpData.ibm3270ctlr[ASSUME0].selDevChar);
 528         return;
 529       }
 530     uv_tcp_t * stn_client = fnpData.ibm3270ctlr[ASSUME0].stations[stn_no].client;
 531     if (! stn_client || uv_is_closing ((uv_handle_t *) stn_client))
 532       return;
 533 
 534     // Allocate write request
 535 /* Suppress Clang Analyzer's possible memory leak warning */
 536 #if !defined (__clang_analyzer__)
 537     uv_write_t * req = (uv_write_t *) malloc (sizeof (uv_write_t));
 538     if (!req)
 539       {
 540         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 541                        __func__, __FILE__, __LINE__);
 542 # if defined(USE_BACKTRACE)
 543 #  if defined(SIGUSR2)
 544         (void)raise(SIGUSR2);
 545         /*NOTREACHED*/ /* unreachable */
 546 #  endif /* if defined(SIGUSR2) */
 547 # endif /* if defined(USE_BACKTRACE) */
 548         abort();
 549       }
 550     // This makes sure that bufs*.base and bufsml*.base are NULL
 551     (void)memset (req, 0, sizeof (uv_write_t));
 552     uv_buf_t buf = uv_buf_init ((char *) malloc ((unsigned long) datalen), (uint) datalen);
 553 //sim_printf ("allocated req %p data %p\n", req, buf.base);
 554 # if defined(USE_REQ_DATA)
 555     req->data = buf.base;
 556 # endif
 557 //sim_printf ("fnpuv_start_write_actual req %p buf.base %p\n", req, buf.base);
 558     memcpy (buf.base, data, (unsigned long) datalen);
 559     int ret = uv_write (req, (uv_stream_t *) stn_client, & buf, 1, fuv_write_3270_cb);
 560 // There seems to be a race condition when Multics signals a disconnect_line;
 561 // We close the socket, but Multics is still writing its goodbye text trailing
 562 // NULs.
 563 // If the socket has been closed, write will return BADF; just ignore it.
 564     if (ret < 0 && ret != -EBADF)
 565       sim_printf ("\r[FNP emulation: uv_write returned %d]\r\n", ret);
 566 #endif /* if !defined(__clang_analyzer__) */
 567   }
 568 
 569 void fnpuv_start_write_actual (uv_tcp_t * client, unsigned char * data, ssize_t datalen)
     /* [previous][next][first][last][top][bottom][index][help] */
 570   {
 571     if (! client || uv_is_closing ((uv_handle_t *) client))
 572       return;
 573 /* Suppress Clang Analyzer's possible memory leak warning */
 574 #if !defined(__clang_analyzer__)
 575     uv_write_t * req = (uv_write_t *) malloc (sizeof (uv_write_t));
 576     if (!req)
 577       {
 578         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 579                        __func__, __FILE__, __LINE__);
 580 # if defined(USE_BACKTRACE)
 581 #  if defined(SIGUSR2)
 582         (void)raise(SIGUSR2);
 583         /*NOTREACHED*/ /* unreachable */
 584 #  endif /* if defined(SIGUSR2) */
 585 # endif /* if defined(USE_BACKTRACE) */
 586         abort();
 587       }
 588     // This makes sure that bufs*.base and bufsml*.base are NULL
 589     (void)memset (req, 0, sizeof (uv_write_t));
 590     uv_buf_t buf = uv_buf_init ((char *) malloc ((unsigned long) datalen), (uint) datalen);
 591 //sim_printf ("allocated req %p data %p\n", req, buf.base);
 592 # if defined(USE_REQ_DATA)
 593     req->data = buf.base;
 594 # endif
 595 //sim_printf ("fnpuv_start_write_actual req %p buf.base %p\n", req, buf.base);
 596     memcpy (buf.base, data, (unsigned long) datalen);
 597     int ret = uv_write (req, (uv_stream_t *) client, & buf, 1, fuv_write_cb);
 598 // There seems to be a race condition when Multics signals a disconnect_line;
 599 // We close the socket, but Multics is still writing its goodbye text trailing
 600 // NULs.
 601 // If the socket has been closed, write will return BADF; just ignore it.
 602     if (ret < 0 && ret != -EBADF)
 603       sim_printf ("\r[FNP emulation: uv_write returned %d]\r\n", ret);
 604 #endif /* if !defined(__clang_analyzer__) */
 605   }
 606 
 607 //
 608 // Write data to a connection, doing Telnet if needed.
 609 //
 610 
 611 void fnpuv_start_write (uv_tcp_t * client, unsigned char * data, ssize_t datalen)
     /* [previous][next][first][last][top][bottom][index][help] */
 612   {
 613     if (! client || uv_is_closing ((uv_handle_t *) client))
 614       return;
 615     uvClientData * p = (uvClientData *) client->data;
 616     if (! p) //-V547
 617       return;
 618     if (!p->telnetp)
 619       {
 620         sim_warn ("telnetp NULL; dropping fnpuv_start_write()\n");
 621         return;
 622       }
 623     telnet_send (p->telnetp, (char *) data, (size_t) datalen);
 624   }
 625 
 626 void fnpuv_start_3270_write (uv_tcp_t * client, unsigned char * data, ssize_t datalen)
     /* [previous][next][first][last][top][bottom][index][help] */
 627   {
 628     if (! client || uv_is_closing ((uv_handle_t *) client) || ! client->data)
 629       return;
 630     uvClientData * p = (uvClientData *) client->data;
 631     if (! p) //-V547
 632       return;
 633 #if defined(TESTING)
 634 sim_printf ("fnpuv_start_3270_write\r\n");
 635 #endif
 636 
 637     // Strip BSC protocol:
 638     //    STX <text> ETX
 639     //    EOT
 640 
 641     if (datalen == 1 && data [0] == 0x37) // EOT
 642       {
 643 #if defined(TESTING)
 644 sim_printf ("fnpuv: detected EOT\r\n");
 645 #endif /* if defined(TESTING) */
 646         fnpuv_send_eor (client);
 647         return;
 648       }
 649 
 650     unsigned char * actual_data_start = data;
 651     unsigned long actual_datalen = (unsigned long) datalen;
 652     //bool send_eor = false;
 653     if (datalen > 0 && data [datalen - 1] == 0x03) // ETX
 654       {
 655         actual_datalen --;
 656         //send_eor = true;
 657       }
 658     if (datalen > 0 && data [0] == 0x02) // STX
 659       {
 660         actual_data_start ++;
 661         actual_datalen --;
 662         if (actual_datalen > 0 && data [1] == 0x27) // ESC
 663           {
 664             actual_data_start ++;
 665             actual_datalen --;
 666           }
 667       }
 668 
 669     //telnet_send (p->telnetp, (char *) actual_data_start, (size_t) actual_datalen);
 670     //if (send_eor) fnpuv_send_eor (client);
 671     if (actual_datalen > 0)
 672       {
 673         telnet_send (p->telnetp, (char *) actual_data_start, (size_t) actual_datalen);
 674       }
 675   }
 676 
 677 // C-string wrapper for fnpuv_start_write
 678 
 679 void fnpuv_start_writestr (uv_tcp_t * client, unsigned char * data)
     /* [previous][next][first][last][top][bottom][index][help] */
 680   {
 681     //fnpuv_start_write (client, data, (ssize_t) strlen (data));
 682     if (! client || uv_is_closing ((uv_handle_t *) client))
 683       return;
 684     if (! client->data)
 685       {
 686         sim_warn ("fnpuv_start_writestr bad client data\r\n");
 687         return;
 688       }
 689     uvClientData * p = client->data;
 690     (* p->write_cb) (client, data, (ssize_t) strlen ((char *) data));
 691   }
 692 
 693 void fnpuv_send_eor (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 694   {
 695     if (! client || uv_is_closing ((uv_handle_t *) client))
 696       return;
 697     if (! client->data)
 698       {
 699         sim_warn ("fnpuv_send_eor bad client data\r\n");
 700         return;
 701       }
 702     uvClientData * p = (uvClientData *) client->data;
 703     ltnEOR (p->telnetp);
 704     //unsigned char EOR [] = { TELNET_IAC, TELNET_EOR };
 705     //fnpuv_start_write_special (client, (char *) EOR, sizeof (EOR));
 706   }
 707 
 708 void fnpuv_recv_eor (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 709   {
 710     fnpRecvEOR (client);
 711   }
 712 
 713 //
 714 // Enable reading on connection
 715 //
 716 
 717 void fnpuv_read_start (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 718   {
 719     if (! client || uv_is_closing ((uv_handle_t *) client))
 720       return;
 721     uv_read_start ((uv_stream_t *) client, alloc_buffer, fuv_read_cb);
 722   }
 723 
 724 //
 725 // Disable reading on connection
 726 //
 727 
 728 void fnpuv_read_stop (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 729   {
 730     if (! client || uv_is_closing ((uv_handle_t *) client))
 731       return;
 732     uv_read_stop ((uv_stream_t *) client);
 733   }
 734 
 735 //
 736 // Connection callback handler for dialup connections
 737 //
 738 
 739 static void on_new_connection (uv_stream_t * server, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
 740   {
 741     if (status < 0)
 742       {
 743         sim_printf ("\r[FNP emulation: new connection error: %s]\r\n", uv_strerror(status));
 744         // error!
 745         return;
 746       }
 747 
 748     uv_tcp_t * client = (uv_tcp_t *) malloc (sizeof (uv_tcp_t));
 749     if (!client)
 750       {
 751         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 752                        __func__, __FILE__, __LINE__);
 753 #if defined(USE_BACKTRACE)
 754 # if defined(SIGUSR2)
 755         (void)raise(SIGUSR2);
 756         /*NOTREACHED*/ /* unreachable */
 757 # endif /* if defined(SIGUSR2) */
 758 #endif /* if defined(USE_BACKTRACE) */
 759         abort();
 760       }
 761     uv_tcp_init (fnpData.loop, client);
 762     if (uv_accept (server, (uv_stream_t *) client) != 0)
 763       {
 764         uv_close ((uv_handle_t *) client, fuv_close_cb);
 765         return;
 766       }
 767 
 768     // if server->data is non-null, this is a slave server; else a dialup
 769     // server
 770     if (server->data)
 771       {
 772         uvClientData * p = (uvClientData *) server->data;
 773         struct t_line * linep = & fnpData.fnpUnitData[p->fnpno].MState.line[p->lineno];
 774 
 775         // Slave servers only handle a single connection at a time
 776         if (linep->line_client)
 777           {
 778             uv_close ((uv_handle_t *) client, fuv_close_cb);
 779 # if defined(TESTING)
 780 sim_printf ("\r[FNP emulation: dropping 2nd slave]\r\n");
 781 # endif
 782             return;
 783           }
 784 
 785         linep->line_client = client;
 786       }
 787 
 788     struct sockaddr name;
 789     int namelen = sizeof (name);
 790     uv_tcp_nodelay (client,1);
 791     int ret = uv_tcp_getpeername (client, & name, & namelen);
 792     if (ret < 0)
 793       {
 794         sim_printf ("\r[FNP emulation: CONNECT; addr err %d]\r\n", ret);
 795       }
 796     else
 797       {
 798         struct sockaddr_in * p = (struct sockaddr_in *) & name;
 799         sim_printf ("\r[FNP emulation: CONNECT %s]\r\n", inet_ntoa (p -> sin_addr));
 800       }
 801 
 802     uvClientData * p = (uvClientData *) malloc (sizeof (uvClientData));
 803     if (! p)
 804       {
 805          (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 806                         __func__, __FILE__, __LINE__);
 807 #if defined(USE_BACKTRACE)
 808 # if defined(SIGUSR2)
 809         (void)raise(SIGUSR2);
 810         /*NOTREACHED*/ /* unreachable */
 811 # endif /* if defined(SIGUSR2) */
 812 #endif /* if defined(USE_BACKTRACE) */
 813         abort();
 814       }
 815     client->data       = p;
 816     p->assoc           = false;
 817     p->nPos            = 0;
 818     p->ttype           = NULL;
 819     p->write_actual_cb = fnpuv_start_write_actual;
 820     // dialup connections are routed through libtelent
 821     if (! server->data)
 822       {
 823         p->read_cb  = fnpuv_unassociated_readcb;
 824         p->write_cb = fnpuv_start_write;
 825         p->telnetp  = ltnConnect (client);
 826 
 827         if (! p->telnetp)
 828           {
 829              sim_warn ("ltnConnect failed\n");
 830              return;
 831           }
 832       }
 833     else
 834       {
 835         p->read_cb       = fnpuv_associated_readcb;
 836         p->write_cb      = fnpuv_start_write_actual;
 837         p->telnetp       = NULL;
 838         uvClientData * q = (uvClientData *) server->data;
 839         p->fnpno         = q->fnpno;
 840         p->lineno        = q->lineno;
 841         p->assoc         = true;
 842       }
 843     fnpuv_read_start (client);
 844     if (! server->data)
 845       fnpConnectPrompt (client);
 846     else
 847       {
 848         uvClientData * p = (uvClientData *) server->data;
 849         struct t_line * linep = & fnpData.fnpUnitData[p->fnpno].MState.line[p->lineno];
 850         if (linep->lineType == 0) /* LINE_NONE */
 851           linep->lineType = 1; /* LINE_ASCII */
 852         linep->accept_new_terminal = true;
 853         reset_line (linep);
 854       }
 855   }
 856 
 857 //
 858 // Setup the dialup listener
 859 //
 860 
 861 int fnpuvInit (int telnet_port, char * telnet_address)
     /* [previous][next][first][last][top][bottom][index][help] */
 862   {
 863     // Ignore multiple calls; this means that once the listen port is
 864     // opened, it can't be changed. Fixing this requires non-trivial
 865     // changes.
 866     if (fnpData.du_server_inited)
 867       {
 868         sim_printf ("\r[FNP emulation: FNP already initialized]\r\n");
 869         return 1;
 870       }
 871 
 872     if (! fnpData.loop)
 873       fnpData.loop = uv_default_loop ();
 874 
 875     // Initialize the server socket
 876     fnpData.loop = uv_default_loop ();
 877     uv_tcp_init (fnpData.loop, & fnpData.du_server);
 878 
 879 // XXX to do clean shutdown
 880 // uv_loop_close (fnpData.loop);
 881 
 882     // Flag the this server as being the dialup server
 883     fnpData.du_server.data = NULL;
 884 
 885     // Bind and listen
 886     struct sockaddr_in addr;
 887     uv_ip4_addr (telnet_address, telnet_port, & addr);
 888     uv_tcp_bind (& fnpData.du_server, (const struct sockaddr *) & addr, 0);
 889     int r = uv_listen ((uv_stream_t *) & fnpData.du_server, DEFAULT_BACKLOG,
 890                        on_new_connection);
 891     if (r)
 892      {
 893         sim_printf ("\r[FNP emulation: listen error: %s:%ld: %s]\r\n", telnet_address, (long) telnet_port, uv_strerror(r));
 894         return 1;
 895      }
 896     fnpData.du_server_inited = true;
 897     sim_printf ("\r[FNP emulation: TELNET server listening on %s:%ld]\r\n", telnet_address, (long) telnet_port);
 898     return 0;
 899   }
 900 
 901 // Make a single pass through the libuv event queue.
 902 
 903 void fnpuvProcessEvent (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 904   {
 905     // UV_RUN_NOWAIT: Poll for i/o once but don’t block if there are no
 906     // pending callbacks. Returns zero if done (no active handles or
 907     // requests left), or non-zero if more callbacks are expected (meaning
 908     // you should run the event loop again sometime in the future).
 909 
 910     // Note that uv_run returns non-zero if that are any active_handles
 911     // (e.g. TCP connection listener open); that means a non-zero
 912     // return does not mean i/o is pending.
 913     if (! fnpData.loop)
 914       return;
 915     /* int ret = */ uv_run (fnpData.loop, UV_RUN_NOWAIT);
 916   }
 917 
 918 //
 919 // dialout line connection callback
 920 //
 921 
 922 static void on_dialout_connect (uv_connect_t * server, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
 923   {
 924     sim_printf ("\r[FNP emulation: dialout connect]\r\n");
 925     uvClientData * p = (uvClientData *) server->handle->data;
 926     // If data is NULL, assume that the line has already been torn down.
 927     if (! p)
 928       {
 929          sim_printf ("\r[FNP emulation: NOTE: on_dialout_connect called with data == NULL]\r\n");
 930          return;
 931       }
 932     struct t_line * linep = & fnpData.fnpUnitData[p->fnpno].MState.line[p->lineno];
 933     if (status < 0)
 934       {
 935         sim_printf ("\r[FNP emulation: dialout connection error: %s]\r\n", uv_strerror(status));
 936         //sim_printf ("%p\n", p);
 937         //sim_printf ("%d.%d\n", p->fnpno, p->lineno);
 938         linep->acu_dial_failure = true;
 939         return;
 940       }
 941 
 942     uv_read_start ((uv_stream_t *) linep->line_client, alloc_buffer, fuv_read_cb);
 943     linep->listen = true;
 944     if (linep->lineType == 0) /* LINE_NONE */
 945       linep->lineType = 1; /* LINE_ASCII */
 946     linep->accept_new_terminal = true;
 947     linep->was_CR              = false;
 948     linep->line_client->data   = p;
 949     if (p->telnetp)
 950       {
 951         ltnDialout (p->telnetp);
 952       }
 953   }
 954 
 955 //
 956 // Perform a dialout
 957 //
 958 
 959 void fnpuv_dial_out (uint fnpno, uint lineno, word36 d1, word36 d2, word36 d3)
     /* [previous][next][first][last][top][bottom][index][help] */
 960   {
 961     if (! fnpData.loop)
 962       return;
 963     sim_printf ("\r[FNP emulation: received dial_out %c.h%03d %012llu %012llu %012llu]\r\n", fnpno+'a', lineno,
 964             (unsigned long long)d1, (unsigned long long)d2, (unsigned long long)d3);
 965     struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
 966     uint d01   = (d1 >> 30) & 017;
 967     uint d02   = (d1 >> 24) & 017;
 968     uint d03   = (d1 >> 18) & 017;
 969     uint d04   = (d1 >> 12) & 017;
 970     uint d05   = (d1 >>  6) & 017;
 971     uint d06   = (d1 >>  0) & 017;
 972     uint d07   = (d2 >> 30) & 017;
 973     uint d08   = (d2 >> 24) & 017;
 974     uint d09   = (d2 >> 18) & 017;
 975     uint d10   = (d2 >> 12) & 017;
 976     uint d11   = (d2 >>  6) & 017;
 977     uint d12   = (d2 >>  0) & 017;
 978     uint flags = (d3 >> 30) & 017;
 979     uint p1    = (d3 >> 24) & 017;
 980     uint p2    = (d3 >> 18) & 017;
 981     uint p3    = (d3 >> 12) & 017;
 982     uint p4    = (d3 >>  6) & 017;
 983     uint p5    = (d3 >>  0) & 017;
 984 
 985     uint oct1  = d01 * 100 + d02 * 10 + d03;
 986     uint oct2  = d04 * 100 + d05 * 10 + d06;
 987     uint oct3  = d07 * 100 + d08 * 10 + d09;
 988     uint oct4  = d10 * 100 + d11 * 10 + d12;
 989 
 990     uint port  = ((((p1 * 10) + p2) * 10 + p3) * 10 + p4) * 10 + p5;
 991 
 992 // Flags
 993 //   1    Use Telnet
 994 #if defined(TUN)
 995 //   2    Use TUN device (in which case 'port' is the netmask
 996 //   4
 997 
 998     linep->is_tun = false;
 999     if (flags & 2)
1000       {
1001         if (linep->tun_fd <= 0)
1002           {
1003             char a_name [IFNAMSIZ] = "dps8m";
1004             linep->tun_fd = tun_alloc (a_name);
1005             if (linep->tun_fd < 0)
1006               {
1007                 sim_printf ("\r[FNP emulation: dialout TUN tun_alloc returned %d errno %d]\r\n", linep->tun_fd, errno);
1008                 return;
1009              }
1010             int flags = fcntl (linep->tun_fd, F_GETFL, 0);
1011             if (flags < 0)
1012               {
1013                 sim_printf ("\r[FNP emulation: dialout TUN F_GETFL returned < 0]\r\n");
1014                 return;
1015               }
1016             flags |= O_NONBLOCK;
1017             int ret = fcntl (linep->tun_fd, F_SETFL, flags);
1018             if (ret)
1019               {
1020                 sim_printf ("\r[FNP emulation: dialout TUN F_SETFL returned %d]\r\n", ret);
1021                 return;
1022               }
1023           }
1024           linep->is_tun = true;
1025           return;
1026         }
1027 #endif /* if defined(TUN) */
1028 
1029     // firewall
1030 
1031     // Default is accept
1032     bool accept = true;
1033     uint32_t ip_addr = (uint32_t) ((oct1 << 24) | (oct2 << 16) | (oct3 << 8) | oct4);
1034     uint this_line = encodeline (fnpno, lineno);
1035     for (int i = 0; i < n_fw_entries; i ++)
1036       {
1037         struct fw_entry_s * p = fw_entries + i;
1038         if (this_line < p->line_0 || this_line > p->line_1)
1039           continue;
1040         if ((ip_addr & p->cidr_mask) != (p->ipaddr & p->cidr_mask))
1041           continue;
1042         accept = p->accept;
1043         break;
1044       }
1045 
1046     if (! accept)
1047       {
1048         sim_printf ("Dialout %c.d%03d denied\r\n", fnpno + 'a', lineno);
1049         linep->acu_dial_failure = true;
1050         return;
1051       }
1052 
1053     char ipaddr [256];
1054     (void)sprintf (ipaddr, "%d.%d.%d.%d", oct1, oct2, oct3, oct4);
1055     sim_printf ("calling %s:%d\n", ipaddr,port);
1056 
1057     struct sockaddr_in dest;
1058     uv_ip4_addr(ipaddr, (int) port, &dest);
1059 
1060     linep->line_client = (uv_tcp_t *) malloc (sizeof (uv_tcp_t));
1061     if (!linep->line_client)
1062       {
1063         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1064                        __func__, __FILE__, __LINE__);
1065 #if defined(USE_BACKTRACE)
1066 # if defined(SIGUSR2)
1067         (void)raise(SIGUSR2);
1068         /*NOTREACHED*/ /* unreachable */
1069 # endif /* if defined(SIGUSR2) */
1070 #endif /* if defined(USE_BACKTRACE) */
1071         abort();
1072       }
1073     uv_tcp_init (fnpData.loop, linep->line_client);
1074 
1075     uvClientData * p = (uvClientData *) malloc (sizeof (uvClientData));
1076     if (! p)
1077       {
1078         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1079                        __func__, __FILE__, __LINE__);
1080 #if defined(USE_BACKTRACE)
1081 # if defined(SIGUSR2)
1082         (void)raise(SIGUSR2);
1083         /*NOTREACHED*/ /* unreachable */
1084 # endif /* if defined(SIGUSR2) */
1085 #endif /* if defined(USE_BACKTRACE) */
1086         abort();
1087       }
1088     p->assoc                 = true;
1089     p->read_cb               = fnpuv_associated_readcb;
1090     p->nPos                  = 0;
1091     p->ttype                 = NULL;
1092     p->fnpno                 = fnpno;
1093     p->lineno                = lineno;
1094     linep->line_client->data = p;
1095 
1096     if (flags & 1)
1097       {
1098         p->write_cb        = fnpuv_start_write;
1099         p->write_actual_cb = fnpuv_start_write_actual;
1100         p->telnetp         = ltnConnect (linep->line_client);
1101         if (! p->telnetp)
1102           {
1103               sim_warn ("ltnConnect failed\n");
1104           }
1105       }
1106     else
1107       {
1108         p->write_cb        = fnpuv_start_write_actual;
1109         p->write_actual_cb = fnpuv_start_write_actual;
1110         p->telnetp         = NULL; // Mark this line as 'not a telnet connection'
1111       }
1112 
1113     uv_tcp_connect (& linep->doConnect, linep->line_client, (const struct sockaddr *) & dest, on_dialout_connect);
1114   }
1115 
1116 
1117 
1118 
1119 
1120 
1121 
1122 
1123 
1124 
1125 
1126 
1127 
1128 
1129 
1130 
1131 
1132 
1133 
1134 //
1135 // Start a slave line connection listener.
1136 //
1137 
1138 void fnpuv_open_slave (uint fnpno, uint lineno)
     /* [previous][next][first][last][top][bottom][index][help] */
1139   {
1140     if (! fnpData.loop)
1141       return;
1142     sim_printf ("\r[FNP emulation: fnpuv_open_slave %d.%d]\r\n", fnpno, lineno);
1143     struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1144 
1145     // Do we already have a listening port (ie not first time)?
1146     if (linep->server.data)
1147       return;
1148 
1149     uv_tcp_init (fnpData.loop, & linep->server);
1150 
1151     // Mark this server has being a slave server
1152     // XXX This does not get freed during a normal shutdown, as Multics
1153     // XXX doesn't tell idle slave lines anything. The emulator shutdown
1154     // XXX needs to call an FNP cleanup routine that frees this.
1155 
1156     uvClientData * p = (uvClientData *) malloc (sizeof (uvClientData));
1157     if (! p)
1158       {
1159         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1160                        __func__, __FILE__, __LINE__);
1161 #if defined(USE_BACKTRACE)
1162 # if defined(SIGUSR2)
1163         (void)raise(SIGUSR2);
1164         /*NOTREACHED*/ /* unreachable */
1165 # endif /* if defined(SIGUSR2) */
1166 #endif /* if defined(USE_BACKTRACE) */
1167         abort();
1168       }
1169     p->assoc           = false;
1170     p->read_cb         = fnpuv_associated_readcb;
1171     p->write_cb        = fnpuv_start_write_actual;
1172     p->write_actual_cb = fnpuv_start_write_actual;
1173     p->nPos            = 0;
1174     p->ttype           = NULL;
1175     p->fnpno           = fnpno;
1176     p->lineno          = lineno;
1177     linep->server.data = p;
1178     linep->line_client = NULL;
1179 
1180     struct sockaddr_in addr;
1181     uv_ip4_addr (fnpData.telnet_address, linep->port, & addr);
1182     uv_tcp_bind (& linep->server, (const struct sockaddr *) & addr, 0);
1183     int r = uv_listen ((uv_stream_t *) & linep->server, DEFAULT_BACKLOG,
1184                        on_new_connection);
1185     if (r)
1186      {
1187         sim_printf ("\r[FNP emulation: listen error: %s:%ld: %s]\r\n", fnpData.telnet_address, (long) linep->port, uv_strerror(r));
1188      }
1189     sim_printf ("\r[FNP emulation: listening on %s:%ld]\r\n", fnpData.telnet_address, (long) linep->port);
1190   }
1191 
1192 #if defined(TUN)
1193 static void processPacketInput (int fnpno, int lineno, unsigned char * buf, ssize_t nread)
     /* [previous][next][first][last][top][bottom][index][help] */
1194   {
1195     //uvClientData * p = (uvClientData *) client->data;
1196     //uint fnpno       = p -> fnpno;
1197     //uint lineno      = p -> lineno;
1198     if (fnpno >= N_FNP_UNITS_MAX || lineno >= MAX_LINES)
1199       {
1200         sim_printf ("\r[FNP emulation: processPacketInput bogus client data]\r\n");
1201         return;
1202       }
1203 //sim_printf ("assoc. %d.%d nread %ld <%*s>\n", fnpno, lineno, (long) nread, buf);
1204 //{for (int i = 0; i < nread; i ++) sim_printf (" %03o", buf[i]);
1205  //sim_printf ("\n");
1206 //}
1207 
1208     struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1209     if (linep->inBuffer)
1210       {
1211         unsigned char * new = realloc (linep->inBuffer, (unsigned long) (linep->inSize + nread));
1212         if (! new)
1213           {
1214             (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1215                            __func__, __FILE__, __LINE__)
1216 # if defined(USE_BACKTRACE)
1217 #  if defined(SIGUSR2)
1218             (void)raise(SIGUSR2);
1219             /*NOTREACHED*/ /* unreachable */
1220 #  endif /* if defined(SIGUSR2) */
1221 # endif /* if defined(USE_BACKTRACE) */
1222             abort();
1223           }
1224         memcpy (new + linep->inSize, buf, (unsigned long) nread);
1225         linep->inSize  += nread;
1226         linep->inBuffer = new;
1227       }
1228     else
1229       {
1230         linep->inBuffer = malloc ((unsigned long) nread);
1231         if (! linep->inBuffer)
1232           {
1233             (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1234                            __func__, __FILE__, __LINE__);
1235 # if defined(USE_BACKTRACE)
1236 #  if defined(SIGUSR2)
1237             (void)raise(SIGUSR2);
1238             /*NOTREACHED*/ /* unreachable */
1239 #  endif /* if defined(SIGUSR2) */
1240 # endif /* if defined(USE_BACKTRACE) */
1241             abort();
1242           }
1243         memcpy (linep->inBuffer, buf, (unsigned long) nread);
1244         linep->inSize = (uint) nread;
1245         linep->inUsed = 0;
1246       }
1247 
1248   }
1249 
1250 static void fnoTUNProcessLine (int fnpno, int lineno, struct t_line * linep)
     /* [previous][next][first][last][top][bottom][index][help] */
1251   {
1252 /* Note that "buffer" should be at least the MTU size of the interface, eg 1500 bytes */
1253     unsigned char buffer [1500 + 16];
1254     ssize_t nread = read (linep->tun_fd, buffer, sizeof (buffer));
1255     if (nread < 0)
1256       {
1257         //perror ("Reading from interface");
1258         //close (linep->tun_fd);
1259         //exit (1);
1260         if (errno == EAGAIN)
1261           return;
1262         sim_printf ("%ld %ld\n", (long) nread, (long) errno);
1263         return;
1264       }
1265 
1266 // To make debugging easier, return data as a hex string rather than binary.
1267 // When the stack interface is debugged, switch to binary.
1268     unsigned char xbufr [2 * (1500 + 16)];
1269     unsigned char bin2hex [16] = "0123456789ABCDEF";
1270 
1271     for (uint i = 0; i > nread; i ++)
1272       {
1273         xbufr [i * 2 + 0] = bin2hex [(buffer [i] >> 4) & 0xf];
1274         xbufr [i * 2 + 1] = bin2hex [(buffer [i] >> 0) & 0xf];
1275       }
1276      xbufr [nread * 2] = 0;
1277      processPacketInput (fnpno, lineno, xbufr, nread * 2 + 1);
1278 
1279 // Debugging
1280 // 4 bytes of metadata
1281 # define ip 4
1282     /* Do whatever with the data */
1283     sim_printf("Read %ld bytes\n", (long) nread);
1284     sim_printf ("%02x %02x %02x %02x %02x %02x %02x %02x\n",
1285       buffer [0],  buffer [1],  buffer [2],  buffer [3],
1286       buffer [4],  buffer [5],  buffer [6],  buffer [7]);
1287     sim_printf ("%02x %02x %02x %02x %02x %02x %02x %02x\n",
1288       buffer [8],  buffer [9],  buffer [10], buffer [11],
1289       buffer [12], buffer [13], buffer [14], buffer [15]);
1290     uint version         =         (buffer [ip +  0] >> 4) & 0xf;
1291     uint ihl             =         (buffer [ip +  0]) & 0xf;
1292     uint payload_offset  =                  ip + ihl * 4;
1293     uint tos             =          buffer [ip +  1];
1294     uint tl              = ((uint) (buffer [ip +  2]) << 8) +
1295                                     buffer [ip +  3];
1296     uint id              = ((uint) (buffer [ip +  4]) << 8) +
1297                                     buffer [ip +  5];
1298     uint df              =         (buffer [ip +  6] & 0x40) ? 1 : 0;
1299     uint mf              =         (buffer [ip +  6] & 0x20) ? 1 : 0;
1300     uint fragment_offset = ((uint) (buffer [ip +  6] & 0x1f) << 8) +
1301                                     buffer [ip +  7];
1302     uint ttl             =          buffer [ip +  8];
1303     uint protocol        =          buffer [ip +  9];
1304     uint header_checksum = (((uint) buffer [ip + 10]) << 8) +
1305                                     buffer [ip + 11];
1306     uint source_address  = (((uint) buffer [ip + 12]) << 24) +
1307                            (((uint) buffer [ip + 13]) << 16) +
1308                            (((uint) buffer [ip + 14]) << 8) +
1309                                     buffer [ip + 15];
1310     uint dest_address    = (((uint) buffer [ip + 16]) << 24) +
1311                            (((uint) buffer [ip + 17]) << 16) +
1312                            (((uint) buffer [ip + 18]) << 8) +
1313                                     buffer [ip + 19];
1314     if (protocol == 1)
1315       {
1316         uint type = buffer [payload_offset + 0];
1317         if (type == 0x08)
1318           {
1319             sim_printf ("ICMP Echo Request %d.%d.%d.%d %d.%d.%d.%d\n",
1320               buffer [ip + 12], buffer [ip + 13], buffer [ip + 14], buffer [ip + 15],
1321               buffer [ip + 16], buffer [ip + 17], buffer [ip + 18], buffer [ip + 19]);
1322           }
1323         else
1324           {
1325             sim_printf ("ICMP 0x%02x\n", type);
1326             sim_printf ("%02x %02x %02x %02x %02x %02x %02x %02x\n",
1327               buffer [payload_offset + 0], buffer [payload_offset + 1], buffer [payload_offset + 2], buffer [payload_offset + 3],
1328               buffer [payload_offset + 4], buffer [payload_offset + 5], buffer [payload_offset + 6], buffer [payload_offset + 7]);
1329           }
1330       }
1331     if (protocol == 0x11)
1332       {
1333         sim_printf ("UDP\n");
1334 
1335       }
1336     else
1337       {
1338         sim_printf ("protocol %02x\n", protocol);
1339       }
1340   }
1341 
1342 void fnpTUNProcessEvent (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1343   {
1344     unint32 numunits = fnp_dev.numunits;
1345     for (int fnpno = 0; fnpno < numnumts; fnpno ++)
1346       {
1347         for (int lineno = 0; lineno < MAX_LINES; lineno ++)
1348           {
1349             struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1350             if (linep->is_tun)
1351               fnoTUNProcessLine (fnpno, lineno, linep);
1352           }
1353       }
1354   }
1355 #endif
1356 
1357 void fnpuv3270Poll (bool start)
     /* [previous][next][first][last][top][bottom][index][help] */
1358   {
1359     // Called at 100Hz; to 1 second poll
1360     fnpData.du3270_poll = start ? 100 : 0;
1361   }
1362 
1363 //
1364 // Connection callback handler for dialup connections
1365 //
1366 
1367 static void on_new_3270_connection (uv_stream_t * server, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
1368   {
1369     if (status < 0)
1370       {
1371         sim_printf ("\r[FNP emulation: TN3270 new connection error: %s]\r\n", uv_strerror(status));
1372         // error!
1373         return;
1374       }
1375 
1376     uv_tcp_t * client = (uv_tcp_t *) malloc (sizeof (uv_tcp_t));
1377     if (!client)
1378       {
1379         (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1380                       __func__, __FILE__, __LINE__);
1381 #if defined(USE_BACKTRACE)
1382 # if defined(SIGUSR2)
1383         (void)raise(SIGUSR2);
1384         /*NOTREACHED*/ /* unreachable */
1385 # endif /* if defined(SIGUSR2) */
1386 #endif /* if defined(USE_BACKTRACE) */
1387         abort();
1388       }
1389 
1390     uv_tcp_init (fnpData.loop, client);
1391     if (uv_accept (server, (uv_stream_t *) client) != 0)
1392       {
1393         uv_close ((uv_handle_t *) client, fuv_close_cb);
1394         return;
1395       }
1396 
1397     // Search for an available station
1398     uint stn_no;
1399     for (stn_no = 0; stn_no < IBM3270_STATIONS_MAX; stn_no ++)
1400       {
1401         if (fnpData.ibm3270ctlr[ASSUME0].stations[stn_no].client == NULL)
1402           break;
1403       }
1404     if (stn_no >= IBM3270_STATIONS_MAX)
1405       {
1406         // No stations available
1407         uv_close ((uv_handle_t *) client, fuv_close_cb);
1408         return;
1409       }
1410 
1411     uint fnpno  = fnpData.ibm3270ctlr[ASSUME0].fnpno;
1412     uint lineno = fnpData.ibm3270ctlr[ASSUME0].lineno;
1413     // Set the line client to NULL; the actual clients are in 'stations'
1414     fnpData.fnpUnitData[fnpno].MState.line[lineno].line_client = NULL;
1415 
1416     fnpData.ibm3270ctlr[ASSUME0].stations[stn_no].client = client;
1417 
1418     // Set up selection so the telnet negotiation can find the station.
1419     fnpData.ibm3270ctlr[ASSUME0].selDevChar = addr_map[stn_no];
1420 
1421     struct sockaddr name;
1422     int namelen = sizeof (name);
1423     int ret = uv_tcp_getpeername (client, & name, & namelen);
1424     if (ret < 0)
1425       {
1426         sim_printf ("\r[FNP emulation: CONNECT; addr err %d]\r\n", ret);
1427       }
1428     else
1429       {
1430         struct sockaddr_in * p = (struct sockaddr_in *) & name;
1431         sim_printf ("\r[FNP emulation: CONNECT %s]\r\n", inet_ntoa (p -> sin_addr));
1432       }
1433 
1434     uvClientData * p = (uvClientData *) malloc (sizeof (uvClientData));
1435     if (! p)
1436       {
1437          (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1438                         __func__, __FILE__, __LINE__);
1439 #if defined(USE_BACKTRACE)
1440 # if defined(SIGUSR2)
1441          (void)raise(SIGUSR2);
1442          /*NOTREACHED*/ /* unreachable */
1443 # endif /* if defined(SIGUSR2) */
1444 #endif /* if defined(USE_BACKTRACE) */
1445          abort();
1446       }
1447     client->data       = p;
1448     p->assoc           = false;
1449     p->fnpno           = fnpno;
1450     p->lineno          = lineno;
1451     p->nPos            = 0;
1452     p->ttype           = NULL;
1453     p->read_cb         = fnpuv_3270_readcb;
1454     p->write_cb        = fnpuv_start_3270_write;
1455     p->write_actual_cb = fnpuv_start_write_3270_actual;
1456     p->stationNo       = stn_no;
1457     p->telnetp         = ltnConnect3270 (client);
1458 
1459     if (! p->telnetp)
1460       {
1461         sim_warn ("ltnConnect3270 failed\n");
1462         return;
1463       }
1464     fnpuv_read_start (client);
1465     fnp3270ConnectPrompt (client);
1466        // uvClientData * p               = (uvClientData *) server->data;
1467        // struct t_line * linep          = & fnpData.fnpUnitData[p->fnpno].MState.line[p->lineno];
1468        // linep->accept_new_terminal     = true;
1469        // linep->was_CR                  = false;
1470        // //linep->listen                = false;
1471        // linep->inputBufferSize         = 0;
1472        // linep->ctrlStrIdx              = 0;
1473        // linep->breakAll                = false;
1474        // linep->handleQuit              = false;
1475        // linep->echoPlex                = false;
1476        // linep->crecho                  = false;
1477        // linep->lfecho                  = false;
1478        // linep->tabecho                 = false;
1479        // linep->replay                  = false;
1480        // linep->polite                  = false;
1481        // linep->prefixnl                = false;
1482        // linep->eight_bit_out           = false;
1483        // linep->eight_bit_in            = false;
1484        // linep->odd_parity              = false;
1485        // linep->output_flow_control     = false;
1486        // linep->input_flow_control      = false;
1487        // linep->block_xfer_in_frame_sz  = 0;
1488        // linep->block_xfer_out_frame_sz = 0;
1489        // (void)memset (linep->delay_table, 0, sizeof (linep->delay_table));
1490        // linep->inputSuspendLen         = 0;
1491        // (void)memset (linep->inputSuspendStr, 0, sizeof (linep->inputSuspendStr));
1492        // linep->inputResumeLen          = 0;
1493        // (void)memset (linep->inputResumeStr, 0, sizeof (linep->inputResumeStr));
1494        // linep->outputSuspendLen        = 0;
1495        // (void)memset (linep->outputSuspendStr, 0, sizeof (linep->outputSuspendStr));
1496        // linep->outputResumeLen         = 0;
1497        // (void)memset (linep->outputResumeStr, 0, sizeof (linep->outputResumeStr));
1498        // linep->frame_begin             = 0;
1499        // linep->frame_end               = 0;
1500        // (void)memset (linep->echnego, 0, sizeof (linep->echnego));
1501        // linep->line_break              = false;
1502   }
1503 
1504 int fnpuv3270Init (int telnet3270_port)
     /* [previous][next][first][last][top][bottom][index][help] */
1505   {
1506     // Ignore multiple calls; this means that once the listen port is
1507     // opened, it can't be changed. Fixing this requires non-trivial
1508     // changes.
1509     if (fnpData.du3270_server_inited)
1510       {
1511         sim_printf ("\r[FNP emulation: FNP already initialized]\r\n");
1512         return 1;
1513       }
1514     if (! fnpData.loop)
1515       fnpData.loop = uv_default_loop ();
1516     // Initialize the server socket
1517     uv_tcp_init (fnpData.loop, & fnpData.du3270_server);
1518 
1519     // Flag the this server as being a 3270
1520     fnpData.du3270_server.data = NULL;
1521 
1522     // Bind and listen
1523     struct sockaddr_in addr;
1524     uv_ip4_addr (fnpData.telnet_address, telnet3270_port, & addr);
1525     uv_tcp_bind (& fnpData.du3270_server, (const struct sockaddr *) & addr, 0);
1526     int r = uv_listen ((uv_stream_t *) & fnpData.du3270_server, DEFAULT_BACKLOG,
1527                    on_new_3270_connection);
1528     if (r)
1529      {
1530         sim_printf ("\r[FNP emulation: listen error: %s:%ld: %s]\r\n",
1531                     fnpData.telnet_address, (long) telnet3270_port, uv_strerror(r));
1532         return 1;
1533      }
1534     fnpData.du3270_server_inited = true;
1535     sim_printf ("\r[FNP emulation: TN3270 server listening on %s:%ld]\r\n",
1536                 fnpData.telnet_address, (long) telnet3270_port);
1537     fnpuv3270Poll (false);
1538     return 0;
1539   }

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