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-2025 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]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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!\r\n");
199
200 return 0;
201 }
202
203 static int udp_find_free_link (void)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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\r\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\r\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]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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\r\n", link);
326 (void)printf("link %d - closed\r\n", link);
327
328 return 0;
329 }
330
331 int udp_send (int link, uint16_t * pdata, uint16_t count, uint16_t flags)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)\r\n", link, ntohl(pkt.sequence), ntohs(pkt.count));
376 (void)printf ("link %d - packet sent (sequence=%u, length=%u)\r\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]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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\r\n", (long) n);
418 return (int) n;
419 }
420
421 int udp_receive (int link, uint16_t * pdata, uint16_t maxbuf)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
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)\r\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)\r\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)\r\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\r\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\r\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\r\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)\r\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)\r\n", link, pktseq, pktlen);
521 (void)printf ("link %lu - packet received (sequence=%lu, length=%lu)\r\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]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
667 {
668 int rc;
669 int linkno;
670
671 # if !defined(NO_LOCALE)
672 (void)setlocale(LC_ALL, "");
673 # endif
674 rc = udp_create ("4500::4426", & linkno);
675 if (rc < 0)
676 {
677 (void)fprintf (stderr, "\rFATAL: udp_create() failed in %s:%d\r\n",
678 __func__, __LINE__);
679 exit (1);
680 }
681
682 while (1)
683 {
684 # define psz 17000
685 uint16_t pkt [psz];
686 rc = udp_receive (linkno, pkt, psz);
687 if (rc < 0)
688 {
689 (void)fprintf (stderr, "\rFATAL: udp_receive() failed in %s:%d\r\n",
690 __func__, __LINE__);
691 exit (1);
692 }
693 else if (rc == 0)
694 {
695 (void)fprintf (stderr, "\rudp_receive 0\r\n");
696 sleep (1);
697 }
698 else
699 {
700 for (int i = 0; i < rc; i ++)
701 {
702 (void)fprintf (stderr, " %06o %04x ", pkt [i], pkt [i]);
703 for (int b = 0; b < 16; b ++)
704 (void)fprintf (stderr, "%c", pkt [i] & (1 << b) ? '1' : '0');
705 (void)fprintf (stderr, "\r\n");
706 }
707 }
708 }
709 }
710 # endif /* if defined(TEST__) */
711 #endif /* if defined(WITH_ABSI_DEV) */