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: X11
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-2022 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]](../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)
*/
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]](../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)
*/
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]](../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)
*/
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]](../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)
*/
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]](../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)
*/
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]](../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)
*/
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]](../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)
*/
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]](../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)
*/
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]](../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)
*/
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 */