commit-inetutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SCM] GNU Inetutils branch, master, updated. inetutils-1_9_1-7-g6c64ece


From: Mats Erik Andersson
Subject: [SCM] GNU Inetutils branch, master, updated. inetutils-1_9_1-7-g6c64ece
Date: Sun, 22 Jan 2012 20:25:47 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU Inetutils ".

The branch, master has been updated
       via  6c64ece3a5f68269de20a3374db983609c75818f (commit)
       via  f01c9cc6b305a3cae0e9ad09f9e52c0887383f1c (commit)
       via  2d945ae465d34d02c7f113af0ff63e20be8d4d8d (commit)
      from  0448969ce20e10376acc6e803abf45e61cd857e2 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://git.savannah.gnu.org/cgit/inetutils.git/commit/?id=6c64ece3a5f68269de20a3374db983609c75818f


commit 6c64ece3a5f68269de20a3374db983609c75818f
Author: Mats Erik Andersson <address@hidden>
Date:   Sun Jan 22 21:06:46 2012 +0100

    Test portability fix.

diff --git a/ChangeLog b/ChangeLog
index bf749e0..b9d10ba 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2012-01-22  Mats Erik Andersson <address@hidden>
 
+       * tests/ftp-localhost.sh: sysctl(1) uses `=' or `:'
+       as delimeter.  Detect both.
+
+2012-01-22  Mats Erik Andersson <address@hidden>
+
        * ftpd/extern.h (usefamily): New declaration.
        * ftpd/ftpd.c (options): New options `-4/ipv4' and `-6/ipv6`.
        (parse_opt): Detect `4' and `6'.
diff --git a/tests/ftp-localhost.sh b/tests/ftp-localhost.sh
index 3f6c9b2..171e9c3 100755
--- a/tests/ftp-localhost.sh
+++ b/tests/ftp-localhost.sh
@@ -321,7 +321,7 @@ if ! $have_address_mapping && $have_sysctl; then
     #
     value_v6only=`sysctl -a 2>/dev/null | grep v6only`
     if test -n "$value_v6only"; then
-       value_v6only=`echo $value_v6only | sed 's/^.*= *//'`
+       value_v6only=`echo $value_v6only | sed 's/^.*[=:] *//'`
        if test "$value_v6only" -eq 0; then
            # This is the good value.  Keep it.
            have_address_mapping=true

http://git.savannah.gnu.org/cgit/inetutils.git/commit/?id=f01c9cc6b305a3cae0e9ad09f9e52c0887383f1c


commit f01c9cc6b305a3cae0e9ad09f9e52c0887383f1c
Author: Mats Erik Andersson <address@hidden>
Date:   Sun Jan 22 17:33:44 2012 +0100

    ftpd: Standalone-mode with IPv6.
    
    Command line options determine in standalone-mode whether
    to start a single stacked service, or a dual stacked service.

diff --git a/ChangeLog b/ChangeLog
index beeccfd..bf749e0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,15 @@
 2012-01-22  Mats Erik Andersson <address@hidden>
 
+       * ftpd/extern.h (usefamily): New declaration.
+       * ftpd/ftpd.c (options): New options `-4/ipv4' and `-6/ipv6`.
+       (parse_opt): Detect `4' and `6'.
+       * ftpd/server_mode.c (usefamily): New variable.
+       [!DEFPORT] (DEFPORT): Use IPPORT_FTP if available.
+       (server_mode): Remove use of AI_ADDRCONFIG.  When seeing AF_UNSPEC,
+       build AF_INET6 socket and reset socket option IPV6_V6ONLY.
+
+2012-01-22  Mats Erik Andersson <address@hidden>
+
        * ftpd/extern.h (passive): New prototype.
        * ftpd/ftpcmd.y (EPRT, EPSV, CHAR): New tokens.
        (net_proto, tcp_port, net_addr): New value returning types.
diff --git a/ftpd/extern.h b/ftpd/extern.h
index 5e68544..422bd99 100644
--- a/ftpd/extern.h
+++ b/ftpd/extern.h
@@ -113,6 +113,7 @@ extern char tmpline[];
 extern off_t restart_point;
 
 /* Exported from server_mode.c.  */
+extern int usefamily;
 extern int server_mode (const char *pidfile, struct sockaddr *phis_addr,
                        socklen_t *phis_addrlen, char *argv[]);
 
diff --git a/ftpd/ftpd.c b/ftpd/ftpd.c
index 9fda874..4103dab 100644
--- a/ftpd/ftpd.c
+++ b/ftpd/ftpd.c
@@ -265,6 +265,12 @@ static struct argp_option options[] = {
   { "debug", 'd', NULL, 0,
     "debug mode",
     GRID+1 },
+  { "ipv4", '4', NULL, 0,
+    "restrict daemon to IPv4",
+    GRID+1 },
+  { "ipv6", '6', NULL, 0,
+    "restrict daemon to IPv6",
+    GRID+1 },
   { "logging", 'l', NULL, 0,
     "increase verbosity of syslog messages",
     GRID+1 },
@@ -318,6 +324,16 @@ parse_opt (int key, char *arg, struct argp_state *state)
 {
   switch (key)
     {
+    case '4':
+      /* Active in daemon mode only.  */
+      usefamily = AF_INET;
+      break;
+
+    case '6':
+      /* Active in daemon mode only.  */
+      usefamily = AF_INET6;
+      break;
+
     case 'A':
       /* Anonymous ftp only.  */
       anon_only = 1;
diff --git a/ftpd/server_mode.c b/ftpd/server_mode.c
index 6ef6ef4..0a48773 100644
--- a/ftpd/server_mode.c
+++ b/ftpd/server_mode.c
@@ -40,9 +40,17 @@
 #include <libinetutils.h>
 #include "unused-parameter.h"
 
+int usefamily = AF_UNSPEC;     /* Address family for daemon.  */
+
 static void reapchild (int);
 
-#define DEFPORT 21
+#ifndef DEFPORT
+# ifdef IPPORT_FTP
+#  define DEFPORT IPPORT_FTP
+# else /* !IPPORT_FTP */
+#  define DEFPORT 21
+# endif
+#endif /* !DEFPORT */
 
 #ifdef WITH_WRAP
 
@@ -81,7 +89,7 @@ check_host (struct sockaddr *sa, socklen_t len)
     }
   return (1);
 }
-#endif
+#endif /* WITH_WRAP */
 
 static void
 reapchild (int signo _GL_UNUSED_PARAMETER)
@@ -95,7 +103,8 @@ reapchild (int signo _GL_UNUSED_PARAMETER)
 
 /* The parameter '*phis_addrlen' must be initiated
    with the space available at calling time.
-   The actually used space will then be returned.  */
+   The size of used space will then be returned.
+ */
 int
 server_mode (const char *pidfile, struct sockaddr *phis_addr,
             socklen_t *phis_addrlen, char *argv[])
@@ -121,12 +130,15 @@ server_mode (const char *pidfile, struct sockaddr 
*phis_addr,
   snprintf (portstr, sizeof (portstr), "%u", port);
 
   memset (&hints, 0, sizeof (hints));
-  hints.ai_family = AF_INET;
   hints.ai_socktype = SOCK_STREAM;
   hints.ai_flags = AI_PASSIVE;
-#ifdef AI_ADDRCONFIG
-  hints.ai_flags |= AI_ADDRCONFIG;
-#endif
+
+  /* If an undetermined address family is passed,
+   * we build a dual stacked listener from an AF_INET6
+   * socket by unsetting the sockopt IPV6_V6ONLY.
+   * Otherwise the resolver often prefers AF_INET.
+   */
+  hints.ai_family = (usefamily != AF_UNSPEC) ? usefamily : AF_INET6;
 
   err = getaddrinfo (NULL, portstr, &hints, &res);
   if (err)
@@ -149,6 +161,15 @@ server_mode (const char *pidfile, struct sockaddr 
*phis_addr,
          syslog (LOG_ERR, "control setsockopt: %m");
       }
 
+      /* Upgrade to dual stacked socket.  */
+      if (usefamily == AF_UNSPEC && ai->ai_family == AF_INET6)
+       {
+         int off = 0;
+         if (setsockopt (ctl_sock, IPPROTO_IPV6, IPV6_V6ONLY,
+                         (char *) &off, sizeof (off)) < 0)
+           syslog (LOG_DEBUG, "setsockopt bindv6only: %m");
+       }
+
       if (bind (ctl_sock, ai->ai_addr, ai->ai_addrlen))
        {
          close (ctl_sock);

http://git.savannah.gnu.org/cgit/inetutils.git/commit/?id=2d945ae465d34d02c7f113af0ff63e20be8d4d8d


commit 2d945ae465d34d02c7f113af0ff63e20be8d4d8d
Author: Mats Erik Andersson <address@hidden>
Date:   Sun Jan 22 17:30:04 2012 +0100

    ftpd: Inetd-mode with EPRT and EPSV.
    
    Provide tested IPv6 support in inetd-mode, with special
    care to IPv4-mapped-IPv6 addressing also in legacy mode.

diff --git a/ChangeLog b/ChangeLog
index 3e862d1..beeccfd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2012-01-22  Mats Erik Andersson <address@hidden>
+
+       * ftpd/extern.h (passive): New prototype.
+       * ftpd/ftpcmd.y (EPRT, EPSV, CHAR): New tokens.
+       (net_proto, tcp_port, net_addr): New value returning types.
+       (cmd) <PORT>: Use value returned from `host_port'. Erase PDATA
+       and USEDEFAULT only if successful.
+       (cmd) <PASV>: Changed call to passive().
+       (cmd) <EPRT, EPSV>: New code for token parsers.
+       (host_port): Declare as `%type <i>', returning an integer.
+       Handle mapped IPv4 addresses.  Erase DATA_DEST after an address
+       resolving failure.
+       (DLIST): New macro used as state in finite state automata.
+       (cmdtab): New entries EPRT and EPSV.
+       (yylex) <DLIST>: Implement new state.
+       * ftpd/ftpd.c (passive): New prototype `passive(int, int)'.
+       New variable TRY_AF.  Select address family based on parameters
+       and CTRL_ADDR.
+       (passive) <EPSV>: Handle AF_INET and AF_INET6.
+       (passive) <PASV>: Process also mapped IPv4 address.
+       * tests/ftp-localhost.sh: More tests in inetd mode: EPSV and
+       EPRT for IPv4 and IPv6.  Suppress FTP dialog unless verbose.
+
 2012-01-18  Simon Josefsson  <address@hidden>
 
        * ftpd/ftpd.c (options): Don't use OPTION_ARG_OPTIONAL for -a.
diff --git a/ftpd/extern.h b/ftpd/extern.h
index d9103fb..5e68544 100644
--- a/ftpd/extern.h
+++ b/ftpd/extern.h
@@ -68,7 +68,7 @@ extern void lreply (int, const char *, ...);
 extern void makedir (const char *);
 extern void nack (const char *);
 extern void pass (const char *);
-extern void passive (void);
+extern void passive (int, int);
 extern void perror_reply (int, const char *);
 extern void pwd (void);
 extern void removedir (const char *);
diff --git a/ftpd/ftpcmd.y b/ftpd/ftpcmd.y
index 49a4355..e637ac8 100644
--- a/ftpd/ftpcmd.y
+++ b/ftpd/ftpcmd.y
@@ -50,6 +50,14 @@
 /*
  * Grammar for FTP commands.
  * See RFC 959.
+ *
+ * TODO Update to RFC 3659
+ *
+ * RFC 2428
+ *
+ * TODO RFC 1639
+ *
+ * FIXME: Rewrite with GNU standard formatting.  Legacy code is changed!
  */
 
 %{
@@ -136,16 +144,20 @@ static void yyerror       (const char *s);
        STAT    HELP    NOOP    MKD     RMD     PWD
        CDUP    STOU    SMNT    SYST    SIZE    MDTM
 
+       EPRT    EPSV
+
        UMASK   IDLE    CHMOD
 
        LEXERR
 
 %token <s> STRING
-%token <i> NUMBER
+%token <i> NUMBER CHAR
 
 %type  <i> check_login octal_number byte_size
 %type  <i> struct_code mode_code type_code form_code
 %type  <s> pathstring pathname password username
+%type  <i> host_port net_proto tcp_port
+%type  <s> net_addr
 
 %start cmd_list
 
@@ -176,20 +188,33 @@ cmd
                }
        | PORT check_login SP host_port CRLF
                {
-                       usedefault = 0;
-                       if (pdata >= 0) {
-                                close(pdata);
-                               pdata = -1;
-                       }
                        if ($2) {
-                               if (memcmp (&((struct sockaddr_in *) 
&his_addr)->sin_addr,
-                                       &((struct sockaddr_in *) 
&data_dest)->sin_addr,
-                                       sizeof (struct in_addr)) == 0 &&
-                                       ntohs (((struct sockaddr_in *) 
&data_dest)->sin_port) >
-                                       IPPORT_RESERVED) {
+                               if ($4
+                                   && (his_addr.ss_family == AF_INET
+                                       && memcmp (&((struct sockaddr_in *) 
&his_addr)->sin_addr,
+                                                  &((struct sockaddr_in *) 
&data_dest)->sin_addr,
+                                                  sizeof (struct in_addr)) == 0
+                                       && ntohs (((struct sockaddr_in *) 
&data_dest)->sin_port)
+                                          > IPPORT_RESERVED
+                                       ||
+                                       his_addr.ss_family == AF_INET6
+                                       && memcmp (&((struct sockaddr_in6 *) 
&his_addr)->sin6_addr,
+                                                  &((struct sockaddr_in6 *) 
&data_dest)->sin6_addr,
+                                                  sizeof (struct in6_addr)) == 0
+                                       && ntohs (((struct sockaddr_in6 *) 
&data_dest)->sin6_port)
+                                          > IPPORT_RESERVED
+                                      )
+                                  )
+                               {
+                                       usedefault = 0;
+                                       if (pdata >= 0) {
+                                               close(pdata);
+                                               pdata = -1;
+                                       }
                                        reply (200, "PORT command successful.");
                                }
                                else {
+                                       usedefault = 1;
                                        memset (&data_dest, 0,
                                                sizeof (data_dest));
                                        reply(500, "Illegal PORT Command");
@@ -199,7 +224,7 @@ cmd
        | PASV check_login CRLF
                {
                        if ($2)
-                               passive();
+                               passive(0, AF_INET);
                }
        | TYPE SP type_code CRLF
                {
@@ -555,6 +580,143 @@ cmd
                        }
                        free($4);
                }
+       | EPRT check_login SP CHAR net_proto CHAR net_addr CHAR tcp_port CHAR 
CRLF
+               {
+                       usedefault = 0;
+                       if (pdata >= 0) {
+                               close(pdata);
+                               pdata = -1;
+                       }
+                       /* A first sanity check.  */
+                       if ($2                          /* valid login */
+                           && ($5 > 0)                 /* valid protocols */
+                           && ($4 > 32 && $4 < 127)    /* legal first 
delimiter */
+                                                       /* identical delimiters 
*/
+                           && ($4 == $6 && $4 == $8 && $4 == $10))
+                       {
+                               /* We only accept connections using
+                                * the same address family as is
+                                * currently in use, unless we
+                                * detect IPv4-mapped-to-IPv6.  */
+                               if (his_addr.ss_family == $5
+#ifdef AI_V4MAPPED
+                                   || ($5 == AF_INET6 && his_addr.ss_family == 
AF_INET)
+                                   || ($5 == AF_INET && his_addr.ss_family == 
AF_INET6)
+#endif
+                                   ) {
+                                       int err;
+                                       char p[8];
+                                       struct addrinfo hints, *res;
+
+                                       memset (&hints, 0, sizeof (hints));
+                                       snprintf (p, sizeof (p), "%u", $9 & 
0xffff);
+                                       hints.ai_family = $5;
+                                       hints.ai_socktype = SOCK_STREAM;
+                                       hints.ai_flags = AI_NUMERICHOST | 
AI_NUMERICSERV;
+
+#ifdef AI_V4MAPPED
+                                       if (his_addr.ss_family != $5)
+                                               hints.ai_flags |= AI_V4MAPPED;
+#endif
+
+                                       err = getaddrinfo ($7, p, &hints, &res);
+                                       if (err)
+                                               reply (500, "Illegal EPRT 
Command");
+                                       else if (/* sanity check */
+                                                (his_addr.ss_family == AF_INET
+                                                 && memcmp (&((struct 
sockaddr_in *) &his_addr)->sin_addr,
+                                                            &((struct 
sockaddr_in *) res->ai_addr)->sin_addr,
+                                                            sizeof (struct 
in_addr)) == 0
+                                                 && ntohs (((struct 
sockaddr_in *) res->ai_addr)->sin_port) > IPPORT_RESERVED
+                                                )
+                                                ||
+                                                (his_addr.ss_family == AF_INET6
+                                                 && memcmp (&((struct 
sockaddr_in6 *) &his_addr)->sin6_addr,
+                                                            &((struct 
sockaddr_in6 *) res->ai_addr)->sin6_addr,
+                                                            sizeof (struct 
in6_addr)) == 0
+                                                 && ntohs (((struct 
sockaddr_in6 *) res->ai_addr)->sin6_port) > IPPORT_RESERVED
+                                                )
+#ifdef AI_V4MAPPED
+                                                ||
+                                                (his_addr.ss_family == AF_INET 
&& res->ai_family == AF_INET6
+                                                 && IN6_IS_ADDR_V4MAPPED 
(&((struct sockaddr_in6 *) res->ai_addr)->sin6_addr)
+                                                 && memcmp (&((struct 
sockaddr_in *) &his_addr)->sin_addr,
+                                                            &((struct in_addr 
*) &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr)[3],
+                                                            sizeof (struct 
in_addr)) == 0
+                                                 && ntohs (((struct 
sockaddr_in6 *) res->ai_addr)->sin6_port) > IPPORT_RESERVED
+                                                )
+                                                ||
+                                                (his_addr.ss_family == 
AF_INET6 && res->ai_family == AF_INET
+                                                 && IN6_IS_ADDR_V4MAPPED 
(&((struct sockaddr_in6 *) &his_addr)->sin6_addr)
+                                                 && memcmp (&((struct in_addr 
*) &((struct sockaddr_in6 *) &his_addr)->sin6_addr)[3],
+                                                            &((struct 
sockaddr_in *) res->ai_addr)->sin_addr,
+                                                            sizeof (struct 
in_addr)) == 0
+                                                 && ntohs (((struct 
sockaddr_in *) res->ai_addr)->sin_port) > IPPORT_RESERVED
+                                                )
+#endif /* AI_V4MAPPED */
+                                               )
+                                       {
+                                               /* In the case of IPv4 mapped 
as IPv6,
+                                                * the addresses were proven to 
coincide,
+                                                * only the extraction remains. 
 Since
+                                                * non-mapped is the standard, 
test that
+                                                * situation first.
+                                                */
+                                               if (his_addr.ss_family == 
res->ai_family)
+                                               {
+                                               memcpy (&data_dest, 
res->ai_addr, res->ai_addrlen);
+                                               data_dest_len = res->ai_addrlen;
+                                               }
+                                               else if (his_addr.ss_family == 
AF_INET && res->ai_family == AF_INET6)
+                                               {
+                                               /* `his_addr' contains the 
reduced IPv4 address.  */
+                                               memcpy (&data_dest, &his_addr, 
sizeof (struct sockaddr_in));
+                                               data_dest_len = sizeof (struct 
sockaddr_in);
+                                               ((struct sockaddr_in *) 
&data_dest)->sin_port = ((struct sockaddr_in6 *) res->ai_addr)->sin6_port;
+                                               } else
+                                               {
+                                               /* `res->ai_addr' contains the 
reduced IPv4 address,
+                                                * but the connection stands on 
`his_addr' which is
+                                                * an IPv4-mapped-IPv6 address. 
 */
+                                               memcpy (&data_dest, &his_addr, 
sizeof (struct sockaddr_in6));
+                                               data_dest_len = sizeof (struct 
sockaddr_in6);
+                                               ((struct sockaddr_in6 *) 
&data_dest)->sin6_port = ((struct sockaddr_in *) res->ai_addr)->sin_port;
+                                               }
+                                               freeaddrinfo (res);
+                                               reply (200, "EPRT command 
successful.");
+                                       } else {
+                                               /* failed identity check */
+                                               if (res)
+                                                       freeaddrinfo (res);
+                                               reply (500, "Illegal EPRT 
Command");
+                                       }
+                               } else {
+                                       /* Not fit for established connection.  
*/
+                                       reply (522, "Network protocol not 
supported, use (%d)",
+                                               ($5 == 1) ? 2 : 1);
+                               }
+                       } else if ($2 && ($5 <= 0)) {
+                               reply (522, "Network protocol not supported, 
use (1,2)");
+                       } else if ($2) {
+                               /* Incorrect delimiters detected,
+                                * the other conditions are true. */
+                               reply (500, "Illegal EPRT Command");
+                       }
+               }
+       | EPSV check_login CRLF
+               {
+                       if ($2)
+                               passive(1, AF_UNSPEC);
+               }
+       | EPSV check_login SP net_proto CRLF
+               {
+                       if ($2) {
+                               if ($4 > 0)
+                                       passive(1, $4);
+                               else
+                                       reply (522, "Network protocol not 
supported, use (1,2)");
+                       }
+               }
        | QUIT CRLF
                {
                        reply(221, "Goodbye.");
@@ -605,12 +767,33 @@ byte_size
        : NUMBER
        ;
 
+net_proto
+       : NUMBER
+               {       /* Rewrite as valid address family.  */
+                       if ($1 == 1)
+                               $$ = AF_INET;
+                       else if ($1 == 2)
+                               $$ = AF_INET6;
+                       else
+                               $$ = -1;        /* Invalid protocol.  */
+
+               }
+       ;
+
+tcp_port
+       : NUMBER
+       ;
+
+net_addr
+       : STRING
+       ;
+
 host_port
        : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
                NUMBER COMMA NUMBER
                {
                        int err;
-                       char a[INET_ADDRSTRLEN], p[8];
+                       char a[INET6_ADDRSTRLEN], p[8];
                        struct addrinfo hints, *res;
 
                        snprintf (a, sizeof (a), "%u.%u.%u.%u",
@@ -618,18 +801,33 @@ host_port
                        snprintf (p, sizeof (p), "%u",
                                (($9 & 0xff) << 8) + ($11 & 0xff));
                        memset (&hints, 0, sizeof (hints));
-                       hints.ai_family = AF_INET;
+                       hints.ai_family = his_addr.ss_family;
                        hints.ai_socktype = SOCK_STREAM;
                        hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
 
+                       if (his_addr.ss_family == AF_INET6) {
+                           /* IPv4 mapped to IPv6.  */
+                           hints.ai_family = AF_INET6;
+#ifdef AI_V4MAPPED
+                           hints.ai_flags |= AI_V4MAPPED;
+#endif
+                           snprintf (a, sizeof (a), "::ffff:%u.%u.%u.%u",
+                                     $1 & 0xff, $3 & 0xff, $5 & 0xff, $7 & 
0xff);
+                       }
+
                        err = getaddrinfo (a, p, &hints, &res);
-                       if (err)
-                         reply (550, "Address failure: %s,%s", a, p);
+                       if (err) {
+                           reply (550, "Address failure: %s,%s", a, p);
+                           memset (&data_dest, 0, sizeof (data_dest));
+                           data_dest_len = 0;
+                           $$ = 0;
+                         }
                        else
                          {
                            memcpy (&data_dest, res->ai_addr, res->ai_addrlen);
                            data_dest_len = res->ai_addrlen;
                            freeaddrinfo (res);
+                           $$ = 1;
                          }
                }
        ;
@@ -789,7 +987,6 @@ octal_number
                }
        ;
 
-
 check_login
        : /* empty */
                {
@@ -813,6 +1010,7 @@ check_login
 #define        ZSTR2   6       /* optional STRING after SP */
 #define        SITECMD 7       /* SITE command */
 #define        NSTR    8       /* Number followed by a string */
+#define        DLIST   9       /* SP and delimited list for EPRT/EPSV */
 
 struct tab cmdtab[] = {                /* In order defined in RFC 765 */
        { "USER", USER, STR1, 1,        "<sp> username" },
@@ -862,6 +1060,8 @@ struct tab cmdtab[] = {            /* In order defined in 
RFC 765 */
        { "STOU", STOU, STR1, 1,        "<sp> file-name" },
        { "SIZE", SIZE, OSTR, 1,        "<sp> path-name" },
        { "MDTM", MDTM, OSTR, 1,        "<sp> path-name" },
+       { "EPRT", EPRT, DLIST, 1,       "<sp> <d> proto <d> addr <d> port <d>" 
},
+       { "EPSV", EPSV, ARGS, 1,        "[ <sp> af ]" },
        { NULL,   0,    0,    0,        0 }
 };
 
@@ -1109,6 +1309,51 @@ yylex(void)
                        state = STR1;
                        goto dostr1;
 
+               case DLIST:
+                       /* Either numerical strings or
+                        * address strings for IPv4 and IPv6.
+                        * The consist of hexadecimal chars,
+                        * colon and periods.  A period can
+                        * not begin a valid address.  */
+                       if (isxdigit(cbuf[cpos]) || cbuf[cpos] == ':') {
+                               int is_num = 1; /* Only to turn off.  */
+
+                               cp = &cbuf[cpos];
+                               while (isxdigit(cbuf[cpos])
+                                               || cbuf[cpos] == ':'
+                                               || cbuf[cpos] == '.') {
+                                       if (!isdigit(cbuf[cpos]))
+                                               is_num = 0;
+                                       cpos++;
+                               }
+                               c = cbuf[cpos];
+                               cbuf[cpos] = '\0';
+                               if (is_num) {
+                                       yylval.i = atoi(cp);
+                                       cbuf[cpos] = c;
+                                       return (NUMBER);
+                               } else {
+                                       yylval.s = copy(cp);
+                                       cbuf[cpos] = c;
+                                       return (STRING);
+                               }
+                       }
+
+                       switch (c = cbuf[cpos++]) {
+
+                       case ' ':
+                               return (SP);
+
+                       case '\n':
+                               state = CMD;
+                               return (CRLF);
+
+                       default:
+                               yylval.i = c;
+                               return (CHAR);
+                       }
+                       break;
+
                case ARGS:
                        if (isdigit(cbuf[cpos])) {
                                cp = &cbuf[cpos];
diff --git a/ftpd/ftpd.c b/ftpd/ftpd.c
index bc31558..9fda874 100644
--- a/ftpd/ftpd.c
+++ b/ftpd/ftpd.c
@@ -1748,11 +1748,18 @@ myoob (int signo _GL_UNUSED_PARAMETER)
    a legitimate response by Jon Postel in a telephone conversation
    with Rick Adams on 25 Jan 89.  */
 void
-passive (void)
+passive (int epsv, int af)
 {
   char *p, *a;
+  int try_af;
 
-  pdata = socket (ctrl_addr.ss_family, SOCK_STREAM, 0);
+  /* EPSV might ask for a particular address family.  */
+  if (epsv && af > 0)
+    try_af = af;
+  else
+    try_af = ctrl_addr.ss_family;
+
+  pdata = socket (try_af, SOCK_STREAM, 0);
   if (pdata < 0)
     {
       perror_reply (425, "Can't open passive connection");
@@ -1760,6 +1767,7 @@ passive (void)
     }
   memcpy (&pasv_addr, &ctrl_addr, sizeof (pasv_addr));
   pasv_addrlen = ctrl_addrlen;
+
   /* Erase the port number.  */
   if (pasv_addr.ss_family == AF_INET6)
     ((struct sockaddr_in6 *) &pasv_addr)->sin6_port = 0;
@@ -1779,16 +1787,34 @@ passive (void)
   if (listen (pdata, 1) < 0)
     goto pasv_error;
 
-  if (pasv_addr.ss_family == AF_INET6)
+  if (epsv)
     {
-      reply (229, "Extended Passive Mode OK (|||%u|)",
-            ((struct sockaddr_in6 *) &pasv_addr)->sin6_port);
+      /* EPSV for IPv4 and IPv6.  */
+      reply (229, "Entering Extended Passive Mode (|||%u|)",
+            ntohs((pasv_addr.ss_family == AF_INET)
+                   ? ((struct sockaddr_in *) &pasv_addr)->sin_port
+                   : ((struct sockaddr_in6 *) &pasv_addr)->sin6_port));
       return;
     }
-  else
+  else /* !epsv */
     {
-      a = (char *) &((struct sockaddr_in *) &pasv_addr)->sin_addr;
-      p = (char *) &((struct sockaddr_in *) &pasv_addr)->sin_port;
+      /* PASV for IPv4.
+       *
+       * Some systems, like OpenSolaris, prefer to return
+       * an IPv4-mapped-IPv6 address, which must be processed
+       * for printout.  */
+      if (pasv_addr.ss_family == AF_INET6
+         && IN6_IS_ADDR_V4MAPPED (&((struct sockaddr_in6 *) 
&pasv_addr)->sin6_addr))
+       {
+         a = (char *) &((struct sockaddr_in6 *) &pasv_addr)->sin6_addr;
+         a += 3 * sizeof (struct in_addr);     /* Skip padding up to IPv4 
content.  */
+         p = (char *) &((struct sockaddr_in6 *) &pasv_addr)->sin6_port;
+       }
+      else
+       {
+         a = (char *) &((struct sockaddr_in *) &pasv_addr)->sin_addr;
+         p = (char *) &((struct sockaddr_in *) &pasv_addr)->sin_port;
+       }
 
 #define UC(b) (((int) b) & 0xff)
 
diff --git a/tests/ftp-localhost.sh b/tests/ftp-localhost.sh
index 556a717..3f6c9b2 100755
--- a/tests/ftp-localhost.sh
+++ b/tests/ftp-localhost.sh
@@ -19,9 +19,13 @@
 
 # Written by Simon Josefsson
 
-# FIXME: Strict IPv4 setup, until Mats has completed the migration
-# of ftpd to support IPv6.  Address mapping IPv4-to-IPv6 is not
-# uniform an all platforms, thus `tcp4' for inetd and '-4' for ftp.
+# FIXME: Separate tests of IPv4 and IPv6 using `inetd', no testing
+# of standalone daemon yet.
+
+# Address mapping IPv4-to-IPv6 is not uniform an all platforms,
+# thus separately using `tcp4' and `tcp6' for streams in `inetd.conf'.
+# FIXME: Once functionality attains, reduce code duplication when
+# evaluating partial tests.
 
 set -e
 
@@ -29,6 +33,8 @@ FTP=${FTP:-../ftp/ftp$EXEEXT}
 FTPD=${FTPD:-../ftpd/ftpd$EXEEXT}
 INETD=${INETD:-../src/inetd$EXEEXT}
 TARGET=${TARGET:-127.0.0.1}
+TARGET6=${TARGET6:-::1}
+TARGET46=${TARGET46:-::ffff:127.0.0.1}
 
 if [ $VERBOSE ]; then
     set -x
@@ -66,7 +72,10 @@ posttesting () {
 trap posttesting 0 1 2 3 15
 
 echo "4711 stream tcp4 nowait root $PWD/$FTPD ftpd -A -l" > $TMPDIR/inetd.conf
+echo "4711 stream tcp6 nowait root $PWD/$FTPD ftpd -A -l" >> $TMPDIR/inetd.conf
 echo "machine $TARGET login ftp password foobar" > $TMPDIR/.netrc
+echo "machine $TARGET6 login ftp password foobar" >> $TMPDIR/.netrc
+echo "machine $TARGET46 login ftp password foobar" >> $TMPDIR/.netrc
 chmod 600 $TMPDIR/.netrc
 
 $INETD --pidfile=$TMPDIR/inetd.pid $TMPDIR/inetd.conf
@@ -74,8 +83,9 @@ $INETD --pidfile=$TMPDIR/inetd.pid $TMPDIR/inetd.conf
 # Wait for inetd to write pid and open socket
 sleep 2
 
-# Test a passive connection.
+# Test a passive connection: PASV and IPv4.
 #
+echo "PASV to $TARGET (IPv4) using inetd."
 cat <<STOP |
 rstatus
 dir
@@ -83,7 +93,7 @@ STOP
 HOME=$TMPDIR $FTP $TARGET 4711 -4 -v -p -t >$TMPDIR/ftp.stdout
 
 errno=$?
-cat $TMPDIR/ftp.stdout
+[ -z "$VERBOSE" ] || cat $TMPDIR/ftp.stdout
 
 if [ $errno != 0 ]; then
     echo running ftp failed? errno $errno
@@ -107,8 +117,9 @@ if ! grep '226 Transfer complete.' $TMPDIR/ftp.stdout; then
     exit 1
 fi
 
-# Test an active connection.
+# Test an active connection: PORT and IPv4.
 #
+echo "PORT to $TARGET (IPv4) using inetd."
 cat <<STOP |
 rstatus
 dir
@@ -116,7 +127,145 @@ STOP
 HOME=$TMPDIR $FTP $TARGET 4711 -4 -v -t >$TMPDIR/ftp.stdout
 
 errno=$?
-cat $TMPDIR/ftp.stdout
+[ -z "$VERBOSE" ] || cat $TMPDIR/ftp.stdout
+
+if [ $errno != 0 ]; then
+    echo running ftp failed? errno $errno
+    exit 77
+fi
+
+if [ $errno != 0 ]; then
+    echo running ftp failed? errno $errno
+    exit 77
+fi
+
+# Standing control connection?
+if ! grep 'FTP server status' $TMPDIR/ftp.stdout; then
+    echo cannot find expected output for active ftp client?
+    exit 1
+fi
+
+# Was data transfer successful?
+if ! grep '226 Transfer complete.' $TMPDIR/ftp.stdout; then
+    echo cannot find transfer result for active ftp client?
+    exit 1
+fi
+
+# Test a passive connection: EPSV and IPv4.
+#
+echo "EPSV to $TARGET (IPv4) using inetd."
+cat <<STOP |
+rstatus
+epsv4
+dir
+STOP
+HOME=$TMPDIR $FTP $TARGET 4711 -4 -v -p -t >$TMPDIR/ftp.stdout
+
+errno=$?
+[ -z "$VERBOSE" ] || cat $TMPDIR/ftp.stdout
+
+if [ $errno != 0 ]; then
+    echo running ftp failed? errno $errno
+    exit 77
+fi
+
+if [ $errno != 0 ]; then
+    echo running ftp failed? errno $errno
+    exit 77
+fi
+
+# Standing control connection?
+if ! grep 'FTP server status' $TMPDIR/ftp.stdout; then
+    echo cannot find expected output for passive ftp client?
+    exit 1
+fi
+
+# Was data transfer successful?
+if ! grep '226 Transfer complete.' $TMPDIR/ftp.stdout; then
+    echo cannot find transfer result for passive ftp client?
+    exit 1
+fi
+
+# Test an active connection: EPRT and IPv4.
+#
+echo "EPRT to $TARGET (IPv4) using inetd."
+cat <<STOP |
+rstatus
+epsv4
+dir
+STOP
+HOME=$TMPDIR $FTP $TARGET 4711 -4 -v -t >$TMPDIR/ftp.stdout
+
+errno=$?
+[ -z "$VERBOSE" ] || cat $TMPDIR/ftp.stdout
+
+if [ $errno != 0 ]; then
+    echo running ftp failed? errno $errno
+    exit 77
+fi
+
+if [ $errno != 0 ]; then
+    echo running ftp failed? errno $errno
+    exit 77
+fi
+
+# Standing control connection?
+if ! grep 'FTP server status' $TMPDIR/ftp.stdout; then
+    echo cannot find expected output for active ftp client?
+    exit 1
+fi
+
+# Was data transfer successful?
+if ! grep '226 Transfer complete.' $TMPDIR/ftp.stdout; then
+    echo cannot find transfer result for active ftp client?
+    exit 1
+fi
+
+# Test a passive connection: EPSV and IPv6.
+#
+echo "EPSV to $TARGET6 (IPv6) using inetd."
+cat <<STOP |
+rstatus
+dir
+STOP
+HOME=$TMPDIR $FTP $TARGET6 4711 -6 -v -p -t >$TMPDIR/ftp.stdout
+
+errno=$?
+[ -z "$VERBOSE" ] || cat $TMPDIR/ftp.stdout
+
+if [ $errno != 0 ]; then
+    echo running ftp failed? errno $errno
+    exit 77
+fi
+
+if [ $errno != 0 ]; then
+    echo running ftp failed? errno $errno
+    exit 77
+fi
+
+# Standing control connection?
+if ! grep 'FTP server status' $TMPDIR/ftp.stdout; then
+    echo cannot find expected output for passive ftp client?
+    exit 1
+fi
+
+# Was data transfer successful?
+if ! grep '226 Transfer complete.' $TMPDIR/ftp.stdout; then
+    echo cannot find transfer result for passive ftp client?
+    exit 1
+fi
+
+# Test an active connection: EPRT and IPv6.
+#
+echo "EPRT to $TARGET6 (IPv6) using inetd."
+cat <<STOP |
+rstatus
+dir
+STOP
+HOME=$TMPDIR $FTP $TARGET6 4711 -6 -v -t >$TMPDIR/ftp.stdout
+
+errno=$?
+[ -z "$VERBOSE" ] || cat $TMPDIR/ftp.stdout
 
 if [ $errno != 0 ]; then
     echo running ftp failed? errno $errno
@@ -140,4 +289,126 @@ if ! grep '226 Transfer complete.' $TMPDIR/ftp.stdout; 
then
     exit 1
 fi
 
+# Availability of IPv4-mapped IPv6 addresses.
+#
+# These are impossible on OpenBSD, so a flexible test
+# is implemented using sysctl(1) as tool.
+
+# Helpers tp catch relevant execution path.
+have_sysctl=false
+have_address_mapping=false
+
+# Known exceptions:
+#
+# OpenSolaris is known to allow address mapping
+test `uname -s` = 'SunOS' && have_address_mapping=true
+
+if ! $have_address_mapping; then
+    # Do we have sysctl(1) available?
+    if ! which sysctl >/dev/null 2>&1; then
+       echo "Warning: Not testing IPv4-mapped addresses."
+    else
+       have_sysctl=true
+    fi
+fi
+
+if ! $have_address_mapping && $have_sysctl; then
+    # Extract the present setting of
+    #
+    #    net.ipv6.bindv6only (Linux)
+    # or
+    #    net.inet6.ip6.v6only (BSD).
+    #
+    value_v6only=`sysctl -a 2>/dev/null | grep v6only`
+    if test -n "$value_v6only"; then
+       value_v6only=`echo $value_v6only | sed 's/^.*= *//'`
+       if test "$value_v6only" -eq 0; then
+           # This is the good value.  Keep it.
+           have_address_mapping=true
+       else
+           echo "Warning: Address mapping IPv4-to-Ipv6 is disabled."
+           # Set a non-zero value for later testing.
+           value_v6only=2
+       fi
+    else
+       # Simulate a non-mapping answer in cases where "v6only" missed.
+       value_v6only=2
+    fi
+fi
+
+# Test functionality of IPv4-mapped IPv6 addresses.
+#
+if $have_address_mapping && test -n "$TARGET46" ; then
+    # Test a passive connection: EPSV and IPv4-mapped-IPv6.
+    #
+    echo "EPSV to $TARGET46 (IPv4-as-IPv6) using inetd."
+    cat <<-STOP |
+       rstatus
+       dir
+       STOP
+    HOME=$TMPDIR $FTP $TARGET46 4711 -6 -v -p -t >$TMPDIR/ftp.stdout
+
+    errno=$?
+    [ -z "$VERBOSE" ] || cat $TMPDIR/ftp.stdout
+
+    if [ $errno != 0 ]; then
+       echo running ftp failed? errno $errno
+       exit 77
+    fi
+
+    if [ $errno != 0 ]; then
+       echo running ftp failed? errno $errno
+       exit 77
+    fi
+
+    # Standing control connection?
+    if ! grep 'FTP server status' $TMPDIR/ftp.stdout; then
+       echo cannot find expected output for passive ftp client?
+       exit 1
+    fi
+
+    # Was data transfer successful?
+    if ! grep '226 Transfer complete.' $TMPDIR/ftp.stdout; then
+       echo cannot find transfer result for passive ftp client?
+       exit 1
+    fi
+
+    # Test an active connection: EPRT and IPvIPv6.
+    #
+    echo "EPRT to $TARGET46 (IPv4-as-IPv6) using inetd."
+    cat <<-STOP |
+       rstatus
+       dir
+       STOP
+    HOME=$TMPDIR $FTP $TARGET46 4711 -6 -v -t >$TMPDIR/ftp.stdout
+
+    errno=$?
+    [ -z "$VERBOSE" ] || cat $TMPDIR/ftp.stdout
+
+    if [ $errno != 0 ]; then
+       echo running ftp failed? errno $errno
+       exit 77
+    fi
+
+    if [ $errno != 0 ]; then
+       echo running ftp failed? errno $errno
+       exit 77
+    fi
+
+    # Standing control connection?
+    if ! grep 'FTP server status' $TMPDIR/ftp.stdout; then
+       echo cannot find expected output for active ftp client?
+       exit 1
+    fi
+
+    # Was data transfer successful?
+    if ! grep '226 Transfer complete.' $TMPDIR/ftp.stdout; then
+       echo cannot find transfer result for active ftp client?
+       exit 1
+    fi
+else
+    # The IPv4-as-IPv6 tests were not performed.
+    echo 'Skipping two tests of IPv4 mapped as IPv6.'
+fi
+
 exit 0

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog              |   38 +++++++
 ftpd/extern.h          |    3 +-
 ftpd/ftpcmd.y          |  279 ++++++++++++++++++++++++++++++++++++++++++++---
 ftpd/ftpd.c            |   58 +++++++++--
 ftpd/server_mode.c     |   35 +++++-
 tests/ftp-localhost.sh |  285 ++++++++++++++++++++++++++++++++++++++++++++++-
 6 files changed, 658 insertions(+), 40 deletions(-)


hooks/post-receive
-- 
GNU Inetutils 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]