| /* source: xio-socket.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 socket related functions, and the |
| implementation of generic socket addresses */ |
| |
| #include "xiosysincludes.h" |
| |
| #if _WITH_SOCKET |
| |
| #include "xioopen.h" |
| #include "xio-ascii.h" |
| #include "xio-socket.h" |
| #include "xio-named.h" |
| #include "xio-unix.h" |
| #if WITH_VSOCK |
| #include "xio-vsock.h" |
| #endif |
| #if WITH_IP4 |
| #include "xio-ip4.h" |
| #endif /* WITH_IP4 */ |
| #if WITH_IP6 |
| #include "xio-ip6.h" |
| #endif /* WITH_IP6 */ |
| #include "xio-ip.h" |
| #include "xio-listen.h" |
| #include "xio-interface.h" |
| #include "xio-ipapp.h" /*! not clean */ |
| #include "xio-tcpwrap.h" |
| |
| |
| static int xioopen_socket_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc); |
| static int xioopen_socket_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc); |
| static int xioopen_socket_sendto(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc); |
| static int xioopen_socket_datagram(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc); |
| static int xioopen_socket_recvfrom(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc); |
| static int xioopen_socket_recv(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, const struct addrdesc *addrdesc); |
| |
| static |
| int _xioopen_socket_sendto(const char *pfname, const char *type, |
| const char *proto, const char *address, |
| struct opt *opts, int xioflags, xiofile_t *xxfd, |
| groups_t groups); |
| |
| static int |
| xiolog_ancillary_socket(struct single *sfd, struct cmsghdr *cmsg, int *num, |
| char *typbuff, int typlen, |
| char *nambuff, int namlen, |
| char *envbuff, int envlen, |
| char *valbuff, int vallen); |
| static int xiobind( |
| struct single *sfd, |
| union sockaddr_union *us, |
| size_t uslen, |
| struct opt *opts, |
| int pf, |
| bool alt, |
| int level); |
| |
| |
| #if WITH_GENERICSOCKET |
| /* generic socket addresses */ |
| const struct addrdesc xioaddr_socket_connect = { "SOCKET-CONNECT", 1+XIO_RDWR, xioopen_socket_connect, GROUP_FD|GROUP_SOCKET|GROUP_CHILD|GROUP_RETRY, 0, 0, 0 HELP(":<domain>:<protocol>:<remote-address>") }; |
| #if WITH_LISTEN |
| const struct addrdesc xioaddr_socket_listen = { "SOCKET-LISTEN", 1+XIO_RDWR, xioopen_socket_listen, GROUP_FD|GROUP_SOCKET|GROUP_LISTEN|GROUP_RANGE|GROUP_CHILD|GROUP_RETRY, 0, 0, 0 HELP(":<domain>:<protocol>:<local-address>") }; |
| #endif /* WITH_LISTEN */ |
| const struct addrdesc xioaddr_socket_sendto = { "SOCKET-SENDTO", 1+XIO_RDWR, xioopen_socket_sendto, GROUP_FD|GROUP_SOCKET, 0, 0, 0 HELP(":<domain>:<type>:<protocol>:<remote-address>") }; |
| const struct addrdesc xioaddr_socket_datagram= { "SOCKET-DATAGRAM", 1+XIO_RDWR, xioopen_socket_datagram, GROUP_FD|GROUP_SOCKET|GROUP_RANGE, 0, 0, 0 HELP(":<domain>:<type>:<protocol>:<remote-address>") }; |
| const struct addrdesc xioaddr_socket_recvfrom= { "SOCKET-RECVFROM", 1+XIO_RDWR, xioopen_socket_recvfrom, GROUP_FD|GROUP_SOCKET|GROUP_RANGE|GROUP_CHILD, 0, 0, 0 HELP(":<domain>:<type>:<protocol>:<local-address>") }; |
| const struct addrdesc xioaddr_socket_recv = { "SOCKET-RECV", 1+XIO_RDONLY, xioopen_socket_recv, GROUP_FD|GROUP_SOCKET|GROUP_RANGE, 0, 0, 0 HELP(":<domain>:<type>:<protocol>:<local-address>") }; |
| #endif /* WITH_GENERICSOCKET */ |
| |
| |
| /* the following options apply not only to generic socket addresses but to all |
| addresses that have anything to do with sockets */ |
| const struct optdesc opt_so_debug = { "so-debug", "debug", OPT_SO_DEBUG, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_DEBUG }; |
| #ifdef SO_ACCEPTCONN /* AIX433 */ |
| const struct optdesc opt_so_acceptconn={ "so-acceptconn","acceptconn",OPT_SO_ACCEPTCONN,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_ACCEPTCONN}; |
| #endif /* SO_ACCEPTCONN */ |
| const struct optdesc opt_so_broadcast= { "so-broadcast", "broadcast", OPT_SO_BROADCAST,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_BROADCAST}; |
| const struct optdesc opt_so_reuseaddr= { "so-reuseaddr", "reuseaddr", OPT_SO_REUSEADDR,GROUP_SOCKET, PH_PREBIND, TYPE_INT_NULL, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEADDR}; |
| const struct optdesc opt_so_keepalive= { "so-keepalive", "keepalive", OPT_SO_KEEPALIVE,GROUP_SOCKET, PH_FD, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_KEEPALIVE}; |
| #if HAVE_STRUCT_LINGER |
| const struct optdesc opt_so_linger = { "so-linger", "linger", OPT_SO_LINGER, GROUP_SOCKET, PH_PASTSOCKET, TYPE_LINGER,OFUNC_SOCKOPT,SOL_SOCKET, SO_LINGER }; |
| #else /* !HAVE_STRUCT_LINGER */ |
| const struct optdesc opt_so_linger = { "so-linger", "linger", OPT_SO_LINGER, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_LINGER }; |
| #endif /* !HAVE_STRUCT_LINGER */ |
| const struct optdesc opt_so_oobinline= { "so-oobinline", "oobinline", OPT_SO_OOBINLINE,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_OOBINLINE}; |
| const struct optdesc opt_so_sndbuf = { "so-sndbuf", "sndbuf", OPT_SO_SNDBUF, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_SNDBUF}; |
| const struct optdesc opt_so_sndbuf_late={ "so-sndbuf-late","sndbuf-late",OPT_SO_SNDBUF_LATE,GROUP_SOCKET,PH_LATE,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_SNDBUF }; |
| const struct optdesc opt_so_rcvbuf = { "so-rcvbuf", "rcvbuf", OPT_SO_RCVBUF, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_RCVBUF}; |
| const struct optdesc opt_so_rcvbuf_late={"so-rcvbuf-late","rcvbuf-late",OPT_SO_RCVBUF_LATE,GROUP_SOCKET,PH_LATE,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_RCVBUF }; |
| const struct optdesc opt_so_error = { "so-error", "error", OPT_SO_ERROR, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_ERROR}; |
| const struct optdesc opt_so_type = { "so-type", "type", OPT_SO_TYPE, GROUP_SOCKET, PH_SOCKET, TYPE_INT, OFUNC_SPEC, SOL_SOCKET, SO_TYPE }; |
| const struct optdesc opt_so_dontroute= { "so-dontroute", "dontroute", OPT_SO_DONTROUTE,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_DONTROUTE }; |
| #ifdef SO_RCVLOWAT |
| const struct optdesc opt_so_rcvlowat = { "so-rcvlowat", "rcvlowat", OPT_SO_RCVLOWAT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_RCVLOWAT }; |
| #endif |
| #ifdef SO_SNDLOWAT |
| const struct optdesc opt_so_sndlowat = { "so-sndlowat", "sndlowat", OPT_SO_SNDLOWAT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_SNDLOWAT }; |
| #endif |
| /* end of setsockopt options of UNIX98 standard */ |
| |
| #ifdef SO_RCVTIMEO |
| const struct optdesc opt_so_rcvtimeo = { "so-rcvtimeo", "rcvtimeo", OPT_SO_RCVTIMEO, GROUP_SOCKET, PH_PASTSOCKET, TYPE_TIMEVAL, OFUNC_SOCKOPT, SOL_SOCKET, SO_RCVTIMEO }; |
| #endif |
| #ifdef SO_SNDTIMEO |
| const struct optdesc opt_so_sndtimeo = { "so-sndtimeo", "sndtimeo", OPT_SO_SNDTIMEO, GROUP_SOCKET, PH_PASTSOCKET, TYPE_TIMEVAL, OFUNC_SOCKOPT, SOL_SOCKET, SO_SNDTIMEO }; |
| #endif |
| |
| #ifdef SO_AUDIT /* AIX 4.3.3 */ |
| const struct optdesc opt_so_audit = { "so-audit", "audit", OPT_SO_AUDIT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_AUDIT }; |
| #endif /* SO_AUDIT */ |
| #ifdef SO_ATTACH_FILTER |
| const struct optdesc opt_so_attach_filter={"so-attach-filter","attachfilter",OPT_SO_ATTACH_FILTER,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_ATTACH_FILTER}; |
| #endif |
| #ifdef SO_DETACH_FILTER |
| const struct optdesc opt_so_detach_filter={"so-detach-filter","detachfilter",OPT_SO_DETACH_FILTER,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_DETACH_FILTER}; |
| #endif |
| #ifdef SO_BINDTODEVICE /* Linux: man 7 socket */ |
| const struct optdesc opt_so_bindtodevice={"so-bindtodevice","if",OPT_SO_BINDTODEVICE,GROUP_SOCKET,PH_PASTSOCKET,TYPE_NAME,OFUNC_SOCKOPT,SOL_SOCKET,SO_BINDTODEVICE}; |
| #endif |
| #ifdef SO_BSDCOMPAT |
| const struct optdesc opt_so_bsdcompat= { "so-bsdcompat","bsdcompat",OPT_SO_BSDCOMPAT,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_BSDCOMPAT }; |
| #endif |
| #ifdef SO_CKSUMRECV |
| const struct optdesc opt_so_cksumrecv= { "so-cksumrecv","cksumrecv",OPT_SO_CKSUMRECV,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_CKSUMRECV }; |
| #endif /* SO_CKSUMRECV */ |
| #ifdef SO_TIMESTAMP |
| const struct optdesc opt_so_timestamp= { "so-timestamp","timestamp",OPT_SO_TIMESTAMP,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_TIMESTAMP }; |
| #endif |
| #ifdef SO_KERNACCEPT /* AIX 4.3.3 */ |
| const struct optdesc opt_so_kernaccept={ "so-kernaccept","kernaccept",OPT_SO_KERNACCEPT,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_KERNACCEPT}; |
| #endif /* SO_KERNACCEPT */ |
| #ifdef SO_NO_CHECK |
| const struct optdesc opt_so_no_check = { "so-no-check", "nocheck",OPT_SO_NO_CHECK, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_NO_CHECK }; |
| #endif |
| #ifdef SO_NOREUSEADDR /* AIX 4.3.3 */ |
| const struct optdesc opt_so_noreuseaddr={"so-noreuseaddr","noreuseaddr",OPT_SO_NOREUSEADDR,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET, SO_NOREUSEADDR}; |
| #endif /* SO_NOREUSEADDR */ |
| #ifdef SO_PASSCRED |
| const struct optdesc opt_so_passcred = { "so-passcred", "passcred", OPT_SO_PASSCRED, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_PASSCRED}; |
| #endif |
| #ifdef SO_PEERCRED |
| const struct optdesc opt_so_peercred = { "so-peercred", "peercred", OPT_SO_PEERCRED, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT3,OFUNC_SOCKOPT, SOL_SOCKET, SO_PEERCRED}; |
| #endif |
| #ifdef SO_PRIORITY |
| const struct optdesc opt_so_priority = { "so-priority", "priority", OPT_SO_PRIORITY, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_PRIORITY}; |
| #endif |
| #ifdef SO_REUSEPORT /* AIX 4.3.3, BSD, HP-UX, Linux >=3.9 */ |
| const struct optdesc opt_so_reuseport = { "so-reuseport","reuseport",OPT_SO_REUSEPORT,GROUP_SOCKET, PH_PREBIND, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEPORT }; |
| #endif /* defined(SO_REUSEPORT) */ |
| #ifdef SO_SECURITY_AUTHENTICATION |
| const struct optdesc opt_so_security_authentication={"so-security-authentication","securityauthentication",OPT_SO_SECURITY_AUTHENTICATION,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_SECURITY_AUTHENTICATION}; |
| #endif |
| #ifdef SO_SECURITY_ENCRYPTION_NETWORK |
| const struct optdesc opt_so_security_encryption_network= { "so-security-encryption-network","securityencryptionnetwork",OPT_SO_SECURITY_ENCRYPTION_NETWORK,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_SECURITY_ENCRYPTION_NETWORK}; |
| #endif |
| #ifdef SO_SECURITY_ENCRYPTION_TRANSPORT |
| const struct optdesc opt_so_security_encryption_transport= { "so-security-encryption-transport","securityencryptiontransport",OPT_SO_SECURITY_ENCRYPTION_TRANSPORT,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_SECURITY_ENCRYPTION_TRANSPORT}; |
| #endif |
| #ifdef SO_USE_IFBUFS |
| const struct optdesc opt_so_use_ifbufs= { "so-use-ifbufs","useifbufs",OPT_SO_USE_IFBUFS,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_USE_IFBUFS}; |
| #endif /* SO_USE_IFBUFS */ |
| #ifdef SO_USELOOPBACK /* AIX433, Solaris, HP-UX */ |
| const struct optdesc opt_so_useloopback= { "so-useloopback","useloopback",OPT_SO_USELOOPBACK,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT, SOL_SOCKET, SO_USELOOPBACK}; |
| #endif /* SO_USELOOPBACK */ |
| #ifdef SO_DGRAM_ERRIND /* Solaris */ |
| const struct optdesc opt_so_dgram_errind= { "so-dgram-errind","dgramerrind",OPT_SO_DGRAM_ERRIND,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_DGRAM_ERRIND}; |
| #endif /* SO_DGRAM_ERRIND */ |
| #ifdef SO_DONTLINGER /* Solaris */ |
| const struct optdesc opt_so_dontlinger = { "so-dontlinger", "dontlinger", OPT_SO_DONTLINGER, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_SOCKET,SO_DONTLINGER }; |
| #endif |
| /* the SO_PROTOTYPE is OS defined on Solaris, HP-UX; we lend this for a more |
| general purpose */ |
| const struct optdesc opt_so_prototype = { "so-protocol", "protocol", OPT_SO_PROTOTYPE, GROUP_SOCKET,PH_SOCKET, TYPE_INT,OFUNC_SPEC, SOL_SOCKET,SO_PROTOCOL }; |
| #ifdef FIOSETOWN |
| const struct optdesc opt_fiosetown = { "fiosetown", NULL, OPT_FIOSETOWN, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_IOCTL, FIOSETOWN }; |
| #endif |
| #ifdef SIOCSPGRP |
| const struct optdesc opt_siocspgrp = { "siocspgrp", NULL, OPT_SIOCSPGRP, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_IOCTL, SIOCSPGRP }; |
| #endif |
| const struct optdesc opt_bind = { "bind", NULL, OPT_BIND, GROUP_SOCKET, PH_BIND, TYPE_STRING,OFUNC_SPEC }; |
| const struct optdesc opt_connect_timeout = { "connect-timeout", NULL, OPT_CONNECT_TIMEOUT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_TIMEVAL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.connect_timeout) }; |
| const struct optdesc opt_protocol_family = { "protocol-family", "pf", OPT_PROTOCOL_FAMILY, GROUP_SOCKET, PH_PRESOCKET, TYPE_STRING, OFUNC_SPEC }; |
| |
| /* generic setsockopt() options */ |
| const struct optdesc opt_setsockopt = { "setsockopt", "sockopt", OPT_SETSOCKOPT_BIN, GROUP_SOCKET,PH_CONNECTED, TYPE_INT_INT_BIN, OFUNC_SOCKOPT_GENERIC, 0, 0 }; |
| const struct optdesc opt_setsockopt_int = { "setsockopt-int", "sockopt-int", OPT_SETSOCKOPT_INT, GROUP_SOCKET,PH_CONNECTED, TYPE_INT_INT_INT, OFUNC_SOCKOPT_GENERIC, 0, 0 }; |
| const struct optdesc opt_setsockopt_bin = { "setsockopt-bin", "sockopt-bin", OPT_SETSOCKOPT_BIN, GROUP_SOCKET,PH_CONNECTED, TYPE_INT_INT_BIN, OFUNC_SOCKOPT_GENERIC, 0, 0 }; |
| const struct optdesc opt_setsockopt_string = { "setsockopt-string", "sockopt-string", OPT_SETSOCKOPT_STRING, GROUP_SOCKET,PH_CONNECTED, TYPE_INT_INT_STRING, OFUNC_SOCKOPT_GENERIC, 0, 0 }; |
| const struct optdesc opt_setsockopt_listen = { "setsockopt-listen", "sockopt-listen", OPT_SETSOCKOPT_LISTEN, GROUP_SOCKET,PH_PREBIND, TYPE_INT_INT_BIN, OFUNC_SOCKOPT_GENERIC, 0, 0 }; |
| |
| const struct optdesc opt_null_eof = { "null-eof", NULL, OPT_NULL_EOF, GROUP_SOCKET, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.null_eof) }; |
| |
| |
| #if WITH_GENERICSOCKET |
| |
| static int xioopen_socket_connect( |
| int argc, |
| const char *argv[], |
| struct opt *opts, |
| int xioflags, |
| xiofile_t *xxfd, |
| const struct addrdesc *addrdesc) |
| { |
| struct single *sfd = &xxfd->stream; |
| const char *pfname = argv[1]; |
| const char *protname = argv[2]; |
| const char *address = argv[3]; |
| char *garbage; |
| int pf; |
| int proto; |
| int socktype = SOCK_STREAM; |
| int needbind = 0; |
| union sockaddr_union them; socklen_t themlen; size_t themsize; |
| union sockaddr_union us; socklen_t uslen = sizeof(us); |
| int result; |
| |
| if (argc != 4) { |
| xio_syntax(argv[0], 3, argc-1, addrdesc->syntax); |
| return STAT_NORETRY; |
| } |
| |
| pf = strtoul(pfname, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| proto = strtoul(protname, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| retropt_socket_pf(opts, &pf); |
| retropt_int(opts, OPT_SO_TYPE, &socktype); |
| /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/ |
| if (sfd->howtoend == END_UNSPEC) |
| sfd->howtoend = END_SHUTDOWN; |
| |
| if (applyopts_single(sfd, opts, PH_INIT) < 0) |
| return -1; |
| applyopts(sfd, -1, opts, PH_INIT); |
| applyopts(sfd, -1, opts, PH_EARLY); |
| |
| themsize = 0; |
| if ((result = |
| dalan(address, (uint8_t *)&them.soa.sa_data, &themsize, sizeof(them), 'i')) |
| < 0) { |
| Error1("data too long: \"%s\"", address); |
| } else if (result > 0) { |
| Error1("syntax error in \"%s\"", address); |
| } |
| them.soa.sa_family = pf; |
| themlen = themsize + |
| #if HAVE_STRUCT_SOCKADDR_SALEN |
| sizeof(them.soa.sa_len) + |
| #endif |
| sizeof(them.soa.sa_family); |
| |
| sfd->dtype = XIOREAD_STREAM|XIOWRITE_STREAM; |
| |
| socket_init(0, &us); |
| if (retropt_bind(opts, 0 /*pf*/, socktype, proto, (struct sockaddr *)&us, &uslen, 3, |
| sfd->para.socket.ip.ai_flags) |
| != STAT_NOACTION) { |
| needbind = true; |
| us.soa.sa_family = pf; |
| } |
| |
| if ((result = |
| xioopen_connect(sfd, |
| needbind?&us:NULL, uslen, |
| (struct sockaddr *)&them, themlen, |
| opts, pf, socktype, proto, false)) != 0) { |
| return result; |
| } |
| if ((result = _xio_openlate(sfd, opts)) < 0) { |
| return result; |
| } |
| return STAT_OK; |
| } |
| |
| #if WITH_LISTEN |
| static int xioopen_socket_listen( |
| int argc, |
| const char *argv[], |
| struct opt *opts, |
| int xioflags, |
| xiofile_t *xxfd, |
| const struct addrdesc *addrdesc) |
| { |
| struct single *sfd = &xxfd->stream; |
| const char *pfname = argv[1]; |
| const char *protname = argv[2]; |
| const char *usname = argv[3]; |
| char *garbage; |
| int pf; |
| int proto; |
| int socktype = SOCK_STREAM; |
| union sockaddr_union us; socklen_t uslen; size_t ussize; |
| struct opt *opts0; |
| int result; |
| |
| if (argc != 4) { |
| xio_syntax(argv[0], 3, argc-1, addrdesc->syntax); |
| return STAT_NORETRY; |
| } |
| |
| pf = strtoul(pfname, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| proto = strtoul(protname, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| retropt_socket_pf(opts, &pf); |
| retropt_int(opts, OPT_SO_TYPE, &socktype); |
| /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/ |
| if (sfd->howtoend == END_UNSPEC) |
| sfd->howtoend = END_SHUTDOWN; |
| |
| socket_init(0, &us); |
| ussize = 0; |
| if ((result = |
| dalan(usname, (uint8_t *)&us.soa.sa_data, &ussize, sizeof(us), 'i')) |
| < 0) { |
| Error1("data too long: \"%s\"", usname); |
| } else if (result > 0) { |
| Error1("syntax error in \"%s\"", usname); |
| } |
| uslen = ussize + sizeof(us.soa.sa_family) |
| #if HAVE_STRUCT_SOCKADDR_SALEN |
| + sizeof(us.soa.sa_len) |
| #endif |
| ; |
| us.soa.sa_family = pf; |
| |
| if (applyopts_single(sfd, opts, PH_INIT) < 0) |
| return -1; |
| applyopts(sfd, -1, opts, PH_INIT); |
| applyopts(sfd, -1, opts, PH_EARLY); |
| |
| opts0 = copyopts(opts, GROUP_ALL); |
| |
| if ((result = |
| xioopen_listen(sfd, xioflags, |
| &us.soa, uslen, |
| opts, opts0, 0/*instead of pf*/, socktype, proto)) |
| != STAT_OK) |
| return result; |
| return STAT_OK; |
| } |
| #endif /* WITH_LISTEN */ |
| |
| /* we expect the form: ...:domain:type:protocol:remote-address */ |
| static int xioopen_socket_sendto( |
| int argc, |
| const char *argv[], |
| struct opt *opts, |
| int xioflags, |
| xiofile_t *xxfd, |
| const struct addrdesc *addrdesc) |
| { |
| int result; |
| |
| if (argc != 5) { |
| xio_syntax(argv[0], 4, argc-1, addrdesc->syntax); |
| return STAT_NORETRY; |
| } |
| if ((result = |
| _xioopen_socket_sendto(argv[1], argv[2], argv[3], argv[4], |
| opts, xioflags, xxfd, addrdesc->groups)) |
| != STAT_OK) { |
| return result; |
| } |
| _xio_openlate(&xxfd->stream, opts); |
| return STAT_OK; |
| } |
| |
| static |
| int _xioopen_socket_sendto(const char *pfname, const char *type, |
| const char *protname, const char *address, |
| struct opt *opts, int xioflags, xiofile_t *xxfd, |
| groups_t groups) { |
| xiosingle_t *sfd = &xxfd->stream; |
| char *garbage; |
| union sockaddr_union us = {{0}}; |
| socklen_t uslen = 0; size_t ussize; |
| size_t themsize; |
| int pf; |
| int socktype = SOCK_RAW; |
| int proto; |
| bool needbind = false; |
| char *bindstring = NULL; |
| int result; |
| |
| pf = strtoul(pfname, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| socktype = strtoul(type, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| proto = strtoul(protname, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| retropt_socket_pf(opts, &pf); |
| retropt_int(opts, OPT_SO_TYPE, &socktype); |
| /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/ |
| if (sfd->howtoend == END_UNSPEC) |
| sfd->howtoend = END_SHUTDOWN; |
| |
| sfd->peersa.soa.sa_family = pf; |
| themsize = 0; |
| if ((result = |
| dalan(address, (uint8_t *)&sfd->peersa.soa.sa_data, &themsize, |
| sizeof(sfd->peersa), 'i')) |
| < 0) { |
| Error1("data too long: \"%s\"", address); |
| } else if (result > 0) { |
| Error1("syntax error in \"%s\"", address); |
| } |
| sfd->salen = themsize + sizeof(sa_family_t) |
| #if HAVE_STRUCT_SOCKADDR_SALEN |
| + sizeof(sfd->peersa.soa.sa_len) |
| #endif |
| ; |
| #if HAVE_STRUCT_SOCKADDR_SALEN |
| sfd->peersa.soa.sa_len = |
| sizeof(sfd->peersa.soa.sa_len) + sizeof(sfd->peersa.soa.sa_family) + |
| themsize; |
| #endif |
| |
| if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1; |
| applyopts(sfd, -1, opts, PH_INIT); |
| |
| if (pf == PF_UNSPEC) { |
| pf = sfd->peersa.soa.sa_family; |
| } |
| |
| sfd->dtype = XIODATA_RECVFROM; |
| |
| if (retropt_string(opts, OPT_BIND, &bindstring) == 0) { |
| ussize = 0; |
| if ((result = |
| dalan(bindstring, (uint8_t *)&us.soa.sa_data, &ussize, sizeof(us), 'i')) |
| < 0) { |
| Error1("data too long: \"%s\"", bindstring); |
| } else if (result > 0) { |
| Error1("syntax error in \"%s\"", bindstring); |
| } |
| us.soa.sa_family = pf; |
| uslen = ussize + sizeof(sa_family_t) |
| #if HAVE_STRUCT_SOCKADDR_SALEN |
| + sizeof(us.soa.sa_len) |
| #endif |
| ; |
| needbind = true; |
| } |
| |
| return |
| _xioopen_dgram_sendto(needbind?&us:NULL, uslen, |
| opts, xioflags, sfd, groups, pf, socktype, proto, 0); |
| } |
| |
| |
| /* we expect the form: ...:domain:socktype:protocol:local-address */ |
| static |
| int xioopen_socket_recvfrom( |
| int argc, |
| const char *argv[], |
| struct opt *opts, |
| int xioflags, |
| xiofile_t *xxfd, |
| const struct addrdesc *addrdesc) |
| { |
| struct single *sfd = &xxfd->stream; |
| const char *pfname = argv[1]; |
| const char *typename = argv[2]; |
| const char *protname = argv[3]; |
| const char *address = argv[4]; |
| char *garbage; |
| union sockaddr_union *us = &sfd->para.socket.la; |
| socklen_t uslen; size_t ussize; |
| int pf, socktype, proto; |
| char *rangename; |
| int result; |
| |
| if (argc != 5) { |
| xio_syntax(argv[0], 4, argc-1, addrdesc->syntax); |
| return STAT_NORETRY; |
| } |
| |
| pf = strtoul(pfname, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| socktype = strtoul(typename, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| proto = strtoul(protname, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| retropt_socket_pf(opts, &pf); |
| retropt_int(opts, OPT_SO_TYPE, &socktype); |
| /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/ |
| if (sfd->howtoend == END_UNSPEC) |
| sfd->howtoend = END_NONE; |
| |
| ussize = 0; |
| if ((result = |
| dalan(address, (uint8_t *)&us->soa.sa_data, &ussize, sizeof(*us), 'i')) |
| < 0) { |
| Error1("data too long: \"%s\"", address); |
| } else if (result > 0) { |
| Error1("syntax error in \"%s\"", address); |
| } |
| us->soa.sa_family = pf; |
| uslen = ussize + sizeof(us->soa.sa_family) |
| #if HAVE_STRUCT_SOCKADDR_SALEN |
| + sizeof(us->soa.sa_len); |
| #endif |
| ; |
| sfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO; |
| |
| if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) { |
| if (xioparserange(rangename, 0, &sfd->para.socket.range, |
| sfd->para.socket.ip.ai_flags) |
| < 0) { |
| free(rangename); |
| return STAT_NORETRY; |
| } |
| sfd->para.socket.dorange = true; |
| free(rangename); |
| } |
| |
| if ((result = |
| _xioopen_dgram_recvfrom(sfd, xioflags, &us->soa, uslen, |
| opts, pf, socktype, proto, E_ERROR)) |
| != STAT_OK) { |
| return result; |
| } |
| _xio_openlate(sfd, opts); |
| return STAT_OK; |
| } |
| |
| /* we expect the form: ...:domain:type:protocol:local-address */ |
| static |
| int xioopen_socket_recv( |
| int argc, |
| const char *argv[], |
| struct opt *opts, |
| int xioflags, |
| xiofile_t *xxfd, |
| const struct addrdesc *addrdesc) |
| { |
| struct single *sfd = &xxfd->stream; |
| const char *pfname = argv[1]; |
| const char *typename = argv[2]; |
| const char *protname = argv[3]; |
| const char *address = argv[4]; |
| char *garbage; |
| union sockaddr_union us; |
| socklen_t uslen; size_t ussize; |
| int pf, socktype, proto; |
| char *rangename; |
| int result; |
| |
| if (argc != 5) { |
| xio_syntax(argv[0], 4, argc-1, addrdesc->syntax); |
| return STAT_NORETRY; |
| } |
| |
| pf = strtoul(pfname, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| socktype = strtoul(typename, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| proto = strtoul(protname, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| retropt_socket_pf(opts, &pf); |
| retropt_int(opts, OPT_SO_TYPE, &socktype); |
| /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/ |
| if (sfd->howtoend == END_UNSPEC) |
| sfd->howtoend = END_NONE; |
| |
| ussize = 0; |
| if ((result = |
| dalan(address, (uint8_t *)&us.soa.sa_data, &ussize, sizeof(us), 'i')) |
| < 0) { |
| Error1("data too long: \"%s\"", address); |
| } else if (result > 0) { |
| Error1("syntax error in \"%s\"", address); |
| } |
| us.soa.sa_family = pf; |
| uslen = ussize + sizeof(sa_family_t) |
| #if HAVE_STRUCT_SOCKADDR_SALEN |
| +sizeof(us.soa.sa_len) |
| #endif |
| ; |
| sfd->dtype = XIOREAD_RECV; |
| sfd->para.socket.la.soa.sa_family = pf; |
| |
| if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) { |
| if (xioparserange(rangename, 0, &sfd->para.socket.range, |
| sfd->para.socket.ip.ai_flags) |
| < 0) { |
| free(rangename); |
| return STAT_NORETRY; |
| } |
| sfd->para.socket.dorange = true; |
| free(rangename); |
| } |
| |
| if ((result = |
| _xioopen_dgram_recv(sfd, xioflags, &us.soa, |
| uslen, opts, pf, socktype, proto, E_ERROR)) |
| != STAT_OK) { |
| return result; |
| } |
| _xio_openlate(sfd, opts); |
| return STAT_OK; |
| } |
| |
| |
| /* we expect the form: ...:domain:type:protocol:remote-address */ |
| static int xioopen_socket_datagram( |
| int argc, |
| const char *argv[], |
| struct opt *opts, |
| int xioflags, |
| xiofile_t *xxfd, |
| const struct addrdesc *addrdesc) |
| { |
| xiosingle_t *sfd = &xxfd->stream; |
| const char *pfname = argv[1]; |
| const char *typename = argv[2]; |
| const char *protname = argv[3]; |
| const char *address = argv[4]; |
| char *garbage; |
| char *rangename; |
| size_t themsize; |
| int pf; |
| int result; |
| |
| if (argc != 5) { |
| xio_syntax(argv[0], 4, argc-1, addrdesc->syntax); |
| return STAT_NORETRY; |
| } |
| |
| pf = strtoul(pfname, &garbage, 0); |
| if (*garbage != '\0') { |
| Warn1("garbage in parameter: \"%s\"", garbage); |
| } |
| |
| retropt_socket_pf(opts, &pf); |
| /*retropt_int(opts, OPT_IP_PROTOCOL, &proto);*/ |
| if (sfd->howtoend == END_UNSPEC) |
| sfd->howtoend = END_SHUTDOWN; |
| |
| sfd->peersa.soa.sa_family = pf; |
| themsize = 0; |
| if ((result = |
| dalan(address, (uint8_t *)&sfd->peersa.soa.sa_data, &themsize, |
| sizeof(sfd->peersa), 'i')) |
| < 0) { |
| Error1("data too long: \"%s\"", address); |
| } else if (result > 0) { |
| Error1("syntax error in \"%s\"", address); |
| } |
| sfd->salen = themsize + sizeof(sa_family_t); |
| #if HAVE_STRUCT_SOCKADDR_SALEN |
| sfd->peersa.soa.sa_len = |
| sizeof(sfd->peersa.soa.sa_len) + sizeof(sfd->peersa.soa.sa_family) + |
| themsize; |
| #endif |
| |
| if ((result = |
| _xioopen_socket_sendto(pfname, typename, protname, address, |
| opts, xioflags, xxfd, addrdesc->groups)) |
| != STAT_OK) { |
| return result; |
| } |
| |
| sfd->dtype = XIOREAD_RECV|XIOWRITE_SENDTO; |
| |
| sfd->para.socket.la.soa.sa_family = sfd->peersa.soa.sa_family; |
| |
| /* which reply sockets will accept - determine by range option */ |
| if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) { |
| if (xioparserange(rangename, 0, &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); |
| } |
| |
| _xio_openlate(sfd, opts); |
| return STAT_OK; |
| } |
| |
| #endif /* WITH_GENERICSOCKET */ |
| |
| /* EINTR not handled specially */ |
| int xiogetpacketinfo(struct single *sfd, int fd) |
| { |
| #if defined(MSG_ERRQUEUE) |
| int _errno = errno; |
| char peername[256]; |
| union sockaddr_union _peername; |
| /* union sockaddr_union _sockname; */ |
| union sockaddr_union *pa = &_peername; /* peer address */ |
| /* union sockaddr_union *la = &_sockname; */ /* local address */ |
| socklen_t palen = sizeof(_peername); /* peer address size */ |
| char ctrlbuff[1024]; /* ancillary messages */ |
| struct msghdr msgh = {0}; |
| |
| msgh.msg_name = pa; |
| msgh.msg_namelen = palen; |
| #if HAVE_STRUCT_MSGHDR_MSGCONTROL |
| msgh.msg_control = ctrlbuff; |
| #endif |
| #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN |
| msgh.msg_controllen = sizeof(ctrlbuff); |
| #endif |
| if (xiogetancillary(fd, |
| &msgh, |
| MSG_ERRQUEUE |
| #ifdef MSG_TRUNC |
| |MSG_TRUNC |
| #endif |
| ) >= 0 |
| ) { |
| palen = msgh.msg_namelen; |
| |
| Notice1("receiving packet from %s"/*"src"*/, |
| sockaddr_info(&pa->soa, palen, peername, sizeof(peername))/*, |
| sockaddr_info(&la->soa, sockname, sizeof(sockname))*/); |
| |
| xiodopacketinfo(sfd, &msgh, true, true); |
| } |
| errno = _errno; |
| #endif /* defined(MSG_ERRQUEUE) */ |
| return 0; |
| } |
| |
| |
| |
| /* A subroutine that is common to all socket addresses that want to connect() |
| a socket to a peer. |
| Applies and consumes the following options: |
| PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECT, |
| PH_CONNECTED, PH_LATE, |
| OFUNC_OFFSET, |
| OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC |
| Does not fork, does not retry. |
| Alternate (alt) bind semantics are: |
| with IP sockets: lowport (selects randomly a free port from 640 to 1023) |
| with UNIX and abstract sockets: uses tmpname() to find a free file system |
| entry. |
| returns 0 on success. |
| */ |
| int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen, |
| struct sockaddr *them, size_t themlen, |
| struct opt *opts, int pf, int socktype, int protocol, |
| bool alt, int level) { |
| int fcntl_flags = 0; |
| char infobuff[256]; |
| union sockaddr_union la; |
| socklen_t lalen = themlen; |
| int _errno; |
| int result; |
| |
| #if WITH_UNIX |
| if (pf == PF_UNIX && us != NULL) { |
| applyopts_named(us->un.sun_path, opts, PH_EARLY); |
| } |
| #endif |
| |
| if ((sfd->fd = xiosocket(opts, pf, socktype, protocol, level)) < 0) { |
| return STAT_RETRYLATER; |
| } |
| |
| applyopts_offset(sfd, opts); |
| applyopts(sfd, -1, opts, PH_PASTSOCKET); |
| applyopts(sfd, -1, opts, PH_FD); |
| |
| applyopts_cloexec(sfd->fd, opts); |
| |
| if (xiobind(sfd, us, uslen, opts, pf, alt, level) < 0) { |
| return -1; |
| } |
| |
| applyopts(sfd, -1, opts, PH_CONNECT); |
| |
| if (sfd->para.socket.connect_timeout.tv_sec != 0 || |
| sfd->para.socket.connect_timeout.tv_usec != 0) { |
| fcntl_flags = Fcntl(sfd->fd, F_GETFL); |
| Fcntl_l(sfd->fd, F_SETFL, fcntl_flags|O_NONBLOCK); |
| } |
| |
| result = Connect(sfd->fd, them, themlen); |
| _errno = errno; |
| la.soa.sa_family = them->sa_family; lalen = sizeof(la); |
| if (Getsockname(sfd->fd, &la.soa, &lalen) < 0) { |
| Msg4(level-1, "getsockname(%d, %p, {%d}): %s", |
| sfd->fd, &la.soa, lalen, strerror(errno)); |
| } |
| errno = _errno; |
| if (result < 0) { |
| if (errno == EINPROGRESS) { |
| if (sfd->para.socket.connect_timeout.tv_sec != 0 || |
| sfd->para.socket.connect_timeout.tv_usec != 0) { |
| struct timeval timeout; |
| struct pollfd writefd; |
| int err; |
| socklen_t errlen = sizeof(err); |
| int result; |
| |
| Info4("connect(%d, %s, "F_Zd"): %s", |
| sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), |
| themlen, strerror(errno)); |
| timeout = sfd->para.socket.connect_timeout; |
| writefd.fd = sfd->fd; |
| writefd.events = (POLLOUT|POLLERR); |
| result = xiopoll(&writefd, 1, &timeout); |
| if (result < 0) { |
| Msg4(level, "xiopoll({%d,POLLOUT|POLLERR},,{"F_tv_sec"."F_tv_usec"): %s", |
| sfd->fd, timeout.tv_sec, timeout.tv_usec, strerror(errno)); |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } |
| if (result == 0) { |
| Msg2(level, "connecting to %s: %s", |
| sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), |
| strerror(ETIMEDOUT)); |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } |
| if (writefd.revents & POLLERR) { |
| #if 0 |
| unsigned char dummy[1]; |
| Read(sfd->fd, &dummy, 1); /* get error message */ |
| Msg2(level, "connecting to %s: %s", |
| sockaddr_info(them, infobuff, sizeof(infobuff)), |
| strerror(errno)); |
| #else |
| Connect(sfd->fd, them, themlen); /* get error message */ |
| Msg4(level, "connect(%d, %s, "F_Zd"): %s", |
| sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), |
| themlen, strerror(errno)); |
| #endif |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } |
| /* otherwise OK or network error */ |
| result = Getsockopt(sfd->fd, SOL_SOCKET, SO_ERROR, &err, &errlen); |
| if (result != 0) { |
| Msg2(level, "getsockopt(%d, SOL_SOCKET, SO_ERROR, ...): %s", |
| sfd->fd, strerror(err)); |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } |
| Debug2("getsockopt(%d, SOL_SOCKET, SO_ERROR, { %d }) -> 0", |
| sfd->fd, err); |
| if (err != 0) { |
| Msg4(level, "connect(%d, %s, "F_Zd"): %s", |
| sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), |
| themlen, strerror(err)); |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } |
| Fcntl_l(sfd->fd, F_SETFL, fcntl_flags); |
| } else { |
| Warn4("connect(%d, %s, "F_Zd"): %s", |
| sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), |
| themlen, strerror(errno)); |
| } |
| } else if (pf == PF_UNIX) { |
| /* this is for UNIX domain sockets: a connect attempt seems to be |
| the only way to distinguish stream and datagram sockets. |
| And no ancillary message expected |
| */ |
| int _errno = errno; |
| Info4("connect(%d, %s, "F_Zd"): %s", |
| sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), |
| themlen, strerror(errno)); |
| /* caller must handle this condition */ |
| Close(sfd->fd); sfd->fd = -1; |
| errno = _errno; |
| return STAT_RETRYLATER; |
| } else { |
| /* try to find details about error, especially from ICMP */ |
| xiogetpacketinfo(sfd, sfd->fd); |
| |
| /* continue mainstream */ |
| Msg4(level, "connect(%d, %s, "F_Zd"): %s", |
| sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), |
| themlen, strerror(errno)); |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } |
| } else { /* result >= 0 */ |
| Notice1("successfully connected from local address %s", |
| sockaddr_info(&la.soa, themlen, infobuff, sizeof(infobuff))); |
| } |
| |
| applyopts_fchown(sfd->fd, opts); /* OPT_USER, OPT_GROUP */ |
| applyopts(sfd, -1, opts, PH_CONNECTED); |
| #if WITH_UNIX |
| if (pf == PF_UNIX && us != NULL) { |
| applyopts_named(us->un.sun_path, opts, PH_LATE); |
| } |
| #endif |
| applyopts(sfd, -1, opts, PH_LATE); |
| |
| return STAT_OK; |
| } |
| |
| |
| /* a subroutine that is common to all socket addresses that want to connect |
| to a peer address. |
| might fork. |
| applies and consumes the following option: |
| PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECT, |
| PH_CONNECTED, PH_LATE, |
| OFUNC_OFFSET, |
| OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC |
| returns 0 on success. |
| */ |
| int xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen, |
| struct sockaddr *them, size_t themlen, |
| struct opt *opts, int pf, int socktype, int protocol, |
| bool alt) { |
| bool dofork = false; |
| struct opt *opts0; |
| char infobuff[256]; |
| int level; |
| int result; |
| |
| retropt_bool(opts, OPT_FORK, &dofork); |
| |
| opts0 = copyopts(opts, GROUP_ALL); |
| |
| Notice1("opening connection to %s", |
| sockaddr_info(them, themlen, infobuff, sizeof(infobuff))); |
| |
| do { /* loop over retries and forks */ |
| |
| #if WITH_RETRY |
| if (sfd->forever || sfd->retry) { |
| level = E_INFO; |
| } else |
| #endif /* WITH_RETRY */ |
| level = E_ERROR; |
| result = |
| _xioopen_connect(sfd, us, uslen, them, themlen, opts, |
| pf, socktype, protocol, alt, level); |
| switch (result) { |
| case STAT_OK: break; |
| #if WITH_RETRY |
| case STAT_RETRYLATER: |
| if (sfd->forever || sfd->retry) { |
| --sfd->retry; |
| if (result == STAT_RETRYLATER) { |
| Nanosleep(&sfd->intervall, NULL); |
| } |
| dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); |
| continue; |
| } |
| return STAT_NORETRY; |
| #endif /* WITH_RETRY */ |
| default: |
| return result; |
| } |
| |
| if (dofork) { |
| xiosetchilddied(); /* set SIGCHLD handler */ |
| } |
| |
| #if WITH_RETRY |
| if (dofork) { |
| pid_t pid; |
| int level = E_ERROR; |
| if (sfd->forever || sfd->retry) { |
| level = E_WARN; /* most users won't expect a problem here, |
| so Notice is too weak */ |
| } |
| |
| while ((pid = xio_fork(false, level, sfd->shutup)) < 0) { |
| --sfd->retry; |
| if (sfd->forever || sfd->retry) { |
| dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); |
| Nanosleep(&sfd->intervall, NULL); continue; |
| } |
| return STAT_RETRYLATER; |
| } |
| |
| if (pid == 0) { /* child process */ |
| break; |
| } |
| |
| /* parent process */ |
| Close(sfd->fd); |
| /* with and without retry */ |
| Nanosleep(&sfd->intervall, NULL); |
| dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); |
| continue; /* with next socket() bind() connect() */ |
| } else |
| #endif /* WITH_RETRY */ |
| { |
| break; |
| } |
| #if 0 |
| if ((result = _xio_openlate(fd, opts)) < 0) |
| return result; |
| #endif |
| } while (true); |
| |
| return 0; |
| } |
| |
| |
| /* common to xioopen_udp_sendto, ..unix_sendto, ..rawip |
| applies and consumes the following option: |
| PH_PASTSOCKET, PH_FD, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_CONNECTED, PH_LATE |
| OFUNC_OFFSET |
| OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_USER, OPT_GROUP, OPT_CLOEXEC |
| */ |
| int _xioopen_dgram_sendto(/* them is already in xfd->peersa */ |
| union sockaddr_union *us, socklen_t uslen, |
| struct opt *opts, |
| int xioflags, xiosingle_t *sfd, groups_t groups, |
| int pf, int socktype, int ipproto, bool alt) { |
| int level = E_ERROR; |
| union sockaddr_union la; socklen_t lalen = sizeof(la); |
| char infobuff[256]; |
| |
| #if WITH_UNIX |
| if (pf == PF_UNIX && us != NULL) { |
| applyopts_named(us->un.sun_path, opts, PH_EARLY); |
| } |
| #endif |
| |
| if ((sfd->fd = xiosocket(opts, pf, socktype, ipproto, level)) < 0) { |
| return STAT_RETRYLATER; |
| } |
| |
| applyopts_offset(sfd, opts); |
| applyopts_single(sfd, opts, PH_PASTSOCKET); |
| applyopts(sfd, -1, opts, PH_PASTSOCKET); |
| applyopts_single(sfd, opts, PH_FD); |
| applyopts(sfd, -1, opts, PH_FD); |
| |
| applyopts_cloexec(sfd->fd, opts); |
| |
| if (xiobind(sfd, us, uslen, opts, pf, alt, level) < 0) { |
| return -1; |
| } |
| |
| /*applyopts(sfd, -1, opts, PH_CONNECT);*/ |
| |
| if (Getsockname(sfd->fd, &la.soa, &lalen) < 0) { |
| Warn4("getsockname(%d, %p, {%d}): %s", |
| sfd->fd, &la.soa, lalen, strerror(errno)); |
| } |
| |
| applyopts_fchown(sfd->fd, opts); |
| applyopts(sfd, -1, opts, PH_CONNECTED); |
| #if WITH_UNIX |
| if (pf == PF_UNIX && us != NULL) { |
| applyopts_named(us->un.sun_path, opts, PH_LATE); |
| } |
| #endif |
| /*0 applyopts(sfd, -1, opts, PH_LATE); */ |
| |
| /* sfd->dtype = DATA_RECVFROM; *//* no, the caller must set this (ev _SKIPIP) */ |
| Notice1("successfully prepared local socket %s", |
| sockaddr_info(&la.soa, lalen, infobuff, sizeof(infobuff))); |
| |
| return STAT_OK; |
| } |
| |
| |
| /* when the recvfrom address (with option fork) receives a packet it keeps this |
| packet in the IP stacks input queue and forks a sub process. The sub process |
| then reads this packet for processing its data. |
| There is a problem because the parent process would find the same packet |
| again if it calls select()/poll() before the child process has read the |
| packet. |
| To solve this problem we implement the following mechanism: |
| Before forking an unnamed pipe (fifo) is created. The sub process closes the |
| write side when it has read the packet. The parent process waits until the |
| read side of the pipe gives EOF and only then continues to listen. |
| */ |
| |
| /* waits for incoming packet, checks its source address and port. Depending |
| on fork option, it may fork a subprocess. |
| Returns STAT_OK if a packet was accepted; with fork option, this is already in |
| a new subprocess! |
| Other return values indicate a problem; this can happen in the master |
| process or in a subprocess. |
| This function does not retry. If you need retries, handle this is a |
| loop in the calling function. |
| after fork, we set the forever/retry of the child process to 0 |
| applies and consumes the following options: |
| PH_INIT, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY, PH_PREOPEN, PH_FD, |
| PH_CONNECTED, PH_LATE, PH_LATE2 |
| OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, cloexec, OPT_RANGE, tcpwrap |
| EINTR is not handled specially. |
| */ |
| int _xioopen_dgram_recvfrom(struct single *sfd, int xioflags, |
| struct sockaddr *us, socklen_t uslen, |
| struct opt *opts, |
| int pf, int socktype, int proto, int level) { |
| char *rangename; |
| bool dofork = false; |
| pid_t pid; /* mostly int; only used with fork */ |
| char infobuff[256]; |
| char lisname[256]; |
| bool drop = false; /* true if current packet must be dropped */ |
| int result; |
| |
| retropt_bool(opts, OPT_FORK, &dofork); |
| |
| if (dofork) { |
| if (!(xioflags & XIO_MAYFORK)) { |
| Error("option fork not allowed here"); |
| return STAT_NORETRY; |
| } |
| sfd->flags |= XIO_DOESFORK; |
| } |
| |
| if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY; |
| |
| if ((sfd->fd = xiosocket(opts, pf, socktype, proto, level)) < 0) { |
| return STAT_RETRYLATER; |
| } |
| |
| applyopts(sfd, -1, opts, PH_PASTSOCKET); |
| applyopts(sfd, -1, opts, PH_FD); |
| |
| applyopts_cloexec(sfd->fd, opts); |
| |
| if (xiobind(sfd, (union sockaddr_union *)us, uslen, |
| opts, pf, 0, level) < 0) { |
| return -1; |
| } |
| |
| applyopts(sfd, -1, opts, PH_PASTBIND); |
| |
| #if WITH_UNIX |
| if (pf == AF_UNIX && us != NULL) { |
| applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD); |
| applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY); |
| applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN); |
| } |
| #endif /* WITH_UNIX */ |
| |
| #if WITH_IP4 /*|| WITH_IP6*/ |
| switch (proto) { |
| case IPPROTO_UDP: |
| #ifdef IPPROTO_UDPLITE |
| case IPPROTO_UDPLITE: |
| #endif |
| if (pf == PF_INET && ((struct sockaddr_in *)us)->sin_port == 0 || |
| pf == PF_INET6 && ((struct sockaddr_in6 *)us)->sin6_port == 0) { |
| struct sockaddr_storage bound; |
| socklen_t bndlen = sizeof(bound); |
| char sockbuff[256]; |
| Getsockname(sfd->fd, (struct sockaddr *)&bound, &bndlen); |
| sockaddr_info((struct sockaddr *)&bound, sizeof(struct sockaddr_storage), sockbuff, sizeof(sockbuff)); |
| Notice1("_xioopen_dgram_recvfrom(): bound to %s", sockbuff); |
| } |
| } |
| #endif |
| |
| /* for generic sockets, this has already been retrieved */ |
| 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; |
| } |
| |
| #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP |
| xio_retropt_tcpwrap(sfd, opts); |
| #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */ |
| |
| if (xioparms.logopt == 'm') { |
| Info("starting recvfrom loop, switching to syslog"); |
| diag_set('y', xioparms.syslogfac); xioparms.logopt = 'y'; |
| } else { |
| Info("starting recvfrom loop"); |
| } |
| |
| if (dofork) { |
| xiosetchilddied(); |
| } |
| |
| while (true) { /* but we only loop if fork option is set */ |
| char peername[256]; |
| union sockaddr_union _peername; |
| union sockaddr_union _sockname; |
| union sockaddr_union *pa = &_peername; /* peer address */ |
| union sockaddr_union *la = &_sockname; /* local address */ |
| socklen_t palen = sizeof(_peername); /* peer address size */ |
| char ctrlbuff[1024]; /* ancillary messages */ |
| struct msghdr msgh = {0}; |
| int trigger[2]; /* for socketpair that indicates consumption of packet */ |
| int rc; |
| |
| socket_init(pf, pa); |
| |
| if (drop) { |
| char *dummy[2]; |
| |
| Recv(sfd->fd, dummy, sizeof(dummy), 0); |
| drop = true; |
| } |
| |
| Info("Recvfrom: Checking/waiting for next packet"); |
| /* loop until select()/poll() returns valid */ |
| do { |
| struct pollfd readfd; |
| /*? int level = E_ERROR;*/ |
| if (us != NULL) { |
| Notice1("receiving on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname))); |
| } else { |
| Notice1("receiving IP protocol %u", proto); |
| } |
| readfd.fd = sfd->fd; |
| readfd.events = POLLIN; |
| if (xiopoll(&readfd, 1, NULL) > 0) { |
| break; |
| } |
| |
| if (errno == EINTR) { |
| continue; |
| } |
| |
| Msg2(level, "poll({%d,,},,-1): %s", sfd->fd, strerror(errno)); |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } while (true); |
| |
| msgh.msg_name = pa; |
| msgh.msg_namelen = palen; |
| #if HAVE_STRUCT_MSGHDR_MSGCONTROL |
| msgh.msg_control = ctrlbuff; |
| #endif |
| #if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN |
| msgh.msg_controllen = sizeof(ctrlbuff); |
| #endif |
| while ((rc = xiogetancillary(sfd->fd, |
| &msgh, |
| MSG_PEEK |
| #ifdef MSG_TRUNC |
| |MSG_TRUNC |
| #endif |
| )) < 0 && |
| errno == EINTR) ; |
| if (rc < 0) return STAT_RETRYLATER; |
| palen = msgh.msg_namelen; |
| |
| Notice1("receiving packet from %s"/*"src"*/, |
| sockaddr_info(&pa->soa, palen, peername, sizeof(peername))/*, |
| sockaddr_info(&la->soa, sockname, sizeof(sockname))*/); |
| |
| xiodopacketinfo(sfd, &msgh, true, true); |
| |
| if (xiocheckpeer(sfd, pa, la) < 0) { |
| /* drop packet */ |
| char buff[512]; |
| Recv(sfd->fd, buff, sizeof(buff), 0); |
| continue; |
| } |
| Info1("permitting packet from %s", |
| sockaddr_info(&pa->soa, palen, |
| infobuff, sizeof(infobuff))); |
| |
| /* set the env vars describing the local and remote sockets */ |
| /*xiosetsockaddrenv("SOCK", la, lalen, proto);*/ |
| xiosetsockaddrenv("PEER", pa, palen, proto); |
| |
| applyopts(sfd, -1, opts, PH_FD); |
| |
| applyopts(sfd, -1, opts, PH_CONNECTED); |
| |
| sfd->peersa = *(union sockaddr_union *)pa; |
| sfd->salen = palen; |
| |
| if (dofork) { |
| Info("Generating socketpair that triggers parent when packet has been consumed"); |
| if (Socketpair(PF_UNIX, SOCK_STREAM, 0, trigger) < 0) { |
| Error1("socketpair(PF_UNIX, SOCK_STREAM, 0, ...): %s", strerror(errno)); |
| } |
| |
| if ((pid = xio_fork(false, level, sfd->shutup)) < 0) { |
| Close(trigger[0]); |
| Close(trigger[1]); |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } |
| |
| if (pid == 0) { /* child */ |
| Close(trigger[0]); |
| sfd->triggerfd = trigger[1]; |
| Fcntl_l(sfd->triggerfd, F_SETFD, FD_CLOEXEC); |
| |
| #if WITH_RETRY |
| /* !? */ |
| sfd->retry = 0; |
| sfd->forever = 0; |
| level = E_ERROR; |
| #endif /* WITH_RETRY */ |
| |
| #if WITH_UNIX |
| /* with UNIX sockets: only listening parent is allowed to remove |
| the socket file */ |
| sfd->opt_unlink_close = false; |
| #endif /* WITH_UNIX */ |
| |
| break; |
| } |
| |
| /* Parent */ |
| Close(trigger[1]); |
| |
| { |
| char buf[1]; |
| while (Read(trigger[0], buf, 1) < 0 && errno == EINTR) ; |
| } |
| |
| Info("continue listening"); |
| } else { |
| break; |
| } |
| } |
| if ((result = _xio_openlate(sfd, opts)) != 0) |
| return STAT_NORETRY; |
| |
| return STAT_OK; |
| } |
| |
| |
| /* returns STAT_* */ |
| int _xioopen_dgram_recv(struct single *sfd, int xioflags, |
| struct sockaddr *us, socklen_t uslen, |
| struct opt *opts, int pf, int socktype, int proto, |
| int level) { |
| char *rangename; |
| |
| if (applyopts_single(sfd, opts, PH_INIT) < 0) return STAT_NORETRY; |
| |
| if ((sfd->fd = xiosocket(opts, pf, socktype, proto, level)) < 0) { |
| return STAT_RETRYLATER; |
| } |
| |
| applyopts(sfd, -1, opts, PH_PASTSOCKET); |
| applyopts(sfd, -1, opts, PH_FD); |
| |
| applyopts_cloexec(sfd->fd, opts); |
| |
| if (xiobind(sfd, (union sockaddr_union *)us, uslen, opts, pf, 0, level) < 0) { |
| return -1; |
| } |
| |
| #if WITH_UNIX |
| if (pf == AF_UNIX && us != NULL) { |
| applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD); |
| applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY); |
| applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN); |
| } |
| #endif /* WITH_UNIX */ |
| |
| #if WITH_IP4 /*|| WITH_IP6*/ |
| switch (proto) { |
| case IPPROTO_UDP: |
| #ifdef IPPROTO_UDPLITE |
| case IPPROTO_UDPLITE: |
| #endif |
| if (pf == PF_INET && ((struct sockaddr_in *)us)->sin_port == 0 || |
| pf == PF_INET6 && ((struct sockaddr_in6 *)us)->sin6_port == 0) { |
| struct sockaddr_storage bound; |
| socklen_t bndlen = sizeof(bound); |
| char sockbuff[256]; |
| Getsockname(sfd->fd, (struct sockaddr *)&bound, &bndlen); |
| sockaddr_info((struct sockaddr *)&bound, sizeof(struct sockaddr_storage), sockbuff, sizeof(sockbuff)); |
| Notice1("_xioopen_dgram_recv(): bound to %s", sockbuff); |
| } |
| } |
| #endif |
| |
| 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; |
| } |
| |
| #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP |
| xio_retropt_tcpwrap(sfd, opts); |
| #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */ |
| |
| if (xioparms.logopt == 'm') { |
| Info("starting recv loop, switching to syslog"); |
| diag_set('y', xioparms.syslogfac); xioparms.logopt = 'y'; |
| } else { |
| Info("starting recv loop"); |
| } |
| |
| return STAT_OK; |
| } |
| |
| |
| int retropt_socket_pf(struct opt *opts, int *pf) { |
| char *pfname; |
| |
| if (retropt_string(opts, OPT_PROTOCOL_FAMILY, &pfname) >= 0) { |
| if (isdigit((unsigned char)pfname[0])) { |
| *pf = strtoul(pfname, NULL /*!*/, 0); |
| #if WITH_IP4 |
| } else if (!strcasecmp("inet", pfname) || |
| !strcasecmp("inet4", pfname) || |
| !strcasecmp("ip4", pfname) || |
| !strcasecmp("ipv4", pfname) || |
| !strcasecmp("2", pfname)) { |
| *pf = PF_INET; |
| #endif /* WITH_IP4 */ |
| #if WITH_IP6 |
| } else if (!strcasecmp("inet6", pfname) || |
| !strcasecmp("ip6", pfname) || |
| !strcasecmp("ipv6", pfname) || |
| !strcasecmp("10", pfname)) { |
| *pf = PF_INET6; |
| #endif /* WITH_IP6 */ |
| } else { |
| Error1("unknown protocol family \"%s\"", pfname); |
| /*! Warn("falling back to INET");*/ |
| } |
| free(pfname); |
| return 0; |
| } |
| return -1; |
| } |
| |
| |
| /* This function calls recvmsg(..., MSG_PEEK, ...) to obtain information about |
| the arriving packet, thus it does not "consume" the packet. |
| In msgh the msg_name pointer must refer to an (empty) sockaddr storage. |
| Returns STAT_OK on success, or STAT_RETRYLATER when an error occurred, |
| including EINTR. |
| (recvmsg() retrieves just meta info, not the data) |
| */ |
| int xiogetancillary(int fd, struct msghdr *msgh, int flags) { |
| char peekbuff[1]; |
| #if HAVE_STRUCT_IOVEC |
| struct iovec iovec; |
| #endif |
| |
| #if HAVE_STRUCT_IOVEC |
| iovec.iov_base = peekbuff; |
| iovec.iov_len = sizeof(peekbuff); |
| msgh->msg_iov = &iovec; |
| msgh->msg_iovlen = 1; |
| #endif |
| #if HAVE_STRUCT_MSGHDR_MSGFLAGS |
| msgh->msg_flags = 0; |
| #endif |
| if (Recvmsg(fd, msgh, flags) < 0) { |
| Info1("recvmsg(): %s", strerror(errno)); |
| return STAT_RETRYLATER; |
| } |
| return STAT_OK; |
| } |
| |
| |
| /* works through the ancillary messages found in the given socket header record |
| and logs the relevant information (E_DEBUG, E_INFO). |
| calls protocol/layer specific functions for handling the messages |
| creates appropriate environment vars if withenv is set */ |
| int xiodopacketinfo( |
| struct single *sfd, |
| struct msghdr *msgh, |
| bool withlog, |
| bool withenv) |
| { |
| #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) |
| struct cmsghdr *cmsg; |
| |
| /* parse ancillary messages */ |
| cmsg = CMSG_FIRSTHDR(msgh); |
| while (cmsg != NULL) { |
| int num = 0; /* number of data components of a ancill.msg */ |
| int i; |
| char typbuff[16], *typp; |
| char nambuff[128], *namp; |
| char valbuff[256], *valp; |
| char envbuff[256], *envp; |
| |
| Info3("ancillary message in xiodopacketinfo(): len="F_Zu", level=%d, type=%d", |
| cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type); |
| if (withlog) { |
| xiodump(CMSG_DATA(cmsg), |
| cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg), |
| valbuff, sizeof(valbuff)-1, 0); |
| Debug4("ancillary message: len="F_cmsg_len", level=%d, type=%d, data=%s", |
| cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type, |
| valbuff); |
| } |
| |
| /* try to get the anc.msg. contents in handy components, protocol/level |
| dependent */ |
| switch (cmsg->cmsg_level) { |
| case SOL_SOCKET: |
| xiolog_ancillary_socket(sfd, cmsg, &num, typbuff, sizeof(typbuff)-1, |
| nambuff, sizeof(nambuff)-1, |
| envbuff, sizeof(envbuff)-1, |
| valbuff, sizeof(valbuff)-1); |
| break; |
| #if WITH_IP4 || WITH_IP6 |
| case SOL_IP: |
| xiolog_ancillary_ip(sfd, cmsg, &num, typbuff, sizeof(typbuff)-1, |
| nambuff, sizeof(nambuff)-1, |
| envbuff, sizeof(envbuff)-1, |
| valbuff, sizeof(valbuff)-1); |
| break; |
| #endif /* WITH_IP4 || WITH_IP6 */ |
| #if WITH_IP6 |
| case SOL_IPV6: |
| xiolog_ancillary_ip6(sfd, cmsg, &num, typbuff, sizeof(typbuff)-1, |
| nambuff, sizeof(nambuff)-1, |
| envbuff, sizeof(envbuff)-1, |
| valbuff, sizeof(valbuff)-1); |
| break; |
| #endif /* WITH_IP6 */ |
| #if _WITH_INTERFACE && HAVE_STRUCT_CMSGHDR && HAVE_STRUCT_TPACKET_AUXDATA |
| case SOL_PACKET: |
| xiolog_ancillary_packet(sfd, cmsg, &num, typbuff, sizeof(typbuff)-1, |
| nambuff, sizeof(nambuff)-1, |
| envbuff, sizeof(envbuff)-1, |
| valbuff, sizeof(valbuff)-1); |
| break; |
| #endif /* HAVE_STRUCT_CMSGHDR && HAVE_STRUCT_TPACKET_AUXDATA */ |
| default: |
| num = 1; |
| snprintf(typbuff, sizeof(typbuff)-1, "LEVEL%u", cmsg->cmsg_level); |
| snprintf(nambuff, sizeof(nambuff)-1, "type%u", cmsg->cmsg_type); |
| xiodump(CMSG_DATA(cmsg), |
| cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg), |
| valbuff, sizeof(valbuff)-1, 0); |
| } |
| /* here the info is in typbuff (one string), nambuff (num consecutive |
| strings), and valbuff (num consecutive strings) */ |
| i = 0; |
| typp = typbuff; namp = nambuff; envp = envbuff; valp = valbuff; |
| while (i < num) { |
| if (withlog) { |
| Info3("ancillary message: %s: %s=%s", typp, namp, valp); |
| } |
| if (withenv) { |
| if (*envp) { |
| xiosetenv(envp, valp, 1, NULL); |
| } else if (!strcasecmp(typp+strlen(typp)-strlen(namp), namp)) { |
| xiosetenv(typp, valp, 1, NULL); |
| } else { |
| xiosetenv2(typp, namp, valp, 1, NULL); |
| } |
| } |
| if (++i == num) break; |
| namp = strchr(namp, '\0')+1; |
| envp = strchr(envp, '\0')+1; |
| valp = strchr(valp, '\0')+1; |
| } |
| cmsg = CMSG_NXTHDR(msgh, cmsg); |
| } |
| return 0; |
| #else /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */ |
| return -1; |
| #endif /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */ |
| } |
| |
| |
| /* check if peer address is within permitted range. |
| return >= 0 if so. */ |
| int xiocheckrange(union sockaddr_union *sa, struct xiorange *range) { |
| switch (sa->soa.sa_family) { |
| #if WITH_IP4 |
| case PF_INET: |
| return |
| xiocheckrange_ip4(&sa->ip4, range); |
| #endif /* WITH_IP4 */ |
| #if WITH_IP6 |
| case PF_INET6: |
| return |
| xiocheckrange_ip6(&sa->ip6, range); |
| #endif /* WITH_IP6 */ |
| #if 0 |
| case PF_UNSPEC: |
| { |
| socklen_t i; |
| for (i = 0; i < sizeof(sa->soa.sa_data); ++i) { |
| if ((range->netmask.soa.sa_data[i] & sa->soa.sa_data[i]) != range->netaddr.soa.sa_data[i]) { |
| return -1; |
| } |
| } |
| return 0; |
| } |
| #endif |
| } |
| return -1; |
| } |
| |
| int xiocheckpeer(xiosingle_t *sfd, |
| union sockaddr_union *pa, union sockaddr_union *la) { |
| char infobuff[256]; |
| int result; |
| |
| #if WITH_IP4 |
| if (sfd->para.socket.dorange) { |
| if (pa == NULL) { return -1; } |
| if (xiocheckrange(pa, &sfd->para.socket.range) < 0) { |
| char infobuff[256]; |
| Warn1("refusing connection from %s due to range option", |
| sockaddr_info(&pa->soa, 0, |
| infobuff, sizeof(infobuff))); |
| return -1; |
| } |
| Info1("permitting connection from %s due to range option", |
| sockaddr_info(&pa->soa, 0, |
| infobuff, sizeof(infobuff))); |
| } |
| #endif /* WITH_IP4 */ |
| |
| #if WITH_TCP || WITH_UDP |
| if (sfd->para.socket.ip.dosourceport) { |
| if (pa == NULL) { return -1; } |
| #if WITH_IP4 |
| if (pa->soa.sa_family == AF_INET && |
| ntohs(((struct sockaddr_in *)pa)->sin_port) != sfd->para.socket.ip.sourceport) { |
| Warn1("refusing connection from %s due to wrong sourceport", |
| sockaddr_info(&pa->soa, 0, |
| infobuff, sizeof(infobuff))); |
| return -1; |
| } |
| #endif /* WITH_IP4 */ |
| #if WITH_IP6 |
| if (pa->soa.sa_family == AF_INET6 && |
| ntohs(((struct sockaddr_in6 *)pa)->sin6_port) != sfd->para.socket.ip.sourceport) { |
| Warn1("refusing connection from %s due to wrong sourceport", |
| sockaddr_info(&pa->soa, 0, |
| infobuff, sizeof(infobuff))); |
| return -1; |
| } |
| #endif /* WITH_IP6 */ |
| Info1("permitting connection from %s due to sourceport option", |
| sockaddr_info(&pa->soa, 0, |
| infobuff, sizeof(infobuff))); |
| } else if (sfd->para.socket.ip.lowport) { |
| if (pa == NULL) { return -1; } |
| if (pa->soa.sa_family == AF_INET && |
| ntohs(((struct sockaddr_in *)pa)->sin_port) >= IPPORT_RESERVED) { |
| Warn1("refusing connection from %s due to lowport option", |
| sockaddr_info(&pa->soa, 0, |
| infobuff, sizeof(infobuff))); |
| return -1; |
| } |
| #if WITH_IP6 |
| else if (pa->soa.sa_family == AF_INET6 && |
| ntohs(((struct sockaddr_in6 *)pa)->sin6_port) >= |
| IPPORT_RESERVED) { |
| Warn1("refusing connection from %s due to lowport option", |
| sockaddr_info(&pa->soa, 0, |
| infobuff, sizeof(infobuff))); |
| return -1; |
| } |
| #endif /* WITH_IP6 */ |
| Info1("permitting connection from %s due to lowport option", |
| sockaddr_info(&pa->soa, 0, |
| infobuff, sizeof(infobuff))); |
| } |
| #endif /* WITH_TCP || WITH_UDP */ |
| |
| #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP |
| result = xio_tcpwrap_check(sfd, la, pa); |
| if (result < 0) { |
| char infobuff[256]; |
| Warn1("refusing connection from %s due to tcpwrapper option", |
| sockaddr_info(&pa->soa, 0, |
| infobuff, sizeof(infobuff))); |
| return -1; |
| } else if (result > 0) { |
| Info1("permitting connection from %s due to tcpwrapper option", |
| sockaddr_info(&pa->soa, 0, |
| infobuff, sizeof(infobuff))); |
| } |
| #endif /* (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */ |
| |
| return 0; /* permitted */ |
| } |
| |
| |
| #if HAVE_STRUCT_CMSGHDR |
| /* converts the ancillary message in *cmsg into a form useable for further |
| processing. knows the specifics of common message types. |
| returns the number of resulting syntax elements in *num |
| returns a sequence of \0 terminated type strings in *typbuff |
| returns a sequence of \0 terminated name strings in *nambuff |
| returns a sequence of \0 terminated value strings in *valbuff |
| the respective len parameters specify the available space in the buffers |
| returns STAT_OK or other STAT_* |
| */ |
| static int |
| xiolog_ancillary_socket( |
| struct single *sfd, |
| struct cmsghdr *cmsg, |
| int *num, |
| char *typbuff, int typlen, |
| char *nambuff, int namlen, |
| char *envbuff, int envlen, |
| char *valbuff, int vallen) |
| { |
| const char *cmsgtype, *cmsgname, *cmsgenvn; |
| size_t msglen; |
| struct timeval *tv; |
| int rc = STAT_OK; |
| |
| #if defined(CMSG_DATA) |
| |
| msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg); |
| switch (cmsg->cmsg_type) { |
| #ifdef SO_PASSCRED |
| case SO_PASSCRED: /* this is really a UNIX/LOCAL message */ |
| /*! needs implementation */ |
| #endif /* SO_PASSCRED */ |
| #ifdef SO_RIGHTS |
| case SO_RIGHTS: /* this is really a UNIX/LOCAL message */ |
| /*! needs implementation */ |
| #endif |
| default: /* binary data */ |
| snprintf(typbuff, typlen, "SOCKET.%u", cmsg->cmsg_type); |
| nambuff[0] = '\0'; strncat(nambuff, "data", namlen-1); |
| xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); |
| return STAT_OK; |
| #ifdef SO_TIMESTAMP |
| # ifdef SCM_TIMESTAMP |
| case SCM_TIMESTAMP: |
| # else |
| case SO_TIMESTAMP: |
| # endif |
| tv = (struct timeval *)CMSG_DATA(cmsg); |
| cmsgtype = |
| #ifdef SCM_TIMESTAMP |
| "SCM_TIMESTAMP" /* FreeBSD */ |
| #else |
| "SO_TIMESTAMP" /* Linux */ |
| #endif |
| ; |
| cmsgname = "timestamp"; |
| cmsgenvn = "TIMESTAMP"; |
| { time_t t = tv->tv_sec; ctime_r(&t, valbuff); } |
| snprintf(strchr(valbuff, '\0')-1/*del \n*/, vallen-strlen(valbuff)+1, ", %06ld usecs", (long)tv->tv_usec); |
| break; |
| #endif /* defined(SO_TIMESTAMP) */ |
| ; |
| } |
| /* when we come here we provide a single parameter |
| with type in cmsgtype, name in cmsgname, |
| and value already in valbuff */ |
| *num = 1; |
| if (strlen(cmsgtype) >= typlen) rc = STAT_WARNING; |
| typbuff[0] = '\0'; strncat(typbuff, cmsgtype, typlen-1); |
| if (strlen(cmsgname) >= namlen) rc = STAT_WARNING; |
| nambuff[0] = '\0'; strncat(nambuff, cmsgname, namlen-1); |
| if (strlen(cmsgenvn) >= envlen) rc = STAT_WARNING; |
| envbuff[0] = '\0'; strncat(envbuff, cmsgenvn, envlen-1); |
| return rc; |
| |
| #else /* !defined(CMSG_DATA) */ |
| |
| return STAT_NORETRY; |
| |
| #endif /* !defined(CMSG_DATA) */ |
| } |
| #endif /* HAVE_STRUCT_CMSGHDR */ |
| |
| |
| /* return the name of the interface with given index |
| or NULL if is fails |
| The system call requires an arbitrary socket; the calling program may |
| provide one in parameter ins to avoid creation of a dummy socket. ins must |
| be <0 if it does not specify a socket fd. */ |
| char *xiogetifname(int ind, char *val, int ins) { |
| #if !HAVE_PROTOTYPE_LIB_if_indextoname |
| int s; |
| struct ifreq ifr; |
| |
| if (ins >= 0) { |
| s = ins; |
| } else { |
| if ((s = Socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) { |
| Error1("socket(PF_INET, SOCK_DGRAM, IPPROTO_IP): %s", strerror(errno)); |
| return NULL; |
| } |
| } |
| |
| #if HAVE_STRUCT_IFREQ_IFR_INDEX |
| ifr.ifr_index = ind; |
| #elif HAVE_STRUCT_IFREQ_IFR_IFINDEX |
| ifr.ifr_ifindex = ind; |
| #endif |
| #ifdef SIOCGIFNAME |
| if(Ioctl(s, SIOCGIFNAME, &ifr) < 0) { |
| #if HAVE_STRUCT_IFREQ_IFR_INDEX |
| Info3("ioctl(%d, SIOCGIFNAME, {..., ifr_index=%d, ...}: %s", |
| s, ifr.ifr_index, strerror(errno)); |
| #elif HAVE_STRUCT_IFREQ_IFR_IFINDEX |
| Info3("ioctl(%d, SIOCGIFNAME, {..., ifr_ifindex=%d, ...}: %s", |
| s, ifr.ifr_ifindex, strerror(errno)); |
| #endif |
| if (ins < 0) Close(s); |
| return NULL; |
| } |
| #endif /* SIOCGIFNAME */ |
| if (ins < 0) Close(s); |
| strcpy(val, ifr.ifr_name); |
| return val; |
| #else /* HAVE_PROTOTYPE_LIB_if_indextoname */ |
| return if_indextoname(ind, val); |
| #endif /* HAVE_PROTOTYPE_LIB_if_indextoname */ |
| } |
| |
| |
| /* parses a network specification consisting of an address and a mask. */ |
| int xioparsenetwork( |
| const char *rangename, |
| int pf, |
| struct xiorange *range, |
| const int ai_flags[2]) |
| { |
| size_t addrlen = 0, masklen = 0; |
| int result; |
| |
| switch (pf) { |
| #if WITH_IP4 |
| case PF_INET: |
| return xioparsenetwork_ip4(rangename, range, ai_flags); |
| break; |
| #endif /* WITH_IP4 */ |
| #if WITH_IP6 |
| case PF_INET6: |
| return xioparsenetwork_ip6(rangename, range, ai_flags); |
| break; |
| #endif /* WITH_IP6 */ |
| case PF_UNSPEC: |
| { |
| char *addrname; |
| const char *maskname; |
| if ((maskname = strchr(rangename, ':')) == NULL) { |
| Error1("syntax error in range \"%s\" of unspecified address family: use <addr>:<mask>", rangename); |
| return STAT_NORETRY; |
| } |
| ++maskname; /* skip ':' */ |
| if ((addrname = Malloc(maskname-rangename)) == NULL) { |
| return STAT_NORETRY; |
| } |
| strncpy(addrname, rangename, maskname-rangename-1); /* ok */ |
| addrname[maskname-rangename-1] = '\0'; |
| result = |
| dalan(addrname, (uint8_t *)&range->netaddr.soa.sa_data, &addrlen, |
| sizeof(range->netaddr)-(size_t)(&((struct sockaddr *)0)->sa_data) |
| /* data length */, 'i'); |
| if (result < 0) { |
| Error1("data too long: \"%s\"", addrname); |
| free(addrname); return STAT_NORETRY; |
| } else if (result > 0) { |
| Error1("syntax error in \"%s\"", addrname); |
| free(addrname); return STAT_NORETRY; |
| } |
| free(addrname); |
| result = |
| dalan(maskname, (uint8_t *)&range->netmask.soa.sa_data, &masklen, |
| sizeof(range->netaddr)-(size_t)(&((struct sockaddr *)0)->sa_data) |
| /* data length */, 'i'); |
| if (result < 0) { |
| Error1("data too long: \"%s\"", maskname); |
| return STAT_NORETRY; |
| } else if (result > 0) { |
| Error1("syntax error in \"%s\"", maskname); |
| return STAT_NORETRY; |
| } |
| if (addrlen != masklen) { |
| Error2("network address is "F_Zu" bytes long, mask is "F_Zu" bytes long", |
| addrlen, masklen); |
| /* recover by padding the shorter component with 0 */ |
| memset((char *)&range->netaddr.soa.sa_data+addrlen, 0, |
| MAX(0, addrlen-masklen)); |
| memset((char *)&range->netmask.soa.sa_data+masklen, 0, |
| MAX(0, masklen-addrlen)); |
| } |
| } |
| break; |
| default: |
| Error1("range option not supported with address family %d", pf); |
| return STAT_NORETRY; |
| } |
| return STAT_OK; |
| } |
| |
| |
| /* parses a string of form address/bits or address:mask, and fills the fields |
| of the range union. The addr component is masked with mask. */ |
| int xioparserange( |
| const char *rangename, |
| int pf, |
| struct xiorange *range, |
| const int ai_flags[2]) |
| { |
| int i; |
| if (xioparsenetwork(rangename, pf, range, ai_flags) < 0) { |
| Error2("failed to parse or resolve range \"%s\" (pf=%d)", rangename, pf); |
| return -1; |
| } |
| /* we have parsed the address and mask; now we make sure that the stored |
| address has 0 where mask is 0, to simplify comparisions */ |
| switch (pf) { |
| #if WITH_IP4 |
| case PF_INET: |
| range->netaddr.ip4.sin_addr.s_addr &= range->netmask.ip4.sin_addr.s_addr; |
| break; |
| #endif /* WITH_IP4 */ |
| #if WITH_IP6 |
| case PF_INET6: |
| return xiorange_ip6andmask(range); |
| break; |
| #endif /* WITH_IP6 */ |
| case PF_UNSPEC: |
| for (i = 0; i < sizeof(range->netaddr); ++i) { |
| ((char *)&range->netaddr)[i] &= ((char *)&range->netmask)[i]; |
| } |
| break; |
| default: |
| Error1("range option not supported with address family %d", pf); |
| return STAT_NORETRY; |
| } |
| return 0; |
| } |
| |
| |
| /* set environment variables describing (part of) a socket address, e.g. |
| SOCAT_SOCKADDR. lr (local/remote) specifies a string like "SOCK" or "PEER". |
| proto should correspond to the third parameter of socket(2) and is used to |
| determine the presence of port information. */ |
| int xiosetsockaddrenv(const char *lr, |
| union sockaddr_union *sau, socklen_t salen, |
| int proto) { |
| # define XIOSOCKADDRENVLEN 256 |
| char namebuff[XIOSOCKADDRENVLEN]; |
| char valuebuff[XIOSOCKADDRENVLEN]; |
| int idx = 0, result; |
| |
| strcpy(namebuff, lr); |
| switch (sau->soa.sa_family) { |
| #if WITH_UNIX |
| case PF_UNIX: |
| result = |
| xiosetsockaddrenv_unix(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr), |
| valuebuff, XIOSOCKADDRENVLEN, |
| &sau->un, salen, proto); |
| xiosetenv(namebuff, valuebuff, 1, NULL); |
| break; |
| #endif /* WITH_UNIX */ |
| #if WITH_IP4 |
| case PF_INET: |
| do { |
| result = |
| xiosetsockaddrenv_ip4(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr), |
| valuebuff, XIOSOCKADDRENVLEN, |
| &sau->ip4, proto); |
| xiosetenv(namebuff, valuebuff, 1, NULL); |
| namebuff[strlen(lr)] = '\0'; ++idx; |
| } while (result > 0); |
| break; |
| #endif /* WITH_IP4 */ |
| #if WITH_IP6 |
| case PF_INET6: |
| strcpy(namebuff, lr); |
| do { |
| result = |
| xiosetsockaddrenv_ip6(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr), |
| valuebuff, XIOSOCKADDRENVLEN, |
| &sau->ip6, proto); |
| xiosetenv(namebuff, valuebuff, 1, NULL); |
| namebuff[strlen(lr)] = '\0'; ++idx; |
| } while (result > 0); |
| break; |
| #endif /* WITH_IP6 */ |
| #if WITH_VSOCK |
| case PF_VSOCK: |
| strcpy(namebuff, lr); |
| do { |
| result = |
| xiosetsockaddrenv_vsock(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr), |
| valuebuff, XIOSOCKADDRENVLEN, |
| &sau->vm, proto); |
| xiosetenv(namebuff, valuebuff, 1, NULL); |
| namebuff[strlen(lr)] = '\0'; ++idx; |
| } while (result > 0); |
| break; |
| #endif /* WITH_VSOCK */ |
| #if LATER |
| case PF_PACKET: |
| result = xiosetsockaddrenv_packet(lr, (void *)sau, proto); break; |
| #endif |
| default: |
| result = -1; |
| break; |
| } |
| return result; |
| # undef XIOSOCKADDRENVLEN |
| } |
| |
| #endif /* _WITH_SOCKET */ |
| |
| /* these do sockets internally */ |
| |
| /* retrieves options so-type and so-prototype from opts, calls socket, and |
| ev. generates an appropriate error message. |
| returns 0 on success or -1 if an error occurred. */ |
| int |
| xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) { |
| int result; |
| |
| retropt_int(opts, OPT_SO_TYPE, &socktype); |
| retropt_int(opts, OPT_SO_PROTOTYPE, &proto); |
| applyopts(NULL, -1, opts, PH_PRESOCKET); |
| result = Socket(pf, socktype, proto); |
| if (result < 0) { |
| int _errno = errno; |
| Msg4(msglevel, "socket(%d, %d, %d): %s", |
| pf, socktype, proto, strerror(errno)); |
| errno = _errno; |
| return -1; |
| } |
| return result; |
| } |
| |
| /* retrieves options so-type and so-prototype from opts, calls socketpair, and |
| ev. generates an appropriate error message. |
| returns 0 on success or -1 if an error occurred. */ |
| int |
| xiosocketpair(struct opt *opts, int pf, int socktype, int proto, int sv[2]) { |
| int result; |
| |
| retropt_int(opts, OPT_SO_TYPE, &socktype); |
| retropt_int(opts, OPT_SO_PROTOTYPE, &proto); |
| result = Socketpair(pf, socktype, proto, sv); |
| if (result < 0) { |
| Error5("socketpair(%d, %d, %d, %p): %s", |
| pf, socktype, proto, sv, strerror(errno)); |
| return -1; |
| } |
| return result; |
| } |
| |
| /* Binds a socket to a socket address. Handles IP (internet protocol), UNIX |
| domain, Linux abstract UNIX domain. |
| The bind address us may be NULL in which case no bind() happens, except with |
| alt (on option unix-bind-tempname (bind-tempname)). |
| Alternate (atl) bind semantics are: |
| with IP sockets: lowport (selects randomly a free port from 640 to 1023) |
| with UNIX and abstract sockets: uses a method similar to tmpname() to |
| find a free file system entry. |
| */ |
| int xiobind( |
| struct single *sfd, |
| union sockaddr_union *us, |
| size_t uslen, |
| struct opt *opts, |
| int pf, |
| bool alt, |
| int level) |
| { |
| char infobuff[256]; |
| int result; |
| |
| if (false /* for canonical reasons */) { |
| ; |
| #if WITH_UNIX |
| } else if (pf == PF_UNIX) { |
| if (alt && us != NULL) { |
| bool abstract = false; |
| char *usrname = NULL, *sockname; |
| |
| #if WITH_ABSTRACT_UNIXSOCKET |
| abstract = (us->un.sun_path[0] == '\0'); |
| #endif |
| |
| if (uslen == ((char *)&us->un.sun_path-(char *)us)) { |
| usrname = NULL; |
| } else { |
| #if WITH_ABSTRACT_UNIXSOCKET |
| if (abstract) |
| usrname = strndup(us->un.sun_path+1, sizeof(us->un.sun_path)-1); |
| else |
| #endif |
| usrname = strndup(us->un.sun_path, sizeof(us->un.sun_path)); |
| if (usrname == NULL) { |
| int _errno = errno; |
| Error2("strndup(\"%s\", "F_Zu"): out of memory", |
| us->un.sun_path, sizeof(us->un.sun_path)); |
| errno = _errno; |
| return -1; |
| } |
| } |
| |
| do { /* loop over tempnam bind() attempts */ |
| sockname = xio_tempnam(usrname, abstract); |
| if (sockname == NULL) { |
| Error2("tempnam(\"%s\"): %s", usrname, strerror(errno)); |
| free(usrname); |
| return -1; |
| } |
| strncpy(us->un.sun_path+(abstract?1:0), sockname, sizeof(us->un.sun_path)); |
| uslen = sizeof(&((struct sockaddr_un *)0)->sun_path) + |
| Min(strlen(sockname), sizeof(us->un.sun_path)); /*?*/ |
| free(sockname); |
| |
| if (Bind(sfd->fd, (struct sockaddr *)us, uslen) < 0) { |
| Msg4(errno==EADDRINUSE?E_INFO:level, "bind(%d, {%s}, "F_Zd"): %s", |
| sfd->fd, sockaddr_info((struct sockaddr *)us, uslen, |
| infobuff, sizeof(infobuff)), |
| uslen, strerror(errno)); |
| if (errno != EADDRINUSE) { |
| free(usrname); |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } |
| } else { |
| break; /* could bind to path, good, continue past loop */ |
| } |
| } while (true); |
| free(usrname); |
| applyopts_named(us->un.sun_path, opts, PH_PREOPEN); |
| } else |
| |
| if (us != NULL) { |
| if (Bind(sfd->fd, &us->soa, uslen) < 0) { |
| Msg4(level, "bind(%d, {%s}, "F_Zd"): %s", |
| sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), |
| uslen, strerror(errno)); |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } |
| applyopts_named(us->un.sun_path, opts, PH_PREOPEN); |
| } |
| |
| applyopts(sfd, sfd->fd, opts, PH_PREBIND); |
| applyopts(sfd, sfd->fd, opts, PH_BIND); |
| #endif /* WITH_UNIX */ |
| |
| #if WITH_TCP || WITH_UDP |
| } else if (alt) { |
| union sockaddr_union sin, *sinp; |
| unsigned short *port, i, N; |
| div_t dv; |
| |
| applyopts(sfd, sfd->fd, opts, PH_PREBIND); |
| applyopts(sfd, sfd->fd, opts, PH_BIND); |
| /* prepare sockaddr for bind probing */ |
| if (us) { |
| sinp = us; |
| } else { |
| if (pf == AF_INET) { |
| socket_in_init(&sin.ip4); |
| #if WITH_IP6 |
| } else { |
| socket_in6_init(&sin.ip6); |
| #endif |
| } |
| sinp = &sin; |
| } |
| if (pf == AF_INET) { |
| port = &sin.ip4.sin_port; |
| #if WITH_IP6 |
| } else if (pf == AF_INET6) { |
| port = &sin.ip6.sin6_port; |
| #endif |
| } else { |
| port = 0; /* just to make compiler happy */ |
| } |
| /* combine random+step variant to quickly find a free port when only |
| few are in use, and certainly find a free port in defined time even |
| if there are almost all in use */ |
| /* dirt 1: having tcp/udp code in socket function */ |
| /* dirt 2: using a time related system call for init of random */ |
| { |
| /* generate a random port, with millisecond random init */ |
| #if 0 |
| struct timeb tb; |
| ftime(&tb); |
| srandom(tb.time*1000+tb.millitm); |
| #else |
| struct timeval tv; |
| struct timezone tz; |
| tz.tz_minuteswest = 0; |
| tz.tz_dsttime = 0; |
| if ((result = Gettimeofday(&tv, &tz)) < 0) { |
| Warn2("gettimeofday(%p, {0,0}): %s", &tv, strerror(errno)); |
| } |
| srandom(tv.tv_sec*1000000+tv.tv_usec); |
| #endif |
| } |
| /* Note: IPPORT_RESERVED is from includes, 1024 */ |
| dv = div(random(), IPPORT_RESERVED-XIO_IPPORT_LOWER); |
| i = N = XIO_IPPORT_LOWER + dv.rem; |
| do { /* loop over lowport bind() attempts */ |
| *port = htons(i); |
| if (Bind(sfd->fd, &sinp->soa, sizeof(*sinp)) < 0) { |
| Msg4(errno==EADDRINUSE?E_INFO:level, |
| "bind(%d, {%s}, "F_Zd"): %s", sfd->fd, |
| sockaddr_info(&sinp->soa, sizeof(*sinp), infobuff, sizeof(infobuff)), |
| sizeof(*sinp), strerror(errno)); |
| if (errno != EADDRINUSE) { |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } |
| } else { |
| break; /* could bind to port, good, continue past loop */ |
| } |
| --i; if (i < XIO_IPPORT_LOWER) i = IPPORT_RESERVED-1; |
| if (i == N) { |
| Msg(level, "no low port available"); |
| /*errno = EADDRINUSE; still assigned */ |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } |
| } while (i != N); |
| #endif /* WITH_TCP || WITH_UDP */ |
| |
| } else { |
| applyopts(sfd, sfd->fd, opts, PH_PREBIND); |
| if (us) { |
| applyopts(sfd, sfd->fd, opts, PH_BIND); |
| if (Bind(sfd->fd, &us->soa, uslen) < 0) { |
| Msg4(level, "bind(%d, {%s}, "F_Zd"): %s", |
| sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), |
| uslen, strerror(errno)); |
| Close(sfd->fd); |
| return STAT_RETRYLATER; |
| } |
| } |
| } |
| |
| applyopts(sfd, -1, opts, PH_PASTBIND); |
| return 0; |
| } |
| |
| /* Handles the SO_REUSEADDR socket option for TCP LISTEN addresses depending on |
| Socat option so-reuseaddr: |
| Option not applied: set it to 1 |
| Option applied with a value: set it to the value |
| Option applied eith empty value "so-reuseaddr=": do not call setsockopt() for |
| SO_REUSEADDR |
| Return 0 on success, or -1 with errno when an error occurred. |
| */ |
| int xiosock_reuseaddr(int fd, int ipproto, struct opt *opts) |
| { |
| union integral val; |
| union integral notnull; |
| int _errno; |
| |
| val.u_int = 0; |
| notnull.u_bool = false; |
| if (ipproto == IPPROTO_TCP) { |
| val.u_int = 1; |
| notnull.u_bool = true; |
| } |
| retropt_2integrals(opts, OPT_SO_REUSEADDR, &val, ¬null); |
| if (notnull.u_bool) { |
| if (Setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val.u_int, sizeof(int)) |
| != 0) { |
| _errno = errno; |
| Error4("setsockopt(%d, SOL_SOCKET, SO_REUSEADDR, { %d }, "F_Zu"): %s", |
| fd, val.u_int, sizeof(val.u_int), strerror(errno)); |
| errno = _errno; |
| return -1; |
| } |
| } |
| return 0; |
| } |