Index: openssl/crypto/bio/b_sock.c |
=================================================================== |
--- openssl/crypto/bio/b_sock.c (revision 105093) |
+++ openssl/crypto/bio/b_sock.c (working copy) |
@@ -72,11 +72,9 @@ |
#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 |
@@ -90,6 +88,17 @@ |
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; |
@@ -226,6 +235,10 @@ |
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 |
@@ -466,7 +479,12 @@ |
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); |
@@ -510,8 +528,8 @@ |
if (wsa_init_done) |
{ |
wsa_init_done=0; |
-#ifndef OPENSSL_SYS_WINCE |
- WSACancelBlockingCall(); /* Winsock 1.1 specific */ |
+#if 0 /* this call is claimed to be non-present in Winsock2 */ |
+ WSACancelBlockingCall(); |
#endif |
WSACleanup(); |
} |
@@ -533,7 +551,30 @@ |
#ifdef __DJGPP__ |
i=ioctlsocket(fd,type,(char *)arg); |
#else |
- i=ioctlsocket(fd,type,arg); |
+# if defined(OPENSSL_SYS_VMS) |
+ /* 2011-02-18 SMS. |
+ * VMS ioctl() can't tolerate a 64-bit "void *arg", but we |
+ * observe that all the consumers pass in an "unsigned long *", |
+ * so we arrange a local copy with a short pointer, and use |
+ * that, instead. |
+ */ |
+# if __INITIAL_POINTER_SIZE == 64 |
+# define ARG arg_32p |
+# pragma pointer_size save |
+# pragma pointer_size 32 |
+ unsigned long arg_32; |
+ unsigned long *arg_32p; |
+# pragma pointer_size restore |
+ arg_32p = &arg_32; |
+ arg_32 = *((unsigned long *) arg); |
+# else /* __INITIAL_POINTER_SIZE == 64 */ |
+# define ARG arg |
+# endif /* __INITIAL_POINTER_SIZE == 64 [else] */ |
+# else /* defined(OPENSSL_SYS_VMS) */ |
+# define ARG arg |
+# endif /* defined(OPENSSL_SYS_VMS) [else] */ |
+ |
+ i=ioctlsocket(fd,type,ARG); |
#endif /* __DJGPP__ */ |
if (i < 0) |
SYSerr(SYS_F_IOCTLSOCKET,get_last_socket_error()); |
@@ -581,12 +622,18 @@ |
int BIO_get_accept_socket(char *host, int bind_mode) |
{ |
int ret=0; |
- struct sockaddr_in server,client; |
- int s=INVALID_SOCKET,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,addrlen; |
unsigned char ip[4]; |
unsigned short port; |
char *str=NULL,*e; |
- const char *h,*p; |
+ char *h,*p; |
unsigned long l; |
int err_num; |
@@ -600,8 +647,7 @@ |
{ |
if (*e == ':') |
{ |
- p= &(e[1]); |
- *e='\0'; |
+ p=e; |
} |
else if (*e == '/') |
{ |
@@ -609,21 +655,74 @@ |
break; |
} |
} |
+ if (p) *p++='\0'; /* points at last ':', '::port' is special [see below] */ |
+ else p=h,h=NULL; |
- if (p == 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)); |
+ hint.ai_flags = AI_PASSIVE; |
+ 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') |
+ { |
+ hint.ai_family = AF_INET; |
+ h=NULL; |
+ } |
+ } |
+ |
+ if ((*p_getaddrinfo.f)(h,p,&hint,&res)) break; |
+ |
+ addrlen = res->ai_addrlen<=sizeof(server) ? |
+ res->ai_addrlen : |
+ sizeof(server); |
+ memcpy(&server, res->ai_addr, addrlen); |
+ |
+ (*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); |
+ addrlen = sizeof(server.sa_in); |
- 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]))) goto err; |
@@ -632,11 +731,11 @@ |
((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()); |
@@ -654,7 +753,7 @@ |
bind_mode=BIO_BIND_NORMAL; |
} |
#endif |
- if (bind(s,(struct sockaddr *)&server,sizeof(server)) == -1) |
+ if (bind(s,&server.sa,addrlen) == -1) |
{ |
#ifdef SO_REUSEADDR |
err_num=get_last_socket_error(); |
@@ -668,15 +767,28 @@ |
(err_num == EADDRINUSE)) |
#endif |
{ |
- 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,addrlen); |
closesocket(cs); |
if (ii == INVALID_SOCKET) |
{ |
@@ -715,20 +827,52 @@ |
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; |
@@ -739,8 +883,46 @@ |
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) |