b_sock.c: correct indirect calls on WinSock platforms.
[openssl.git] / crypto / bio / b_sock.c
index da2ff2900f05ede11a8044fddcd4e396dc9383f2..8a69e99f82b2c411aab830bc778185c9b2faed0f 100644 (file)
  * [including the GNU Public Licence.]
  */
 
-#ifndef NO_SOCK
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #define USE_SOCKETS
 #include "cryptlib.h"
 #include <openssl/bio.h>
+#if defined(OPENSSL_SYS_NETWARE) && defined(NETWARE_BSDSOCK)
+#include <netdb.h>
+#if defined(NETWARE_CLIB)
+#include <sys/ioctl.h>
+NETDB_DEFINE_CONTEXT
+#endif
+#endif
+
+#ifndef OPENSSL_NO_SOCK
+
+#include <openssl/dso.h>
 
-#ifdef WIN16
-#define SOCKET_PROTOCOL 0 /* more microsoft stupidity */
-#else
 #define SOCKET_PROTOCOL IPPROTO_TCP
-#endif
 
 #ifdef SO_MAXCONN
-#define MAX_LISTEN  SOMAXCONN
-#elif defined(SO_MAXCONN)
 #define MAX_LISTEN  SO_MAXCONN
+#elif defined(SOMAXCONN)
+#define MAX_LISTEN  SOMAXCONN
 #else
 #define MAX_LISTEN  32
 #endif
 
-#ifdef WINDOWS
+#if defined(OPENSSL_SYS_WINDOWS) || (defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK))
 static int wsa_init_done=0;
 #endif
 
+/*
+ * WSAAPI specifier is required to make indirect calls to run-time
+ * linked WinSock 2 functions used in this module, to be specific
+ * [get|free]addrinfo and getnameinfo. This is because WinSock uses
+ * uses non-C calling convention, __stdcall vs. __cdecl, on x86
+ * Windows. On non-WinSock platforms WSAAPI needs to be void.
+ */
+#ifndef WSAAPI
+#define WSAAPI
+#endif
+
+#if 0
 static unsigned long BIO_ghbn_hits=0L;
 static unsigned long BIO_ghbn_miss=0L;
 
@@ -93,10 +110,13 @@ static struct ghbn_cache_st
        struct hostent *ent;
        unsigned long order;
        } ghbn_cache[GHBN_NUM];
+#endif
 
 static int get_ip(const char *str,unsigned char *ip);
+#if 0
 static void ghbn_free(struct hostent *a);
 static struct hostent *ghbn_dup(struct hostent *a);
+#endif
 int BIO_get_host_ip(const char *str, unsigned char *ip)
        {
        int i;
@@ -171,11 +191,11 @@ int BIO_get_port(const char *str, unsigned short *port_ptr)
                /* Note: under VMS with SOCKETSHR, it seems like the first
                 * parameter is 'char *', instead of 'const char *'
                 */
-               s=getservbyname(
 #ifndef CONST_STRICT
-                   (char *)
+               s=getservbyname((char *)str,"tcp");
+#else
+               s=getservbyname(str,"tcp");
 #endif
-                   str,"tcp");
                if(s != NULL)
                        *port_ptr=ntohs((unsigned short)s->s_port);
                CRYPTO_w_unlock(CRYPTO_LOCK_GETSERVBYNAME);
@@ -215,6 +235,10 @@ int BIO_sock_error(int sock)
        int j,i;
        int size;
                 
+#if defined(OPENSSL_SYS_BEOS_R5)
+       return 0;
+#endif
+                
        size=sizeof(int);
        /* Note: under Windows the third parameter is of type (char *)
         * whereas under other systems it is (void *) if you don't have
@@ -228,6 +252,7 @@ int BIO_sock_error(int sock)
                return(j);
        }
 
+#if 0
 long BIO_ghbn_ctrl(int cmd, int iarg, char *parg)
        {
        int i;
@@ -265,7 +290,9 @@ long BIO_ghbn_ctrl(int cmd, int iarg, char *parg)
                }
        return(1);
        }
+#endif
 
+#if 0
 static struct hostent *ghbn_dup(struct hostent *a)
        {
        struct hostent *ret;
@@ -343,20 +370,31 @@ static void ghbn_free(struct hostent *a)
        OPENSSL_free(a);
        }
 
+#endif
+
 struct hostent *BIO_gethostbyname(const char *name)
        {
+#if 1
+       /* Caching gethostbyname() results forever is wrong,
+        * so we have to let the true gethostbyname() worry about this */
+#if (defined(NETWARE_BSDSOCK) && !defined(__NOVELL_LIBC__))
+       return gethostbyname((char*)name);
+#else
+       return gethostbyname(name);
+#endif
+#else
        struct hostent *ret;
        int i,lowi=0,j;
        unsigned long low= (unsigned long)-1;
 
-/*     return(gethostbyname(name)); */
 
-#if 0 /* It doesn't make sense to use locking here: The function interface
-          * is not thread-safe, because threads can never be sure when
-          * some other thread destroys the data they were given a pointer to.
-          */
+#  if 0
+       /* It doesn't make sense to use locking here: The function interface
+        * is not thread-safe, because threads can never be sure when
+        * some other thread destroys the data they were given a pointer to.
+        */
        CRYPTO_w_lock(CRYPTO_LOCK_GETHOSTBYNAME);
-#endif
+#  endif
        j=strlen(name);
        if (j < 128)
                {
@@ -383,21 +421,22 @@ struct hostent *BIO_gethostbyname(const char *name)
                /* Note: under VMS with SOCKETSHR, it seems like the first
                 * parameter is 'char *', instead of 'const char *'
                 */
-               ret=gethostbyname(
-#ifndef CONST_STRICT
-                   (char *)
-#endif
-                   name);
+#  ifndef CONST_STRICT
+               ret=gethostbyname((char *)name);
+#  else
+               ret=gethostbyname(name);
+#  endif
 
                if (ret == NULL)
                        goto end;
                if (j > 128) /* too big to cache */
                        {
-#if 0 /* If we were trying to make this function thread-safe (which
-          * is bound to fail), we'd have to give up in this case
-          * (or allocate more memory). */
+#  if 0
+                       /* If we were trying to make this function thread-safe (which
+                        * is bound to fail), we'd have to give up in this case
+                        * (or allocate more memory). */
                        ret = NULL;
-#endif
+#  endif
                        goto end;
                        }
 
@@ -421,27 +460,31 @@ struct hostent *BIO_gethostbyname(const char *name)
                ghbn_cache[i].order=BIO_ghbn_miss+BIO_ghbn_hits;
                }
 end:
-#if 0
+#  if 0
        CRYPTO_w_unlock(CRYPTO_LOCK_GETHOSTBYNAME);
-#endif
+#  endif
        return(ret);
+#endif
        }
 
+
 int BIO_sock_init(void)
        {
-#ifdef WINDOWS
+#ifdef OPENSSL_SYS_WINDOWS
        static struct WSAData wsa_state;
 
        if (!wsa_init_done)
                {
                int err;
          
-#ifdef SIGINT
-               signal(SIGINT,(void (*)(int))BIO_sock_cleanup);
-#endif
                wsa_init_done=1;
                memset(&wsa_state,0,sizeof(wsa_state));
-               if (WSAStartup(0x0101,&wsa_state)!=0)
+               /* Not making wsa_state available to the rest of the
+                * code is formally wrong. But the structures we use
+                * are [beleived to be] invariable among Winsock DLLs,
+                * while API availability is [expected to be] probed
+                * at run-time with DSO_global_lookup. */
+               if (WSAStartup(0x0202,&wsa_state)!=0)
                        {
                        err=WSAGetLastError();
                        SYSerr(SYS_F_WSASTARTUP,err);
@@ -449,29 +492,67 @@ int BIO_sock_init(void)
                        return(-1);
                        }
                }
-#endif /* WINDOWS */
+#endif /* OPENSSL_SYS_WINDOWS */
+#ifdef WATT32
+       extern int _watt_do_exit;
+       _watt_do_exit = 0;    /* don't make sock_init() call exit() */
+       if (sock_init())
+               return (-1);
+#endif
+
+#if defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK)
+    WORD wVerReq;
+    WSADATA wsaData;
+    int err;
+
+    if (!wsa_init_done)
+    {
+        wsa_init_done=1;
+        wVerReq = MAKEWORD( 2, 0 );
+        err = WSAStartup(wVerReq,&wsaData);
+        if (err != 0)
+        {
+            SYSerr(SYS_F_WSASTARTUP,err);
+            BIOerr(BIO_F_BIO_SOCK_INIT,BIO_R_WSASTARTUP);
+            return(-1);
+                       }
+               }
+#endif
+
        return(1);
        }
 
 void BIO_sock_cleanup(void)
        {
-#ifdef WINDOWS
+#ifdef OPENSSL_SYS_WINDOWS
        if (wsa_init_done)
                {
                wsa_init_done=0;
+#if 0          /* this call is claimed to be non-present in Winsock2 */
                WSACancelBlockingCall();
+#endif
                WSACleanup();
                }
+#elif defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK)
+   if (wsa_init_done)
+        {
+        wsa_init_done=0;
+        WSACleanup();
+               }
 #endif
        }
 
-#if !defined(VMS) || __VMS_VER >= 70000000
+#if !defined(OPENSSL_SYS_VMS) || __VMS_VER >= 70000000
 
-int BIO_socket_ioctl(int fd, long type, unsigned long *arg)
+int BIO_socket_ioctl(int fd, long type, void *arg)
        {
        int i;
 
+#ifdef __DJGPP__
+       i=ioctlsocket(fd,type,(char *)arg);
+#else
        i=ioctlsocket(fd,type,arg);
+#endif /* __DJGPP__ */
        if (i < 0)
                SYSerr(SYS_F_IOCTLSOCKET,get_last_socket_error());
        return(i);
@@ -494,16 +575,16 @@ static int get_ip(const char *str, unsigned char ip[4])
                        {
                        ok=1;
                        tmp[num]=tmp[num]*10+c-'0';
-                       if (tmp[num] > 255) return(-1);
+                       if (tmp[num] > 255) return(0);
                        }
                else if (c == '.')
                        {
                        if (!ok) return(-1);
-                       if (num == 3) break;
+                       if (num == 3) return(0);
                        num++;
                        ok=0;
                        }
-               else if ((num == 3) && ok)
+               else if (c == '\0' && (num == 3) && ok)
                        break;
                else
                        return(0);
@@ -518,12 +599,18 @@ static int get_ip(const char *str, unsigned char ip[4])
 int BIO_get_accept_socket(char *host, int bind_mode)
        {
        int ret=0;
-       struct sockaddr_in server,client;
-       int s= -1,cs;
+       union {
+               struct sockaddr sa;
+               struct sockaddr_in sa_in;
+#if OPENSSL_USE_IPV6
+               struct sockaddr_in6 sa_in6;
+#endif
+       } server,client;
+       int s=INVALID_SOCKET,cs;
        unsigned char ip[4];
        unsigned short port;
-       char *str,*e;
-       const char *h,*p;
+       char *str=NULL,*e;
+       char *h,*p;
        unsigned long l;
        int err_num;
 
@@ -537,8 +624,7 @@ int BIO_get_accept_socket(char *host, int bind_mode)
                {
                if (*e == ':')
                        {
-                       p= &(e[1]);
-                       *e='\0';
+                       p=e;
                        }
                else if (*e == '/')
                        {
@@ -546,34 +632,80 @@ int BIO_get_accept_socket(char *host, int bind_mode)
                        break;
                        }
                }
-
-       if (p == NULL)
+       if (p)  *p++='\0';      /* points at last ':', '::port' is special [see below] */
+       else    p=h,h=NULL;
+
+#ifdef EAI_FAMILY
+       do {
+       static union {  void *p;
+                       int (WSAAPI *f)(const char *,const char *,
+                                const struct addrinfo *,
+                                struct addrinfo **);
+                       } p_getaddrinfo = {NULL};
+       static union {  void *p;
+                       void (WSAAPI *f)(struct addrinfo *);
+                       } p_freeaddrinfo = {NULL};
+       struct addrinfo *res,hint;
+
+       if (p_getaddrinfo.p==NULL)
                {
-               p=h;
-               h="*";
+               if ((p_getaddrinfo.p=DSO_global_lookup("getaddrinfo"))==NULL ||
+                   (p_freeaddrinfo.p=DSO_global_lookup("freeaddrinfo"))==NULL)
+                       p_getaddrinfo.p=(void*)-1;
                }
+       if (p_getaddrinfo.p==(void *)-1) break;
+
+       /* '::port' enforces IPv6 wildcard listener. Some OSes,
+        * e.g. Solaris, default to IPv6 without any hint. Also
+        * note that commonly IPv6 wildchard socket can service
+        * IPv4 connections just as well...  */
+       memset(&hint,0,sizeof(hint));
+       if (h)
+               {
+               if (strchr(h,':'))
+                       {
+                       if (h[1]=='\0') h=NULL;
+#if OPENSSL_USE_IPV6
+                       hint.ai_family = AF_INET6;
+#else
+                       h=NULL;
+#endif
+                       }
+               else if (h[0]=='*' && h[1]=='\0')
+                       h=NULL;
+               }
+
+       if ((*p_getaddrinfo.f)(h,p,&hint,&res)) break;
+
+       memcpy(&server, res->ai_addr,
+               res->ai_addrlen<=sizeof(server)?res->ai_addrlen:sizeof(server));
 
-       if (!BIO_get_port(p,&port)) return(INVALID_SOCKET);
+       (*p_freeaddrinfo.f)(res);
+       goto again;
+       } while (0);
+#endif
+
+       if (!BIO_get_port(p,&port)) goto err;
 
        memset((char *)&server,0,sizeof(server));
-       server.sin_family=AF_INET;
-       server.sin_port=htons(port);
+       server.sa_in.sin_family=AF_INET;
+       server.sa_in.sin_port=htons(port);
 
-       if (strcmp(h,"*") == 0)
-               server.sin_addr.s_addr=INADDR_ANY;
+       if (h == NULL || strcmp(h,"*") == 0)
+               server.sa_in.sin_addr.s_addr=INADDR_ANY;
        else
                {
-               if (!BIO_get_host_ip(h,&(ip[0]))) return(INVALID_SOCKET);
+                if (!BIO_get_host_ip(h,&(ip[0]))) goto err;
                l=(unsigned long)
                        ((unsigned long)ip[0]<<24L)|
                        ((unsigned long)ip[1]<<16L)|
                        ((unsigned long)ip[2]<< 8L)|
                        ((unsigned long)ip[3]);
-               server.sin_addr.s_addr=htonl(l);
+               server.sa_in.sin_addr.s_addr=htonl(l);
                }
 
 again:
-       s=socket(AF_INET,SOCK_STREAM,SOCKET_PROTOCOL);
+       s=socket(server.sa.sa_family,SOCK_STREAM,SOCKET_PROTOCOL);
        if (s == INVALID_SOCKET)
                {
                SYSerr(SYS_F_SOCKET,get_last_socket_error());
@@ -591,22 +723,35 @@ again:
                bind_mode=BIO_BIND_NORMAL;
                }
 #endif
-       if (bind(s,(struct sockaddr *)&server,sizeof(server)) == -1)
+       if (bind(s,&server.sa,sizeof(server)) == -1)
                {
 #ifdef SO_REUSEADDR
                err_num=get_last_socket_error();
                if ((bind_mode == BIO_BIND_REUSEADDR_IF_UNUSED) &&
                        (err_num == EADDRINUSE))
                        {
-                       memcpy((char *)&client,(char *)&server,sizeof(server));
-                       if (strcmp(h,"*") == 0)
-                               client.sin_addr.s_addr=htonl(0x7F000001);
-                       cs=socket(AF_INET,SOCK_STREAM,SOCKET_PROTOCOL);
+                       client = server;
+                       if (h == NULL || strcmp(h,"*") == 0)
+                               {
+#if OPENSSL_USE_IPV6
+                               if (client.sa.sa_family == AF_INET6)
+                                       {
+                                       memset(&client.sa_in6.sin6_addr,0,sizeof(client.sa_in6.sin6_addr));
+                                       client.sa_in6.sin6_addr.s6_addr[15]=1;
+                                       }
+                               else
+#endif
+                               if (client.sa.sa_family == AF_INET)
+                                       {
+                                       client.sa_in.sin_addr.s_addr=htonl(0x7F000001);
+                                       }
+                               else    goto err;
+                               }
+                       cs=socket(client.sa.sa_family,SOCK_STREAM,SOCKET_PROTOCOL);
                        if (cs != INVALID_SOCKET)
                                {
                                int ii;
-                               ii=connect(cs,(struct sockaddr *)&client,
-                                       sizeof(client));
+                               ii=connect(cs,&client.sa,sizeof(client));
                                closesocket(cs);
                                if (ii == INVALID_SOCKET)
                                        {
@@ -645,20 +790,52 @@ err:
 int BIO_accept(int sock, char **addr)
        {
        int ret=INVALID_SOCKET;
-       static struct sockaddr_in from;
        unsigned long l;
        unsigned short port;
-       int len;
        char *p;
 
-       memset((char *)&from,0,sizeof(from));
-       len=sizeof(from);
-       /* Note: under VMS with SOCKETSHR the fourth parameter is currently
-        * of type (int *) whereas under other systems it is (void *) if
-        * you don't have a cast it will choke the compiler: if you do
-        * have a cast then you can either go for (int *) or (void *).
+       struct {
+       /*
+        * As for following union. Trouble is that there are platforms
+        * that have socklen_t and there are platforms that don't, on
+        * some platforms socklen_t is int and on some size_t. So what
+        * one can do? One can cook #ifdef spaghetti, which is nothing
+        * but masochistic. Or one can do union between int and size_t.
+        * One naturally does it primarily for 64-bit platforms where
+        * sizeof(int) != sizeof(size_t). But would it work? Note that
+        * if size_t member is initialized to 0, then later int member
+        * assignment naturally does the job on little-endian platforms
+        * regardless accept's expectations! What about big-endians?
+        * If accept expects int*, then it works, and if size_t*, then
+        * length value would appear as unreasonably large. But this
+        * won't prevent it from filling in the address structure. The
+        * trouble of course would be if accept returns more data than
+        * actual buffer can accomodate and overwrite stack... That's
+        * where early OPENSSL_assert comes into picture. Besides, the
+        * only 64-bit big-endian platform found so far that expects
+        * size_t* is HP-UX, where stack grows towards higher address.
+        * <appro>
         */
-       ret=accept(sock,(struct sockaddr *)&from,(void *)&len);
+       union { size_t s; int i; } len;
+       union {
+               struct sockaddr sa;
+               struct sockaddr_in sa_in;
+#if OPENSSL_USE_IPV6
+               struct sockaddr_in6 sa_in6;
+#endif
+               } from;
+       } sa;
+
+       sa.len.s=0;
+       sa.len.i=sizeof(sa.from);
+       memset(&sa.from,0,sizeof(sa.from));
+       ret=accept(sock,&sa.from.sa,(void *)&sa.len);
+       if (sizeof(sa.len.i)!=sizeof(sa.len.s) && sa.len.i==0)
+               {
+               OPENSSL_assert(sa.len.s<=sizeof(sa.from));
+               sa.len.i = (int)sa.len.s;
+               /* use sa.len.i from this point */
+               }
        if (ret == INVALID_SOCKET)
                {
                if(BIO_sock_should_retry(ret)) return -2;
@@ -669,8 +846,46 @@ int BIO_accept(int sock, char **addr)
 
        if (addr == NULL) goto end;
 
-       l=ntohl(from.sin_addr.s_addr);
-       port=ntohs(from.sin_port);
+#ifdef EAI_FAMILY
+       do {
+       char   h[NI_MAXHOST],s[NI_MAXSERV];
+       size_t nl;
+       static union {  void *p;
+                       int (WSAAPI *f)(const struct sockaddr *,size_t/*socklen_t*/,
+                                char *,size_t,char *,size_t,int);
+                       } p_getnameinfo = {NULL};
+                       /* 2nd argument to getnameinfo is specified to
+                        * be socklen_t. Unfortunately there is a number
+                        * of environments where socklen_t is not defined.
+                        * As it's passed by value, it's safe to pass it
+                        * as size_t... <appro> */
+
+       if (p_getnameinfo.p==NULL)
+               {
+               if ((p_getnameinfo.p=DSO_global_lookup("getnameinfo"))==NULL)
+                       p_getnameinfo.p=(void*)-1;
+               }
+       if (p_getnameinfo.p==(void *)-1) break;
+
+       if ((*p_getnameinfo.f)(&sa.from.sa,sa.len.i,h,sizeof(h),s,sizeof(s),
+           NI_NUMERICHOST|NI_NUMERICSERV)) break;
+       nl = strlen(h)+strlen(s)+2;
+       p = *addr;
+       if (p)  { *p = '\0'; p = OPENSSL_realloc(p,nl); }
+       else    { p = OPENSSL_malloc(nl);               }
+       if (p==NULL)
+               {
+               BIOerr(BIO_F_BIO_ACCEPT,ERR_R_MALLOC_FAILURE);
+               goto end;
+               }
+       *addr = p;
+       BIO_snprintf(*addr,nl,"%s:%s",h,s);
+       goto end;
+       } while(0);
+#endif
+       if (sa.from.sa.sa_family != AF_INET) goto end;
+       l=ntohl(sa.from.sa_in.sin_addr.s_addr);
+       port=ntohs(sa.from.sa_in.sin_port);
        if (*addr == NULL)
                {
                if ((p=OPENSSL_malloc(24)) == NULL)
@@ -680,12 +895,12 @@ int BIO_accept(int sock, char **addr)
                        }
                *addr=p;
                }
-       sprintf(*addr,"%d.%d.%d.%d:%d",
-               (unsigned char)(l>>24L)&0xff,
-               (unsigned char)(l>>16L)&0xff,
-               (unsigned char)(l>> 8L)&0xff,
-               (unsigned char)(l     )&0xff,
-               port);
+       BIO_snprintf(*addr,24,"%d.%d.%d.%d:%d",
+                    (unsigned char)(l>>24L)&0xff,
+                    (unsigned char)(l>>16L)&0xff,
+                    (unsigned char)(l>> 8L)&0xff,
+                    (unsigned char)(l     )&0xff,
+                    port);
 end:
        return(ret);
        }
@@ -713,7 +928,7 @@ int BIO_set_tcp_ndelay(int s, int on)
 int BIO_socket_nbio(int s, int mode)
        {
        int ret= -1;
-       unsigned long l;
+       int l;
 
        l=mode;
 #ifdef FIONBIO