It's inappropraite to override application signal, nor is it appropriate
[openssl.git] / crypto / bio / b_sock.c
index 86f38172fba682f8b46b8b477bf0358f0a03add9..e4c7ee12778667ee7daa076f709530ef108a9508 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  SO_MAXCONN
 #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 defined(OPENSSL_SYS_BEOS_BONE)             
+/* BONE's IP6 support is incomplete */
+#undef AF_INET6
+#endif
+
 #if 0
 static unsigned long BIO_ghbn_hits=0L;
 static unsigned long BIO_ghbn_miss=0L;
@@ -219,6 +225,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
@@ -453,12 +463,14 @@ int BIO_sock_init(void)
                {
                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);
@@ -473,6 +485,26 @@ int BIO_sock_init(void)
        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);
        }
 
@@ -482,17 +514,23 @@ void BIO_sock_cleanup(void)
        if (wsa_init_done)
                {
                wsa_init_done=0;
-#ifndef OPENSSL_SYS_WINCE
+#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(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;
 
@@ -547,12 +585,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 *sa_in;
        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;
 
@@ -566,8 +605,7 @@ int BIO_get_accept_socket(char *host, int bind_mode)
                {
                if (*e == ':')
                        {
-                       p= &(e[1]);
-                       *e='\0';
+                       p=e;
                        }
                else if (*e == '/')
                        {
@@ -575,21 +613,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 **);
+                       } p_getaddrinfo = {NULL};
+       static union {  void *p;
+                       void (*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;
+#ifdef AF_INET6
+                       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;
+       server = *res->ai_addr;
+       (*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);
+       sa_in = (struct sockaddr_in *)&server;
+       sa_in->sin_family=AF_INET;
+       sa_in->sin_port=htons(port);
 
-       if (strcmp(h,"*") == 0)
-               server.sin_addr.s_addr=INADDR_ANY;
+       if (h == NULL || strcmp(h,"*") == 0)
+               sa_in->sin_addr.s_addr=INADDR_ANY;
        else
                {
                 if (!BIO_get_host_ip(h,&(ip[0]))) goto err;
@@ -598,11 +680,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);
+               sa_in->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());
@@ -620,17 +702,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 *sin6 =
+                                               (struct sockaddr_in6 *)&client;
+                                       memset(&sin6->sin6_addr,0,sizeof(sin6->sin6_addr));
+                                       sin6->sin6_addr.s6_addr[15]=1;
+                                       }
+                               else
+#endif
+                               if (client.sa_family == AF_INET)
+                                       {
+                                       struct sockaddr_in *sin6 =
+                                               (struct sockaddr_in *)&client;
+                                       sin6->sin_addr.s_addr=htonl(0x7F000001);
+                                       }
+                               else    goto err;
+                               }
+                       cs=socket(client.sa_family,SOCK_STREAM,SOCKET_PROTOCOL);
                        if (cs != INVALID_SOCKET)
                                {
                                int ii;
@@ -674,20 +774,21 @@ err:
 int BIO_accept(int sock, char **addr)
        {
        int ret=INVALID_SOCKET;
-       static struct sockaddr_in from;
+       struct sockaddr from;
+       struct sockaddr_in *sa_in;
        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;
@@ -698,8 +799,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
+# if defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_BEOS_BONE)
+#  define SOCKLEN_T size_t
+# else
+#  define SOCKLEN_T socklen_t
+#endif
+       do {
+       char   h[NI_MAXHOST],s[NI_MAXSERV];
+       size_t nl;
+       static union {  void *p;
+                       int (*f)(const struct sockaddr *,SOCKLEN_T,
+                                char *,size_t,char *,size_t,int);
+                       } p_getnameinfo = {NULL};
+
+       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)(&from,sizeof(from),h,sizeof(h),s,sizeof(s),
+           NI_NUMERICHOST|NI_NUMERICSERV)) break;
+       nl = strlen(h)+strlen(s)+2; if (len<24) len=24;
+       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 (from.sa_family != AF_INET) goto end;
+       sa_in = (struct sockaddr_in *)&from;
+       l=ntohl(sa_in->sin_addr.s_addr);
+       port=ntohs(sa_in->sin_port);
        if (*addr == NULL)
                {
                if ((p=OPENSSL_malloc(24)) == NULL)
@@ -709,12 +849,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);
        }
@@ -742,7 +882,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