On Tue, Jul 18, 2006 at 02:06:12PM +0200, Richard Levitte via RT wrote:
> 
> I'm taking a look at it.  Don't worry about RT "stripping" the 
> attachment, that's just for outgoing email.  The patch is in the 
> database, and I just downloaded it.

Richard,

I prepared new version of the patch.

Fixes:

- indentation -- I did the previous patch with -b, which was
not exactly clever;

- OPENSSL_free(*host_ptr) replaces OPENSSL_free(*port_ptr); 

- freeaddrinfo(res0) is now in the err:;

- NI_MAXHOST instead of INET6_ADDRSTRLEN + 16.

Changes:

The patch also changes the logic in conn_state, to go back to
BIO_CONN_S_CREATE_SOCKET from later stages, provided there are some
more records in res left.

The patch also no longer adds those *_ipv6 functions, so that 
changes to the ABI are minimal. Those two functions can always be
added any time later on.

I can also generate just an incremental patch from the previous
patch2, if you prefer.

Are you aware of any software which actually uses the BIO networking?
I'm affraid I did not test these code paths beyond compile.

Yours,

-- 
Jan Pazdziora
diff --git a/crypto/bio/b_sock.c b/crypto/bio/b_sock.c
index 4b3860b..aa6f0e1 100644
--- a/crypto/bio/b_sock.c
+++ b/crypto/bio/b_sock.c
@@ -64,6 +64,7 @@ #include "cryptlib.h"
 #include <openssl/bio.h>
 #if defined(OPENSSL_SYS_NETWARE) && defined(NETWARE_BSDSOCK)
 #include "netdb.h"
+#include "bio_lcl.h"
 #endif
 
 #ifndef OPENSSL_NO_SOCK
@@ -99,6 +100,7 @@ static struct ghbn_cache_st
        } ghbn_cache[GHBN_NUM];
 #endif
 
+#ifndef OPENSSL_NO_DEPRECATED /* not needed since we have IPv6 support */
 static int get_ip(const char *str,unsigned char *ip);
 #if 0
 static void ghbn_free(struct hostent *a);
@@ -216,6 +218,7 @@ #endif
                }
        return(1);
        }
+#endif /* not needed since we have IPv6 support */
 
 int BIO_sock_error(int sock)
        {
@@ -355,6 +358,7 @@ static void ghbn_free(struct hostent *a)
 
 #endif
 
+#ifndef OPENSSL_NO_DEPRECATED /* not needed since we have IPv6 support */
 struct hostent *BIO_gethostbyname(const char *name)
        {
 #if 1
@@ -445,6 +449,56 @@ #  endif
        return(ret);
 #endif
        }
+#endif /* not needed since we have IPv6 support */
+
+/* Utility function to parse host and service (hostname and port)
+   information from a string. The hostname can also be an 
+   IPv6 address in brackets. */
+int extract_host_port(char *str, char **host_ptr, char **port_ptr)
+       {
+       char *end, *port_start;
+
+       *host_ptr = *port_ptr = NULL;
+       end = NULL;
+       port_start = NULL;
+       if (*str == '[' && (end = strchr(str + 1, ']')))
+               {
+               *host_ptr = BUF_strndup(str + 1, end - str - 1);
+               end++;
+               if (*end == ':')
+                       port_start = end + 1;
+               }
+       else
+               {
+               if ((end = strchr(str, ':')) != NULL)
+                       {
+                       *host_ptr = BUF_strndup(str, end - str);
+                       port_start = end + 1;
+                       }
+               else if ((end = strchr(str, '/')) != NULL)
+                       {
+                       *host_ptr = BUF_strndup(str, end - str);
+                       }
+               else
+                       *host_ptr = BUF_strdup(str);
+               }
+       if (*host_ptr == NULL)
+               return -1;
+       if (port_start != NULL)
+               {
+               if ((end = strchr(port_start, '/')) != NULL)
+                       *port_ptr = BUF_strndup(port_start, end - port_start);
+               else
+                       *port_ptr = BUF_strdup(port_start);
+               if (*port_ptr == NULL)
+                       {
+                       OPENSSL_free(*host_ptr);
+                       *host_ptr = *port_ptr = NULL;
+                       return -1;
+                       }
+               }
+       return 0;
+       }
 
 
 int BIO_sock_init(void)
@@ -541,6 +595,7 @@ #endif /* __DJGPP__ */
        }
 #endif /* __VMS_VER */
 
+#ifndef OPENSSL_NO_DEPRECATED /* not needed since we have IPv6 support */
 /* The reason I have implemented this instead of using sscanf is because
  * Visual C 1.52c gives an unresolved external when linking a DLL :-( */
 static int get_ip(const char *str, unsigned char ip[4])
@@ -577,126 +632,151 @@ static int get_ip(const char *str, unsig
        ip[3]=tmp[3];
        return(1);
        }
+#endif /* not needed since we have IPv6 support */
 
 int BIO_get_accept_socket(char *host, int bind_mode)
        {
        int ret=0;
-       struct sockaddr_in server,client;
-       int s=INVALID_SOCKET,cs;
-       unsigned char ip[4];
-       unsigned short port;
-       char *str=NULL,*e;
-       const char *h,*p;
-       unsigned long l;
+       struct addrinfo *res, *res0, hints;
+       int s;
+       char *h, *p;
+       int port_is_numeric;
+       int gai_e;
        int err_num;
 
        if (BIO_sock_init() != 1) return(INVALID_SOCKET);
 
-       if ((str=BUF_strdup(host)) == NULL) return(INVALID_SOCKET);
+       if (extract_host_port(host, &h, &p) != 0)
+               return(INVALID_SOCKET);
 
-       h=p=NULL;
-       h=str;
-       for (e=str; *e; e++)
+       memset(&hints, '\0', sizeof(hints));
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_flags = AI_ADDRCONFIG;
+       if (strcmp(h,"*") == 0)
                {
-               if (*e == ':')
-                       {
-                       p= &(e[1]);
-                       *e='\0';
-                       }
-               else if (*e == '/')
-                       {
-                       *e='\0';
-                       break;
-                       }
+               hints.ai_flags |= AI_PASSIVE;
+               h = NULL;
                }
+       port_is_numeric = 0;
 
-       if (p == NULL)
+redo_with_numeric:
+       gai_e = getaddrinfo(h, p, &hints, &res);
+       if (gai_e)
                {
-               p=h;
-               h="*";
-               }
-
-       if (!BIO_get_port(p,&port)) goto err;
-
-       memset((char *)&server,0,sizeof(server));
-       server.sin_family=AF_INET;
-       server.sin_port=htons(port);
-
-       if (strcmp(h,"*") == 0)
-               server.sin_addr.s_addr=INADDR_ANY;
-       else
-               {
-                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);
+               if (! port_is_numeric)
+                       {
+                       port_is_numeric = 1;
+                       if (strcmp(p,"http") == 0)
+                               p = "80";
+                       else if (strcmp(p,"telnet") == 0)
+                               p = "23";
+                       else if (strcmp(p,"socks") == 0)
+                               p = "1080";
+                       else if (strcmp(p,"https") == 0)
+                               p = "443";
+                       else if (strcmp(p,"ssl") == 0)
+                               p = "443";
+                       else if (strcmp(p,"ftp") == 0)
+                               p = "21";
+                       else if (strcmp(p,"gopher") == 0)
+                               p = "70";
+#if 0
+                       else if (strcmp(p,"wais") == 0)
+                               p = "21";
+#endif
+                       else
+                               port_is_numeric = 0;
+                       if (port_is_numeric)
+                               goto redo_with_numeric;
                }
 
-again:
-       s=socket(AF_INET,SOCK_STREAM,SOCKET_PROTOCOL);
-       if (s == INVALID_SOCKET)
-               {
-               SYSerr(SYS_F_SOCKET,get_last_socket_error());
-               ERR_add_error_data(3,"port='",host,"'");
-               
BIOerr(BIO_F_BIO_GET_ACCEPT_SOCKET,BIO_R_UNABLE_TO_CREATE_SOCKET);
+               ERR_add_error_data(3,"host='",host,"'");
+               ERR_add_error_data(2,"error=", gai_strerror(gai_e));
+               BIOerr(BIO_F_BIO_GET_ACCEPT_SOCKET,BIO_R_FAILED_TO_GETADDRINFO);
                goto err;
                }
 
-#ifdef SO_REUSEADDR
-       if (bind_mode == BIO_BIND_REUSEADDR)
+       res0 = res;
+       s = INVALID_SOCKET;
+       while (res)
                {
-               int i=1;
+again:
+               s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+               if (s == INVALID_SOCKET)
+                       {
+                       if (res->ai_next)
+                               goto nextres;
+                       SYSerr(SYS_F_SOCKET,get_last_socket_error());
+                       ERR_add_error_data(3,"port='",host,"'");
+                       
BIOerr(BIO_F_BIO_GET_ACCEPT_SOCKET,BIO_R_UNABLE_TO_CREATE_SOCKET);
+                       goto err;
+                       }
+#ifdef SO_REUSEADDR
+               if (bind_mode == BIO_BIND_REUSEADDR)
+                       {
+                       int i=1;
 
-               ret=setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&i,sizeof(i));
-               bind_mode=BIO_BIND_NORMAL;
-               }
+                       ret=setsockopt(s,SOL_SOCKET,SO_REUSEADDR,
+                               (char *)&i,sizeof(i));
+                       bind_mode=BIO_BIND_NORMAL;
+                       }
 #endif
-       if (bind(s,(struct sockaddr *)&server,sizeof(server)) == -1)
-               {
-#ifdef SO_REUSEADDR
-               err_num=get_last_socket_error();
-               if ((bind_mode == BIO_BIND_REUSEADDR_IF_UNUSED) &&
-                       (err_num == EADDRINUSE))
+               if (bind(s, res->ai_addr, res->ai_addrlen) == -1)
                        {
-                       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);
-                       if (cs != INVALID_SOCKET)
+#ifdef SO_REUSEADDR
+                       err_num=get_last_socket_error();
+                       if ((bind_mode == BIO_BIND_REUSEADDR_IF_UNUSED) &&
+                               (err_num == EADDRINUSE))
                                {
-                               int ii;
-                               ii=connect(cs,(struct sockaddr *)&client,
-                                       sizeof(client));
-                               closesocket(cs);
-                               if (ii == INVALID_SOCKET)
+                               int cs;
+                               cs=socket(res->ai_family, res->ai_socktype, 
res->ai_protocol);
+                               if (cs != INVALID_SOCKET)
                                        {
-                                       bind_mode=BIO_BIND_REUSEADDR;
-                                       closesocket(s);
-                                       goto again;
+                                       int ii;
+                                       ii=connect(cs, res->ai_addr, 
res->ai_addrlen);
+                                       closesocket(cs);
+                                       if (ii == INVALID_SOCKET)
+                                               {
+                                               bind_mode=BIO_BIND_REUSEADDR;
+                                               closesocket(s);
+                                               goto again;
+                                               }
+                                       /* else error */
                                        }
                                /* else error */
                                }
-                       /* else error */
-                       }
 #endif
-               SYSerr(SYS_F_BIND,err_num);
-               ERR_add_error_data(3,"port='",host,"'");
-               BIOerr(BIO_F_BIO_GET_ACCEPT_SOCKET,BIO_R_UNABLE_TO_BIND_SOCKET);
-               goto err;
-               }
-       if (listen(s,MAX_LISTEN) == -1)
-               {
-               SYSerr(SYS_F_BIND,get_last_socket_error());
-               ERR_add_error_data(3,"port='",host,"'");
-               
BIOerr(BIO_F_BIO_GET_ACCEPT_SOCKET,BIO_R_UNABLE_TO_LISTEN_SOCKET);
-               goto err;
+                       if (res->ai_next)
+                               goto nextres;
+                       SYSerr(SYS_F_BIND,err_num);
+                       ERR_add_error_data(3,"port='",host,"'");
+                       
BIOerr(BIO_F_BIO_GET_ACCEPT_SOCKET,BIO_R_UNABLE_TO_BIND_SOCKET);
+                       goto err;
+                       }
+               if (listen(s,MAX_LISTEN) == -1)
+                       {
+                       if (res->ai_next)
+                               goto nextres;
+                       SYSerr(SYS_F_BIND,get_last_socket_error());
+                       ERR_add_error_data(3,"port='",host,"'");
+                       
BIOerr(BIO_F_BIO_GET_ACCEPT_SOCKET,BIO_R_UNABLE_TO_LISTEN_SOCKET);
+                       goto err;
+                       }
+
+               ret = 1;
+               break;
+
+nextres:
+               if (s != INVALID_SOCKET)
+                       closesocket(s);
+               res = res->ai_next;
                }
-       ret=1;
+
 err:
-       if (str != NULL) OPENSSL_free(str);
+       freeaddrinfo(res0);
+
+       if (h != NULL) OPENSSL_free(h);
+       if (p != NULL && !port_is_numeric) OPENSSL_free(p);
        if ((ret == 0) && (s != INVALID_SOCKET))
                {
                closesocket(s);
@@ -708,20 +788,20 @@ 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;
+       static struct sockaddr_storage from;
+       int fromlen;
+       char address_name[NI_MAXHOST];
+       char port_name[NI_MAXSERV];
+       int outlen;
 
        memset((char *)&from,0,sizeof(from));
-       len=sizeof(from);
+       fromlen=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,(struct sockaddr *)&from,(void *)&fromlen);
        if (ret == INVALID_SOCKET)
                {
                if(BIO_sock_should_retry(ret)) return -2;
@@ -732,23 +812,35 @@ int BIO_accept(int sock, char **addr)
 
        if (addr == NULL) goto end;
 
-       l=ntohl(from.sin_addr.s_addr);
-       port=ntohs(from.sin_port);
+       if (*addr != NULL)
+               OPENSSL_free(*addr);
+       *addr = NULL;
+
+       if (! getnameinfo((struct sockaddr *)&from, fromlen,
+               address_name, sizeof(address_name),
+               port_name, sizeof(port_name),
+               NI_NUMERICHOST | NI_NUMERICSERV))
+               goto end;
+
+       outlen = strlen(address_name) + 1
+               + strlen(port_name) + 1;
+       if (from.ss_family == AF_INET6)
+               outlen += 2;
+       *addr = OPENSSL_malloc(outlen);
+
        if (*addr == NULL)
                {
-               if ((p=OPENSSL_malloc(24)) == NULL)
-                       {
-                       BIOerr(BIO_F_BIO_ACCEPT,ERR_R_MALLOC_FAILURE);
-                       goto end;
-                       }
-               *addr=p;
+               BIOerr(BIO_F_BIO_ACCEPT,ERR_R_MALLOC_FAILURE);
+               goto end;
                }
-       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);
+
+       snprintf(*addr, outlen, "%s%s%s:%s",
+               ( from.ss_family == AF_INET6 ? "[" : "" ),
+               address_name,
+               ( from.ss_family == AF_INET6 ? "]" : "" ),
+               ":",
+               port_name);
+
 end:
        return(ret);
        }
diff --git a/crypto/bio/bio.h b/crypto/bio/bio.h
index 07333cf..0b2196d 100644
--- a/crypto/bio/bio.h
+++ b/crypto/bio/bio.h
@@ -175,12 +175,14 @@ #ifndef   BIO_FLAGS_UPLINK
 #define        BIO_FLAGS_UPLINK        0
 #endif
 
+#ifndef OPENSSL_NO_DEPRECATED /* not needed since we have IPv6 support */
 /* Used in BIO_gethostbyname() */
 #define BIO_GHBN_CTRL_HITS             1
 #define BIO_GHBN_CTRL_MISSES           2
 #define BIO_GHBN_CTRL_CACHE_SIZE       3
 #define BIO_GHBN_CTRL_GET_ENTRY                4
 #define BIO_GHBN_CTRL_FLUSH            5
+#endif /* not needed since we have IPv6 support */
 
 /* Mostly used in the SSL BIO */
 /* Not used anymore
@@ -335,6 +337,7 @@ #define BIO_CONN_S_CONNECT          5
 #define BIO_CONN_S_OK                  6
 #define BIO_CONN_S_BLOCKED_CONNECT     7
 #define BIO_CONN_S_NBIO                        8
+#define BIO_CONN_S_GET_ADDRINFO                9
 /*#define BIO_CONN_get_param_hostname  BIO_ctrl */
 
 #define BIO_C_SET_CONNECT                      100
@@ -639,6 +642,7 @@ #ifndef OPENSSL_NO_FP_API
 int BIO_dump_fp(FILE *fp, const char *s, int len);
 int BIO_dump_indent_fp(FILE *fp, const char *s, int len, int indent);
 #endif
+#ifndef OPENSSL_NO_DEPRECATED /* not needed since we have IPv6 support */
 struct hostent *BIO_gethostbyname(const char *name);
 /* We might want a thread-safe interface too:
  * struct hostent *BIO_gethostbyname_r(const char *name,
@@ -648,11 +652,14 @@ struct hostent *BIO_gethostbyname(const 
  * substructures; if the buffer does not suffice, NULL is returned
  * and an appropriate error code is set).
  */
+#endif /* not needed since we have IPv6 support */
 int BIO_sock_error(int sock);
 int BIO_socket_ioctl(int fd, long type, void *arg);
 int BIO_socket_nbio(int fd,int mode);
+#ifndef OPENSSL_NO_DEPRECATED /* not needed since we have IPv6 support */
 int BIO_get_port(const char *str, unsigned short *port_ptr);
 int BIO_get_host_ip(const char *str, unsigned char *ip);
+#endif /* not needed since we have IPv6 support */
 int BIO_get_accept_socket(char *host_port,int mode);
 int BIO_accept(int sock,char **ip_port);
 int BIO_sock_init(void );
@@ -763,6 +770,7 @@ #define BIO_R_UNINITIALIZED                          120
 #define BIO_R_UNSUPPORTED_METHOD                        121
 #define BIO_R_WRITE_TO_READ_ONLY_BIO                    126
 #define BIO_R_WSASTARTUP                                122
+#define BIO_R_FAILED_TO_GETADDRINFO                     132
 
 #ifdef  __cplusplus
 }
diff --git a/crypto/bio/bio_lcl.h b/crypto/bio/bio_lcl.h
index dba2919..a730a49 100644
--- a/crypto/bio/bio_lcl.h
+++ b/crypto/bio/bio_lcl.h
@@ -26,3 +26,5 @@ #define       UP_write        write
 #define        UP_lseek        lseek
 #define        UP_close        close
 #endif
+
+extern int extract_host_port(char *str, char **host_ptr, char **port_ptr);
diff --git a/crypto/bio/bss_conn.c b/crypto/bio/bss_conn.c
index c147278..20065c3 100644
--- a/crypto/bio/bss_conn.c
+++ b/crypto/bio/bss_conn.c
@@ -61,6 +61,7 @@ #include <errno.h>
 #define USE_SOCKETS
 #include "cryptlib.h"
 #include <openssl/bio.h>
+#include "bio_lcl.h"
 
 #ifndef OPENSSL_NO_SOCK
 
@@ -84,10 +85,9 @@ typedef struct bio_connect_st
        char *param_port;
        int nbio;
 
-       unsigned char ip[4];
-       unsigned short port;
-
-       struct sockaddr_in them;
+       struct sockaddr_storage them_addr;
+       socklen_t them_addr_len;
+       int them_port;
 
        /* int socket; this will be kept in bio->num so that it is
         * compatible with the bss_sock bio */ 
@@ -128,88 +128,81 @@ static BIO_METHOD methods_connectp=
 static int conn_state(BIO *b, BIO_CONNECT *c)
        {
        int ret= -1,i;
-       unsigned long l;
-       char *p,*q;
+       struct addrinfo *res, *res0, hints;
+       int gai_e;
+
        int (*cb)(const BIO *,int,int)=NULL;
 
        if (c->info_callback != NULL)
                cb=c->info_callback;
 
+       res0 = res = NULL;
+
        for (;;)
                {
                switch (c->state)
                        {
                case BIO_CONN_S_BEFORE:
-                       p=c->param_hostname;
-                       if (p == NULL)
+                       if (c->param_hostname == NULL)
                                {
                                
BIOerr(BIO_F_CONN_STATE,BIO_R_NO_HOSTNAME_SPECIFIED);
                                goto exit_loop;
                                }
-                       for ( ; *p != '\0'; p++)
-                               {
-                               if ((*p == ':') || (*p == '/')) break;
-                               }
-
-                       i= *p;
-                       if ((i == ':') || (i == '/'))
-                               {
-
-                               *(p++)='\0';
-                               if (i == ':')
-                                       {
-                                       for (q=p; *q; q++)
-                                               if (*q == '/')
-                                                       {
-                                                       *q='\0';
-                                                       break;
-                                                       }
-                                       if (c->param_port != NULL)
-                                               OPENSSL_free(c->param_port);
-                                       c->param_port=BUF_strdup(p);
-                                       }
-                               }
-
-                       if (c->param_port == NULL)
-                               {
-                               
BIOerr(BIO_F_CONN_STATE,BIO_R_NO_PORT_SPECIFIED);
-                               ERR_add_error_data(2,"host=",c->param_hostname);
-                               goto exit_loop;
-                               }
                        c->state=BIO_CONN_S_GET_IP;
                        break;
 
                case BIO_CONN_S_GET_IP:
-                       if (BIO_get_host_ip(c->param_hostname,&(c->ip[0])) <= 0)
-                               goto exit_loop;
-                       c->state=BIO_CONN_S_GET_PORT;
-                       break;
-
                case BIO_CONN_S_GET_PORT:
                        if (c->param_port == NULL)
                                {
                                /* abort(); */
                                goto exit_loop;
                                }
-                       else if (BIO_get_port(c->param_port,&c->port) <= 0)
+                       c->state=BIO_CONN_S_GET_ADDRINFO;
+                       break;
+
+               case BIO_CONN_S_GET_ADDRINFO:
+                       memset(&hints, '\0', sizeof(hints));
+                       hints.ai_socktype = SOCK_STREAM;
+                       hints.ai_flags = AI_ADDRCONFIG;
+                       gai_e = getaddrinfo(c->param_hostname, c->param_port,
+                               &hints, &res);
+                       if (gai_e != 0)
+                               {
+                               
BIOerr(BIO_F_CONN_STATE,BIO_R_FAILED_TO_GETADDRINFO);
+                               ERR_add_error_data(1, gai_strerror(gai_e));
                                goto exit_loop;
+                               }
+                       res0 = res;
                        c->state=BIO_CONN_S_CREATE_SOCKET;
                        break;
 
                case BIO_CONN_S_CREATE_SOCKET:
-                       /* now setup address */
-                       memset((char *)&c->them,0,sizeof(c->them));
-                       c->them.sin_family=AF_INET;
-                       c->them.sin_port=htons((unsigned short)c->port);
-                       l=(unsigned long)
-                               ((unsigned long)c->ip[0]<<24L)|
-                               ((unsigned long)c->ip[1]<<16L)|
-                               ((unsigned long)c->ip[2]<< 8L)|
-                               ((unsigned long)c->ip[3]);
-                       c->them.sin_addr.s_addr=htonl(l);
-                       c->state=BIO_CONN_S_CREATE_SOCKET;
+                       while (res)
+                               {
+                               ret = INVALID_SOCKET;
+                               ret = socket(res->ai_family,
+                                       res->ai_socktype, res->ai_protocol);
+                               if (ret != INVALID_SOCKET)
+                                       {
+                                       memcpy((void*)&(c->them_addr), 
res->ai_addr,
+                                               sizeof(c->them_addr));
+                                       c->them_addr_len = res->ai_addrlen;
+                                       if (res->ai_family == AF_INET)
+                                               c->them_port = ntohs(
+                                               ((struct sockaddr_in 
*)(res->ai_addr))->sin_port
+                                               );
+                                       else if (res->ai_family == AF_INET6)
+                                               c->them_port = ntohs(
+                                               ((struct sockaddr_in6 
*)(res->ai_addr))->sin6_port
+                                               );
+                                       else
+                                               c->them_port = 0;
+                                       break;
+                                       }
+                               res = res->ai_next;
+                               }
 
-                       ret=socket(AF_INET,SOCK_STREAM,SOCKET_PROTOCOL);
                        if (ret == INVALID_SOCKET)
                                {
                                SYSerr(SYS_F_SOCKET,get_last_socket_error());
@@ -218,6 +211,7 @@ static int conn_state(BIO *b, BIO_CONNEC
                                
BIOerr(BIO_F_CONN_STATE,BIO_R_UNABLE_TO_CREATE_SOCKET);
                                goto exit_loop;
                                }
+                       
                        b->num=ret;
                        c->state=BIO_CONN_S_NBIO;
                        break;
@@ -227,9 +221,14 @@ static int conn_state(BIO *b, BIO_CONNEC
                                {
                                if (!BIO_socket_nbio(b->num,1))
                                        {
+                                       closesocket(b->num);
+                                       if (res)
+                                               {
+                                               
c->state=BIO_CONN_S_CREATE_SOCKET;
+                                               break;
+                                               }
                                        
BIOerr(BIO_F_CONN_STATE,BIO_R_ERROR_SETTING_NBIO);
-                                       ERR_add_error_data(4,"host=",
-                                               c->param_hostname,
+                                       
ERR_add_error_data(4,"host=",c->param_hostname,
                                                ":",c->param_port);
                                        goto exit_loop;
                                        }
@@ -241,6 +240,12 @@ #if defined(SO_KEEPALIVE) && !defined(OP
                        i=setsockopt(b->num,SOL_SOCKET,SO_KEEPALIVE,(char 
*)&i,sizeof(i));
                        if (i < 0)
                                {
+                               closesocket(b->num);
+                               if (res)
+                                       {
+                                       c->state=BIO_CONN_S_CREATE_SOCKET;
+                                       break;
+                                       }
                                SYSerr(SYS_F_SOCKET,get_last_socket_error());
                                ERR_add_error_data(4,"host=",c->param_hostname,
                                        ":",c->param_port);
@@ -253,8 +258,7 @@ #endif
                case BIO_CONN_S_CONNECT:
                        BIO_clear_retry_flags(b);
                        ret=connect(b->num,
-                               (struct sockaddr *)&c->them,
-                               sizeof(c->them));
+                               (struct sockaddr *)&(c->them_addr), 
c->them_addr_len);
                        b->retry_reason=0;
                        if (ret < 0)
                                {
@@ -266,6 +270,12 @@ #endif
                                        }
                                else
                                        {
+                                       closesocket(b->num);
+                                       if (res)
+                                               {
+                                               
c->state=BIO_CONN_S_CREATE_SOCKET;
+                                               break;
+                                               }
                                        
SYSerr(SYS_F_CONNECT,get_last_socket_error());
                                        ERR_add_error_data(4,"host=",
                                                c->param_hostname,
@@ -315,6 +325,7 @@ exit_loop:
        if (cb != NULL)
                ret=cb((BIO *)b,c->state,ret);
 end:
+       freeaddrinfo(res0);
        return(ret);
        }
 
@@ -329,12 +340,8 @@ BIO_CONNECT *BIO_CONNECT_new(void)
        ret->param_port=NULL;
        ret->info_callback=NULL;
        ret->nbio=0;
-       ret->ip[0]=0;
-       ret->ip[1]=0;
-       ret->ip[2]=0;
-       ret->ip[3]=0;
-       ret->port=0;
-       memset((char *)&ret->them,0,sizeof(ret->them));
+       ret->them_addr_len = 0;
+       ret->them_port = 0;
        return(ret);
        }
 
@@ -489,11 +496,17 @@ static long conn_ctrl(BIO *b, int cmd, l
                                }
                        else if (num == 2)
                                {
-                               *pptr= (char *)&(data->ip[0]);
+                               if (data->them_addr_len == 0)
+                                       *pptr = "\0\0\0";
+                               else if (data->them_addr.ss_family != AF_INET)
+                                       *pptr = NULL;
+                               else
+                                       *pptr = (char *)&(((struct sockaddr_in 
*)
+                                               &(data->them_addr))->sin_addr);
                                }
                        else if (num == 3)
                                {
-                               *((int *)ptr)=data->port;
+                               *((int *)ptr)=data->them_port;
                                }
                        if ((!b->init) || (ptr == NULL))
                                *pptr="not initialized";
@@ -506,8 +519,20 @@ static long conn_ctrl(BIO *b, int cmd, l
                        b->init=1;
                        if (num == 0)
                                {
+                               char *host, *port;
                                if (data->param_hostname != NULL)
                                        OPENSSL_free(data->param_hostname);
+                               if (extract_host_port((char *)ptr, &host, 
&port) == 0)
+                                       {
+                                       data->param_hostname = host;
+                                       if (port)
+                                               {
+                                               if (data->param_port != NULL)
+                                                       
OPENSSL_free(data->param_port);
+                                               data->param_port = port;
+                                               }
+                                       }
+                               else
                                data->param_hostname=BUF_strdup(ptr);
                                }
                        else if (num == 1)
@@ -520,13 +545,19 @@ static long conn_ctrl(BIO *b, int cmd, l
                                {
                                char buf[16];
                                unsigned char *p = ptr;
+                               struct sockaddr_in *in_addr;
 
                                BIO_snprintf(buf,sizeof buf,"%d.%d.%d.%d",
                                             p[0],p[1],p[2],p[3]);
                                if (data->param_hostname != NULL)
                                        OPENSSL_free(data->param_hostname);
                                data->param_hostname=BUF_strdup(buf);
-                               memcpy(&(data->ip[0]),ptr,4);
+
+                               data->them_addr_len = sizeof(struct 
sockaddr_in);
+                               in_addr = (struct sockaddr_in 
*)&(data->them_addr);
+                               in_addr->sin_family = AF_INET;
+                               memcpy((void *)&(in_addr->sin_addr),ptr,4);
+                               in_addr->sin_port = htons(data->them_port);
                                }
                        else if (num == 3)
                                {
@@ -536,7 +567,18 @@ static long conn_ctrl(BIO *b, int cmd, l
                                if (data->param_port != NULL)
                                        OPENSSL_free(data->param_port);
                                data->param_port=BUF_strdup(buf);
-                               data->port= *(int *)ptr;
+                               data->them_port= *(int *)ptr;
+                               if (data->them_addr_len)
+                                       {
+                                       if (data->them_addr.ss_family == 
AF_INET)
+                                               ((struct sockaddr_in *)
+                                                       
&(data->them_addr))->sin_port
+                                                               = 
htons(data->them_port);
+                                       else if (data->them_addr.ss_family == 
AF_INET6)
+                                               ((struct sockaddr_in6 *)
+                                                       
&(data->them_addr))->sin6_port
+                                                               = 
htons(data->them_port);
+                                       }
                                }
                        }
                break;
diff --git a/doc/crypto/BIO_s_connect.pod b/doc/crypto/BIO_s_connect.pod
index bcf7d8d..76fea04 100644
--- a/doc/crypto/BIO_s_connect.pod
+++ b/doc/crypto/BIO_s_connect.pod
@@ -59,16 +59,17 @@ type (int *).
 BIO_set_conn_hostname() uses the string B<name> to set the hostname.
 The hostname can be an IP address. The hostname can also include the
 port in the form hostname:port . It is also acceptable to use the
-form "hostname/any/other/path" or "hostname:port/any/other/path".
+form "hostname/any/other/path", "hostname:port/any/other/path",
+"[IPv6 address]/any/other/path",
+or "[IPv6 address]:port/any/other/path".
 
 BIO_set_conn_port() sets the port to B<port>. B<port> can be the
 numerical form or a string such as "http". A string will be looked
-up first using getservbyname() on the host platform but if that
-fails a standard table of port names will be used. Currently the
-list is http, telnet, socks, https, ssl, ftp, gopher and wais.
+up using getaddrinfo() on the host platform.
 
-BIO_set_conn_ip() sets the IP address to B<ip> using binary form,
+BIO_set_conn_ip() sets the IPv4 address to B<ip> using binary form,
 that is four bytes specifying the IP address in big-endian form.
+At the same time it sets the family to AF_INET.
 
 BIO_set_conn_int_port() sets the port using B<port>. B<port> should
 be of type (int *).
@@ -79,7 +80,7 @@ This return value is an internal pointer
 
 BIO_get_conn_port() returns the port as a string.
 
-BIO_get_conn_ip() returns the IP address in binary form.
+BIO_get_conn_ip() returns the IPv4 address in binary form.
 
 BIO_get_conn_int_port() returns the port as an int.
 
@@ -112,9 +113,9 @@ character in the passed hostname and eit
 truncating the string at that point.
 
 The values returned by BIO_get_conn_hostname(), BIO_get_conn_port(),
-BIO_get_conn_ip() and BIO_get_conn_int_port() are updated when a
-connection attempt is made. Before any connection attempt the values
-returned are those set by the application itself.
+BIO_get_conn_ip(), and BIO_get_conn_int_port() are
+updated when a connection attempt is made. Before any connection
+attempt the values returned are those set by the application itself.
 
 Applications do not have to call BIO_do_connect() but may wish to do
 so to separate the connection process from other I/O processing.
@@ -150,7 +151,8 @@ BIO_get_conn_port() returns a string rep
 port or NULL if not set.
 
 BIO_get_conn_ip() returns a pointer to the connected IP address in
-binary form or all zeros if not set.
+binary form or all zeros if not set. If the address set is not
+an IPv4 address, NULL is returned;
 
 BIO_get_conn_int_port() returns the connected port or 0 if none was
 set.

Reply via email to