Added IPv6 support
authorMatthew Mondor <mmondor@pulsar-zone.net>
Fri, 11 Jan 2008 19:08:49 +0000 (19:08 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Fri, 11 Jan 2008 19:08:49 +0000 (19:08 +0000)
mmsoftware/mmftpd/ChangeLog
mmsoftware/mmftpd/GNUmakefile
mmsoftware/mmftpd/src/mmftpd.8
mmsoftware/mmftpd/src/mmftpd.c
mmsoftware/mmftpd/src/mmftpd.conf.5
mmsoftware/mmftpd/src/mmftpd.h
mmsoftware/mmftpd/src/mmftpdpasswd.5

index 2b7f72c..9e234a6 100644 (file)
@@ -1,4 +1,17 @@
-$Id: ChangeLog,v 1.53 2007/03/19 08:58:49 mmondor Exp $
+$Id: ChangeLog,v 1.54 2008/01/11 19:08:39 mmondor Exp $
+
+
+
+Release: mmftpd 0.3.0 devl
+Date   : Jan, 2008
+By     : Matthew Mondor
+
+* Added IPv6 support
+  - Can bind to IPv4 and IPv6 addresses/interfaces, will only accept v4
+    connections to v4 sockets and v6 connections to v6 sockets.
+  - Remapping system now uses | separator instead of : so that IPv6
+    addresses can be used (which contain :, also serving as
+    differenciation).
 
 
 
index 5e3ff6e..4b76d8c 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: GNUmakefile,v 1.7 2007/03/13 20:28:22 mmondor Exp $
+# $Id: GNUmakefile,v 1.8 2008/01/11 19:08:39 mmondor Exp $
 
 MMLIBS := $(addprefix ../mmlib/,mmarch.o mmfd.o mmhash.o mmlimitrate.o \
 mmlog.o mmpath.o mmpool.o mmreadcfg.o mmserver.o mmstat.o \
@@ -23,7 +23,8 @@ src/mmftpd: $(MMLIBS) $(OBJS)
        $(CC) -o $@ $(LDFLAGS) $(OBJS) $(MMLIBS)
 
 install:
-       install -cs -o 0 -g 0 -m 500 src/mmftpd /usr/local/sbin
+#      install -cs -o 0 -g 0 -m 500 src/mmftpd /usr/local/sbin
+       install -c -o 0 -g 0 -m 500 src/mmftpd /usr/local/sbin
        install -c -o 0 -g 0 -m 444 src/mmftpd.8 /usr/local/man/man8
        install -c -o 0 -g 0 -m 444 src/mmftpd.conf.5 /usr/local/man/man5
        install -c -o 0 -g 0 -m 444 src/mmftpdpasswd.5 /usr/local/man/man5
index bddef5a..39ff000 100644 (file)
@@ -1,4 +1,4 @@
-.\" $Id: mmftpd.8,v 1.7 2004/05/05 23:59:55 mmondor Exp $
+.\" $Id: mmftpd.8,v 1.8 2008/01/11 19:08:49 mmondor Exp $
 .\"
 .\" Copyright (C) 2001-2004, Matthew Mondor
 .\" All rights reserved.
@@ -137,8 +137,7 @@ alternate
 .Sy EPSV , LPSV , EPRT
 and
 .Sy LPRT
-extentions, although only support for IPv4 is currently available, and the
-permissions manipulation commands
+extentions, IPv4 and IPv6, and the permissions manipulation commands
 .Sy ( CHMOD , UMASK )
 under the
 .Sy SITE
@@ -433,4 +432,4 @@ prior to release.
 .Xr setgroups 2 ,
 .Xr chroot 2 .
 .Sh BUGS
-Please report any bug to mmondor@accela.net
+Please report any bug to mmsoftware@pulsar-zone.net
index 1821636..424587e 100644 (file)
@@ -1,7 +1,7 @@
-/* $Id: mmftpd.c,v 1.70 2007/12/05 23:47:55 mmondor Exp $ */
+/* $Id: mmftpd.c,v 1.71 2008/01/11 19:08:49 mmondor Exp $ */
 
 /*
- * Copyright (C) 2001-2004, Matthew Mondor
+ * Copyright (C) 2001-2008, Matthew Mondor
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -18,6 +18,8 @@
  * 4. The name of Matthew Mondor may not be used to endorse or promote
  *    products derived from this software without specific prior written
  *    permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ *    any GNU Public License derivate.
  *
  * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -90,7 +92,7 @@
 
 MMCOPYRIGHT("@(#) Copyright (c) 2001-2004\n\
 \tMatthew Mondor. All rights reserved.\n");
-MMRCSID("$Id: mmftpd.c,v 1.70 2007/12/05 23:47:55 mmondor Exp $");
+MMRCSID("$Id: mmftpd.c,v 1.71 2008/01/11 19:08:49 mmondor Exp $");
 
 
 
@@ -789,34 +791,36 @@ main_cdup(clientenv *clenv)
 static int
 main_port(clientenv *clenv)
 {
-    int i;
     uint16_t port;
     fdbuf *fdb = clenv->fdb;
-    char *args[7], ipaddr[16];
-    uint8_t p[7];
+    char *args[7];
+    uint8_t p[2];
     transfermsg *msg = &(clenv->tmsg);
 
     /* PORT 127,0,0,1,255,26 */
 
+    if (clenv->advaddr_s == NULL) {
+       reply(fdb, 500, FALSE, "Protocol requires LPRT or EPRT");
+       REGISTER_ERROR();
+       return STATE_CURRENT;
+    }
+
     /* First make sure that the command holds two space separated sections */
     if ((mm_straspl(args, clenv->buffer, 2)) == 2) {
        /* Now make sure that the second section holds 6 values */
        if ((mm_strspl(args, args[1], 6, ',')) == 6) {
-           for (i = 0; i < 6; i++)
-               p[i] = (uint8_t)atoi(args[i]);
            /* Get port number */
-           port = BYTEORDER_HOST16(p[4] | (p[5] << 8));
-           /* port = (256 * p[4]) + p[5]; */
-           /* Get address */
-           sprintf(ipaddr, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
+           p[0] = (uint8_t)atoi(args[4]);
+           p[1] = (uint8_t)atoi(args[5]);
+           port = BYTEORDER_HOST16(p[0] | (p[1] << 8));
+           /* port = (256 * p[0]) + p[1]; */
            /* Perform sanity check */
-           if ((port > 1023) && (!mm_strcmp(ipaddr, clenv->c_ipaddr))) {
+           if (port > 1023) {
                /* Everything seems valid make sure no transfers in progress */
                if (transfer_request(REQ_STATUS, TRUE, clenv)) {
                    if (!msg->ongoing) {
                        /* Send our PORT transfer request */
                        msg->port = port;
-                       msg->ipaddr = ipaddr;
                        msg->passive = FALSE;
                        if (transfer_request(REQ_NEWPORT, TRUE, clenv))
                            reply(fdb, 200, FALSE, "PORT command successful");
@@ -836,8 +840,8 @@ main_port(clientenv *clenv)
                    DEBUG_PRINTF("main_port", "transfer_reqiest(REQ_STATUS)");
                }
            } else {
-               mmsyslog(0, LOGLEVEL, "%08X Illegal port %s:%u",
-                       clenv->id, ipaddr, port);
+               mmsyslog(0, LOGLEVEL, "%08X Illegal port %u",
+                       clenv->id, port);
                reply(fdb, 500, FALSE, "Illegal port or address");
                REGISTER_PORT();
                REGISTER_ERROR();
@@ -858,66 +862,67 @@ main_port(clientenv *clenv)
 static int
 main_lprt(clientenv *clenv)
 {
+    bool ok;
     int i;
     uint16_t port;
     fdbuf *fdb = clenv->fdb;
-    char *args[10], ipaddr[16];
-    uint8_t p[10];
+    char *args[20];
+    uint8_t p[2];
     transfermsg *msg = &(clenv->tmsg);
 
     /* LPRT 4,4,127,0,0,1,2,204,36 */
+    /* LPRT 6,16,...,2,204,36 */
 
     /* First make sure that the command holds two space separated sections */
     if ((mm_straspl(args, clenv->buffer, 2)) == 2) {
-       /* Now make sure that the second section holds 9 values */
-       if ((mm_strspl(args, args[1], 9, ',')) == 9) {
-           for (i = 0; i < 9; i++)
-               p[i] = (uint8_t)atoi(args[i]);
-           if (p[0] == 4 && p[1] == 4 && p[6] == 2) {
-               /* Get port number */
-               port = BYTEORDER_HOST16(p[7] | (p[8] << 8));
-               /* port = (256 * p[7]) + p[8]; */
-               /* Get address */
-               sprintf(ipaddr, "%u.%u.%u.%u", p[2], p[3], p[4], p[5]);
-               /* Perform sanity check */
-               if ((port > 1023) && (!mm_strcmp(ipaddr, clenv->c_ipaddr))) {
-                   /* Everything seems valid make sure no transfers in
-                    * progress
-                    */
-                   if (transfer_request(REQ_STATUS, TRUE, clenv)) {
-                       if (!msg->ongoing) {
-                           /* Send our PORT transfer request */
-                           msg->port = port;
-                           msg->ipaddr = ipaddr;
-                           msg->passive = FALSE;
-                           if (transfer_request(REQ_NEWPORT, TRUE, clenv))
-                               reply(fdb, 200, FALSE,
-                                       "LPRT command successful");
-                           else {
-                               reply(fdb, 500, FALSE, "Error");
-                               REGISTER_ERROR();
-                               DEBUG_PRINTF("main_lprt",
-                                       "transfer_request(REQ_NEWPORT)");
-                           }
-                       } else {
-                           reply(fdb, 500, FALSE, "Transfer ongoing");
+       /* Get port number */
+       ok = FALSE;
+       if ((i = mm_strspl(args, args[1], 21, ',')) == 9) {
+           ok = TRUE;
+           p[0] = (uint8_t)atoi(args[7]);
+           p[1] = (uint8_t)atoi(args[8]);
+       } else if (i == 21) {
+           ok = TRUE;
+           p[0] = (uint8_t)atoi(args[19]);
+           p[1] = (uint8_t)atoi(args[20]);
+       }
+       if (ok) {
+           port = BYTEORDER_HOST16(p[0] | (p[1] << 8));
+           /* port = (256 * p[0]) + p[1]; */
+           /* Perform sanity check */
+           if (port > 1023) {
+               /* Everything seems valid make sure no transfers in
+                * progress
+                */
+               if (transfer_request(REQ_STATUS, TRUE, clenv)) {
+                   if (!msg->ongoing) {
+                       /* Send our PORT transfer request */
+                       msg->port = port;
+                       msg->passive = FALSE;
+                       if (transfer_request(REQ_NEWPORT, TRUE, clenv))
+                           reply(fdb, 200, FALSE,
+                                   "LPRT command successful");
+                       else {
+                           reply(fdb, 500, FALSE, "Error");
                            REGISTER_ERROR();
+                           DEBUG_PRINTF("main_lprt",
+                                   "transfer_request(REQ_NEWPORT)");
                        }
                    } else {
-                       reply(fdb, 500, FALSE, "Error");
+                       reply(fdb, 500, FALSE, "Transfer ongoing");
                        REGISTER_ERROR();
-                       DEBUG_PRINTF("main_lptr",
-                               "transfer_request(REQ_STATUS)");
                    }
                } else {
-                   mmsyslog(0, LOGLEVEL, "%08X Illegal port %s:%u",
-                           clenv->id, ipaddr, port);
-                   reply(fdb, 500, FALSE, "Illegal port or address");
-                   REGISTER_PORT();
+                   reply(fdb, 500, FALSE, "Error");
                    REGISTER_ERROR();
+                   DEBUG_PRINTF("main_lptr",
+                           "transfer_request(REQ_STATUS)");
                }
            } else {
-               reply(fdb, 522, FALSE, "Unimplemented protocol");
+               mmsyslog(0, LOGLEVEL, "%08X Illegal port %u",
+                       clenv->id, port);
+               reply(fdb, 500, FALSE, "Illegal port or address");
+               REGISTER_PORT();
                REGISTER_ERROR();
            }
        } else {
@@ -936,23 +941,24 @@ main_lprt(clientenv *clenv)
 static int
 main_eprt(clientenv *clenv)
 {
-    int port;
+    int port, i;
     fdbuf *fdb = clenv->fdb;
-    char *args[5], ipaddr[16];
+    char *args[5];
     transfermsg *msg = &(clenv->tmsg);
 
     /* EPRT |1|127.0.0.1|32035| */
+    /* EPRT |2|::1|32035| */
 
     /* First make sure that the command holds two space separated sections */
     if ((mm_straspl(args, clenv->buffer, 2)) == 2) {
        /* Now make sure that the second section holds 4 values */
        if ((mm_strspl(args, args[1], 4, '|')) == 4) {
-           if (atoi(args[1]) == 1) {
-               /* Get port number, address is args[2] */
+           i = atoi(args[1]);
+           if (i == 1 || i == 2) {
+               /* Get port number */
                port = atoi(args[3]);
                /* Perform sanity check */
-               if ((port > 1023 && port < 65536)
-                       && (!mm_strcmp(args[2], clenv->c_ipaddr))) {
+               if (port > 1023 && port < 65536) {
                    /* Everything seems valid, make sure no transfers in
                     * progress
                     */
@@ -960,7 +966,6 @@ main_eprt(clientenv *clenv)
                        if (!msg->ongoing) {
                            /* Send our PORT transfer request */
                            msg->port = (uint16_t)port;
-                           msg->ipaddr = args[2];
                            msg->passive = FALSE;
                            if (transfer_request(REQ_NEWPORT, TRUE, clenv))
                                reply(fdb, 200, FALSE,
@@ -982,8 +987,8 @@ main_eprt(clientenv *clenv)
                                "transfer_request(REQ_STATUS)");
                    }
                } else {
-                   mmsyslog(0, LOGLEVEL, "%08X Illegal port %s:%d",
-                           clenv->id, ipaddr, port);
+                   mmsyslog(0, LOGLEVEL, "%08X Illegal port %d",
+                           clenv->id, port);
                    reply(fdb, 500, FALSE, "Illegal port or address");
                    REGISTER_PORT();
                    REGISTER_ERROR();
@@ -1012,6 +1017,12 @@ main_pasv(clientenv *clenv)
     transfermsg *msg = &(clenv->tmsg);
     uint8_t a, b;
 
+    if (clenv->advaddr_s == NULL) {
+       reply(fdb, 500, FALSE, "Protocol requires LPSV or EPSV");
+       REGISTER_ERROR();
+       return STATE_CURRENT;
+    }
+
     if (!clenv->buffer[4]) {
        /* Make sure no transfers are in progress */
        if (transfer_request(REQ_STATUS, TRUE, clenv)) {
@@ -1029,7 +1040,7 @@ main_pasv(clientenv *clenv)
                    */
                    reply(fdb, 227, FALSE,
                                "Entering passive mode (%s,%u,%u)",
-                               clenv->sipaddr, a, b);
+                               clenv->advaddr_s, a, b);
                } else {
                    reply(fdb, 500, FALSE, "Error");
                    REGISTER_ERROR();
@@ -1077,8 +1088,8 @@ main_lpsv(clientenv *clenv)
                    b = msg->port % 256;
                    */
                    reply(fdb, 228, FALSE,
-                               "Entering long passive mode (4,4,%s,2,%u,%u)",
-                               clenv->sipaddr, a, b);
+                               "Entering long passive mode (%s,2,%u,%u)",
+                               clenv->advaddr_l, a, b);
                } else {
                    reply(fdb, 500, FALSE, "Error");
                    REGISTER_ERROR();
@@ -2494,6 +2505,8 @@ main(int argc, char **argv)
        printf("\nPASV_RANGE_MAX should be higher than PASV_RANGE_MIN!\n\n");
        exit(EXIT_FAILURE);
     }
+    if (!server_init_afi())
+       exit(EXIT_FAILURE);
     if (*CONF.PASV_REMAP != '\0') {
        if (!pasv_remap_parse())
            exit(EXIT_FAILURE);
@@ -3268,37 +3281,47 @@ best_match(const char *str, const char *pat)
 }
 
 
-/* Returns TRUE if supplied IP address string is valid, FALSE otherwise */
-static bool
-valid_ipaddress(const char *addr)
+/*
+ * On success, fills specified string <buf> with the LPRT/LPSV AF-specific
+ * string, excluding the port number, and fills the specified <shrt> pointer
+ * to pont to the PRT/PSV string for IPv4, or NULL for IPv6, returning the
+ * number of characters written to <buf>.
+ * On error, returns -1 (buffer too small or unsupported address family).
+ */
+static ssize_t
+foobar_address(char *buf, size_t bufsize, char **shrt,
+       struct server_sockaddr *saddr)
 {
-    char unit[5], *uptr, *utptr;
-    int units;
+    char       *cptr;
+    uint8_t    *bptr, *tbptr;
 
-    for (units = 0, uptr = unit, utptr = unit + 4; uptr < utptr; addr++) {
-       if (*addr == '\0' || *addr == '.') {
-           if (uptr > unit && units < 4) {
-               register int n;
+    if (bufsize < 6)
+       return -1;
 
-               *uptr = '\0';
-               n = atoi(unit);
-               if (n < 0 || n > 255)
-                   break;
-               uptr = unit;
-               units++;
-           } else
-               return FALSE;
-           if (*addr == '\0')
-               break;
-       } else if (isdigit((int)*addr))
-           *uptr++ = *addr;
-       else
-           return FALSE;
+    cptr = buf;
+    switch (*(SERVER_SOCKADDR_FAMILY(saddr))) {
+       case AF_INET6:
+           cptr += sprintf(cptr, "6,16,");
+           *shrt = NULL;
+           break;
+       case AF_INET:
+           cptr += sprintf(cptr, "4,4,");
+           *shrt = cptr;
+           break;
+       default:
+           return -1;
     }
-    if (!(units == 4 && *addr == '\0'))
-       return FALSE;
 
-    return TRUE;
+    for (bptr = SERVER_SOCKADDR_ADDRESS(saddr),
+           tbptr = &bptr[SERVER_SOCKADDR_ADDRLEN(saddr)];
+           bptr < tbptr; bptr++) {
+       if (cptr - buf + 5 > bufsize)
+           return -1;
+       cptr += sprintf(cptr, "%u,", (unsigned int)*bptr);
+    }
+    *(--cptr) = '\0';
+
+    return (cptr - buf);
 }
 
 
@@ -3309,9 +3332,8 @@ valid_ipaddress(const char *addr)
 static bool
 pasv_remap_parse(void)
 {
-    char               *cols[64], *cols2[4], *ptr;
+    char               *cols[64], *cols2[4];
     int                        i, entries;
-    struct sockaddr_in inaddr;
 
     if ((pasv_map_string = _mm_strdup(CONF.PASV_REMAP)) == NULL) {
        (void) fprintf(stderr, "pasv_remap_parse() - mm_strdup()\n");
@@ -3322,74 +3344,99 @@ pasv_remap_parse(void)
      * pasv_remap array of necessary size
      */
     if ((entries = mm_straspl(cols, pasv_map_string, 63)) < 1) {
-       free(pasv_map_string);
        (void) fprintf(stderr,
               "No entries in PASV_REMAP although string was not empty\n");
-       return FALSE;
+       goto err;
     }
     if ((pasv_map = malloc(sizeof(struct pasv_remap) * entries)) == NULL) {
-       free(pasv_map_string);
        (void) fprintf(stderr, "pasv_remap_parse() - malloc()\n");
-       return FALSE;
+       goto err;
     }
+    for (i = 0; i < entries; i++)
+       pasv_map[i].advertize_l = NULL;
     pasv_map_size = entries;
 
     /* Now parse each entry, verifying validity and filling them */
     for (i = 0; i < entries; i++) {
-       if (mm_strspl(cols2, cols[i], 3, ':') != 3) {
-           free(pasv_map);
-           free(pasv_map_string);
+       if (mm_strspl(cols2, cols[i], 3, '|') != 3) {
            (void) fprintf(stderr,
-                          "All PASV_REMAP entries must have three components"
-                          " separated by colons (':')\n");
-           return FALSE;
+                   "All PASV_REMAP entries must have three components"
+                   " separated by vertical separators ('|')\n");
+           goto err;
        }
-       if (!valid_ipaddress(cols2[1]) || !valid_ipaddress(cols2[2])) {
-           free(pasv_map);
-           free(pasv_map_string);
+
+       pasv_map[i].bind.ss_family = ((mm_strchr(cols2[1], ':') != NULL ||
+                   mm_strchr(cols2[2], ':') != NULL) ?  AF_INET6 : AF_INET);
+       if (inet_pton(*(SERVER_SOCKADDR_FAMILY(&pasv_map[i].bind)),
+                   cols2[2], SERVER_SOCKADDR_ADDRESS(&pasv_map[i].bind))
+               != 1) {
            (void) fprintf(stderr,
-                          "all PASV_REMAP entries must have valid IPv4 "
-                          "addresses for both second and third entries\n");
-           return FALSE;
+                   "Invalid address [%s] in field 3 of entry %d of "
+                   "PASV_REMAP\n", cols2[2], i + 1);
+           goto err;
        }
-       /* Fill apparently valid entry */
+       if (inet_pton(*(SERVER_SOCKADDR_FAMILY(&pasv_map[i].bind)),
+                   cols2[1], SERVER_SOCKADDR_ADDRESS(&pasv_map[i].bind))
+               != 1) {
+           (void) fprintf(stderr,
+                   "Invalid address [%s] in field 2 of entry %d of "
+                   "PASV_REMAP\n", cols2[1], i + 1);
+           goto err;
+       }
+
        pasv_map[i].pattern = cols2[0];
-       for (ptr = cols2[2]; *ptr != '\0'; ptr++) {
-           if (*ptr == '.')
-               *ptr = ',';
+
+       if ((pasv_map[i].advertize_l = malloc(256)) == NULL) {
+           (void) fprintf(stderr, "malloc(256)\n");
+           goto err;
        }
-       pasv_map[i].advertize = cols2[2];
-       if (inet_pton(AF_INET, cols2[1], &inaddr.sin_addr) != 1) {
-           free(pasv_map);
-           free(pasv_map_string);
-           (void) fprintf(stderr,
-                          "Apparently unvalid address '%s' in PASV_REMAP\n",
-                          cols2[1]);
-           return FALSE;
+       if (foobar_address(pasv_map[i].advertize_l, 255,
+                   &pasv_map[i].advertize_s, &pasv_map[i].bind) == -1) {
+           (void) fprintf(stderr, "foobar_address()\n");
+           goto err;
        }
-       pasv_map[i].bind = inaddr.sin_addr.s_addr;
     }
 
     return TRUE;
+
+err:
+    if (pasv_map != NULL) {
+       for (i = 0; i < pasv_map_size; i++) {
+           if (pasv_map[i].advertize_l != NULL)
+               free(pasv_map[i].advertize_l);
+       }
+       free(pasv_map);
+       pasv_map = NULL;
+    }
+    if (pasv_map_string != NULL) {
+       free(pasv_map_string);
+       pasv_map_string = NULL;
+    }
+
+    return FALSE;
 }
 
 
 /* Allows to remapping of client address pattern to bind/advertize address
  * pair using a best-matching algorithm. Returns TRUE if the entry was
- * remapped, or FALSE otherwise. <bind> and <advertize> are set if remapped.
+ * remapped, or FALSE otherwise. <bind> and <advertize_l>. <advertize_s> are
+ * set if remapped.
  */
 static bool
-pasv_remap(uint32_t *bind, char **advertize, const char *client)
+pasv_remap(struct server_sockaddr *bind, char **advertize_l,
+       char **advertize_s, const char *client)
 {
-    int                        i, cur, max;
+    int                        i, cur, max, af;
     struct pasv_remap  *entry = NULL;
 
-    /* Run through patterns to find the best matching entry. Because the
-     * number of entries should be rather small in general, we do not need to
-     * perform any verification calling yield. If no patterns match, we'll
-     * return FALSE.
+    af = (mm_strchr(client, ':') != NULL ? AF_INET6 : AF_INET);
+
+    /* Run through patterns to find the best matching entry.
+     * If no patterns match, we'll return FALSE.
      */
     for (cur = 0, max = -1, i = 0; i < pasv_map_size; i++) {
+       if (pasv_map[i].bind.ss_family != af)
+           continue;
        if ((cur = best_match(client, pasv_map[i].pattern)) != -1) {
            if (cur > max) {
                max = cur;
@@ -3399,8 +3446,9 @@ pasv_remap(uint32_t *bind, char **advertize, const char *client)
     }
 
     if (entry != NULL) {
-       *bind = entry->bind;
-       *advertize = (char *)entry->advertize;
+       (void) mm_memcpy(bind, &entry->bind, sizeof(struct server_sockaddr));
+       *advertize_l = entry->advertize_l;
+       *advertize_s = entry->advertize_s;
        return TRUE;
     }
 
@@ -3419,11 +3467,10 @@ static int
 handleclient(unsigned long cid, int fd, clientlistnode *clientlnode,
        struct iface *iface, struct async_clenv *aclenv, void *tudata)
 {
-    char buffer[1024], ipaddr[20], sipaddr[20];
-    register char *tmp;
+    char buffer[1024], ipaddr[64], advaddr_l[256];
     int state, nstate, timeout, dstatus;
     clientenv *clenv = NULL;
-    struct sockaddr_in *sinaddr, sname;
+    struct server_sockaddr sname, *saddr;
     socklen_t slen;
     fdbuf *fdb;
     transfermsg *msg;
@@ -3437,9 +3484,10 @@ handleclient(unsigned long cid, int fd, clientlistnode *clientlnode,
     dstatus = MMS_RESOURCE_ERROR;
 
     /* Obtain IP address of client */
-    sinaddr = (struct sockaddr_in *)&clientlnode->client;
-    mm_strcpy(ipaddr, "0.0.0.0");
-    inet_ntop(AF_INET, &(sinaddr->sin_addr), ipaddr, 19);
+    saddr = &clientlnode->client;
+    if (inet_ntop(*(SERVER_SOCKADDR_FAMILY(saddr)),
+               SERVER_SOCKADDR_ADDRESS(saddr), ipaddr, 64) == NULL)
+       mm_strcpy(ipaddr, "0.0.0.0");
 
     if (clientlnode->hostname)
        /* Log user's IP and hostname */
@@ -3465,13 +3513,13 @@ handleclient(unsigned long cid, int fd, clientlistnode *clientlnode,
             * PASV/LPSV. We will still be performing client originator
             * address verification for accepting clients after listen(2)
             * for security.
-            * clenv->sipaddr consists of string to use as advertizing
+            * clenv->advaddr_* consist of strings to use as advertizing
             *                address for PASV and LPSV. Since EPSV can be
             *                used without needing to specify an address to
             *                connect to (wiser), we don't need any special
             *                treatment for it.
-            * clenv->uipaddr consists of 32-bit IP address to bind(2)
-            *                listening passive ports with.
+            * clenv->uipaddr consists of IP address to bind(2) listening
+            *                passive ports with (struct server_sockaddr).
             */
            remapped = FALSE;
            if (*CONF.PASV_REMAP != '\0') {
@@ -3480,26 +3528,26 @@ handleclient(unsigned long cid, int fd, clientlistnode *clientlnode,
                 * daemon launch time for efficiency. Find closest matching
                 * entry, if any.
                 */
-               remapped = pasv_remap(&clenv->uipaddr, &clenv->sipaddr,
-                       ipaddr);
+               remapped = pasv_remap(&clenv->uipaddr, &clenv->advaddr_l,
+                       &clenv->advaddr_s, ipaddr);
            }
            if (!remapped) {
-               /* Default case, and fallback for no mapping pattern:
-                * Obtain IP address of server to not have to do this
-                * everytime a PASV/LPSV command is issued. sipaddr is stored
-                * in n,n,n,n format. Set uipaddr as well for port to bind to.
+               /*
+                * Default case, no mapping.  Obtain IP address of server to
+                * not have to do this everytime and assign PASV/LPSV strings.
                 */
-               slen = sizeof(struct sockaddr_in);
-               mm_strcpy(sipaddr, "0.0.0.0");
-               clenv->sipaddr = sipaddr;
-               sname.sin_addr.s_addr = 0;
+               slen = sizeof(struct server_sockaddr);
                if (getsockname(fd, (struct sockaddr *)&sname, &slen) == 0) {
-                   inet_ntop(AF_INET, &(sname.sin_addr), sipaddr, 19);
-                   for (tmp = sipaddr; *tmp != '\0'; tmp++)
-                       if (*tmp == '.')
-                           *tmp = ',';
-                   clenv->uipaddr = sname.sin_addr.s_addr;
+                   (void) foobar_address(advaddr_l, 255, &clenv->advaddr_s,
+                           &sname);
+                   (void) mm_memcpy(&clenv->uipaddr, &sname,
+                           sizeof(struct server_sockaddr));
                }
+               else {
+                   syslog(LOG_NOTICE, "getsockaddr()");
+                   goto err;
+               }
+               clenv->advaddr_l = advaddr_l;
            }
 
            /* Open our reply port and start our file transfer thread */
@@ -3515,7 +3563,7 @@ handleclient(unsigned long cid, int fd, clientlistnode *clientlnode,
                clenv->timeout = timeout;
                clenv->c_hostname = clientlnode->hostname;
                clenv->c_ipaddr = ipaddr;
-               clenv->cipaddr = sinaddr->sin_addr.s_addr;
+               clenv->cipaddr = &clientlnode->client;
                clenv->id = cid;
                clenv->iface = iface;
                clenv->aclenv = aclenv;
@@ -3646,6 +3694,7 @@ handleclient(unsigned long cid, int fd, clientlistnode *clientlnode,
            } else
                DEBUG_PRINTF("handleclient", "pthread_port_init()");
 
+err:
            if (pthread_port_valid(&clenv->rport))
                pthread_port_destroy(&clenv->rport);
            if (pthread_ring_valid(&clenv->rring))
@@ -3716,38 +3765,40 @@ handleclient(unsigned long cid, int fd, clientlistnode *clientlnode,
 /* This function is useful for the transfer thread to bind passive ports,
  * which may be within a range. Returns TRUE on success, with socket and
  * port set into the specified pointers, or FALSE. It uses the supplied
- * clenv and sockaddr_in.
+ * clenv and server_sockaddr.
  */
 static bool
-pasv_bind(clientenv *clenv, struct sockaddr_in *server, int *sock, int *port)
+pasv_bind(clientenv *clenv, struct server_sockaddr *server, int *sock, int *port)
 {
     int fd;
 
-    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
+    if ((fd = socket(*(SERVER_SOCKADDR_FAMILY(&clenv->uipaddr)), SOCK_STREAM,
+                   0)) != -1) {
        int opt;
 
        opt = 1;
        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) == -1)
            DEBUG_PRINTF("pasv_bind", "setsockopt(SO_REUSEADDR)");
-       mm_memclr(server, sizeof(struct sockaddr_in));
-       server->sin_family = AF_INET;
-       server->sin_addr.s_addr = clenv->uipaddr;
+
+       (void) mm_memcpy(server, &clenv->uipaddr,
+               sizeof(struct server_sockaddr));
+
        if (!CONF.PASV_RANGE) {
            /* Just let the system assign us a free high port, which is very
             * fast and most likely to succeed.
             */
-           server->sin_port = 0;
-           if (bind(fd, (struct sockaddr *)server, sizeof(struct sockaddr_in))
-                   != -1) {
+           *(SERVER_SOCKADDR_PORT(server)) = 0;
+           if (bind(fd, (struct sockaddr *)server,
+                       SERVER_SOCKADDR_SOCKLEN(server)) != -1) {
                socklen_t len;
 
                /* Get port number we were able to bind */
-               len = sizeof(struct sockaddr_in);
+               len = sizeof(struct server_sockaddr);
                if (getsockname(fd, (struct sockaddr *)server,
                            (socklen_t *)&len) != -1) {
                    if (listen(fd, 0) == 0) {
                        *sock = fd;
-                       *port = ntohs(server->sin_port);
+                       *port = ntohs(*(SERVER_SOCKADDR_PORT(server)));
 
                        return TRUE;
                    } else
@@ -3775,16 +3826,19 @@ pasv_bind(clientenv *clenv, struct sockaddr_in *server, int *sock, int *port)
            maxp = range < 64 ? range : 64;
            for (err = -1, maxs = 0; err == -1 && maxs < 30; maxs++) {
                for (i = 0; i < maxp; i++) {
-                   server->sin_port = htons(p);
+                   *(SERVER_SOCKADDR_PORT(server)) = htons(p);
                    if ((err = bind(fd, (struct sockaddr *)server,
-                                   sizeof(struct sockaddr_in))) != -1)
+                                   SERVER_SOCKADDR_SOCKLEN(server))) != -1)
                        break;
                    p++;
                    if (p > CONF.PASV_RANGE_MAX)
                        p = CONF.PASV_RANGE_MIN;
                }
-               if (err == -1)
+               if (err == -1) {
+                   if (errno != EADDRINUSE)
+                       break;
                    pthread_sleep(1);
+               }
            }
            if (err != -1) {
                /* Successful bind(2) */
@@ -3915,10 +3969,10 @@ transferthread_main(clientenv *clenv, fdbuf *fdb)
 {
     int state = TTSTATE_INITIAL, l, reason, err;
     register char *from, *to;
-    char ipaddr[20], buffer[T_BUFSIZE], *from2;
+    char ipaddr[64], buffer[T_BUFSIZE], *from2;
     pthread_ring_t ring;
     transfermsg *msg;
-    struct sockaddr_in server, client, addr;
+    struct server_sockaddr server, client, addr;
     socklen_t addrl;
     bool cont;
 
@@ -4084,7 +4138,8 @@ transferthread_main(clientenv *clenv, fdbuf *fdb)
                     * message, ECANCELED is.  Other errror codes could be
                     * returned, or the new file descriptor.
                     */
-                   addrl = sizeof(struct sockaddr);
+                   addr.ss_family = clenv->cipaddr->ss_family;
+                   addrl = sizeof(struct server_sockaddr);
                    if ((err = pthread_accept_ring(fdpasv,
                                    (struct sockaddr *)&addr,
                                    &addrl, CONF.DATA_TIMEOUT * 1000,
@@ -4097,11 +4152,11 @@ transferthread_main(clientenv *clenv, fdbuf *fdb)
                    }
                } else {
                    /* Start connection to the client */
-                   if ((fd = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
-                       mm_memclr(&client, sizeof(struct sockaddr_in));
-                       client.sin_family = AF_INET;
-                       client.sin_addr.s_addr = clenv->cipaddr;
-                       client.sin_port = htons(port);
+                   if ((fd = socket(*(SERVER_SOCKADDR_FAMILY(clenv->cipaddr)),
+                                   SOCK_STREAM, 0)) != -1) {
+                       mm_memcpy(&client, clenv->cipaddr,
+                               sizeof(struct server_sockaddr));
+                       *(SERVER_SOCKADDR_PORT(&client)) = htons(port);
                        /*
                         * Finally attempt connection.
                         * On timeout, ETIMEDOUT is returned, on interrupting
@@ -4127,12 +4182,12 @@ transferthread_main(clientenv *clenv, fdbuf *fdb)
                         */
                        if ((pthread_connect_ring(fd,
                                        (struct sockaddr *)&client,
-                                       sizeof(struct sockaddr_in),
+                                       SERVER_SOCKADDR_SOCKLEN(&client),
                                        CONF.DATA_TIMEOUT * 1000,
                                        &ring)) != 0 && errno != ETIMEDOUT &&
                                        errno != ECANCELED) {
                            mmsyslog(1, LOGLEVEL,
-                                   "%08X PORT data connection failiure",
+                                   "%08X PORT data connection failure",
                                    clenv->id);
                            reply(clenv->fdb, 425, FALSE,
                                    "Can't establish data connection");
@@ -4212,15 +4267,18 @@ transferthread_main(clientenv *clenv, fdbuf *fdb)
                             * connected client and continue listening on this
                             * socket for the expected client.
                             */
-                           if (addr.sin_addr.s_addr == clenv->cipaddr)
+                           if (mm_memcmp(SERVER_SOCKADDR_ADDRESS(&addr),
+                                   SERVER_SOCKADDR_ADDRESS(clenv->cipaddr),
+                                   SERVER_SOCKADDR_ADDRLEN(&addr)) == 0)
                                state = TTSTATE_TRANSFER;
                            else {
                                (void) shutdown(fd, SHUT_RDWR);
                                (void) close(fd);
                                fd = -1;
                                *ipaddr = '\0';
-                               inet_ntop(AF_INET, &(addr.sin_addr), ipaddr,
-                                       19);
+                               inet_ntop(*(SERVER_SOCKADDR_FAMILY(&addr)),
+                                       SERVER_SOCKADDR_ADDRESS(&addr),
+                                       ipaddr, 63);
                                mmsyslog(0, LOGLEVEL,
                                         "%08X Failed passive data connection"
                                         "from invalid address (%s != %s)",
index f88bab9..c307bfb 100644 (file)
@@ -1,6 +1,6 @@
-.\" $Id: mmftpd.conf.5,v 1.10 2004/09/19 10:59:08 mmondor Exp $
+.\" $Id: mmftpd.conf.5,v 1.11 2008/01/11 19:08:49 mmondor Exp $
 .\"
-.\" Copyright (C) 2001-2004, Matthew Mondor
+.\" Copyright (C) 2001-2008, Matthew Mondor
 .\" All rights reserved.
 .\"
 .\" Redistribution and use in source and binary forms, with or without
@@ -17,6 +17,8 @@
 .\" 4. The name of Matthew Mondor may not be used to endorse or promote
 .\"    products derived from this software without specific prior written
 .\"    permission.
+.\" 5. Redistribution of source code may not be released under the terms of
+.\"    any GNU Public License derivate.
 .\"
 .\" THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
 .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -155,13 +157,14 @@ facility. Here consists of the allowable levels and their meaning:
 .Nm TCP server parameters
 .Pp
 .Bl -tag -width XXXXXXXX -offset indent -compact
-.It Nm LISTEN_IPS Ar "n.n.n.n ..."
-IPv4 addresses we should
+.It Nm LISTEN_IPS Ar "address ..."
+IPv4 and/or IPv6 addresses we should
 .Xr  bind 2
 to, separated by spaces. These typically consist of the addresses bound to
-a wanted interface to accept connections from. 0.0.0.0 may be used if
-connections from all interfaces should be allowed. Obviously, if more than
-a single address is specified, they should be enclosed in double quotes.
+a wanted interface to accept connections from. 0.0.0.0 may be used for
+IPv4, or :: for IPv6, if connections from all interfaces should be allowed.
+Obviously, if more than a single address is specified, they should be
+enclosed in double quotes.
 .Pp
 .It Nm SERVER_NAMES Ar "name ..."
 Advertized server hostnames, separated by spaces, there should be the same
@@ -283,9 +286,9 @@ and
 .Nm PASV_RANGE_MAX .
 .Pp
 Each remapping entry must be followed by a space, and each entry must be in
-the following format: <addresspattern>:<bindaddress>:<advertizeaddress>.
-An example would be: "127.0.0.1:127.0.0.1:127.0.0.1
-192.168.1.*:192.168.1.10:192.168.1.10 *:0.0.0.0:66.11.161.166". In this
+the following format: <addresspattern>|<bindaddress>|<advertizeaddress>.
+An example would be: "127.0.0.1|127.0.0.1|127.0.0.1
+192.168.1.*|192.168.1.10|192.168.1.10 *|0.0.0.0|66.11.161.166". In this
 example, this allows connections from localhost to have listening sockets
 bound to the localhost interface only, and to advertize to the client to
 connect to localhost, for connections from the local LAN (192.168.1.*) to
@@ -311,6 +314,10 @@ Note also that EPSV does not need this, since it being wiser supports an empty
 address to tell the client to connect at the same location it connected to
 open the FTP control connection, but many clients only support PASV or LPSV.
 .Pp
+Note that both IPv6 and IPv4 may be used here.  The patterns which contain a
+column character will be considered to be IPv6 and matching will be done
+separately for both IPv4 and IPv6 address families.  There thus could be two
+match-all rules which will not clash.
 .It Nm PASV_RANGE Ar "boolean"
 If
 .Nm PASV_RANGE
@@ -456,4 +463,4 @@ FTP server binary itself.
 Not really a bug, but tied to each interface could be most connection control
 options.
 .Pp
-Please report any bug to mmondor@accela.net
+Please report any bug to mmsoftware@pulsar-zone.net
index 5d3ac64..e09d115 100644 (file)
@@ -1,7 +1,7 @@
-/* $Id: mmftpd.h,v 1.29 2007/12/05 23:47:56 mmondor Exp $ */
+/* $Id: mmftpd.h,v 1.30 2008/01/11 19:08:49 mmondor Exp $ */
 
 /*
- * Copyright (C) 2001-2004, Matthew Mondor
+ * Copyright (C) 2001-2008, Matthew Mondor
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -18,6 +18,8 @@
  * 4. The name of Matthew Mondor may not be used to endorse or promote
  *    products derived from this software without specific prior written
  *    permission.
+ * 5. Redistribution of source code may not be released under the terms of
+ *    any GNU Public License derivate.
  *
  * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -67,7 +69,7 @@
 /* DEFINITIONS */
 
 #define DAEMON_NAME            "mmftpd"
-#define DAEMON_VERSION         "0.2.1/mmondor"
+#define DAEMON_VERSION         "0.3.0/mmondor"
 
 /* Transfer buffer size */
 #define T_BUFSIZE              16384
@@ -180,7 +182,6 @@ typedef struct transfermsg {
     uint16_t port;               /* PORT or PASV port for next transfer */
     int file;                    /* File descriptor to read/write from/to */
     int rrate, wrate;            /* Transfer dl/ul rate */
-    char *ipaddr;                /* If PORT, address to connect to */
     char *path;                          /* Path to be passed to ls() if list=TRUE */
     bool passive;                /* Either next xfer is PORT or PASV mode */
     bool ongoing;                /* TRUE when a transfer is in progress */
@@ -203,7 +204,7 @@ typedef struct clientenv {
     char *buffer;                /* Our command line buffer */
     char *c_hostname;            /* Pointer to client's hostname */
     char *c_ipaddr;              /* Pointer to client's IP address string */
-    char *sipaddr;               /* Ptr to IP addr for PASV/LPSV replies */
+    char *advaddr_l, *advaddr_s;  /* Ptrs to IP addr for LPASV/PSV replies */
     char *user;                          /* Username of logged user */
     char *tuser;                 /* Temporary username used by state_auth() */
     char *passwd;                /* Temporary, used by auth code */
@@ -232,8 +233,8 @@ typedef struct clientenv {
     bool checkowner;             /* If we should check for file ownership */
     unsigned char type;                  /* Current transfer TYPE */
     fifo64_t visited;             /* FIFO buffer of visited dirs hashes */
-    uint32_t uipaddr;            /* Used for bind() on passive ports */
-    uint32_t cipaddr;            /* Client's IP address */
+    struct server_sockaddr uipaddr;    /* Used for bind() on passive ports */
+    struct server_sockaddr *cipaddr;   /* Client's IP address */
     struct fifonode *fifobuf;    /* Internal buffer for visited */
     struct iface *iface;         /* To obtain our server hostname */
     struct async_clenv *aclenv;          /* Thread context for async_call() support */
@@ -348,8 +349,8 @@ struct async_getuserline_msg {
 
 struct pasv_remap {
     const char *pattern;
-    const char *advertize;
-    uint32_t   bind;
+    char *advertize_l, *advertize_s;
+    struct server_sockaddr bind;
 };
 
 
@@ -417,14 +418,16 @@ static bool transfer_request(int, bool, clientenv *);
 static bool file_out(fdbuf *, char *);
 static bool directory_message(clientenv *, int);
 static int best_match(const char *, const char *);
-static bool valid_ipaddress(const char *);
+static ssize_t foobar_address(char *, size_t, char **,
+       struct server_sockaddr *);
 static bool pasv_remap_parse(void);
-static bool pasv_remap(uint32_t *, char **, const char *);
+static bool pasv_remap(struct server_sockaddr *, char **, char **,
+       const char *);
 
 static int handleclient(unsigned long, int, clientlistnode *, struct iface *,
        struct async_clenv *, void *);
 
-static bool pasv_bind(clientenv *, struct sockaddr_in *, int *, int *);
+static bool pasv_bind(clientenv *, struct server_sockaddr *, int *, int *);
 static void transferthread(pthread_object_t *, void *, void *);
 static void transferthread_main(clientenv *, fdbuf *);
 
index 80ecfae..892a6dd 100644 (file)
@@ -1,4 +1,4 @@
-.\" $Id: mmftpdpasswd.5,v 1.5 2004/05/05 23:59:55 mmondor Exp $
+.\" $Id: mmftpdpasswd.5,v 1.6 2008/01/11 19:08:49 mmondor Exp $
 .\"
 .\" Copyright (C) 2001-2004, Matthew Mondor
 .\" All rights reserved.
@@ -215,4 +215,4 @@ FTP server binary itself.
 .Xr umask 2 ,
 .Xr chmod 2 .
 .Sh BUGS
-Please report any bug to mmondor@accela.net
+Please report any bug to mmsoftware@pulsar-zone.net