root/src/dps8/fnpuv.c

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

DEFINITIONS

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

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