diff -ru wu-ftpd-2.6.2/README.AUTOCONF wu-ftpd-2.6.2hl/README.AUTOCONF
--- wu-ftpd-2.6.2/README.AUTOCONF	1999-10-01 17:04:13.000000000 +0200
+++ wu-ftpd-2.6.2hl/README.AUTOCONF	2002-03-28 02:09:44.000000000 +0100
@@ -51,6 +51,8 @@
 --disable-throughput	Don't keep track of user's throughput
 --disable-count		Don't keep track of transferred bytes (for
 			statistics)
+--disable-hostlimit	Disables support for the host-limit keyword in
+			the ftpaccess file
 --disable-newlines	Suppress some extra blank lines
 --enable-crackers	Don't wait for password entry if someone tries to
 			log in with a wrong username.
--- wu-ftpd-2.6.2/config.h.in	2001-11-29 18:10:57.000000000 +0100
+++ wu-ftpd-2.6.2hl/config.h.in	2002-03-28 02:09:44.000000000 +0100
@@ -211,6 +211,13 @@
 #undef TRANSFER_LIMIT
 
 /*
+ * HOST_LIMIT
+ * Limit number of simultaneous connections from a given host IP address.
+ */
+
+#undef HOST_LIMIT
+
+/*
  * NO_SUCKING_NEWLINES
  * Don't suppress some extra blank lines on messages and banners.
  */
diff -ru wu-ftpd-2.6.2/config.h.noac wu-ftpd-2.6.2hl/config.h.noac
--- wu-ftpd-2.6.2/config.h.noac	2001-11-29 18:10:58.000000000 +0100
+++ wu-ftpd-2.6.2hl/config.h.noac	2002-03-28 02:09:44.000000000 +0100
@@ -205,6 +205,13 @@
 #endif
 
 /*
+ * HOST_LIMIT
+ * Limit number of simultaneous connections from a given host IP address.
+ */
+
+#define HOST_LIMIT
+
+/*
  * NO_SUCKING_NEWLINES
  * Don't suppress some extra blank lines on messages and banners.
  */
--- wu-ftpd-2.6.2hl/configure.in	2001-11-29 18:10:58.000000000 +0100
+++ wu-ftpd-2.6.2hl-lf/configure.in	2004-05-04 16:36:55.349155000 +0200
@@ -162,6 +162,8 @@
 	[ throughput=$enableval ], [ throughput=yes ])
 AC_ARG_ENABLE(count,	 [  --disable-count         don't keep track of bytes for statistics],
 	[ count=$enableval ], [ count=yes ])
+AC_ARG_ENABLE(hostlimit, [  --disable-hostlimit     disable the HOST-LIMIT keyword in ftpaccess
+	[ hostlimit=$enableval ], [ hostlimit=yes ])
 AC_ARG_ENABLE(newlines,	 [  --disable-newlines      suppress some extra blank lines],
 	[ newlines=$enableval ], [ newlines=yes ])
 AC_ARG_ENABLE(crackers,  [  --enable-crackers       don't wait for password if the username doesn't exist],
@@ -900,6 +902,9 @@
 	AC_DEFINE(TRANSFER_COUNT)
 	AC_DEFINE(TRANSFER_LIMIT)
 fi
+if test $hostlimit = yes; then
+	AC_DEFINE(HOST_LIMIT)
+fi
 if test $newlines = no; then
 	AC_DEFINE(NO_SUCKING_NEWLINES)
 fi
diff -ru wu-ftpd-2.6.2/doc/ftpaccess.5 wu-ftpd-2.6.2hl/doc/ftpaccess.5
--- wu-ftpd-2.6.2/doc/ftpaccess.5	2000-07-01 19:49:09.000000000 +0200
+++ wu-ftpd-2.6.2hl/doc/ftpaccess.5	2002-03-28 02:09:44.000000000 +0100
@@ -256,6 +256,17 @@
 UUCP L.sys file.
 
 .TP 0.5i
+.B host-limit <class> <n> <times> <message_file>
+
+Limit <class> to <n> simultaneous connections per host IP address at
+times <times>, displaying <message_file> if the user is denied access.
+Limit check is performed at login time only.
+If multiple "host-limit" commands can apply to the current session, the first
+applicable one is used.  Failing to define a valid limit, or a limit of -1,
+is equivalent to unlimited. <times> is in same format as the times in the
+UUCP L.sys file.
+
+.TP 0.5i
 .B noretrieve [absolute|relative] [class=<classname>] ... [-] <filename> <filename> ...
 
 Always deny retrieve-ability of these files.  If the files are a path
diff -ru wu-ftpd-2.6.2/src/access.c wu-ftpd-2.6.2hl/src/access.c
--- wu-ftpd-2.6.2/src/access.c	2000-07-01 20:17:38.000000000 +0200
+++ wu-ftpd-2.6.2hl/src/access.c	2002-03-28 02:09:44.000000000 +0100
@@ -88,6 +88,9 @@
 #define MAXLINE	80
 static char incline[MAXLINE];
 int pidfd = -1;
+#ifdef HOST_LIMIT
+int ripfd = -1;
+#endif
 extern int Bypass_PID_Files;
 
 #ifndef HELP_CRACKERS
@@ -786,6 +789,39 @@
     return (-1);
 }
 
+#ifdef HOST_LIMIT
+/*************************************************************************/
+/* FUNCTION  : acl_gethostlimit                                          */
+/* PURPOSE   : Scan the ACL buffer and determine what limit applies to   */
+/*             the user                                                  */
+/* ARGUMENTS : pointer class name, pointer to ACL buffer                 */
+/*************************************************************************/
+
+int acl_gethostlimit(char *class, char *msgpathbuf)
+{
+    int limit;
+    struct aclmember *entry = NULL;
+
+    if (msgpathbuf)
+        *msgpathbuf = '\0';
+
+    /* host-limit <class> <n> <times> [<message_file>] */
+    while (getaclentry("host-limit", &entry)) {
+        if (!ARG0 || !ARG1 || !ARG2)
+            continue;
+        if (!strcasecmp(class, ARG0)) {
+            limit = atoi(ARG1);
+            if (validtime(ARG2)) {
+                if (ARG3 && msgpathbuf)
+                    strcpy(msgpathbuf, ARG3);
+                return (limit);
+            }
+        }
+    }
+    return (-1);
+}
+#endif
+
 /*************************************************************************/
 /* FUNCTION  : acl_getnice                                               */
 /* PURPOSE   : Scan the ACL buffer and determine what nice value applies */
@@ -1200,6 +1236,231 @@
     return (count);
 }
 
+#ifdef HOST_LIMIT
+/*************************************************************************/
+/* FUNCTION  : acl_counthosts                                            */
+/* PURPOSE   : Check the anonymous FTP access lists to see if this       */
+/*             access is permitted.                                      */
+/* ARGUMENTS : none                                                      */
+/*************************************************************************/
+
+int acl_counthosts(char *class)
+{
+    int count,
+      which;
+    char ripfile[MAXPATHLEN];
+    char buf[4*MAXUSERS];
+    int r[4];
+#ifndef HAVE_FLOCK
+struct flock arg;
+#endif
+
+    if (Bypass_PID_Files)
+        return (0);
+    /* 
+     * if ripfd was not opened previously... 
+     * ripfd must stay open after the chroot(~ftp)  
+     */
+
+    sprintf(ripfile, _PATH_RIPNAMES, class);
+
+    if (ripfd < 0) {
+        mode_t oldmask;
+        oldmask = umask(0);
+        ripfd = open(ripfile, O_RDWR | O_CREAT, 0644);
+        (void) umask(oldmask);
+    }
+
+    if (ripfd < 0) {
+        syslog(LOG_ERR, "cannot open pid file %s: %m", ripfile);
+        return -1;
+    }
+
+#ifdef HAVE_FLOCK
+    while (flock(ripfd, LOCK_EX)) {
+#ifndef NO_PID_SLEEP_MSGS
+        syslog(LOG_ERR, "sleeping: flock of pid file failed: %m");
+#endif
+#else 
+	arg.l_type = F_WRLCK;
+	arg.l_whence = arg.l_start = arg.l_len = 0;
+	while ( -1 == fcntl( ripfd, F_SETLK, &arg) ) {
+#ifndef NO_PID_SLEEP_MSGS
+		syslog(LOG_ERR, "sleeping: fcntl lock of pid file failed: %m");
+#endif
+#endif
+        sleep(1);
+    }
+    lseek(ripfd, (off_t)0, SEEK_SET);
+
+    sscanf(remoteaddr,"%d.%d.%d.%d",r,r+1,r+2,r+3);
+    count = 0;
+
+    if (read(ripfd, (void *)buf, sizeof(buf)) == sizeof(buf)) {
+        for (which = 0; which < 4*MAXUSERS; which+=4)
+            if ((buf[which] & 0xFF) == r[0]
+                && (buf[which+1] & 0xFF) == r[1]
+                && (buf[which+2] & 0xFF) == r[2]
+                && (buf[which+3] & 0xFF) == r[3])
+                count++;
+    }
+#ifdef HAVE_FLOCK
+    flock(ripfd, LOCK_UN);
+#else
+	arg.l_type = F_UNLCK; arg.l_whence = arg.l_start = arg.l_len = 0;
+	fcntl(ripfd, F_SETLK, &arg);
+#endif
+    return (count);
+}
+
+/*************************************************************************/
+/* FUNCTION  : acl_addhost                                               */
+/* PURPOSE   : Add the current host IP to the list of addresses in the   */
+/*             specified class.                                          */
+/* ARGUMENTS : The name of the class to join, the "slot" number          */
+/*************************************************************************/
+
+void acl_addhost(char *class, int avail)
+{
+    int which;
+    char buf[4*MAXUSERS];
+    int r[4];
+    char ripfile[MAXPATHLEN];
+#ifndef HAVE_FLOCK
+    struct flock arg;
+#endif
+
+    /* 
+     * if ripfd was not opened previously... 
+     * ripfd must stay open after the chroot(~ftp)  
+     */
+
+    sprintf(ripfile, _PATH_RIPNAMES, class);
+
+    if (ripfd < 0) {
+        mode_t oldmask;
+        oldmask = umask(0);
+        ripfd = open(ripfile, O_RDWR | O_CREAT, 0644);
+        (void) umask(oldmask);
+    }
+
+    if (ripfd < 0) {
+        syslog(LOG_ERR, "cannot open rip file %s: %m", ripfile);
+        return;
+    }
+
+#ifdef HAVE_FLOCK
+    while (flock(ripfd, LOCK_EX)) {
+#ifndef NO_PID_SLEEP_MSGS
+        syslog(LOG_ERR, "sleeping: flock of rip file failed: %m");
+#endif
+#else 
+    arg.l_type = F_WRLCK;
+    arg.l_whence = arg.l_start = arg.l_len = 0;
+    while ( -1 == fcntl( ripfd, F_SETLK, &arg) ) {
+#ifndef NO_PID_SLEEP_MSGS
+        syslog(LOG_ERR, "sleeping: fcntl lock of rip file failed: %m");
+#endif
+#endif
+        sleep(1);
+    }
+
+    lseek(ripfd, (off_t)0, SEEK_SET);
+    if (read(ripfd, (void *)buf, sizeof(buf)) < sizeof(buf))
+        for (which = 0; which < 4*MAXUSERS; buf[which++] = 0)
+            continue;
+
+    sscanf(remoteaddr,"%d.%d.%d.%d",r,r+1,r+2,r+3);
+    avail *= 4;
+    buf[avail] = r[0];
+    buf[avail+1] = r[1];
+    buf[avail+2] = r[2];
+    buf[avail+3] = r[3];
+
+    lseek(ripfd, (off_t)0, SEEK_SET);
+    write(ripfd, (void *)buf, sizeof(buf));
+#ifdef HAVE_FLOCK
+    flock(ripfd, LOCK_UN);
+#else
+    arg.l_type = F_UNLCK; arg.l_whence = arg.l_start = arg.l_len = 0;
+    fcntl(ripfd, F_SETLK, &arg);
+#endif
+
+}
+
+/*************************************************************************/
+/* FUNCTION  : acl_remhost                                               */
+/* PURPOSE   : Remove the current host IP from the list of addresses in  */
+/*             the specified class.                                      */
+/* ARGUMENTS : The name of the class to remove, the "slot" number        */
+/*************************************************************************/
+
+void acl_remhost(char *class, int avail)
+{
+    int which;
+    char buf[4*MAXUSERS];
+    char ripfile[MAXPATHLEN];
+#ifndef HAVE_FLOCK
+    struct flock arg;
+#endif
+
+    /* 
+     * if ripfd was not opened previously... 
+     * ripfd must stay open after the chroot(~ftp)  
+     */
+
+    sprintf(ripfile, _PATH_RIPNAMES, class);
+
+    if (ripfd < 0) {
+        mode_t oldmask;
+        oldmask = umask(0);
+        ripfd = open(ripfile, O_RDWR | O_CREAT, 0644);
+        (void) umask(oldmask);
+    }
+
+    if (ripfd < 0) {
+        syslog(LOG_ERR, "cannot open rip file %s: %m", ripfile);
+        return;
+    }
+
+#ifdef HAVE_FLOCK
+    while (flock(ripfd, LOCK_EX)) {
+#ifndef NO_PID_SLEEP_MSGS
+        syslog(LOG_ERR, "sleeping: flock of rip file failed: %m");
+#endif
+#else 
+    arg.l_type = F_WRLCK;
+    arg.l_whence = arg.l_start = arg.l_len = 0;
+    while ( -1 == fcntl( ripfd, F_SETLK, &arg) ) {
+#ifndef NO_PID_SLEEP_MSGS
+        syslog(LOG_ERR, "sleeping: fcntl lock of rip file failed: %m");
+#endif
+#endif
+        sleep(1);
+    }
+
+    lseek(ripfd, (off_t)0, SEEK_SET);
+    if (read(ripfd, (void *)buf, sizeof(buf)) < sizeof(buf))
+        for (which = 0; which < 4*MAXUSERS; buf[which++] = 0)
+            continue;
+
+    avail *= 4;
+    buf[avail] = buf[avail+1] = buf[avail+2] = buf[avail+3] = 0;
+
+    lseek(ripfd, (off_t)0, SEEK_SET);
+    write(ripfd, (void *)buf, sizeof(buf));
+#ifdef HAVE_FLOCK
+    flock(ripfd, LOCK_UN);
+#else
+    arg.l_type = F_UNLCK; arg.l_whence = arg.l_start = arg.l_len = 0;
+    fcntl(ripfd, F_SETLK, &arg);
+#endif
+
+    close(ripfd);
+    ripfd = -1;
+}
+#endif
+
 /*************************************************************************/
 /* FUNCTION  : acl_join                                                  */
 /* PURPOSE   : Add the current process to the list of processes in the   */
@@ -1292,6 +1553,9 @@
     arg.l_whence = arg.l_start = arg.l_len = 0;
     fcntl(pidfd, F_SETLK, &arg);
 #endif
+#ifdef HOST_LIMIT
+    acl_addhost(class, avail);
+#endif
 
 }
 
@@ -1370,6 +1634,9 @@
 	}
 	else if (buf[which] == procid) {
 	    buf[which] = 0;
+#ifdef HOST_LIMIT
+	    acl_remhost(class, which);
+#endif
 	}
     }
 
@@ -1509,6 +1776,23 @@
 #endif
 #endif
 #endif
+
+#ifdef HOST_LIMIT
+    limit = acl_gethostlimit(class, msgfile);
+    if ((limit != -1) && (acl_counthosts(class) >= limit)) {
+#ifdef LOG_TOOMANY
+        syslog(LOG_NOTICE, "ACCESS DENIED (host limit %d; class %s) TO %s",
+               limit, class, remoteident);
+#endif
+#ifndef HELP_CRACKERS
+        memcpy (DelayedMessageFile, msgfile, sizeof (msgfile));
+#else
+        pr_mesg(msgcode, msgfile);
+#endif
+        return (-1);
+    }
+#endif
+
     /* if no limits defined, no limits apply -- access OK */
     limit = acl_getlimit(class, msgfile);
 
diff -ru wu-ftpd-2.6.2/src/pathnames.h.in wu-ftpd-2.6.2hl/src/pathnames.h.in
--- wu-ftpd-2.6.2/src/pathnames.h.in	2000-07-01 20:04:21.000000000 +0200
+++ wu-ftpd-2.6.2hl/src/pathnames.h.in	2002-03-28 02:09:44.000000000 +0100
@@ -63,6 +63,7 @@
    **  _PATH_FTPSERVERS
    **  _PATH_EXECPATH
    **  _PATH_PIDNAMES
+   **  _PATH_RlPNAMES
    **  _PATH_UTMP
    **  _PATH_WTMP
    **  _PATH_LASTLOG
@@ -97,6 +98,7 @@
 /* _PATH_FTPD_PIDFILE is only used if DAEMON is defined */
 
 #define _PATH_PIDNAMES  "@PIDDIR@/ftp.pids-%s"
+#define _PATH_RIPNAMES  "@PIDDIR@/ftp.rips-%s"
 #define _PATH_FTPD_PID  "@PIDDIR@/ftpd.pid"
 #define _PATH_XFERLOG   "@LOGDIR@/xferlog"
 
diff -ru wu-ftpd-2.6.2/src/pathnames.h.noac wu-ftpd-2.6.2hl/src/pathnames.h.noac
--- wu-ftpd-2.6.2/src/pathnames.h.noac	2000-07-01 20:17:39.000000000 +0200
+++ wu-ftpd-2.6.2hl/src/pathnames.h.noac	2002-03-28 02:09:44.000000000 +0100
@@ -121,13 +121,16 @@
 #ifdef USE_VAR
 #ifdef USE_PID
 #define _PATH_PIDNAMES  "/var/pid/ftp.pids-%s"
+#define _PATH_RIPNAMES  "/var/pid/ftp.rips-%s"
 #define _PATH_FTPD_PID  "/var/pid/ftpd.pid"
 #else
 #ifdef VAR_RUN
 #define _PATH_PIDNAMES  "/var/run/ftp.pids-%s"
+#define _PATH_RIPNAMES  "/var/run/ftp.rips-%s"
 #define _PATH_FTPD_PID  "/var/run/ftpd.pid"
 #else
 #define _PATH_PIDNAMES  "/var/adm/ftp.pids-%s"
+#define _PATH_RIPNAMES  "/var/adm/ftp.rips-%s"
 #define _PATH_FTPD_PID  "/var/adm/ftpd.pid"
 #endif
 #endif
@@ -139,10 +142,12 @@
 #else
 #ifdef USE_USR
 #define _PATH_PIDNAMES  "/usr/adm/ftp.pids-%s"
+#define _PATH_RIPNAMES  "/usr/adm/ftp.rips-%s"
 #define _PATH_FTPD_PID  "/usr/adm/ftpd.pid"
 #define _PATH_XFERLOG   "/usr/adm/xferlog"
 #else
 #define _PATH_PIDNAMES  "/usr/local/lib/ftpd/pids/%s"
+#define _PATH_RIPNAMES  "/usr/local/lib/ftpd/pids/%s.rips"
 #define _PATH_FTPD_PID  "/usr/local/lib/ftpd/pids/ftpd.pid"
 #define _PATH_XFERLOG   "/usr/local/logs/xferlog"
 #endif
