From 2ffe5a324eeb3aba48cfda17ef0f9a3cc4a22149 Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Mon, 22 Sep 2008 22:17:55 +0200 Subject: [PATCH] merged features ancillary, envvar --- CHANGES | 33 +++- README | 12 ++ VERSION | 2 +- config.h.in | 12 ++ configure.in | 15 ++ doc/socat-multicast.html | 3 +- doc/socat.yo | 177 ++++++++++++++--- hostan.c | 9 +- socat.c | 2 + sycls.c | 31 ++- sycls.h | 6 +- sysincludes.h | 11 +- sysutils.c | 154 +++++++++++---- sysutils.h | 11 +- test.sh | 415 ++++++++++++++++++++++++++++++++++++++- utils.c | 16 +- utils.h | 5 +- xio-ascii.c | 51 ++++- xio-ascii.h | 6 +- xio-ip.c | 122 +++++++++++- xio-ip.h | 10 +- xio-ip4.c | 39 +++- xio-ip4.h | 6 +- xio-ip6.c | 228 ++++++++++++++++++++- xio-ip6.h | 29 ++- xio-ipapp.c | 25 +-- xio-listen.c | 46 ++--- xio-openssl.c | 29 +-- xio-progcall.c | 18 +- xio-proxy.c | 25 +-- xio-socket.c | 379 ++++++++++++++++++++++++++++------- xio-socket.h | 13 +- xio-socks.c | 23 +-- xio-tcpwrap.c | 3 +- xio-udp.c | 19 +- xio-unix.c | 23 ++- xio-unix.h | 6 +- xio.h | 2 + xioinitialize.c | 51 +++++ xioopts.c | 70 ++++++- xioopts.h | 20 ++ xioread.c | 28 ++- 42 files changed, 1898 insertions(+), 287 deletions(-) diff --git a/CHANGES b/CHANGES index 042c977..f402c88 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,34 @@ new features: new address option "escape" allows to break a socat instance even when raw terminal mode prevents ^C etc. + socat sets environment variables SOCAT_VERSION, SOCAT_PID, SOCAT_PPID + for use in executed scripts + + socat sets environment variables SOCAT_SOCKADDR, SOCAT_SOCKPORT, + SOCAT_PEERADDR, SOCAT_PEERPORT in LISTEN type addresses (feature + suggested by Ed Sawicki) + + socat receives all ancillary messages with each received packet on + datagram related addresses. The messages are logged in raw form with + debug level, and broken down with info level. note: each type of + ancillary message must be enabled by appropriate address options. + + socat provides the contents of ancillary messages received on RECVFROM + addresses in appropriate environment variables: + SOCAT_IP_PKTINFO_DSTADDR, SOCAT_IP_PKTINFO_IF, SOCAT_IP_PKTINFO_LOCADDR + (not on BSD); SOCAT_IP_RECVDSTADDR, SOCAT_IP_RECVIF (BSD); + SOCAT_SCM_TIMESTAMP, SOCAT_IP_OPTIONS, SOCAT_IP_TOS, SOCAT_IP_TTL, + SOCAT_IPV6_HOPLIMIT, SOCAT_IPV6_TCLASS, SOCAT_IPV6_PKTINFO_DSTADDR + + the following address options were added to enable ancillary messages: + so-timestamp, ip-pktinfo, ip-recvdstaddr, ip-recverr, ip-recvif, + ip-recvopts, ip-recvtos, ip-recvttl, ipv6-recvdstopts, ipv6-recverr, + ipv6-recvhoplimit, ipv6-recvhopopts, ipv6-recvpathmtu, + ipv6-recvpktinfo, ipv6-recvrthdr, ipv6-recvtclass + + new address options ipv6-tclass and ipv6-unicast-hops set the related + socket options. + corrections: some raw IP and UNIX datagram modes failed on BSD systems @@ -13,9 +41,10 @@ corrections: there was a bug in ip*-recv with bind option: it did not bind, and with the first received packet an error occurred: socket_init(): unknown address family 0 + test: RAWIP4RECVBIND RECVFROM addresses with FORK option hung after processing the first - packet. + packet. test: UDP4RECVFROM_FORK corrected a few mistakes that caused compiler warnings on 64bit hosts @@ -24,7 +53,7 @@ corrections: when the EXEC address got a string with consecutive spaces it created additional empty arguments (thanks to Olivier Hervieu for reporting - this bug) + this bug). test: EXECSPACES in ignoreeof polling mode socat also blocked data transfer in the other direction during the 1s wait intervalls (thanks to Jorgen Cederlof for diff --git a/README b/README index 6baad77..d92eb4a 100644 --- a/README +++ b/README @@ -180,10 +180,22 @@ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/sfw/lib For some shell scripts, it is preferable to have /usr/xpg4/bin at a prominent position in $PATH. +With the default compiler define _GNU_SOURCE, the CMSG_* macros are not +available, and therefore ancillary messages cannot be used. To enable these try +the following: +After running ./configure, edit Makefile and replace "-D_GNU_SOURCE" with +"-D_XPG4_2 -D__EXTENSIONS__" and run make + platform specifics - hp-ux -------------------------- +Ancillary messages cannot be compiled in with socat: both struct msghdr and +strutc cmsghdr are required. Compiling with -D_XOPEN_SOURCE_EXTENDED provides +struct msghdr but disables struct cmsghdr while -D_OPEN_SOURCE disables struct +msghdr but disables struct cmsghdr. Please contact socat development if you +know a solution. + Shutting down the write channel of a UNIX domain socket does not seem to trigger an EOF on the other socket. This makes problems with the exec and system addresses. diff --git a/VERSION b/VERSION index f509df5..f881903 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -"1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll+udplistencont+ignoreeofunblock+escape" +"1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll+udplistencont+ignoreeofunblock+escape+timestamp+ancillary+envvar" diff --git a/config.h.in b/config.h.in index 2e330da..0d8c9cc 100644 --- a/config.h.in +++ b/config.h.in @@ -224,6 +224,15 @@ /* Define if you have the header file. */ #undef HAVE_NET_IF_H +/* Define if you have the header file. */ +#undef HAVE_NET_IF_DL_H + +/* Define if you have the header file. */ +#undef HAVE_LINUX_TYPES_H + +/* Define if you have the header file. */ +#undef HAVE_LINUX_ERRQUEUE_H + /* Define if you have the header file. */ #undef HAVE_LINUX_IF_TUN_H @@ -334,6 +343,9 @@ /* define if your struct msghdr has msg_flag */ #undef HAVE_STRUCT_MSGHDR_MSGFLAGS +/* define if you have struct cmsghdr */ +#undef HAVE_STRUCT_CMSGHDR + /* define if your struct ip has ip_hl; otherwise assume ip_vhl */ #undef HAVE_STRUCT_IP_IP_HL diff --git a/configure.in b/configure.in index a0a3d80..013a2c3 100644 --- a/configure.in +++ b/configure.in @@ -64,6 +64,8 @@ AC_CHECK_HEADERS(arpa/nameser.h) AC_HEADER_RESOLV() AC_CHECK_HEADERS(termios.h linux/if_tun.h) +AC_CHECK_HEADERS(net/if_dl.h) +AC_CHECK_HEADERS(linux/types.h linux/errqueue.h) AC_CHECK_HEADERS(sys/utsname.h sys/select.h sys/file.h) AC_CHECK_HEADERS(util.h libutil.h sys/stropts.h regex.h) AC_CHECK_HEADERS(linux/fs.h linux/ext2_fs.h) @@ -1044,6 +1046,19 @@ if test $sc_cv_struct_msghdr_msgflags = yes; then fi AC_MSG_RESULT($sc_cv_struct_msghdr_msgflags) +dnl check for struct cmsghdr +AC_MSG_CHECKING(for struct cmsghdr) +AC_CACHE_VAL(sc_cv_struct_cmsghdr, +[AC_TRY_COMPILE([#include +#include +#include ],[struct cmsghdr s;], +[sc_cv_struct_cmsghdr=yes], +[sc_cv_struct_cmsghdr=no])]) +if test $sc_cv_struct_cmsghdr = yes; then + AC_DEFINE(HAVE_STRUCT_CMSGHDR) +fi +AC_MSG_RESULT($sc_cv_struct_cmsghdr) + dnl check for ip_hl in struct ip AC_MSG_CHECKING(for struct ip.ip_hl) AC_CACHE_VAL(sc_cv_struct_ip_ip_hl, diff --git a/doc/socat-multicast.html b/doc/socat-multicast.html index fc31f7b..829eba3 100644 --- a/doc/socat-multicast.html +++ b/doc/socat-multicast.html @@ -240,8 +240,7 @@ interfaces. This membership cannot be dropped on Linux. sockets without exception accept packets that are directly addressed to them; the multi- and broadcast receiving features are just extensions to the normal functionality. socat has no way to find out if an incoming packet is addressed -to a unicast, multicast or broadcast address. Please contact the author if you -know how the target address can be determined.

+to a unicast, multicast, or broadcast address.

Authentication or encryption are not available.

diff --git a/doc/socat.yo b/doc/socat.yo index d422aef..a432e90 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -127,7 +127,8 @@ dit(bf(tt(-lf))tt( )) dit(bf(tt(-ls))) Writes messages to stderr (this is the default). label(option_lp)dit(bf(tt(-lp))tt()) - Overrides the program name printed in error messages. + Overrides the program name printed in error messages and used for + constructing environment variable names. dit(bf(tt(-lu))) Extends the timestamp of error messages to microsecond resolution. Does not work when logging to syslog. @@ -364,6 +365,7 @@ label(ADDRESS_IP_DATAGRAM)dit(bf(tt(IP-DATAGRAM:
:))) Option groups: link(FD)(GROUP_FD), link(SOCKET)(GROUP_SOCKET), link(IP4)(GROUP_IP4), link(IP6)(GROUP_IP6), link(RANGE)(GROUP_RANGE) nl() Useful options: + link(bind)(OPTION_BIND), link(range)(OPTION_RANGE), link(tcpwrap)(OPTION_TCPWRAPPERS), link(broadcast)(OPTION_SO_BROADCAST), @@ -373,7 +375,6 @@ label(ADDRESS_IP_DATAGRAM)dit(bf(tt(IP-DATAGRAM:
:))) link(ip-add-membership)(OPTION_IP_ADD_MEMBERSHIP), link(ttl)(OPTION_TTL), link(tos)(OPTION_TOS), - link(bind)(OPTION_BIND), link(pf)(OPTION_PROTOCOL_FAMILY)nl() See also: link(IP4-DATAGRAM)(ADDRESS_IP4_DATAGRAM), @@ -783,7 +784,8 @@ label(ADDRESS_UDP_DATAGRAM)dit(bf(tt(UDP-DATAGRAM:
:))) options. This address type can for example be used for implementing symmetric or asymmetric broadcast or multicast communications.nl() Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET),link(IP4)(GROUP_IP4),link(IP6)(GROUP_IP6),link(RANGE)(GROUP_RANGE) nl() - Useful options: + Useful options: + link(bind)(OPTION_BIND), link(range)(OPTION_RANGE), link(tcpwrap)(OPTION_TCPWRAPPERS), link(broadcast)(OPTION_SO_BROADCAST), @@ -793,7 +795,6 @@ label(ADDRESS_UDP_DATAGRAM)dit(bf(tt(UDP-DATAGRAM:
:))) link(ip-add-membership)(OPTION_IP_ADD_MEMBERSHIP), link(ttl)(OPTION_TTL), link(tos)(OPTION_TOS), - link(bind)(OPTION_BIND), link(sourceport)(OPTION_SOURCEPORT), link(pf)(OPTION_PROTOCOL_FAMILY)nl() See also: @@ -1543,8 +1544,8 @@ label(OPTION_INTERFACE)dit(bf(tt(interface=))) label(OPTION_SO_BROADCAST)dit(bf(tt(broadcast))) For datagram sockets, allows sending to broadcast addresses and receiving packets addressed to broadcast addresses. -label(OPTION_BSDCOMPAT)dit(bf(tt(bsdcompat))) - Emulates some (old?) bugs of the BSD socket implementation. +COMMENT(label(OPTION_BSDCOMPAT)dit(bf(tt(bsdcompat))) + Emulates some (old?) bugs of the BSD socket implementation.) label(OPTION_DEBUG)dit(bf(tt(debug))) Enables socket debugging. label(OPTION_DONTROUTE)dit(bf(tt(dontroute))) @@ -1639,6 +1640,9 @@ COMMENT(label(OPTION_USEIFBUFS)dit(bf(tt(useifbufs))) label(OPTION_PROTOCOL_FAMILY)dit(bf(tt(pf=))) Forces the use of the specified IP version. can be something like "ip4" or "ip6". +label(OPTION_SO_TIMESTAMP)dit(bf(tt(so-timestamp))) + Sets the SO_TIMESTAMP socket option. This enables receiving and logging of + timestamp ancillary messages. enddit() startdit()enddit()nl() @@ -1665,13 +1669,13 @@ label(OPTION_TOS)dit(bf(tt(tos=))) label(OPTION_TTL)dit(bf(tt(ttl=))) Sets the TTL (time to live) field of outgoing packets to [link(byte)(TYPE_BYTE)]. -label(OPTION_IPOPTIONS)dit(bf(tt(ipoptions=))) +label(OPTION_IPOPTIONS)dit(bf(tt(ip-options=))) Sets IP options like source routing. Must be given in binary form, recommended format is a leading "x" followed by an even number of hex digits. This option may be used multiple times, data are appended. E.g., to connect to host 10.0.0.1 via some gateway using a loose source route, use the gateway as address parameter and set a loose source route - using the option code(ipoptions=x8307040a000001).nl() + using the option code(ip-options=x8307040a000001).nl() IP options are defined in RFC 791. COMMENT(, RFC 2113)nl() COMMENT( x00 end of option list x01 no operation (nop) @@ -1690,20 +1694,32 @@ COMMENT(label(OPTION_IP_MULTICAST_LOOP)dit(bf(tt(multicastloop))) Allow looping back outgoing multicast to the local interface.) COMMENT(label(OPTION_IP_MULTICAST_TTL)dit(bf(tt(multicastttl))) Set the TTL for outgoing multicast packets.) -COMMENT(label(OPTION_PKTINFO)dit(bf(tt(pktinfo))) - Set the IP_PKTINFO socket option.) +label(OPTION_IP_PKTINFO)dit(bf(tt(pktinfo))) + Sets the IP_PKTINFO socket option. This enables receiving and logging of + ancillary messages containing destination address and interface (Linux). COMMENT(label(OPTION_PKTOPTS)dit(bf(tt(pktopts))) Set the IP_PKTOPTIONS socket option.) -COMMENT(label(OPTION_RECVERR)dit(bf(tt(recverr))) - Set the IP_RECVERR socket option.) -COMMENT(label(OPTION_RECVOPTS)dit(bf(tt(recvopts))) - Set the IP_RECVOPTS socket option.) -COMMENT(label(OPTION_RECVTOS)dit(bf(tt(recvtos))) - Set the IP_RECVTOS socket option.) -COMMENT(label(OPTION_RECVTTL)dit(bf(tt(recvttl))) - Set the IP_RECVTTL socket option.) +label(OPTION_IP_RECVERR)dit(bf(tt(recverr))) + Sets the IP_RECVERR socket option. This enables receiving and logging of + ancillary messages containing detailled error information. +label(OPTION_IP_RECVOPTS)dit(bf(tt(recvopts))) + Sets the IP_RECVOPTS socket option. This enables receiving and logging of IP + options ancillary messages (Linux, *BSD). +label(OPTION_IP_RECVTOS)dit(bf(tt(recvtos))) + Sets the IP_RECVTOS socket option. This enables receiving and logging of TOS + (type of service) ancillary messages (Linux). +label(OPTION_IP_RECVTTL)dit(bf(tt(recvttl))) + Sets the IP_RECVTTL socket option. This enables receiving and logging of TTL + (time to live) ancillary messages (Linux, *BSD). COMMENT(label(OPTION_RETOPTS)dit(bf(tt(retopts))) Set the IP_RETOPTS socket option.) +label(OPTION_IP_RECVDSTADDR)dit(bf(tt(recvdstaddr))) + Sets the IP_RECVDSTADDR socket option. This enables receiving and logging of + ancillary messages containing destination address + (*BSD). +label(OPTION_IP_RECVIF)dit(bf(tt(recvif))) + Sets the IP_RECVIF socket option. This enables receiving and logging of + interface ancillary messages (*BSD). COMMENT(label(OPTION_ROUTERALERT)dit(bf(tt(routeralert))) Set the IP_ROUTER_ALERT socket option.) label(OPTION_IP_ADD_MEMBERSHIP) @@ -1758,6 +1774,30 @@ label(OPTION_IPV6_V6ONLY)dit(bf(tt(ipv6only=))) Sets the IPV6_V6ONLY socket option. If 0, the TCP stack will also accept connections using IPv4 protocol on the same port. The default is system dependent. +label(OPTION_IPV6_RECVDSTOPTS)dit(bf(tt(ipv6-recvdstopts))) + Sets the IPV6_RECVDSTOPTS socket option. This enables receiving and logging + of ancillary messages containing the destination options. +label(OPTION_IPV6_RECVHOPLIMIT)dit(bf(tt(ipv6-recvhoplimit))) + Sets the IPV6_RECVHOPLIMIT socket option. This enables receiving and logging + of ancillary messages containing the hoplimit. +label(OPTION_IPV6_RECVHOPOPTS)dit(bf(tt(ipv6-recvhopopts))) + Sets the IPV6_RECVHOPOPTS socket option. This enables receiving and logging + of ancillary messages containing the hop options. +label(OPTION_IPV6_RECVPKTINFO)dit(bf(tt(ipv6-recvpktinfo))) + Sets the IPV6_RECVPKTINFO socket option. This enables receiving and logging + of ancillary messages containing destination address and interface. +label(OPTION_IPV6_UNICAST_HOPS)dit(bf(tt(ipv6-unicast-hops=link(TYPE_INT)()))) + Sets the IPV6_UNICAST_HOPS socket option. This sets the hop count limit + (TTL) for outgoing unicast packets. +label(OPTION_IPV6_RECVRTHDR)dit(bf(tt(ipv6-recvrthdr))) + Sets the IPV6_RECVRTHDR socket option. This enables receiving and logging + of ancillary messages containing routing information. +label(OPTION_IPV6_TCLASS)dit(bf(tt(ipv6-tclass))) + Sets the IPV6_TCLASS socket option. This sets the transfer class of outgoing + packets. +label(OPTION_IPV6_RECVTCLASS)dit(bf(tt(ipv6-recvtclass))) + Sets the IPV6_RECVTCLASS socket option. This enables receiving and logging + of ancillary messages containing the transfer class. enddit() startdit()enddit()nl() @@ -2932,45 +2972,126 @@ manpagefiles() label(ENVIRONMENT_VARIABLES) manpagesection(ENVIRONMENT VARIABLES) +Input variables carry information from the environment to socat, output +variables are set by socat for use in executed scripts and programs. + +In the output variables beginning with "SOCAT" this prefix is actually replaced +by the upper case name of the executable or the value of option +link(-lp)(option_lp). + startdit() -dit(bf(SOCAT_DEFAULT_LISTEN_IP)) (Values 4 or 6) Sets the IP version to be used +dit(bf(SOCAT_DEFAULT_LISTEN_IP) (input)) (Values 4 or 6) Sets the IP version to +be used for listen, recv, and recvfrom addresses if no link(pf)(OPTION_PROTOCOL_FAMILY) (protocol-family) option is given. Is overridden by socat options link(-4)(option_4) or link(-6)(option_6). -dit(bf(SOCAT_PREFERRED_RESOLVE_IP)) (Values 0, 4, or 6) Sets the IP version to +dit(bf(SOCAT_PREFERRED_RESOLVE_IP) (input)) (Values 0, 4, or 6) Sets the IP +version to be used when resolving target host names when version is not specified by address type, option link(pf)(OPTION_PROTOCOL_FAMILY) (protocol-family), or address format. If name resolution does not return a matching entry, the first result (with differing IP version) is taken. With value 0, socat always selects the first record and its IP version. -dit(bf(SOCAT_FORK_WAIT)) Specifies the time (seconds) to sleep the parent and -child processes after successful fork(). Useful for debugging. +dit(bf(SOCAT_FORK_WAIT) (input)) Specifies the time (seconds) to sleep the +parent and child processes after successful fork(). Useful for debugging. -dit(bf(HOSTNAME)) Is used to determine the hostname for logging (see +dit(bf(SOCAT_VERSION) (output)) Socat sets this variable to its version string, +e.g. tt("1.6.1.0") for released versions or e.g. tt("1.6.0.1+envvar") for +temporary versions; can be used in scripts invoked by socat. + +dit(bf(SOCAT_PID) (output)) Socat sets this variable to its process id. In case +of link(fork)(OPTION_FORK) address option, SOCAT_PID gets the child processes +id. Forking for link(exec)(ADDRESS_EXEC) and link(system)(ADDRESS_SYSTEM) does +not change SOCAT_PID. + +dit(bf(SOCAT_PPID) (output)) Socat sets this variable to its process id. In +case of link(fork)(OPTION_FORK), SOCAT_PPID keeps the pid of the master process. + +dit(bf(SOCAT_PEERADDR) (output)) With passive socket addresses (all LISTEN and +RECVFROM addresses), this variable is set to a string describing the peers +socket address. Port information is not included. + +dit(bf(SOCAT_PEERPORT) (output)) With appropriate passive socket addresses (TCP +and UDP - LISTEN and RECVFROM), this variable is set to a string containing the +number of the peer port. + +dit(bf(SOCAT_SOCKADDR) (output)) With all LISTEN addresses, this variable is +set to a string describing the local socket address. Port information is not +included. + +dit(bf(SOCAT_SOCKPORT) (output)) With TCP-LISTEN and UDP-LISTEN addresses, this +variable is set to the local port. + +dit(bf(SOCAT_TIMESTAMP) (output)) With all RECVFROM addresses where address +option link(so-timestamp)(OPTION_SO_TIMESTAMP) is applied, socat sets this +variable to the resulting timestamp. + +dit(bf(SOCAT_IP_OPTIONS) (output)) With all IPv4 based RECVFROM addresses where +address option link(ip-recvopts)(OPTION_IP_RECVOPTS) is applied, socat fills +this variable with the IP options of the received packet. + +dit(bf(SOCAT_IP_DSTADDR) (output)) With all IPv4 based RECVFROM addresses where +address option link(ip-recvdstaddr)(OPTION_IP_RECVDSTADDR) (BSD) or +link(ip-pktinfo)(OPTION_IP_PKTINFO) (other platforms) is applied, socat sets +this variable to the destination address of the received packet. This is +particularly useful to identify broadcast and multicast addressed packets. + +dit(bf(SOCAT_IP_IF) (output)) With all IPv4 based RECVFROM addresses where +address option link(ip-recvif)(OPTION_IP_RECVIF) (BSD) or +link(ip-pktinfo)(OPTION_IP_PKTINFO) (other platforms) is applied, socat sets +this variable to the name of the interface where the packet was received. + +dit(bf(SOCAT_IP_LOCADDR) (output)) With all IPv4 based RECVFROM +addresses where address option link(ip-pktinfo)(OPTION_IP_PKTINFO) is applied, +socat sets this variable to the address of the interface where the packet was +received. + +dit(bf(SOCAT_IP_TOS) (output)) With all IPv4 based RECVFROM addresses where +address option link(ip-recvtos)(OPTION_IP_RECVTOS) is applied, socat sets this +variable to the TOS (type of service) of the received packet. + +dit(bf(SOCAT_IP_TTL) (output)) With all IPv4 based RECVFROM addresses where +address option link(ip-recvttl)(OPTION_IP_RECVTTL) is applied, socat sets this +variable to the TTL (time to live) of the received packet. + +dit(bf(SOCAT_IPV6_HOPLIMIT) (output)) With all IPv6 based RECVFROM addresses +where address option link(ipv6-recvhoplimit)(OPTION_IPV6_RECVHOPLIMIT) is +applied, socat sets this variable to the hoplimit value of the received packet. + +dit(bf(SOCAT_IPV6_DSTADDR) (output)) With all IPv6 based RECVFROM +addresses where address option link(ipv6-recvpktinfo)(OPTION_IPV6_RECVPKTINFO) +is applied, socat sets this variable to the destination address of the received +packet. + +dit(bf(SOCAT_IPV6_TCLASS) (output)) With all IPv6 based RECVFROM addresses +where address option link(ipv6-recvtclass)(OPTION_IPV6_RECVTCLASS) is applied, +socat sets this variable to the transfer class of the received packet. + +dit(bf(HOSTNAME) (input)) Is used to determine the hostname for logging (see link(-lh)(option_lh)). -dit(bf(LOGNAME)) Is used as name for the socks client user name if no +dit(bf(LOGNAME) (input)) Is used as name for the socks client user name if no link(socksuser)(OPTION_SOCKSUSER) is given.nl() With options link(su)(OPTION_SUBSTUSER) and link(su-d)(OPTION_SUBSTUSER_DELAYED), LOGNAME is set to the given user name. -dit(bf(USER)) Is used as name for the socks client user name if no +dit(bf(USER) (input)) Is used as name for the socks client user name if no link(socksuser)(OPTION_SOCKSUSER) is given and LOGNAME is empty.nl() With options link(su)(OPTION_SUBSTUSER) and link(su-d)(OPTION_SUBSTUSER_DELAYED), USER is set to the given user name. -dit(bf(SHELL)) +dit(bf(SHELL) (output)) With options link(su)(OPTION_SUBSTUSER) and link(su-d)(OPTION_SUBSTUSER_DELAYED), SHELL is set to the login shell of the given user. -dit(bf(PATH)) +dit(bf(PATH) (output)) Can be set with option link(path)(OPTION_PATH) for link(exec)(ADDRESS_EXEC) and link(system)(ADDRESS_SYSTEM) addresses. -dit(bf(HOME)) +dit(bf(HOME) (output)) With options link(su)(OPTION_SUBSTUSER) and link(su-d)(OPTION_SUBSTUSER_DELAYED), HOME is set to the home directory of the given user. diff --git a/hostan.c b/hostan.c index 8862bf5..19829b2 100644 --- a/hostan.c +++ b/hostan.c @@ -30,12 +30,11 @@ int hostan(FILE *outfile) { #if WITH_SOCKET static int iffan(FILE *outfile) { /* Linux: man 7 netdevice */ - /* FreeBSD: man 4 networking */ + /* FreeBSD, NetBSD: man 4 networking */ /* Solaris: man 7 if_tcp */ /* currently we support Linux and a little FreeBSD */ #ifdef SIOCGIFCONF /* not Solaris */ -#ifdef SIOCGIFINDEX /* not OpenBSD */ #define IFBUFSIZ 32*sizeof(struct ifreq) /*1024*/ int s; @@ -62,22 +61,24 @@ static int iffan(FILE *outfile) { struct ifreq *ifp = (struct ifreq *)((caddr_t)ic.ifc_req + i); struct ifreq ifr; +#if 0 || defined(SIOCGIFINDEX) /* not NetBSD, OpenBSD */ strcpy(ifr.ifr_name, ifp->ifr_name); if (Ioctl(s, SIOCGIFINDEX, &ifr) < 0) { Error3("ioctl(%d, SIOCGIFINDEX, {\"%s\"}): %s", s, &ifr.ifr_name, strerror(errno)); return 1; } - /*fprintf(outfile, "%2d: %s\n", ifr.ifr_ifindex, ifp->ifr_ifrn.ifrn_name);*/ #if HAVE_STRUCT_IFREQ_IFR_INDEX fprintf(outfile, "%2d: %s\n", ifr.ifr_index, ifp->ifr_name); #elif HAVE_STRUCT_IFREQ_IFR_IFINDEX fprintf(outfile, "%2d: %s\n", ifr.ifr_ifindex, ifp->ifr_name); #endif /* HAVE_STRUCT_IFREQ_IFR_INDEX */ +#else /* !defined(SIOCGIFINDEX) */ + fprintf(outfile, "%2d: %s\n", i/sizeof(struct ifreq), ifp->ifr_name); +#endif /* defined(SIOCGIFINDEX) */ } Close(s); #endif /* defined(SIOCGIFCONF) */ -#endif /* defined(SIOCGIFINDEX) */ return 0; } #endif /* WITH_SOCKET */ diff --git a/socat.c b/socat.c index 3ff3d17..28698bf 100644 --- a/socat.c +++ b/socat.c @@ -267,12 +267,14 @@ int main(int argc, const char *argv[]) { Error("-U and -u must not be combined"); } + xioinitialize2(); Info(copyright_socat); #if WITH_OPENSSL Info(copyright_openssl); Info(copyright_ssleay); #endif Debug2("socat version %s on %s", socatversion, timestamp); + xiosetenv("VERSION", socatversion, 1); /* SOCAT_VERSION */ uname(&ubuf); /* ! here we circumvent internal tracing (Uname) */ Debug4("running on %s version %s, release %s, machine %s\n", ubuf.sysname, ubuf.version, ubuf.release, ubuf.machine); diff --git a/sycls.c b/sycls.c index 347a7cb..4ab902c 100644 --- a/sycls.c +++ b/sycls.c @@ -1,5 +1,5 @@ /* source: sycls.c */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* explicit system call and C library trace function, for those who miss strace @@ -1055,11 +1055,14 @@ int Recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, int Recvmsg(int s, struct msghdr *msgh, int flags) { int retval, _errno; char infobuff[256]; - Debug3("recvmsg(%d, %p, %d)", s, msgh, flags); + Debug10("recvmsg(%d, %p{%p,%u,%p,%u,%p,%u,%d}, %d)", s, msgh, + msgh->msg_name, msgh->msg_namelen, msgh->msg_iov, msgh->msg_iovlen, + msgh->msg_control, msgh->msg_controllen, msgh->msg_flags, flags); retval = recvmsg(s, msgh, flags); _errno = errno; - Debug2("recvmsg(, {%s}, ) -> %d", + Debug5("recvmsg(, {%s,%u,,%u,,%u,}, ) -> %d", msgh->msg_name?sockaddr_info(msgh->msg_name, msgh->msg_namelen, infobuff, sizeof(infobuff)):"NULL", + msgh->msg_namelen, msgh->msg_iovlen, msgh->msg_controllen, retval); errno = _errno; return retval; @@ -1419,6 +1422,28 @@ int Mkstemp(char *template) { return result; } +int Setenv(const char *name, const char *value, int overwrite) { + int result, _errno; + Debug3("setenv(\"%s\", \"%s\", %d)", name, value, overwrite); + result = setenv(name, value, overwrite); + _errno = errno; + Debug1("setenv() -> %d", result); + errno = _errno; + return result; +} + +/* on Linux it returns int but on FreeBSD void. + we do not expect many errors, so we take void which works on all systems. */ +void Unsetenv(const char *name) { + int _errno; + Debug1("unsetenv(\"%s\")", name); + unsetenv(name); + _errno = errno; + Debug("unsetenv() ->"); + errno = _errno; + return; +} + #if WITH_READLINE char *Readline(const char *prompt) { diff --git a/sycls.h b/sycls.h index 8166d96..03ecff0 100644 --- a/sycls.h +++ b/sycls.h @@ -1,5 +1,5 @@ /* source: sycls.h */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __sycls_h_included @@ -137,6 +137,8 @@ int Atexit(void (*func)(void)); void Exit(int status); void Abort(void); int Mkstemp(char *template); +int Setenv(const char *name, const char *value, int overwrite); +void Unsetenv(const char *name); char *Readline(const char *prompt); void Using_history(void); @@ -256,6 +258,8 @@ void Add_history(const char *string); #define Exit(s) exit(s) #define Abort() abort() #define Mkstemp(t) mkstemp(t) +#define Setenv(n,v,o) setenv(n,v,o) +#define Unsetenv(n) unsetenv(n) #define Readline(p) readline(p) #define Using_history() using_history() diff --git a/sysincludes.h b/sysincludes.h index f233358..6d053b9 100644 --- a/sysincludes.h +++ b/sysincludes.h @@ -1,5 +1,5 @@ /* source: sysincludes.h */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __sysincludes_h_included @@ -108,6 +108,9 @@ #include /* req for resolv.h (esp. on MacOSX) */ #endif #include +#if HAVE_NET_IF_DL_H +#include /* FreeBSD: struct sockaddr_dl */ +#endif #if HAVE_RESOLV_H #include /* _res */ #endif @@ -116,6 +119,12 @@ #if HAVE_NET_IF_H #include #endif /* HAVE_NET_IF_H */ +#if HAVE_LINUX_TYPES_H +#include /* __u32 for linux/errqueue.h */ +#endif +#if HAVE_LINUX_ERRQUEUE_H +#include /* struct sock_extended_err */ +#endif #if HAVE_LINUX_IF_TUN_H #include #endif diff --git a/sysutils.c b/sysutils.c index 28ccae9..4a27fe9 100644 --- a/sysutils.c +++ b/sysutils.c @@ -129,60 +129,48 @@ socklen_t socket_init(int af, union sockaddr_union *sa) { #if _WITH_SOCKET char *sockaddr_info(const struct sockaddr *sa, socklen_t salen, char *buff, size_t blen) { - char ubuff[5*UNIX_PATH_MAX+3]; + union sockaddr_union *sau = (union sockaddr_union *)sa; char *lbuff = buff; char *cp = lbuff; int n; - if ((n = snprintf(cp, blen, "AF=%d ", sa->sa_family)) < 0) { + if ((n = snprintf(cp, blen, "AF=%d ", sau->soa.sa_family)) < 0) { Warn1("sockaddr_info(): buffer too short ("F_Zu")", blen); *buff = '\0'; return buff; } cp += n, blen -= n; - switch (sa->sa_family) { + switch (sau->soa.sa_family) { #if WITH_UNIX case 0: - case AF_UNIX: -#if WITH_ABSTRACT_UNIXSOCKET - if (salen > XIOUNIXSOCKOVERHEAD && - sa->sa_data[0] == '\0') { - char *nextc; - nextc = - sanitize_string((char *)&sa->sa_data, salen-XIOUNIXSOCKOVERHEAD, - ubuff, XIOSAN_DEFAULT_BACKSLASH_OCT_3); - *nextc = '\0'; - snprintf(cp, blen, "\"%s\"", ubuff); - } else -#endif /* WITH_ABSTRACT_UNIXSOCKET */ - { - char *nextc; - nextc = - sanitize_string((char *)&sa->sa_data, - MIN(UNIX_PATH_MAX, strlen((char *)&sa->sa_data)), - ubuff, XIOSAN_DEFAULT_BACKSLASH_OCT_3); - *nextc = '\0'; - snprintf(cp, blen, "\"%s\"", ubuff); - } + case AF_UNIX: sockaddr_unix_info(&sau->un, salen, cp+1, blen-1); + cp[0] = '"'; + *strchr(cp+1, '\0') = '"'; break; #endif #if WITH_IP4 - case AF_INET: sockaddr_inet4_info((struct sockaddr_in *)sa, cp, blen); + case AF_INET: sockaddr_inet4_info(&sau->ip4, cp, blen); break; #endif #if WITH_IP6 - case AF_INET6: sockaddr_inet6_info((struct sockaddr_in6 *)sa, cp, blen); + case AF_INET6: sockaddr_inet6_info(&sau->ip6, cp, blen); break; #endif default: + if ((n = snprintf(cp, blen, "AF=%d ", sa->sa_family)) < 0) { + Warn1("sockaddr_info(): buffer too short ("F_Zu")", blen); + *buff = '\0'; + return buff; + } + cp += n, blen -= n; if ((snprintf(cp, blen, "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - sa->sa_data[0], sa->sa_data[1], sa->sa_data[2], - sa->sa_data[3], sa->sa_data[4], sa->sa_data[5], - sa->sa_data[6], sa->sa_data[7], sa->sa_data[8], - sa->sa_data[9], sa->sa_data[10], sa->sa_data[11], - sa->sa_data[12], sa->sa_data[13])) < 0) { + sau->soa.sa_data[0], sau->soa.sa_data[1], sau->soa.sa_data[2], + sau->soa.sa_data[3], sau->soa.sa_data[4], sau->soa.sa_data[5], + sau->soa.sa_data[6], sau->soa.sa_data[7], sau->soa.sa_data[8], + sau->soa.sa_data[9], sau->soa.sa_data[10], sau->soa.sa_data[11], + sau->soa.sa_data[12], sau->soa.sa_data[13])) < 0) { Warn("sockaddr_info(): buffer too short"); *buff = '\0'; return buff; @@ -195,10 +183,26 @@ char *sockaddr_info(const struct sockaddr *sa, socklen_t salen, char *buff, size #if WITH_UNIX char *sockaddr_unix_info(const struct sockaddr_un *sa, socklen_t salen, char *buff, size_t blen) { - blen = Min(blen, sizeof(sa->sun_path)); - strncpy(buff, sa->sun_path, blen); - if (strlen(buff) >= blen) { - buff[blen-1] = '\0'; + char ubuff[5*UNIX_PATH_MAX+3]; + char *nextc; + +#if WITH_ABSTRACT_UNIXSOCKET + if (salen > XIOUNIXSOCKOVERHEAD && + sa->sun_path[0] == '\0') { + nextc = + sanitize_string(sa->sun_path, salen-XIOUNIXSOCKOVERHEAD, + ubuff, XIOSAN_DEFAULT_BACKSLASH_OCT_3); + *nextc = '\0'; + strncpy(buff, ubuff, blen); + } else +#endif /* WITH_ABSTRACT_UNIXSOCKET */ + { + nextc = + sanitize_string(sa->sun_path, + MIN(UNIX_PATH_MAX, strlen(sa->sun_path)), + ubuff, XIOSAN_DEFAULT_BACKSLASH_OCT_3); + *nextc = '\0'; + strncpy(buff, ubuff, blen); } return buff; } @@ -276,7 +280,7 @@ const char *inet_ntop(int pf, const void *binaddr, #if WITH_IP6 /* convert the IP6 socket address to human readable form. buff should be at - least 50 chars long */ + least 50 chars long. output includes the port number */ char *sockaddr_inet6_info(const struct sockaddr_in6 *sa, char *buff, size_t blen) { if (snprintf(buff, blen, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]:%hu", #if HAVE_IP6_SOCKADDR==0 @@ -434,6 +438,7 @@ int parseport(const char *portname, int ipproto) { } #endif /* WITH_TCP || WITH_UDP */ + #if WITH_IP4 || WITH_IP6 /* check the systems interfaces for ifname and return its index or -1 if no interface with this name was found */ @@ -500,3 +505,80 @@ int ifindex(const char *ifname, unsigned int *ifindex) { return 0; } #endif /* WITH_IP4 || WITH_IP6 */ + + +/* constructs an environment variable whose name is built from socats uppercase + program name, and underscore and varname; if a variable of this name already + exists a non zero value of overwrite lets the old value be overwritten. + returns 0 on success or <0 if an error occurred. */ +int xiosetenv(const char *varname, const char *value, int overwrite) { +# define XIO_ENVNAMELEN 256 + const char *progname; + char envname[XIO_ENVNAMELEN]; + size_t i, l; + + progname = diag_get_string('p'); + strncpy(envname, progname, XIO_ENVNAMELEN-1); + l = strlen(progname); + strncpy(envname+l, "_", XIO_ENVNAMELEN-1-l); + for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]); + strncpy(envname+l+1, varname, XIO_ENVNAMELEN-1-l); + if (Setenv(envname, value, overwrite) < 0) { + Warn3("setenv(\"...\", \"%s\", 1): %s", + envname, value, strerror(errno)); + Unsetenv(envname); /* dont want to have a wrong value */ + return -1; + } + return 0; +# undef XIO_ENVNAMELEN +} + +int xiosetenv2(const char *varname, const char *varname2, const char *value, + int overwrite) { +# define XIO_ENVNAMELEN 256 + const char *progname; + char envname[XIO_ENVNAMELEN]; + size_t i, l; + + progname = diag_get_string('p'); + strncpy(envname, progname, XIO_ENVNAMELEN-1); + l = strlen(progname); + strncpy(envname+l, "_", XIO_ENVNAMELEN-1-l); + l += 1; + strncpy(envname+l, varname, XIO_ENVNAMELEN-1-l); + l += strlen(varname); + strncpy(envname+l, "_", XIO_ENVNAMELEN-1-l); + l += 1; + strncpy(envname+l, varname2, XIO_ENVNAMELEN-1-l); + l += strlen(varname2); + for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]); + if (Setenv(envname, value, overwrite) < 0) { + Warn3("setenv(\"...\", \"%s\", 1): %s", + envname, value, strerror(errno)); + Unsetenv(envname); /* dont want to have a wrong value */ + return -1; + } + return 0; +# undef XIO_ENVNAMELEN +} + + +/* like xiosetenv(), but uses an unsigned long value */ +int xiosetenvulong(const char *varname, unsigned long value, int overwrite) { +# define XIO_LONGLEN 21 /* should suffice for 64bit longs with \0 */ + char envbuff[XIO_LONGLEN]; + + snprintf(envbuff, XIO_LONGLEN, "%lu", value); + return xiosetenv(varname, envbuff, overwrite); +# undef XIO_LONGLEN +} + +/* like xiosetenv(), but uses an unsigned short value */ +int xiosetenvushort(const char *varname, unsigned short value, int overwrite) { +# define XIO_SHORTLEN 11 /* should suffice for 32bit shorts with \0 */ + char envbuff[XIO_SHORTLEN]; + + snprintf(envbuff, XIO_SHORTLEN, "%hu", value); + return xiosetenv(varname, envbuff, overwrite); +# undef XIO_SHORTLEN +} diff --git a/sysutils.h b/sysutils.h index b9074b3..33cfc45 100644 --- a/sysutils.h +++ b/sysutils.h @@ -1,5 +1,5 @@ /* source: sysutils.h */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __sysutils_h_included @@ -97,4 +97,13 @@ extern int parseport(const char *portname, int proto); extern int ifindexbyname(const char *ifname); extern int ifindex(const char *ifname, unsigned int *ifindex); +extern int xiosetenv(const char *varname, const char *value, int overwrite); +extern int +xiosetenv2(const char *varname, const char *varname2, const char *value, + int overwrite); +extern int xiosetenvulong(const char *varname, unsigned long value, + int overwrite); +extern int xiosetenvushort(const char *varname, unsigned short value, + int overwrite); + #endif /* !defined(__sysutils_h_included) */ diff --git a/test.sh b/test.sh index 054673f..61455f8 100755 --- a/test.sh +++ b/test.sh @@ -48,13 +48,15 @@ opts="$opt_t $OPTS" export SOCAT_OPTS="$opts" #debug="1" debug= -TESTS="$@" +TESTS="$@"; export TESTS INTERFACE=eth0; # not used for function tests MCINTERFACE=lo # !!! Linux only #LOCALHOST=192.168.58.1 #LOCALHOST=localhost LOCALHOST=127.0.0.1 LOCALHOST6=[::1] +#PROTO=$(awk '{print($2);}' /etc/protocols |sort -n |tail -n 1) +#PROTO=$(($PROTO+1)) PROTO=$((144+RANDOM/2048)) PORT=12002 SOURCEPORT=2002 @@ -1912,14 +1914,21 @@ waitudp6port () { return 1 } +# we need this misleading function name for canonical reasons +waitunixport () { + waitfile "$1" "$2" "$3" +} + # wait until a filesystem entry exists waitfile () { local crit=-e case "X$1" in X-*) crit="$1"; shift ;; esac local file="$1" - local logic="$2" # 0..wait until gone; 1..wait until exists (default) + local logic="$2" # 0..wait until gone; 1..wait until exists (default); + # 2..wait until not empty local timeout="$3" [ "$logic" ] || logic=1 + [ "$logic" -eq 2 ] && crit=-s [ "$timeout" ] || timeout=5 while [ $timeout -gt 0 ]; do if [ \( \( $logic -ne 0 \) -a $crit "$file" \) -o \ @@ -7657,7 +7666,7 @@ printf "test $F_n $TEST... " $N $CMD1 2>"${te}1" & pid1="$!" waitip4port $ts1p 1 -usleep 100000 # give process a chance to add membership +usleep 100000 # give process a chance to add multicast membership echo "$da" |$CMD2 >>"$tf" 2>>"${te}2" rc2="$?" kill "$pid1" 2>/dev/null; wait; @@ -8147,13 +8156,12 @@ rc2=$? sleep 1 #read -p ">" l="$(childprocess $pid1)" -rcc=$? kill $pid1 2>/dev/null; wait if [ $rc2 -ne 0 ]; then - $PRINTF "$NO_RESULT\n" # already handled in test UDP4STREAM + $PRINTF "$NO_RESULT (client failed)\n" # already handled in test UDP4STREAM numCANT=$((numCANT+1)) elif ! echo "$da" |diff - "$tf" >"$tdiff"; then - $PRINTF "$NO_RESULT\n" # already handled in test UDP4STREAM + $PRINTF "$NO_RESULT (diff failed)\n" # already handled in test UDP4STREAM numCANT=$((numCANT+1)) elif $(isdefunct "$l"); then $PRINTF "$FAILED: $SOCAT:\n" @@ -8169,7 +8177,7 @@ fi ;; esac PORT=$((PORT+1)) N=$((N+1)) - +set +vx # there was a bug with udp-recvfrom and fork: terminating sub processes became # zombies because the master process caught SIGCHLD but did not wait() @@ -8198,7 +8206,6 @@ rc2=$? sleep 1 #read -p ">" l="$(childprocess $pid1)" -rcc=$? kill $pid1 2>/dev/null; wait if [ $rc2 -ne 0 ]; then $PRINTF "$NO_RESULT\n" # already handled in test UDP4DGRAM @@ -8513,6 +8520,398 @@ esac N=$((N+1)) +while read SCM_ENABLE SCM_RECV SCM_TYPE SCM_NAME SCM_VALUE +do + +# test: logging of ancillary message with ip-recvopt +NAME=UDP4SCM_$SCM_TYPE +case "$TESTS" in +*%functions%*|*%ip4%*|*%dgram%*|*%udp%*|*%udp4%*|*%recv%*|*%ancillary%*|*%$NAME%*) +#set -vx +TEST="$NAME: IPv4 ancillary messages" +# idea: start a socat process with udp4-recv:..,ip-recvopts and send it a packet +# with IP options (ip-options). check the info log for the appropriate output. +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +ts1p=$PORT; PORT=$((PORT+1)) +ts1a="127.0.0.1" +ts1="$ts1a:$ts1p" +CMD0="$SOCAT $opts -d -d -d -u UDP4-RECV:$ts1p,reuseaddr,$SCM_RECV -" +CMD1="$SOCAT $opts -u - UDP4-SENDTO:$ts1,$SCM_ENABLE" +printf "test $F_n $TEST... " $N +# is this option supported? +if $SOCAT -hhh |grep "[[:space:]]$SCM_RECV[[:space:]]" >/dev/null; then +$CMD0 >"$tf" 2>"${te}0" & +pid0="$!" +waitudp4port $ts1p 1 +echo "XYZ" |$CMD1 2>>"${te}1" +rc1="$?" +sleep 1 +i=0; while [ ! -s "${te}0" -a "$i" -lt 10 ]; do usleep 100000; i=$((i+1)); done +kill "$pid0" 2>/dev/null; wait +# do not show more messages than requested +case "$opts" in +*-d*-d*-d*-d*) LEVELS="[EWNID]" ;; +*-d*-d*-d*) LEVELS="[EWNI]" ;; +*-d*-d*) LEVELS="[EWN]" ;; +*-d*) LEVELS="[EW]" ;; +*) LEVELS="[E]" ;; +esac +if [ "$rc1" -ne 0 ]; then + $PRINTF "$FAILED: $SOCAT:\n" + echo "$CMD0 &" + echo "$CMD1" + grep " $LEVELS " "${te}0" + grep " $LEVELS " "${te}1" + numFAIL=$((numFAIL+1)) +elif ! grep "ancillary message: $SCM_TYPE: $SCM_NAME=$SCM_VALUE" ${te}0 >/dev/null; then + $PRINTF "$FAILED\n" + echo "$CMD0 &" + echo "$CMD1" + grep " $LEVELS " "${te}0" + grep " $LEVELS " "${te}1" + numFAIL=$((numFAIL+1)) +else + $PRINTF "$OK\n" + if [ -n "$debug" ]; then + grep " $LEVELS " "${te}0"; echo; grep " $LEVELS " "${te}1"; + fi + numOK=$((numOK+1)) +fi +else # option is not supported + $PRINTF "${YELLOW}$SCM_RECV not available${NORMAL}\n" + numCANT=$((numCANT+1)) +fi # option is not supported +set +vx +;; +esac +N=$((N+1)) + +done <<<"ip-options=x01000000 ip-recvopts IP_OPTIONS options x01000000 +, so-timestamp SCM_TIMESTAMP timestamp $(date '+%a %b %e %H:%M:.. %Y') +ip-ttl=53 ip-recvttl IP_TTL ttl 53 +ip-tos=7 ip-recvtos IP_TOS tos 7 +, ip-pktinfo IP_PKTINFO locaddr 127.0.0.1 +, ip-recvif IP_RECVIF if lo +, ip-recvdstaddr IP_RECVDSTADDR dstaddr 127.0.0.1" + + +# test: logging of ancillary message +while read PF KEYW ADDR IPPORT SCM_ENABLE SCM_RECV SCM_TYPE SCM_NAME ROOT SCM_VALUE +do +if [ -z "$PF" ]; then continue; fi +# +pf="$(echo $PF |tr A-Z a-z)" +proto="$(echo $KEYW |tr A-Z a-z)" +NAME=${KEYW}SCM_$SCM_TYPE +case "$TESTS" in +*%functions%*|*%$pf%*|*%dgram%*|*%udp%*|*%$proto%*|*%recv%*|*%ancillary%*|*%$ROOT%*|*%$NAME%*) +TEST="$NAME: $KEYW log ancillary message $SCM_TYPE $SCM_NAME" +# idea: start a socat process with *-RECV:..,... , ev. with ancillary message +# enabling option and send it a packet, ev. with some option. check the info log +# for the appropriate output. +if [ "$ROOT" = root -a $(id -u) -ne 0 -a "$withroot" -eq 0 ]; then + $PRINTF "test $F_n $TEST... ${YELLOW}must be root${NORMAL}\n" $N + numCANT=$((numCANT+1)) +else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +case "X$IPPORT" in + "XPORT") + tra="$PORT" # test recv address + tsa="$ADDR:$PORT" # test sendto address + PORT=$((PORT+1)) ;; + "XPROTO") + tra="$PROTO" # test recv address + tsa="$ADDR:$PROTO" # test sendto address + PROTO=$((PROTO+1)) ;; + *) + tra="$(eval echo "$ADDR")" # resolve $N + tsa="$tra" +esac +CMD0="$SOCAT $opts -d -d -d -u $KEYW-RECV:$tra,reuseaddr,$SCM_RECV -" +CMD1="$SOCAT $opts -u - $KEYW-SENDTO:$tsa,$SCM_ENABLE" +printf "test $F_n $TEST... " $N +# is this option supported? +if $SOCAT -hhh |grep "[[:space:]]$SCM_RECV[[:space:]]" >/dev/null; then +$CMD0 >"$tf" 2>"${te}0" & +pid0="$!" +wait${proto}port $tra 1 +echo "XYZ" |$CMD1 2>"${te}1" +rc1="$?" +sleep 1 +i=0; while [ ! -s "${te}0" -a "$i" -lt 10 ]; do usleep 100000; i=$((i+1)); done +kill "$pid0" 2>/dev/null; wait +# do not show more messages than requested +case "$opts" in +*-d*-d*-d*-d*) LEVELS="[EWNID]" ;; +*-d*-d*-d*) LEVELS="[EWNI]" ;; +*-d*-d*) LEVELS="[EWN]" ;; +*-d*) LEVELS="[EW]" ;; +*) LEVELS="[E]" ;; +esac +if [ "$rc1" -ne 0 ]; then + $PRINTF "$NO_RESULT: $SOCAT:\n" + echo "$CMD0 &" + echo "$CMD1" + grep " $LEVELS " "${te}0" + grep " $LEVELS " "${te}1" + numCANT=$((numCANT+1)) +elif ! grep "ancillary message: $SCM_TYPE: $SCM_NAME=$SCM_VALUE" ${te}0 >/dev/null; then + $PRINTF "$FAILED\n" + echo "$CMD0 &" + echo "$CMD1" + grep " $LEVELS " "${te}0" + grep " $LEVELS " "${te}1" + numFAIL=$((numFAIL+1)) +else + $PRINTF "$OK\n" + if [ -n "$debug" ]; then + grep " $LEVELS " "${te}0"; echo; grep " $LEVELS " "${te}1"; + fi + numOK=$((numOK+1)) +fi +set +vx +else # option is not supported + $PRINTF "${YELLOW}$SCM_RECV not available${NORMAL}\n" + numCANT=$((numCANT+1)) +fi # option is not supported +fi # must be root +;; +esac +N=$((N+1)) +# +done <<<" +IP4 UDP4 127.0.0.1 PORT ip-options=x01000000 ip-recvopts IP_OPTIONS options user x01000000 +IP4 UDP4 127.0.0.1 PORT , so-timestamp SCM_TIMESTAMP timestamp user $(date '+%a %b %e %H:%M:.. %Y') +IP4 UDP4 127.0.0.1 PORT ip-ttl=53 ip-recvttl IP_TTL ttl user 53 +IP4 UDP4 127.0.0.1 PORT ip-tos=7 ip-recvtos IP_TOS tos user 7 +IP4 UDP4 127.0.0.1 PORT , ip-pktinfo IP_PKTINFO locaddr user 127.0.0.1 +IP4 UDP4 127.0.0.1 PORT , ip-pktinfo IP_PKTINFO dstaddr user 127.0.0.1 +IP4 UDP4 127.0.0.1 PORT , ip-pktinfo IP_PKTINFO if user lo +IP4 UDP4 127.0.0.1 PORT , ip-recvif IP_RECVIF if user lo0 +IP4 UDP4 127.0.0.1 PORT , ip-recvdstaddr IP_RECVDSTADDR dstaddr user 127.0.0.1 +IP4 IP4 127.0.0.1 PROTO ip-options=x01000000 ip-recvopts IP_OPTIONS options root x01000000 +IP4 IP4 127.0.0.1 PROTO , so-timestamp SCM_TIMESTAMP timestamp root $(date '+%a %b %e %H:%M:.. %Y') +IP4 IP4 127.0.0.1 PROTO ip-ttl=53 ip-recvttl IP_TTL ttl root 53 +IP4 IP4 127.0.0.1 PROTO ip-tos=7 ip-recvtos IP_TOS tos root 7 +IP4 IP4 127.0.0.1 PROTO , ip-pktinfo IP_PKTINFO locaddr root 127.0.0.1 +IP4 IP4 127.0.0.1 PROTO , ip-pktinfo IP_PKTINFO dstaddr root 127.0.0.1 +IP4 IP4 127.0.0.1 PROTO , ip-pktinfo IP_PKTINFO if root lo +IP4 IP4 127.0.0.1 PROTO , ip-recvif IP_RECVIF if root lo0 +IP4 IP4 127.0.0.1 PROTO , ip-recvdstaddr IP_RECVDSTADDR dstaddr root 127.0.0.1 +IP6 UDP6 [::1] PORT , so-timestamp SCM_TIMESTAMP timestamp user $(date '+%a %b %e %H:%M:.. %Y') +IP6 UDP6 [::1] PORT , ipv6-recvpktinfo IPV6_PKTINFO dstaddr user [[]0000:0000:0000:0000:0000:0000:0000:0001[]] +IP6 UDP6 [::1] PORT ipv6-unicast-hops=35 ipv6-recvhoplimit IPV6_HOPLIMIT hoplimit user 35 +IP6 UDP6 [::1] PORT ipv6-tclass=0xaa ipv6-recvtclass IPV6_TCLASS tclass user xaa000000 +IP6 IP6 [::1] PROTO , so-timestamp SCM_TIMESTAMP timestamp root $(date '+%a %b %e %H:%M:.. %Y') +IP6 IP6 [::1] PROTO , ipv6-recvpktinfo IPV6_PKTINFO dstaddr root [[]0000:0000:0000:0000:0000:0000:0000:0001[]] +IP6 IP6 [::1] PROTO ipv6-unicast-hops=35 ipv6-recvhoplimit IPV6_HOPLIMIT hoplimit root 35 +IP6 IP6 [::1] PROTO ipv6-tclass=0xaa ipv6-recvtclass IPV6_TCLASS tclass root xaa000000 +UNIX UNIX $td/test\$N.server - , so-timestamp SCM_TIMESTAMP timestamp user $(date '+%a %b %e %H:%M:.. %Y') +" +# this one fails, appearently due to a Linux weakness: +# UNIX so-timestamp + + +# test: setting of environment variables that describe a stream socket +# connection: SOCAT_SOCKADDR, SOCAT_PEERADDR; and SOCAT_SOCKPORT, +# SOCAT_PEERPORT when applicable +while read KEYW TEST_SOCKADDR TEST_PEERADDR TEST_SOCKPORT TEST_PEERPORT; do +if [ -z "$KEYW" ]; then continue; fi +# +test_proto="$(echo $KEYW |tr A-Z a-z)" +NAME=${KEYW}LISTENENV +case "$TESTS" in +*%functions%*|*%ip4%*|*%ipapp%*|*%tcp%*|*%$test_proto%*|*%envvar%*|*%$NAME%*) +TEST="$NAME: $KEYW-LISTEN fills environment variables with socket addresses" +# have a server accepting a connection and invoking some shell code. The shell +# code extracts and prints the SOCAT related environment vars. +# outside code then checks if the environment contains the variables correctly +# describing the peer and local sockets. +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +TEST_SOCKADDR="$(echo $TEST_SOCKADDR |sed "s/\$N/$N/g")" # actual vars +tsa="$TEST_SOCKADDR" # test server address +tsp="$TEST_SOCKPORT" # test server port +if [ "$tsp" != ',' ]; then + tsa1="$tsp"; tsa2="$tsa"; tsa="$tsa:$tsp" # tsa2 used for server bind= +else + tsa1="$tsa"; tsa2= # tsa1 used for addr parameter +fi +TEST_PEERADDR="$(echo $TEST_PEERADDR |sed "s/\$N/$N/g")" # actual vars +tca="$TEST_PEERADDR" # test client address +tcp="$TEST_PEERPORT" # test client port +if [ "$tcp" != ',' ]; then + tca="$tca:$tcp" +fi +CMD0="$SOCAT $opts -u $KEYW-LISTEN:$tsa1 system:\"export -p\"" +CMD1="$SOCAT $opts -u - $KEYW-CONNECT:$tsa,bind=$tca" +printf "test $F_n $TEST... " $N +eval "$CMD0 2>\"${te}0\" >\"$tf\" &" +pid0=$! +wait${test_proto}port $tsa1 1 +echo |$CMD1 2>"${te}1" +rc1=$? +waitfile "$tf" 2 +kill $pid0 2>/dev/null; wait +#set -vx +if [ $rc1 != 0 ]; then + $PRINTF "$NO_RESULT (client failed):\n" + echo "$CMD0 &" + cat "${te}0" + echo "$CMD1" + cat "${te}1" + numCANT=$((numCANT+1)) +elif [ "$(grep SOCAT_SOCKADDR "${tf}" |sed -e 's/^[^=]*=//' |sed -e "s/[\"']//g")" = "$TEST_SOCKADDR" -a \ + "$(grep SOCAT_PEERADDR "${tf}" |sed -e 's/^[^=]*=//' -e "s/[\"']//g")" = "$TEST_PEERADDR" -a \ + \( "$TEST_SOCKPORT" = ',' -o "$(grep SOCAT_SOCKPORT "${tf}" |sed -e 's/^[^=]*=//' |sed -e 's/"//g')" = "$tsp" \) -a \ + \( "$TEST_PEERPORT" = ',' -o "$(grep SOCAT_PEERPORT "${tf}" |sed -e 's/^[^=]*=//' |sed -e 's/"//g')" = "$tcp" \) \ + ]; then + $PRINTF "$OK\n" + if [ "$debug" ]; then + echo "$CMD0 &" + cat "${te}0" + echo "$CMD1" + cat "${te}1" + fi + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD0 &" + cat "${te}0" + echo "$CMD1" + cat "${te}1" + numFAIL=$((numFAIL+1)) +fi +set +xv +;; +esac +N=$((N+1)) +# +done <<<" +TCP4 $LOCALHOST $SECONDADDR $PORT $((PORT+1)) +TCP6 [0000:0000:0000:0000:0000:0000:0000:0001] [0000:0000:0000:0000:0000:0000:0000:0001] $((PORT+2)) $((PORT+3)) +UDP6 [0000:0000:0000:0000:0000:0000:0000:0001] [0000:0000:0000:0000:0000:0000:0000:0001] $((PORT+6)) $((PORT+7)) +UNIX $td/test\$N.server $td/test\$N.client , , +" +# this one fails do to weakness in socats UDP4-LISTEN implementation: +#UDP4 $LOCALHOST $SECONDADDR $((PORT+4)) $((PORT+5)) + + +# test: environment variables from ancillary message +while read PF KEYW ADDR IPPORT SCM_ENABLE SCM_RECV SCM_ENVNAME ROOT SCM_VALUE +do +if [ -z "$PF" ]; then continue; fi +# +pf="$(echo $PF |tr A-Z a-z)" +proto="$(echo $KEYW |tr A-Z a-z)" +NAME=${KEYW}ENV_$SCM_ENVNAME +case "$TESTS" in +*%functions%*|*%$pf%*|*%dgram%*|*%udp%*|*%$proto%*|*%recv%*|*%ancillary%*|*%envvar%*|*%$ROOT%*|*%$NAME%*) +#set -vx +TEST="$NAME: $KEYW ancillary message brings $SCM_ENVNAME into environment" +# idea: start a socat process with *-RECVFROM:..,... , ev. with ancillary +# message enabling option and send it a packet, ev. with some option. write +# the resulting environment to a file and check its contents for the +# appropriate variable. +if [ "$ROOT" = root -a $(id -u) -ne 0 -a "$withroot" -eq 0 ]; then + $PRINTF "test $F_n $TEST... ${YELLOW}must be root${NORMAL}\n" $N + numCANT=$((numCANT+1)) +else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +case "X$IPPORT" in + "XPORT") + tra="$PORT" # test recv address + tsa="$ADDR:$PORT" # test sendto address + PORT=$((PORT+1)) ;; + "XPROTO") + tra="$PROTO" # test recv address + tsa="$ADDR:$PROTO" # test sendto address + PROTO=$((PROTO+1)) ;; + *) + tra="$(eval echo "$ADDR")" # resolve $N + tsa="$tra" +esac +#CMD0="$SOCAT $opts -u $KEYW-RECVFROM:$tra,reuseaddr,$SCM_RECV system:\"export -p\"" +CMD0="$SOCAT $opts -u $KEYW-RECVFROM:$tra,reuseaddr,$SCM_RECV system:\"echo \\\$SOCAT_$SCM_ENVNAME\"" +CMD1="$SOCAT $opts -u - $KEYW-SENDTO:$tsa,$SCM_ENABLE" +printf "test $F_n $TEST... " $N +# is this option supported? +if $SOCAT -hhh |grep "[[:space:]]$SCM_RECV[[:space:]]" >/dev/null; then +eval "$CMD0 >\"$tf\" 2>\"${te}0\" &" +pid0="$!" +wait${proto}port $tra 1 +echo "XYZ" |$CMD1 2>"${te}1" +rc1="$?" +waitfile "$tf" 2 +#i=0; while [ ! -s "${te}0" -a "$i" -lt 10 ]; do usleep 100000; i=$((i+1)); done +kill "$pid0" 2>/dev/null; wait +# do not show more messages than requested +#set -vx +if [ "$rc1" -ne 0 ]; then + $PRINTF "$NO_RESULT: $SOCAT:\n" + echo "$CMD0 &" + echo "$CMD1" + cat "${te}0" + cat "${te}1" + numCANT=$((numCANT+1)) +#elif ! egrep "^export SOCAT_$SCM_ENVNAME=[\"']?$SCM_VALUE[\"']?\$" ${tf} >/dev/null; then +#elif ! eval echo "$SOCAT_\$SCM_VALUE" |diff - "${tf}" >/dev/null; then +elif ! expr "$(cat "$tf")" : "$(eval echo "\$SCM_VALUE")" >/dev/null; then + $PRINTF "$FAILED\n" + echo "$CMD0 &" + echo "$CMD1" + cat "${te}0" + cat "${te}1" + numFAIL=$((numFAIL+1)) +else + $PRINTF "$OK\n" + if [ -n "$debug" ]; then + cat "${te}0"; echo; cat "${te}1"; + fi + numOK=$((numOK+1)) +fi +set +vx +else # option is not supported + $PRINTF "${YELLOW}$SCM_RECV not available${NORMAL}\n" + numCANT=$((numCANT+1)) +fi # option is not supported +fi # must be root +;; +esac +N=$((N+1)) +# +done <<<" +IP4 UDP4 127.0.0.1 PORT ip-options=x01000000 ip-recvopts IP_OPTIONS user x01000000 +IP4 UDP4 127.0.0.1 PORT , so-timestamp TIMESTAMP user $(date '+%a %b %e %H:%M:.. %Y'), ...... usecs +IP4 UDP4 127.0.0.1 PORT ip-ttl=53 ip-recvttl IP_TTL user 53 +IP4 UDP4 127.0.0.1 PORT ip-tos=7 ip-recvtos IP_TOS user 7 +IP4 UDP4 127.0.0.1 PORT , ip-pktinfo IP_LOCADDR user 127.0.0.1 +IP4 UDP4 127.0.0.1 PORT , ip-pktinfo IP_DSTADDR user 127.0.0.1 +IP4 UDP4 127.0.0.1 PORT , ip-pktinfo IP_IF user lo +IP4 UDP4 127.0.0.1 PORT , ip-recvif IP_RECVIF user lo0 +IP4 UDP4 127.0.0.1 PORT , ip-recvdstaddr IP_RECVDSTADDR user 127.0.0.1 +IP4 IP4 127.0.0.1 PROTO ip-options=x01000000 ip-recvopts IP_OPTIONS root x01000000 +IP4 IP4 127.0.0.1 PROTO , so-timestamp TIMESTAMP root $(date '+%a %b %e %H:%M:.. %Y'), ...... usecs +IP4 IP4 127.0.0.1 PROTO ip-ttl=53 ip-recvttl IP_TTL root 53 +IP4 IP4 127.0.0.1 PROTO ip-tos=7 ip-recvtos IP_TOS root 7 +IP4 IP4 127.0.0.1 PROTO , ip-pktinfo IP_LOCADDR root 127.0.0.1 +IP4 IP4 127.0.0.1 PROTO , ip-pktinfo IP_DSTADDR root 127.0.0.1 +IP4 IP4 127.0.0.1 PROTO , ip-pktinfo IP_IF root lo +IP4 IP4 127.0.0.1 PROTO , ip-recvif IP_RECVIF root lo0 +IP4 IP4 127.0.0.1 PROTO , ip-recvdstaddr IP_RECVDSTADDR root 127.0.0.1 +IP6 UDP6 [::1] PORT , ipv6-recvpktinfo IPV6_DSTADDR user [[]0000:0000:0000:0000:0000:0000:0000:0001[]] +IP6 UDP6 [::1] PORT ipv6-unicast-hops=35 ipv6-recvhoplimit IPV6_HOPLIMIT user 35 +IP6 UDP6 [::1] PORT ipv6-tclass=0xaa ipv6-recvtclass IPV6_TCLASS user xaa000000 +IP6 IP6 [::1] PROTO , ipv6-recvpktinfo IPV6_DSTADDR root [[]0000:0000:0000:0000:0000:0000:0000:0001[]] +IP6 IP6 [::1] PROTO ipv6-unicast-hops=35 ipv6-recvhoplimit IPV6_HOPLIMIT root 35 +IP6 IP6 [::1] PROTO ipv6-tclass=0xaa ipv6-recvtclass IPV6_TCLASS root xaa000000 +UNIX UNIX $td/test\$N.server - , so-timestamp TIMESTAMP user $(date '+%a %b %e %H:%M:.. %Y') +" + + echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed" if [ "$numFAIL" -gt 0 ]; then diff --git a/utils.c b/utils.c index eeca60c..57904d9 100644 --- a/utils.c +++ b/utils.c @@ -1,5 +1,5 @@ /* source: utils.c */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* useful additions to C library */ @@ -145,3 +145,17 @@ char *sanitize_string(const char *data, /* input data */ } return coded; } + +/* copies a substring out of a given buff + returns scratch, \0 terminated; scratch must provide len+1 bytes +*/ +char *xiosubstr(char *scratch, const char *str, size_t from, size_t len) { + char *scratch0 = scratch; + str += from; + while (len--) { + *scratch++ = *str++; + } + *scratch = '\0'; + return scratch0; +} + diff --git a/utils.h b/utils.h index 4488358..f942db1 100644 --- a/utils.h +++ b/utils.h @@ -1,5 +1,5 @@ /* source: utils.h */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __utils_h_included @@ -63,6 +63,7 @@ char *sanitize_string(const char *data, /* input data */ size_t bytes, /* length of input data, >=0 */ char *coded, /* output buffer, must be long enough */ int style); +extern +char *xiosubstr(char *scratch, const char *str, size_t from, size_t len); #endif /* !defined(__utils_h_included) */ - diff --git a/xio-ascii.c b/xio-ascii.c index 88909dc..7cd8103 100644 --- a/xio-ascii.c +++ b/xio-ascii.c @@ -1,5 +1,5 @@ /* source: xio-ascii.c */ -/* Copyright Gerhard Rieger 2002-2006 */ +/* Copyright Gerhard Rieger 2002-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains functions for text encoding, decoding, and conversions */ @@ -105,3 +105,52 @@ char * } return coded; } + +/* write the binary data to output buffer codbuff in human readable form. + bytes gives the length of the data, codlen the available space in codbuff. + coding specifies how the data is to be presented. Not much to select now. + returns a pointer to the first char in codbuff that has not been overwritten; + it might also point to the first char after the buffer! +*/ +static char * +_xiodump(const unsigned char *data, size_t bytes, char *codbuff, size_t codlen, + int coding) { + int start = 1; + int space = coding & 0xff; + + if (bytes <= 0) { codbuff[0] = '\0'; return codbuff; } + if (space == 0) space = -1; + if (0) { + ; /* for canonical reasons */ + } else if (1) { + /* simple hexadecimal output */ + if (bytes > 2*codlen+1) { + bytes = (codlen-1)/2; + } + *codbuff++ = 'x'; --codlen; + while (bytes-- > 0) { + if (start == 0 && space == 0) { + *codbuff++ = ' '; + space = (coding & 0xff); + } + codbuff += sprintf(codbuff, "%02x", *data++); + start = 0; + } + } + return codbuff; +} + +/* write the binary data to codbuff in human readable form. + bytes gives the length of the data, codlen the available space in codbuff. + coding specifies how the data is to be presented. Not much to select now. + null terminates the output. returns a pointer to the output string. +*/ +char * +xiodump(const unsigned char *data, size_t bytes, char *codbuff, size_t codlen, + int coding) { + char *result; + + result = _xiodump(data, bytes, codbuff, codlen-1, coding); + *result = '\0'; + return codbuff; +} diff --git a/xio-ascii.h b/xio-ascii.h index 875623d..bcb760f 100644 --- a/xio-ascii.h +++ b/xio-ascii.h @@ -1,5 +1,5 @@ /* source: xio-ascii.h */ -/* Copyright Gerhard Rieger 2002-2006 */ +/* Copyright Gerhard Rieger 2002-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __xio_ascii_h_included @@ -17,4 +17,8 @@ extern char *xiosanitize(const char *data, /* input data */ extern char * xiohexdump(const unsigned char *data, size_t bytes, char *coded); +extern char * +xiodump(const unsigned char *data, size_t bytes, char *coded, size_t codlen, + int coding); + #endif /* !defined(__xio_ascii_h_included) */ diff --git a/xio-ip.c b/xio-ip.c index 77dd71d..f04d1fa 100644 --- a/xio-ip.c +++ b/xio-ip.c @@ -9,6 +9,8 @@ #if _WITH_IP4 || _WITH_IP6 #include "xioopen.h" + +#include "xio-ascii.h" #include "xio-socket.h" #include "xio-ip.h" #include "xio-ip6.h" @@ -25,7 +27,7 @@ const struct optdesc opt_ip_pktinfo = { "ip-pktinfo", "pktinfo", OPT_IP_PKTINF #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 +#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 @@ -65,6 +67,12 @@ 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_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 #if HAVE_RESOLV_H const struct optdesc opt_res_debug = { "res-debug", NULL, OPT_RES_DEBUG, GROUP_SOCK_IP, PH_INIT, TYPE_BOOL, OFUNC_OFFSET_MASKS, (size_t)&((xiosingle_t *)0)->para.socket.ip.res_opts, sizeof(unsigned long), RES_DEBUG }; @@ -509,4 +517,116 @@ int parserange(const char *rangename, int pf, union xiorange_union *range) { return 0; } + +#if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) +/* these are valid for IPv4 and IPv6 */ +int xiolog_ancillary_ip(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 = NULL, *cmsgenvn = NULL, *cmsgfmt = NULL; + size_t msglen; + char scratch1[16]; /* can hold an IPv4 address in ASCII */ + char scratch2[16]; + char scratch3[16]; + + msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg); + envbuff[0] = '\0'; + switch (cmsg->cmsg_type) { + default: + *num = 1; + strncpy(typbuff, "IP", typlen); + snprintf(nambuff, namlen, "type_%u", cmsg->cmsg_type); + xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); + return STAT_OK; +#ifdef IP_PKTINFO + case IP_PKTINFO: { + struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg); + *num = 3; + strncpy(typbuff, "IP_PKTINFO", typlen); + 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', + inet4addr_info(ntohl(pktinfo->ipi_spec_dst.s_addr), scratch2, sizeof(scratch2)), '\0', + inet4addr_info(ntohl(pktinfo->ipi_addr.s_addr), scratch3, sizeof(scratch3))); + } + return STAT_OK; +#endif /* IP_PKTINFO */ +#ifdef IP_RECVERR + case IP_RECVERR: { + struct sock_extended_err *err = + (struct sock_extended_err *)CMSG_DATA(cmsg); + *num = 6; + strncpy(typbuff, "IP_RECVERR", typlen); + 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->ee_errno, '\0', err->ee_origin, '\0', err->ee_type, '\0', + err->ee_code, '\0', err->ee_info, '\0', err->ee_data); + return STAT_OK; + } +#endif /* IP_RECVERR */ +#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; + strncpy(typbuff, "IP_RECVIF", typlen); + strncpy(nambuff, "if", namlen); + strncpy(envbuff, "IP_IF", envlen); + strncpy(valbuff, + xiosubstr(scratch1, sadl->sdl_data, 0, sadl->sdl_nlen), vallen); + return STAT_OK; + } +#endif /* defined(IP_RECVIF) */ +#ifdef IP_RECVDSTADDR + case IP_RECVDSTADDR: + *num = 1; + strncpy(typbuff, "IP_RECVDSTADDR", typlen); + strncpy(nambuff, "dstaddr", namlen); + strncpy(envbuff, "IP_DSTADDR", envlen); + inet4addr_info(ntohl(*(uint32_t *)CMSG_DATA(cmsg)), valbuff, vallen); + return STAT_OK; +#endif + case IP_OPTIONS: + case IP_RECVOPTS: + cmsgtype = "IP_OPTIONS"; cmsgname = "options"; cmsgfmt = NULL; break; + case IP_TOS: + cmsgtype = "IP_TOS"; cmsgname = "tos"; cmsgfmt = "%u"; break; + case IP_TTL: /* Linux */ +#ifdef IP_RECVTTL + case IP_RECVTTL: /* FreeBSD */ +#endif + cmsgtype = "IP_TTL"; cmsgname = "ttl"; cmsgfmt = "%u"; break; + } + /* when we come here we provide a single parameter + with type in cmsgtype, name in cmsgname, printf format in cmsgfmt */ + *num = 1; + if (strlen(cmsgtype) >= typlen) Fatal("buff too short"); + strncpy(typbuff, cmsgtype, typlen); + if (strlen(cmsgname) >= namlen) Fatal("buff too short"); + strncpy(nambuff, cmsgname, namlen); + if (cmsgenvn) { + if (strlen(cmsgenvn) >= envlen) Fatal("buff too short"); + strncpy(envbuff, cmsgenvn, envlen); + } else { + envbuff[0] = '\0'; + } + if (cmsgfmt != NULL) { + snprintf(valbuff, vallen, cmsgfmt, *(unsigned char *)CMSG_DATA(cmsg)); + } else { + xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); + } + return STAT_OK; +} +#endif /* defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) */ + #endif /* _WITH_IP4 || _WITH_IP6 */ diff --git a/xio-ip.h b/xio-ip.h index 4d68012..6522f49 100644 --- a/xio-ip.h +++ b/xio-ip.h @@ -1,5 +1,5 @@ /* source: xio-ip.h */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __xio_ip_h_included @@ -24,6 +24,8 @@ 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_recvdstaddr; +extern const struct optdesc opt_ip_recvif; extern const struct optdesc opt_res_debug; extern const struct optdesc opt_res_aaonly; @@ -44,5 +46,11 @@ int xioparsenetwork(const char *rangename, int pf, union xiorange_union *range); extern int parserange(const char *rangename, int pf, union xiorange_union *range); +extern +int xiolog_ancillary_ip(struct cmsghdr *cmsg, int *num, + char *typbuff, int typlen, + char *nambuff, int namlen, + char *envbuff, int envlen, + char *valbuff, int vallen); #endif /* !defined(__xio_ip_h_included) */ diff --git a/xio-ip4.c b/xio-ip4.c index 6e8ff23..27b9128 100644 --- a/xio-ip4.c +++ b/xio-ip4.c @@ -1,5 +1,5 @@ /* source: xio-ip4.c */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains the source for IP4 related functions */ @@ -43,4 +43,41 @@ int xiocheckrange_ip4(struct sockaddr_in *pa, struct xiorange_ip4 *range) { return 0; } +/* returns information that can be used for constructing an environment + variable describing the socket address. + if idx is 0, this function writes "ADDR" into namebuff and the IP address + into valuebuff, and returns 1 (which means that one more info is there). + if idx is 1, it writes "PORT" into namebuff and the port number into + valuebuff, and returns 0 (no more info) + namelen and valuelen contain the max. allowed length of output chars in the + respective buffer. + on error this function returns -1. +*/ +int +xiosetsockaddrenv_ip4(int idx, char *namebuff, size_t namelen, + char *valuebuff, size_t valuelen, + struct sockaddr_in *sa, int ipproto) { + switch (idx) { + case 0: + strcpy(namebuff, "ADDR"); + strcpy(valuebuff, + inet4addr_info(ntohl(sa->sin_addr.s_addr), valuebuff, valuelen)); + switch (ipproto) { + case IPPROTO_TCP: + case IPPROTO_UDP: +#ifdef IPPROTO_SCTP + case IPPROTO_SCTP: +#endif + return 1; /* there is port information to also be retrieved */ + default: + return 0; /* no port info coming */ + } + case 1: + strcpy(namebuff, "PORT"); + snprintf(valuebuff, valuelen, "%u", ntohs(sa->sin_port)); + return 0; + } + return -1; +} + #endif /* WITH_IP4 */ diff --git a/xio-ip4.h b/xio-ip4.h index 9357dcc..15aefed 100644 --- a/xio-ip4.h +++ b/xio-ip4.h @@ -1,5 +1,5 @@ /* source: xio-ip4.h */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __xio_ip4_h_included @@ -9,5 +9,9 @@ extern const struct optdesc opt_ip4_add_membership; extern int xiocheckrange_ip4(struct sockaddr_in *pa, struct xiorange_ip4 *range); +extern int +xiosetsockaddrenv_ip4(int idx, char *namebuff, size_t namelen, + char *valuebuff, size_t valuelen, + struct sockaddr_in *sa, int ipproto); #endif /* !defined(__xio_ip4_h_included) */ diff --git a/xio-ip6.c b/xio-ip6.c index 0bf8577..870e80e 100644 --- a/xio-ip6.c +++ b/xio-ip6.c @@ -1,5 +1,5 @@ /* source: xio-ip6.c */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains the source for IP6 related functions */ @@ -9,17 +9,47 @@ #if WITH_IP6 #include "xioopen.h" +#include "xio-ascii.h" #include "xio-socket.h" #include "xio-ip.h" /* xiogetaddrinfo() */ #include "xio-ip6.h" + +static char *inet6addr_info(const struct in6_addr *sa, char *buff, size_t blen); + + #ifdef IPV6_V6ONLY const struct optdesc opt_ipv6_v6only = { "ipv6-v6only", "ipv6only", OPT_IPV6_V6ONLY, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_V6ONLY }; #endif #ifdef IPV6_JOIN_GROUP const struct optdesc opt_ipv6_join_group = { "ipv6-join-group", "join-group", OPT_IPV6_JOIN_GROUP, GROUP_SOCK_IP6, PH_PASTBIND, TYPE_IP_MREQN, OFUNC_SOCKOPT, SOL_IPV6, IPV6_JOIN_GROUP }; #endif +const struct optdesc opt_ipv6_pktinfo = { "ipv6-pktinfo", "pktinfo", OPT_IPV6_PKTINFO, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_PKTINFO }; +const struct optdesc opt_ipv6_recvpktinfo = { "ipv6-recvpktinfo", "recvpktinfo", OPT_IPV6_RECVPKTINFO, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVPKTINFO }; +const struct optdesc opt_ipv6_rthdr = { "ipv6-rthdr", "rthdr", OPT_IPV6_RTHDR, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RTHDR }; +const struct optdesc opt_ipv6_recvrthdr = { "ipv6-recvrthdr", "recvrthdr", OPT_IPV6_RECVRTHDR, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVRTHDR }; +#ifdef IPV6_AUTHHDR +const struct optdesc opt_ipv6_authhdr = { "ipv6-authhdr", "authhdr", OPT_IPV6_AUTHHDR, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_AUTHHDR }; +#endif +const struct optdesc opt_ipv6_dstopts = { "ipv6-dstopts", "dstopts", OPT_IPV6_DSTOPTS, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_DSTOPTS }; +const struct optdesc opt_ipv6_recvdstopts = { "ipv6-recvdstopts", "recvdstopts", OPT_IPV6_RECVDSTOPTS, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVDSTOPTS }; +const struct optdesc opt_ipv6_hopopts = { "ipv6-hopopts", "hopopts", OPT_IPV6_HOPOPTS, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_HOPOPTS }; +const struct optdesc opt_ipv6_recvhopopts = { "ipv6-recvhopopts", "recvhopopts", OPT_IPV6_RECVHOPOPTS, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVHOPOPTS }; +#ifdef IPV6_FLOWINFO /* is in linux/in6.h */ +const struct optdesc opt_ipv6_flowinfo= { "ipv6-flowinfo","flowinfo",OPT_IPV6_FLOWINFO,GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_FLOWINFO }; +#endif +const struct optdesc opt_ipv6_hoplimit= { "ipv6-hoplimit","hoplimit",OPT_IPV6_HOPLIMIT,GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_HOPLIMIT }; +const struct optdesc opt_ipv6_unicast_hops= { "ipv6-unicast-hops","unicast-hops",OPT_IPV6_UNICAST_HOPS,GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_UNICAST_HOPS }; +const struct optdesc opt_ipv6_recvhoplimit= { "ipv6-recvhoplimit","recvhoplimit",OPT_IPV6_RECVHOPLIMIT,GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVHOPLIMIT }; +#ifdef IPV6_RECVERR +const struct optdesc opt_ipv6_recverr = { "ipv6-recverr", "recverr", OPT_IPV6_RECVERR, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVERR }; +#endif +const struct optdesc opt_ipv6_tclass = { "ipv6-tclass", "tclass", OPT_IPV6_TCLASS, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_TCLASS }; +const struct optdesc opt_ipv6_recvtclass = { "ipv6-recvtclass", "recvtclass", OPT_IPV6_RECVTCLASS, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVTCLASS }; +#ifdef IPV6_RECVPATHMTU +const struct optdesc opt_ipv6_recvpathmtu = { "ipv6-recvpathmtu", "recvpathmtu", OPT_IPV6_RECVPATHMTU, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVPATHMTU }; +#endif int xioparsenetwork_ip6(const char *rangename, struct xiorange_ip6 *range) { char *delimpos; /* absolute address of delimiter */ @@ -142,4 +172,200 @@ int xiocheckrange_ip6(struct sockaddr_in6 *pa, struct xiorange_ip6 *range) { return 0; } + +#if defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) +/* provides info about the ancillary message */ +int xiolog_ancillary_ip6(struct cmsghdr *cmsg, int *num, + char *typbuff, int typlen, + char *nambuff, int namlen, + char *envbuff, int envlen, + char *valbuff, int vallen) { + char scratch1[42]; /* can hold an IPv6 address in ASCII */ + char scratch2[32]; + size_t msglen; + + *num = 1; /* good for most message types */ + msglen = cmsg->cmsg_len-((char *)CMSG_DATA(cmsg)-(char *)cmsg); + envbuff[0] = '\0'; + switch (cmsg->cmsg_type) { + case IPV6_PKTINFO: { + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg); + *num = 2; + strncpy(typbuff, "IPV6_PKTINFO", typlen); + snprintf(nambuff, namlen, "%s%c%s", "dstaddr", '\0', "if"); + snprintf(envbuff, envlen, "%s%c%s", "IPV6_DSTADDR", '\0', "IPV6_IF"); + snprintf(valbuff, vallen, "%s%c%s", + inet6addr_info(&pktinfo->ipi6_addr, scratch1, sizeof(scratch1)), + '\0', xiogetifname(pktinfo->ipi6_ifindex, scratch2, -1)); + } + return STAT_OK; + case IPV6_HOPLIMIT: + strncpy(typbuff, "IPV6_HOPLIMIT", typlen); + strncpy(nambuff, "hoplimit", namlen); + snprintf(valbuff, vallen, "%d", *(int *)CMSG_DATA(cmsg)); + return STAT_OK; + case IPV6_RTHDR: + strncpy(typbuff, "IPV6_RTHDR", typlen); + strncpy(nambuff, "rthdr", namlen); + xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); + return STAT_OK; +#ifdef IPV6_AUTHHDR + case IPV6_AUTHHDR: + strncpy(typbuff, "IPV6_AUTHHDR", typlen); + strncpy(nambuff, "authhdr", namlen); + xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); + return STAT_OK; +#endif + case IPV6_DSTOPTS: + strncpy(typbuff, "IPV6_DSTOPTS", typlen); + strncpy(nambuff, "dstopts", namlen); + xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); + return STAT_OK; + case IPV6_HOPOPTS: + strncpy(typbuff, "IPV6_HOPOPTS", typlen); + strncpy(nambuff, "hopopts", namlen); + xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); + return STAT_OK; +#ifdef IPV6_FLOWINFO + case IPV6_FLOWINFO: + strncpy(typbuff, "IPV6_FLOWINFO", typlen); + strncpy(nambuff, "flowinfo", namlen); + xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); + return STAT_OK; +#endif + case IPV6_TCLASS: + strncpy(typbuff, "IPV6_TCLASS", typlen); + strncpy(nambuff, "tclass", namlen); + xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); + return STAT_OK; + default: + snprintf(typbuff, typlen, "IPV6.%u", cmsg->cmsg_type); + strncpy(nambuff, "data", namlen); + xiodump(CMSG_DATA(cmsg), msglen, valbuff, vallen, 0); + return STAT_OK; + } + return STAT_OK; +} +#endif /* defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA) */ + + +/* convert the IP6 socket address to human readable form. buff should be at + least 50 chars long. output includes the port number */ +static char *inet6addr_info(const struct in6_addr *sa, char *buff, size_t blen) { + if (snprintf(buff, blen, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]", +#if HAVE_IP6_SOCKADDR==0 + (sa->s6_addr[0]<<8)+sa->s6_addr[1], + (sa->s6_addr[2]<<8)+sa->s6_addr[3], + (sa->s6_addr[4]<<8)+sa->s6_addr[5], + (sa->s6_addr[6]<<8)+sa->s6_addr[7], + (sa->s6_addr[8]<<8)+sa->s6_addr[9], + (sa->s6_addr[10]<<8)+sa->s6_addr[11], + (sa->s6_addr[12]<<8)+sa->s6_addr[13], + (sa->s6_addr[14]<<8)+sa->s6_addr[15] +#elif HAVE_IP6_SOCKADDR==1 + ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[0]), + ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[1]), + ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[2]), + ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[3]), + ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[4]), + ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[5]), + ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[6]), + ntohs(((unsigned short *)&sa->u6_addr.u6_addr16)[7]) +#elif HAVE_IP6_SOCKADDR==2 + ntohs(((unsigned short *)&sa->u6_addr16)[0]), + ntohs(((unsigned short *)&sa->u6_addr16)[1]), + ntohs(((unsigned short *)&sa->u6_addr16)[2]), + ntohs(((unsigned short *)&sa->u6_addr16)[3]), + ntohs(((unsigned short *)&sa->u6_addr16)[4]), + ntohs(((unsigned short *)&sa->u6_addr16)[5]), + ntohs(((unsigned short *)&sa->u6_addr16)[6]), + ntohs(((unsigned short *)&sa->u6_addr16)[7]) +#elif HAVE_IP6_SOCKADDR==3 + ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[0]), + ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[1]), + ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[2]), + ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[3]), + ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[4]), + ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[5]), + ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[6]), + ntohs(((unsigned short *)&sa->in6_u.u6_addr16)[7]) +#elif HAVE_IP6_SOCKADDR==4 + (sa->_S6_un._S6_u8[0]<<8)|(sa->_S6_un._S6_u8[1]&0xff), + (sa->_S6_un._S6_u8[2]<<8)|(sa->_S6_un._S6_u8[3]&0xff), + (sa->_S6_un._S6_u8[4]<<8)|(sa->_S6_un._S6_u8[5]&0xff), + (sa->_S6_un._S6_u8[6]<<8)|(sa->_S6_un._S6_u8[7]&0xff), + (sa->_S6_un._S6_u8[8]<<8)|(sa->_S6_un._S6_u8[9]&0xff), + (sa->_S6_un._S6_u8[10]<<8)|(sa->_S6_un._S6_u8[11]&0xff), + (sa->_S6_un._S6_u8[12]<<8)|(sa->_S6_un._S6_u8[13]&0xff), + (sa->_S6_un._S6_u8[14]<<8)|(sa->_S6_un._S6_u8[15]&0xff) +#elif HAVE_IP6_SOCKADDR==5 + ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[0]), + ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[1]), + ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[2]), + ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[3]), + ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[4]), + ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[5]), + ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[6]), + ntohs(((unsigned short *)&sa->__u6_addr.__u6_addr16)[7]) +#endif + ) < 0) { + Warn("sockaddr_inet6_info(): buffer too short"); + buff[blen-1] = '\0'; + } + return buff; +} + + +/* returns information that can be used for constructing an environment + variable describing the socket address. + if idx is 0, this function writes "ADDR" into namebuff and the IP address + into valuebuff, and returns 1 (which means that one more info is there). + if idx is 1, it writes "PORT" into namebuff and the port number into + valuebuff, and returns 0 (no more info) + namelen and valuelen contain the max. allowed length of output chars in the + respective buffer. + on error this function returns -1. +*/ +int +xiosetsockaddrenv_ip6(int idx, char *namebuff, size_t namelen, + char *valuebuff, size_t valuelen, + struct sockaddr_in6 *sa, int ipproto) { + switch (idx) { + case 0: + strcpy(namebuff, "ADDR"); + snprintf(valuebuff, valuelen, "[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]", + (sa->sin6_addr.s6_addr[0]<<8)+ + sa->sin6_addr.s6_addr[1], + (sa->sin6_addr.s6_addr[2]<<8)+ + sa->sin6_addr.s6_addr[3], + (sa->sin6_addr.s6_addr[4]<<8)+ + sa->sin6_addr.s6_addr[5], + (sa->sin6_addr.s6_addr[6]<<8)+ + sa->sin6_addr.s6_addr[7], + (sa->sin6_addr.s6_addr[8]<<8)+ + sa->sin6_addr.s6_addr[9], + (sa->sin6_addr.s6_addr[10]<<8)+ + sa->sin6_addr.s6_addr[11], + (sa->sin6_addr.s6_addr[12]<<8)+ + sa->sin6_addr.s6_addr[13], + (sa->sin6_addr.s6_addr[14]<<8)+ + sa->sin6_addr.s6_addr[15]); + switch (ipproto) { + case IPPROTO_TCP: + case IPPROTO_UDP: +#ifdef IPPROTO_SCTP + case IPPROTO_SCTP: +#endif + return 1; /* there is port information to also be retrieved */ + default: + return 0; /* no port info coming */ + } + case 1: + strcpy(namebuff, "PORT"); + snprintf(valuebuff, valuelen, "%u", ntohs(sa->sin6_port)); + return 0; + } + return -1; +} + #endif /* WITH_IP6 */ diff --git a/xio-ip6.h b/xio-ip6.h index 431d4d1..2e86725 100644 --- a/xio-ip6.h +++ b/xio-ip6.h @@ -1,5 +1,5 @@ /* source: xio-ip6.h */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __xio_ip6_h_included @@ -9,6 +9,23 @@ extern const struct optdesc opt_ipv6_v6only; extern const struct optdesc opt_ipv6_join_group; +extern const struct optdesc opt_ipv6_pktinfo; +extern const struct optdesc opt_ipv6_recvpktinfo; +extern const struct optdesc opt_ipv6_rthdr; +extern const struct optdesc opt_ipv6_recvrthdr; +extern const struct optdesc opt_ipv6_authhdr; +extern const struct optdesc opt_ipv6_dstopts; +extern const struct optdesc opt_ipv6_recvdstopts; +extern const struct optdesc opt_ipv6_hopopts; +extern const struct optdesc opt_ipv6_unicast_hops; +extern const struct optdesc opt_ipv6_recvhopopts; +extern const struct optdesc opt_ipv6_flowinfo; +extern const struct optdesc opt_ipv6_hoplimit; +extern const struct optdesc opt_ipv6_recvhoplimit; +extern const struct optdesc opt_ipv6_recverr; +extern const struct optdesc opt_ipv6_tclass; +extern const struct optdesc opt_ipv6_recvtclass; +extern const struct optdesc opt_ipv6_recvpathmtu; extern int xioparsenetwork_ip6(const char *rangename, struct xiorange_ip6 *range); @@ -16,6 +33,16 @@ extern int xiorange_ip6andmask(struct xiorange_ip6 *range); extern int xiocheckrange_ip6(struct sockaddr_in6 *pa, struct xiorange_ip6 *range); +extern +int xiolog_ancillary_ip6(struct cmsghdr *cmsg, int *num, + char *typbuff, int typlen, + char *nambuff, int namlen, + char *envbuff, int envlen, + char *valbuff, int vallen); +extern int +xiosetsockaddrenv_ip6(int idx, char *namebuff, size_t namelen, + char *valuebuff, size_t valuelen, + struct sockaddr_in6 *sa, int ipproto); #endif /* WITH_IP6 */ diff --git a/xio-ipapp.c b/xio-ipapp.c index 1a3effd..213a76f 100644 --- a/xio-ipapp.c +++ b/xio-ipapp.c @@ -104,30 +104,24 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, #if WITH_RETRY if (dofork) { pid_t pid; - while ((pid = Fork()) < 0) { - int level = E_ERROR; - if (xfd->forever || --xfd->retry) { - level = E_WARN; /* most users won't expect a problem here, + int level = E_ERROR; + if (xfd->forever || xfd->retry) { + level = E_WARN; /* most users won't expect a problem here, so Notice is too weak */ - } - Msg1(level, "fork(): %s", strerror(errno)); - if (xfd->forever || xfd->retry) { - dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); + } + while ((pid = xio_fork(false, level)) < 0) { + if (xfd->forever || --xfd->retry) { Nanosleep(&xfd->intervall, NULL); continue; } return STAT_RETRYLATER; } - if (pid == 0) { /* child process */ - Info1("just born: TCP client process "F_pid, Getpid()); - /* drop parents locks, reset FIPS... */ - if (xio_forked_inchild() != 0) { - Exit(1); - } + if (pid == 0) { /* child process */ + xfd->forever = false; xfd->retry = 0; break; } + /* parent process */ - Notice1("forked off child process "F_pid, pid); Close(xfd->fd); /* with and without retry */ Nanosleep(&xfd->intervall, NULL); @@ -139,6 +133,7 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, break; } } while (true); + /* only "active" process breaks (master without fork, or child) */ if ((result = _xio_openlate(xfd, opts)) < 0) { return result; diff --git a/xio-listen.c b/xio-listen.c index 38a65c6..0acb3dd 100644 --- a/xio-listen.c +++ b/xio-listen.c @@ -98,9 +98,14 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl int backlog = 5; /* why? 1 seems to cause problems under some load */ char *rangename; bool dofork = false; - pid_t pid; /* mostly int; only used with fork */ char infobuff[256]; char lisname[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 pas = sizeof(_peername); /* peer address size */ + socklen_t las = sizeof(_sockname); /* local address size */ int result; retropt_bool(opts, OPT_FORK, &dofork); @@ -199,12 +204,6 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl char peername[256]; char sockname[256]; int ps; /* peer socket */ - union sockaddr_union _peername; - union sockaddr_union _sockname; - union sockaddr_union *pa = &_peername; /* peer address */ - union sockaddr_union *la = &_sockname; /* local address */ - socklen_t pas = sizeof(_peername); /* peer address size */ - socklen_t las = sizeof(_sockname); /* local address size */ salen = sizeof(struct sockaddr); do { @@ -232,16 +231,18 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl if (Getpeername(ps, &pa->soa, &pas) < 0) { Warn4("getpeername(%d, %p, {"F_socklen"}): %s", ps, pa, pas, strerror(errno)); + pa = NULL; } if (Getsockname(ps, &la->soa, &las) < 0) { Warn4("getsockname(%d, %p, {"F_socklen"}): %s", - ps, pa, pas, strerror(errno)); + ps, la, las, strerror(errno)); + la = NULL; } Notice2("accepting connection from %s on %s", - sockaddr_info(&pa->soa, pas, peername, sizeof(peername)), - sockaddr_info(&la->soa, las, sockname, sizeof(sockname))); + sockaddr_info(pa?&pa->soa:NULL, pas, peername, sizeof(peername)), + sockaddr_info(pa?&la->soa:NULL, las, sockname, sizeof(sockname))); - if (xiocheckpeer(xfd, pa, la) < 0) { + if (pa != NULL && la != NULL && xiocheckpeer(xfd, pa, la) < 0) { if (Shutdown(ps, 2) < 0) { Info2("shutdown(%d, 2): %s", ps, strerror(errno)); } @@ -253,16 +254,20 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl infobuff, sizeof(infobuff))); applyopts(xfd->fd, opts, PH_FD); - applyopts(xfd->fd, opts, PH_CONNECTED); if (dofork) { - if ((pid = Fork()) < 0) { - Msg1(level, "fork(): %s", strerror(errno)); + pid_t pid; /* mostly int; only used with fork */ + if ((pid = xio_fork(false, level==E_ERROR?level:E_WARN)) < 0) { Close(xfd->fd); return STAT_RETRYLATER; } if (pid == 0) { /* child */ + pid_t cpid = Getpid(); + + Info1("just born: client process "F_pid, cpid); + xiosetenvulong("PID", cpid, 1); + if (Close(xfd->fd) < 0) { Info2("close(%d): %s", xfd->fd, strerror(errno)); } @@ -270,16 +275,10 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl #if WITH_RETRY /* !? */ - xfd->retry = 0; - xfd->forever = 0; + xfd->forever = false; xfd->retry = 0; level = E_ERROR; #endif /* WITH_RETRY */ - /* drop parents locks, reset FIPS... */ - if (xio_forked_inchild() != 0) { - Exit(1); - } - #if WITH_UNIX /* with UNIX sockets: only listening parent is allowed to remove the socket file */ @@ -295,7 +294,6 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl if (Close(ps) < 0) { Info2("close(%d): %s", ps, strerror(errno)); } - Notice1("forked off child process "F_pid, pid); Info("still listening"); } else { if (Close(xfd->fd) < 0) { @@ -308,6 +306,10 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl if ((result = _xio_openlate(xfd, opts)) < 0) return result; + /* set the env vars describing the local and remote sockets */ + xiosetsockaddrenv("SOCK", la, las, proto); + xiosetsockaddrenv("PEER", pa, pas, proto); + return 0; } diff --git a/xio-openssl.c b/xio-openssl.c index 4fa17bc..d03f291 100644 --- a/xio-openssl.c +++ b/xio-openssl.c @@ -275,32 +275,23 @@ static int #if WITH_RETRY if (dofork) { pid_t pid; - while ((pid = Fork()) < 0) { - int level = E_ERROR; - if (xfd->forever || xfd->retry) { - level = E_WARN; - } - Msg1(level, "fork(): %s", strerror(errno)); - if (xfd->forever || xfd->retry) { - Nanosleep(&xfd->intervall, NULL); - --xfd->retry; - continue; + int level = E_ERROR; + if (xfd->forever || xfd->retry) { + level = E_WARN; + } + while ((pid = xio_fork(false, level)) < 0) { + if (xfd->forever || --xfd->retry) { + Nanosleep(&xfd->intervall, NULL); continue; } return STAT_RETRYLATER; } - if (pid == 0) { /* child process */ - Info1("just born: OpenSSL client process "F_pid, Getpid()); - /* drop parents locks, reset FIPS... */ - if (xio_forked_inchild() != 0) { - Exit(1); - } - xfd->forever = false; - xfd->retry = 0; + if (pid == 0) { /* child process */ + xfd->forever = false; xfd->retry = 0; break; } + /* parent process */ - Notice1("forked off child process "F_pid, pid); Close(xfd->fd); sycSSL_free(xfd->para.openssl.ssl); xfd->para.openssl.ssl = NULL; diff --git a/xio-progcall.c b/xio-progcall.c index 0c9f8ed..a9f151e 100644 --- a/xio-progcall.c +++ b/xio-progcall.c @@ -404,26 +404,10 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */ xiosetchilddied(); /* set SIGCHLD handler */ if (withfork) { - const char *forkwaitstring; - int forkwaitsecs = 0; - - pid = Fork(); + pid = xio_fork(true, E_ERROR); if (pid < 0) { - Error1("fork(): %s", strerror(errno)); return -1; } - /* gdb recommends to have env controlled sleep after fork */ - if (forkwaitstring = getenv("SOCAT_FORK_WAIT")) { - forkwaitsecs = atoi(forkwaitstring); - Sleep(forkwaitsecs); - } - - if (pid == 0) { /* child */ - /* drop parents locks, reset FIPS... */ - if (xio_forked_inchild() != 0) { - Exit(1); - } - } } if (!withfork || pid == 0) { /* child */ uid_t user; diff --git a/xio-proxy.c b/xio-proxy.c index 23ab032..f5d1f38 100644 --- a/xio-proxy.c +++ b/xio-proxy.c @@ -192,30 +192,23 @@ static int xioopen_proxy_connect(int argc, const char *argv[], struct opt *opts, #if WITH_RETRY if (dofork) { pid_t pid; - while ((pid = Fork()) < 0) { - int level = E_ERROR; - if (xfd->forever || xfd->retry) { - level = E_WARN; - } - Msg1(level, "fork(): %s", strerror(errno)); - if (xfd->forever || xfd->retry--) { - Nanosleep(&xfd->intervall, NULL); - continue; + int level = E_ERROR; + if (xfd->forever || xfd->retry) { + level = E_WARN; + } + while ((pid = xio_fork(false, level)) < 0) { + if (xfd->forever || --xfd->retry) { + Nanosleep(&xfd->intervall, NULL); continue; } return STAT_RETRYLATER; } - if (pid == 0) { /* child process */ - Info1("just born: proxy client process "F_pid, Getpid()); - /* drop parents locks, reset FIPS... */ - if (xio_forked_inchild() != 0) { - Exit(1); - } + if (pid == 0) { /* child process */ xfd->forever = false; xfd->retry = 0; break; } + /* parent process */ - Notice1("forked off child process "F_pid, pid); Close(xfd->fd); Nanosleep(&xfd->intervall, NULL); dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); diff --git a/xio-socket.c b/xio-socket.c index d93bb98..669925c 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -9,8 +9,10 @@ #if _WITH_SOCKET #include "xioopen.h" +#include "xio-ascii.h" #include "xio-socket.h" #include "xio-named.h" +#include "xio-unix.h" #if WITH_IP4 #include "xio-ip4.h" #endif /* WITH_IP4 */ @@ -21,6 +23,15 @@ #include "xio-ipapp.h" /*! not clean */ #include "xio-tcpwrap.h" + +static int +xiolog_ancillary_socket(struct cmsghdr *cmsg, int *num, + char *typbuff, int typlen, + char *nambuff, int namlen, + char *envbuff, int envlen, + char *valbuff, int vallen); + + 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}; @@ -73,6 +84,9 @@ const struct optdesc opt_so_bsdcompat= { "so-bsdcompat","bsdcompat",OPT_SO_BSDCO #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 */ @@ -410,30 +424,26 @@ int xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, #if WITH_RETRY if (dofork) { pid_t pid; - while ((pid = Fork()) < 0) { - int level = E_ERROR; - if (xfd->forever || --xfd->retry) { - level = E_WARN; /* most users won't expect a problem here, + int level = E_ERROR; + if (xfd->forever || xfd->retry) { + level = E_WARN; /* most users won't expect a problem here, so Notice is too weak */ - } - Msg1(level, "fork(): %s", strerror(errno)); + } + + while ((pid = xio_fork(false, level)) < 0) { + --xfd->retry; if (xfd->forever || xfd->retry) { dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); Nanosleep(&xfd->intervall, NULL); continue; } return STAT_RETRYLATER; } - if (pid == 0) { /* child process */ - Info1("just born: TCP client process "F_pid, Getpid()); - /* drop parents locks, reset FIPS... */ - if (xio_forked_inchild() != 0) { - Exit(1); - } + if (pid == 0) { /* child process */ break; } + /* parent process */ - Notice1("forked off child process "F_pid, pid); Close(xfd->fd); /* with and without retry */ Nanosleep(&xfd->intervall, NULL); @@ -720,6 +730,8 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, 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}; socket_init(pf, pa); salen = sizeof(struct sockaddr); @@ -755,14 +767,25 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, return STAT_RETRYLATER; } while (true); - if (xiogetpacketsrc(xfd->fd, pa, &palen) < 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 (xiogetpacketsrc(xfd->fd, &msgh) < 0) { return STAT_RETRYLATER; } + palen = msgh.msg_namelen; Notice1("receiving packet from %s"/*"src"*/, sockaddr_info((struct sockaddr *)pa, palen, peername, sizeof(peername))/*, sockaddr_info(&la->soa, sockname, sizeof(sockname))*/); + xiodopacketinfo(&msgh, true, true); + if (xiocheckpeer(xfd, pa, la) < 0) { /* drop packet */ char buff[512]; @@ -773,6 +796,10 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, sockaddr_info((struct sockaddr *)pa, 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(xfd->fd, opts, PH_FD); applyopts(xfd->fd, opts, PH_CONNECTED); @@ -782,8 +809,6 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, if (dofork) { sigset_t mask_sigchldusr1; - const char *forkwaitstring; - int forkwaitsecs = 0; /* we must prevent that the current packet triggers another fork; therefore we wait for a signal from the recent child: USR1 @@ -795,17 +820,11 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, sigaddset(&mask_sigchldusr1, SIGUSR1); Sigprocmask(SIG_BLOCK, &mask_sigchldusr1, NULL); - if ((pid = Fork()) < 0) { - Msg1(level, "fork(): %s", strerror(errno)); + if ((pid = xio_fork(false, level)) < 0) { Close(xfd->fd); Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL); return STAT_RETRYLATER; } - /* gdb recommends to have env controlled sleep after fork */ - if (forkwaitstring = getenv("SOCAT_FORK_WAIT")) { - forkwaitsecs = atoi(forkwaitstring); - Sleep(forkwaitsecs); - } if (pid == 0) { /* child */ /* no reason to block SIGCHLD in child process */ @@ -820,11 +839,6 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, level = E_ERROR; #endif /* WITH_RETRY */ - /* drop parents locks, reset FIPS... */ - if (xio_forked_inchild() != 0) { - Exit(1); - } - #if WITH_UNIX /* with UNIX sockets: only listening parent is allowed to remove the socket file */ @@ -835,8 +849,6 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, } /* server: continue loop with listen */ - Notice1("forked off child process "F_pid, pid); - xio_waitingfor = pid; /* now we are ready to handle signals */ Sigprocmask(SIG_UNBLOCK, &mask_sigchldusr1, NULL); @@ -964,65 +976,120 @@ int retropt_socket_pf(struct opt *opts, int *pf) { } - -int xiogetpacketsrc(int fd, union sockaddr_union *pa, socklen_t *palen) { - char infobuff[256]; +/* this function calls recvmsg(..., MSG_PEEK, ...) to obtain information about + the arriving packet. in msgh the msg_name pointer must refer to an (empty) + sockaddr storage. */ +int xiogetpacketsrc(int fd, struct msghdr *msgh) { char peekbuff[1]; - -#if 0 - - struct msghdr msgh = {0}; #if HAVE_STRUCT_IOVEC struct iovec iovec; #endif - char ctrlbuff[5120]; - msgh.msg_name = pa; - msgh.msg_namelen = *palen; #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_MSGCONTROL - msgh.msg_control = ctrlbuff; -#endif -#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN - msgh.msg_controllen = sizeof(ctrlbuff); + msgh->msg_iov = &iovec; + msgh->msg_iovlen = 1; #endif #if HAVE_STRUCT_MSGHDR_MSGFLAGS - msgh.msg_flags = 0; + msgh->msg_flags = 0; #endif - if (Recvmsg(fd, &msgh, MSG_PEEK + if (Recvmsg(fd, msgh, MSG_PEEK #ifdef MSG_TRUNC - |MSG_TRUNC + |MSG_TRUNC #endif ) < 0) { - Notice1("packet from %s", - sockaddr_info(&pa->soa, infobuff, sizeof(infobuff))); Warn1("recvmsg(): %s", strerror(errno)); return STAT_RETRYLATER; } - *palen = msgh.msg_namelen; return STAT_OK; +} -#else - if (Recvfrom(fd, peekbuff, sizeof(peekbuff), MSG_PEEK -#ifdef MSG_TRUNC - |MSG_TRUNC -#endif - , - &pa->soa, palen) < 0) { - Notice1("packet from %s", - sockaddr_info(&pa->soa, *palen, infobuff, sizeof(infobuff))); - Warn1("recvfrom(): %s", strerror(errno)); - return STAT_RETRYLATER; +/* 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 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; + + 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_socklen", 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(cmsg, &num, typbuff, sizeof(typbuff)-1, + nambuff, sizeof(nambuff)-1, + envbuff, sizeof(envbuff)-1, + valbuff, sizeof(valbuff)-1); + break; + case SOL_IP: + xiolog_ancillary_ip(cmsg, &num, typbuff, sizeof(typbuff)-1, + nambuff, sizeof(nambuff)-1, + envbuff, sizeof(envbuff)-1, + valbuff, sizeof(valbuff)-1); + break; + case SOL_IPV6: + xiolog_ancillary_ip6(cmsg, &num, typbuff, sizeof(typbuff)-1, + nambuff, sizeof(nambuff)-1, + envbuff, sizeof(envbuff)-1, + valbuff, sizeof(valbuff)-1); + break; + 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); + } else if (!strcasecmp(typp+strlen(typp)-strlen(namp), namp)) { + xiosetenv(typp, valp, 1); + } else { + xiosetenv2(typp, namp, valp, 1); + } + } + 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 STAT_OK; - -#endif + return 0; +#else /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */ + return -1; +#endif /* !(defined(HAVE_STRUCT_CMSGHDR) && defined(CMSG_DATA)) */ } @@ -1049,6 +1116,7 @@ int xiocheckpeer(xiosingle_t *xfd, #if WITH_IP4 if (xfd->para.socket.dorange) { + if (pa == NULL) { return -1; } if (xiocheckrange(pa, &xfd->para.socket.range) < 0) { char infobuff[256]; Warn1("refusing connection from %s due to range option", @@ -1064,6 +1132,7 @@ int xiocheckpeer(xiosingle_t *xfd, #if WITH_TCP || WITH_UDP if (xfd->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) != xfd->para.socket.ip.sourceport) { @@ -1086,6 +1155,7 @@ int xiocheckpeer(xiosingle_t *xfd, sockaddr_info((struct sockaddr *)pa, 0, infobuff, sizeof(infobuff))); } else if (xfd->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", @@ -1127,4 +1197,177 @@ int xiocheckpeer(xiosingle_t *xfd, return 0; /* permitted */ } +/* 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 is *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 + */ +static int +xiolog_ancillary_socket(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; + +#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); + strncpy(nambuff, "data", namlen); + 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); } + sprintf(strchr(valbuff, '\0')-1/*del \n*/, ", %06ld usecs", 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) Fatal("buff too short"); + strncpy(typbuff, cmsgtype, typlen); + if (strlen(cmsgname) >= namlen) Fatal("buff too short"); + strncpy(nambuff, cmsgname, namlen); + if (strlen(cmsgenvn) >= envlen) Fatal("buff too short"); + strncpy(envbuff, cmsgenvn, envlen); + return STAT_OK; + +#else /* !defined(CMSG_DATA) */ + + return STAT_NORETRY; + +#endif /* !defined(CMSG_DATA) */ +} + + +/* 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 0 + 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) { + Info3("ioctl(%d, SIOCGIFNAME, {..., ifr_ifindex=%d, ...}: %s", + s, ifr.ifr_ifindex, strerror(errno)); + if (ins < 0) Close(s); + return NULL; + } +#endif /* SIOCGIFNAME */ + if (ins < 0) Close(s); + strcpy(val, ifr.ifr_name); + return val; +#else /* ! 0 */ + return if_indextoname(ind, val); +#endif +} + + +/* 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) { + case PF_UNIX: + result = + xiosetsockaddrenv_unix(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr), + valuebuff, XIOSOCKADDRENVLEN, + &sau->un, salen, proto); + xiosetenv(namebuff, valuebuff, 1); + break; + case PF_INET: + do { + result = + xiosetsockaddrenv_ip4(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr), + valuebuff, XIOSOCKADDRENVLEN, + &sau->ip4, proto); + xiosetenv(namebuff, valuebuff, 1); + namebuff[strlen(lr)] = '\0'; ++idx; + } while (result > 0); + break; + 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); + namebuff[strlen(lr)] = '\0'; ++idx; + } while (result > 0); + break; +#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 */ diff --git a/xio-socket.h b/xio-socket.h index 04734f9..6277bd9 100644 --- a/xio-socket.h +++ b/xio-socket.h @@ -1,5 +1,5 @@ /* source: xio-socket.h */ -/* Copyright Gerhard Rieger 2001-2006 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __xio_socket_h_included @@ -31,6 +31,7 @@ extern const struct optdesc opt_so_detach_filter; extern const struct optdesc opt_so_bindtodevice; extern const struct optdesc opt_so_bsdcompat; extern const struct optdesc opt_so_cksumrecv; +extern const struct optdesc opt_so_timestamp; extern const struct optdesc opt_so_kernaccept; extern const struct optdesc opt_so_no_check; extern const struct optdesc opt_so_noreuseaddr; @@ -51,6 +52,10 @@ extern const struct optdesc opt_siocspgrp; extern const struct optdesc opt_bind; extern const struct optdesc opt_protocol_family; + +extern +char *xiogetifname(int ind, char *val, int ins); + extern int retropt_socket_pf(struct opt *opts, int *pf); extern int xioopen_connect(struct single *fd, @@ -81,10 +86,14 @@ int _xioopen_dgram_recv(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen, struct opt *opts, int pf, int socktype, int proto, int level); +extern +int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv); extern -int xiogetpacketsrc(int fd, union sockaddr_union *pa, socklen_t *palen); +int xiogetpacketsrc(int fd, struct msghdr *msgh); extern int xiocheckpeer(xiosingle_t *xfd, union sockaddr_union *pa, union sockaddr_union *la); +extern +int xiosetsockaddrenv(const char *lr, union sockaddr_union *sau, socklen_t salen, int proto); #endif /* !defined(__xio_socket_h_included) */ diff --git a/xio-socks.c b/xio-socks.c index 71c0ded..9efa675 100644 --- a/xio-socks.c +++ b/xio-socks.c @@ -170,30 +170,25 @@ static int xioopen_socks4_connect(int argc, const char *argv[], struct opt *opts #if WITH_RETRY if (dofork) { pid_t pid; - while ((pid = Fork()) < 0) { - int level = E_ERROR; - if (xfd->forever || xfd->retry) { - level = E_WARN; - } - Msg1(level, "fork(): %s", strerror(errno)); - if (xfd->forever || xfd->retry--) { + int level = E_ERROR; + if (xfd->forever || xfd->retry) { + level = E_WARN; /* most users won't expect a problem here, + so Notice is too weak */ + } + while ((pid = xio_fork(false, level)) < 0) { + if (xfd->forever || --xfd->retry) { Nanosleep(&xfd->intervall, NULL); continue; } return STAT_RETRYLATER; } - if (pid == 0) { /* child process */ - Info1("just born: socks client process "F_pid, Getpid()); - /* drop parents locks, reset FIPS... */ - if (xio_forked_inchild() != 0) { - Exit(1); - } + if (pid == 0) { /* child process */ xfd->forever = false; xfd->retry = 0; break; } + /* parent process */ - Notice1("forked off child process "F_pid, pid); Close(xfd->fd); Nanosleep(&xfd->intervall, NULL); dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); diff --git a/xio-tcpwrap.c b/xio-tcpwrap.c index 2029ed2..20d482b 100644 --- a/xio-tcpwrap.c +++ b/xio-tcpwrap.c @@ -1,5 +1,5 @@ /* source: xio-tcpwrap.c */ -/* Copyright Gerhard Rieger 2006-2007 */ +/* Copyright Gerhard Rieger 2006-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains the source for tcpwrapper handling stuff */ @@ -93,6 +93,7 @@ int xio_tcpwrap_check(xiosingle_t *xfd, union sockaddr_union *us, if (!xfd->para.socket.ip.dolibwrap) { return 0; } + if (us == NULL || them == NULL) { return -1; } #if defined(HAVE_HOSTS_ALLOW_TABLE) save_hosts_allow_table = hosts_allow_table; diff --git a/xio-udp.c b/xio-udp.c index 293b8be..f2bff42 100644 --- a/xio-udp.c +++ b/xio-udp.c @@ -238,26 +238,21 @@ int xioopen_ipdgram_listen(int argc, const char *argv[], struct opt *opts, sockaddr_info(&them->soa, themlen, infobuff, sizeof(infobuff))); if (dofork) { - pid = Fork(); + pid = xio_fork(false, E_ERROR); if (pid < 0) { - Error1("fork(): %s", strerror(errno)); return STAT_RETRYLATER; } - if (pid == 0) { /* child */ - /* drop parents locks, reset FIPS... */ - if (xio_forked_inchild() != 0) { - Exit(1); - } + if (pid == 0) { /* child */ break; } + /* server: continue loop with socket()+recvfrom() */ /* when we dont close this we get awkward behaviour on Linux 2.4: recvfrom gives 0 bytes with invalid socket address */ if (Close(fd->stream.fd) < 0) { Info2("close(%d): %s", fd->stream.fd, strerror(errno)); } - Notice1("forked off child process "F_pid, pid); Sleep(1); /*! give child a chance to consume the old packet */ continue; @@ -274,6 +269,14 @@ int xioopen_ipdgram_listen(int argc, const char *argv[], struct opt *opts, return STAT_RETRYLATER; } + /* set the env vars describing the local and remote sockets */ + if (Getsockname(fd->stream.fd, &us.soa, &uslen) < 0) { + Warn4("getsockname(%d, %p, {%d}): %s", + fd->stream.fd, &us.soa, uslen, strerror(errno)); + } + xiosetsockaddrenv("SOCK", &us, uslen, IPPROTO_UDP); + xiosetsockaddrenv("PEER", them, themlen, IPPROTO_UDP); + fd->stream.howtoend = END_SHUTDOWN; applyopts_fchown(fd->stream.fd, opts); applyopts(fd->stream.fd, opts, PH_LATE); diff --git a/xio-unix.c b/xio-unix.c index b0c6b95..acfc6a6 100644 --- a/xio-unix.c +++ b/xio-unix.c @@ -1,5 +1,5 @@ /* source: xio-unix.c */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains the source for opening addresses of UNIX socket type */ @@ -741,4 +741,25 @@ static int xioopen_abstract_client(int argc, const char *argv[], struct opt *opt #endif /* WITH_ABSTRACT_UNIXSOCKET */ +/* returns information that can be used for constructing an environment + variable describing the socket address. + if idx is 0, this function writes "ADDR" into namebuff and the path into + valuebuff, and returns 0 (which means that no more info is there). + if idx is != 0, it returns -1 + namelen and valuelen contain the max. allowed length of output chars in the + respective buffer. + on error this function returns -1. +*/ +int +xiosetsockaddrenv_unix(int idx, char *namebuff, size_t namelen, + char *valuebuff, size_t valuelen, + struct sockaddr_un *sa, socklen_t salen, int ipproto) { + if (idx != 0) { + return -1; + } + strcpy(namebuff, "ADDR"); + sockaddr_unix_info(sa, salen, valuebuff, valuelen); + return 0; +} + #endif /* WITH_UNIX */ diff --git a/xio-unix.h b/xio-unix.h index 9b09efe..8050bab 100644 --- a/xio-unix.h +++ b/xio-unix.h @@ -1,5 +1,5 @@ /* source: xio-unix.h */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __xio_unix_h_included @@ -25,5 +25,9 @@ xiosetunix(struct sockaddr_un *saun, const char *path, bool abstract, bool tight); +extern int +xiosetsockaddrenv_unix(int idx, char *namebuff, size_t namelen, + char *valuebuff, size_t valuelen, + struct sockaddr_un *sa, socklen_t salen, int ipproto); #endif /* !defined(__xio_unix_h_included) */ diff --git a/xio.h b/xio.h index 930d6bd..598e8ba 100644 --- a/xio.h +++ b/xio.h @@ -375,6 +375,8 @@ extern xiofile_t *sock[XIO_MAXSOCK]; not even by external changes correctable */ extern int xioinitialize(void); +extern int xioinitialize2(void); +extern pid_t xio_fork(bool subchild, int level); extern int xio_forked_inchild(void); extern int xiosetopt(char what, const char *arg); extern int xioinqopt(char what, char *arg, size_t n); diff --git a/xioinitialize.c b/xioinitialize.c index 81c6a29..d04ad71 100644 --- a/xioinitialize.c +++ b/xioinitialize.c @@ -107,6 +107,14 @@ int xioinitialize(void) { return 0; } +/* call this function when option -lp (reset program name) has been applied */ +int xioinitialize2(void) { + pid_t pid = Getpid(); + xiosetenvulong("PID", pid, 1); + xiosetenvulong("PPID", pid, 1); + return 0; +} + /* well, this function is not for initialization, but I could not find a better place for it @@ -162,6 +170,7 @@ static int xio_nokill(xiofile_t *sock) { returns 0 on success or != 0 if an error occurred */ int xio_forked_inchild(void) { int result = 0; + xiodroplocks(); #if WITH_FIPS if (xio_reset_fips_mode() != 0) { @@ -185,3 +194,45 @@ int xio_forked_inchild(void) { return result; } + +/* subchild != 0 means that the current process is already a child process of + the master process and thus the new sub child process should not set the + SOCAT_PID variable */ +pid_t xio_fork(bool subchild, int level) { + pid_t pid; + const char *forkwaitstring; + int forkwaitsecs = 0; + + if ((pid = Fork()) < 0) { + Msg1(level, "fork(): %s", strerror(errno)); + return pid; + } + + if (pid == 0) { /* child process */ + pid_t cpid = Getpid(); + + Info1("just born: client process "F_pid, cpid); + if (!subchild) { + /* set SOCAT_PID to new value */ + xiosetenvulong("PID", pid, 1); + } + /* gdb recommends to have env controlled sleep after fork */ + if (forkwaitstring = getenv("SOCAT_FORK_WAIT")) { + forkwaitsecs = atoi(forkwaitstring); + Sleep(forkwaitsecs); + } + if (xio_forked_inchild() != 0) { + Exit(1); + } + return 0; + } + + /* parent process */ + Notice1("forked off child process "F_pid, pid); + /* gdb recommends to have env controlled sleep after fork */ + if (forkwaitstring = getenv("SOCAT_FORK_WAIT")) { + forkwaitsecs = atoi(forkwaitstring); + Sleep(forkwaitsecs); + } + return pid; +} diff --git a/xioopts.c b/xioopts.c index dfbb1ef..2ac62c8 100644 --- a/xioopts.c +++ b/xioopts.c @@ -165,6 +165,9 @@ const struct optname optionnames[] = { #ifdef SO_AUDIT /* AIX 4.3.3 */ IF_SOCKET ("audit", &opt_so_audit) #endif /* SO_AUDIT */ +#ifdef IPV6_AUTHHDR + IF_IP6 ("authhdr", &opt_ipv6_authhdr) +#endif IF_TUN ("automedia", &opt_iff_automedia) #ifdef CBAUD IF_TERMIOS("b0", &opt_b0) @@ -375,6 +378,7 @@ const struct optname optionnames[] = { IF_SOCKET ("dontlinger", &opt_so_dontlinger) #endif IF_SOCKET ("dontroute", &opt_so_dontroute) + IF_IP6 ("dstopts", &opt_ipv6_dstopts) #ifdef VDSUSP /* HP-UX */ IF_TERMIOS("dsusp", &opt_vdsusp) #endif @@ -501,6 +505,9 @@ const struct optname optionnames[] = { IF_ANY ("flock-nb", &opt_flock_ex_nb) IF_ANY ("flock-sh", &opt_flock_sh) IF_ANY ("flock-sh-nb", &opt_flock_sh_nb) +#endif +#ifdef IPV4_FLOWINFO + IF_IP6 ("flowinfo", &opt_ipv6_flowinfo) #endif IF_TERMIOS("flusho", &opt_flusho) IF_RETRY ("forever", &opt_forever) @@ -528,6 +535,8 @@ const struct optname optionnames[] = { #endif IF_READLINE("history", &opt_history_file) IF_READLINE("history-file", &opt_history_file) + IF_IP6 ("hoplimit", &opt_ipv6_hoplimit) + IF_IP6 ("hopopts", &opt_ipv6_hopopts) #if WITH_LIBWRAP && defined(HAVE_HOSTS_ALLOW_TABLE) IF_IPAPP ("hosts-allow", &opt_tcpwrap_hosts_allow_table) #endif @@ -614,9 +623,15 @@ const struct optname optionnames[] = { #ifdef IP_PKTOPTIONS IF_IP ("ip-pktoptions", &opt_ip_pktoptions) #endif +#ifdef IP_RECVDSTADDR + IF_IP ("ip-recvdstaddr", &opt_ip_recvdstaddr) +#endif #ifdef IP_RECVERR IF_IP ("ip-recverr", &opt_ip_recverr) #endif +#ifdef IP_RECVIF + IF_IP ("ip-recvif", &opt_ip_recvif) +#endif #ifdef IP_RECVOPTS IF_IP ("ip-recvopts", &opt_ip_recvopts) #endif @@ -657,6 +672,9 @@ const struct optname optionnames[] = { #ifdef IP_PKTOPTIONS IF_IP ("ippktoptions", &opt_ip_pktoptions) #endif +#ifdef IP_RECVDSTADDR + IF_IP ("iprecvdstaddr", &opt_ip_recvdstaddr) +#endif #ifdef IP_RECVERR IF_IP ("iprecverr", &opt_ip_recverr) #endif @@ -678,7 +696,32 @@ const struct optname optionnames[] = { IF_IP ("iptos", &opt_ip_tos) IF_IP ("ipttl", &opt_ip_ttl) IF_IP6 ("ipv6-add-membership", &opt_ipv6_join_group) +#ifdef IPV6_AUTHHDR + IF_IP6 ("ipv6-authhdr", &opt_ipv6_authhdr) +#endif + IF_IP6 ("ipv6-dstopts", &opt_ipv6_dstopts) +#ifdef IPV4_FLOWINFO + IF_IP6 ("ipv6-flowinfo", &opt_ipv6_flowinfo) +#endif + IF_IP6 ("ipv6-hoplimit", &opt_ipv6_hoplimit) + IF_IP6 ("ipv6-hopopts", &opt_ipv6_hopopts) IF_IP6 ("ipv6-join-group", &opt_ipv6_join_group) + IF_IP6 ("ipv6-pktinfo", &opt_ipv6_pktinfo) + IF_IP6 ("ipv6-recvdstopts", &opt_ipv6_recvdstopts) +#ifdef IPV6_RECVERR + IF_IP6 ("ipv6-recverr", &opt_ipv6_recverr) +#endif + IF_IP6 ("ipv6-recvhoplimit", &opt_ipv6_recvhoplimit) + IF_IP6 ("ipv6-recvhopopts", &opt_ipv6_recvhopopts) +#ifdef IPV6_PATHMTU + IF_IP6 ("ipv6-recvpathmtu", &opt_ipv6_recvpathmtu) +#endif + IF_IP6 ("ipv6-recvpktinfo", &opt_ipv6_recvpktinfo) + IF_IP6 ("ipv6-recvrthdr", &opt_ipv6_recvrthdr) + IF_IP6 ("ipv6-recvtclass", &opt_ipv6_recvtclass) + IF_IP6 ("ipv6-rthdr", &opt_ipv6_rthdr) + IF_IP6 ("ipv6-tclass", &opt_ipv6_tclass) + IF_IP6 ("ipv6-unicast-hops", &opt_ipv6_unicast_hops) #ifdef IPV6_V6ONLY IF_IP6 ("ipv6-v6only", &opt_ipv6_v6only) IF_IP6 ("ipv6only", &opt_ipv6_v6only) @@ -1087,12 +1130,23 @@ const struct optname optionnames[] = { #if HAVE_RESOLV_H IF_IP ("recurse", &opt_res_recurse) #endif /* HAVE_RESOLV_H */ +#ifdef IP_RECVDSTADDR + IF_IP ("recvdstaddr", &opt_ip_recvdstaddr) +#endif + IF_IP6 ("recvdstopts", &opt_ipv6_recvdstopts) #ifdef IP_RECVERR IF_IP ("recverr", &opt_ip_recverr) +#endif + IF_IP6 ("recvhoplimit", &opt_ipv6_recvhoplimit) + IF_IP6 ("recvhopopts", &opt_ipv6_recvhopopts) +#ifdef IP_RECVIF + IF_IP ("recvif", &opt_ip_recvif) #endif #ifdef IP_RECVOPTS IF_IP ("recvopts", &opt_ip_recvopts) #endif + IF_IP6 ("recvpktinfo", &opt_ipv6_recvpktinfo) + IF_IP6 ("recvrthdr", &opt_ipv6_recvrthdr) #ifdef IP_RECVTOS IF_IP ("recvtos", &opt_ip_recvtos) #endif @@ -1139,6 +1193,7 @@ const struct optname optionnames[] = { #ifdef O_RSYNC IF_OPEN ("rsync", &opt_o_rsync) #endif + IF_IP6 ("rthdr", &opt_ipv6_rthdr) IF_TUN ("running", &opt_iff_running) #ifdef TCP_SACK_DISABLE IF_TCP ("sack-disable", &opt_tcp_sack_disable) @@ -1297,6 +1352,9 @@ const struct optname optionnames[] = { #endif #ifdef SO_SNDTIMEO IF_SOCKET ("so-sndtimeo", &opt_so_sndtimeo) +#endif +#ifdef SO_TIMESTAMP + IF_SOCKET ("so-timestamp", &opt_so_timestamp) #endif IF_SOCKET ("so-type", &opt_so_type) #ifdef SO_USE_IFBUFS @@ -1446,6 +1504,9 @@ const struct optname optionnames[] = { #endif IF_UNIX ("tightsocklen", &opt_unix_tightsocklen) IF_TERMIOS("time", &opt_vtime) +#ifdef SO_TIMESTAMP + IF_SOCKET ("timestamp", &opt_so_timestamp) +#endif IF_TERMIOS("tiocsctty", &opt_tiocsctty) #if WITH_EXT2 && defined(EXT2_TOPDIR_FL) IF_ANY ("topdir", &opt_ext2_topdir) @@ -1471,6 +1532,7 @@ const struct optname optionnames[] = { IF_NAMED ("uid-e", &opt_user_early) IF_ANY ("uid-l", &opt_user_late) IF_NAMED ("umask", &opt_umask) + IF_IP6 ("unicast-hops", &opt_ipv6_unicast_hops) IF_UNIX ("unix-tightsocklen", &opt_unix_tightsocklen) IF_NAMED ("unlink", &opt_unlink) IF_NAMED ("unlink-close", &opt_unlink_close) @@ -1713,7 +1775,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts, (*opts)[i].value.u_bin.b_len = optlen; break; case TYPE_BYTE: - { + if (assign) { unsigned long ul; char *rest; ul = strtoul(token, &rest/*!*/, 0); @@ -1722,11 +1784,13 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts, a0, ul, UCHAR_MAX); (*opts)[i].value.u_byte = UCHAR_MAX; } else { - Info2("setting option \"%s\" to %d", ent->desc->defname, - (*opts)[i].value.u_byte); (*opts)[i].value.u_byte = ul; } + } else { + (*opts)[i].value.u_byte = 1; } + Info2("setting option \"%s\" to %d", ent->desc->defname, + (*opts)[i].value.u_byte); break; case TYPE_INT: if (assign) { diff --git a/xioopts.h b/xioopts.h index 81ff412..0f11223 100644 --- a/xioopts.h +++ b/xioopts.h @@ -327,7 +327,24 @@ enum e_optcode { OPT_INLCR, /* termios.c_iflag */ OPT_INPCK, /* termios.c_iflag */ OPT_INTERVALL, + OPT_IPV6_AUTHHDR, + OPT_IPV6_DSTOPTS, + OPT_IPV6_FLOWINFO, + OPT_IPV6_HOPLIMIT, + OPT_IPV6_HOPOPTS, OPT_IPV6_JOIN_GROUP, + OPT_IPV6_PKTINFO, + OPT_IPV6_RECVDSTOPTS, + OPT_IPV6_RECVERR, + OPT_IPV6_RECVHOPLIMIT, + OPT_IPV6_RECVHOPOPTS, + OPT_IPV6_RECVPATHMTU, + OPT_IPV6_RECVPKTINFO, + OPT_IPV6_RECVRTHDR, + OPT_IPV6_RECVTCLASS, + OPT_IPV6_RTHDR, + OPT_IPV6_TCLASS, + OPT_IPV6_UNICAST_HOPS, OPT_IPV6_V6ONLY, #if 0 /* see Linux: man 7 netlink; probably not what we need yet */ OPT_IO_SIOCGIFNAME, @@ -355,9 +372,11 @@ enum e_optcode { #ifdef IP_PKTOPTIONS OPT_IP_PKTOPTIONS, #endif + OPT_IP_RECVDSTADDR, #ifdef IP_RECVERR OPT_IP_RECVERR, #endif + OPT_IP_RECVIF, #ifdef IP_RECVOPTS OPT_IP_RECVOPTS, #endif @@ -628,6 +647,7 @@ enum e_optcode { #ifdef SO_SNDTIMEO OPT_SO_SNDTIMEO, #endif + OPT_SO_TIMESTAMP, /* Linux */ OPT_SO_TYPE, #ifdef SO_USELOOPBACK OPT_SO_USELOOPBACK, diff --git a/xioread.c b/xioread.c index 5db886c..d8bb574 100644 --- a/xioread.c +++ b/xioread.c @@ -113,10 +113,23 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { case XIOREAD_RECV: if (pipe->dtype & XIOREAD_RECV_FROM) { #if WITH_RAWIP || WITH_UDP || WITH_UNIX + struct msghdr msgh = {0}; union sockaddr_union from = {{0}}; socklen_t fromlen = sizeof(from); char infobuff[256]; + char ctrlbuff[1024]; /* ancillary messages */ + msgh.msg_name = &from; + msgh.msg_namelen = fromlen; +#if HAVE_STRUCT_MSGHDR_MSGCONTROL + msgh.msg_control = ctrlbuff; +#endif +#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN + msgh.msg_controllen = sizeof(ctrlbuff); +#endif + if (xiogetpacketsrc(pipe->fd, &msgh) < 0) { + return -1; + } do { bytes = Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen); @@ -283,12 +296,23 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { } else /* ~XIOREAD_RECV_FROM */ { union sockaddr_union from; socklen_t fromlen = sizeof(from); char infobuff[256]; + struct msghdr msgh = {0}; + char ctrlbuff[1024]; /* ancillary messages */ socket_init(pipe->para.socket.la.soa.sa_family, &from); /* get source address */ - if (xiogetpacketsrc(pipe->fd, &from, &fromlen) < 0) { - return STAT_RETRYNOW; + msgh.msg_name = &from; + msgh.msg_namelen = fromlen; +#if HAVE_STRUCT_MSGHDR_MSGCONTROL + msgh.msg_control = ctrlbuff; +#endif +#if HAVE_STRUCT_MSGHDR_MSGCONTROLLEN + msgh.msg_controllen = sizeof(ctrlbuff); +#endif + if (xiogetpacketsrc(pipe->fd, &msgh) < 0) { + return -1; } + xiodopacketinfo(&msgh, true, false); if (xiocheckpeer(pipe, &from, &pipe->para.socket.la) < 0) { Recvfrom(pipe->fd, buff, bufsiz, 0, &from.soa, &fromlen); /* drop */ errno = EAGAIN; return -1;