Version 1.8.0.0
[socat.git] / xio-udp.c
blobb624281957c768c8a92702c62fb87c94ad460ff6
1 /* source: xio-udp.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
5 /* this file contains the source for handling UDP addresses */
7 #include "xiosysincludes.h"
9 #if _WITH_UDP && (WITH_IP4 || WITH_IP6)
11 #include "xioopen.h"
12 #include "xio-socket.h"
13 #include "xio-ip4.h"
14 #include "xio-ip6.h"
15 #include "xio-ip.h"
16 #include "xio-ipapp.h"
17 #include "xio-tcpwrap.h"
19 #include "xio-udp.h"
21 #if WITH_UDP
23 const struct addrdesc xioaddr_udp_connect = { "UDP-CONNECT", 1+XIO_RDWR, xioopen_ipapp_connect, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_UDP, SOCK_DGRAM, IPPROTO_UDP, PF_UNSPEC HELP(":<host>:<port>") };
24 #if WITH_LISTEN
25 const struct addrdesc xioaddr_udp_listen = { "UDP-LISTEN", 1+XIO_RDWR, xioopen_ipdgram_listen, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_LISTEN|GROUP_CHILD|GROUP_RANGE, PF_UNSPEC, IPPROTO_UDP, PF_UNSPEC HELP(":<port>") };
26 #endif /* WITH_LISTEN */
27 const struct addrdesc xioaddr_udp_sendto = { "UDP-SENDTO", 1+XIO_RDWR, xioopen_udp_sendto, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_UDP, PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
28 const struct addrdesc xioaddr_udp_recvfrom = { "UDP-RECVFROM", 1+XIO_RDWR, xioopen_udp_recvfrom, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_CHILD|GROUP_RANGE, PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
29 const struct addrdesc xioaddr_udp_recv = { "UDP-RECV", 1+XIO_RDONLY, xioopen_udp_recv, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_RANGE, PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
30 const struct addrdesc xioaddr_udp_datagram = { "UDP-DATAGRAM", 1+XIO_RDWR, xioopen_udp_datagram, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_RANGE, PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
32 #if WITH_IP4
33 const struct addrdesc xioaddr_udp4_connect = { "UDP4-CONNECT", 1+XIO_RDWR, xioopen_ipapp_connect, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP, SOCK_DGRAM, IPPROTO_UDP, PF_INET HELP(":<host>:<port>") };
34 #if WITH_LISTEN
35 const struct addrdesc xioaddr_udp4_listen = { "UDP4-LISTEN", 1+XIO_RDWR, xioopen_ipdgram_listen, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP|GROUP_LISTEN|GROUP_CHILD|GROUP_RANGE, PF_INET, IPPROTO_UDP, PF_INET HELP(":<port>") };
36 #endif /* WITH_LISTEN */
37 const struct addrdesc xioaddr_udp4_sendto = { "UDP4-SENDTO", 1+XIO_RDWR, xioopen_udp_sendto, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP, PF_INET, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
38 const struct addrdesc xioaddr_udp4_datagram = { "UDP4-DATAGRAM", 1+XIO_RDWR, xioopen_udp_datagram, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP|GROUP_RANGE, PF_INET, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
39 const struct addrdesc xioaddr_udp4_recvfrom= { "UDP4-RECVFROM", 1+XIO_RDWR, xioopen_udp_recvfrom, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP|GROUP_CHILD|GROUP_RANGE, PF_INET, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
40 const struct addrdesc xioaddr_udp4_recv = { "UDP4-RECV", 1+XIO_RDONLY, xioopen_udp_recv, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP|GROUP_RANGE, PF_INET, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
41 #endif /* WITH_IP4 */
43 #if WITH_IP6
44 const struct addrdesc xioaddr_udp6_connect = { "UDP6-CONNECT", 1+XIO_RDWR, xioopen_ipapp_connect, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP, SOCK_DGRAM, IPPROTO_UDP, PF_INET6 HELP(":<host>:<port>") };
45 #if WITH_LISTEN
46 const struct addrdesc xioaddr_udp6_listen = { "UDP6-LISTEN", 1+XIO_RDWR, xioopen_ipdgram_listen, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_LISTEN|GROUP_CHILD|GROUP_RANGE, PF_INET6, IPPROTO_UDP, 0 HELP(":<port>") };
47 #endif /* WITH_LISTEN */
48 const struct addrdesc xioaddr_udp6_sendto = { "UDP6-SENDTO", 1+XIO_RDWR, xioopen_udp_sendto, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP, PF_INET6, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
49 const struct addrdesc xioaddr_udp6_datagram= { "UDP6-DATAGRAM", 1+XIO_RDWR, xioopen_udp_datagram,GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_RANGE, PF_INET6, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
50 const struct addrdesc xioaddr_udp6_recvfrom= { "UDP6-RECVFROM", 1+XIO_RDWR, xioopen_udp_recvfrom, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_CHILD|GROUP_RANGE, PF_INET6, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
51 const struct addrdesc xioaddr_udp6_recv = { "UDP6-RECV", 1+XIO_RDONLY, xioopen_udp_recv, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_RANGE, PF_INET6, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
52 #endif /* WITH_IP6 */
54 #endif /* WITH_UDP */
57 int _xioopen_ipdgram_listen(struct single *sfd,
58 int xioflags, union sockaddr_union *us, socklen_t uslen,
59 struct opt *opts, int pf, int socktype, int ipproto) {
60 union sockaddr_union themunion;
61 union sockaddr_union *them = &themunion;
62 struct pollfd readfd;
63 bool dofork = false;
64 int maxchildren = 0;
65 pid_t pid;
66 char *rangename;
67 char infobuff[256];
68 unsigned char buff1[1];
69 socklen_t themlen;
70 int result;
72 retropt_bool(opts, OPT_FORK, &dofork);
74 if (dofork) {
75 if (!(xioflags & XIO_MAYFORK)) {
76 Error("option fork not allowed here");
77 return STAT_NORETRY;
81 retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);
83 if (! dofork && maxchildren) {
84 Error("option max-children not allowed without option fork");
85 return STAT_NORETRY;
88 #if WITH_IP4 /*|| WITH_IP6*/
89 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
90 if (xioparserange(rangename, pf, &sfd->para.socket.range,
91 sfd->para.socket.ip.ai_flags)
92 < 0) {
93 free(rangename);
94 return STAT_NORETRY;
96 free(rangename);
97 sfd->para.socket.dorange = true;
99 #endif
101 #if WITH_LIBWRAP
102 xio_retropt_tcpwrap(sfd, opts);
103 #endif /* WITH_LIBWRAP */
105 if (retropt_ushort(opts, OPT_SOURCEPORT, &sfd->para.socket.ip.sourceport)
106 >= 0) {
107 sfd->para.socket.ip.dosourceport = true;
109 retropt_bool(opts, OPT_LOWPORT, &sfd->para.socket.ip.lowport);
111 if (dofork) {
112 xiosetchilddied(); /* set SIGCHLD handler */
115 while (true) { /* we loop with fork or prohibited packets */
116 /* now wait for some packet on this datagram socket, get its sender
117 address, connect there, and return */
118 union integral notnull;
119 union integral reuseaddr;
120 int doreuseaddr = (dofork != 0);
121 char infobuff[256];
122 union sockaddr_union _sockname;
123 union sockaddr_union *la = &_sockname; /* local address */
125 reuseaddr.u_int = dofork;
127 if ((sfd->fd = xiosocket(opts, pf, socktype, ipproto, E_ERROR)) < 0) {
128 return STAT_RETRYLATER;
130 doreuseaddr |= (retropt_2integrals(opts, OPT_SO_REUSEADDR,
131 &reuseaddr, &notnull) >= 0);
132 applyopts(sfd, -1, opts, PH_PASTSOCKET);
134 /* SO_REUSEADDR handling of UDP sockets is helpful on Solaris */
135 if (doreuseaddr) {
136 if (Setsockopt(sfd->fd, opt_so_reuseaddr.major,
137 opt_so_reuseaddr.minor, &reuseaddr.u_int, sizeof(reuseaddr.u_int))
138 < 0) {
139 Warn6("setsockopt(%d, %d, %d, {%d}, "F_Zd"): %s",
140 sfd->fd, opt_so_reuseaddr.major,
141 opt_so_reuseaddr.minor, reuseaddr.u_int, sizeof(reuseaddr.u_int),
142 strerror(errno));
145 applyopts_cloexec(sfd->fd, opts);
146 applyopts(sfd, -1, opts, PH_PREBIND);
147 applyopts(sfd, -1, opts, PH_BIND);
148 if (Bind(sfd->fd, &us->soa, uslen) < 0) {
149 Error4("bind(%d, {%s}, "F_socklen"): %s", sfd->fd,
150 sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)),
151 uslen, strerror(errno));
152 return STAT_RETRYLATER;
154 /* under some circumstances bind() fills sockaddr with interesting info. */
155 if (Getsockname(sfd->fd, &us->soa, &uslen) < 0) {
156 Error4("getsockname(%d, %p, {%d}): %s",
157 sfd->fd, &us->soa, uslen, strerror(errno));
159 applyopts(sfd, -1, opts, PH_PASTBIND);
161 if (ipproto == IPPROTO_UDP) {
162 Notice1("listening on UDP %s",
163 sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)));
164 } else {
165 Notice2("listening on PROTO%d %s", ipproto,
166 sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)));
169 readfd.fd = sfd->fd;
170 readfd.events = POLLIN|POLLERR;
171 while (xiopoll(&readfd, 1, NULL) < 0) {
172 if (errno != EINTR) break;
175 themlen = socket_init(pf, them);
176 do {
177 result = Recvfrom(sfd->fd, buff1, 1, MSG_PEEK,
178 &them->soa, &themlen);
179 } while (result < 0 && errno == EINTR);
180 if (result < 0) {
181 Error5("recvfrom(%d, %p, 1, MSG_PEEK, {%s}, {"F_socklen"}): %s",
182 sfd->fd, buff1,
183 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)),
184 themlen, strerror(errno));
185 return STAT_RETRYLATER;
188 Notice1("accepting UDP connection from %s",
189 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)));
191 if (xiocheckpeer(sfd, them, la) < 0) {
192 Notice1("forbidding UDP connection from %s",
193 sockaddr_info(&them->soa, themlen,
194 infobuff, sizeof(infobuff)));
195 /* drop packet */
196 char buff[512];
197 Recv(sfd->fd, buff, sizeof(buff), 0); /* drop packet */
198 Close(sfd->fd);
199 continue;
201 Info1("permitting UDP connection from %s",
202 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)));
204 if (dofork) {
205 pid = xio_fork(false, E_ERROR, sfd->shutup);
206 if (pid < 0) {
207 return STAT_RETRYLATER;
210 if (pid == 0) { /* child */
211 pid_t cpid = Getpid();
212 xiosetenvulong("PID", cpid, 1);
213 break;
216 /* server: continue loop with socket()+recvfrom() */
217 /* This avoids the requirement of a sync (trigger) mechanism as with
218 RECVFROM addresses */
219 /* And when we dont close this we got awkward behaviour on Linux 2.4:
220 recvfrom gives 0 bytes with invalid socket address */
221 if (Close(sfd->fd) < 0) {
222 Info2("close(%d): %s", sfd->fd, strerror(errno));
225 while (maxchildren) {
226 if (num_child < maxchildren) break;
227 Notice("maxchildren are active, waiting");
228 /* UINT_MAX would even be nicer, but Openindiana works only
229 with 31 bits */
230 while (!Sleep(INT_MAX)) ; /* any signal lets us continue */
232 Info("still listening");
233 continue;
235 break;
236 } /* end of the big while loop */
238 applyopts(sfd, -1, opts, PH_CONNECT);
239 if ((result = Connect(sfd->fd, &them->soa, themlen)) < 0) {
240 Error4("connect(%d, {%s}, "F_socklen"): %s",
241 sfd->fd,
242 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)),
243 themlen, strerror(errno));
244 return STAT_RETRYLATER;
247 /* set the env vars describing the local and remote sockets */
248 if (Getsockname(sfd->fd, &us->soa, &uslen) < 0) {
249 Warn4("getsockname(%d, %p, {%d}): %s",
250 sfd->fd, &us->soa, uslen, strerror(errno));
252 xiosetsockaddrenv("SOCK", us, uslen, IPPROTO_UDP);
253 xiosetsockaddrenv("PEER", them, themlen, IPPROTO_UDP);
255 if (sfd->howtoend == END_UNSPEC)
256 sfd->howtoend = END_SHUTDOWN;
257 applyopts_fchown(sfd->fd, opts);
258 applyopts(sfd, -1, opts, PH_LATE);
260 if ((result = _xio_openlate(sfd, opts)) < 0)
261 return result;
263 return 0;
266 /* we expect the form: port */
267 int xioopen_ipdgram_listen(
268 int argc,
269 const char *argv[],
270 struct opt *opts,
271 int xioflags,
272 xiofile_t *xfd,
273 const struct addrdesc *addrdesc)
275 struct single *sfd = &xfd->stream;
276 const char *portname = argv[1];
277 int pf = addrdesc->arg1;
278 int ipproto = addrdesc->arg2;
279 union sockaddr_union us;
280 int socktype = SOCK_DGRAM;
281 socklen_t uslen;
283 if (argc != 2) {
284 xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
285 return STAT_NORETRY;
288 xioinit_ip(&pf, xioparms.default_ip);
289 if (pf == PF_UNSPEC) {
290 #if WITH_IP4 && WITH_IP6
291 switch (xioparms.default_ip) {
292 case '4': pf = PF_INET; break;
293 case '6': pf = PF_INET6; break;
294 default: break; /* includes \0 */
296 #elif WITH_IP6
297 pf = PF_INET6;
298 #else
299 pf = PF_INET;
300 #endif
303 retropt_socket_pf(opts, &pf);
304 retropt_int(opts, OPT_SO_PROTOTYPE, &ipproto);
306 if (applyopts_single(sfd, opts, PH_INIT) < 0)
307 return -1;
308 applyopts(sfd, -1, opts, PH_INIT);
310 uslen = socket_init(pf, &us);
311 retropt_bind(opts, pf, socktype, ipproto,
312 (struct sockaddr *)&us, &uslen, 1,
313 xfd->stream.para.socket.ip.ai_flags);
315 if (false) {
317 #if WITH_IP4
318 } else if (pf == PF_INET) {
319 us.ip4.sin_port = parseport(portname, ipproto);
320 #endif
321 #if WITH_IP6
322 } else if (pf == PF_INET6) {
323 us.ip6.sin6_port = parseport(portname, ipproto);
324 #endif
325 } else {
326 Error1("xioopen_ipdgram_listen(): unknown address family %d", pf);
329 return _xioopen_ipdgram_listen(&xfd->stream, xioflags, &us, uslen,
330 opts, pf, socktype, ipproto);
333 int xioopen_udp_sendto(
334 int argc,
335 const char *argv[],
336 struct opt *opts,
337 int xioflags,
338 xiofile_t *xfd,
339 const struct addrdesc *addrdesc)
341 int pf = addrdesc->arg1;
342 int socktype = addrdesc->arg2;
343 int ipproto = addrdesc->arg3;
344 int result;
346 if (argc != 3) {
347 xio_syntax(argv[0], 2, argc-1, addrdesc->syntax);
348 return STAT_NORETRY;
351 retropt_socket_pf(opts, &pf);
352 if ((result = _xioopen_udp_sendto(argv[1], argv[2], opts, xioflags, xfd,
353 addrdesc->groups, pf, socktype, ipproto))
354 != STAT_OK) {
355 return result;
357 _xio_openlate(&xfd->stream, opts);
358 return STAT_OK;
362 applies and consumes the following option:
363 PH_INIT, PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE
364 OFUNC_OFFSET
365 OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
367 int _xioopen_udp_sendto(const char *hostname, const char *servname,
368 struct opt *opts,
369 int xioflags, xiofile_t *xxfd, groups_t groups,
370 int pf, int socktype, int ipproto) {
371 struct single *sfd = &xxfd->stream;
372 union sockaddr_union us;
373 socklen_t uslen;
374 int feats = 3; /* option bind supports address and port */
375 bool needbind = false;
376 int result;
378 if (sfd->howtoend == END_UNSPEC)
379 sfd->howtoend = END_SHUTDOWN;
381 /* ...res_opts[] */
382 if (applyopts_single(sfd, opts, PH_INIT) < 0)
383 return -1;
384 applyopts(sfd, -1, opts, PH_INIT);
386 sfd->salen = sizeof(sfd->peersa);
387 if ((result =
388 xioresolve(hostname, servname, pf, socktype, ipproto,
389 &sfd->peersa, &sfd->salen,
390 sfd->para.socket.ip.ai_flags))
391 != STAT_OK) {
392 return result;
394 if (pf == PF_UNSPEC) {
395 pf = sfd->peersa.soa.sa_family;
397 uslen = socket_init(pf, &us);
398 if (retropt_bind(opts, pf, socktype, ipproto, &us.soa, &uslen, feats,
399 sfd->para.socket.ip.ai_flags)
400 != STAT_NOACTION) {
401 needbind = true;
404 if (retropt_ushort(opts, OPT_SOURCEPORT,
405 &sfd->para.socket.ip.sourceport) >= 0) {
406 switch (pf) {
407 #if WITH_IP4
408 case PF_INET:
409 us.ip4.sin_port = htons(sfd->para.socket.ip.sourceport);
410 break;
411 #endif
412 #if WITH_IP6
413 case PF_INET6:
414 us.ip6.sin6_port = htons(sfd->para.socket.ip.sourceport);
415 break;
416 #endif
418 needbind = true;
421 retropt_bool(opts, OPT_LOWPORT, &sfd->para.socket.ip.lowport);
423 sfd->dtype = XIODATA_RECVFROM;
424 return _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
425 opts, xioflags, sfd, groups,
426 pf, socktype, ipproto,
427 sfd->para.socket.ip.lowport);
431 int xioopen_udp_datagram(
432 int argc,
433 const char *argv[],
434 struct opt *opts,
435 int xioflags,
436 xiofile_t *xxfd,
437 const struct addrdesc *addrdesc)
439 struct single *sfd = &xxfd->stream;
440 int pf = addrdesc->arg1;
441 int socktype = addrdesc->arg2;
442 int ipproto = addrdesc->arg3;
443 char *rangename;
444 char *hostname;
445 int result;
447 if (argc != 3) {
448 xio_syntax(argv[0], 2, argc-1, addrdesc->syntax);
449 return STAT_NORETRY;
452 if ((hostname = strdup(argv[1])) == NULL) {
453 Error1("strdup(\"%s\"): out of memory", argv[1]);
454 return STAT_RETRYLATER;
457 /* only accept packets with correct remote ports */
458 if (retropt_ushort(opts, OPT_SOURCEPORT, &sfd->para.socket.ip.sourceport)
459 >= 0) {
460 sfd->para.socket.ip.dosourceport = true;
463 retropt_socket_pf(opts, &pf);
465 result =
466 _xioopen_udp_sendto(hostname, argv[2], opts, xioflags, xxfd,
467 addrdesc->groups, pf, socktype, ipproto);
468 free(hostname);
469 if (result != STAT_OK) {
470 return result;
473 if (sfd->para.socket.ip.dosourceport) {
474 switch (sfd->peersa.soa.sa_family) {
475 default:
476 #if WITH_IP4
477 case PF_INET:
478 sfd->para.socket.ip.sourceport = ntohs(sfd->peersa.ip4.sin_port);
479 break;
480 #endif /* WITH_IP4 */
481 #if WITH_IP6
482 case PF_INET6:
483 sfd->para.socket.ip.sourceport = ntohs(sfd->peersa.ip6.sin6_port);
484 break;
485 #endif /* WITH_IP6 */
489 sfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO;
491 sfd->para.socket.la.soa.sa_family = sfd->peersa.soa.sa_family;
493 /* which reply packets will be accepted - determine by range option */
494 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
495 if (xioparserange(rangename, pf, &sfd->para.socket.range,
496 sfd->para.socket.ip.ai_flags)
497 < 0) {
498 free(rangename);
499 return STAT_NORETRY;
501 sfd->para.socket.dorange = true;
502 sfd->dtype |= XIOREAD_RECV_CHECKRANGE;
503 free(rangename);
506 #if WITH_LIBWRAP
507 xio_retropt_tcpwrap(sfd, opts);
508 #endif /* WITH_LIBWRAP */
510 _xio_openlate(sfd, opts);
511 return STAT_OK;
515 int xioopen_udp_recvfrom(
516 int argc,
517 const char *argv[],
518 struct opt *opts,
519 int xioflags,
520 xiofile_t *xfd,
521 const struct addrdesc *addrdesc)
523 struct single *sfd = &xfd->stream;
524 int pf = addrdesc->arg1;
525 int socktype = addrdesc->arg2;
526 int ipproto = addrdesc->arg3;
527 union sockaddr_union us;
528 socklen_t uslen = sizeof(us);
529 int ai_flags2[2];
530 int result;
532 if (argc != 2) {
533 xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
534 return STAT_NORETRY;
537 xioinit_ip(&pf, xioparms.default_ip);
538 sfd->howtoend = END_NONE;
539 if (sfd->howtoend == END_UNSPEC)
540 sfd->howtoend = END_NONE;
541 retropt_socket_pf(opts, &pf);
542 if (pf == PF_UNSPEC) {
543 #if WITH_IP4 && WITH_IP6
544 switch (xioparms.default_ip) {
545 case '4': pf = PF_INET; break;
546 case '6': pf = PF_INET6; break;
547 default: break; /* includes \0 */
549 #elif WITH_IP6
550 pf = PF_INET6;
551 #else
552 pf = PF_INET;
553 #endif
556 /* Set AI_PASSIVE, except when it is explicitely disabled */
557 ai_flags2[0] = xfd->stream.para.socket.ip.ai_flags[0];
558 ai_flags2[1] = xfd->stream.para.socket.ip.ai_flags[1];
559 if (!(ai_flags2[1] & AI_PASSIVE))
560 ai_flags2[0] |= AI_PASSIVE;
562 if ((result =
563 xioresolve(NULL, argv[1], pf, socktype, ipproto, &us, &uslen,
564 ai_flags2))
565 != STAT_OK) {
566 return result;
568 if (pf == PF_UNSPEC) {
569 pf = us.soa.sa_family;
573 union sockaddr_union la;
574 socklen_t lalen = sizeof(la);
576 if (retropt_bind(opts, pf, socktype, ipproto, &la.soa, &lalen, 1,
577 sfd->para.socket.ip.ai_flags)
578 != STAT_NOACTION) {
579 switch (pf) {
580 #if WITH_IP4
581 case PF_INET: us.ip4.sin_addr = la.ip4.sin_addr; break;
582 #endif
583 #if WITH_IP6
584 case PF_INET6: us.ip6.sin6_addr = la.ip6.sin6_addr; break;
585 #endif
590 if (retropt_ushort(opts, OPT_SOURCEPORT, &sfd->para.socket.ip.sourceport) >= 0) {
591 sfd->para.socket.ip.dosourceport = true;
593 retropt_bool(opts, OPT_LOWPORT, &sfd->para.socket.ip.lowport);
595 xfd->stream.dtype = XIODATA_RECVFROM_ONE;
596 if ((result =
597 _xioopen_dgram_recvfrom(sfd, xioflags, &us.soa, uslen,
598 opts, pf, socktype, ipproto, E_ERROR))
599 != STAT_OK) {
600 return result;
602 _xio_openlate(&xfd->stream, opts);
603 return STAT_OK;
607 int xioopen_udp_recv(
608 int argc,
609 const char *argv[],
610 struct opt *opts,
611 int xioflags,
612 xiofile_t *xfd,
613 const struct addrdesc *addrdesc)
615 int pf = addrdesc->arg1;
616 int socktype = addrdesc->arg2;
617 int ipproto = addrdesc->arg3;
618 union sockaddr_union us;
619 socklen_t uslen = sizeof(us);
620 char *rangename;
621 int ai_flags2[2];
622 int result;
624 if (argc != 2) {
625 xio_syntax(argv[0], 1, argc-1, addrdesc->syntax);
626 return STAT_NORETRY;
629 retropt_socket_pf(opts, &pf);
630 if (pf == PF_UNSPEC) {
631 #if WITH_IP4 && WITH_IP6
632 switch (xioparms.default_ip) {
633 case '4': pf = PF_INET; break;
634 case '6': pf = PF_INET6; break;
635 default: break; /* includes \0 */
637 #elif WITH_IP6
638 pf = PF_INET6;
639 #else
640 pf = PF_INET;
641 #endif
644 /* Set AI_PASSIVE, except when it is explicitely disabled */
645 ai_flags2[0] = xfd->stream.para.socket.ip.ai_flags[0];
646 ai_flags2[1] = xfd->stream.para.socket.ip.ai_flags[1];
647 if (!(ai_flags2[1] & AI_PASSIVE))
648 ai_flags2[0] |= AI_PASSIVE;
650 if ((result =
651 xioresolve(NULL, argv[1], pf, socktype, ipproto, &us, &uslen,
652 ai_flags2))
653 != STAT_OK) {
654 return result;
656 if (pf == PF_UNSPEC) {
657 pf = us.soa.sa_family;
660 #if 1
662 union sockaddr_union la;
663 socklen_t lalen = sizeof(la);
665 if (retropt_bind(opts, pf, socktype, ipproto,
666 &xfd->stream.para.socket.la.soa, &lalen, 1,
667 ai_flags2)
668 != STAT_NOACTION) {
669 switch (pf) {
670 #if WITH_IP4
671 case PF_INET:
672 us.ip4.sin_addr = xfd->stream.para.socket.la.ip4.sin_addr; break;
673 #endif
674 #if WITH_IP6
675 case PF_INET6:
676 us.ip6.sin6_addr = xfd->stream.para.socket.la.ip6.sin6_addr; break;
677 #endif
679 } else {
680 xfd->stream.para.socket.la.soa.sa_family = pf;
683 #endif
685 #if WITH_IP4 /*|| WITH_IP6*/
686 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
687 if (xioparserange(rangename, pf, &xfd->stream.para.socket.range,
688 xfd->stream.para.socket.ip.ai_flags)
689 < 0) {
690 free(rangename);
691 return STAT_NORETRY;
693 xfd->stream.para.socket.dorange = true;
695 #endif
697 #if WITH_LIBWRAP
698 xio_retropt_tcpwrap(&xfd->stream, opts);
699 #endif /* WITH_LIBWRAP */
701 if (retropt_ushort(opts, OPT_SOURCEPORT,
702 &xfd->stream.para.socket.ip.sourceport)
703 >= 0) {
704 xfd->stream.para.socket.ip.dosourceport = true;
706 retropt_bool(opts, OPT_LOWPORT, &xfd->stream.para.socket.ip.lowport);
708 xfd->stream.dtype = XIODATA_RECV;
709 if ((result = _xioopen_dgram_recv(&xfd->stream, xioflags, &us.soa, uslen,
710 opts, pf, socktype, ipproto, E_ERROR))
711 != STAT_OK) {
712 return result;
714 _xio_openlate(&xfd->stream, opts);
715 return result;
718 #endif /* _WITH_UDP && (WITH_IP4 || WITH_IP6) */