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

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