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

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