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) /* */ 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) /* */ 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) /* */ 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) /* */ 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) /* */ 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) /* */ 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) /* */ 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) /* */ 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 []) /* */ 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 */