Fix warnings about "sin" conflicting with sin(3) definition.
[openssl.git] / crypto / bio / b_sock.c
index d619bcf995610681e852607a7b6b23992fb03b54..710a8d6d962518ea4af07c10ef4d026c8aac7487 100644 (file)
 #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
 
-#ifdef OPENSSL_SYS_WIN16
-#define SOCKET_PROTOCOL 0 /* more microsoft stupidity */
-#else
+#include <openssl/dso.h>
+
 #define SOCKET_PROTOCOL IPPROTO_TCP
-#endif
 
 #ifdef SO_MAXCONN
 #define MAX_LISTEN  SO_MAXCONN
@@ -79,7 +80,7 @@
 #define MAX_LISTEN  32
 #endif
 
-#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_NETWARE)
+#if defined(OPENSSL_SYS_WINDOWS) || (defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK))
 static int wsa_init_done=0;
 #endif
 
@@ -458,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);
@@ -474,7 +480,7 @@ int BIO_sock_init(void)
                return (-1);
 #endif
 
-#if defined(OPENSSL_SYS_NETWARE)
+#if defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK)
     WORD wVerReq;
     WSADATA wsaData;
     int err;
@@ -512,7 +518,7 @@ void BIO_sock_cleanup(void)
 #endif
                WSACleanup();
                }
-#elif defined(OPENSSL_SYS_NETWARE)
+#elif defined(OPENSSL_SYS_NETWARE) && !defined(NETWARE_BSDSOCK)
    if (wsa_init_done)
         {
         wsa_init_done=0;
@@ -578,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 *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;
 
@@ -597,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 == '/')
                        {
@@ -606,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 **);
+                       } p_getaddrinfo = {NULL};
+       static union {  void *p;
+                       void (*f)(struct addrinfo *);
+                       } p_freeaddrinfo = {NULL};
+       struct addrinfo *res,hint;
+
+       if (p_getaddrinfo.p==NULL)
+               {
+               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)
                {
-               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 ((*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;
@@ -629,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);
+               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());
@@ -651,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 *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;
@@ -705,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 *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;
@@ -729,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 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)
@@ -740,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);
        }