]> granicus.if.org Git - curl/commitdiff
Jun-ichiro itojun Hagino's IPv6 adjustments
authorDaniel Stenberg <daniel@haxx.se>
Mon, 5 Feb 2001 23:04:44 +0000 (23:04 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 5 Feb 2001 23:04:44 +0000 (23:04 +0000)
CHANGES
configure.in
docs/curl.1
lib/ftp.c
lib/hostip.c
lib/hostip.h
lib/url.c
lib/urldata.h

diff --git a/CHANGES b/CHANGES
index 549a71a546f84320a4504edd549146597e56ddb1..4278d01b594307725eb6b805a55c421c2fac42a1 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -8,6 +8,9 @@
 
 
 Daniel (5 February 2001)
+- Jun-ichiro itojun Hagino brought a big patch that brings IPv6-awareness to
+  a bunch of different areas within libcurl.
+
 - Robert Weaver told me about the problems the MS VC++ 6.0 compiler has with
   the 'static' keyword on a number of libcurl functions. I might need to add a
   patch that redefines static when libcurl is compiled with that compiler.
index 4ad067aea6fcf11c24a27dcee7e26cdae0e43ff4..de707cd2175e401a3c263205951e6770d872db4c 100644 (file)
@@ -53,15 +53,9 @@ dnl
 AC_DEFUN(CURL_CHECK_WORKING_GETADDRINFO,[
   AC_CACHE_CHECK(for working getaddrinfo, ac_cv_working_getaddrinfo,[
   AC_TRY_RUN( [
-#ifdef HAVE_NETDB_H
 #include <netdb.h>
-#endif
-#ifdef HAVE_STRING_H
-#include <string.h>
-#endif
-#ifdef HAVE_SYS_SOCKET_H
+#include <sys/types.h>
 #include <sys/socket.h>
-#endif
 
 void main(void) {
     struct addrinfo hints, *ai;
@@ -434,6 +428,10 @@ AC_MSG_CHECKING([if Kerberos4 support is requested])
 
 if test "$want_krb4" = yes
 then
+  if test "$ipv6" = "yes"; then
+    echo krb4 is not compatible with IPv6
+    exit 1
+  fi
   AC_MSG_RESULT(yes)
 
   dnl Check for & handle argument to --with-krb4
@@ -691,7 +689,8 @@ AC_CHECK_FUNCS( socket \
                 setvbuf \
                 sigaction \
                 signal \
-                getpass_r
+                getpass_r \
+                strlcat
 )
 
 dnl removed 'getpass' check on October 26, 2000
index 62331433df125c2253b51c752af09e21ceee27fe..f57e93fd7808223f90f09a9dd65fd76b97a9974e 100644 (file)
@@ -788,6 +788,7 @@ If you do find bugs, mail them to curl-bug@haxx.se.
  - Loic Dachary <loic@senga.org>
  - Robert Weaver <robert.weaver@sabre.com>
  - Ingo Ralf Blum <ingoralfblum@ingoralfblum.com>
+ - Jun-ichiro itojun Hagino <itojun@iijlab.net>
 
 .SH WWW
 http://curl.haxx.se
index 0a2c43aaf6ca7869cf1d488c9d9937f2bb83288a..069b9d100317cc5aa9de15a562b52d65427ec0f1 100644 (file)
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -564,6 +564,9 @@ CURLcode _ftp(struct connectdata *conn)
 #if defined (HAVE_INET_NTOA_R)
   char ntoa_buf[64];
 #endif
+#ifdef ENABLE_IPV6
+  struct addrinfo *ai;
+#endif
 
   struct curl_slist *qitem; /* QUOTE item */
   /* the ftp struct is already inited in ftp_connect() */
@@ -702,6 +705,174 @@ CURLcode _ftp(struct connectdata *conn)
 
   /* We have chosen to use the PORT command */
   if(data->bits.ftp_use_port) {
+#ifdef ENABLE_IPV6
+    struct addrinfo hints, *res, *ai;
+    struct sockaddr_storage ss;
+    int sslen;
+    char hbuf[NI_MAXHOST];
+    char *localaddr;
+#ifdef NI_WITHSCOPEID
+    const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
+#else
+    const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+#endif
+    unsigned char *ap;
+    unsigned char *pp;
+    int alen, plen;
+    char portmsgbuf[4096], tmp[4096];
+    char *p;
+    char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
+    char **modep;
+
+    /*
+     * we should use Curl_if2ip?  given pickiness of recent ftpd,
+     * I believe we should use the same address as the control connection.
+     */
+    sslen = sizeof(ss);
+    if (getsockname(data->firstsocket, (struct sockaddr *)&ss, &sslen) < 0)
+      return CURLE_FTP_PORT_FAILED;
+
+    if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
+       niflags))
+      return CURLE_FTP_PORT_FAILED;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = ss.ss_family;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = AI_PASSIVE;
+    if (getaddrinfo(hbuf, "0", &hints, &res))
+      return CURLE_FTP_PORT_FAILED;
+
+    portsock = -1;
+    for (ai = res; ai; ai = ai->ai_next) {
+      portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+      if (portsock < 0)
+       continue;
+
+      if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
+       close(portsock);
+       portsock = -1;
+       continue;
+      }
+
+      if (listen(portsock, 1) < 0) {
+       close(portsock);
+       portsock = -1;
+       continue;
+      }
+
+      break;
+    }
+    if (portsock < 0) {
+      failf(data, strerror(errno));
+      freeaddrinfo(res);
+      return CURLE_FTP_PORT_FAILED;
+    }
+
+    sslen = sizeof(ss);
+    if (getsockname(portsock, (struct sockaddr *)&ss, &sslen) < 0) {
+      failf(data, strerror(errno));
+      freeaddrinfo(res);
+      return CURLE_FTP_PORT_FAILED;
+    }
+
+    for (modep = mode; modep && *modep; modep++) {
+      int lprtaf, eprtaf;
+
+      switch (ss.ss_family) {
+      case AF_INET:
+       ap = (char *)&((struct sockaddr_in *)&ss)->sin_addr;
+       alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
+       pp = (char *)&((struct sockaddr_in *)&ss)->sin_port;
+       plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
+       lprtaf = 4;
+       eprtaf = 1;
+       break;
+      case AF_INET6:
+       ap = (char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
+       alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
+       pp = (char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
+       plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
+       lprtaf = 6;
+       eprtaf = 2;
+       break;
+      default:
+       ap = pp = NULL;
+       lprtaf = eprtaf = -1;
+       break;
+      }
+
+      if (strcmp(*modep, "EPRT") == 0) {
+       if (eprtaf < 0)
+         continue;
+       if (getnameinfo((struct sockaddr *)&ss, sslen,
+           portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags))
+         continue;
+       /* do not transmit IPv6 scope identifier to the wire */
+       if (ss.ss_family == AF_INET6) {
+         char *q = strchr(portmsgbuf, '%');
+         if (q)
+           *q = '\0';
+       }
+       ftpsendf(data->firstsocket, conn, "%s |%d|%s|%s|", *modep, eprtaf,
+           portmsgbuf, tmp);
+      } else if (strcmp(*modep, "LPRT") == 0 || strcmp(*modep, "PORT") == 0) {
+       int i;
+
+        if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0)
+         continue;
+        if (strcmp(*modep, "PORT") == 0 && ss.ss_family != AF_INET)
+         continue;
+
+       portmsgbuf[0] = '\0';
+        if (strcmp(*modep, "LPRT") == 0) {
+         snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
+         if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) {
+           goto again;
+         }
+       }
+       for (i = 0; i < alen; i++) {
+         if (portmsgbuf[0])
+           snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
+         else
+           snprintf(tmp, sizeof(tmp), "%u", ap[i]);
+         if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) {
+           goto again;
+         }
+       }
+        if (strcmp(*modep, "LPRT") == 0) {
+         snprintf(tmp, sizeof(tmp), ",%d", plen);
+         if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
+           goto again;
+       }
+       for (i = 0; i < plen; i++) {
+         snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
+         if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf)) {
+           goto again;
+         }
+       }
+       ftpsendf(data->firstsocket, conn, "%s %s", *modep, portmsgbuf);
+      }
+
+      nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode);
+      if (nread < 0)
+       return CURLE_OPERATION_TIMEOUTED;
+
+      if (ftpcode != 200) {
+       failf(data, "Server does not grok %s", *modep);
+       continue;
+      } else
+             break;
+again:;
+    }
+
+    if (!*modep) {
+      close(portsock);
+      freeaddrinfo(res);
+      return CURLE_FTP_PORT_FAILED;
+    }
+
+#else
     struct sockaddr_in sa;
     struct hostent *h=NULL;
     char *hostdataptr=NULL;
@@ -809,26 +980,43 @@ CURLcode _ftp(struct connectdata *conn)
       failf(data, "Server does not grok PORT, try without it!");
       return CURLE_FTP_PORT_FAILED;
     }     
+#endif /* ENABLE_IPV6 */
   }
   else { /* we use the PASV command */
+#if 0
+    char *mode[] = { "EPSV", "LPSV", "PASV", NULL };
+    int results[] = { 229, 228, 227, 0 };
+#else
+    char *mode[] = { "PASV", NULL };
+    int results[] = { 227, 0 };
+#endif
+    int modeoff;
 
-    ftpsendf(data->firstsocket, conn, "PASV");
+    for (modeoff = 0; mode[modeoff]; modeoff++) {
+      ftpsendf(data->firstsocket, conn, mode[modeoff]);
+      nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode);
+      if(nread < 0)
+       return CURLE_OPERATION_TIMEOUTED;
 
-    nread = Curl_GetFTPResponse(data->firstsocket, buf, conn, &ftpcode);
-    if(nread < 0)
-      return CURLE_OPERATION_TIMEOUTED;
+      if (ftpcode == results[modeoff])
+       break;
+    }
 
-    if(ftpcode != 227) {
+    if (!mode[modeoff]) {
       failf(data, "Odd return code after PASV");
       return CURLE_FTP_WEIRD_PASV_REPLY;
     }
-    else {
+    else if (strcmp(mode[modeoff], "PASV") == 0) {
       int ip[4];
       int port[2];
       unsigned short newport; /* remote port, not necessary the local one */
       unsigned short connectport; /* the local port connect() should use! */
       char newhost[32];
+#ifdef ENABLE_IPV6
+      struct addrinfo *res;
+#else
       struct hostent *he;
+#endif
       char *str=buf,*ip_addr;
       char *hostdataptr=NULL;
 
@@ -863,20 +1051,78 @@ CURLcode _ftp(struct connectdata *conn)
          * proxy again here. We already have the name info for it since the
          * previous lookup.
          */
+#ifdef ENABLE_IPV6
+        res = conn->res;
+#else
         he = conn->hp;
+#endif
         connectport =
           (unsigned short)data->port; /* we connect to the proxy's port */
       }
       else {
         /* normal, direct, ftp connection */
+#ifdef ENABLE_IPV6
+        res = Curl_getaddrinfo(data, newhost, newport);
+        if(!res)
+#else
         he = Curl_gethost(data, newhost, &hostdataptr);
-        if(!he) {
+        if(!he)
+#endif
+        {
           failf(data, "Can't resolve new host %s", newhost);
           return CURLE_FTP_CANT_GET_HOST;
         }
         connectport = newport; /* we connect to the remote port */
       }
        
+#ifdef ENABLE_IPV6
+      data->secondarysocket = -1;
+      for (ai = res; ai; ai = ai->ai_next) {
+       /* XXX for now, we can do IPv4 only */
+       if (ai->ai_family != AF_INET)
+         continue;
+
+       data->secondarysocket = socket(ai->ai_family, ai->ai_socktype,
+           ai->ai_protocol);
+       if (data->secondarysocket < 0)
+         continue;
+
+       if(data->bits.verbose) {
+         char hbuf[NI_MAXHOST];
+         char nbuf[NI_MAXHOST];
+         char sbuf[NI_MAXSERV];
+#ifdef NI_WITHSCOPEID
+         const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
+#else
+         const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
+#endif
+         if (getnameinfo(res->ai_addr, res->ai_addrlen, nbuf, sizeof(nbuf),
+             sbuf, sizeof(sbuf), niflags)) {
+           snprintf(nbuf, sizeof(nbuf), "?");
+           snprintf(sbuf, sizeof(sbuf), "?");
+         }
+         if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
+             NULL, 0, 0)) {
+           infof(data, "Connecting to %s port %s\n", nbuf, sbuf);
+         } else {
+           infof(data, "Connecting to %s (%s) port %s\n", hbuf, nbuf, sbuf);
+         }
+       }
+
+       if (connect(data->secondarysocket, ai->ai_addr, ai->ai_addrlen) < 0) {
+         close(data->secondarysocket);
+         data->secondarysocket = -1;
+         continue;
+       }
+
+       break;
+      }
+
+      if (data->secondarysocket < 0) {
+       failf(data, strerror(errno));
+        return CURLE_FTP_CANT_RECONNECT;
+      }
+#else
       data->secondarysocket = socket(AF_INET, SOCK_STREAM, 0);
 
       memset((char *) &serv_addr, '\0', sizeof(serv_addr));
@@ -971,6 +1217,7 @@ CURLcode _ftp(struct connectdata *conn)
         }
         return CURLE_FTP_CANT_RECONNECT;
       }
+#endif /*ENABLE_IPV6*/
 
       if (data->bits.tunnel_thru_httpproxy) {
         /* We want "seamless" FTP operations through HTTP proxy tunnel */
@@ -979,6 +1226,8 @@ CURLcode _ftp(struct connectdata *conn)
         if(CURLE_OK != result)
           return result;
       }
+    } else {
+      return CURLE_FTP_CANT_RECONNECT;
     }
   }
   /* we have the (new) data connection ready */
index cb7bc19a56585a4943464f4988ae5632f8e004c9..c1f3fde1196cc3a8af0351398d9fe5b2954ab934 100644 (file)
@@ -83,6 +83,29 @@ static char *MakeIP(unsigned long num,char *addr, int addr_len)
   return (addr);
 }
 
+#ifdef ENABLE_IPV6
+struct addrinfo *Curl_getaddrinfo(struct UrlData *data,
+                              char *hostname,
+                              int port)
+{
+  struct addrinfo hints, *res;
+  int error;
+  char sbuf[NI_MAXSERV];
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = PF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_flags = AI_CANONNAME;
+  snprintf(sbuf, sizeof(sbuf), "%d", port);
+  error = getaddrinfo(hostname, sbuf, &hints, &res);
+  if (error) {
+    infof(data, "getaddrinfo(3) failed for %s\n", hostname);
+    return NULL;
+  }
+  return res;
+}
+#endif
+
 /* The original code to this function was once stolen from the Dancer source
    code, written by Bjorn Reese, it has since been patched and modified
    considerably. */
index 78453487b644dffac369b9b2bce9aafb81784078..d7c9b26f1e104206f0da3f8a9f378edbf1a65129 100644 (file)
  * $Id$
  *****************************************************************************/
 
+struct addrinfo;
+struct addrinfo *Curl_getaddrinfo(struct UrlData *data,
+                             char *hostname,
+                             int port);
+
 struct hostent *Curl_gethost(struct UrlData *data,
                              char *hostname,
                              char **bufp);
index e37f241637a62d6c68c42904231bb342e4822d10..98d41acb7970b93cec3ba1cabeaeda26ec36d7ca 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -562,8 +562,13 @@ CURLcode curl_disconnect(CURLconnect *c_connect)
 
   struct UrlData *data = conn->data;
 
+#ifdef ENABLE_IPV6
+  if(conn->res) /* host name info */
+    freeaddrinfo(conn->res);
+#else
   if(conn->hostent_buf) /* host name info */
     free(conn->hostent_buf);
+#endif
 
   if(conn->path) /* the URL path part */
     free(conn->path);
@@ -589,6 +594,9 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect)
   struct sigaction sigact;
 #endif
   int urllen;
+#ifdef ENABLE_IPV6
+  struct addrinfo *ai;
+#endif
 
   /*************************************************************
    * Check input data
@@ -1189,13 +1197,23 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect)
     data->port =  data->remote_port; /* it is the same port */
 
     /* Connect to target host right on */
+#ifdef ENABLE_IPV6
+    conn->res = Curl_getaddrinfo(data, conn->name, data->port);
+    if(!conn->res)
+#else
     conn->hp = Curl_gethost(data, conn->name, &conn->hostent_buf);
-    if(!conn->hp) {
+    if(!conn->hp)
+#endif
+    {
       failf(data, "Couldn't resolve host '%s'", conn->name);
       return CURLE_COULDNT_RESOLVE_HOST;
     }
   }
   else {
+#ifdef ENABLE_IPV6
+    failf(data, "proxy yet to be supported");
+    return CURLE_OUT_OF_MEMORY;
+#else
     char *prox_portno;
     char *endofprot;
 
@@ -1244,9 +1262,11 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect)
     }
 
     free(proxydup); /* free the duplicate pointer and not the modified */
+#endif
   }
   Curl_pgrsTime(data, TIMER_NAMELOOKUP);
 
+#ifndef ENABLE_IPV6
   data->firstsocket = socket(AF_INET, SOCK_STREAM, 0);
 
   memset((char *) &conn->serv_addr, '\0', sizeof(conn->serv_addr));
@@ -1254,6 +1274,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect)
          conn->hp->h_addr, conn->hp->h_length);
   conn->serv_addr.sin_family = conn->hp->h_addrtype;
   conn->serv_addr.sin_port = htons(data->port);
+#endif
 
 #if !defined(WIN32)||defined(__CYGWIN32__)
   /* We don't generally like checking for OS-versions, we should make this
@@ -1266,6 +1287,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect)
 #define INADDR_NONE (unsigned long) ~0
 #endif
 
+#ifndef ENABLE_IPV6
   /*************************************************************
    * Select device to bind socket to
    *************************************************************/
@@ -1374,10 +1396,31 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect)
   } /* end of device selection support */
 #endif  /* end of HAVE_INET_NTOA */
 #endif /* end of not WIN32 */
+#endif /*ENABLE_IPV6*/
 
   /*************************************************************
    * Connect to server/proxy
    *************************************************************/
+#ifdef ENABLE_IPV6
+  data->firstsocket = -1;
+  for (ai = conn->res; ai; ai = ai->ai_next) {
+    data->firstsocket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+    if (data->firstsocket < 0)
+      continue;
+
+    if (connect(data->firstsocket, ai->ai_addr, ai->ai_addrlen) < 0) {
+      close(data->firstsocket);
+      data->firstsocket = -1;
+      continue;
+    }
+
+    break;
+  }
+  if (data->firstsocket < 0) {
+    failf(data, strerror(errno));
+    return CURLE_COULDNT_CONNECT;
+  }
+#else
   if (connect(data->firstsocket,
               (struct sockaddr *) &(conn->serv_addr),
               sizeof(conn->serv_addr)
@@ -1426,6 +1469,7 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect)
     }
     return CURLE_COULDNT_CONNECT;
   }
+#endif
 
   /*************************************************************
    * Proxy authentication
@@ -1473,11 +1517,31 @@ static CURLcode _connect(CURL *curl, CURLconnect **in_connect)
   conn->bytecount = 0;
   
   /* Figure out the ip-number and display the first host name it shows: */
+#ifdef ENABLE_IPV6
+  {
+    char hbuf[NI_MAXHOST];
+#ifdef NI_WITHSCOPEID
+    const int niflags = NI_NUMERICHOST | NI_WITHSCOPEID;
+#else
+    const int niflags = NI_NUMERICHOST;
+#endif
+    if (getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0,
+       niflags)) {
+      snprintf(hbuf, sizeof(hbuf), "?");
+    }
+    if (ai->ai_canonname) {
+      infof(data, "Connected to %s (%s)\n", ai->ai_canonname, hbuf);
+    } else {
+      infof(data, "Connected to %s\n", hbuf);
+    }
+  }
+#else
   {
     struct in_addr in;
     (void) memcpy(&in.s_addr, *conn->hp->h_addr_list, sizeof (in.s_addr));
     infof(data, "Connected to %s (%s)\n", conn->hp->h_name, inet_ntoa(in));
   }
+#endif
 
 #ifdef __EMX__
   /* 20000330 mgs
@@ -1509,8 +1573,13 @@ CURLcode curl_connect(CURL *curl, CURLconnect **in_connect)
     if(conn) {
       if(conn->path)
         free(conn->path);
+#ifdef ENABLE_IPV6
+      if(conn->res)
+        freeaddrinfo(conn->res);
+#else
       if(conn->hostent_buf)
         free(conn->hostent_buf);
+#endif
       free(conn);
       *in_connect=NULL;
     }
index e053caffdec9a428d677e3b3412e1f3aa487a410..325c06b4946d75bf5f8a812014c42dbec01e0b9c 100644 (file)
@@ -159,9 +159,13 @@ struct connectdata {
 #define PROT_LDAP    (1<<7)
 #define PROT_FILE    (1<<8)
 
+#ifdef ENABLE_IPV6
+  struct addrinfo *res;
+#else
   char *hostent_buf; /* pointer to allocated memory for name info */
   struct hostent *hp;
   struct sockaddr_in serv_addr;
+#endif
   char proto[64];  /* store the protocol string in this buffer */
   char gname[257]; /* store the hostname in this buffer */
   char *name;      /* host name pointer to fool around with */