ipvs: support more FTP PASV responses
authorJulian Anastasov <ja@ssi.bg>
Sun, 29 May 2011 21:02:23 +0000 (00:02 +0300)
committerSimon Horman <horms@verge.net.au>
Mon, 13 Jun 2011 01:03:01 +0000 (10:03 +0900)
Change the parsing of FTP commands and responses to
support skip character. It allows to detect variations in
the 227 PASV response.

Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Simon Horman <horms@verge.net.au>
net/netfilter/ipvs/ip_vs_ftp.c

index af63553fa332e06bf0562a6f1630e33e30ca889c..4490a32ad5b2fbfb3b47f3df0b21e54b30020597 100644 (file)
@@ -44,8 +44,8 @@
 #include <net/ip_vs.h>
 
 
-#define SERVER_STRING "227 Entering Passive Mode ("
-#define CLIENT_STRING "PORT "
+#define SERVER_STRING "227 "
+#define CLIENT_STRING "PORT"
 
 
 /*
@@ -79,14 +79,17 @@ ip_vs_ftp_done_conn(struct ip_vs_app *app, struct ip_vs_conn *cp)
 
 /*
  * Get <addr,port> from the string "xxx.xxx.xxx.xxx,ppp,ppp", started
- * with the "pattern" and terminated with the "term" character.
+ * with the "pattern", ignoring before "skip" and terminated with
+ * the "term" character.
  * <addr,port> is in network order.
  */
 static int ip_vs_ftp_get_addrport(char *data, char *data_limit,
-                                 const char *pattern, size_t plen, char term,
+                                 const char *pattern, size_t plen,
+                                 char skip, char term,
                                  __be32 *addr, __be16 *port,
                                  char **start, char **end)
 {
+       char *s, c;
        unsigned char p[6];
        int i = 0;
 
@@ -101,19 +104,38 @@ static int ip_vs_ftp_get_addrport(char *data, char *data_limit,
        if (strnicmp(data, pattern, plen) != 0) {
                return 0;
        }
-       *start = data + plen;
+       s = data + plen;
+       if (skip) {
+               int found = 0;
+
+               for (;; s++) {
+                       if (s == data_limit)
+                               return -1;
+                       if (!found) {
+                               if (*s == skip)
+                                       found = 1;
+                       } else if (*s != skip) {
+                               break;
+                       }
+               }
+       }
 
-       for (data = *start; *data != term; data++) {
+       for (data = s; ; data++) {
                if (data == data_limit)
                        return -1;
+               if (*data == term)
+                       break;
        }
        *end = data;
 
        memset(p, 0, sizeof(p));
-       for (data = *start; data != *end; data++) {
-               if (*data >= '0' && *data <= '9') {
-                       p[i] = p[i]*10 + *data - '0';
-               } else if (*data == ',' && i < 5) {
+       for (data = s; ; data++) {
+               c = *data;
+               if (c == term)
+                       break;
+               if (c >= '0' && c <= '9') {
+                       p[i] = p[i]*10 + c - '0';
+               } else if (c == ',' && i < 5) {
                        i++;
                } else {
                        /* unexpected character */
@@ -124,8 +146,9 @@ static int ip_vs_ftp_get_addrport(char *data, char *data_limit,
        if (i != 5)
                return -1;
 
-       *addr = get_unaligned((__be32 *)p);
-       *port = get_unaligned((__be16 *)(p + 4));
+       *start = s;
+       *addr = get_unaligned((__be32 *) p);
+       *port = get_unaligned((__be16 *) (p + 4));
        return 1;
 }
 
@@ -185,7 +208,8 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp,
 
                if (ip_vs_ftp_get_addrport(data, data_limit,
                                           SERVER_STRING,
-                                          sizeof(SERVER_STRING)-1, ')',
+                                          sizeof(SERVER_STRING)-1,
+                                          '(', ')',
                                           &from.ip, &port,
                                           &start, &end) != 1)
                        return 1;
@@ -345,7 +369,7 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp,
         */
        if (ip_vs_ftp_get_addrport(data_start, data_limit,
                                   CLIENT_STRING, sizeof(CLIENT_STRING)-1,
-                                  '\r', &to.ip, &port,
+                                  ' ', '\r', &to.ip, &port,
                                   &start, &end) != 1)
                return 1;