/* source: xio-ip.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 IP related functions */ #include "xiosysincludes.h" #if _WITH_IP4 || _WITH_IP6 #include "xioopen.h" #include "xio-ascii.h" #include "xio-socket.h" #include "xio-ip.h" #include "xio-ip6.h" #include "nestlex.h" #if WITH_IP4 || WITH_IP6 #ifdef IP_OPTIONS const struct optdesc opt_ip_options = { "ip-options", "ipoptions", OPT_IP_OPTIONS, GROUP_SOCK_IP, PH_PASTSOCKET,TYPE_BIN, OFUNC_SOCKOPT_APPEND, SOL_IP, IP_OPTIONS }; #endif #ifdef IP_PKTINFO const struct optdesc opt_ip_pktinfo = { "ip-pktinfo", "pktinfo", OPT_IP_PKTINFO, GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_PKTINFO }; #endif #ifdef IP_RECVTOS const struct optdesc opt_ip_recvtos = { "ip-recvtos", "recvtos", OPT_IP_RECVTOS, GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_RECVTOS }; #endif #ifdef IP_RECVTTL /* -Cygwin */ const struct optdesc opt_ip_recvttl = { "ip-recvttl", "recvttl", OPT_IP_RECVTTL, GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_RECVTTL }; #endif #ifdef IP_RECVOPTS const struct optdesc opt_ip_recvopts= { "ip-recvopts","recvopts", OPT_IP_RECVOPTS,GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_RECVOPTS }; #endif #ifdef IP_RETOPTS const struct optdesc opt_ip_retopts = { "ip-retopts", "retopts", OPT_IP_RETOPTS, GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_RETOPTS }; #endif const struct optdesc opt_ip_tos = { "ip-tos", "tos", OPT_IP_TOS, GROUP_SOCK_IP, PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_TOS }; const struct optdesc opt_ip_ttl = { "ip-ttl", "ttl", OPT_IP_TTL, GROUP_SOCK_IP, PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_TTL }; #ifdef IP_HDRINCL const struct optdesc opt_ip_hdrincl = { "ip-hdrincl", "hdrincl", OPT_IP_HDRINCL, GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_HDRINCL }; #endif #ifdef IP_RECVERR const struct optdesc opt_ip_recverr = { "ip-recverr", "recverr", OPT_IP_RECVERR, GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_RECVERR }; #endif #ifdef IP_MTU_DISCOVER const struct optdesc opt_ip_mtu_discover={"ip-mtu-discover","mtudiscover",OPT_IP_MTU_DISCOVER,GROUP_SOCK_IP,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_IP,IP_MTU_DISCOVER }; #endif #ifdef IP_MTU const struct optdesc opt_ip_mtu = { "ip-mtu", "mtu", OPT_IP_MTU, GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_MTU }; #endif #ifdef IP_TRANSPARENT const struct optdesc opt_ip_transparent = {"ip-transparent", "transparent", OPT_IP_TRANSPARENT, GROUP_SOCK_IP, PH_PREBIND, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IP, IP_TRANSPARENT}; #endif #ifdef IP_FREEBIND const struct optdesc opt_ip_freebind= { "ip-freebind","freebind", OPT_IP_FREEBIND,GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_FREEBIND }; #endif #ifdef IP_ROUTER_ALERT const struct optdesc opt_ip_router_alert={"ip-router-alert","routeralert",OPT_IP_ROUTER_ALERT,GROUP_SOCK_IP,PH_PASTSOCKET,TYPE_INT,OFUNC_SOCKOPT,SOL_IP,IP_ROUTER_ALERT}; #endif /* following: Linux allows int but OpenBSD reqs char/byte */ const struct optdesc opt_ip_multicast_ttl={"ip-multicast-ttl","multicastttl",OPT_IP_MULTICAST_TTL,GROUP_SOCK_IP,PH_PASTSOCKET,TYPE_BYTE,OFUNC_SOCKOPT,SOL_IP,IP_MULTICAST_TTL}; /* following: Linux allows int but OpenBSD reqs char/byte */ const struct optdesc opt_ip_multicast_loop={"ip-multicast-loop","multicastloop",OPT_IP_MULTICAST_LOOP,GROUP_SOCK_IP,PH_PASTSOCKET,TYPE_BYTE,OFUNC_SOCKOPT,SOL_IP,IP_MULTICAST_LOOP}; const struct optdesc opt_ip_multicast_if ={"ip-multicast-if", "multicast-if", OPT_IP_MULTICAST_IF, GROUP_SOCK_IP,PH_PASTSOCKET,TYPE_IP4NAME,OFUNC_SOCKOPT,SOL_IP,IP_MULTICAST_IF}; #ifdef IP_PKTOPTIONS const struct optdesc opt_ip_pktoptions = { "ip-pktoptions", "pktopts", OPT_IP_PKTOPTIONS, GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_PKTOPTIONS }; #endif #ifdef IP_ADD_MEMBERSHIP const struct optdesc opt_ip_add_membership = { "ip-add-membership", "membership",OPT_IP_ADD_MEMBERSHIP, GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_IP_MREQN, OFUNC_SPEC, SOL_IP, IP_ADD_MEMBERSHIP }; #endif #if defined(HAVE_STRUCT_IP_MREQ_SOURCE) && defined(IP_ADD_SOURCE_MEMBERSHIP) const struct optdesc opt_ip_add_source_membership = { "ip-add-source-membership", "source-membership",OPT_IP_ADD_SOURCE_MEMBERSHIP, GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_IP_MREQ_SOURCE, OFUNC_SPEC, SOL_IP, IP_ADD_SOURCE_MEMBERSHIP }; #endif #ifdef IP_RECVDSTADDR const struct optdesc opt_ip_recvdstaddr = { "ip-recvdstaddr", "recvdstaddr",OPT_IP_RECVDSTADDR, GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_RECVDSTADDR }; #endif #ifdef IP_RECVIF const struct optdesc opt_ip_recvif = { "ip-recvif", "recvdstaddrif",OPT_IP_RECVIF, GROUP_SOCK_IP, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IP, IP_RECVIF }; #endif #ifdef AI_ADDRCONFIG const struct optdesc opt_ai_addrconfig = { "ai-addrconfig", "addrconfig", OPT_AI_ADDRCONFIG, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.ai_flags), XIO_SIZEOF(para.socket.ip.ai_flags), AI_ADDRCONFIG }; #endif #ifdef AI_ALL const struct optdesc opt_ai_all = { "ai-all", NULL, OPT_AI_ALL, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.ai_flags), XIO_SIZEOF(para.socket.ip.ai_flags), AI_ALL }; #endif #ifdef AI_V4MAPPED const struct optdesc opt_ai_v4mapped = { "ai-v4mapped", "v4mapped", OPT_AI_V4MAPPED, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.ai_flags), XIO_SIZEOF(para.socket.ip.ai_flags), AI_V4MAPPED }; #endif #ifdef AI_PASSIVE const struct optdesc opt_ai_passive = { "ai-passive", "passive", OPT_AI_PASSIVE, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.ai_flags), XIO_SIZEOF(para.socket.ip.ai_flags), AI_PASSIVE }; #endif #if WITH_RESOLVE #if WITH_RES_DEPRECATED # define WITH_RES_AAONLY 1 # define WITH_RES_PRIMARY 1 #endif /* WITH_RES_DEPRECATED */ #if HAVE_RESOLV_H const struct optdesc opt_res_debug = { "res-debug", NULL, OPT_RES_DEBUG, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_DEBUG }; #if WITH_RES_AAONLY const struct optdesc opt_res_aaonly = { "res-aaonly", "aaonly", OPT_RES_AAONLY, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_AAONLY }; #endif const struct optdesc opt_res_usevc = { "res-usevc", "usevc", OPT_RES_USEVC, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_USEVC }; #if WITH_RES_PRIMARY const struct optdesc opt_res_primary = { "res-primary", "primary", OPT_RES_PRIMARY, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_PRIMARY }; #endif const struct optdesc opt_res_igntc = { "res-igntc", "igntc", OPT_RES_IGNTC, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_IGNTC }; const struct optdesc opt_res_recurse = { "res-recurse", "recurse", OPT_RES_RECURSE, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_RECURSE }; const struct optdesc opt_res_defnames = { "res-defnames", "defnames", OPT_RES_DEFNAMES, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_DEFNAMES }; const struct optdesc opt_res_stayopen = { "res-stayopen", "stayopen", OPT_RES_STAYOPEN, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_STAYOPEN }; const struct optdesc opt_res_dnsrch = { "res-dnsrch", "dnsrch", OPT_RES_DNSRCH, GROUP_SOCK_IP, PH_OFFSET, TYPE_BOOL, OFUNC_OFFSET_MASKS, XIO_OFFSETOF(para.socket.ip.res.opts), XIO_SIZEOF(para.socket.ip.res.opts), RES_DNSRCH }; #if HAVE_RES_RETRANS const struct optdesc opt_res_retrans = { "res-retrans", "retrans", OPT_RES_RETRANS, GROUP_SOCK_IP, PH_OFFSET, TYPE_INT, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.ip.res.retrans), XIO_SIZEOF(para.socket.ip.res.retrans), RES_MAXRETRANS }; #endif #if HAVE_RES_RETRY const struct optdesc opt_res_retry = { "res-retry", NULL, OPT_RES_RETRY, GROUP_SOCK_IP, PH_OFFSET, TYPE_INT, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.ip.res.retry), XIO_SIZEOF(para.socket.ip.res.retry), RES_MAXRETRY }; #endif #if HAVE_RES_NSADDR_LIST const struct optdesc opt_res_nsaddr = { "res-nsaddr", "dns", OPT_RES_NSADDR, GROUP_SOCK_IP, PH_OFFSET, TYPE_IP4SOCK, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.ip.res.nsaddr), XIO_SIZEOF(para.socket.ip.res.retry), RES_MAXRETRY }; #endif #endif /* HAVE_RESOLV_H */ #endif /* WITH_RESOLVE */ #endif /* WITH_IP4 || WITH_IP6 */ int xioinit_ip( int *pf, char ipv) { if (*pf == PF_UNSPEC) { #if WITH_IP4 && WITH_IP6 switch (ipv) { 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 } return 0; } #if HAVE_RESOLV_H int Res_init(void) { int result; Debug("res_init()"); result = res_init(); Debug1("res_init() -> %d", result); return result; } #endif /* HAVE_RESOLV_H */ #if WITH_DEVTESTS /* Have a couple of hard coded sockaddr records, to be copied and adapted when needed */ static bool devtests_inited = false; static struct sockaddr_in sockaddr_localhost_4 = { #if HAVE_STRUCT_SOCKADDR_SALEN sizeof(struct sockaddr_in), #endif AF_INET, /*htons*/0, { 0 } }; static struct sockaddr_in6 sockaddr_localhost_6 = { #if HAVE_STRUCT_SOCKADDR_SALEN sizeof(struct sockaddr_in6), #endif AF_INET6, /*htons*/0, 0, { { { 0 } } }, 0 }; static struct addrinfo addrinfo_localhost_4 = { 0, AF_INET, 0, 0, sizeof(struct sockaddr_in), (struct sockaddr *)&sockaddr_localhost_4, NULL, NULL } ; static struct addrinfo addrinfo_localhost_6 = { 0, AF_INET6, 0, 0, sizeof(struct sockaddr_in6), (struct sockaddr *)&sockaddr_localhost_6, NULL, NULL } ; static struct addrinfo addrinfo_localhost_4_6[2] = { { 0, AF_INET, 0, 0, sizeof(sockaddr_localhost_4), NULL, /* memdup(sockaddr_localhost_4) */ NULL, NULL /* &addrinfo_localhost_4_6[1] */ }, { 0, AF_INET6, 0, 0, sizeof(sockaddr_localhost_6), NULL, /* memdup(sockaddr_localhost_6) */ NULL, NULL }, } ; static struct addrinfo addrinfo_localhost_6_4[2] = { { 0, AF_INET6, 0, 0, sizeof(sockaddr_localhost_6), NULL, /* memdup(sockaddr_localhost_6) */ NULL, NULL, /* &addrinfo_localhost_6_4[1] */ }, { 0, AF_INET, 0, 0, sizeof(sockaddr_localhost_4), NULL, /* memdup(sockaddr_localhost_4) */ NULL, NULL }, } ; /* We keep track of the copied records because they must not be paaed to freeaddrinfo() */ #define MAX_HARDCODED_RECORDS 16 static struct addrinfo *keep_hardcoded_records[MAX_HARDCODED_RECORDS]; static int count_hardcoded_records; /* returns 0 on success, EAI_NODATA when no matching af, or EAI_NONAME when node did not match the special names */ static int xioip_getaddrinfo_devtests( const char *node, const char *service, int family, int socktype, int protocol, struct addrinfo **res, const int ai_flags[2]) { if (!devtests_inited) { devtests_inited = true; sockaddr_localhost_4.sin_addr.s_addr = htonl((127<<24)+1); /* 127.0.0.1 */ #if WITH_IP6 xioip6_pton("::1", &sockaddr_localhost_6.sin6_addr, 0); #endif } if (node == NULL) { ; #if WITH_IP4 } else if (!strcmp(node, "localhost-4") || !strcmp(node, "localhost-4-6") && family == AF_INET || !strcmp(node, "localhost-6-4") && family == AF_INET #if !WITH_IP6 || !strcmp(node, "localhost-4-6") || !strcmp(node, "localhost-6-4") #endif /* !WITH_IP6 */ ) { if (family == AF_INET6) return EAI_NODATA; *res = memdup(&addrinfo_localhost_4, sizeof(addrinfo_localhost_4)); (*res)->ai_socktype = socktype; (*res)->ai_protocol = protocol; (*res)->ai_addr = memdup(&sockaddr_localhost_4, sizeof(sockaddr_localhost_4)); ((struct sockaddr_in *)((*res)->ai_addr))->sin_port = (service?htons(atoi(service)):0); keep_hardcoded_records[count_hardcoded_records++] = *res; return 0; #endif /* WITH_IP4 */ #if WITH_IP6 } else if (!strcmp(node, "localhost-6") || !strcmp(node, "localhost-4-6") && family == AF_INET6 || !strcmp(node, "localhost-6-4") && family == AF_INET6 #if !WITH_IP4 || !strcmp(node, "localhost-4-6") || !strcmp(node, "localhost-6-4") #endif /* !WITH_IP4 */ ) { if (family == AF_INET) return EAI_NODATA; *res = memdup(&addrinfo_localhost_6, sizeof(addrinfo_localhost_6)); (*res)->ai_socktype = socktype; (*res)->ai_protocol = protocol; (*res)->ai_addr = memdup(&sockaddr_localhost_6, sizeof(sockaddr_localhost_6)); ((struct sockaddr_in6 *)((*res)->ai_addr))->sin6_port = (service?htons(atoi(service)):0); keep_hardcoded_records[count_hardcoded_records++] = *res; return 0; #endif /* !WITH_IP6 */ #if WITH_IP4 && WITH_IP6 } else if (!strcmp(node, "localhost-4-6")) { /* here we come only when both WITH_IP4,WITH_IP6, and family not 4 or 6 */ *res = memdup(&addrinfo_localhost_4_6, sizeof(addrinfo_localhost_4_6)); (*res)[0].ai_socktype = socktype; (*res)[0].ai_protocol = protocol; (*res)[0].ai_addr = memdup(&sockaddr_localhost_4, sizeof(sockaddr_localhost_4)); ((struct sockaddr_in *)((*res)[0].ai_addr))->sin_port = (service?htons(atoi(service)):0); (*res)[0].ai_next = &(*res)[1]; (*res)[1].ai_socktype = socktype; (*res)[1].ai_protocol = protocol; (*res)[1].ai_addr = memdup(&sockaddr_localhost_6, sizeof(sockaddr_localhost_6)); ((struct sockaddr_in6 *)((*res)[1].ai_addr))->sin6_port = (service?htons(atoi(service)):0); keep_hardcoded_records[count_hardcoded_records++] = *res; return 0; #endif /* WITH_IP4 && WITH_IP6 */ #if WITH_IP4 && WITH_IP6 } else if (!strcmp(node, "localhost-6-4")) { /* here we come only when both WITH_IP4,WITH_IP6, and family not 4,6 */ *res = memdup(&addrinfo_localhost_6_4, sizeof(addrinfo_localhost_6_4)); (*res)[0].ai_socktype = socktype; (*res)[0].ai_protocol = protocol; (*res)[0].ai_addr = memdup(&sockaddr_localhost_6, sizeof(sockaddr_localhost_6)); ((struct sockaddr_in6 *)((*res)[0].ai_addr))->sin6_port = (service?htons(atoi(service)):0); (*res)[0].ai_next = &(*res)[1]; (*res)[1].ai_socktype = socktype; (*res)[1].ai_protocol = protocol; (*res)[1].ai_addr = memdup(&sockaddr_localhost_4, sizeof(sockaddr_localhost_4)); ((struct sockaddr_in *)((*res)[1].ai_addr))->sin_port = service?htons(atoi(service)):0; keep_hardcoded_records[count_hardcoded_records++] = *res; return 0; #endif /* WITH_IP4 && WITH_IP6 */ } if (count_hardcoded_records == MAX_HARDCODED_RECORDS) --count_hardcoded_records; /* more records will leak memory */ return EAI_NONAME; } /* Checks if res is a devtests construct, returns 0 if so, or 1 otherwise */ static int xioip_freeaddrinfo_devtests( struct addrinfo *res) { int i; for (i=0; i<16; ++i) { if (res == keep_hardcoded_records[i]) { free(res); keep_hardcoded_records[i] = NULL; return 0; } } return 1; } #endif /* WITH_DEVTESTS */ /* A socat resolver function node: the address to be resolved; supported forms: 1.2.3.4 (IPv4 address) [::2] (IPv6 address) hostname (hostname resolving to IPv4 or IPv6 address) hostname.domain (fq hostname resolving to IPv4 or IPv6 address) service: the port specification; may be numeric or symbolic family: PF_INET, PF_INET6, or PF_UNSPEC permitting both socktype: SOCK_STREAM, SOCK_DGRAM, ... protocol: IPPROTO_UDP, IPPROTO_TCP res: a pointer to an uninitialized ptr var for the resulting socket address returns: STAT_OK, STAT_RETRYLATER, STAT_NORETRY, prints message */ int _xiogetaddrinfo(const char *node, const char *service, int family, int socktype, int protocol, struct addrinfo **res, const int ai_flags[2]) { char *numnode = NULL; size_t nodelen; #if HAVE_GETADDRINFO struct addrinfo hints = {0}; #else /* HAVE_PROTOTYPE_LIB_getipnodebyname || nothing */ struct hostent *host; #endif int error_num; Debug8("_xiogetaddrinfo(node=\"%s\", service=\"%s\", family=%d, socktype=%d, protoco=%d, ai_flags={0x%04x/0x%04x} }, res=%p", node?node:"NULL", service?service:"NULL", family, socktype, protocol, ai_flags?ai_flags[0]:0, ai_flags?ai_flags[1]:0, res); if (service && service[0]=='\0') { Error("_xiogetaddrinfo(): empty port and service"); return EAI_NONAME; } #if LATER #ifdef WITH_VSOCK if (family == AF_VSOCK) { error_num = sockaddr_vm_parse(&sau->vm, node, service); if (error_num < 0) { errno = EINVAL; return EAI_SYSTEM; } return 0; } #endif /* WITH_VSOCK */ #endif /* LATER */ #if WITH_DEVTESTS if (node != NULL && strchr(node, '.') && (!strcmp(strchr(node, '.'), ".dest-unreach.net") || !strcmp(strchr(node, '.'), ".dest-unreach.net."))) { char *hname = strdup(node); Info("dest-unreach.net domain handled specially"); if (hname == NULL) return EAI_MEMORY; if (hname[strlen(hname)-1] == '.') hname[strlen(hname)-1] = '\0'; *strchr(hname, '.') = '\0'; error_num = xioip_getaddrinfo_devtests(hname, service, family, socktype, protocol, res, ai_flags); if (error_num == EAI_NONAME) { Warn("dest-unreach.net domain name does not resolve specially"); /* Pass through to libc resolver */ } else if (error_num != 0) { Error7("getaddrinfo(\"%s\", \"%s\", {0x%02x,%d,%d,%d}, {}): %s", node?node:"NULL", service?service:"NULL", hints.ai_flags, hints.ai_family, hints.ai_socktype, hints.ai_protocol, (error_num == EAI_SYSTEM)? strerror(errno):gai_strerror(error_num)); return error_num; } else { /* ok */ #if WITH_MSGLEVEL <= E_DEBUG struct addrinfo *record; record = *res; while (record) { char buff[256/*!*/]; sockaddr_info(record->ai_addr, record->ai_addrlen, buff, sizeof(buff)); Debug5("getaddrinfo() -> flags=0x%02x family=%d socktype=%d protocol=%d addr=%s", record->ai_flags, record->ai_family, record->ai_socktype, record->ai_protocol, buff); record = record->ai_next; } #endif /* WITH_MSGLEVEL <= E_DEBUG */ return error_num; } } #endif /* WITH_DEVTESTS */ /* the resolver functions might handle numeric forms of node names by reverse lookup, that's not what we want. So we detect these and handle them specially */ if (0) { /* for canonical reasons */ ; #if WITH_IP6 } else if (node && node[0] == '[' && node[(nodelen=strlen(node))-1]==']') { if ((numnode = Malloc(nodelen-1)) == NULL) return EAI_MEMORY; strncpy(numnode, node+1, nodelen-2); /* ok */ numnode[nodelen-2] = '\0'; node = numnode; #if HAVE_GETADDRINFO hints.ai_flags |= AI_NUMERICHOST; #endif /* HAVE_GETADDRINFO */ if (family == PF_UNSPEC) family = PF_INET6; #endif /* WITH_IP6 */ } #if HAVE_GETADDRINFO #ifdef AI_ADDRCONFIG if (family == 0) hints.ai_flags |= AI_ADDRCONFIG; #endif if (node != NULL || service != NULL) { struct addrinfo *record; if (ai_flags != NULL) { hints.ai_flags |= ai_flags[0]; hints.ai_flags &= ~ai_flags[1]; } hints.ai_family = family; hints.ai_socktype = socktype; hints.ai_protocol = protocol; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; do { error_num = Getaddrinfo(node, service, &hints, res); if (error_num == 0) break; if (error_num == EAI_SOCKTYPE && socktype != 0) { /* there are systems where kernel goes SCTP but not getaddrinfo() */ hints.ai_socktype = 0; continue; } if (error_num == EAI_SERVICE && protocol != 0) { if (hints.ai_protocol == 0) { Error7("getaddrinfo(\"%s\", \"%s\", {0x%02x,%d,%d,%d}, {}): %s", node?node:"NULL", service?service:"NULL", hints.ai_flags, hints.ai_family, hints.ai_socktype, hints.ai_protocol, gai_strerror(error_num)); if (*res != NULL) freeaddrinfo(*res); if (numnode) free(numnode); return EAI_SERVICE; } /* Probably unsupported protocol (e.g. UDP-Lite), fallback to 0 */ hints.ai_protocol = 0; continue; } if ((error_num = Getaddrinfo(node, service, &hints, res)) != 0) { Warn7("getaddrinfo(\"%s\", \"%s\", {0x%02x,%d,%d,%d}, {}): %d", node?node:"NULL", service?service:"NULL", hints.ai_flags, hints.ai_family, hints.ai_socktype, hints.ai_protocol, error_num); if (numnode) free(numnode); return error_num; } } while (1); service = NULL; /* do not resolve later again */ #if WITH_MSGLEVEL <= E_DEBUG record = *res; while (record) { char buff[256/*!*/]; sockaddr_info(record->ai_addr, record->ai_addrlen, buff, sizeof(buff)); Debug5("getaddrinfo() -> flags=0x%02x family=%d socktype=%d protocol=%d addr=%s", record->ai_flags, record->ai_family, record->ai_socktype, record->ai_protocol, buff); record = record->ai_next; } #endif /* WITH_MSGLEVEL <= E_DEBUG */ } #elif HAVE_PROTOTYPE_LIB_getipnodebyname /* !HAVE_GETADDRINFO */ if (node != NULL) { /* first fallback is getipnodebyname() */ if (family == 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 family = PF_INET6; #else family = PF_INET; #endif } host = Getipnodebyname(node, family, AI_V4MAPPED, &error_num); if (host == NULL) { const static char ai_host_not_found[] = "Host not found"; const static char ai_no_address[] = "No address"; const static char ai_no_recovery[] = "No recovery"; const static char ai_try_again[] = "Try again"; const char *error_msg = "Unknown error"; switch (error_num) { case HOST_NOT_FOUND: error_msg = ai_host_not_found; break; case NO_ADDRESS: error_msg = ai_no_address; case NO_RECOVERY: error_msg = ai_no_recovery; case TRY_AGAIN: error_msg = ai_try_again; } Error2("getipnodebyname(\"%s\", ...): %s", node, error_msg); } else { switch (family) { #if WITH_IP4 case PF_INET: *socklen = sizeof(sau->ip4); sau->soa.sa_family = PF_INET; memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4); break; #endif #if WITH_IP6 case PF_INET6: *socklen = sizeof(sau->ip6); sau->soa.sa_family = PF_INET6; memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16); break; #endif } } freehostent(host); } #elsif 0 /* !HAVE_PROTOTYPE_LIB_getipnodebyname */ if (node != NULL) { /* this is not a typical IP6 resolver function - but Linux "man gethostbyname" says that the only supported address type with this function is AF_INET _at present_, so maybe this fallback will be useful somewhere sometimes in a future even for IP6 */ if (family == 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 family = PF_INET6; #else family = PF_INET; #endif } /*!!! try gethostbyname2 for IP6 */ if ((host = Gethostbyname(node)) == NULL) { Error2("gethostbyname(\"%s\"): %s", node, h_errno == NETDB_INTERNAL ? strerror(errno) : hstrerror(h_errno)); return STAT_RETRYLATER; } if (host->h_addrtype != family) { Error2("_xiogetaddrinfo(): \"%s\" does not resolve to %s", node, family==PF_INET?"IP4":"IP6"); } else { switch (family) { #if WITH_IP4 case PF_INET: *socklen = sizeof(sau->ip4); sau->soa.sa_family = PF_INET; memcpy(&sau->ip4.sin_addr, host->h_addr_list[0], 4); break; #endif /* WITH_IP4 */ #if WITH_IP6 case PF_INET6: *socklen = sizeof(sau->ip6); sau->soa.sa_family = PF_INET6; memcpy(&sau->ip6.sin6_addr, host->h_addr_list[0], 16); break; #endif /* WITH_IP6 */ } } } #else Error("no resolver function available"); errno = ENOSYS; return EAI_SYSTEM; #endif if (numnode) free(numnode); return 0; } /* Sort the records of an addrinfo list themp (as returned by getaddrinfo), return the sorted list in the array ai_sorted (takes at most n entries including the terminating NULL) Returns 0 on success. */ int _xio_sort_ip_addresses( struct addrinfo *themlist, struct addrinfo **ai_sorted) { struct addrinfo *themp; int i; int ipv[3]; int ipi = 0; /* Make a simple array of IP version preferences */ switch (xioparms.preferred_ip) { case '0': ipv[0] = PF_UNSPEC; ipv[1] = -1; break; case '4': ipv[0] = PF_INET; ipv[1] = PF_INET6; ipv[2] = -1; break; case '6': ipv[0] = PF_INET6; ipv[1] = PF_INET; ipv[2] = -1; break; default: Error("INTERNAL: undefined preferred_ip value"); return -1; } /* Create the sorted list */ ipi = 0; i = 0; while (ipv[ipi] >= 0) { themp = themlist; while (themp != NULL) { if (ipv[ipi] == PF_UNSPEC) { ai_sorted[i] = themp; ++i; } else if (ipv[ipi] == themp->ai_family) { ai_sorted[i] = themp; ++i; } themp = themp->ai_next; } ++ipi; } ai_sorted[i] = NULL; return 0; } /* Wrapper around _xiogetaddrinfo() (which is a wrapper arount getaddrinfo()) that sorts the results according to xioparms.preferred_ip when family is AF_UNSPEC; it returns an array of record pointers instead of a list! */ int xiogetaddrinfo(const char *node, const char *service, int family, int socktype, int protocol, struct addrinfo ***ai_sorted, const int ai_flags[2]) { struct addrinfo *res; struct addrinfo **_ai_sorted; struct addrinfo *aip; int ain; int rc; rc = _xiogetaddrinfo(node, service, family, socktype, protocol, &res, ai_flags); if (rc != 0) return rc; /* Sort results - first, count records for mem allocation */ aip = res; ain = 0; while (aip != NULL) { ++ain; aip = aip->ai_next; } _ai_sorted = Calloc((ain+2), sizeof(struct addrinfo *)); if (_ai_sorted == NULL) return STAT_RETRYLATER; /* Generate a list of addresses sorted by preferred ip version */ _xio_sort_ip_addresses(res, _ai_sorted); _ai_sorted[ain+1] = res; /* save list past NULL for later freeing */ *ai_sorted = _ai_sorted; return 0; } void _xiofreeaddrinfo(struct addrinfo *res) { #if WITH_DEVTESTS if (!xioip_freeaddrinfo_devtests(res)) { return; } #endif #if HAVE_GETADDRINFO freeaddrinfo(res); #else ; #endif } void xiofreeaddrinfo(struct addrinfo **ai_sorted) { int ain; struct addrinfo *res; /* Find the original *res from getaddrinfo past NULL */ ain = 0; while (ai_sorted[ain] != NULL) ++ain; res = ai_sorted[ain+1]; _xiofreeaddrinfo(res); free(ai_sorted); } /* A simple resolver interface that just returns one address, the first found by calling xiogetaddrinfo(), but ev.respects preferred_ip; pf may be AF_INET, AF_INET6, or AF_UNSPEC; on failure logs error message; returns STAT_OK, STAT_RETRYLATER, STAT_NORETRY */ int xioresolve(const char *node, const char *service, int pf, int socktype, int protocol, union sockaddr_union *addr, socklen_t *addrlen, const int ai_flags[2]) { struct addrinfo **res = NULL; struct addrinfo *aip; int rc; rc = xiogetaddrinfo(node, service, pf, socktype, protocol, &res, ai_flags); if (rc == EAI_AGAIN) { Warn3("xioresolve(node=\"%s\", pf=%d, ...): %s", node?node:"NULL", pf, gai_strerror(rc)); return STAT_RETRYLATER; } else if (rc != 0) { Error3("xioresolve(node=\"%s\", pf=%d, ...): %s", node?node:"NULL", pf, (rc == EAI_SYSTEM)?strerror(errno):gai_strerror(rc)); return STAT_NORETRY; } if (res == NULL) { Error3("xioresolve(node=\"%s\", pf=%d, ...): %s", node?node:"NULL", pf, gai_strerror(EAI_NODATA)); xiofreeaddrinfo(res); return STAT_NORETRY; } if ((*res)->ai_addrlen > *addrlen) { Error3("xioresolve(node=\"%s\", addrlen="F_socklen", ...): "F_socklen" bytes required", node, *addrlen, (*res)->ai_addrlen); xiofreeaddrinfo(res); return STAT_NORETRY; } if ((*res)->ai_next != NULL) { Info4("xioresolve(node=\"%s\", service=%s%s%s, ...): More than one address found", node?node:"NULL", service?"\"":"", service?service:"NULL", service?"\"":""); } aip = *res; if (ai_flags != NULL && ai_flags[0] & AI_PASSIVE && pf == PF_UNSPEC) { /* We select the first IPv6 address, if available, because this might accept IPv4 connections too */ while (aip != NULL) { if (aip->ai_family == PF_INET6) break; aip = aip->ai_next; } if (aip == NULL) aip = *res; } else if (pf == PF_UNSPEC && xioparms.preferred_ip != '0') { int prefip = PF_UNSPEC; xioinit_ip(&prefip, xioparms.preferred_ip); while (aip != NULL) { if (aip->ai_family == prefip) break; aip = aip->ai_next; } if (aip == NULL) aip = *res; } memcpy(addr, aip->ai_addr, aip->ai_addrlen); *addrlen = aip->ai_addrlen; xiofreeaddrinfo(res); return STAT_OK; } #if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) /* Converts the ancillary message in *cmsg into a form useable for further processing. knows the specifics of common message types. These are valid for IPv4 and IPv6 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 on success Returns STAT_WARNING if a buffer was too short and data truncated. */ int xiolog_ancillary_ip( struct single *sfd, struct cmsghdr *cmsg, int *num, char *typbuff, int typlen, char *nambuff, int namlen, char *envbuff, int envlen, char *valbuff, int vallen) { int cmsgctr = 0; const char *cmsgtype, *cmsgname = NULL, *cmsgenvn = NULL; size_t msglen; char scratch1[16]; /* can hold an IPv4 address in ASCII */ #if WITH_IP4 && defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO char scratch2[16]; char scratch3[16]; #endif int rc = 0; msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg); envbuff[0] = '\0'; switch (cmsg->cmsg_type) { default: *num = 1; typbuff[0] = '\0'; strncat(typbuff, "IP", typlen-1); snprintf(nambuff, namlen, "type_%u", cmsg->cmsg_type); xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); return STAT_OK; #if WITH_IP4 #if defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO case IP_PKTINFO: { struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); *num = 3; typbuff[0] = '\0'; strncat(typbuff, "IP_PKTINFO", typlen-1); snprintf(nambuff, namlen, "%s%c%s%c%s", "if", '\0', "locaddr", '\0', "dstaddr"); snprintf(envbuff, envlen, "%s%c%s%c%s", "IP_IF", '\0', "IP_LOCADDR", '\0', "IP_DSTADDR"); snprintf(valbuff, vallen, "%s%c%s%c%s", xiogetifname(pktinfo->ipi_ifindex, scratch1, -1), '\0', #if HAVE_PKTINFO_IPI_SPEC_DST inet4addr_info(ntohl(pktinfo->ipi_spec_dst.s_addr), scratch2, sizeof(scratch2)), #else "", #endif '\0', inet4addr_info(ntohl(pktinfo->ipi_addr.s_addr), scratch3, sizeof(scratch3))); Notice3("Ancillary message: interface \"%s\", locaddr=%s, dstaddr=%s", xiogetifname(pktinfo->ipi_ifindex, scratch1, -1), #if HAVE_PKTINFO_IPI_SPEC_DST inet4addr_info(ntohl(pktinfo->ipi_spec_dst.s_addr), scratch2, sizeof(scratch2)), #else "", #endif inet4addr_info(ntohl(pktinfo->ipi_addr.s_addr), scratch3, sizeof(scratch3))); } return STAT_OK; #endif /* defined(IP_PKTINFO) && HAVE_STRUCT_IN_PKTINFO */ #endif /* WITH_IP4 */ #if defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR case IP_RECVERR: { struct xio_extended_err { struct sock_extended_err see; __u32 data0; __u32 data1; __u32 data2; __u32 data3; } ; struct xio_extended_err *err = (struct xio_extended_err *)CMSG_DATA(cmsg); *num = 6; typbuff[0] = '\0'; strncat(typbuff, "IP_RECVERR", typlen-1); snprintf(nambuff, namlen, "%s%c%s%c%s%c%s%c%s%c%s", "errno", '\0', "origin", '\0', "type", '\0', "code", '\0', "info", '\0', "data"); snprintf(envbuff, envlen, "%s%c%s%c%s%c%s%c%s%c%s", "IP_RECVERR_ERRNO", '\0', "IP_RECVERR_ORIGIN", '\0', "IP_RECVERR_TYPE", '\0', "IP_RECVERR_CODE", '\0', "IP_RECVERR_INFO", '\0', "IP_RECVERR_DATA"); snprintf(valbuff, vallen, "%u%c%u%c%u%c%u%c%u%c%u", err->see.ee_errno, '\0', err->see.ee_origin, '\0', err->see.ee_type, '\0', err->see.ee_code, '\0', err->see.ee_info, '\0', err->see.ee_data); /* semantic part */ switch (err->see.ee_origin) { char addrbuff[40]; #if WITH_IP4 case SO_EE_ORIGIN_ICMP: if (1) { inet4addr_info(ntohl(err->data1), addrbuff, sizeof(addrbuff)); Notice6("received ICMP from %s, type %d, code %d, info %d, data %d, resulting in errno %d", addrbuff, err->see.ee_type, err->see.ee_code, err->see.ee_info, err->see.ee_data, err->see.ee_errno); } break; #endif /* WITH_IP4 */ #if WITH_IP6 case SO_EE_ORIGIN_ICMP6: if (1) { Notice5("received ICMP type %d, code %d, info %d, data %d, resulting in errno %d", err->see.ee_type, err->see.ee_code, err->see.ee_info, err->see.ee_data, err->see.ee_errno); } break; #endif /* WITH_IP6 */ default: Notice6("received error message origin %d, type %d, code %d, info %d, data %d, generating errno %d", err->see.ee_origin, err->see.ee_type, err->see.ee_code, err->see.ee_info, err->see.ee_data, err->see.ee_errno); break; } return STAT_OK; } #endif /* defined(IP_RECVERR) && HAVE_STRUCT_SOCK_EXTENDED_ERR */ #ifdef IP_RECVIF case IP_RECVIF: { /* spec in FreeBSD: /usr/include/net/if_dl.h */ struct sockaddr_dl *sadl = (struct sockaddr_dl *)CMSG_DATA(cmsg); *num = 1; typbuff[0] = '\0'; strncat(typbuff, "IP_RECVIF", typlen-1); nambuff[0] = '\0'; strncat(nambuff, "if", namlen-1); envbuff[0] = '\0'; strncat(envbuff, "IP_IF", envlen-1); valbuff[0] = '\0'; strncat(valbuff, xiosubstr(scratch1, sadl->sdl_data, 0, sadl->sdl_nlen), vallen-1); Notice1("IP_RECVIF: %s", valbuff); return STAT_OK; } #endif /* defined(IP_RECVIF) */ #if WITH_IP4 #ifdef IP_RECVDSTADDR case IP_RECVDSTADDR: *num = 1; typbuff[0] = '\0'; strncat(typbuff, "IP_RECVDSTADDR", typlen-1); nambuff[0] = '\0'; strncat(nambuff, "dstaddr", namlen-1); envbuff[0] = '\0'; strncat(envbuff, "IP_DSTADDR", envlen-1); inet4addr_info(ntohl(*(uint32_t *)CMSG_DATA(cmsg)), valbuff, vallen); Notice1("IP_RECVDSTADDR: %s", valbuff); return STAT_OK; #endif #endif /* WITH_IP4 */ case IP_OPTIONS: #ifdef IP_RECVOPTS case IP_RECVOPTS: #endif cmsgtype = "IP_OPTIONS"; cmsgname = "options"; cmsgctr = -1; /*!!!*/ break; #if XIO_ANCILLARY_TYPE_SOLARIS case IP_RECVTOS: #else case IP_TOS: #endif cmsgtype = "IP_TOS"; cmsgname = "tos"; cmsgctr = msglen; break; case IP_TTL: /* Linux */ #ifdef IP_RECVTTL case IP_RECVTTL: /* FreeBSD */ #endif cmsgtype = "IP_TTL"; cmsgname = "ttl"; cmsgctr = msglen; break; } /* when we come here we provide a single parameter with name in cmsgname, value length in msglen */ *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 (cmsgenvn) { if (strlen(cmsgenvn) >= envlen) rc = STAT_WARNING; envbuff[0] = '\0'; strncat(envbuff, cmsgenvn, envlen-1); } else { envbuff[0] = '\0'; } switch (cmsgctr) { case sizeof(char): snprintf(valbuff, vallen, "%u", *(unsigned char *)CMSG_DATA(cmsg)); Notice2("Ancillary message: %s=%u", cmsgname, *(unsigned char *)CMSG_DATA(cmsg)); break; case sizeof(int): snprintf(valbuff, vallen, "%u", (*(unsigned int *)CMSG_DATA(cmsg))); Notice2("Ancillary message: %s=%u", cmsgname, *(unsigned int *)CMSG_DATA(cmsg)); break; case 0: xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); break; default: break; } return rc; } #endif /* defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) */ #if defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN) int xiotype_ip_add_membership( char *tokp, const struct optname *ent, struct opt *opt) { /* we do not resolve the addresses here because we do not yet know if we are coping with a IPv4 or IPv6 socat address */ const char *ends[] = { ":", NULL }; const char *nests[] = { "[","]", NULL }; char buff[512], *buffp=buff; size_t bufspc = sizeof(buff)-1; int parsres; /* parse first IP address, expect ':' */ /*! result= */ parsres = nestlex((const char **)&tokp, &buffp, &bufspc, ends, NULL, NULL, nests, true, false, false); if (parsres < 0) { Error1("option too long: \"%s\"", tokp); return -1; } else if (parsres > 0) { Error1("syntax error in \"%s\"", tokp); return -1; } if (*tokp != ':') { Error1("syntax in option %s: missing ':'", tokp); } *buffp++ = '\0'; if ((opt->value.u_string/*multiaddr*/ = strdup(buff)) == NULL) { Error1("strdup(\"%s\"): out of memory", buff); return -1; } ++tokp; /* parse second IP address, expect ':' or '\0'' */ buffp = buff; /*! result= */ parsres = nestlex((const char **)&tokp, &buffp, &bufspc, ends, NULL, NULL, nests, true, false, false); if (parsres < 0) { Error1("option too long: \"%s\"", tokp); return -1; } else if (parsres > 0) { Error1("syntax error in \"%s\"", tokp); return -1; } *buffp++ = '\0'; if ((opt->value2.u_string/*param2*/ = strdup(buff)) == NULL) { Error1("strdup(\"%s\"): out of memory", buff); free(opt->value.u_string); return -1; } #if HAVE_STRUCT_IP_MREQN if (*tokp++ == ':') { strncpy(opt->value3.u_string/*ifindex*/, tokp, IF_NAMESIZE); /* ok */ Info4("setting option \"%s\" to {\"%s\",\"%s\",\"%s\"}", ent->desc->defname, opt->value.u_string/*multiaddr*/, opt->value2.u_string/*param2*/, opt->value3.u_string/*ifindex*/); } else { /*0 opt->value3.u_string = NULL; / * is NULL from init */ Info3("setting option \"%s\" to {\"%s\",\"%s\"}", ent->desc->defname, opt->value.u_string/*multiaddr*/, opt->value2.u_string/*param2*/); } #else /* !HAVE_STRUCT_IP_MREQN */ Info3("setting option \"%s\" to {\"%s\",\"%s\"}", ent->desc->defname, opt->value.u_string/*multiaddr*/, opt->value2.u_string/*param2*/); #endif /* !HAVE_STRUCT_IP_MREQN */ return 0; } #endif /* defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN) */ #if defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN) int xioapply_ip_add_membership( struct single *sfd, struct opt *opt) { union { #if HAVE_STRUCT_IP_MREQN struct ip_mreqn mreqn; #endif struct ip_mreq mreq; } ip4_mreqn = {{{0}}}; /* IPv6 not supported - seems to have different handling */ /* mc:addr:ifname|ifind mc:ifname|ifind mc:addr */ union sockaddr_union sockaddr1; socklen_t socklen1 = sizeof(sockaddr1.ip4); union sockaddr_union sockaddr2; socklen_t socklen2 = sizeof(sockaddr2.ip4); /* First parameter is always multicast address */ /*! result */ xioresolve(opt->value.u_string/*multiaddr*/, NULL, sfd->para.socket.la.soa.sa_family, SOCK_DGRAM, IPPROTO_IP, &sockaddr1, &socklen1, sfd->para.socket.ip.ai_flags); ip4_mreqn.mreq.imr_multiaddr = sockaddr1.ip4.sin_addr; if (0) { ; /* for canonical reasons */ #if HAVE_STRUCT_IP_MREQN } else if (opt->value3.u_string/*ifindex*/ != NULL) { /* three parameters */ /* second parameter is interface address */ xioresolve(opt->value2.u_string/*param2*/, NULL, sfd->para.socket.la.soa.sa_family, SOCK_DGRAM, IPPROTO_IP, &sockaddr2, &socklen2, sfd->para.socket.ip.ai_flags); ip4_mreqn.mreq.imr_interface = sockaddr2.ip4.sin_addr; /* third parameter is interface */ if (ifindex(opt->value3.u_string/*ifindex*/, (unsigned int *)&ip4_mreqn.mreqn.imr_ifindex, -1) < 0) { Error1("cannot resolve interface \"%s\"", opt->value3.u_string/*ifindex*/); } #endif /* HAVE_STRUCT_IP_MREQN */ } else { /* two parameters */ if (0) { ; /* for canonical reasons */ #if HAVE_STRUCT_IP_MREQN /* there is a form with two parameters that uses mreqn */ } else if (ifindex(opt->value2.u_string/*param2*/, (unsigned int *)&ip4_mreqn.mreqn.imr_ifindex, -1) >= 0) { /* yes, second param converts to interface */ ip4_mreqn.mreq.imr_interface.s_addr = htonl(0); #endif /* HAVE_STRUCT_IP_MREQN */ } else { /*! result */ xioresolve(opt->value2.u_string/*param2*/, NULL, sfd->para.socket.la.soa.sa_family, SOCK_DGRAM, IPPROTO_IP, &sockaddr2, &socklen2, sfd->para.socket.ip.ai_flags); ip4_mreqn.mreq.imr_interface = sockaddr2.ip4.sin_addr; } } #if LATER if (0) { ; /* for canonical reasons */ } else if (sfd->para.socket.la.soa.sa_family == PF_INET) { } else if (sfd->para.socket.la.soa.sa_family == PF_INET6) { ip6_mreqn.mreq.imr_multiaddr = sockaddr1.ip6.sin6_addr; ip6_mreqn.mreq.imr_interface = sockaddr2.ip6.sin6_addr; } #endif #if HAVE_STRUCT_IP_MREQN if (Setsockopt(sfd->fd, opt->desc->major, opt->desc->minor, &ip4_mreqn.mreqn, sizeof(ip4_mreqn.mreqn)) < 0) { Error8("setsockopt(%d, %d, %d, {0x%08x,0x%08x,%d}, "F_Zu"): %s", sfd->fd, opt->desc->major, opt->desc->minor, ip4_mreqn.mreqn.imr_multiaddr.s_addr, ip4_mreqn.mreqn.imr_address.s_addr, ip4_mreqn.mreqn.imr_ifindex, sizeof(ip4_mreqn.mreqn), strerror(errno)); opt->desc = ODESC_ERROR; return -1; } #else if (Setsockopt(sfd->fd, opt->desc->major, opt->desc->minor, &ip4_mreqn.mreq, sizeof(ip4_mreqn.mreq)) < 0) { Error7("setsockopt(%d, %d, %d, {0x%08x,0x%08x}, "F_Zu"): %s", sfd->fd, opt->desc->major, opt->desc->minor, ip4_mreqn.mreq.imr_multiaddr, ip4_mreqn.mreq.imr_interface, sizeof(ip4_mreqn.mreq), strerror(errno)); opt->desc = ODESC_ERROR; return -1; } #endif return 0; } #endif /* defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN) */ #if HAVE_STRUCT_IP_MREQ_SOURCE int xiotype_ip_add_source_membership(char *token, const struct optname *ent, struct opt *opt) { /* we do not resolve the addresses here because we do not yet know if we are coping with an IPv4 or IPv6 socat address */ const char *ends[] = { ":", NULL }; const char *nests[] = { "[","]", NULL }; char buff[512], *buffp=buff; size_t bufspc = sizeof(buff)-1; char *tokp = token; int parsres; /* parse first IP address, expect ':' */ parsres = nestlex((const char **)&tokp, &buffp, &bufspc, ends, NULL, NULL, nests, true, false, false); if (parsres < 0) { Error1("option too long: \"%s\"", token); return -1; } else if (parsres > 0) { Error1("syntax error in \"%s\"", token); return -1; } if (*tokp != ':') { Error1("syntax in option %s: missing ':'", token); } *buffp++ = '\0'; if ((opt->value.u_string/*mcaddr*/ = strdup(buff)) == NULL) { Error1("strdup(\"%s\"): out of memory", buff); return -1; } ++tokp; /* parse second IP address, expect ':' or '\0'' */ buffp = buff; /*! result= */ parsres = nestlex((const char **)&tokp, &buffp, &bufspc, ends, NULL, NULL, nests, true, false, false); if (parsres < 0) { Error1("option too long: \"%s\"", token); return -1; } else if (parsres > 0) { Error1("syntax error in \"%s\"", token); return -1; } if (*tokp != ':') { Error1("syntax in option %s: missing ':'", token); } *buffp++ = '\0'; if ((opt->value2.u_string/*ifaddr*/ = strdup(buff)) == NULL) { Error1("strdup(\"%s\"): out of memory", buff); free(opt->value.u_string); return -1; } ++tokp; /* parse third IP address, expect ':' or '\0'' */ buffp = buff; /*! result= */ parsres = nestlex((const char **)&tokp, &buffp, &bufspc, ends, NULL, NULL, nests, true, false, false); if (parsres < 0) { Error1("option too long: \"%s\"", token); return -1; } else if (parsres > 0) { Error1("syntax error in \"%s\"", token); return -1; } if (*tokp) { Error1("syntax in option %s: trailing cruft", token); } *buffp++ = '\0'; if ((opt->value3.u_string/*srcaddr*/ = strdup(buff)) == NULL) { Error1("strdup(\"%s\"): out of memory", buff); free(opt->value.u_string); free(opt->value2.u_string); return -1; } Info4("setting option \"%s\" to {0x%08x,0x%08x,0x%08x}", ent->desc->defname, ntohl(*(unsigned int *)opt->value.u_string/*mcaddr*/), ntohl(*(unsigned int *)opt->value2.u_string/*ifaddr*/), ntohl(*(unsigned int *)opt->value3.u_string/*srcaddr*/)); return 0; } int xioapply_ip_add_source_membership(struct single *sfd, struct opt *opt) { struct ip_mreq_source ip4_mreq_src = {{0}}; /* IPv6 not supported - seems to have different handling */ union sockaddr_union sockaddr1; socklen_t socklen1 = sizeof(sockaddr1.ip4); union sockaddr_union sockaddr2; socklen_t socklen2 = sizeof(sockaddr2.ip4); union sockaddr_union sockaddr3; socklen_t socklen3 = sizeof(sockaddr3.ip4); int rc; /* first parameter is always multicast address */ rc = xioresolve(opt->value.u_string/*mcaddr*/, NULL, sfd->para.socket.la.soa.sa_family, SOCK_DGRAM, IPPROTO_IP, &sockaddr1, &socklen1, sfd->para.socket.ip.ai_flags); if (rc < 0) { return -1; } ip4_mreq_src.imr_multiaddr = sockaddr1.ip4.sin_addr; /* second parameter is interface address */ rc = xioresolve(opt->value.u_string/*ifaddr*/, NULL, sfd->para.socket.la.soa.sa_family, SOCK_DGRAM, IPPROTO_IP, &sockaddr2, &socklen2, sfd->para.socket.ip.ai_flags); if (rc < 0) { return -1; } ip4_mreq_src.imr_interface = sockaddr2.ip4.sin_addr; /* third parameter is source address */ rc = xioresolve(opt->value.u_string/*srcaddr*/, NULL, sfd->para.socket.la.soa.sa_family, SOCK_DGRAM, IPPROTO_IP, &sockaddr3, &socklen3, sfd->para.socket.ip.ai_flags); if (rc < 0) { return -1; } ip4_mreq_src.imr_sourceaddr = sockaddr3.ip4.sin_addr; if (Setsockopt(sfd->fd, opt->desc->major, opt->desc->minor, &ip4_mreq_src, sizeof(ip4_mreq_src)) < 0) { Error8("setsockopt(%d, %d, %d, {0x%08x,0x%08x,0x%08x}, "F_Zu"): %s", sfd->fd, opt->desc->major, opt->desc->minor, htonl((uint32_t)ip4_mreq_src.imr_multiaddr.s_addr), ip4_mreq_src.imr_interface.s_addr, ip4_mreq_src.imr_sourceaddr.s_addr, sizeof(struct ip_mreq_source), strerror(errno)); opt->desc = ODESC_ERROR; return -1; } return 0; } #endif /* HAVE_STRUCT_IP_MREQ_SOURCE */ #if WITH_RESOLVE #if HAVE_RESOLV_H /* When there are options for resolver then this function saves the current resolver settings to save_res and applies the options to resolver libs state in _res. Returns 1 when there were options (state needs to be restored later, see xio_res_restore()); Returns 0 when there were no options; Returns -1 on error. */ int xio_res_init( struct single *sfd, struct __res_state *save_res) { if (sfd->para.socket.ip.res.opts[0] || sfd->para.socket.ip.res.opts[1] || #if HAVE_RES_RETRANS sfd->para.socket.ip.res.retrans >= 0 || #endif #if HAVE_RES_RETRY sfd->para.socket.ip.res.retry >= 0 || #endif #if HAVE_RES_NSADDR_LIST sfd->para.socket.ip.res.nsaddr.sin_family != PF_UNSPEC || #endif 0 /* for canonical reasons */ ) { if (!(_res.options & RES_INIT)) { if (Res_init() < 0) { Error("res_init() failed"); return -1; } } *save_res = _res; _res.options |= sfd->para.socket.ip.res.opts[0]; _res.options &= ~sfd->para.socket.ip.res.opts[1]; Debug2("changed _res.options from 0x%lx to 0x%lx", save_res->options, _res.options); #if HAVE_RES_RETRANS if (sfd->para.socket.ip.res.retrans >= 0) { _res.retrans = sfd->para.socket.ip.res.retrans; Debug2("changed _res.retrans from 0x%x to 0x%x", save_res->retrans, _res.retrans); } #endif #if HAVE_RES_RETRY if (sfd->para.socket.ip.res.retry >= 0) { _res.retry = sfd->para.socket.ip.res.retry; Debug2("changed _res.retry from 0x%x to 0x%x", save_res->retry, _res.retry); } #endif #if HAVE_RES_NSADDR_LIST if (sfd->para.socket.ip.res.nsaddr.sin_family == PF_INET) { _res.nscount = 1; _res.nsaddr_list[0] = sfd->para.socket.ip.res.nsaddr; if (_res.nsaddr_list[0].sin_port == htons(0)) _res.nsaddr_list[0].sin_port = htons(53); Debug10("changed _res.nsaddr_list[0] from %u.%u.%u.%u:%u to %u.%u.%u.%u:%u", ((unsigned char *)&save_res->nsaddr_list[0].sin_addr.s_addr)[0], ((unsigned char *)&save_res->nsaddr_list[0].sin_addr.s_addr)[1], ((unsigned char *)&save_res->nsaddr_list[0].sin_addr.s_addr)[2], ((unsigned char *)&save_res->nsaddr_list[0].sin_addr.s_addr)[3], ntohs(save_res->nsaddr_list[0].sin_port), ((unsigned char *)&_res.nsaddr_list[0].sin_addr.s_addr)[0], ((unsigned char *)&_res.nsaddr_list[0].sin_addr.s_addr)[1], ((unsigned char *)&_res.nsaddr_list[0].sin_addr.s_addr)[2], ((unsigned char *)&_res.nsaddr_list[0].sin_addr.s_addr)[3], ntohs(_res.nsaddr_list[0].sin_port)); } #endif /* HAVE_RES_NSADDR_LIST */ return 1; } return 0; } int xio_res_restore( struct __res_state *save_res) { _res = *save_res; return 0; } #endif /* HAVE_RESOLV_H */ #endif /* WITH_RESOLVE */ #endif /* _WITH_IP4 || _WITH_IP6 */