Check which getprotobynumber_r() variant to use
[socat.git] / xio-udp.c
blob433ef777a73c2fbc6a3af5936658105d8bf75439
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(":<host>:<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(":<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 int _xioopen_ipdgram_listen(struct single *sfd,
78 int xioflags, union sockaddr_union *us, socklen_t uslen,
79 struct opt *opts, int pf, int socktype, int ipproto) {
80 union sockaddr_union themunion;
81 union sockaddr_union *them = &themunion;
82 struct pollfd readfd;
83 bool dofork = false;
84 int maxchildren = 0;
85 pid_t pid;
86 char *rangename;
87 char infobuff[256];
88 unsigned char buff1[1];
89 socklen_t themlen;
90 int result;
92 retropt_bool(opts, OPT_FORK, &dofork);
94 if (dofork) {
95 if (!(xioflags & XIO_MAYFORK)) {
96 Error("option fork not allowed here");
97 return STAT_NORETRY;
101 retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);
103 if (! dofork && maxchildren) {
104 Error("option max-children not allowed without option fork");
105 return STAT_NORETRY;
108 #if WITH_IP4 /*|| WITH_IP6*/
109 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
110 if (xioparserange(rangename, pf, &sfd->para.socket.range) < 0) {
111 free(rangename);
112 return STAT_NORETRY;
114 free(rangename);
115 sfd->para.socket.dorange = true;
117 #endif
119 #if WITH_LIBWRAP
120 xio_retropt_tcpwrap(sfd, opts);
121 #endif /* WITH_LIBWRAP */
123 if (retropt_ushort(opts, OPT_SOURCEPORT, &sfd->para.socket.ip.sourceport)
124 >= 0) {
125 sfd->para.socket.ip.dosourceport = true;
127 retropt_bool(opts, OPT_LOWPORT, &sfd->para.socket.ip.lowport);
129 if (dofork) {
130 xiosetchilddied(); /* set SIGCHLD handler */
133 while (true) { /* we loop with fork or prohibited packets */
134 /* now wait for some packet on this datagram socket, get its sender
135 address, connect there, and return */
136 int reuseaddr = dofork;
137 int doreuseaddr = (dofork != 0);
138 char infobuff[256];
139 union sockaddr_union _sockname;
140 union sockaddr_union *la = &_sockname; /* local address */
142 if ((sfd->fd = xiosocket(opts, pf, socktype, ipproto, E_ERROR)) < 0) {
143 return STAT_RETRYLATER;
145 doreuseaddr |= (retropt_int(opts, OPT_SO_REUSEADDR, &reuseaddr) >= 0);
146 applyopts(sfd->fd, opts, PH_PASTSOCKET);
147 if (doreuseaddr) {
148 if (Setsockopt(sfd->fd, opt_so_reuseaddr.major,
149 opt_so_reuseaddr.minor, &reuseaddr, sizeof(reuseaddr))
150 < 0) {
151 Warn6("setsockopt(%d, %d, %d, {%d}, "F_Zd"): %s",
152 sfd->fd, opt_so_reuseaddr.major,
153 opt_so_reuseaddr.minor, reuseaddr, sizeof(reuseaddr),
154 strerror(errno));
157 applyopts_cloexec(sfd->fd, opts);
158 applyopts(sfd->fd, opts, PH_PREBIND);
159 applyopts(sfd->fd, opts, PH_BIND);
160 if (Bind(sfd->fd, &us->soa, uslen) < 0) {
161 Error4("bind(%d, {%s}, "F_socklen"): %s", sfd->fd,
162 sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)),
163 uslen, strerror(errno));
164 return STAT_RETRYLATER;
166 /* under some circumstances bind() fills sockaddr with interesting info. */
167 if (Getsockname(sfd->fd, &us->soa, &uslen) < 0) {
168 Error4("getsockname(%d, %p, {%d}): %s",
169 sfd->fd, &us->soa, uslen, strerror(errno));
171 applyopts(sfd->fd, opts, PH_PASTBIND);
173 if (ipproto == IPPROTO_UDP) {
174 Notice1("listening on UDP %s",
175 sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)));
176 } else {
177 Notice2("listening on PROTO%d %s", ipproto,
178 sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)));
181 readfd.fd = sfd->fd;
182 readfd.events = POLLIN|POLLERR;
183 while (xiopoll(&readfd, 1, NULL) < 0) {
184 if (errno != EINTR) break;
187 themlen = socket_init(pf, them);
188 do {
189 result = Recvfrom(sfd->fd, buff1, 1, MSG_PEEK,
190 &them->soa, &themlen);
191 } while (result < 0 && errno == EINTR);
192 if (result < 0) {
193 Error5("recvfrom(%d, %p, 1, MSG_PEEK, {%s}, {"F_socklen"}): %s",
194 sfd->fd, buff1,
195 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)),
196 themlen, strerror(errno));
197 return STAT_RETRYLATER;
200 Notice1("accepting UDP connection from %s",
201 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)));
203 if (xiocheckpeer(sfd, them, la) < 0) {
204 Notice1("forbidding UDP connection from %s",
205 sockaddr_info(&them->soa, themlen,
206 infobuff, sizeof(infobuff)));
207 /* drop packet */
208 char buff[512];
209 Recv(sfd->fd, buff, sizeof(buff), 0); /* drop packet */
210 Close(sfd->fd);
211 continue;
213 Info1("permitting UDP connection from %s",
214 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)));
216 if (dofork) {
217 pid = xio_fork(false, E_ERROR);
218 if (pid < 0) {
219 return STAT_RETRYLATER;
222 if (pid == 0) { /* child */
223 pid_t cpid = Getpid();
224 xiosetenvulong("PID", cpid, 1);
225 break;
228 /* server: continue loop with socket()+recvfrom() */
229 /* when we dont close this we get awkward behaviour on Linux 2.4:
230 recvfrom gives 0 bytes with invalid socket address */
231 if (Close(sfd->fd) < 0) {
232 Info2("close(%d): %s", sfd->fd, strerror(errno));
235 while (maxchildren) {
236 if (num_child < maxchildren) break;
237 Notice("maxchildren are active, waiting");
238 /* UINT_MAX would even be nicer, but Openindiana works only
239 with 31 bits */
240 while (!Sleep(INT_MAX)) ; /* any signal lets us continue */
242 Info("still listening");
243 continue;
245 break;
246 } /* end of the big while loop */
248 applyopts(sfd->fd, opts, PH_CONNECT);
249 if ((result = Connect(sfd->fd, &them->soa, themlen)) < 0) {
250 Error4("connect(%d, {%s}, "F_socklen"): %s",
251 sfd->fd,
252 sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)),
253 themlen, strerror(errno));
254 return STAT_RETRYLATER;
257 /* set the env vars describing the local and remote sockets */
258 if (Getsockname(sfd->fd, &us->soa, &uslen) < 0) {
259 Warn4("getsockname(%d, %p, {%d}): %s",
260 sfd->fd, &us->soa, uslen, strerror(errno));
262 xiosetsockaddrenv("SOCK", us, uslen, IPPROTO_UDP);
263 xiosetsockaddrenv("PEER", them, themlen, IPPROTO_UDP);
265 sfd->howtoend = END_SHUTDOWN;
266 applyopts_fchown(sfd->fd, opts);
267 applyopts(sfd->fd, opts, PH_LATE);
269 if ((result = _xio_openlate(sfd, opts)) < 0)
270 return result;
272 return 0;
275 /* we expect the form: port */
276 int xioopen_ipdgram_listen(int argc, const char *argv[], struct opt *opts,
277 int xioflags, xiofile_t *fd,
278 unsigned groups, int pf, int ipproto,
279 int protname) {
280 const char *portname = argv[1];
281 union sockaddr_union us;
282 int socktype = SOCK_DGRAM;
283 socklen_t uslen;
285 if (argc != 2) {
286 Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1);
289 if (pf == PF_UNSPEC) {
290 #if WITH_IP4 && WITH_IP6
291 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET;
292 #elif WITH_IP6
293 pf = PF_INET6;
294 #else
295 pf = PF_INET;
296 #endif
299 retropt_socket_pf(opts, &pf);
300 retropt_int(opts, OPT_SO_PROTOTYPE, &ipproto);
302 if (applyopts_single(&fd->stream, opts, PH_INIT) < 0) return -1;
303 applyopts(-1, opts, PH_INIT);
305 uslen = socket_init(pf, &us);
306 retropt_bind(opts, pf, socktype, ipproto,
307 (struct sockaddr *)&us, &uslen, 1,
308 fd->stream.para.socket.ip.res_opts[1],
309 fd->stream.para.socket.ip.res_opts[0]);
311 if (false) {
313 #if WITH_IP4
314 } else if (pf == PF_INET) {
315 us.ip4.sin_port = parseport(portname, ipproto);
316 #endif
317 #if WITH_IP6
318 } else if (pf == PF_INET6) {
319 us.ip6.sin6_port = parseport(portname, ipproto);
320 #endif
321 } else {
322 Error1("xioopen_ipdgram_listen(): unknown address family %d", pf);
325 return _xioopen_ipdgram_listen(&fd->stream, xioflags, &us, uslen,
326 opts, pf, socktype, ipproto);
329 static
330 int xioopen_udp_sendto(int argc, const char *argv[], struct opt *opts,
331 int xioflags, xiofile_t *xxfd, unsigned groups,
332 int pf, int socktype, int ipproto) {
333 int result;
335 if (argc != 3) {
336 Error2("%s: wrong number of parameters (%d instead of 2)",
337 argv[0], argc-1);
338 return STAT_NORETRY;
341 retropt_socket_pf(opts, &pf);
342 if ((result = _xioopen_udp_sendto(argv[1], argv[2], opts, xioflags, xxfd,
343 groups, pf, socktype, ipproto))
344 != STAT_OK) {
345 return result;
347 _xio_openlate(&xxfd->stream, opts);
348 return STAT_OK;
352 applies and consumes the following option:
353 PH_INIT, PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE
354 OFUNC_OFFSET
355 OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC
357 static
358 int _xioopen_udp_sendto(const char *hostname, const char *servname,
359 struct opt *opts,
360 int xioflags, xiofile_t *xxfd, unsigned groups,
361 int pf, int socktype, int ipproto) {
362 xiosingle_t *xfd = &xxfd->stream;
363 union sockaddr_union us;
364 socklen_t uslen;
365 int feats = 3; /* option bind supports address and port */
366 bool needbind = false;
367 int result;
369 xfd->howtoend = END_SHUTDOWN;
371 /* ...res_opts[] */
372 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
373 applyopts(-1, opts, PH_INIT);
375 xfd->salen = sizeof(xfd->peersa);
376 if ((result =
377 xiogetaddrinfo(hostname, servname, pf, socktype, ipproto,
378 &xfd->peersa, &xfd->salen,
379 xfd->para.socket.ip.res_opts[0],
380 xfd->para.socket.ip.res_opts[1]))
381 != STAT_OK) {
382 return result;
384 if (pf == PF_UNSPEC) {
385 pf = xfd->peersa.soa.sa_family;
387 uslen = socket_init(pf, &us);
388 if (retropt_bind(opts, pf, socktype, ipproto, &us.soa, &uslen, feats,
389 xfd->para.socket.ip.res_opts[0],
390 xfd->para.socket.ip.res_opts[1])
391 != STAT_NOACTION) {
392 needbind = true;
395 if (retropt_ushort(opts, OPT_SOURCEPORT,
396 &xfd->para.socket.ip.sourceport) >= 0) {
397 switch (pf) {
398 #if WITH_IP4
399 case PF_INET:
400 us.ip4.sin_port = htons(xfd->para.socket.ip.sourceport);
401 break;
402 #endif
403 #if WITH_IP6
404 case PF_INET6:
405 us.ip6.sin6_port = htons(xfd->para.socket.ip.sourceport);
406 break;
407 #endif
409 needbind = true;
412 retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport);
413 if (xfd->para.socket.ip.lowport) {
414 switch (pf) {
415 #if WITH_IP4
416 case PF_INET:
417 /*!!! this is buggy */
418 us.ip4.sin_port = htons(xfd->para.socket.ip.lowport); break;
419 #endif
420 #if WITH_IP6
421 case PF_INET6:
422 /*!!! this is buggy */
423 us.ip6.sin6_port = htons(xfd->para.socket.ip.lowport); break;
424 #endif
426 needbind = true;
429 xfd->dtype = XIODATA_RECVFROM;
430 return _xioopen_dgram_sendto(needbind?&us:NULL, uslen,
431 opts, xioflags, xfd, groups,
432 pf, socktype, ipproto);
436 static
437 int xioopen_udp_datagram(int argc, const char *argv[], struct opt *opts,
438 int xioflags, xiofile_t *xxfd, unsigned groups,
439 int pf, int socktype, int ipproto) {
440 xiosingle_t *xfd = &xxfd->stream;
441 char *rangename;
442 char *hostname;
443 int result;
445 if (argc != 3) {
446 Error2("%s: wrong number of parameters (%d instead of 2)",
447 argv[0], argc-1);
448 return STAT_NORETRY;
451 if ((hostname = strdup(argv[1])) == NULL) {
452 Error1("strdup(\"%s\"): out of memory", argv[1]);
453 return STAT_RETRYLATER;
456 /* only accept packets with correct remote ports */
457 if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->para.socket.ip.sourceport)
458 >= 0) {
459 xfd->para.socket.ip.dosourceport = true;
460 xfd->para.socket.ip.sourceport = ntohs(xfd->peersa.ip4.sin_port);
463 retropt_socket_pf(opts, &pf);
464 result =
465 _xioopen_udp_sendto(hostname, argv[2], opts, xioflags, xxfd, groups,
466 pf, socktype, ipproto);
467 free(hostname);
468 if (result != STAT_OK) {
469 return result;
472 xfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO;
474 xfd->para.socket.la.soa.sa_family = xfd->peersa.soa.sa_family;
476 /* which reply packets will be accepted - determine by range option */
477 if (retropt_string(opts, OPT_RANGE, &rangename)
478 >= 0) {
479 if (xioparserange(rangename, pf, &xfd->para.socket.range) < 0) {
480 free(rangename);
481 return STAT_NORETRY;
483 xfd->para.socket.dorange = true;
484 xfd->dtype |= XIOREAD_RECV_CHECKRANGE;
485 free(rangename);
488 #if WITH_LIBWRAP
489 xio_retropt_tcpwrap(xfd, opts);
490 #endif /* WITH_LIBWRAP */
492 _xio_openlate(xfd, opts);
493 return STAT_OK;
497 static
498 int xioopen_udp_recvfrom(int argc, const char *argv[], struct opt *opts,
499 int xioflags, xiofile_t *xfd, unsigned groups,
500 int pf, int socktype, int ipproto) {
501 union sockaddr_union us;
502 socklen_t uslen = sizeof(us);
503 int result;
505 if (argc != 2) {
506 Error2("%s: wrong number of parameters (%d instead of 1)",
507 argv[0], argc-1);
508 return STAT_NORETRY;
511 xfd->stream.howtoend = END_NONE;
512 retropt_socket_pf(opts, &pf);
513 if (pf == PF_UNSPEC) {
514 #if WITH_IP4 && WITH_IP6
515 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET;
516 #elif WITH_IP6
517 pf = PF_INET6;
518 #else
519 pf = PF_INET;
520 #endif
523 if ((result =
524 xiogetaddrinfo(NULL, argv[1], pf, socktype, ipproto,
525 &us, &uslen, xfd->stream.para.socket.ip.res_opts[0],
526 xfd->stream.para.socket.ip.res_opts[1]))
527 != STAT_OK) {
528 return result;
530 if (pf == PF_UNSPEC) {
531 pf = us.soa.sa_family;
535 union sockaddr_union la;
536 socklen_t lalen = sizeof(la);
538 if (retropt_bind(opts, pf, socktype, ipproto, &la.soa, &lalen, 1,
539 xfd->stream.para.socket.ip.res_opts[0],
540 xfd->stream.para.socket.ip.res_opts[1])
541 != STAT_NOACTION) {
542 switch (pf) {
543 #if WITH_IP4
544 case PF_INET: us.ip4.sin_addr = la.ip4.sin_addr; break;
545 #endif
546 #if WITH_IP6
547 case PF_INET6: us.ip6.sin6_addr = la.ip6.sin6_addr; break;
548 #endif
553 if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->stream.para.socket.ip.sourceport) >= 0) {
554 xfd->stream.para.socket.ip.dosourceport = true;
556 retropt_bool(opts, OPT_LOWPORT, &xfd->stream.para.socket.ip.lowport);
558 xfd->stream.dtype = XIODATA_RECVFROM_ONE;
559 if ((result =
560 _xioopen_dgram_recvfrom(&xfd->stream, xioflags, &us.soa, uslen,
561 opts, pf, socktype, ipproto, E_ERROR))
562 != STAT_OK) {
563 return result;
565 _xio_openlate(&xfd->stream, opts);
566 return STAT_OK;
570 static
571 int xioopen_udp_recv(int argc, const char *argv[], struct opt *opts,
572 int xioflags, xiofile_t *xfd, unsigned groups,
573 int pf, int socktype, int ipproto) {
574 union sockaddr_union us;
575 socklen_t uslen = sizeof(us);
576 char *rangename;
577 int result;
579 if (argc != 2) {
580 Error2("%s: wrong number of parameters (%d instead of 1)",
581 argv[0], argc-1);
582 return STAT_NORETRY;
585 retropt_socket_pf(opts, &pf);
586 if (pf == PF_UNSPEC) {
587 #if WITH_IP4 && WITH_IP6
588 pf = xioopts.default_ip=='6'?PF_INET6:PF_INET;
589 #elif WITH_IP6
590 pf = PF_INET6;
591 #else
592 pf = PF_INET;
593 #endif
596 if ((result =
597 xiogetaddrinfo(NULL, argv[1], pf, socktype, ipproto,
598 &us, &uslen, xfd->stream.para.socket.ip.res_opts[0],
599 xfd->stream.para.socket.ip.res_opts[1]))
600 != STAT_OK) {
601 return result;
603 if (pf == PF_UNSPEC) {
604 pf = us.soa.sa_family;
607 #if 1
609 union sockaddr_union la;
610 socklen_t lalen = sizeof(la);
612 if (retropt_bind(opts, pf, socktype, ipproto,
613 &xfd->stream.para.socket.la.soa, &lalen, 1,
614 xfd->stream.para.socket.ip.res_opts[0],
615 xfd->stream.para.socket.ip.res_opts[1])
616 != STAT_NOACTION) {
617 switch (pf) {
618 #if WITH_IP4
619 case PF_INET:
620 us.ip4.sin_addr = xfd->stream.para.socket.la.ip4.sin_addr; break;
621 #endif
622 #if WITH_IP6
623 case PF_INET6:
624 us.ip6.sin6_addr = xfd->stream.para.socket.la.ip6.sin6_addr; break;
625 #endif
627 } else {
628 xfd->stream.para.socket.la.soa.sa_family = pf;
631 #endif
633 #if WITH_IP4 /*|| WITH_IP6*/
634 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
635 if (xioparserange(rangename, pf, &xfd->stream.para.socket.range) < 0) {
636 return STAT_NORETRY;
638 xfd->stream.para.socket.dorange = true;
640 #endif
642 #if WITH_LIBWRAP
643 xio_retropt_tcpwrap(&xfd->stream, opts);
644 #endif /* WITH_LIBWRAP */
646 if (retropt_ushort(opts, OPT_SOURCEPORT,
647 &xfd->stream.para.socket.ip.sourceport)
648 >= 0) {
649 xfd->stream.para.socket.ip.dosourceport = true;
651 retropt_bool(opts, OPT_LOWPORT, &xfd->stream.para.socket.ip.lowport);
653 xfd->stream.dtype = XIODATA_RECV;
654 if ((result = _xioopen_dgram_recv(&xfd->stream, xioflags, &us.soa, uslen,
655 opts, pf, socktype, ipproto, E_ERROR))
656 != STAT_OK) {
657 return result;
659 _xio_openlate(&xfd->stream, opts);
660 return result;
663 #endif /* WITH_UDP && (WITH_IP4 || WITH_IP6) */