diff --git a/CHANGES b/CHANGES index 022e719..989d162 100644 --- a/CHANGES +++ b/CHANGES @@ -155,6 +155,9 @@ New features: Test: ACCEPTTIMEOUT Proposed by Roland + New option ip-add-source-membership + Feature inspired by Brian (b f31415) + ####################### V 1.7.3.4: Corrections: diff --git a/config.h.in b/config.h.in index 3cb88e0..c523df1 100644 --- a/config.h.in +++ b/config.h.in @@ -379,6 +379,9 @@ /* Define if you have struct ipv6_mreq */ #undef HAVE_STRUCT_IPV6_MREQ +/* Define if you have struct ip_mreq_source */ +#undef HAVE_STRUCT_IP_MREQ_SOURCE + /* Define if you have struct ifreq */ #undef HAVE_STRUCT_IFREQ diff --git a/configure.ac b/configure.ac index cda8903..461e586 100644 --- a/configure.ac +++ b/configure.ac @@ -1176,6 +1176,19 @@ if test $sc_cv_struct_ipv6_mreq = yes; then fi AC_MSG_RESULT($sc_cv_struct_ipv6_mreq) +# struct ip_mreq_source (for multicasting options) +AC_MSG_CHECKING(for struct ip_mreq_source) +AC_CACHE_VAL(sc_cv_struct_ip_mreq_source, +[AC_TRY_COMPILE([#include +#include +#include ],[struct ip_mreq_source s;], +[sc_cv_struct_ip_mreq_source=yes], +[sc_cv_struct_ip_mreq_source=no])]) +if test $sc_cv_struct_ip_mreq_source = yes; then + AC_DEFINE(HAVE_STRUCT_IP_MREQ_SOURCE) +fi +AC_MSG_RESULT($sc_cv_struct_ip_mreqn) + # struct ifreq (for network interfaces) AC_MSG_CHECKING(for struct ifreq) diff --git a/doc/socat.yo b/doc/socat.yo index 0a388f1..e3168f0 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -394,6 +394,7 @@ label(ADDRESS_IP_DATAGRAM)dit(bf(tt(IP-DATAGRAM:
:))) link(ip-multicast-ttl)(OPTION_IP_MULTICAST_TTL), link(ip-multicast-if)(OPTION_IP_MULTICAST_IF), link(ip-add-membership)(OPTION_IP_ADD_MEMBERSHIP), + link(ip-add-source-membership)(OPTION_IP_ADD_SOURCE_MEMBERSHIP), link(ttl)(OPTION_TTL), link(tos)(OPTION_TOS), link(pf)(OPTION_PROTOCOL_FAMILY)nl() @@ -1101,6 +1102,7 @@ label(ADDRESS_UDP_DATAGRAM)dit(bf(tt(UDP-DATAGRAM:
:))) link(ip-multicast-ttl)(OPTION_IP_MULTICAST_TTL), link(ip-multicast-if)(OPTION_IP_MULTICAST_IF), link(ip-add-membership)(OPTION_IP_ADD_MEMBERSHIP), + link(ip-add-source-membership)(OPTION_IP_ADD_SOURCE_MEMBERSHIP), link(ttl)(OPTION_TTL), link(tos)(OPTION_TOS), link(sourceport)(OPTION_SOURCEPORT), @@ -2108,6 +2110,11 @@ dit(bf(tt(ip-add-membership=))) + Makes the socket member of the specified multicast group for the specified + source, i.e. only multicast traffic from this address is to be delivered. + This is currently only implemented for IPv4.nl() label(OPTION_IP_MULTICAST_IF) dit(bf(tt(ip-multicast-if=))) Specifies hostname or address of the network interface to be used for diff --git a/xio-ip.c b/xio-ip.c index afa3c94..ee572e1 100644 --- a/xio-ip.c +++ b/xio-ip.c @@ -14,6 +14,7 @@ #include "xio-socket.h" #include "xio-ip.h" #include "xio-ip6.h" +#include "nestlex.h" #if WITH_IP4 || WITH_IP6 @@ -70,6 +71,9 @@ const struct optdesc opt_ip_pktoptions = { "ip-pktoptions", "pktopts", OPT_IP_PK #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_SOCKOPT, SOL_IP, IP_ADD_MEMBERSHIP }; #endif +#ifdef 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_SOCKOPT, 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 @@ -581,4 +585,128 @@ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num, } #endif /* defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) */ + +#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'; + opt->value.u_ip_mreq_source.mcaddr = strdup(buff); /*!!! NULL */ + + ++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'; + opt->value.u_ip_mreq_source.ifaddr = strdup(buff); /*!!! NULL */ + + ++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'; + opt->value.u_ip_mreq_source.srcaddr = strdup(buff); /*!!! NULL */ + + Info4("setting option \"%s\" to {0x%08x,0x%08x,0x08x}", + ent->desc->defname, + opt->value.u_ip_mreq_source.mcaddr, + opt->value.u_ip_mreq_source.ifaddr, + opt->value.u_ip_mreq_source.srcaddr); + return 0; +} + +int xioapply_ip_add_source_membership(struct single *xfd, 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); + + /* first parameter is always multicast address */ + /*! result */ + xiogetaddrinfo(opt->value.u_ip_mreq_source.mcaddr, NULL, + xfd->para.socket.la.soa.sa_family, + SOCK_DGRAM, IPPROTO_IP, + &sockaddr1, &socklen1, 0, 0); + ip4_mreq_src.imr_multiaddr = sockaddr1.ip4.sin_addr; + /* second parameter is interface address */ + xiogetaddrinfo(opt->value.u_ip_mreq_source.ifaddr, NULL, + xfd->para.socket.la.soa.sa_family, + SOCK_DGRAM, IPPROTO_IP, + &sockaddr2, &socklen2, 0, 0); + ip4_mreq_src.imr_interface = sockaddr2.ip4.sin_addr; + /* third parameter is source address */ + xiogetaddrinfo(opt->value.u_ip_mreq_source.srcaddr, NULL, + xfd->para.socket.la.soa.sa_family, + SOCK_DGRAM, IPPROTO_IP, + &sockaddr3, &socklen3, 0, 0); + ip4_mreq_src.imr_sourceaddr = sockaddr3.ip4.sin_addr; + if (Setsockopt(xfd->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", + xfd->fd, opt->desc->major, opt->desc->minor, + ip4_mreq_src.imr_multiaddr, + ip4_mreq_src.imr_interface, + ip4_mreq_src.imr_sourceaddr, + sizeof(struct ip_mreq_source), + strerror(errno)); + opt->desc = ODESC_ERROR; + return -1; + } + return 0; +} +#endif /* HAVE_STRUCT_IP_MREQ_SOURCE */ + #endif /* _WITH_IP4 || _WITH_IP6 */ diff --git a/xio-ip.h b/xio-ip.h index 801bd95..8b3c922 100644 --- a/xio-ip.h +++ b/xio-ip.h @@ -24,6 +24,7 @@ extern const struct optdesc opt_ip_multicast_loop; extern const struct optdesc opt_ip_multicast_if; extern const struct optdesc opt_ip_pktoptions; extern const struct optdesc opt_ip_add_membership; +extern const struct optdesc opt_ip_add_source_membership; extern const struct optdesc opt_ip_recvdstaddr; extern const struct optdesc opt_ip_recvif; extern const struct optdesc opt_ip_transparent; @@ -48,5 +49,7 @@ int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num, char *nambuff, int namlen, char *envbuff, int envlen, char *valbuff, int vallen); +extern int xiotype_ip_add_source_membership(char* token, const struct optname *ent, struct opt *opt); +extern int xioapply_ip_add_source_membership(struct single *xfd, struct opt *opt); #endif /* !defined(__xio_ip_h_included) */ diff --git a/xio.h b/xio.h index beb3188..58487e1 100644 --- a/xio.h +++ b/xio.h @@ -354,6 +354,13 @@ union integral { #endif } u_ip_mreq; #endif +#if HAVE_STRUCT_IP_MREQ_SOURCE + struct { + char *mcaddr; + char *ifaddr; /* address, interface */ + char *srcaddr; /* source address */ + } u_ip_mreq_source; +#endif #if WITH_IP4 struct in_addr u_ip4addr; #endif diff --git a/xiohelp.c b/xiohelp.c index 1977f4c..f4431ff 100644 --- a/xiohelp.c +++ b/xiohelp.c @@ -21,6 +21,7 @@ static const char *optiontypenames[] = { "DOUBLE", "STRING-NULL", "LONG-LONG", "OFF_T", "OFF64_T", "INT:INT", "INT:INTP", "INT:BIN", "INT:STRING", "INT:INT:INT", "INT:INT:BIN", "INT:INT:STRING", + "INT:INT:GENERIC", "IP4NAME", #if HAVE_STRUCT_LINGER @@ -31,6 +32,10 @@ static const char *optiontypenames[] = { #elif HAVE_STRUCT_IP_MREQ "STRUCT-IP_MREQ", #endif +#if HAVE_STRUCT_IP_MREQ_SOURCE + "IP-MREQ-SOURCE", +#endif + "GENERIC", } ; diff --git a/xioopts.c b/xioopts.c index 08dd7c6..7a104b6 100644 --- a/xioopts.c +++ b/xioopts.c @@ -7,6 +7,7 @@ #include "xiosysincludes.h" #include "xioopen.h" #include "xio-unix.h" +#include "xio-ip.h" #include "xiomodes.h" #include "xiolockfile.h" @@ -156,6 +157,9 @@ const struct optname optionnames[] = { #endif /* SO_ACCEPTCONN */ #ifdef IP_ADD_MEMBERSHIP IF_IP ("add-membership", &opt_ip_add_membership) +#endif +#ifdef IP_ADD_SOURCE_MEMBERSHIP + IF_IP ("add-source-membership", &opt_ip_add_source_membership) #endif IF_TUN ("allmulti", &opt_iff_allmulti) #if WITH_LIBWRAP && defined(HAVE_HOSTS_ALLOW_TABLE) @@ -668,6 +672,9 @@ const struct optname optionnames[] = { #ifdef IP_ADD_MEMBERSHIP IF_IP ("ip-add-membership", &opt_ip_add_membership) #endif +#ifdef IP_ADD_SOURCE_MEMBERSHIP + IF_IP ("ip-add-source-membership", &opt_ip_add_source_membership) +#endif #ifdef IP_FREEBIND IF_IP ("ip-freebind", &opt_ip_freebind) #endif @@ -1547,6 +1554,9 @@ const struct optname optionnames[] = { IF_SOCKS4 ("socksport", &opt_socksport) IF_SOCKS4 ("socksuser", &opt_socksuser) IF_SOCKET ("socktype", &opt_so_type) +#ifdef IP_ADD_SOURCE_MEMBERSHIP + IF_IP ("source-membership", &opt_ip_add_source_membership) +#endif IF_IPAPP ("sourceport", &opt_sourceport) IF_IPAPP ("sp", &opt_sourceport) IF_TERMIOS("start", &opt_vstart) @@ -2488,6 +2498,10 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts, break; #endif /* defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN) */ + case TYPE_IP_MREQ_SOURCE: + xiotype_ip_add_source_membership(token, ent, opt); + break; + #if WITH_IP4 case TYPE_IP4NAME: { @@ -4057,7 +4071,13 @@ mc:addr break; #endif /* WITH_IP4 && (defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN)) */ - +#if WITH_IP4 && defined(HAVE_STRUCT_IP_MREQ_SOURCE) && defined(IP_ADD_SOURCE_MEMBERSHIP) + case OPT_IP_ADD_SOURCE_MEMBERSHIP: + if (xioapply_ip_add_source_membership(xfd, opt) < 0) { + continue; + } + break; +#endif /* WITH_IP4 && defined(HAVE_STRUCT_IP_MREQ_SOURCE) && defined(IP_ADD_SOURCE_MEMBERSHIP) */ #if WITH_IP6 && defined(HAVE_STRUCT_IPV6_MREQ) case OPT_IPV6_JOIN_GROUP: diff --git a/xioopts.h b/xioopts.h index 85b0a3b..9b4d573 100644 --- a/xioopts.h +++ b/xioopts.h @@ -73,8 +73,11 @@ enum e_types { #if HAVE_STRUCT_IP_MREQ || HAVE_STRUCT_IP_MREQN TYPE_IP_MREQN, /* for struct ip_mreq or struct ip_mreqn */ #endif +#if HAVE_STRUCT_IP_MREQ_SOURCE + TYPE_IP_MREQ_SOURCE, /* for struct ip_mreq_source */ +#endif - TYPE_GENERIC, /* type is determined from (text) data provided */ + TYPE_GENERIC, /* type is determined from (text) data provided (dalan syntax) */ } ; enum e_func { @@ -380,6 +383,7 @@ enum e_optcode { OPT_IOCTL_STRING, /* generic ioctl with integer value (pointed to) */ OPT_IOCTL_VOID, /* generic ioctl without value */ OPT_IP_ADD_MEMBERSHIP, + OPT_IP_ADD_SOURCE_MEMBERSHIP, #ifdef IP_HDRINCL OPT_IP_HDRINCL, #endif