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;
}

Attachment: pgp4X3HsQNPiQ.pgp
Description: PGP signature

Reply via email to