Commit f6e2bfd4 authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

Jun-ichiro itojun Hagino's IPv6 adjustments

parent 1ae5dab8
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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.
+7 −8
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+256 −7
Original line number Diff line number Diff line
@@ -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;

    if(ftpcode != 227) {
      if (ftpcode == results[modeoff])
	break;
    }

    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 */
+23 −0
Original line number Diff line number Diff line
@@ -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. */
Loading