blob: 68743e60ef5bb6a2ee66af8af0c126f62a70e51b [file] [log] [blame]
/* 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"
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>") };
#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(":<port>") };
#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(":<host>:<port>") };
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>") };
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>") };
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>") };
#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(":<host>:<port>") };
#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(":<port>") };
#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(":<host>:<port>") };
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>") };
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>") };
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>") };
#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(":<host>:<port>") };
#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(":<port>") };
#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(":<host>:<port>") };
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>") };
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>") };
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>") };
#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, &notnull) >= 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);
if (sfd->howtoend == END_UNSPEC)
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);
}
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
*/
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;
if (sfd->howtoend == END_UNSPEC)
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);
}
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;
}
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;
}
if (sfd->para.socket.ip.dosourceport) {
switch (sfd->peersa.soa.sa_family) {
case PF_INET:
default:
sfd->para.socket.ip.sourceport = ntohs(sfd->peersa.ip4.sin_port);
break;
case PF_INET6:
sfd->para.socket.ip.sourceport = ntohs(sfd->peersa.ip6.sin6_port);
break;
}
}
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;
}
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;
if (sfd->howtoend == END_UNSPEC)
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;
}
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) */