GOPEN handles UNIX seqpacket sockets
[socat.git] / xio-udp.c
blob0d027eb651bc02ef7796b07d7a2abfe94f14d938
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"
22 static
23 int xioopen_udp_sendto(int argc, const char *argv[], struct opt *opts,
24 int xioflags, xiofile_t *xfd, unsigned groups,
25 int pf, int socktype, int ipproto);
26 static
27 int xioopen_udp_datagram(int argc, const char *argv[], struct opt *opts,
28 int xioflags, xiofile_t *xfd, unsigned groups,
29 int pf, int socktype, int ipproto);
30 static
31 int xioopen_udp_recvfrom(int argc, const char *argv[], struct opt *opts,
32 int xioflags, xiofile_t *xfd, unsigned groups,
33 int pf, int socktype, int ipproto);
34 static
35 int xioopen_udp_recv(int argc, const char *argv[], struct opt *opts,
36 int xioflags, xiofile_t *xfd, unsigned groups,
37 int pf, int socktype, int ipproto);
39 static
40 int _xioopen_udp_sendto(const char *hostname, const char *servname,
41 struct opt *opts,
42 int xioflags, xiofile_t *xxfd, unsigned groups,
43 int pf, int socktype, int ipproto);
45 const struct addrdesc addr_udp_connect = { "udp-connect", 3, 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>") };
46 #if WITH_LISTEN
47 const struct addrdesc addr_udp_listen = { "udp-listen", 3, 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>") };
48 #endif /* WITH_LISTEN */
49 const struct addrdesc addr_udp_sendto = { "udp-sendto", 3, 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>") };
50 const struct addrdesc addr_udp_recvfrom = { "udp-recvfrom", 3, 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>") };
51 const struct addrdesc addr_udp_recv = { "udp-recv", 1, 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>") };
52 const struct addrdesc addr_udp_datagram = { "udp-datagram", 3, 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>") };
54 #if WITH_IP4
55 const struct addrdesc addr_udp4_connect = { "udp4-connect", 3, xioopen_ipapp_connect, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP, SOCK_DGRAM, IPPROTO_UDP, PF_INET HELP(":<host>:<port>") };
56 #if WITH_LISTEN
57 const struct addrdesc addr_udp4_listen = { "udp4-listen", 3, 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>") };
58 #endif /* WITH_LISTEN */
59 const struct addrdesc addr_udp4_sendto = { "udp4-sendto", 3, xioopen_udp_sendto, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP, PF_INET, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
60 const struct addrdesc addr_udp4_datagram = { "udp4-datagram",3, xioopen_udp_datagram, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP|GROUP_RANGE, PF_INET, SOCK_DGRAM, IPPROTO_UDP HELP(":<remote-address>:<port>") };
61 const struct addrdesc addr_udp4_recvfrom= { "udp4-recvfrom", 3, xioopen_udp_recvfrom, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP|GROUP_CHILD|GROUP_RANGE, PF_INET, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
62 const struct addrdesc addr_udp4_recv = { "udp4-recv", 1, xioopen_udp_recv, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP4|GROUP_IP_UDP|GROUP_RANGE, PF_INET, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
63 #endif /* WITH_IP4 */
65 #if WITH_IP6
66 const struct addrdesc addr_udp6_connect = { "udp6-connect", 3, xioopen_ipapp_connect, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP, SOCK_DGRAM, IPPROTO_UDP, PF_INET6 HELP(":<host>:<port>") };
67 #if WITH_LISTEN
68 const struct addrdesc addr_udp6_listen = { "udp6-listen", 3, 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>") };
69 #endif /* WITH_LISTEN */
70 const struct addrdesc addr_udp6_sendto = { "udp6-sendto", 3, xioopen_udp_sendto, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP, PF_INET6, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
71 const struct addrdesc addr_udp6_datagram= { "udp6-datagram", 3, xioopen_udp_datagram,GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_RANGE, PF_INET6, SOCK_DGRAM, IPPROTO_UDP HELP(":<host>:<port>") };
72 const struct addrdesc addr_udp6_recvfrom= { "udp6-recvfrom", 3, 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>") };
73 const struct addrdesc addr_udp6_recv = { "udp6-recv", 1, xioopen_udp_recv, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_IP6|GROUP_IP_UDP|GROUP_RANGE, PF_INET6, SOCK_DGRAM, IPPROTO_UDP HELP(":<port>") };
74 #endif /* WITH_IP6 */
77 /* we expect the form: port */
78 int xioopen_ipdgram_listen(int argc, const char *argv[], struct opt *opts,
79 int xioflags, xiofile_t *fd,
80 unsigned groups, int pf, int ipproto,
81 int protname) {
82 const char *portname = argv[1];
83 union sockaddr_union us;
84 union sockaddr_union themunion;
85 union sockaddr_union *them = &themunion;
86 int socktype = SOCK_DGRAM;
87 struct pollfd readfd;
88 bool dofork = false;
89 int maxchildren = 0;
90 pid_t pid;
91 char *rangename;
92 char infobuff[256];
93 unsigned char buff1[1];
94 socklen_t uslen;
95 socklen_t themlen;
96 int result;
98 if (argc != 2) {
99 Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1);
102 if (pf == PF_UNSPEC) {
103 #if WITH_IP4 && WITH_IP6
104 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET;
105 #elif WITH_IP6
106 pf = PF_INET6;
107 #else
108 pf = PF_INET;
109 #endif
112 retropt_socket_pf(opts, &pf);
114 if (applyopts_single(&fd->stream, opts, PH_INIT) < 0) return -1;
115 applyopts(-1, opts, PH_INIT);
117 uslen = socket_init(pf, &us);
118 retropt_bind(opts, pf, socktype, IPPROTO_UDP,
119 (struct sockaddr *)&us, &uslen, 1,
120 fd->stream.para.socket.ip.res_opts[1],
121 fd->stream.para.socket.ip.res_opts[0]);
123 if (false) {
125 #if WITH_IP4
126 } else if (pf == PF_INET) {
127 us.ip4.sin_port = parseport(portname, ipproto);
128 #endif
129 #if WITH_IP6
130 } else if (pf == PF_INET6) {
131 us.ip6.sin6_port = parseport(portname, ipproto);
132 #endif
133 } else {
134 Error1("xioopen_ipdgram_listen(): unknown address family %d", pf);
137 retropt_bool(opts, OPT_FORK, &dofork);
139 if (dofork) {
140 if (!(xioflags & XIO_MAYFORK)) {
141 Error("option fork not allowed here");
142 return STAT_NORETRY;
146 retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);
148 if (! dofork && maxchildren) {
149 Error("option max-children not allowed without option fork");
150 return STAT_NORETRY;
153 #if WITH_IP4 /*|| WITH_IP6*/
154 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
155 if (xioparserange(rangename, pf, &fd->stream.para.socket.range) < 0) {
156 free(rangename);
157 return STAT_NORETRY;
159 free(rangename);
160 fd->stream.para.socket.dorange = true;
162 #endif
164 #if WITH_LIBWRAP
165 xio_retropt_tcpwrap(&fd->stream, opts);
166 #endif /* WITH_LIBWRAP */
168 if (retropt_ushort(opts, OPT_SOURCEPORT, &fd->stream.para.socket.ip.sourceport)
169 >= 0) {
170 fd->stream.para.socket.ip.dosourceport = true;
172 retropt_bool(opts, OPT_LOWPORT, &fd->stream.para.socket.ip.lowport);
174 if (dofork) {
175 xiosetchilddied(); /* set SIGCHLD handler */
178 while (true) { /* we loop with fork or prohibited packets */
179 /* now wait for some packet on this datagram socket, get its sender
180 address, connect there, and return */
181 int reuseaddr = dofork;
182 int doreuseaddr = (dofork != 0);
183 char infobuff[256];
184 union sockaddr_union _sockname;
185 union sockaddr_union *la = &_sockname; /* local address */
187 if ((fd->stream.fd = xiosocket(opts, pf, socktype, ipproto, E_ERROR)) < 0) {
188 return STAT_RETRYLATER;
190 doreuseaddr |= (retropt_int(opts, OPT_SO_REUSEADDR, &reuseaddr) >= 0);
191 applyopts(fd->stream.fd, opts, PH_PASTSOCKET);
192 if (doreuseaddr) {
193 if (Setsockopt(fd->stream.fd, opt_so_reuseaddr.major,
194 opt_so_reuseaddr.minor, &reuseaddr, sizeof(reuseaddr))
195 < 0) {
196 Warn6("setsockopt(%d, %d, %d, {%d}, "F_Zd"): %s",
197 fd->stream.fd, opt_so_reuseaddr.major,
198 opt_so_reuseaddr.minor, reuseaddr, sizeof(reuseaddr),
199 strerror(errno));
202 applyopts_cloexec(fd->stream.fd, opts);
203 applyopts(fd->stream.fd, opts, PH_PREBIND);
204 applyopts(fd->stream.fd, opts, PH_BIND);
205 if (Bind(fd->stream.fd, &us.soa, uslen) < 0) {
206 Error4("bind(%d, {%s}, "F_socklen"): %s", fd->stream.fd,
207 sockaddr_info(&us.soa, uslen, infobuff, sizeof(infobuff)),
208 uslen, strerror(errno));
209 return STAT_RETRYLATER;
211 /* under some circumstances bind() fills sockaddr with interesting info. */
212 if (Getsockname(fd->stream.fd, &us.soa, &uslen) < 0) {
213 Error4("getsockname(%d, %p, {%d}): %s",
214 fd->stream.fd, &us.soa, uslen, strerror(errno));
216 applyopts(fd->stream.fd, opts, PH_PASTBIND);
218 Notice1("listening on UDP %s",
219 sockaddr_info(&us.soa, uslen, infobuff, sizeof(infobuff)));
220 readfd.fd = fd->stream.fd;
221 readfd.events = POLLIN|POLLERR;
222 while (xiopoll(&readfd, 1, NULL) < 0) {
223 if (errno != EINTR) break;
226 themlen = socket_init(pf, them);
227 do {
228 result = Recvfrom(fd->stream.fd, buff1, 1, MSG_PEEK,
229 &them->soa, &themlen);
230 } while (result < 0 && errno == EINTR);
231 if (result < 0) {
232 Error5("recvfrom(%d, %p, 1, MSG_PEEK, {%s}, {"F_socklen"}): %s",
233 fd->stream.fd, buff1,
234 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)),
235 themlen, strerror(errno));
236 return STAT_RETRYLATER;
239 Notice1("accepting UDP connection from %s",
240 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)));
242 if (xiocheckpeer(&fd->stream, them, la) < 0) {
243 /* drop packet */
244 char buff[512];
245 Recv(fd->stream.fd, buff, sizeof(buff), 0); /* drop packet */
246 Close(fd->stream.fd);
247 continue;
249 Info1("permitting UDP connection from %s",
250 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)));
252 if (dofork) {
253 pid = xio_fork(false, E_ERROR);
254 if (pid < 0) {
255 return STAT_RETRYLATER;
258 if (pid == 0) { /* child */
259 pid_t cpid = Getpid();
260 xiosetenvulong("PID", cpid, 1);
261 break;
264 /* server: continue loop with socket()+recvfrom() */
265 /* when we dont close this we get awkward behaviour on Linux 2.4:
266 recvfrom gives 0 bytes with invalid socket address */
267 if (Close(fd->stream.fd) < 0) {
268 Info2("close(%d): %s", fd->stream.fd, strerror(errno));
271 while (maxchildren) {
272 if (num_child < maxchildren) break;
273 Notice("maxchildren are active, waiting");
274 /* UINT_MAX would even be nicer, but Openindiana works only
275 with 31 bits */
276 while (!Sleep(INT_MAX)) ; /* any signal lets us continue */
278 Info("still listening");
279 continue;
281 break;
284 applyopts(fd->stream.fd, opts, PH_CONNECT);
285 if ((result = Connect(fd->stream.fd, &them->soa, themlen)) < 0) {
286 Error4("connect(%d, {%s}, "F_socklen"): %s",
287 fd->stream.fd,
288 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)),
289 themlen, strerror(errno));
290 return STAT_RETRYLATER;
293 /* set the env vars describing the local and remote sockets */
294 if (Getsockname(fd->stream.fd, &us.soa, &uslen) < 0) {
295 Warn4("getsockname(%d, %p, {%d}): %s",
296 fd->stream.fd, &us.soa, uslen, strerror(errno));
298 xiosetsockaddrenv("SOCK", &us, uslen, IPPROTO_UDP);
299 xiosetsockaddrenv("PEER", them, themlen, IPPROTO_UDP);
301 fd->stream.howtoend = END_SHUTDOWN;
302 applyopts_fchown(fd->stream.fd, opts);
303 applyopts(fd->stream.fd, opts, PH_LATE);
305 if ((result = _xio_openlate(&fd->stream, opts)) < 0)
306 return result;
308 return 0;
312 static
313 int xioopen_udp_sendto(int argc, const char *argv[], struct opt *opts,
314 int xioflags, xiofile_t *xxfd, unsigned groups,
315 int pf, int socktype, int ipproto) {
316 int result;
318 if (argc != 3) {
319 Error2("%s: wrong number of parameters (%d instead of 2)",
320 argv[0], argc-1);
321 return STAT_NORETRY;
324 retropt_socket_pf(opts, &pf);
325 if ((result = _xioopen_udp_sendto(argv[1], argv[2], opts, xioflags, xxfd,
326 groups, pf, socktype, ipproto))
327 != STAT_OK) {
328 return result;
330 _xio_openlate(&xxfd->stream, opts);
331 return STAT_OK;
335 applies and consumes the following option:
336 PH_INIT, PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE
337 OFUNC_OFFSET
338 OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
340 static
341 int _xioopen_udp_sendto(const char *hostname, const char *servname,
342 struct opt *opts,
343 int xioflags, xiofile_t *xxfd, unsigned groups,
344 int pf, int socktype, int ipproto) {
345 xiosingle_t *xfd = &xxfd->stream;
346 union sockaddr_union us;
347 socklen_t uslen;
348 int feats = 3; /* option bind supports address and port */
349 bool needbind = false;
350 int result;
352 xfd->howtoend = END_SHUTDOWN;
354 /* ...res_opts[] */
355 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
356 applyopts(-1, opts, PH_INIT);
358 xfd->salen = sizeof(xfd->peersa);
359 if ((result =
360 xiogetaddrinfo(hostname, servname, pf, socktype, ipproto,
361 &xfd->peersa, &xfd->salen,
362 xfd->para.socket.ip.res_opts[0],
363 xfd->para.socket.ip.res_opts[1]))
364 != STAT_OK) {
365 return result;
367 if (pf == PF_UNSPEC) {
368 pf = xfd->peersa.soa.sa_family;
370 uslen = socket_init(pf, &us);
371 if (retropt_bind(opts, pf, socktype, ipproto, &us.soa, &uslen, feats,
372 xfd->para.socket.ip.res_opts[0],
373 xfd->para.socket.ip.res_opts[1])
374 != STAT_NOACTION) {
375 needbind = true;
378 if (retropt_ushort(opts, OPT_SOURCEPORT,
379 &xfd->para.socket.ip.sourceport) >= 0) {
380 switch (pf) {
381 #if WITH_IP4
382 case PF_INET:
383 us.ip4.sin_port = htons(xfd->para.socket.ip.sourceport);
384 break;
385 #endif
386 #if WITH_IP6
387 case PF_INET6:
388 us.ip6.sin6_port = htons(xfd->para.socket.ip.sourceport);
389 break;
390 #endif
392 needbind = true;
395 retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport);
396 if (xfd->para.socket.ip.lowport) {
397 switch (pf) {
398 #if WITH_IP4
399 case PF_INET:
400 /*!!! this is buggy */
401 us.ip4.sin_port = htons(xfd->para.socket.ip.lowport); break;
402 #endif
403 #if WITH_IP6
404 case PF_INET6:
405 /*!!! this is buggy */
406 us.ip6.sin6_port = htons(xfd->para.socket.ip.lowport); break;
407 #endif
409 needbind = true;
412 xfd->dtype = XIODATA_RECVFROM;
413 return _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
414 opts, xioflags, xfd, groups,
415 pf, socktype, ipproto);
419 static
420 int xioopen_udp_datagram(int argc, const char *argv[], struct opt *opts,
421 int xioflags, xiofile_t *xxfd, unsigned groups,
422 int pf, int socktype, int ipproto) {
423 xiosingle_t *xfd = &xxfd->stream;
424 char *rangename;
425 char *hostname;
426 int result;
428 if (argc != 3) {
429 Error2("%s: wrong number of parameters (%d instead of 2)",
430 argv[0], argc-1);
431 return STAT_NORETRY;
434 if ((hostname = strdup(argv[1])) == NULL) {
435 Error1("strdup(\"%s\"): out of memory", argv[1]);
436 return STAT_RETRYLATER;
439 retropt_socket_pf(opts, &pf);
440 result =
441 _xioopen_udp_sendto(hostname, argv[2], opts, xioflags, xxfd, groups,
442 pf, socktype, ipproto);
443 free(hostname);
444 if (result != STAT_OK) {
445 return result;
448 xfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO;
450 xfd->para.socket.la.soa.sa_family = xfd->peersa.soa.sa_family;
452 /* only accept packets with correct remote ports */
453 xfd->para.socket.ip.sourceport = ntohs(xfd->peersa.ip4.sin_port);
454 xfd->para.socket.ip.dosourceport = true;
456 /* which reply packets will be accepted - determine by range option */
457 if (retropt_string(opts, OPT_RANGE, &rangename)
458 >= 0) {
459 if (xioparserange(rangename, pf, &xfd->para.socket.range) < 0) {
460 free(rangename);
461 return STAT_NORETRY;
463 xfd->para.socket.dorange = true;
464 xfd->dtype |= XIOREAD_RECV_CHECKRANGE;
465 free(rangename);
468 #if WITH_LIBWRAP
469 xio_retropt_tcpwrap(xfd, opts);
470 #endif /* WITH_LIBWRAP */
472 _xio_openlate(xfd, opts);
473 return STAT_OK;
477 static
478 int xioopen_udp_recvfrom(int argc, const char *argv[], struct opt *opts,
479 int xioflags, xiofile_t *xfd, unsigned groups,
480 int pf, int socktype, int ipproto) {
481 union sockaddr_union us;
482 socklen_t uslen = sizeof(us);
483 int result;
485 if (argc != 2) {
486 Error2("%s: wrong number of parameters (%d instead of 1)",
487 argv[0], argc-1);
488 return STAT_NORETRY;
491 xfd->stream.howtoend = END_NONE;
492 retropt_socket_pf(opts, &pf);
493 if (pf == PF_UNSPEC) {
494 #if WITH_IP4 && WITH_IP6
495 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET;
496 #elif WITH_IP6
497 pf = PF_INET6;
498 #else
499 pf = PF_INET;
500 #endif
503 if ((result =
504 xiogetaddrinfo(NULL, argv[1], pf, socktype, ipproto,
505 &us, &uslen, xfd->stream.para.socket.ip.res_opts[0],
506 xfd->stream.para.socket.ip.res_opts[1]))
507 != STAT_OK) {
508 return result;
510 if (pf == PF_UNSPEC) {
511 pf = us.soa.sa_family;
515 union sockaddr_union la;
516 socklen_t lalen = sizeof(la);
518 if (retropt_bind(opts, pf, socktype, ipproto, &la.soa, &lalen, 1,
519 xfd->stream.para.socket.ip.res_opts[0],
520 xfd->stream.para.socket.ip.res_opts[1])
521 != STAT_NOACTION) {
522 switch (pf) {
523 #if WITH_IP4
524 case PF_INET: us.ip4.sin_addr = la.ip4.sin_addr; break;
525 #endif
526 #if WITH_IP6
527 case PF_INET6: us.ip6.sin6_addr = la.ip6.sin6_addr; break;
528 #endif
533 if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->stream.para.socket.ip.sourceport) >= 0) {
534 xfd->stream.para.socket.ip.dosourceport = true;
536 retropt_bool(opts, OPT_LOWPORT, &xfd->stream.para.socket.ip.lowport);
538 xfd->stream.dtype = XIODATA_RECVFROM_ONE;
539 if ((result =
540 _xioopen_dgram_recvfrom(&xfd->stream, xioflags, &us.soa, uslen,
541 opts, pf, socktype, ipproto, E_ERROR))
542 != STAT_OK) {
543 return result;
545 _xio_openlate(&xfd->stream, opts);
546 return STAT_OK;
550 static
551 int xioopen_udp_recv(int argc, const char *argv[], struct opt *opts,
552 int xioflags, xiofile_t *xfd, unsigned groups,
553 int pf, int socktype, int ipproto) {
554 union sockaddr_union us;
555 socklen_t uslen = sizeof(us);
556 char *rangename;
557 int result;
559 if (argc != 2) {
560 Error2("%s: wrong number of parameters (%d instead of 1)",
561 argv[0], argc-1);
562 return STAT_NORETRY;
565 retropt_socket_pf(opts, &pf);
566 if (pf == PF_UNSPEC) {
567 #if WITH_IP4 && WITH_IP6
568 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET;
569 #elif WITH_IP6
570 pf = PF_INET6;
571 #else
572 pf = PF_INET;
573 #endif
576 if ((result =
577 xiogetaddrinfo(NULL, argv[1], pf, socktype, ipproto,
578 &us, &uslen, xfd->stream.para.socket.ip.res_opts[0],
579 xfd->stream.para.socket.ip.res_opts[1]))
580 != STAT_OK) {
581 return result;
583 if (pf == PF_UNSPEC) {
584 pf = us.soa.sa_family;
587 #if 1
589 union sockaddr_union la;
590 socklen_t lalen = sizeof(la);
592 if (retropt_bind(opts, pf, socktype, ipproto,
593 &xfd->stream.para.socket.la.soa, &lalen, 1,
594 xfd->stream.para.socket.ip.res_opts[0],
595 xfd->stream.para.socket.ip.res_opts[1])
596 != STAT_NOACTION) {
597 switch (pf) {
598 #if WITH_IP4
599 case PF_INET:
600 us.ip4.sin_addr = xfd->stream.para.socket.la.ip4.sin_addr; break;
601 #endif
602 #if WITH_IP6
603 case PF_INET6:
604 us.ip6.sin6_addr = xfd->stream.para.socket.la.ip6.sin6_addr; break;
605 #endif
607 } else {
608 xfd->stream.para.socket.la.soa.sa_family = pf;
611 #endif
613 #if WITH_IP4 /*|| WITH_IP6*/
614 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
615 if (xioparserange(rangename, pf, &xfd->stream.para.socket.range) < 0) {
616 return STAT_NORETRY;
618 xfd->stream.para.socket.dorange = true;
620 #endif
622 #if WITH_LIBWRAP
623 xio_retropt_tcpwrap(&xfd->stream, opts);
624 #endif /* WITH_LIBWRAP */
626 if (retropt_ushort(opts, OPT_SOURCEPORT,
627 &xfd->stream.para.socket.ip.sourceport)
628 >= 0) {
629 xfd->stream.para.socket.ip.dosourceport = true;
631 retropt_bool(opts, OPT_LOWPORT, &xfd->stream.para.socket.ip.lowport);
633 xfd->stream.dtype = XIODATA_RECV;
634 if ((result = _xioopen_dgram_recv(&xfd->stream, xioflags, &us.soa, uslen,
635 opts, pf, socktype, ipproto, E_ERROR))
636 != STAT_OK) {
637 return result;
639 _xio_openlate(&xfd->stream, opts);
640 return result;
643 #endif /* WITH_UDP && (WITH_IP4 || WITH_IP6) */