root/src/dps8/udplib.c

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

DEFINITIONS

This source file includes following definitions.
  1. udp_parse_remote
  2. udp_find_free_link
  3. udp_create
  4. udp_release
  5. udp_send
  6. udp_receive_packet
  7. udp_receive
  8. main

   1 /*
   2  * udplib.c: IMP/TIP Modem and Host Interface socket routines using UDP
   3  *
   4  * vim: filetype=c:tabstop=4:ai:expandtab
   5  * SPDX-License-Identifier: MIT
   6  * scspell-id: 18c1dbd2-f630-11ec-8f2f-80ee73e9b8e7
   7  *
   8  * ---------------------------------------------------------------------------
   9  *
  10  * Copyright (c) 2013 Robert Armstrong <bob@jfcl.com>
  11  * Copyright (c) 2015-2016 Charles Anthony
  12  * Copyright (c) 2021-2024 The DPS8M Development Team
  13  *
  14  * Permission is hereby granted, free of charge, to any person obtaining a
  15  * copy of this software and associated documentation files (the "Software"),
  16  * to deal in the Software without restriction, including without limitation
  17  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  18  * and/or sell copies of the Software, and to permit persons to whom the
  19  * Software is furnished to do so, subject to the following conditions:
  20  *
  21  * The above copyright notice and this permission notice shall be included
  22  * in all copies or substantial portions of the Software.
  23  *
  24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  25  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  27  * IN NO EVENT SHALL ROBERT ARMSTRONG BE LIABLE FOR ANY CLAIM, DAMAGES OR
  28  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  29  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  30  * OTHER DEALINGS IN THE SOFTWARE.
  31  *
  32  * Except as contained in this notice, the name of Robert Armstrong shall
  33  * not be used in advertising or otherwise to promote the sale, use or
  34  * other dealings in this Software without prior written authorization from
  35  * Robert Armstrong.
  36  *
  37  * ---------------------------------------------------------------------------
  38  */
  39 
  40 /*
  41  * INTERFACE
  42  *
  43  * This module provides a simplified UDP socket interface.  These functions are
  44  * implemented -
  45  *
  46  *      udp_create      define a connection to the remote IMP
  47  *      udp_release     release a connection
  48  *      udp_send        send an IMP message to the other end
  49  *      udp_receive     receive (w/o blocking!) a message if available
  50  *
  51  * Note that each connection is assigned a unique "handle", a small integer,
  52  * which is used as an index into our internal connection data table.  There
  53  * is a limit on the maximum number of connections available, as set my the
  54  * MAXLINKS parameter.  Also, notice that all links are intrinsically full
  55  * duplex and bidirectional - data can be sent and received in both directions
  56  * independently.  Real modems and host cards were exactly the same.
  57  */
  58 
  59 #include <stdint.h>
  60 #include <stdbool.h>
  61 #include <string.h>
  62 #include <stdio.h>
  63 #include <stdlib.h>
  64 #include <sys/socket.h>
  65 #include <arpa/inet.h>
  66 #include <unistd.h>
  67 #include <fcntl.h>
  68 #include <sys/types.h>
  69 #include <netdb.h>
  70 #include <errno.h>
  71 #if defined(__APPLE__)
  72 # include <xlocale.h>
  73 #endif
  74 #include <locale.h>
  75 #include <netinet/in.h>
  76 
  77 #include "udplib.h"
  78 #include "h316_imp.h"
  79 
  80 #if defined(WITH_ABSI_DEV)
  81 # define MAXLINKS        10      // maximum number of simultaneous connections
  82 //   This constant determines the longest possible IMP data payload that can be
  83 // sent. Most IMP messages are trivially small - 68 words or so - but, when one
  84 // IMP asks for a reload the neighbor IMP sends the entire memory image in a
  85 // single message!  That message is about 14K words long.
  86 //   The next thing you should worry about is whether the underlying IP network
  87 // can actually send a UDP packet of this size.  It turns out that there's no
  88 // simple answer to that - it'll be fragmented for sure, but as long as all
  89 // the fragments arrive intact then the destination should reassemble them.
  90 # define MAXDATA      16384      // longest possible IMP packet (in H316 words)
  91 
  92 // UDP connection data structure ...
  93 //   One of these blocks is allocated for every simulated modem link.
  94 struct _UDP_LINK
  95   {
  96     bool      used;                 // TRUE if this UDP_LINK is in use
  97     char      rhost [64];
  98     char      rport [64];           // Remote host:port
  99     char      lport [64];           // Local port
 100     int32_t   lportno;
 101     int32_t   rportno;
 102     int       sock;
 103     uint32_t  rxsequence;           // next message sequence number for receive
 104     uint32_t  txsequence;           // next message sequence number for transmit
 105   };
 106 
 107 typedef struct _UDP_LINK UDP_LINK;
 108 
 109 //   This magic number is stored at the beginning of every UDP message and is
 110 // checked on receive.  It's hardly foolproof, but it's a simple attempt to
 111 // guard against other applications dumping unsolicited UDP messages into our
 112 // receiver socket...
 113 # define MAGIC   ((uint32_t) (((((('H' << 8) | '3') << 8) | '1') << 8) | '6'))
 114 
 115 // UDP wrapper data structure ...
 116 //   This is the UDP packet which is actually transmitted or received.  It
 117 // contains the actual IMP packet, plus whatever additional information we
 118 // need to keep track of things.  NOTE THAT ALL DATA IN THIS PACKET, INCLUDING
 119 // THE H316 MEMORY WORDS, ARE SENT AND RECEIVED WITH NETWORK BYTE ORDER!
 120 struct _UDP_PACKET {
 121   uint32_t  magic;                // UDP "magic number" (see above)
 122   uint32_t  sequence;             // UDP packet sequence number
 123   uint16_t  count;                // number of H316 words to follow
 124   uint16_t  flags;                // number of H316 words to follow
 125   uint16_t  data[MAXDATA];        // and the actual H316 data words/IMP packet
 126 };
 127 typedef struct _UDP_PACKET UDP_PACKET;
 128 # define UDP_HEADER_LEN  (2*sizeof(uint32_t) + sizeof(uint16_t))
 129 
 130 static UDP_LINK udp_links [MAXLINKS];
 131 
 132 int sim_parse_addr (const char * cptr, char * host, size_t hostlen, const char * default_host,
 133                     char * port, size_t port_len, const char * default_port,
 134                     const char * validate_addr);
 135 
 136 static int udp_parse_remote (int link, char * premote)
     /* [previous][next][first][last][top][bottom][index][help] */
 137   {
 138     // This routine will parse a remote address string in any of these forms -
 139     //
 140     //            llll:w.x.y.z:rrrr
 141     //            llll:name.domain.com:rrrr
 142     //            llll::rrrr
 143     //            w.x.y.z:rrrr
 144     //            name.domain.com:rrrr
 145     //
 146     // In all examples, "llll" is the local port number that we use for listening,
 147     // and "rrrr" is the remote port number that we use for transmitting.  The
 148     // local port is optional and may be omitted, in which case it defaults to the
 149     // same as the remote port.  This works fine if the other IMP is actually on
 150     // a different host, but don't try that with localhost - you'll be talking to
 151     // yourself!!  In both cases, "w.x.y.z" is a dotted IP for the remote machine
 152     // and "name.domain.com" is its name (which will be looked up to get the IP).
 153     // If the host name/IP is omitted then it defaults to "localhost".
 154 
 155     char * end;
 156     int32_t lportno, rport;
 157     char host [64], port [16];
 158     if (* premote == '\0')
 159       return -1;
 160     (void)memset (udp_links [link] . lport, 0, sizeof (udp_links [link] . lport));
 161     (void)memset (udp_links [link] . rhost, 0, sizeof (udp_links [link] . rhost));
 162     (void)memset (udp_links [link] . rport, 0, sizeof (udp_links [link] . rport));
 163     // Handle the llll::rrrr case first
 164     if (2 == sscanf (premote, "%d::%d", & lportno, & rport))
 165       {
 166         if ((lportno < 1) || (lportno >65535) || (rport < 1) || (rport >65535))
 167          return -1;
 168         (void)sprintf (udp_links [link] . lport, "%d", lportno);
 169                        udp_links [link] . lportno =  lportno;
 170         (void)sprintf (udp_links [link] . rhost, "localhost");
 171         (void)sprintf (udp_links [link] . rport, "%d", rport);
 172                        udp_links [link] . rportno = rport;
 173         return 0;
 174       }
 175 
 176     // Look for the local port number and save it away.
 177     lportno = (int) strtoul (premote, & end, 10);
 178     if ((* end == ':') && (lportno > 0))
 179       {
 180         (void)sprintf (udp_links [link] . lport, "%d", lportno);
 181                        udp_links [link] . lportno =  lportno;
 182         premote = end + 1;
 183       }
 184 
 185     if (sim_parse_addr (premote, host, sizeof (host), "localhost", port,
 186                         sizeof (port), NULL, NULL) != -1 /* SCPE_OK */)
 187       return -1;
 188     (void)sprintf (udp_links [link] . rhost, "%s", host);
 189     (void)sprintf (udp_links [link] . rport, "%s", port);
 190                    udp_links [link] . rportno = atoi (port);
 191     if (udp_links [link] . lport [0] == '\0')
 192       {
 193         strcpy (udp_links [link] . lport, port);
 194         udp_links [link] . lportno =  atoi (port);
 195       }
 196     if ((strcmp (udp_links [link] . lport, port) == 0) &&
 197         (strcmp ("localhost", host) == 0))
 198       (void)fprintf (stderr, "WARNING - use different transmit and receive ports!\n");
 199 
 200     return 0;
 201   }
 202 
 203 static int udp_find_free_link (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 204 {
 205   //   Find a free UDP_LINK block, initialize it and return its index.  If none
 206   // are free, then return -1 ...
 207   int i;
 208   for (i = 0;  i < MAXLINKS; i ++)
 209     {
 210       if (udp_links [i] . used == 0)
 211         {
 212           (void)memset (& udp_links [i], 0, sizeof (UDP_LINK));
 213           return i;
 214         }
 215      }
 216   return NOLINK;
 217 }
 218 
 219 // return values
 220 // 0: ok
 221 // -1: out of links
 222 
 223 int udp_create (const char * premote, int * pln)
     /* [previous][next][first][last][top][bottom][index][help] */
 224   {
 225     int rc;
 226     //   Create a logical UDP link to the specified remote system.  The "remote"
 227     // string specifies both the remote host name or IP and a port number.  The
 228     // port number is both the port we send datagrams to, and also the port we
 229     // listen on for incoming datagrams.  UDP doesn't have any real concept of a
 230     // "connection" of course, and this routine simply creates the necessary
 231     // sockets in this host. We have no way of knowing whether the remote host is
 232     // listening or even if it exists.
 233     //
 234     //   We return SCPE_OK if we're successful and an error code if we aren't. If
 235     // we are successful, then the ln parameter is assigned the link number,
 236     // which is a handle used to identify this connection to all future udp_xyz()
 237     //  calls.
 238 
 239     /* cppcheck-suppress shadowFunction */
 240     int link = udp_find_free_link ();
 241     if (link < 0)
 242       return -1; // out of links
 243 
 244     // Parse the remote name and set up the ipaddr and port ...
 245     if (udp_parse_remote (link, (char *) premote) != 0)
 246       return -2;
 247 
 248 
 249 
 250 
 251 
 252 
 253 
 254 
 255 
 256     int sock = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 257     if (sock == -1)
 258       return -3;
 259 
 260     rc = fcntl (sock, F_SETFL, fcntl (sock, F_GETFL, 0) | O_NONBLOCK);
 261     if (rc == -1)
 262       return -4;
 263 
 264     struct sockaddr_in si_me;
 265     (void)memset ((char *) & si_me, 0, sizeof (si_me));
 266 
 267     si_me . sin_family = AF_INET;
 268     si_me . sin_port = htons ((uint16_t) udp_links [link] . lportno);
 269     si_me . sin_addr . s_addr = htonl (INADDR_ANY);
 270 
 271     rc = bind (sock, (struct sockaddr *) & si_me, sizeof (si_me));
 272     if (rc == -1)
 273       return -5;
 274 
 275 // As I understand it, a connect on UDP sets the send address and limits
 276 // receiving to the specified address; creating a 'connection'; I am not
 277 // sure the udplib wants that. The alternative is to use sendto().
 278 
 279     struct addrinfo * ai;
 280     rc = getaddrinfo (udp_links [link] . rhost, udp_links [link] . rport, NULL,
 281       & ai);
 282     if (rc == -1)
 283       return -6;
 284 
 285     rc = connect (sock, ai -> ai_addr, sizeof (struct sockaddr));
 286     if (rc == -1)
 287       return -7;
 288 
 289     freeaddrinfo (ai);
 290 
 291     udp_links [link] . sock = sock;
 292 
 293     // All done - mark the TCP_LINK data as "used" and return the index.
 294      udp_links [link] . used = true;
 295      * pln = link;
 296      //udp_lines[link].dptr = udp_links[link].dptr = dptr;      // save device
 297      //udp_tmxr.uptr = dptr->units;
 298      //udp_tmxr.last_poll_time = 1;          // h316'a use of TMXR doesn't poll periodically for connects
 299      //tmxr_poll_conn (&udp_tmxr);           // force connection initialization now
 300      //udp_tmxr.last_poll_time = 1;          // h316'a use of TMXR doesn't poll periodically for connects
 301      //sim_debug(IMP_DBG_UDP, dptr, "link %d - listening on port %s and sending to %s\n",
 302      //          link, udp_links[link].lport, udp_links[link].rhostport);
 303 (void)printf ("link %d - listening on port %s and sending to %s:%s\n",
 304               link, udp_links [link] . lport,
 305               udp_links [link] . rhost, udp_links [link] . rport);
 306 
 307     return 0;
 308   }
 309 
 310 int udp_release (int link)
     /* [previous][next][first][last][top][bottom][index][help] */
 311   {
 312     //   Close a link that was created by udp_create() and release any resources
 313     // allocated to it.  We always return SCPE_OK unless the link specified is
 314     // already unused.
 315     if ((link < 0) || (link >= MAXLINKS))
 316       return -1;
 317     if (! udp_links [link] . used)
 318       return -1;
 319     //if (dptr != udp_links [link] . dptr)
 320       //return -1;
 321 
 322     //tmxr_detach_ln (&udp_lines[link]);
 323     close (udp_links [link] . sock);
 324     udp_links [link] . used = false;
 325     //sim_debug(IMP_DBG_UDP, dptr, "link %d - closed\n", link);
 326 (void)printf("link %d - closed\n", link);
 327 
 328     return 0;
 329   }
 330 
 331 int udp_send (int link, uint16_t * pdata, uint16_t count, uint16_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 332   {
 333     //   This routine does all the work of sending an IMP data packet.  pdata
 334     // is a pointer (usually into H316 simulated memory) to the IMP packet data,
 335     // count is the length of the data (in H316 words, not bytes!), and pdest is
 336     // the destination socket.  There are two things worthy of note here - first,
 337     // notice that the H316 words are sent in network order, so the remote
 338     // doesn't necessarily need to have the same endian-ness as this machine.
 339     // Second, notice that transmitting sockets are NOT set to non blocking so
 340     // this routine might wait, but we assume the wait will never be too long.
 341 
 342     UDP_PACKET pkt;
 343     int pktlen;
 344     uint16_t i;
 345 
 346     if ((link < 0) || (link >= MAXLINKS))
 347       return -1;
 348     if (! udp_links [link] . used)
 349       return -1;
 350     if ((pdata == NULL) || (count == 0) || (count > MAXDATA))
 351       return -1;
 352     //if (dptr != udp_links [link].dptr) return SCPE_IERR;
 353 
 354     //   Build the UDP packet, filling in our own header information and copying
 355     // the H316 words from memory.  REMEMBER THAT EVERYTHING IS IN NETWORK ORDER!
 356     pkt . magic    = htonl (MAGIC);
 357     pkt . sequence = htonl (udp_links [link] . txsequence ++);
 358     pkt . count    = htons (count);
 359     pkt . flags    = htons (flags);
 360     for (i = 0; i < count; i ++)
 361       pkt . data [i] = htons (* pdata ++);
 362     pktlen = UDP_HEADER_LEN + count * sizeof (uint16_t);
 363 
 364 
 365 
 366 
 367 
 368 
 369 
 370     ssize_t rc = send (udp_links [link] . sock, & pkt, (size_t) pktlen, 0);
 371     if (rc == -1)
 372       {
 373         return -2;
 374       }
 375     //sim_debug(IMP_DBG_UDP, dptr, "link %d - packet sent (sequence=%d, length=%d)\n", link, ntohl(pkt.sequence), ntohs(pkt.count));
 376 (void)printf ("link %d - packet sent (sequence=%u, length=%u)\n",
 377               link, ntohl (pkt . sequence), ntohs (pkt . count));
 378     return 0;
 379   }
 380 
 381 static int udp_receive_packet (int link, UDP_PACKET * ppkt, size_t pktsiz)
     /* [previous][next][first][last][top][bottom][index][help] */
 382   {
 383     //   This routine will do the hard part of receiving a UDP packet.  If it's
 384     // successful the packet length, in bytes, is returned.  The receiver socket
 385     // is non-blocking, so if no packet is available then zero will be returned
 386     // instead.  Lastly, if a fatal socket I/O error occurs, -1 is returned.
 387     //
 388     //   Note that this routine only receives the packet - it doesn't handle any
 389     // of the checking for valid packets, unexpected packets, duplicate or out of
 390     // sequence packets.  That's strictly the caller's problem!
 391 
 392 
 393 
 394 
 395 
 396 
 397 
 398 
 399 
 400 
 401 
 402 
 403 
 404 
 405 
 406 
 407 
 408 
 409 
 410     ssize_t n = read (udp_links [link] . sock, ppkt, pktsiz);
 411     if (n < 0)
 412       {
 413         if (errno == EAGAIN || errno == EWOULDBLOCK)
 414           return 0;
 415         return -1;
 416       }
 417 //(void)printf ("udp_receive_packet returns %ld\n", (long) n);
 418     return (int) n;
 419   }
 420 
 421 int udp_receive (int link, uint16_t * pdata, uint16_t maxbuf)
     /* [previous][next][first][last][top][bottom][index][help] */
 422   {
 423     //   Receive an IMP packet from the virtual modem. pdata is a pointer usually
 424     // directly into H316 simulated memory) to where the IMP packet data should
 425     // be stored, and maxbuf is the maximum length of that buffer in H316 words
 426     // (not bytes!).  If a message is successfully received then this routine
 427     // returns the length, again in H316 words, of the IMP packet.  The caller
 428     // can detect buffer overflows by comparing this result to maxbuf.  If no
 429     // packets are waiting right now then zero is returned, and -1 is returned
 430     // in the event of any fatal socket I/O error.
 431     //
 432     //  This routine also handles checking for unsolicited messages and duplicate
 433     // or out of sequence messages.  All of these are unceremoniously discarded.
 434     //
 435     //   One final note - it's explicitly allowed for pdata to be null and/or
 436     // maxbuf to be zero.  In either case the received package is discarded, but
 437     // the actual length of the discarded package is still returned.
 438     UDP_PACKET pkt;
 439     int32_t pktlen, explen, implen, i;
 440     uint32_t magic, pktseq;
 441     if ((link < 0) || (link >= MAXLINKS))
 442       return -1;
 443     if (!udp_links [link] . used)
 444       return -1;
 445     //if (dptr != udp_links [link] . dptr)
 446       //return SCPE_IERR;
 447 
 448     while ((pktlen = udp_receive_packet (link, & pkt, sizeof (pkt))) > 0)
 449       {
 450         // First do some header checks for a valid UDP packet ...
 451         if (((size_t) pktlen) < UDP_HEADER_LEN)
 452           {
 453             //sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet w/o header (length=%d)\n",
 454             //          link, pktlen);
 455             continue;
 456           }
 457         magic = ntohl (pkt . magic);
 458         if (magic != MAGIC)
 459           {
 460             //sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet w/bad magic number (magic=%08x)\n",
 461             //          link, magic);
 462             continue;
 463           }
 464         implen = ntohs (pkt . count);
 465         explen = (int32_t) UDP_HEADER_LEN + implen * (int32_t) sizeof (uint16_t);
 466         if (explen != pktlen)
 467           {
 468             //sim_debug(IMP_DBG_UDP, dptr,
 469             //          "link %d - received packet length wrong (expected=%d received=%d)\n",
 470             //          link, explen, pktlen);
 471             continue;
 472           }
 473 
 474         //  Now the hard part = check the sequence number.  The rxsequence value is
 475         // the number of the next packet we expect to receive - that's the number
 476         // this packet should have.  If this packet's sequence is less than that,
 477         // then this packet is out of order or a duplicate and we discard it.  If
 478         // this packet is greater than that, then we must have missed one or two
 479         // packets.  In that case we MUST update rxsequence to match this one;
 480         // otherwise the two ends could never resynchronize after a lost packet.
 481         //
 482         //  And there's one final complication to worry about - if the other end is
 483         // restarted for some reason, then his sequence numbers will reset to zero.
 484         // In that case we'll never recover synchronization without special efforts.
 485         // The hack is to check for a packet sequence number of zero and, if we find
 486         // it, force synchronization.  This improves the situation, but I freely
 487         // admit that it's possible to think of a number of cases where this also
 488         // fails.  The only absolute solution is to implement a more complicated system
 489         // with non-IMP control messages exchanged between the modem emulation on both
 490         // ends.  That'd be nice, but I'll leave it as an exercise for later.
 491 
 492         pktseq = ntohl (pkt . sequence);
 493         if ((pktseq == 0) && (udp_links [link] . rxsequence != 0))
 494           {
 495             //sim_debug(IMP_DBG_UDP, dptr, "link %d - remote modem restarted\n", link);
 496           }
 497         else if (pktseq < udp_links [link] . rxsequence)
 498           {
 499             //sim_debug(IMP_DBG_UDP, dptr,
 500             //          "link %d - received packet out of sequence 1 (expected=%d received=%d\n",
 501             //          link, udp_links[link].rxsequence, pktseq);
 502             continue;  // discard this packet!
 503           }
 504         else if (pktseq != udp_links [link] . rxsequence)
 505           {
 506             //sim_debug(IMP_DBG_UDP, dptr,
 507             //          "link %d - received packet out of sequence 2 (expected=%d received=%d\n",
 508             //          link, udp_links[link].rxsequence, pktseq);
 509           }
 510         udp_links [link] . rxsequence = pktseq + 1;
 511 
 512         // It's a valid packet - if there's no buffer then just discard it.
 513         if ((pdata == NULL) || (maxbuf == 0))
 514           {
 515             //sim_debug(IMP_DBG_UDP, dptr, "link %d - received packet discarded (no buffer available)\n", link);
 516             return implen;
 517           }
 518 
 519         // Copy the data to the H316 memory and we're done!
 520         //sim_debug (IMP_DBG_UDP, dptr, "link %d - packet received (sequence=%d, length=%d)\n", link, pktseq, pktlen);
 521 (void)printf ("link %lu - packet received (sequence=%lu, length=%lu)\n",
 522               (unsigned long)link, (unsigned long)pktseq, (unsigned long)pktlen);
 523         for (i = 0;  i < (implen < maxbuf ? implen : maxbuf);  ++ i)
 524           * pdata ++ = ntohs (pkt . data [i]);
 525         return implen;
 526       }
 527 
 528     // Here if pktlen <= 0 ...
 529     return pktlen;
 530   }
 531 
 532 //#define TEST__
 533 # if defined(TEST__)
 534 
 535 #  define CBUFSIZE        256
 536 #  define SCPE_ARG -1
 537 #  define SCPE_OK -1
 538 
 539 /* sim_parse_addr       host:port
 540 
 541    Presumption is that the input, if it doesn't contain a ':' character is a port specifier.
 542    If the host field contains one or more colon characters (i.e. it is an IPv6 address),
 543    the IPv6 address MUST be enclosed in square bracket characters (i.e. Domain Literal format)
 544 
 545    Inputs:
 546         cptr    =       pointer to input string
 547         default_host
 548                 =       optional pointer to default host if none specified
 549         host_len =      length of host buffer
 550         default_port
 551                 =       optional pointer to default port if none specified
 552         port_len =      length of port buffer
 553         validate_addr = optional name/addr which is checked to be equivalent
 554                         to the host result of parsing the other input.  This
 555                         address would usually be returned by sim_accept_conn.
 556    Outputs:
 557         host    =       pointer to buffer for IP address (may be NULL), 0 = none
 558         port    =       pointer to buffer for IP port (may be NULL), 0 = none
 559         result  =       status (SCPE_OK on complete success or SCPE_ARG if
 560                         parsing can't happen due to bad syntax, a value is
 561                         out of range, a result can't fit into a result buffer,
 562                         a service name doesn't exist, or a validation name
 563                         doesn't match the parsed host)
 564 */
 565 
 566 int sim_parse_addr \
 567         (const char *cptr, char *host, size_t host_len, const char *default_host,
 568          char *port, size_t port_len, const char *default_port, const char *validate_addr)
 569 {
 570 char gbuf[CBUFSIZE];
 571 char *hostp, *portp;
 572 char *endc;
 573 unsigned long portval;
 574 
 575 if ((cptr == NULL) || (*cptr == 0))
 576     return SCPE_ARG;
 577 if ((host != NULL) && (host_len != 0))
 578     (void)memset (host, 0, host_len);
 579 if ((port != NULL) && (port_len != 0))
 580     (void)memset (port, 0, port_len);
 581 gbuf[sizeof(gbuf)-1] = '\0';
 582 strncpy (gbuf, cptr, sizeof(gbuf)-1);
 583 hostp = gbuf;                                           /* default addr */
 584 portp = NULL;
 585 if ((portp = strrchr (gbuf, ':')) &&                    /* x:y? split */
 586     (NULL == strchr (portp, ']'))) {
 587     *portp++ = 0;
 588     if (*portp == '\0')
 589         portp = (char *)default_port;
 590     }
 591 else {                                                  /* No colon in input */
 592     portp = gbuf;                                       /* Input is the port specifier */
 593     hostp = (char *)default_host;                       /* host is defaulted if provided */
 594     }
 595 if (portp != NULL) {
 596     portval = strtoul(portp, &endc, 10);
 597     if ((*endc == '\0') && ((portval == 0) || (portval > 65535)))
 598         return SCPE_ARG;                                /* numeric value too big */
 599     if (*endc != '\0') {
 600         struct servent *se = getservbyname(portp, "tcp");
 601 
 602         if (se == NULL)
 603             return SCPE_ARG;                            /* invalid service name */
 604         }
 605     }
 606 if (port)                                               /* port wanted? */
 607     if (portp != NULL) {
 608         if (strlen(portp) >= port_len)
 609             return SCPE_ARG;                            /* no room */
 610         else
 611             strcpy (port, portp);
 612         }
 613 if (hostp != NULL) {
 614     if (']' == hostp[strlen(hostp)-1]) {
 615         if ('[' != hostp[0])
 616             return SCPE_ARG;                            /* invalid domain literal */
 617         /* host may be the const default_host so move to temp buffer before modifying */
 618         strncpy(gbuf, hostp+1, sizeof(gbuf)-1);         /* remove brackets from domain literal host */
 619         hostp = gbuf;
 620         hostp[strlen(hostp)-1] = '\0';
 621         }
 622     }
 623 if (host)                                               /* host wanted? */
 624     if (hostp != NULL) {
 625         if (strlen(hostp) >= host_len)
 626             return SCPE_ARG;                            /* no room */
 627         else
 628             strcpy (host, hostp);
 629         }
 630 if (validate_addr) {
 631     struct addrinfo *ai_host, *ai_validate, *ai;
 632     int status;
 633 
 634     if (hostp == NULL)
 635         return SCPE_ARG;
 636     if (getaddrinfo(hostp, NULL, NULL, &ai_host))
 637         return SCPE_ARG;
 638     if (getaddrinfo(validate_addr, NULL, NULL, &ai_validate)) {
 639         freeaddrinfo (ai_host);
 640         return SCPE_ARG;
 641         }
 642     status = SCPE_ARG;
 643     for (ai = ai_host; ai != NULL; ai = ai->ai_next) {
 644         if ((ai->ai_addrlen == ai_validate->ai_addrlen) &&
 645             (ai->ai_family == ai_validate->ai_family) &&
 646             (0 == memcmp (ai->ai_addr, ai_validate->ai_addr, ai->ai_addrlen))) {
 647             status = SCPE_OK;
 648             break;
 649             }
 650         }
 651     if (status != SCPE_OK) {
 652         /* be generous and allow successful validations against variations of localhost addresses */
 653         if (((0 == strcmp("127.0.0.1", hostp)) &&
 654              (0 == strcmp("::1", validate_addr))) ||
 655             ((0 == strcmp("127.0.0.1", validate_addr)) &&
 656              (0 == strcmp("::1", hostp))))
 657             status = SCPE_OK;
 658         }
 659     freeaddrinfo (ai_host);
 660     freeaddrinfo (ai_validate);
 661     return status;
 662     }
 663 return SCPE_OK;
 664 }
 665 
 666 int main (int argc, char * argv [])
     /* [previous][next][first][last][top][bottom][index][help] */
 667   {
 668     int rc;
 669     int linkno;
 670 
 671     (void)setlocale(LC_ALL, "");
 672     rc = udp_create ("4500::4426", & linkno);
 673     if (rc < 0)
 674       {
 675         (void)fprintf (stderr, "\rFATAL: udp_create() failed in %s:%d\r\n",
 676                        __func__, __LINE__);
 677         exit (1);
 678       }
 679 
 680     while (1)
 681       {
 682 #  define psz 17000
 683         uint16_t pkt [psz];
 684         rc = udp_receive (linkno, pkt, psz);
 685         if (rc < 0)
 686           {
 687             (void)fprintf (stderr, "\rFATAL: udp_receive() failed in %s:%d\r\n",
 688                            __func__, __LINE__);
 689             exit (1);
 690           }
 691         else if (rc == 0)
 692           {
 693             (void)fprintf (stderr, "\rudp_receive 0\r\n");
 694             sleep (1);
 695           }
 696         else
 697           {
 698             for (int i = 0; i < rc; i ++)
 699               {
 700                 (void)fprintf (stderr, "  %06o  %04x  ", pkt [i], pkt [i]);
 701                 for (int b = 0; b < 16; b ++)
 702                   (void)fprintf (stderr, "%c", pkt [i] & (1 << b) ? '1' : '0');
 703                 (void)fprintf (stderr, "\n");
 704               }
 705           }
 706       }
 707   }
 708 # endif /* if defined(TEST__) */
 709 #endif /* if defined(WITH_ABSI_DEV) */

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