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

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