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 (data [datalen - 1] == 0x03) // ETX
 654       {
 655         actual_datalen --;
 656         //send_eor = true;
 657       }
 658     if (data [0] == 0x02) // STX
 659       {
 660         actual_data_start ++;
 661         actual_datalen --;
 662         if (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)
 671       //fnpuv_send_eor (client);
 672   }
 673 
 674 // C-string wrapper for fnpuv_start_write
 675 
 676 void fnpuv_start_writestr (uv_tcp_t * client, unsigned char * data)
     /* [previous][next][first][last][top][bottom][index][help] */
 677   {
 678     //fnpuv_start_write (client, data, (ssize_t) strlen (data));
 679     if (! client || uv_is_closing ((uv_handle_t *) client))
 680       return;
 681     if (! client->data)
 682       {
 683         sim_warn ("fnpuv_start_writestr bad client data\r\n");
 684         return;
 685       }
 686     uvClientData * p = client->data;
 687     (* p->write_cb) (client, data, (ssize_t) strlen ((char *) data));
 688   }
 689 
 690 void fnpuv_send_eor (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 691   {
 692     if (! client || uv_is_closing ((uv_handle_t *) client))
 693       return;
 694     if (! client->data)
 695       {
 696         sim_warn ("fnpuv_send_eor bad client data\r\n");
 697         return;
 698       }
 699     uvClientData * p = (uvClientData *) client->data;
 700     ltnEOR (p->telnetp);
 701     //unsigned char EOR [] = { TELNET_IAC, TELNET_EOR };
 702     //fnpuv_start_write_special (client, (char *) EOR, sizeof (EOR));
 703   }
 704 
 705 void fnpuv_recv_eor (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 706   {
 707     fnpRecvEOR (client);
 708   }
 709 
 710 //
 711 // Enable reading on connection
 712 //
 713 
 714 void fnpuv_read_start (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 715   {
 716     if (! client || uv_is_closing ((uv_handle_t *) client))
 717       return;
 718     uv_read_start ((uv_stream_t *) client, alloc_buffer, fuv_read_cb);
 719   }
 720 
 721 //
 722 // Disable reading on connection
 723 //
 724 
 725 void fnpuv_read_stop (uv_tcp_t * client)
     /* [previous][next][first][last][top][bottom][index][help] */
 726   {
 727     if (! client || uv_is_closing ((uv_handle_t *) client))
 728       return;
 729     uv_read_stop ((uv_stream_t *) client);
 730   }
 731 
 732 //
 733 // Connection callback handler for dialup connections
 734 //
 735 
 736 static void on_new_connection (uv_stream_t * server, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
 737   {
 738     if (status < 0)
 739       {
 740         sim_printf ("\r[FNP emulation: new connection error: %s]\r\n", uv_strerror(status));
 741         // error!
 742         return;
 743       }
 744 
 745     uv_tcp_t * client = (uv_tcp_t *) malloc (sizeof (uv_tcp_t));
 746     if (!client)
 747       {
 748         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 749                        __func__, __FILE__, __LINE__);
 750 #if defined(USE_BACKTRACE)
 751 # if defined(SIGUSR2)
 752         (void)raise(SIGUSR2);
 753         /*NOTREACHED*/ /* unreachable */
 754 # endif /* if defined(SIGUSR2) */
 755 #endif /* if defined(USE_BACKTRACE) */
 756         abort();
 757       }
 758     uv_tcp_init (fnpData.loop, client);
 759     if (uv_accept (server, (uv_stream_t *) client) != 0)
 760       {
 761         uv_close ((uv_handle_t *) client, fuv_close_cb);
 762         return;
 763       }
 764 
 765     // if server->data is non-null, this is a slave server; else a dialup
 766     // server
 767     if (server->data)
 768       {
 769         uvClientData * p = (uvClientData *) server->data;
 770         struct t_line * linep = & fnpData.fnpUnitData[p->fnpno].MState.line[p->lineno];
 771 
 772         // Slave servers only handle a single connection at a time
 773         if (linep->line_client)
 774           {
 775             uv_close ((uv_handle_t *) client, fuv_close_cb);
 776 # if defined(TESTING)
 777 sim_printf ("\r[FNP emulation: dropping 2nd slave]\r\n");
 778 # endif
 779             return;
 780           }
 781 
 782         linep->line_client = client;
 783       }
 784 
 785     struct sockaddr name;
 786     int namelen = sizeof (name);
 787     uv_tcp_nodelay (client,1);
 788     int ret = uv_tcp_getpeername (client, & name, & namelen);
 789     if (ret < 0)
 790       {
 791         sim_printf ("\r[FNP emulation: CONNECT; addr err %d]\r\n", ret);
 792       }
 793     else
 794       {
 795         struct sockaddr_in * p = (struct sockaddr_in *) & name;
 796         sim_printf ("\r[FNP emulation: CONNECT %s]\r\n", inet_ntoa (p -> sin_addr));
 797       }
 798 
 799     uvClientData * p = (uvClientData *) malloc (sizeof (uvClientData));
 800     if (! p)
 801       {
 802          (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
 803                         __func__, __FILE__, __LINE__);
 804 #if defined(USE_BACKTRACE)
 805 # if defined(SIGUSR2)
 806         (void)raise(SIGUSR2);
 807         /*NOTREACHED*/ /* unreachable */
 808 # endif /* if defined(SIGUSR2) */
 809 #endif /* if defined(USE_BACKTRACE) */
 810         abort();
 811       }
 812     client->data       = p;
 813     p->assoc           = false;
 814     p->nPos            = 0;
 815     p->ttype           = NULL;
 816     p->write_actual_cb = fnpuv_start_write_actual;
 817     // dialup connections are routed through libtelent
 818     if (! server->data)
 819       {
 820         p->read_cb  = fnpuv_unassociated_readcb;
 821         p->write_cb = fnpuv_start_write;
 822         p->telnetp  = ltnConnect (client);
 823 
 824         if (! p->telnetp)
 825           {
 826              sim_warn ("ltnConnect failed\n");
 827              return;
 828           }
 829       }
 830     else
 831       {
 832         p->read_cb       = fnpuv_associated_readcb;
 833         p->write_cb      = fnpuv_start_write_actual;
 834         p->telnetp       = NULL;
 835         uvClientData * q = (uvClientData *) server->data;
 836         p->fnpno         = q->fnpno;
 837         p->lineno        = q->lineno;
 838         p->assoc         = true;
 839       }
 840     fnpuv_read_start (client);
 841     if (! server->data)
 842       fnpConnectPrompt (client);
 843     else
 844       {
 845         uvClientData * p = (uvClientData *) server->data;
 846         struct t_line * linep = & fnpData.fnpUnitData[p->fnpno].MState.line[p->lineno];
 847         if (linep->lineType == 0) /* LINE_NONE */
 848           linep->lineType = 1; /* LINE_ASCII */
 849         linep->accept_new_terminal = true;
 850         reset_line (linep);
 851       }
 852   }
 853 
 854 //
 855 // Setup the dialup listener
 856 //
 857 
 858 int fnpuvInit (int telnet_port, char * telnet_address)
     /* [previous][next][first][last][top][bottom][index][help] */
 859   {
 860     // Ignore multiple calls; this means that once the listen port is
 861     // opened, it can't be changed. Fixing this requires non-trivial
 862     // changes.
 863     if (fnpData.du_server_inited)
 864       {
 865         sim_printf ("\r[FNP emulation: FNP already initialized]\r\n");
 866         return 1;
 867       }
 868 
 869     if (! fnpData.loop)
 870       fnpData.loop = uv_default_loop ();
 871 
 872     // Initialize the server socket
 873     fnpData.loop = uv_default_loop ();
 874     uv_tcp_init (fnpData.loop, & fnpData.du_server);
 875 
 876 // XXX to do clean shutdown
 877 // uv_loop_close (fnpData.loop);
 878 
 879     // Flag the this server as being the dialup server
 880     fnpData.du_server.data = NULL;
 881 
 882     // Bind and listen
 883     struct sockaddr_in addr;
 884     uv_ip4_addr (telnet_address, telnet_port, & addr);
 885     uv_tcp_bind (& fnpData.du_server, (const struct sockaddr *) & addr, 0);
 886     int r = uv_listen ((uv_stream_t *) & fnpData.du_server, DEFAULT_BACKLOG,
 887                        on_new_connection);
 888     if (r)
 889      {
 890         sim_printf ("\r[FNP emulation: listen error: %s:%ld: %s]\r\n", telnet_address, (long) telnet_port, uv_strerror(r));
 891         return 1;
 892      }
 893     fnpData.du_server_inited = true;
 894     sim_printf ("\r[FNP emulation: TELNET server listening on %s:%ld]\r\n", telnet_address, (long) telnet_port);
 895     return 0;
 896   }
 897 
 898 // Make a single pass through the libuv event queue.
 899 
 900 void fnpuvProcessEvent (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 901   {
 902     // UV_RUN_NOWAIT: Poll for i/o once but don’t block if there are no
 903     // pending callbacks. Returns zero if done (no active handles or
 904     // requests left), or non-zero if more callbacks are expected (meaning
 905     // you should run the event loop again sometime in the future).
 906 
 907     // Note that uv_run returns non-zero if that are any active_handles
 908     // (e.g. TCP connection listener open); that means a non-zero
 909     // return does not mean i/o is pending.
 910     if (! fnpData.loop)
 911       return;
 912     /* int ret = */ uv_run (fnpData.loop, UV_RUN_NOWAIT);
 913   }
 914 
 915 //
 916 // dialout line connection callback
 917 //
 918 
 919 static void on_dialout_connect (uv_connect_t * server, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
 920   {
 921     sim_printf ("\r[FNP emulation: dialout connect]\r\n");
 922     uvClientData * p = (uvClientData *) server->handle->data;
 923     // If data is NULL, assume that the line has already been torn down.
 924     if (! p)
 925       {
 926          sim_printf ("\r[FNP emulation: NOTE: on_dialout_connect called with data == NULL]\r\n");
 927          return;
 928       }
 929     struct t_line * linep = & fnpData.fnpUnitData[p->fnpno].MState.line[p->lineno];
 930     if (status < 0)
 931       {
 932         sim_printf ("\r[FNP emulation: dialout connection error: %s]\r\n", uv_strerror(status));
 933         //sim_printf ("%p\n", p);
 934         //sim_printf ("%d.%d\n", p->fnpno, p->lineno);
 935         linep->acu_dial_failure = true;
 936         return;
 937       }
 938 
 939     uv_read_start ((uv_stream_t *) linep->line_client, alloc_buffer, fuv_read_cb);
 940     linep->listen = true;
 941     if (linep->lineType == 0) /* LINE_NONE */
 942       linep->lineType = 1; /* LINE_ASCII */
 943     linep->accept_new_terminal = true;
 944     linep->was_CR              = false;
 945     linep->line_client->data   = p;
 946     if (p->telnetp)
 947       {
 948         ltnDialout (p->telnetp);
 949       }
 950   }
 951 
 952 //
 953 // Perform a dialout
 954 //
 955 
 956 void fnpuv_dial_out (uint fnpno, uint lineno, word36 d1, word36 d2, word36 d3)
     /* [previous][next][first][last][top][bottom][index][help] */
 957   {
 958     if (! fnpData.loop)
 959       return;
 960     sim_printf ("\r[FNP emulation: received dial_out %c.h%03d %012llu %012llu %012llu]\r\n", fnpno+'a', lineno,
 961             (unsigned long long)d1, (unsigned long long)d2, (unsigned long long)d3);
 962     struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
 963     uint d01   = (d1 >> 30) & 017;
 964     uint d02   = (d1 >> 24) & 017;
 965     uint d03   = (d1 >> 18) & 017;
 966     uint d04   = (d1 >> 12) & 017;
 967     uint d05   = (d1 >>  6) & 017;
 968     uint d06   = (d1 >>  0) & 017;
 969     uint d07   = (d2 >> 30) & 017;
 970     uint d08   = (d2 >> 24) & 017;
 971     uint d09   = (d2 >> 18) & 017;
 972     uint d10   = (d2 >> 12) & 017;
 973     uint d11   = (d2 >>  6) & 017;
 974     uint d12   = (d2 >>  0) & 017;
 975     uint flags = (d3 >> 30) & 017;
 976     uint p1    = (d3 >> 24) & 017;
 977     uint p2    = (d3 >> 18) & 017;
 978     uint p3    = (d3 >> 12) & 017;
 979     uint p4    = (d3 >>  6) & 017;
 980     uint p5    = (d3 >>  0) & 017;
 981 
 982     uint oct1  = d01 * 100 + d02 * 10 + d03;
 983     uint oct2  = d04 * 100 + d05 * 10 + d06;
 984     uint oct3  = d07 * 100 + d08 * 10 + d09;
 985     uint oct4  = d10 * 100 + d11 * 10 + d12;
 986 
 987     uint port  = ((((p1 * 10) + p2) * 10 + p3) * 10 + p4) * 10 + p5;
 988 
 989 // Flags
 990 //   1    Use Telnet
 991 #if defined(TUN)
 992 //   2    Use TUN device (in which case 'port' is the netmask
 993 //   4
 994 
 995     linep->is_tun = false;
 996     if (flags & 2)
 997       {
 998         if (linep->tun_fd <= 0)
 999           {
1000             char a_name [IFNAMSIZ] = "dps8m";
1001             linep->tun_fd = tun_alloc (a_name);
1002             if (linep->tun_fd < 0)
1003               {
1004                 sim_printf ("\r[FNP emulation: dialout TUN tun_alloc returned %d errno %d]\r\n", linep->tun_fd, errno);
1005                 return;
1006              }
1007             int flags = fcntl (linep->tun_fd, F_GETFL, 0);
1008             if (flags < 0)
1009               {
1010                 sim_printf ("\r[FNP emulation: dialout TUN F_GETFL returned < 0]\r\n");
1011                 return;
1012               }
1013             flags |= O_NONBLOCK;
1014             int ret = fcntl (linep->tun_fd, F_SETFL, flags);
1015             if (ret)
1016               {
1017                 sim_printf ("\r[FNP emulation: dialout TUN F_SETFL returned %d]\r\n", ret);
1018                 return;
1019               }
1020           }
1021           linep->is_tun = true;
1022           return;
1023         }
1024 #endif /* if defined(TUN) */
1025 
1026     // firewall
1027 
1028     // Default is accept
1029     bool accept = true;
1030     uint32_t ip_addr = (uint32_t) ((oct1 << 24) | (oct2 << 16) | (oct3 << 8) | oct4);
1031     uint this_line = encodeline (fnpno, lineno);
1032     for (int i = 0; i < n_fw_entries; i ++)
1033       {
1034         struct fw_entry_s * p = fw_entries + i;
1035         if (this_line < p->line_0 || this_line > p->line_1)
1036           continue;
1037         if ((ip_addr & p->cidr_mask) != (p->ipaddr & p->cidr_mask))
1038           continue;
1039         accept = p->accept;
1040         break;
1041       }
1042 
1043     if (! accept)
1044       {
1045         sim_printf ("Dialout %c.d%03d denied\r\n", fnpno + 'a', lineno);
1046         linep->acu_dial_failure = true;
1047         return;
1048       }
1049 
1050     char ipaddr [256];
1051     (void)sprintf (ipaddr, "%d.%d.%d.%d", oct1, oct2, oct3, oct4);
1052     sim_printf ("calling %s:%d\n", ipaddr,port);
1053 
1054     struct sockaddr_in dest;
1055     uv_ip4_addr(ipaddr, (int) port, &dest);
1056 
1057     linep->line_client = (uv_tcp_t *) malloc (sizeof (uv_tcp_t));
1058     if (!linep->line_client)
1059       {
1060         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1061                        __func__, __FILE__, __LINE__);
1062 #if defined(USE_BACKTRACE)
1063 # if defined(SIGUSR2)
1064         (void)raise(SIGUSR2);
1065         /*NOTREACHED*/ /* unreachable */
1066 # endif /* if defined(SIGUSR2) */
1067 #endif /* if defined(USE_BACKTRACE) */
1068         abort();
1069       }
1070     uv_tcp_init (fnpData.loop, linep->line_client);
1071 
1072     uvClientData * p = (uvClientData *) malloc (sizeof (uvClientData));
1073     if (! p)
1074       {
1075         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1076                        __func__, __FILE__, __LINE__);
1077 #if defined(USE_BACKTRACE)
1078 # if defined(SIGUSR2)
1079         (void)raise(SIGUSR2);
1080         /*NOTREACHED*/ /* unreachable */
1081 # endif /* if defined(SIGUSR2) */
1082 #endif /* if defined(USE_BACKTRACE) */
1083         abort();
1084       }
1085     p->assoc                 = true;
1086     p->read_cb               = fnpuv_associated_readcb;
1087     p->nPos                  = 0;
1088     p->ttype                 = NULL;
1089     p->fnpno                 = fnpno;
1090     p->lineno                = lineno;
1091     linep->line_client->data = p;
1092 
1093     if (flags & 1)
1094       {
1095         p->write_cb        = fnpuv_start_write;
1096         p->write_actual_cb = fnpuv_start_write_actual;
1097         p->telnetp         = ltnConnect (linep->line_client);
1098         if (! p->telnetp)
1099           {
1100               sim_warn ("ltnConnect failed\n");
1101           }
1102       }
1103     else
1104       {
1105         p->write_cb        = fnpuv_start_write_actual;
1106         p->write_actual_cb = fnpuv_start_write_actual;
1107         p->telnetp         = NULL; // Mark this line as 'not a telnet connection'
1108       }
1109 
1110     uv_tcp_connect (& linep->doConnect, linep->line_client, (const struct sockaddr *) & dest, on_dialout_connect);
1111   }
1112 
1113 
1114 
1115 
1116 
1117 
1118 
1119 
1120 
1121 
1122 
1123 
1124 
1125 
1126 
1127 
1128 
1129 
1130 
1131 //
1132 // Start a slave line connection listener.
1133 //
1134 
1135 void fnpuv_open_slave (uint fnpno, uint lineno)
     /* [previous][next][first][last][top][bottom][index][help] */
1136   {
1137     if (! fnpData.loop)
1138       return;
1139     sim_printf ("\r[FNP emulation: fnpuv_open_slave %d.%d]\r\n", fnpno, lineno);
1140     struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1141 
1142     // Do we already have a listening port (ie not first time)?
1143     if (linep->server.data)
1144       return;
1145 
1146     uv_tcp_init (fnpData.loop, & linep->server);
1147 
1148     // Mark this server has being a slave server
1149     // XXX This does not get freed during a normal shutdown, as Multics
1150     // XXX doesn't tell idle slave lines anything. The emulator shutdown
1151     // XXX needs to call an FNP cleanup routine that frees this.
1152 
1153     uvClientData * p = (uvClientData *) malloc (sizeof (uvClientData));
1154     if (! p)
1155       {
1156         (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1157                        __func__, __FILE__, __LINE__);
1158 #if defined(USE_BACKTRACE)
1159 # if defined(SIGUSR2)
1160         (void)raise(SIGUSR2);
1161         /*NOTREACHED*/ /* unreachable */
1162 # endif /* if defined(SIGUSR2) */
1163 #endif /* if defined(USE_BACKTRACE) */
1164         abort();
1165       }
1166     p->assoc           = false;
1167     p->read_cb         = fnpuv_associated_readcb;
1168     p->write_cb        = fnpuv_start_write_actual;
1169     p->write_actual_cb = fnpuv_start_write_actual;
1170     p->nPos            = 0;
1171     p->ttype           = NULL;
1172     p->fnpno           = fnpno;
1173     p->lineno          = lineno;
1174     linep->server.data = p;
1175     linep->line_client = NULL;
1176 
1177     struct sockaddr_in addr;
1178     uv_ip4_addr (fnpData.telnet_address, linep->port, & addr);
1179     uv_tcp_bind (& linep->server, (const struct sockaddr *) & addr, 0);
1180     int r = uv_listen ((uv_stream_t *) & linep->server, DEFAULT_BACKLOG,
1181                        on_new_connection);
1182     if (r)
1183      {
1184         sim_printf ("\r[FNP emulation: listen error: %s:%ld: %s]\r\n", fnpData.telnet_address, (long) linep->port, uv_strerror(r));
1185      }
1186     sim_printf ("\r[FNP emulation: listening on %s:%ld]\r\n", fnpData.telnet_address, (long) linep->port);
1187   }
1188 
1189 #if defined(TUN)
1190 static void processPacketInput (int fnpno, int lineno, unsigned char * buf, ssize_t nread)
     /* [previous][next][first][last][top][bottom][index][help] */
1191   {
1192     //uvClientData * p = (uvClientData *) client->data;
1193     //uint fnpno       = p -> fnpno;
1194     //uint lineno      = p -> lineno;
1195     if (fnpno >= N_FNP_UNITS_MAX || lineno >= MAX_LINES)
1196       {
1197         sim_printf ("\r[FNP emulation: processPacketInput bogus client data]\r\n");
1198         return;
1199       }
1200 //sim_printf ("assoc. %d.%d nread %ld <%*s>\n", fnpno, lineno, (long) nread, buf);
1201 //{for (int i = 0; i < nread; i ++) sim_printf (" %03o", buf[i]);
1202  //sim_printf ("\n");
1203 //}
1204 
1205     struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1206     if (linep->inBuffer)
1207       {
1208         unsigned char * new = realloc (linep->inBuffer, (unsigned long) (linep->inSize + nread));
1209         if (! new)
1210           {
1211             (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1212                            __func__, __FILE__, __LINE__)
1213 # if defined(USE_BACKTRACE)
1214 #  if defined(SIGUSR2)
1215             (void)raise(SIGUSR2);
1216             /*NOTREACHED*/ /* unreachable */
1217 #  endif /* if defined(SIGUSR2) */
1218 # endif /* if defined(USE_BACKTRACE) */
1219             abort();
1220           }
1221         memcpy (new + linep->inSize, buf, (unsigned long) nread);
1222         linep->inSize  += nread;
1223         linep->inBuffer = new;
1224       }
1225     else
1226       {
1227         linep->inBuffer = malloc ((unsigned long) nread);
1228         if (! linep->inBuffer)
1229           {
1230             (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1231                            __func__, __FILE__, __LINE__);
1232 # if defined(USE_BACKTRACE)
1233 #  if defined(SIGUSR2)
1234             (void)raise(SIGUSR2);
1235             /*NOTREACHED*/ /* unreachable */
1236 #  endif /* if defined(SIGUSR2) */
1237 # endif /* if defined(USE_BACKTRACE) */
1238             abort();
1239           }
1240         memcpy (linep->inBuffer, buf, (unsigned long) nread);
1241         linep->inSize = (uint) nread;
1242         linep->inUsed = 0;
1243       }
1244 
1245   }
1246 
1247 static void fnoTUNProcessLine (int fnpno, int lineno, struct t_line * linep)
     /* [previous][next][first][last][top][bottom][index][help] */
1248   {
1249 /* Note that "buffer" should be at least the MTU size of the interface, eg 1500 bytes */
1250     unsigned char buffer [1500 + 16];
1251     ssize_t nread = read (linep->tun_fd, buffer, sizeof (buffer));
1252     if (nread < 0)
1253       {
1254         //perror ("Reading from interface");
1255         //close (linep->tun_fd);
1256         //exit (1);
1257         if (errno == EAGAIN)
1258           return;
1259         sim_printf ("%ld %ld\n", (long) nread, (long) errno);
1260         return;
1261       }
1262 
1263 // To make debugging easier, return data as a hex string rather than binary.
1264 // When the stack interface is debugged, switch to binary.
1265     unsigned char xbufr [2 * (1500 + 16)];
1266     unsigned char bin2hex [16] = "0123456789ABCDEF";
1267 
1268     for (uint i = 0; i > nread; i ++)
1269       {
1270         xbufr [i * 2 + 0] = bin2hex [(buffer [i] >> 4) & 0xf];
1271         xbufr [i * 2 + 1] = bin2hex [(buffer [i] >> 0) & 0xf];
1272       }
1273      xbufr [nread * 2] = 0;
1274      processPacketInput (fnpno, lineno, xbufr, nread * 2 + 1);
1275 
1276 // Debugging
1277 // 4 bytes of metadata
1278 # define ip 4
1279     /* Do whatever with the data */
1280     sim_printf("Read %ld bytes\n", (long) nread);
1281     sim_printf ("%02x %02x %02x %02x %02x %02x %02x %02x\n",
1282       buffer [0],  buffer [1],  buffer [2],  buffer [3],
1283       buffer [4],  buffer [5],  buffer [6],  buffer [7]);
1284     sim_printf ("%02x %02x %02x %02x %02x %02x %02x %02x\n",
1285       buffer [8],  buffer [9],  buffer [10], buffer [11],
1286       buffer [12], buffer [13], buffer [14], buffer [15]);
1287     uint version         =         (buffer [ip +  0] >> 4) & 0xf;
1288     uint ihl             =         (buffer [ip +  0]) & 0xf;
1289     uint payload_offset  =                  ip + ihl * 4;
1290     uint tos             =          buffer [ip +  1];
1291     uint tl              = ((uint) (buffer [ip +  2]) << 8) +
1292                                     buffer [ip +  3];
1293     uint id              = ((uint) (buffer [ip +  4]) << 8) +
1294                                     buffer [ip +  5];
1295     uint df              =         (buffer [ip +  6] & 0x40) ? 1 : 0;
1296     uint mf              =         (buffer [ip +  6] & 0x20) ? 1 : 0;
1297     uint fragment_offset = ((uint) (buffer [ip +  6] & 0x1f) << 8) +
1298                                     buffer [ip +  7];
1299     uint ttl             =          buffer [ip +  8];
1300     uint protocol        =          buffer [ip +  9];
1301     uint header_checksum = (((uint) buffer [ip + 10]) << 8) +
1302                                     buffer [ip + 11];
1303     uint source_address  = (((uint) buffer [ip + 12]) << 24) +
1304                            (((uint) buffer [ip + 13]) << 16) +
1305                            (((uint) buffer [ip + 14]) << 8) +
1306                                     buffer [ip + 15];
1307     uint dest_address    = (((uint) buffer [ip + 16]) << 24) +
1308                            (((uint) buffer [ip + 17]) << 16) +
1309                            (((uint) buffer [ip + 18]) << 8) +
1310                                     buffer [ip + 19];
1311     if (protocol == 1)
1312       {
1313         uint type = buffer [payload_offset + 0];
1314         if (type == 0x08)
1315           {
1316             sim_printf ("ICMP Echo Request %d.%d.%d.%d %d.%d.%d.%d\n",
1317               buffer [ip + 12], buffer [ip + 13], buffer [ip + 14], buffer [ip + 15],
1318               buffer [ip + 16], buffer [ip + 17], buffer [ip + 18], buffer [ip + 19]);
1319           }
1320         else
1321           {
1322             sim_printf ("ICMP 0x%02x\n", type);
1323             sim_printf ("%02x %02x %02x %02x %02x %02x %02x %02x\n",
1324               buffer [payload_offset + 0], buffer [payload_offset + 1], buffer [payload_offset + 2], buffer [payload_offset + 3],
1325               buffer [payload_offset + 4], buffer [payload_offset + 5], buffer [payload_offset + 6], buffer [payload_offset + 7]);
1326           }
1327       }
1328     if (protocol == 0x11)
1329       {
1330         sim_printf ("UDP\n");
1331 
1332       }
1333     else
1334       {
1335         sim_printf ("protocol %02x\n", protocol);
1336       }
1337   }
1338 
1339 void fnpTUNProcessEvent (void)
     /* [previous][next][first][last][top][bottom][index][help] */
1340   {
1341     unint32 numunits = fnp_dev.numunits;
1342     for (int fnpno = 0; fnpno < numnumts; fnpno ++)
1343       {
1344         for (int lineno = 0; lineno < MAX_LINES; lineno ++)
1345           {
1346             struct t_line * linep = & fnpData.fnpUnitData[fnpno].MState.line[lineno];
1347             if (linep->is_tun)
1348               fnoTUNProcessLine (fnpno, lineno, linep);
1349           }
1350       }
1351   }
1352 #endif
1353 
1354 void fnpuv3270Poll (bool start)
     /* [previous][next][first][last][top][bottom][index][help] */
1355   {
1356     // Called at 100Hz; to 1 second poll
1357     fnpData.du3270_poll = start ? 100 : 0;
1358   }
1359 
1360 //
1361 // Connection callback handler for dialup connections
1362 //
1363 
1364 static void on_new_3270_connection (uv_stream_t * server, int status)
     /* [previous][next][first][last][top][bottom][index][help] */
1365   {
1366     if (status < 0)
1367       {
1368         sim_printf ("\r[FNP emulation: TN3270 new connection error: %s]\r\n", uv_strerror(status));
1369         // error!
1370         return;
1371       }
1372 
1373     uv_tcp_t * client = (uv_tcp_t *) malloc (sizeof (uv_tcp_t));
1374     if (!client)
1375       {
1376         (void)fprintf(stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1377                       __func__, __FILE__, __LINE__);
1378 #if defined(USE_BACKTRACE)
1379 # if defined(SIGUSR2)
1380         (void)raise(SIGUSR2);
1381         /*NOTREACHED*/ /* unreachable */
1382 # endif /* if defined(SIGUSR2) */
1383 #endif /* if defined(USE_BACKTRACE) */
1384         abort();
1385       }
1386 
1387     uv_tcp_init (fnpData.loop, client);
1388     if (uv_accept (server, (uv_stream_t *) client) != 0)
1389       {
1390         uv_close ((uv_handle_t *) client, fuv_close_cb);
1391         return;
1392       }
1393 
1394     // Search for an available station
1395     uint stn_no;
1396     for (stn_no = 0; stn_no < IBM3270_STATIONS_MAX; stn_no ++)
1397       {
1398         if (fnpData.ibm3270ctlr[ASSUME0].stations[stn_no].client == NULL)
1399           break;
1400       }
1401     if (stn_no >= IBM3270_STATIONS_MAX)
1402       {
1403         // No stations available
1404         uv_close ((uv_handle_t *) client, fuv_close_cb);
1405         return;
1406       }
1407 
1408     uint fnpno  = fnpData.ibm3270ctlr[ASSUME0].fnpno;
1409     uint lineno = fnpData.ibm3270ctlr[ASSUME0].lineno;
1410     // Set the line client to NULL; the actual clients are in 'stations'
1411     fnpData.fnpUnitData[fnpno].MState.line[lineno].line_client = NULL;
1412 
1413     fnpData.ibm3270ctlr[ASSUME0].stations[stn_no].client = client;
1414 
1415     // Set up selection so the telnet negotiation can find the station.
1416     fnpData.ibm3270ctlr[ASSUME0].selDevChar = addr_map[stn_no];
1417 
1418     struct sockaddr name;
1419     int namelen = sizeof (name);
1420     int ret = uv_tcp_getpeername (client, & name, & namelen);
1421     if (ret < 0)
1422       {
1423         sim_printf ("\r[FNP emulation: CONNECT; addr err %d]\r\n", ret);
1424       }
1425     else
1426       {
1427         struct sockaddr_in * p = (struct sockaddr_in *) & name;
1428         sim_printf ("\r[FNP emulation: CONNECT %s]\r\n", inet_ntoa (p -> sin_addr));
1429       }
1430 
1431     uvClientData * p = (uvClientData *) malloc (sizeof (uvClientData));
1432     if (! p)
1433       {
1434          (void)fprintf (stderr, "\rFATAL: Out of memory! Aborting at %s[%s:%d]\r\n",
1435                         __func__, __FILE__, __LINE__);
1436 #if defined(USE_BACKTRACE)
1437 # if defined(SIGUSR2)
1438          (void)raise(SIGUSR2);
1439          /*NOTREACHED*/ /* unreachable */
1440 # endif /* if defined(SIGUSR2) */
1441 #endif /* if defined(USE_BACKTRACE) */
1442          abort();
1443       }
1444     client->data       = p;
1445     p->assoc           = false;
1446     p->fnpno           = fnpno;
1447     p->lineno          = lineno;
1448     p->nPos            = 0;
1449     p->ttype           = NULL;
1450     p->read_cb         = fnpuv_3270_readcb;
1451     p->write_cb        = fnpuv_start_3270_write;
1452     p->write_actual_cb = fnpuv_start_write_3270_actual;
1453     p->stationNo       = stn_no;
1454     p->telnetp         = ltnConnect3270 (client);
1455 
1456     if (! p->telnetp)
1457       {
1458         sim_warn ("ltnConnect3270 failed\n");
1459         return;
1460       }
1461     fnpuv_read_start (client);
1462     fnp3270ConnectPrompt (client);
1463        // uvClientData * p               = (uvClientData *) server->data;
1464        // struct t_line * linep          = & fnpData.fnpUnitData[p->fnpno].MState.line[p->lineno];
1465        // linep->accept_new_terminal     = true;
1466        // linep->was_CR                  = false;
1467        // //linep->listen                = false;
1468        // linep->inputBufferSize         = 0;
1469        // linep->ctrlStrIdx              = 0;
1470        // linep->breakAll                = false;
1471        // linep->handleQuit              = false;
1472        // linep->echoPlex                = false;
1473        // linep->crecho                  = false;
1474        // linep->lfecho                  = false;
1475        // linep->tabecho                 = false;
1476        // linep->replay                  = false;
1477        // linep->polite                  = false;
1478        // linep->prefixnl                = false;
1479        // linep->eight_bit_out           = false;
1480        // linep->eight_bit_in            = false;
1481        // linep->odd_parity              = false;
1482        // linep->output_flow_control     = false;
1483        // linep->input_flow_control      = false;
1484        // linep->block_xfer_in_frame_sz  = 0;
1485        // linep->block_xfer_out_frame_sz = 0;
1486        // (void)memset (linep->delay_table, 0, sizeof (linep->delay_table));
1487        // linep->inputSuspendLen         = 0;
1488        // (void)memset (linep->inputSuspendStr, 0, sizeof (linep->inputSuspendStr));
1489        // linep->inputResumeLen          = 0;
1490        // (void)memset (linep->inputResumeStr, 0, sizeof (linep->inputResumeStr));
1491        // linep->outputSuspendLen        = 0;
1492        // (void)memset (linep->outputSuspendStr, 0, sizeof (linep->outputSuspendStr));
1493        // linep->outputResumeLen         = 0;
1494        // (void)memset (linep->outputResumeStr, 0, sizeof (linep->outputResumeStr));
1495        // linep->frame_begin             = 0;
1496        // linep->frame_end               = 0;
1497        // (void)memset (linep->echnego, 0, sizeof (linep->echnego));
1498        // linep->line_break              = false;
1499   }
1500 
1501 int fnpuv3270Init (int telnet3270_port)
     /* [previous][next][first][last][top][bottom][index][help] */
1502   {
1503     // Ignore multiple calls; this means that once the listen port is
1504     // opened, it can't be changed. Fixing this requires non-trivial
1505     // changes.
1506     if (fnpData.du3270_server_inited)
1507       {
1508         sim_printf ("\r[FNP emulation: FNP already initialized]\r\n");
1509         return 1;
1510       }
1511     if (! fnpData.loop)
1512       fnpData.loop = uv_default_loop ();
1513     // Initialize the server socket
1514     uv_tcp_init (fnpData.loop, & fnpData.du3270_server);
1515 
1516     // Flag the this server as being a 3270
1517     fnpData.du3270_server.data = NULL;
1518 
1519     // Bind and listen
1520     struct sockaddr_in addr;
1521     uv_ip4_addr (fnpData.telnet_address, telnet3270_port, & addr);
1522     uv_tcp_bind (& fnpData.du3270_server, (const struct sockaddr *) & addr, 0);
1523     int r = uv_listen ((uv_stream_t *) & fnpData.du3270_server, DEFAULT_BACKLOG,
1524                    on_new_3270_connection);
1525     if (r)
1526      {
1527         sim_printf ("\r[FNP emulation: listen error: %s:%ld: %s]\r\n",
1528                     fnpData.telnet_address, (long) telnet3270_port, uv_strerror(r));
1529         return 1;
1530      }
1531     fnpData.du3270_server_inited = true;
1532     sim_printf ("\r[FNP emulation: TN3270 server listening on %s:%ld]\r\n",
1533                 fnpData.telnet_address, (long) telnet3270_port);
1534     fnpuv3270Poll (false);
1535     return 0;
1536   }

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