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