Hiya, I'm wondering if one of you can share insight here - I've been debugging OpenVPN (-master) on OpenSolaris, which failed in building sockets, due to getaddrinfo() returning "ai_protocol = 0" for everything - while the OpenVPN socket code relies on ai_protocol being IPPROTO_UDP or IPPROTO_TCP (and ASSERT()s out in create_socket() if it's neither).
To understand the issue, I've made a test program, which is appended below - it basically calls getaddrinfo() to resolve two different dual-stack servers, one for SOCK_DGRAM and the other for SOCK_STREAM. On Linux and NetBSD, the result looks like this: getaddrinfo('www.google.de', '80', family=24, socktype=1) -> status=0 ai_: flags=8, family=24, socktype=1, protocol=6 addrlen=28, addr='2a00:1450:4016:801::1018' getaddrinfo('conn-test-server.openvpn.org', '51194', family=24, socktype=2) -> status=0 ai_: flags=8, family=24, socktype=2, protocol=17 addrlen=28, addr='2607:fc50:1001:5200::4' (only the ipv6-only case show, "protocol=6" is TCP, "protocol=17" is UDP) - which is the expected case. Same code on Solaris: getaddrinfo('www.google.de', '80', family=26, socktype=2) -> status=0 ai_: flags=64, family=26, socktype=2, protocol=0 addrlen=32, addr='2a00:1450:4016:802::101f' getaddrinfo('conn-test-server.openvpn.org', '51194', family=26, socktype=1) -> status=0 ai_: flags=64, family=26, socktype=1, protocol=0 addrlen=32, addr='2607:fc50:1001:5200::4' -> ai_protocol is "0", which is basically "undefined", but on *Solaris*, this seems to be "use what is appropriate", from "man socket": The protocol parameter is a protocol-family-specific value which specifies a particular protocol to be used with the socket. Normally this value is zero, as commonly only a single protocol exists to support a particular socket type within a given protocol family. However, multiple protocols may exist, in which case a particular protocol may be speci- fied in this manner. so now I wonder how to tackle this in OpenVPN. One possible approach would be to add this hack to the end of openvpn_getaddrinfo(): #ifdef TARGET_SOLARIS /* fixup Solaris' getaddrinfo() returning ai_protocol=0 */ if (status==0 && (*res)->ai_protocol == 0 ) { struct addrinfo *ai = *res; msg( M_WARN, "invoking Solaris ai_protocol==0 fixup" ); while( ai ) { if(flags & GETADDR_DATAGRAM) ai->ai_protocol = IPPROTO_UDP; else ai->ai_protocol = IPPROTO_TCP; ai = ai->ai_next; } } #endif ... that is, depending on the type of socket we've asked for, fix the return values to always set ai_protocol to the values that create_socket() wants to see. The other approach would be to change create_socket() to decide upon ai_socktype (SOCK_DGRAM/SOCK_STREAM) whether to call create_socket_udp() or create_socket_tcp(), and change *those* to ignore ai_protocol and always use IPPROTO_UDP / IPPROTO_TCP instead for their socket() calls. These are the code bits in question: create_socket() { if (addr->ai_protocol == IPPROTO_UDP) { sock->sd = create_socket_udp (addr, sock->sockflags); ... } else if (addr->ai_protocol == IPPROTO_TCP) { sock->sd = create_socket_tcp (addr); } else { ASSERT (0); <<< this is where solaris bombs out } } create_socket_tcp() { ... ASSERT (addrinfo); ASSERT (addrinfo->ai_socktype == SOCK_STREAM); ASSERT (addrinfo->ai_protocol == IPPROTO_TCP); if ((sd = socket (addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0) create_socket_udp() { ... ASSERT (addrinfo); ASSERT (addrinfo->ai_socktype == SOCK_DGRAM); ASSERT (addrinfo->ai_protocol == IPPROTO_UDP); if ((sd = socket (addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol)) < 0) Arne, what do you think? gert -- USENET is *not* the non-clickable part of WWW! //www.muc.de/~gert/ Gert Doering - Munich, Germany g...@greenie.muc.de fax: +49-89-35655025 g...@net.informatik.tu-muenchen.de
/* getaddrinfo() sanity test */ #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <netinet/in.h> #include <string.h> void gai( char * host, char * service, int ai_family, int ai_socktype ) { struct addrinfo hints, *ai, *ap; int status; char print_host[200]; memset( &hints, 0, sizeof(hints) ); hints.ai_family = ai_family; hints.ai_socktype = ai_socktype; /* hints.ai_protocol = IPPROTO_TCP; */ /* test, no effect */ hints.ai_flags = AI_NUMERICSERV; status = getaddrinfo( host, service, &hints, &ai ); printf( "\ngetaddrinfo('%s', '%s', family=%d, socktype=%d) -> status=%d\n", host, service, ai_family, ai_socktype, status ); if ( status != 0 ) return; for( ap = ai; ap != NULL; ap=ap->ai_next ) { printf( "ai_: flags=%d, family=%d, socktype=%d, protocol=%d\n", ap->ai_flags, ap->ai_family, ap->ai_socktype, ap->ai_protocol ); getnameinfo( ap->ai_addr, ap->ai_addrlen, print_host, sizeof(print_host)-1, NULL, 0, NI_NUMERICHOST ); printf( " addrlen=%d, addr='%s'\n", ap->ai_addrlen, print_host ); } freeaddrinfo(ai); } int main( int argc, char ** argv ) { gai( "www.google.de", "80", AF_UNSPEC, SOCK_STREAM ); gai( "www.google.de", "80", AF_INET6, SOCK_STREAM ); gai( "conn-test-server.openvpn.org", "51194", AF_UNSPEC, SOCK_DGRAM ); gai( "conn-test-server.openvpn.org", "51194", AF_INET6, SOCK_DGRAM ); return 0; }
pgp4X3HsQNPiQ.pgp
Description: PGP signature