changeset 1004:c1abcb7d0cf8

* src/ftpclient.c (connect_to_server): Added support for IPv6. This meant changing from using the PORT command to using EPRT, when setting up the data connection. EPRT is protocol independant. * tests/ftpclient1.c (ftp_test): Added tests which use IPv6 addresses. Fixed a few minor bugs with format strings. * doc/ftpclient.sgml: Mention we support IPv6 and DNS.
author asl
date Mon, 12 May 2003 10:02:08 +0000
parents fae4fcdd86d2
children 1f968b35cd30
files packages/net/ftpclient/current/ChangeLog packages/net/ftpclient/current/doc/ftpclient.sgml packages/net/ftpclient/current/src/ftpclient.c packages/net/ftpclient/current/tests/ftpclient1.c
diffstat 4 files changed, 146 insertions(+), 78 deletions(-) [+]
line wrap: on
line diff
--- a/packages/net/ftpclient/current/ChangeLog
+++ b/packages/net/ftpclient/current/ChangeLog
@@ -1,3 +1,13 @@
+2003-04-28  Andrew Lunn  <andrew.lunn@ascom.ch>
+
+	* src/ftpclient.c (connect_to_server): Added support for
+	IPv6. This meant changing from using the PORT command to using
+	EPRT, when setting up the data connection. EPRT is protocol 
+	independant. 
+	* tests/ftpclient1.c (ftp_test): Added tests which use IPv6
+	addresses. Fixed a few minor bugs with format strings.
+	* doc/ftpclient.sgml: Mention we support IPv6 and DNS.
+	
 2003-02-24  Jonathan Larmour  <jifl@eCosCentric.com>
 
 	* cdl/ftpclient.cdl: Add doc link.
@@ -25,6 +35,7 @@ 2001-11-04  Andrew Lunn  <andrew.lunn@as
 // -------------------------------------------
 // This file is part of eCos, the Embedded Configurable Operating System.
 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+// Copyright (C) 2003 Andrew Lunn.	
 //
 // eCos is free software; you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free
--- a/packages/net/ftpclient/current/doc/ftpclient.sgml
+++ b/packages/net/ftpclient/current/doc/ftpclient.sgml
@@ -11,6 +11,7 @@
 <!--                                                                 -->
 <!-- =============================================================== -->
 <!-- Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.  -->
+<!-- Copyright (C) 2003 Andrew Lunn.                                 -->
 <!-- This material may be distributed only subject to the terms      -->
 <!-- and conditions set forth in the Open Publication License, v1.0  -->
 <!-- or later (the latest version is presently available at          -->
@@ -34,7 +35,8 @@
 <PARTINTRO>
 <PARA>
 The ftpclient package provides an FTP (File Transfer Protocol) client
-for use with the TCP/IP stack in eCos.
+for use with the TCP/IP stack in eCos. It supports both IPv4 and IPv6
+and will use the DNS client, when its is part of the eCos configuration.
 </PARA>
 </PARTINTRO>
 <CHAPTER id="net-ftpclient-features">
--- a/packages/net/ftpclient/current/src/ftpclient.c
+++ b/packages/net/ftpclient/current/src/ftpclient.c
@@ -209,66 +209,54 @@ command(char * cmd,
 
 static int
 connect_to_server(char *hostname, 
-                  struct sockaddr_in * local,
+                  struct sockaddr * local,
                   ftp_printf_t ftp_printf) 
 { 
-  struct sockaddr_in host; 
-  struct servent *sent;
-#ifdef CYGPKG_NS_DNS   
-  struct hostent *hp=NULL; 
-#endif
   int s, len;
+  int error;
+  struct addrinfo *res, *nai;
+  char name[80];
+  char port[8];
 
-  s = socket(AF_INET, SOCK_STREAM, 0);
-  if (s < 0) {
-    ftp_printf(1,"socket: %s\n",strerror(errno));
-    return FTP_BAD;
-  }
-  
-  sent = getservbyname("ftp", "tcp");
-  if (sent == (struct servent *)0) {
-    ftp_printf(1,"FTP: unknown serivice\n");
-    close(s);
-    return FTP_BAD;
+  error = getaddrinfo(hostname, "ftp", NULL, &res);
+  if (error != EAI_NONE) {
+    return FTP_NOSUCHHOST;
   }
 
-#ifdef CYGPKG_NS_DNS  
-  hp = gethostbyname(hostname);
-
-
-  if (hp) {           /* try name first */
-    host.sin_family = hp->h_addrtype;
-    bcopy(hp->h_addr, &host.sin_addr, hp->h_length);
-  } else 
-#endif
-    {			/* maybe it's a numeric address ?*/
-    host.sin_family = AF_INET;
+  nai=res;
+  while (nai) {
+    s = socket(nai->ai_family, nai->ai_socktype,nai->ai_protocol);
+    if (s < 0) {
+      nai = nai->ai_next;
+      continue;
+    }
     
-    if (inet_aton(hostname,&host.sin_addr) == 0)  { 
-      ftp_printf(1,"host not found: %s\n", hostname);
+    if (connect(s, nai->ai_addr, nai->ai_addrlen) < 0) {
+      getnameinfo(nai->ai_addr, nai->ai_addrlen, 
+                  name, sizeof(name), NULL,0, NI_NUMERICHOST);
+      ftp_printf(1,"FTP Connect to %s failed: %s\n",name, strerror(errno));
       close (s);
-      return FTP_NOSUCHHOST;
+      nai = nai->ai_next;
+      continue;
     }
+    
+    len = sizeof(struct sockaddr);
+    if (getsockname(s, (struct sockaddr *)local, &len) < 0) {
+      ftp_printf(1,"getsockname failed %s\n",strerror(errno));
+      close(s);
+      nai = nai->ai_next;
+      continue;
+    }
+    getnameinfo(nai->ai_addr, nai->ai_addrlen, 
+                name, sizeof(name), port, sizeof(port), 
+                NI_NUMERICHOST|NI_NUMERICSERV);
+    
+    ftp_printf(0,"FTP: Connected to %s:%s\n", name, port);
+    freeaddrinfo(res);
+    return (s);
   }
-  
-  host.sin_port = sent->s_port;
-  
-  if (connect(s, (struct sockaddr *)&host, sizeof(host)) < 0) {
-    ftp_printf(1,"FTP Connect failed: %s\n",strerror(errno));
-    close (s);
-    return FTP_NOSUCHHOST;
-  }
-  
-  len = sizeof(struct sockaddr_in);
-  if (getsockname(s, (struct sockaddr *)local, &len) < 0) {
-    ftp_printf(1,"getsockname failed %s\n",strerror(errno));
-    close(s);
-    return FTP_BAD;
-  }
-  ftp_printf(0,"FTP: Connected to %s.%d\n",
-             inet_ntoa(host.sin_addr), ntohs(host.sin_port));
-  
-  return (s);
+  freeaddrinfo(res);
+  return FTP_NOSUCHHOST;
 }
 
 /* Perform a login to the server. Pass the username and password and
@@ -320,20 +308,21 @@ opened send the port command to the serv
 port we are listening on.*/
 static int 
 opendatasock(int ctrl_s,
-             struct sockaddr_in ctrl, 
+             struct sockaddr *ctrl, 
              char *msgbuf, 
              unsigned msgbuflen,
              ftp_printf_t ftp_printf) {
 
-  struct sockaddr_in local;
+  struct sockaddr local;
+  char name[64];
+  char port[10];
   socklen_t len;
   int on = 1;
-  char buf[4*6+1];
-  char *a, *p;
+  char buf[80];
   int ret;
   int s;
 
-  s = socket(AF_INET, SOCK_STREAM, 0);
+  s = socket(ctrl->sa_family, SOCK_STREAM, 0);
   if (s < 0) {
     ftp_printf(1,"socket: %s\n",strerror(errno));
     return FTP_BAD;
@@ -344,18 +333,34 @@ opendatasock(int ctrl_s,
     close(s);
     return FTP_BAD;
   }
+  
+  memcpy(&local,ctrl,sizeof(struct sockaddr));
+  switch (ctrl->sa_family) {
+    case AF_INET: {
+      struct sockaddr_in * sa4 = (struct sockaddr_in *) &local;
+      sa4->sin_port = 0;
+      break;
+    }
+#ifdef CYGPKG_NET_INET6
+    case AF_INET6: {
+      struct sockaddr_in6 * sa6 = (struct sockaddr_in6 *) &local;
+      sa6->sin6_port = 0;
+      break;
+    }
+#endif
+    default:
+      close (s);
+      return FTP_BAD;
+  }
 
-  local = ctrl;
-  local.sin_family = AF_INET;
-  local.sin_port = 0;
-  if (bind(s,(struct sockaddr *)&local,sizeof(local)) < 0) {
+  if (bind(s,&local,local.sa_len) < 0) {
     ftp_printf(1,"bind: %s\n",strerror(errno));
     close(s);
     return FTP_BAD;   
   }
   
   len = sizeof(local);
-  if (getsockname(s,(struct sockaddr *)&local,&len) < 0) {
+  if (getsockname(s,&local,&len) < 0) {
     ftp_printf(1,"getsockname: %s\n",strerror(errno));
     close(s);
     return FTP_BAD;   
@@ -366,15 +371,26 @@ opendatasock(int ctrl_s,
     close(s);
     return FTP_BAD;   
   }
+  
+  getnameinfo(&local,sizeof(local),name,sizeof(name), port, sizeof(port),
+              NI_NUMERICHOST|NI_NUMERICSERV);
+  switch (local.sa_family) {
+  case AF_INET: {
+    sprintf(buf,"|1|%s|%s|", name, port);
+    break;
+  }
+#ifdef CYGPKG_NET_INET6
+  case AF_INET6: {
+    sprintf(buf,"|2|%s|%s|", name, port);
+    break;
+  }
+#endif
+  default:
+    close (s);
+    return (FTP_BAD);
+  }
 
-#define	BtoI(b)	(((int)b)&0xff)
-  a = (char *)&local.sin_addr;
-  p = (char *)&local.sin_port;
-  sprintf(buf,"%d,%d,%d,%d,%d,%d",
-          BtoI(a[0]),BtoI(a[1]),BtoI(a[2]),BtoI(a[3]),
-          BtoI(p[0]),BtoI(p[1]));
-
-  ret = command("PORT",buf,ctrl_s,msgbuf,msgbuflen,ftp_printf);
+  ret = command("EPRT",buf,ctrl_s,msgbuf,msgbuflen,ftp_printf);
   if (ret < 0) {
     close(s);
     return (ret);
@@ -511,7 +527,7 @@ int ftp_get(char * hostname,
             ftp_printf_t ftp_printf)
 {
 
-  struct sockaddr_in local;
+  struct sockaddr local;
   char msgbuf[256];
   int s,data_s;
   int bytes;
@@ -538,7 +554,7 @@ int ftp_get(char * hostname,
   /* We are now logged in and ready to transfer the file. Open the
      data socket ready to receive the file. It also build the PORT
      command ready to send */
-  data_s = opendatasock(s,local,msgbuf,sizeof(msgbuf),ftp_printf);
+  data_s = opendatasock(s,&local,msgbuf,sizeof(msgbuf),ftp_printf);
   if (data_s < 0) {
     close (s);
     return (data_s);
@@ -604,7 +620,7 @@ int ftp_put(char * hostname,
             ftp_printf_t ftp_printf)
 {
 
-  struct sockaddr_in local;
+  struct sockaddr local;
   char msgbuf[256];
   int s,data_s;
   int ret;
@@ -630,7 +646,7 @@ int ftp_put(char * hostname,
   /* We are now logged in and ready to transfer the file. Open the
      data socket ready to receive the file. It also build the PORT
      command ready to send */
-  data_s = opendatasock(s,local,msgbuf,sizeof(msgbuf),ftp_printf);
+  data_s = opendatasock(s,&local,msgbuf,sizeof(msgbuf),ftp_printf);
   if (data_s < 0) {
     close (s);
     return (data_s);
--- a/packages/net/ftpclient/current/tests/ftpclient1.c
+++ b/packages/net/ftpclient/current/tests/ftpclient1.c
@@ -9,6 +9,7 @@
 // -------------------------------------------
 // This file is part of eCos, the Embedded Configurable Operating System.
 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+// Copyright (C) 2003 Andrew Lunn.
 //
 // eCos is free software; you can redistribute it and/or modify it under
 // the terms of the GNU General Public License as published by the Free
@@ -66,7 +67,7 @@ static cyg_handle_t thread_handle;
 #define __xstring(_x) __string(_x)
  
 #define _FTP_SRV           __xstring(172.16.19.254) // farmnet dns0 address
-                                     
+#define _FTP_SRV_V6         __xstring(fec0:0:0:2::1)
 #define FTPBUFSIZE (1024 * 64)
 char ftpbuf[FTPBUFSIZE];
 char ftpbuf1[FTPBUFSIZE];
@@ -80,13 +81,13 @@ ftp_test(cyg_addrword_t p)
   
   init_all_network_interfaces();
  
-  CYG_TEST_INFO("Getting /etc/passwd from %s\n" _FTP_SRV);
+  CYG_TEST_INFO("Getting /etc/passwd from " _FTP_SRV);
   ret = ftp_get(_FTP_SRV,"anonymous","ftpclient1",
                  "/etc/passwd",ftpbuf,FTPBUFSIZE,
                 ftpclient_printf);
 
   if (ret > 0) {
-    diag_printf("PASS:< %s bytes received>\n",ret);
+    diag_printf("PASS:< %d bytes received>\n",ret);
   } else {
     diag_printf("FAIL:< ftp_get returned %d>\n",ret);
   }
@@ -108,13 +109,51 @@ ftp_test(cyg_addrword_t p)
                 ftpclient_printf);
   
   if (ret > 0) {
-    diag_printf("PASS:< %s bytes received>\n",ret);
+    diag_printf("PASS:< %d bytes received>\n",ret);
   } else {
     diag_printf("FAIL:< ftp_get returned %d>\n",ret);
   }
 
   CYG_TEST_PASS_FAIL(!memcmp(ftpbuf,ftpbuf1,ret),"Transfer integrity");
 
+#ifdef CYGPKG_NET_INET6
+  CYG_TEST_INFO("Getting /etc/passwd from " _FTP_SRV_V6);
+  ret = ftp_get(_FTP_SRV_V6,"anonymous","ftpclient1",
+                 "/etc/passwd",ftpbuf,FTPBUFSIZE,
+                ftpclient_printf);
+
+  if (ret > 0) {
+    diag_printf("PASS:< %d bytes received>\n",ret);
+  } else {
+    diag_printf("FAIL:< ftp_get returned %d>\n",ret);
+  }
+
+  CYG_TEST_INFO("Putting passwd file back in /incoming/passwd\n");
+  ret = ftp_put(_FTP_SRV_V6,"anonymous","ftpclient1",
+                "/incoming/passwd",ftpbuf,ret,
+                ftpclient_printf);
+  
+  if (ret > 0) {
+    diag_printf("PASS:\n");
+  } else {
+    diag_printf("FAIL:< ftp_get returned %d>\n",ret);
+  }
+
+  CYG_TEST_INFO("Reading back /incoming/passwd\n");
+  ret = ftp_get(_FTP_SRV_V6,"anonymous","ftpclient1",
+                 "/incoming/passwd",ftpbuf1,FTPBUFSIZE,
+                ftpclient_printf);
+  
+  if (ret > 0) {
+    diag_printf("PASS:< %d bytes received>\n",ret);
+  } else {
+    diag_printf("FAIL:< ftp_get returned %d>\n",ret);
+  }
+
+  CYG_TEST_PASS_FAIL(!memcmp(ftpbuf,ftpbuf1,ret),"Transfer integrity");
+
+#endif
+
   CYG_TEST_INFO("ftp_Get'ing with a bad username\n");
   ret = ftp_get(_FTP_SRV,"nosuchuser","ftpclient1",
                 "/incoming/passwd",ftpbuf1,FTPBUFSIZE,
@@ -140,7 +179,7 @@ ftp_test(cyg_addrword_t p)
   CYG_TEST_PASS_FAIL(ret==FTP_NOSUCHHOST,"Bad server");
 
   CYG_TEST_INFO("ftp_get'ing a file which is too big");
-  ret = ftp_get(_FTP_SRV,"nobody","ftpclient1",
+  ret = ftp_get(_FTP_SRV,"anonymous","ftpclient1",
                 "/incoming/passwd",ftpbuf,2,
                 ftpclient_printf);
   CYG_TEST_PASS_FAIL(ret==FTP_TOOBIG,"File too big");