The VMS I run on doesn't know socklen_t and uses size_t instead.
[openssl.git] / crypto / bio / b_sock.c
index d4b44b62bbaac55f55c488eb6ba16dd6e78229ba..d20f25ba7f04951f230557b1b95926cf6bbdd22d 100644 (file)
  * [including the GNU Public Licence.]
  */
 
-#ifndef OPENSSL_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"
+#endif
+
+#ifndef OPENSSL_NO_SOCK
+
+#include <openssl/dso.h>
 
-#ifdef OPENSSL_SYS_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 OPENSSL_SYS_WINDOWS
+#if defined(OPENSSL_SYS_WINDOWS) || (defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK))
 static int wsa_init_done=0;
 #endif
 
+#if 0
 static unsigned long BIO_ghbn_hits=0L;
 static unsigned long BIO_ghbn_miss=0L;
 
@@ -93,10 +95,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;
@@ -228,6 +233,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 +271,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 +351,27 @@ 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 */
+       return gethostbyname(name);
+#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)
                {
@@ -384,20 +399,21 @@ struct hostent *BIO_gethostbyname(const char *name)
                 * parameter is 'char *', instead of 'const char *'
                 */
                ret=gethostbyname(
-#ifndef CONST_STRICT
+#  ifndef CONST_STRICT
                    (char *)
-#endif
+#  endif
                    name);
 
                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,12 +437,14 @@ 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 OPENSSL_SYS_WINDOWS
@@ -441,7 +459,12 @@ int BIO_sock_init(void)
 #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);
@@ -450,6 +473,37 @@ int BIO_sock_init(void)
                        }
                }
 #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)
+    {
+   
+# ifdef SIGINT
+        signal(SIGINT,(void (*)(int))BIO_sock_cleanup);
+# endif
+
+        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);
        }
 
@@ -459,19 +513,31 @@ void BIO_sock_cleanup(void)
        if (wsa_init_done)
                {
                wsa_init_done=0;
+#ifndef OPENSSL_SYS_WINCE
                WSACancelBlockingCall();
+#endif
                WSACleanup();
                }
+#elif defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK)
+   if (wsa_init_done)
+        {
+        wsa_init_done=0;
+        WSACleanup();
+               }
 #endif
        }
 
 #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 +560,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 +584,13 @@ 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;
+       struct sockaddr server,client;
+       struct sockaddr_in *sin;
        int s=INVALID_SOCKET,cs;
        unsigned char ip[4];
        unsigned short port;
        char *str=NULL,*e;
-       const char *h,*p;
+       char *h,*p;
        unsigned long l;
        int err_num;
 
@@ -537,8 +604,7 @@ int BIO_get_accept_socket(char *host, int bind_mode)
                {
                if (*e == ':')
                        {
-                       p= &(e[1]);
-                       *e='\0';
+                       p=e;
                        }
                else if (*e == '/')
                        {
@@ -546,21 +612,65 @@ 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 (*f)(const char *,const char *,
+                                const struct addrinfo *,
+                                struct addrinfo **);
+                       } getaddrinfo = {NULL};
+       static union {  void *p;
+                       void (*f)(struct addrinfo *);
+                       } freeaddrinfo = {NULL};
+       struct addrinfo *res,hint;
+
+       if (getaddrinfo.p==NULL)
+               {
+               if ((getaddrinfo.p=DSO_global_lookup("getaddrinfo"))==NULL ||
+                   (freeaddrinfo.p=DSO_global_lookup("freeaddrinfo"))==NULL)
+                       getaddrinfo.p=(void*)-1;
+               }
+       if (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)
                {
-               p=h;
-               h="*";
+               if (strchr(h,':'))
+                       {
+                       if (h[1]=='\0') h=NULL;
+#ifdef AF_INET6
+                       hint.ai_family = AF_INET6;
+#else
+                       h=NULL;
+#endif
+                       }
+               else if (h[0]=='*' && h[1]=='\0')
+                       h=NULL;
                }
 
+       if ((*getaddrinfo.f)(h,p,&hint,&res)) break;
+       server = *res->ai_addr;
+       (*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);
+       sin = (struct sockaddr_in *)&server;
+       sin->sin_family=AF_INET;
+       sin->sin_port=htons(port);
 
-       if (strcmp(h,"*") == 0)
-               server.sin_addr.s_addr=INADDR_ANY;
+       if (h == NULL || strcmp(h,"*") == 0)
+               sin->sin_addr.s_addr=INADDR_ANY;
        else
                {
                 if (!BIO_get_host_ip(h,&(ip[0]))) goto err;
@@ -569,11 +679,11 @@ int BIO_get_accept_socket(char *host, int bind_mode)
                        ((unsigned long)ip[1]<<16L)|
                        ((unsigned long)ip[2]<< 8L)|
                        ((unsigned long)ip[3]);
-               server.sin_addr.s_addr=htonl(l);
+               sin->sin_addr.s_addr=htonl(l);
                }
 
 again:
-       s=socket(AF_INET,SOCK_STREAM,SOCKET_PROTOCOL);
+       s=socket(server.sa_family,SOCK_STREAM,SOCKET_PROTOCOL);
        if (s == INVALID_SOCKET)
                {
                SYSerr(SYS_F_SOCKET,get_last_socket_error());
@@ -591,17 +701,35 @@ again:
                bind_mode=BIO_BIND_NORMAL;
                }
 #endif
-       if (bind(s,(struct sockaddr *)&server,sizeof(server)) == -1)
+       if (bind(s,&server,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)
+                               {
+#ifdef AF_INET6
+                               if (client.sa_family == AF_INET6)
+                                       {
+                                       struct sockaddr_in6 *sin =
+                                               (struct sockaddr_in6 *)&client;
+                                       memset(&sin->sin6_addr,0,sizeof(sin->sin6_addr));
+                                       sin->sin6_addr.s6_addr[15]=1;
+                                       }
+                               else
+#endif
+                               if (client.sa_family == AF_INET)
+                                       {
+                                       struct sockaddr_in *sin =
+                                               (struct sockaddr_in *)&client;
+                                       sin->sin_addr.s_addr=htonl(0x7F000001);
+                                       }
+                               else    goto err;
+                               }
+                       cs=socket(client.sa_family,SOCK_STREAM,SOCKET_PROTOCOL);
                        if (cs != INVALID_SOCKET)
                                {
                                int ii;
@@ -645,20 +773,21 @@ err:
 int BIO_accept(int sock, char **addr)
        {
        int ret=INVALID_SOCKET;
-       static struct sockaddr_in from;
+       struct sockaddr from;
+       struct sockaddr_in *sin;
        unsigned long l;
        unsigned short port;
        int len;
        char *p;
 
-       memset((char *)&from,0,sizeof(from));
+       memset(&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 *).
         */
-       ret=accept(sock,(struct sockaddr *)&from,(void *)&len);
+       ret=accept(sock,&from,(void *)&len);
        if (ret == INVALID_SOCKET)
                {
                if(BIO_sock_should_retry(ret)) return -2;
@@ -669,8 +798,47 @@ 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
+# ifdef OPENSSL_SYS_VMS
+#  define SOCKLEN_T size_t
+# else
+#  define SOCKLEN_T socklen_t
+#endif
+       do {
+       char   h[NI_MAXHOST],s[NI_MAXSERV];
+       size_t l;
+       static union {  void *p;
+                       int (*f)(const struct sockaddr *,SOCKLEN_T,
+                                char *,size_t,char *,size_t,int);
+                       } getnameinfo = {NULL};
+
+       if (getnameinfo.p==NULL)
+               {
+               if ((getnameinfo.p=DSO_global_lookup("getnameinfo"))==NULL)
+                       getnameinfo.p=(void*)-1;
+               }
+       if (getnameinfo.p==(void *)-1) break;
+
+       if ((*getnameinfo.f)(&from,sizeof(from),h,sizeof(h),s,sizeof(s),
+           NI_NUMERICHOST|NI_NUMERICSERV)) break;
+       l = strlen(h)+strlen(s)+2; if (len<24) len=24;
+       p = *addr;
+       if (p)  { *p = '\0'; p = OPENSSL_realloc(p,l);  }
+       else    { p = OPENSSL_malloc(l);                }
+       if (p==NULL)
+               {
+               BIOerr(BIO_F_BIO_ACCEPT,ERR_R_MALLOC_FAILURE);
+               goto end;
+               }
+       *addr = p;
+       BIO_snprintf(*addr,l,"%s:%s",h,s);
+       goto end;
+       } while(0);
+#endif
+       if (from.sa_family != AF_INET) goto end;
+       sin = (struct sockaddr_in *)&from;
+       l=ntohl(sin->sin_addr.s_addr);
+       port=ntohs(sin->sin_port);
        if (*addr == NULL)
                {
                if ((p=OPENSSL_malloc(24)) == NULL)
@@ -680,12 +848,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 +881,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