Dear all,

time for the second installment, now making the
FTP client speak also IPv6.

I forgot to state last time, that the default
setting `doepsv4 = 0` is due to the fact that
one well-known FTP server fails to correctly
handle an IPv4-only client asking for EPRT,
when the server itself is configured to allow
IPv6. That other server does not handle mapped
addresses correctly, configuring the data channel
on an IPv6 socket, inspite of the client asking
for IPv4. Had I not discovered this, the setting
`doepsv4 = 1` would have been my choice.

Installment three will implement client internal
commands `ipv4`, `ipv6`, and `ipany`, thus allowing
full control of target addressing. This part is left
out of the present patch in order to simplify checking.

The fourth and last patch will update the Texinfo
source with all the new capacities.


Best regards,
  Mats
From 6406851b3fb6cf4d396c49c7f2143369d7bd4a0e Mon Sep 17 00:00:00 2001
From: Mats Erik Andersson <[email protected]>
Date: Sun, 17 Apr 2011 23:27:31 +0200
Subject: [PATCH] ftp: Activate support for IPv6.

---
 ChangeLog     |   12 +++++++++
 ftp/ftp.c     |   76 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 ftp/ftp_var.h |    1 +
 ftp/main.c    |   11 ++++++++
 4 files changed, 95 insertions(+), 5 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 67e6427..31a9a84 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2011-04-17  Mats Erik Andersson <[email protected]>
+
+	* ftp/ftp_var.h (usefamily): New variable.
+	* ftp/main.c (argp_options): New options `ipv4` and `ipv6`.
+	(parse_opt): Parse cases '4' and '6', set USEFAMILY accordingly.
+	(main): Initialize USEFAMILY to `AF_UNSPEC`.
+	* ftp/ftp.c (hookup): Resolve addresses according to USEFAMILY.
+	Use macro AI_ADDRCONFIG only with family `AF_UNSPEC`.
+	(initconn): New IPv6 protocol methods `EPSV` and `LPSV`.
+	New variable DATA_ADDR_SA6.
+	(initconn) [noport]: New IPv6 methods `EPRT` and `LPRT`.
+
 2011-04-08  Mats Erik Andersson <[email protected]>
 
 	* ftp/cmds.c (setepsv4): New function.
diff --git a/ftp/ftp.c b/ftp/ftp.c
index f144570..e8d21e1 100644
--- a/ftp/ftp.c
+++ b/ftp/ftp.c
@@ -132,9 +132,13 @@ hookup (char *host, int port)
   memset (&hisctladdr, 0, sizeof (hisctladdr));
   memset (&hints, 0, sizeof (hints));
 
-  hints.ai_family = AF_INET;
+  hints.ai_family = usefamily;
   hints.ai_socktype = SOCK_STREAM;
   hints.ai_flags = AI_CANONNAME;
+#ifdef AI_ADDRCONFIG
+  if (hints.ai_family == AF_UNSPEC)
+    hints.ai_flags |= AI_ADDRCONFIG;
+#endif /* AI_ADDRCONFIG */
 
   status = getaddrinfo (host, portstr, &hints, &res);
   if (status)
@@ -1128,8 +1132,9 @@ initconn (void)
   socklen_t len;
   int on = 1;
   uint32_t a0, a1, a2, a3, p0, p1, port;
-  uint32_t af, hal, h[4], pal; /* RFC 1639: LPSV resonse.  */
+  uint32_t af, hal, h[16], pal; /* RFC 1639: LPSV resonse.  */
   struct sockaddr_in *data_addr_sa4 = (struct sockaddr_in *) &data_addr;
+  struct sockaddr_in6 *data_addr_sa6 = (struct sockaddr_in6 *) &data_addr;
 
   if (passivemode)
     {
@@ -1171,11 +1176,25 @@ initconn (void)
 	    printf ("Passive mode refused.\n");
 	    goto bad;
 	    break;
+	  case AF_INET6:
+	    if (command ("EPSV") == COMPLETE)
+	      {
+		good_epsv = 1;
+		break;
+	      }
+	    if (command ("LPSV") == COMPLETE)
+	      {
+		good_lpsv = 1;
+		break;
+	      }
+	    printf ("Passive mode refused.\n");
+	    goto bad;
+	    break;
 	}
 
       if (good_epsv)
 	{
-	  /* EPSV: IPv4
+	  /* EPSV: IPv4 or IPv6
 	   *
 	   * Expected response (perl): pasv =~ '%u|'
 	   * This communicates a port number.
@@ -1193,11 +1212,14 @@ initconn (void)
 	      case AF_INET:
 		data_addr_sa4->sin_port = htons (port);
 		break;
+	      case AF_INET6:
+		data_addr_sa6->sin6_port = htons (port);
+		break;
 	    }
 	} /* EPSV */
       else if (good_lpsv)
 	{
-	  /* LPSV: IPv4
+	  /* LPSV: IPv4 or IPv6
 	   *
 	   * At this point we have got a string of comma
 	   * separated, one-byte unsigned integer values.
@@ -1229,7 +1251,37 @@ initconn (void)
 		  uint32_t *pu32 = (uint32_t *) &data_addr_sa4->sin_addr.s_addr;
 		  pu32[0] = htonl ( (h[0] << 24) | (h[1] << 16) | (h[2] << 8) | h[3]);
 		}
-	    }
+	    } /* LPSV IPv4 */
+	  else /* IPv6 */
+	    {
+	      if ((sscanf (pasv, "%u," /* af */
+				"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u," /* hal, h[16] */
+				"%u,%u,%u", /* pal, p0, p1 */
+				&af, &hal, &h[0], &h[1], &h[2], &h[3], &h[4], &h[5], &h[6], &h[7],
+				&h[8], &h[9], &h[10], &h[11], &h[12], &h[13], &h[14], &h[15],
+				&pal, &p0, &p1) != 21)
+		  || (/* Strong checking */ af != 6 || hal != 16 || pal != 2) )
+		{
+		  printf ("Passive mode address scan failure. "
+			  "Shouldn't happen!\n");
+		  (void) command ("ABOR");	/* Cancel any open connection.  */
+		  goto bad;
+		}
+	      for (j = 0; j < 16; ++j)
+		h[j] &= 0xff; /* Mask only the significant bits.  */
+
+	      data_addr.ss_family = AF_INET6;
+	      data_addr_sa6->sin6_port =
+		  htons (((p0 & 0xff) << 8) | (p1 & 0xff));
+
+		{
+		  uint32_t *pu32 = (uint32_t *) &data_addr_sa6->sin6_addr.s6_addr;
+		  pu32[0] = htonl ( (h[0] << 24) | (h[1] << 16) | (h[2] << 8) | h[3]);
+		  pu32[1] = htonl ( (h[4] << 24) | (h[5] << 16) | (h[6] << 8) | h[7]);
+		  pu32[2] = htonl ( (h[8] << 24) | (h[9] << 16) | (h[10] << 8) | h[11]);
+		  pu32[3] = htonl ( (h[12] << 24) | (h[13] << 16) | (h[14] << 8) | h[15]);
+		}
+	    } /* LPSV IPv6 */
 	}
       else /* !EPSV && !LPSV */
 	{ /* PASV */
@@ -1282,6 +1334,9 @@ noport:
 	case AF_INET:
 	  data_addr_sa4->sin_port = 0;
 	  break;
+	case AF_INET6:
+	  data_addr_sa6->sin6_port = 0;
+	  break;
       }
 
   if (data != -1)
@@ -1322,6 +1377,7 @@ noport:
 #define UC(b)	(((int)b)&0xff)
       /* Preferences:
        *   IPv4: EPRT, PORT, LPRT
+       *   IPv6: EPRT, LPRT
        */
       result = ERROR;	/* For success detection.  */
       if (data_addr.ss_family != AF_INET || doepsv4)
@@ -1362,6 +1418,16 @@ noport:
 		result = command ("LPRT 4,4,%u,%u,%u,%u,2,%u,%u",
 				  h[0], h[1], h[2], h[3], p[0], p[1]);
 		break;
+	      case AF_INET6:
+		h = (uint8_t *) &data_addr_sa6->sin6_addr;
+		p = (uint8_t *) &data_addr_sa6->sin6_port;
+		result = command ("LPRT 6,16," /* af, hal */
+				  "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u," /* h[16] */
+				  "2,%u,%u", /* pal, p[2] */
+				  h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7],
+				  h[8], h[9], h[10], h[11], h[12], h[13], h[14], h[15],
+				  p[0], p[1]);
+		break;
 	    }
 	}
 
diff --git a/ftp/ftp_var.h b/ftp/ftp_var.h
index 52212ee..601b0bf 100644
--- a/ftp/ftp_var.h
+++ b/ftp/ftp_var.h
@@ -88,6 +88,7 @@ FTP_EXTERN int crflag;		/* if 1, strip car. rets. on ascii gets */
 FTP_EXTERN char pasv[64];	/* passive port for proxy data connection */
 FTP_EXTERN int passivemode;	/* passive mode enabled */
 FTP_EXTERN int doepsv4;		/* EPSV/EPRT for IPv4 enabled */
+FTP_EXTERN int usefamily;	/* Precondition on an adress family */
 FTP_EXTERN char *altarg;	/* argv[1] with no shell-like preprocessing  */
 FTP_EXTERN char ntin[17];	/* input translation table */
 FTP_EXTERN char ntout[17];	/* output translation table */
diff --git a/ftp/main.c b/ftp/main.c
index 147f66f..38e36f0 100644
--- a/ftp/main.c
+++ b/ftp/main.c
@@ -108,6 +108,8 @@ static struct argp_option argp_options[] = {
   {"prompt", OPT_PROMPT, "PROMPT", OPTION_ARG_OPTIONAL, "print a command line PROMPT "
    "(optionally), even if not on a tty", GRP+1},
   {"verbose", 'v', NULL, 0, "verbose output", GRP+1},
+  {"ipv4", '4', NULL, 0, "contact IPv4 hosts", GRP+1},
+  {"ipv6", '6', NULL, 0, "contact IPv6 hosts", GRP+1},
 #undef GRP
   {NULL}
 };
@@ -154,6 +156,14 @@ parse_opt (int key, char *arg, struct argp_state *state)
       passivemode = 0;
       break;
 
+    case '4':
+      usefamily = AF_INET;
+      break;
+
+    case '6':
+      usefamily = AF_INET6;
+      break;
+
     default:
       return ARGP_ERR_UNKNOWN;
     }
@@ -179,6 +189,7 @@ main (int argc, char *argv[])
   autologin = 1;
   passivemode = 0;		/* passive mode not active */
   doepsv4 = 0;			/* use EPRT/EPSV for IPv4 */
+  usefamily = AF_UNSPEC;	/* allow any address family */
 
   /* Parse command line */
   iu_argp_init ("ftp", default_program_authors);
-- 
1.7.2.3

Attachment: signature.asc
Description: Digital signature

Reply via email to