/* source: xio-udp.c */ /* Copyright Gerhard Rieger and contributors (see file CHANGES) */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains the source for handling UDP addresses */ #include "xiosysincludes.h" #if WITH_UDP && (WITH_IP4 || WITH_IP6) #include "xioopen.h" #include "xio-socket.h" #include "xio-ip4.h" #include "xio-ip6.h" #include "xio-ip.h" #include "xio-ipapp.h" #include "xio-tcpwrap.h" #include "xio-udp.h" static int xioopen_udp_sendto(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc); static int xioopen_udp_datagram(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc); static int xioopen_udp_recvfrom(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc); static int xioopen_udp_recv(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc); static int _xioopen_udp_sendto(const char *hostname, const char *servname, struct opt *opts, int xioflags, xiofile_t *xxfd, groups_t groups, int pf, int socktype, int ipproto); 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("::") }; #if WITH_LISTEN 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(":") }; #endif /* WITH_LISTEN */ 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("::") }; 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(":") }; 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(":") }; 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("::") }; #if WITH_IP4 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("::") }; #if WITH_LISTEN 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(":") }; #endif /* WITH_LISTEN */ 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("::") }; 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("::") }; 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(":") }; 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(":") }; #endif /* WITH_IP4 */ #if WITH_IP6 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("::") }; #if WITH_LISTEN 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(":") }; #endif /* WITH_LISTEN */ 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("::") }; 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("::") }; 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(":") }; 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(":") }; #endif /* WITH_IP6 */ int _xioopen_ipdgram_listen(struct single *sfd, int xioflags, union sockaddr_union *us, socklen_t uslen, struct opt *opts, int pf, int socktype, int ipproto) { union sockaddr_union themunion; union sockaddr_union *them = &themunion; struct pollfd readfd; bool dofork = false; int maxchildren = 0; pid_t pid; char *rangename; char infobuff[256]; unsigned char buff1[1]; socklen_t themlen; int result; retropt_bool(opts, OPT_FORK, &dofork); if (dofork) { if (!(xioflags & XIO_MAYFORK)) { Error("option fork not allowed here"); return STAT_NORETRY; } } retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren); if (! dofork && maxchildren) { Error("option max-children not allowed without option fork"); return STAT_NORETRY; } #if WITH_IP4 /*|| WITH_IP6*/ if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) { if (xioparserange(rangename, pf, &sfd->para.socket.range, sfd->para.socket.ip.ai_flags) < 0) { free(rangename); return STAT_NORETRY; } free(rangename); sfd->para.socket.dorange = true; } #endif #if WITH_LIBWRAP xio_retropt_tcpwrap(sfd, opts); #endif /* WITH_LIBWRAP */ if (retropt_ushort(opts, OPT_SOURCEPORT, &sfd->para.socket.ip.sourceport) >= 0) { sfd->para.socket.ip.dosourceport = true; } retropt_bool(opts, OPT_LOWPORT, &sfd->para.socket.ip.lowport); if (dofork) { xiosetchilddied(); /* set SIGCHLD handler */ } while (true) { /* we loop with fork or prohibited packets */ /* now wait for some packet on this datagram socket, get its sender address, connect there, and return */ union integral notnull; union integral reuseaddr; int doreuseaddr = (dofork != 0); char infobuff[256]; union sockaddr_union _sockname; union sockaddr_union *la = &_sockname; /* local address */ reuseaddr.u_int = dofork; if ((sfd->fd = xiosocket(opts, pf, socktype, ipproto, E_ERROR)) < 0) { return STAT_RETRYLATER; } doreuseaddr |= (retropt_2integrals(opts, OPT_SO_REUSEADDR, &reuseaddr, ¬null) >= 0); applyopts(sfd, -1, opts, PH_PASTSOCKET); /* SO_REUSEADDR handling of UDP sockets is helpful on Solaris */ if (doreuseaddr) { if (Setsockopt(sfd->fd, opt_so_reuseaddr.major, opt_so_reuseaddr.minor, &reuseaddr.u_int, sizeof(reuseaddr.u_int)) < 0) { Warn6("setsockopt(%d, %d, %d, {%d}, "F_Zd"): %s", sfd->fd, opt_so_reuseaddr.major, opt_so_reuseaddr.minor, reuseaddr.u_int, sizeof(reuseaddr.u_int), strerror(errno)); } } applyopts_cloexec(sfd->fd, opts); applyopts(sfd, -1, opts, PH_PREBIND); applyopts(sfd, -1, opts, PH_BIND); if (Bind(sfd->fd, &us->soa, uslen) < 0) { Error4("bind(%d, {%s}, "F_socklen"): %s", sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), uslen, strerror(errno)); return STAT_RETRYLATER; } /* under some circumstances bind() fills sockaddr with interesting info. */ if (Getsockname(sfd->fd, &us->soa, &uslen) < 0) { Error4("getsockname(%d, %p, {%d}): %s", sfd->fd, &us->soa, uslen, strerror(errno)); } applyopts(sfd, -1, opts, PH_PASTBIND); if (ipproto == IPPROTO_UDP) { Notice1("listening on UDP %s", sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff))); } else { Notice2("listening on PROTO%d %s", ipproto, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff))); } readfd.fd = sfd->fd; readfd.events = POLLIN|POLLERR; while (xiopoll(&readfd, 1, NULL) < 0) { if (errno != EINTR) break; } themlen = socket_init(pf, them); do { result = Recvfrom(sfd->fd, buff1, 1, MSG_PEEK, &them->soa, &themlen); } while (result < 0 && errno == EINTR); if (result < 0) { Error5("recvfrom(%d, %p, 1, MSG_PEEK, {%s}, {"F_socklen"}): %s", sfd->fd, buff1, sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)), themlen, strerror(errno)); return STAT_RETRYLATER; } Notice1("accepting UDP connection from %s", sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff))); if (xiocheckpeer(sfd, them, la) < 0) { Notice1("forbidding UDP connection from %s", sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff))); /* drop packet */ char buff[512]; Recv(sfd->fd, buff, sizeof(buff), 0); /* drop packet */ Close(sfd->fd); continue; } Info1("permitting UDP connection from %s", sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff))); if (dofork) { pid = xio_fork(false, E_ERROR, sfd->shutup); if (pid < 0) { return STAT_RETRYLATER; } if (pid == 0) { /* child */ pid_t cpid = Getpid(); xiosetenvulong("PID", cpid, 1); break; } /* server: continue loop with socket()+recvfrom() */ /* This avoids the requirement of a sync (trigger) mechanism as with RECVFROM addresses */ /* And when we dont close this we got awkward behaviour on Linux 2.4: recvfrom gives 0 bytes with invalid socket address */ if (Close(sfd->fd) < 0) { Info2("close(%d): %s", sfd->fd, strerror(errno)); } while (maxchildren) { if (num_child < maxchildren) break; Notice("maxchildren are active, waiting"); /* UINT_MAX would even be nicer, but Openindiana works only with 31 bits */ while (!Sleep(INT_MAX)) ; /* any signal lets us continue */ } Info("still listening"); continue; } break; } /* end of the big while loop */ applyopts(sfd, -1, opts, PH_CONNECT); if ((result = Connect(sfd->fd, &them->soa, themlen)) < 0) { Error4("connect(%d, {%s}, "F_socklen"): %s", sfd->fd, sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff)), themlen, strerror(errno)); return STAT_RETRYLATER; } /* set the env vars describing the local and remote sockets */ if (Getsockname(sfd->fd, &us->soa, &uslen) < 0) { Warn4("getsockname(%d, %p, {%d}): %s", sfd->fd, &us->soa, uslen, strerror(errno)); } xiosetsockaddrenv("SOCK", us, uslen, IPPROTO_UDP); xiosetsockaddrenv("PEER", them, themlen, IPPROTO_UDP); sfd->howtoend = END_SHUTDOWN; applyopts_fchown(sfd->fd, opts); applyopts(sfd, -1, opts, PH_LATE); if ((result = _xio_openlate(sfd, opts)) < 0) return result; return 0; } /* we expect the form: port */ int xioopen_ipdgram_listen( int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc) { struct single *sfd = &xfd->stream; const char *portname = argv[1]; int pf = addrdesc->arg1; int ipproto = addrdesc->arg2; union sockaddr_union us; int socktype = SOCK_DGRAM; socklen_t uslen; if (argc != 2) { xio_syntax(argv[0], 1, argc-1, addrdesc->syntax); return STAT_NORETRY; } xioinit_ip(&pf, xioparms.default_ip); if (pf == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 switch (xioparms.default_ip) { case '4': pf = PF_INET; break; case '6': pf = PF_INET6; break; default: break; /* includes \0 */ } #elif WITH_IP6 pf = PF_INET6; #else pf = PF_INET; #endif } retropt_socket_pf(opts, &pf); retropt_int(opts, OPT_SO_PROTOTYPE, &ipproto); if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1; applyopts(sfd, -1, opts, PH_INIT); uslen = socket_init(pf, &us); retropt_bind(opts, pf, socktype, ipproto, (struct sockaddr *)&us, &uslen, 1, xfd->stream.para.socket.ip.ai_flags); if (false) { ; #if WITH_IP4 } else if (pf == PF_INET) { us.ip4.sin_port = parseport(portname, ipproto); #endif #if WITH_IP6 } else if (pf == PF_INET6) { us.ip6.sin6_port = parseport(portname, ipproto); #endif } else { Error1("xioopen_ipdgram_listen(): unknown address family %d", pf); } return _xioopen_ipdgram_listen(&xfd->stream, xioflags, &us, uslen, opts, pf, socktype, ipproto); } static int xioopen_udp_sendto( int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc) { int pf = addrdesc->arg1; int socktype = addrdesc->arg2; int ipproto = addrdesc->arg3; int result; if (argc != 3) { xio_syntax(argv[0], 2, argc-1, addrdesc->syntax); return STAT_NORETRY; } retropt_socket_pf(opts, &pf); if ((result = _xioopen_udp_sendto(argv[1], argv[2], opts, xioflags, xfd, addrdesc->groups, pf, socktype, ipproto)) != STAT_OK) { return result; } _xio_openlate(&xfd->stream, opts); return STAT_OK; } /* applies and consumes the following option: PH_INIT, PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE OFUNC_OFFSET OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC */ static int _xioopen_udp_sendto(const char *hostname, const char *servname, struct opt *opts, int xioflags, xiofile_t *xxfd, groups_t groups, int pf, int socktype, int ipproto) { struct single *sfd = &xxfd->stream; union sockaddr_union us; socklen_t uslen; int feats = 3; /* option bind supports address and port */ bool needbind = false; int result; sfd->howtoend = END_SHUTDOWN; /* ...res_opts[] */ if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1; applyopts(sfd, -1, opts, PH_INIT); sfd->salen = sizeof(sfd->peersa); if ((result = xioresolve(hostname, servname, pf, socktype, ipproto, &sfd->peersa, &sfd->salen, sfd->para.socket.ip.ai_flags)) != STAT_OK) { return result; } if (pf == PF_UNSPEC) { pf = sfd->peersa.soa.sa_family; } uslen = socket_init(pf, &us); if (retropt_bind(opts, pf, socktype, ipproto, &us.soa, &uslen, feats, sfd->para.socket.ip.ai_flags) != STAT_NOACTION) { needbind = true; } if (retropt_ushort(opts, OPT_SOURCEPORT, &sfd->para.socket.ip.sourceport) >= 0) { switch (pf) { #if WITH_IP4 case PF_INET: us.ip4.sin_port = htons(sfd->para.socket.ip.sourceport); break; #endif #if WITH_IP6 case PF_INET6: us.ip6.sin6_port = htons(sfd->para.socket.ip.sourceport); break; #endif } needbind = true; } retropt_bool(opts, OPT_LOWPORT, &sfd->para.socket.ip.lowport); sfd->dtype = XIODATA_RECVFROM; return _xioopen_dgram_sendto(needbind?&us:NULL, uslen, opts, xioflags, sfd, groups, pf, socktype, ipproto, sfd->para.socket.ip.lowport); } static int xioopen_udp_datagram( int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xxfd, const struct addrdesc *addrdesc) { struct single *sfd = &xxfd->stream; int pf = addrdesc->arg1; int socktype = addrdesc->arg2; int ipproto = addrdesc->arg3; char *rangename; char *hostname; int result; if (argc != 3) { xio_syntax(argv[0], 2, argc-1, addrdesc->syntax); return STAT_NORETRY; } if ((hostname = strdup(argv[1])) == NULL) { Error1("strdup(\"%s\"): out of memory", argv[1]); return STAT_RETRYLATER; } /* only accept packets with correct remote ports */ if (retropt_ushort(opts, OPT_SOURCEPORT, &sfd->para.socket.ip.sourceport) >= 0) { sfd->para.socket.ip.dosourceport = true; sfd->para.socket.ip.sourceport = ntohs(sfd->peersa.ip4.sin_port); } retropt_socket_pf(opts, &pf); result = _xioopen_udp_sendto(hostname, argv[2], opts, xioflags, xxfd, addrdesc->groups, pf, socktype, ipproto); free(hostname); if (result != STAT_OK) { return result; } sfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO; sfd->para.socket.la.soa.sa_family = sfd->peersa.soa.sa_family; /* which reply packets will be accepted - determine by range option */ if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) { if (xioparserange(rangename, pf, &sfd->para.socket.range, sfd->para.socket.ip.ai_flags) < 0) { free(rangename); return STAT_NORETRY; } sfd->para.socket.dorange = true; sfd->dtype |= XIOREAD_RECV_CHECKRANGE; free(rangename); } #if WITH_LIBWRAP xio_retropt_tcpwrap(sfd, opts); #endif /* WITH_LIBWRAP */ _xio_openlate(sfd, opts); return STAT_OK; } static int xioopen_udp_recvfrom( int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc) { struct single *sfd = &xfd->stream; int pf = addrdesc->arg1; int socktype = addrdesc->arg2; int ipproto = addrdesc->arg3; union sockaddr_union us; socklen_t uslen = sizeof(us); int ai_flags2[2]; int result; if (argc != 2) { xio_syntax(argv[0], 1, argc-1, addrdesc->syntax); return STAT_NORETRY; } xioinit_ip(&pf, xioparms.default_ip); sfd->howtoend = END_NONE; retropt_socket_pf(opts, &pf); if (pf == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 switch (xioparms.default_ip) { case '4': pf = PF_INET; break; case '6': pf = PF_INET6; break; default: break; /* includes \0 */ } #elif WITH_IP6 pf = PF_INET6; #else pf = PF_INET; #endif } /* Set AI_PASSIVE, except when it is explicitely disabled */ ai_flags2[0] = xfd->stream.para.socket.ip.ai_flags[0]; ai_flags2[1] = xfd->stream.para.socket.ip.ai_flags[1]; if (!(ai_flags2[1] & AI_PASSIVE)) ai_flags2[0] |= AI_PASSIVE; if ((result = xioresolve(NULL, argv[1], pf, socktype, ipproto, &us, &uslen, ai_flags2)) != STAT_OK) { return result; } if (pf == PF_UNSPEC) { pf = us.soa.sa_family; } { union sockaddr_union la; socklen_t lalen = sizeof(la); if (retropt_bind(opts, pf, socktype, ipproto, &la.soa, &lalen, 1, sfd->para.socket.ip.ai_flags) != STAT_NOACTION) { switch (pf) { #if WITH_IP4 case PF_INET: us.ip4.sin_addr = la.ip4.sin_addr; break; #endif #if WITH_IP6 case PF_INET6: us.ip6.sin6_addr = la.ip6.sin6_addr; break; #endif } } } if (retropt_ushort(opts, OPT_SOURCEPORT, &sfd->para.socket.ip.sourceport) >= 0) { sfd->para.socket.ip.dosourceport = true; } retropt_bool(opts, OPT_LOWPORT, &sfd->para.socket.ip.lowport); xfd->stream.dtype = XIODATA_RECVFROM_ONE; if ((result = _xioopen_dgram_recvfrom(sfd, xioflags, &us.soa, uslen, opts, pf, socktype, ipproto, E_ERROR)) != STAT_OK) { return result; } _xio_openlate(&xfd->stream, opts); return STAT_OK; } static int xioopen_udp_recv( int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc) { int pf = addrdesc->arg1; int socktype = addrdesc->arg2; int ipproto = addrdesc->arg3; union sockaddr_union us; socklen_t uslen = sizeof(us); char *rangename; int ai_flags2[2]; int result; if (argc != 2) { xio_syntax(argv[0], 1, argc-1, addrdesc->syntax); return STAT_NORETRY; } retropt_socket_pf(opts, &pf); if (pf == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 switch (xioparms.default_ip) { case '4': pf = PF_INET; break; case '6': pf = PF_INET6; break; default: break; /* includes \0 */ } #elif WITH_IP6 pf = PF_INET6; #else pf = PF_INET; #endif } /* Set AI_PASSIVE, except when it is explicitely disabled */ ai_flags2[0] = xfd->stream.para.socket.ip.ai_flags[0]; ai_flags2[1] = xfd->stream.para.socket.ip.ai_flags[1]; if (!(ai_flags2[1] & AI_PASSIVE)) ai_flags2[0] |= AI_PASSIVE; if ((result = xioresolve(NULL, argv[1], pf, socktype, ipproto, &us, &uslen, ai_flags2)) != STAT_OK) { return result; } if (pf == PF_UNSPEC) { pf = us.soa.sa_family; } #if 1 { union sockaddr_union la; socklen_t lalen = sizeof(la); if (retropt_bind(opts, pf, socktype, ipproto, &xfd->stream.para.socket.la.soa, &lalen, 1, ai_flags2) != STAT_NOACTION) { switch (pf) { #if WITH_IP4 case PF_INET: us.ip4.sin_addr = xfd->stream.para.socket.la.ip4.sin_addr; break; #endif #if WITH_IP6 case PF_INET6: us.ip6.sin6_addr = xfd->stream.para.socket.la.ip6.sin6_addr; break; #endif } } else { xfd->stream.para.socket.la.soa.sa_family = pf; } } #endif #if WITH_IP4 /*|| WITH_IP6*/ if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) { if (xioparserange(rangename, pf, &xfd->stream.para.socket.range, xfd->stream.para.socket.ip.ai_flags) < 0) { free(rangename); return STAT_NORETRY; } xfd->stream.para.socket.dorange = true; } #endif #if WITH_LIBWRAP xio_retropt_tcpwrap(&xfd->stream, opts); #endif /* WITH_LIBWRAP */ if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->stream.para.socket.ip.sourceport) >= 0) { xfd->stream.para.socket.ip.dosourceport = true; } retropt_bool(opts, OPT_LOWPORT, &xfd->stream.para.socket.ip.lowport); xfd->stream.dtype = XIODATA_RECV; if ((result = _xioopen_dgram_recv(&xfd->stream, xioflags, &us.soa, uslen, opts, pf, socktype, ipproto, E_ERROR)) != STAT_OK) { return result; } _xio_openlate(&xfd->stream, opts); return result; } #endif /* WITH_UDP && (WITH_IP4 || WITH_IP6) */