Re: fork() flood in tcp/wait service again

Steve G <[email protected]> Sat, 12 Jul 2003 15:19:30 -0700 (PDT)
Newsgroups gmane.network.xinetd
Message-ID <[email protected]>
Hello,

I have thought long and hard about the tcp/wait DOS. I have
a patch that I would like people to try out before I commit
it to cvs.

Basically what it does is create a drain_tcp function. It
is called from the service_postmortum and it calls accept
in a non-blocking mode just one time.

The pros: it will stop DOS conditions, it doesn't lose all
connections in the queue if more requests are queued.

The cons: It might lose 1 legitimate request for perfectly
functioning tcp/wait services. (tcp/nowait services are
unaffected by the patch.)

Improvements: Add a service flags option that enables this
call so that only admins that have a known bad program can
have the tcp/wait socket drained. Everyone with well
behaving tcp/wait applications won't lose the 1 connection
that gets drained.

So, all interested parties please give this a try and let's
see if we can finalize a solution.

-Steve Grubb

__________________________________
Do you Yahoo!?
SBC Yahoo! DSL - Now only $29.95 per month!
http://sbc.yahoo.com
tcp_wait.patch (application/octet-stream, 1.9 KB)
diff -ur xinetd/service.c xinetd.new/service.c
--- xinetd/service.c	2003-07-12 18:00:48.000000000 -0400
+++ xinetd.new/service.c	2003-07-12 17:59:17.000000000 -0400
@@ -894,6 +894,8 @@
       if (cp) {
          if ( SVC_SOCKET_TYPE( sp ) == SOCK_DGRAM )
             drain( cp->co_descriptor ) ;
+         else
+            drain_tcp( cp->co_descriptor );
          free(cp);
 	 cp = NULL;
       }
diff -ur xinetd/util.c xinetd.new/util.c
--- xinetd/util.c	2003-07-12 18:00:49.000000000 -0400
+++ xinetd.new/util.c	2003-07-12 17:59:17.000000000 -0400
@@ -247,7 +247,39 @@
       fcntl( sd, F_SETFL, old_val );
 
    if ( debug.on )
-      msg( LOG_DEBUG, "drain", "Socket should be empty" ) ;
+      msg( LOG_DEBUG, "drain", "UDP socket should be empty" ) ;
+}
+
+/*
+ * Empty the fd of 1 tcp connection at the most in order to assure
+ * that we are not looping on an unaccpeted connection.
+ */
+void drain_tcp( int sd )
+{
+   int sock ;
+   int old_val ;
+
+   /* Put in non-blocking mode so we don't hang. */
+   old_val = fcntl( sd, F_GETFL, FNDELAY );
+   fcntl( sd, F_SETFL, FNDELAY );
+
+   do {
+       sock = accept( sd, NULL,  0 ) ;
+       if (sock < 0) {
+          if (errno == EINTR)
+             continue;
+      }
+      else
+         close( sock );
+      break;      /* We only loop once to get */
+   } while ( 1 );
+
+   /* Restore the value since the connection will be freed, not closed. */
+   if (old_val >= 0)
+      fcntl( sd, F_SETFL, old_val );
+
+   if ( debug.on )
+      msg( LOG_DEBUG, "drain_tcp", "TCP socket should be empty" ) ;
 }
 
 /*
diff -ur xinetd/util.h xinetd.new/util.h
--- xinetd/util.h	2003-07-12 18:00:49.000000000 -0400
+++ xinetd.new/util.h	2003-07-12 17:59:17.000000000 -0400
@@ -19,6 +19,7 @@
  ;
 #endif
 void drain(int sd);
+void drain_tcp(int sd);
 int parse_int(const char *, int , int , int *);
 int parse_base10(const char *, int *);