- (djm) OpenBSD CVS changes:
   - markus@cvs.openbsd.org  2000/07/22 03:14:37
     [servconf.c servconf.h sshd.8 sshd.c sshd_config]
     random early drop; ok theo, niels
   - deraadt@cvs.openbsd.org 2000/07/26 11:46:51
     [ssh.1]
     typo
   - deraadt@cvs.openbsd.org 2000/08/01 11:46:11
     [sshd.8]
     many fixes from pepper@mail.reppep.com
   - provos@cvs.openbsd.org  2000/08/01 13:01:42
     [Makefile.in util.c aux.c]
     rename aux.c to util.c to help with cygwin port
   - deraadt@cvs.openbsd.org 2000/08/02 00:23:31
     [authfd.c]
     correct sun_len; Alexander@Leidinger.net
   - provos@cvs.openbsd.org  2000/08/02 10:27:17
     [readconf.c sshd.8]
     disable kerberos authentication by default
   - provos@cvs.openbsd.org  2000/08/02 11:27:05
     [sshd.8 readconf.c auth-krb4.c]
     disallow kerberos authentication if we can't verify the TGT; from
     dugsong@
     kerberos authentication is on by default only if you have a srvtab.
   - markus@cvs.openbsd.org  2000/08/04 14:30:07
     [auth.c]
     unused
   - markus@cvs.openbsd.org  2000/08/04 14:30:35
     [sshd_config]
     MaxStartups
   - markus@cvs.openbsd.org  2000/08/15 13:20:46
     [authfd.c]
     cleanup; ok niels@
   - markus@cvs.openbsd.org  2000/08/17 14:05:10
     [session.c]
     cleanup login(1)-like jobs, no duplicate utmp entries
   - markus@cvs.openbsd.org  2000/08/17 14:06:34
     [session.c sshd.8 sshd.c]
      sshd -u len, similar to telnetd
diff --git a/sshd.c b/sshd.c
index b6db074..ae02f2c 100644
--- a/sshd.c
+++ b/sshd.c
@@ -14,7 +14,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshd.c,v 1.123 2000/07/18 01:25:01 djm Exp $");
+RCSID("$OpenBSD: sshd.c,v 1.125 2000/08/17 20:06:34 markus Exp $");
 
 #include "xmalloc.h"
 #include "rsa.h"
@@ -139,6 +139,9 @@
 unsigned char *session_id2 = NULL;
 int session_id2_len = 0;
 
+/* record remote hostname or ip */
+unsigned int utmp_len = MAXHOSTNAMELEN;
+
 /* Prototypes for various functions defined later in this file. */
 void do_ssh1_kex();
 void do_ssh2_kex();
@@ -400,6 +403,35 @@
 		key_free(sensitive_data.dsa_host_key);
 }
 
+/*
+ * returns 1 if connection should be dropped, 0 otherwise.
+ * dropping starts at connection #max_startups_begin with a probability
+ * of (max_startups_rate/100). the probability increases linearly until
+ * all connections are dropped for startups > max_startups
+ */
+int
+drop_connection(int startups)
+{
+	double p, r;
+
+	if (startups < options.max_startups_begin)
+		return 0;
+	if (startups >= options.max_startups)
+		return 1;
+	if (options.max_startups_rate == 100)
+		return 1;
+
+	p  = 100 - options.max_startups_rate;
+	p *= startups - options.max_startups_begin;
+	p /= (double) (options.max_startups - options.max_startups_begin);
+	p += options.max_startups_rate;
+	p /= 100.0;
+	r = arc4random() / (double) UINT_MAX;
+
+	debug("drop_connection: p %g, r %g", p, r);
+	return (r < p) ? 1 : 0;
+}
+
 int *startup_pipes = NULL;	/* options.max_startup sized array of fd ints */
 int startup_pipe;		/* in child */
 
@@ -441,7 +473,7 @@
 	initialize_server_options(&options);
 
 	/* Parse command-line arguments. */
-	while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ46")) != EOF) {
+	while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:diqQ46")) != EOF) {
 		switch (opt) {
 		case '4':
 			IPv4or6 = AF_INET;
@@ -488,6 +520,9 @@
 			/* only makes sense with inetd_flag, i.e. no listen() */
 			inetd_flag = 1;
 			break;
+		case 'u':
+			utmp_len = atoi(optarg);
+			break;
 		case '?':
 		default:
 			fprintf(stderr, "sshd version %s\n", SSH_VERSION);
@@ -503,6 +538,7 @@
 			fprintf(stderr, "  -b bits    Size of server RSA key (default: 768 bits)\n");
 			fprintf(stderr, "  -h file    File from which to read host key (default: %s)\n",
 			    HOST_KEY_FILE);
+			fprintf(stderr, "  -u len     Maximum hostname length for utmp recording\n");
 			fprintf(stderr, "  -4         Use IPv4 only\n");
 			fprintf(stderr, "  -6         Use IPv6 only\n");
 			exit(1);
@@ -823,7 +859,8 @@
 					error("newsock del O_NONBLOCK: %s", strerror(errno));
 					continue;
 				}
-				if (startups >= options.max_startups) {
+				if (drop_connection(startups) == 1) {
+					debug("drop connection #%d", startups);
 					close(newsock);
 					continue;
 				}