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

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