home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 25 / CDROM25.iso / Share / linux / apache / contrib / patches / 1.3 / limitipconn.patch < prev    next >
Encoding:
Internet Message Format  |  1998-06-11  |  9.5 KB

  1. From: Ed Korthof <ed@organic.com>
  2. Date: Fri Feb  6 12:24:08 1998
  3. Purpose:
  4.  
  5. This patch is provided thanks to Organic Online, Inc., which assumes no
  6. responsibility whatsoever for it, and makes no claims as to its
  7. usability or suitability for any particular task.  Use it at your own
  8. risk.
  9.  
  10. This has never been tested for platforms other than Solaris, though
  11. it may work fine for them.
  12.  
  13. If you're using Stronghold 2.2, one piece of http_protocol.c needs to
  14. be patched by hand -- but it's just include statements and declarations
  15. of external resources, contained at the top of the file.
  16.  
  17. This patch introduces two new directives, "MaxServersPerIP" and
  18. "MaxServersPerIPRead", which limits the total number of connections
  19. from a given list of IP numbers, both in total and just counting those
  20. in "read" state.  This is a common type of denial-of-service attack,
  21. though some broken pieces of software accidentally launch this too.
  22.  
  23. --- http_conf_globals.h.orig    Sun Jun 29 11:08:35 1997
  24. +++ http_conf_globals.h    Fri Feb  6 12:24:08 1998
  25. @@ -84,3 +84,5 @@
  26.  extern char server_root[MAX_STRING_LEN];
  27.  extern char server_confname[MAX_STRING_LEN];
  28.  
  29. +extern int daemons_max_by_ip;
  30. +extern int daemons_max_by_ip_read;
  31. --- http_config.c.orig    Mon Jan  5 12:46:12 1998
  32. +++ http_config.c    Fri Feb  6 12:24:08 1998
  33. @@ -1045,6 +1045,8 @@
  34.      daemons_to_start = DEFAULT_START_DAEMON;
  35.      daemons_min_free = DEFAULT_MIN_FREE_DAEMON;
  36.      daemons_max_free = DEFAULT_MAX_FREE_DAEMON;
  37. +    daemons_max_by_ip = DEFAULT_MAX_DAEMONS_BY_IP;
  38. +    daemons_max_by_ip_read = DEFAULT_MAX_DAEMONS_BY_IP_READ;
  39.      daemons_limit = HARD_SERVER_LIMIT;
  40.      pid_fname = DEFAULT_PIDLOG;
  41.      scoreboard_fname = DEFAULT_SCOREBOARD;
  42. --- http_core.c.orig    Tue Aug  5 01:20:54 1997
  43. +++ http_core.c    Fri Feb  6 12:24:08 1998
  44. @@ -994,6 +994,16 @@
  45.      return NULL;
  46.  }
  47.  
  48. +const char * set_max_servers_by_ip (cmd_parms *cmd, void *dummy, char *arg) {
  49. +    daemons_max_by_ip = atoi (arg);
  50. +    return NULL;
  51. +}
  52. +
  53. +const char * set_max_servers_by_ip_read (cmd_parms *cmd,void *dummy,char *arg) {
  54. +    daemons_max_by_ip_read = atoi (arg);
  55. +    return NULL;
  56. +}
  57. +
  58.  const char *set_daemons_to_start (cmd_parms *cmd, void *dummy, char *arg) {
  59.      daemons_to_start = atoi (arg);
  60.      return NULL;
  61. @@ -1244,6 +1254,8 @@
  62.  { "StartServers", set_daemons_to_start, NULL, RSRC_CONF, TAKE1, "Number of child processes launched at server startup" },
  63.  { "MinSpareServers", set_min_free_servers, NULL, RSRC_CONF, TAKE1, "Minimum number of idle children, to handle request spikes" },
  64.  { "MaxSpareServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1, "Maximum number of idle children" },
  65. +{ "MaxServersPerIP", set_max_servers_by_ip, NULL, RSRC_CONF, TAKE1, "Maximum number of connections from a single IP address" },
  66. +{ "MaxServersPerIPRead", set_max_servers_by_ip_read, NULL, RSRC_CONF, TAKE1, "Maximum number of connection from a single IP address in read state at any time." },
  67.  { "MaxServers", set_max_free_servers, NULL, RSRC_CONF, TAKE1, "Deprecated equivalent to MaxSpareServers" },
  68.  { "ServersSafetyLimit", set_server_limit, NULL, RSRC_CONF, TAKE1, "Deprecated equivalent to MaxClients" },
  69.  { "MaxClients", set_server_limit, NULL, RSRC_CONF, TAKE1, "Maximum number of children alive at the same time" },
  70. --- http_main.c.orig    Mon Sep 22 14:58:51 1997
  71. +++ http_main.c    Fri Feb  6 14:13:17 1998
  72. @@ -145,6 +145,10 @@
  73.  int daemons_to_start;
  74.  int daemons_min_free;
  75.  int daemons_max_free;
  76. +
  77. +int daemons_max_by_ip;
  78. +int daemons_max_by_ip_read;
  79. +
  80.  int daemons_limit;
  81.  time_t restart_time;
  82.  int suexec_enabled = 0;
  83. @@ -1015,6 +1019,36 @@
  84.      return old_status;
  85.  }
  86.  
  87. +void update_child_status_remote_ip (int child_num, conn_rec * current_conn)
  88. +{
  89. +    int slot_size;
  90. +    short_score new_score_rec;
  91. +
  92. +    if (child_num < 0) { return; }
  93. +
  94. +    sync_scoreboard_image();
  95. +    new_score_rec = scoreboard_image->servers[child_num];
  96. +
  97. +    slot_size = sizeof(new_score_rec.remoteip) - 1;
  98. +
  99. +    if (current_conn)
  100. +    {
  101. +        new_score_rec.remoteip = current_conn->remote_addr.sin_addr.s_addr;
  102. +    }
  103. +    else
  104. +    {
  105. +        new_score_rec.remoteip = 0;
  106. +    }
  107. +#if defined(HAVE_MMAP) || defined(HAVE_SHMGET)
  108. +    memcpy(&scoreboard_image->servers[child_num], &new_score_rec, sizeof new_score_rec);
  109. +#else
  110. +    lseek (scoreboard_fd, (long)child_num * sizeof(short_score), 0);
  111. +    force_write (scoreboard_fd, (char*)&new_score_rec, sizeof(short_score));
  112. +#endif
  113. +
  114. +    sync_scoreboard_image();
  115. +}
  116. +
  117.  void update_scoreboard_global()
  118.      {
  119.  #ifdef SCOREBOARD_FILE
  120. @@ -1106,6 +1140,26 @@
  121.      return res;
  122.  }
  123.  
  124. +int count_connections (conn_rec * current_conn, int state)
  125. +{
  126. +    unsigned long remote_ip = current_conn->remote_addr.sin_addr.s_addr;
  127. +    int res = 0, i;
  128. +
  129. +    for (i = 0; i < HARD_SERVER_LIMIT; i++)
  130. +    {
  131. +        if ((scoreboard_image->servers[i].status == SERVER_DEAD) ||
  132. +            (state > 0 && scoreboard_image->servers[i].status != state))
  133. +        {
  134. +            continue;
  135. +        }
  136. +        if (scoreboard_image->servers[i].remoteip == remote_ip)
  137. +        {
  138. +            res++;
  139. +        }
  140. +    }
  141. +    return res;
  142. +}
  143. +
  144.  int find_free_child_num ()
  145.  {
  146.      int i;
  147. @@ -1880,6 +1934,10 @@
  148.           * until no requests are left or we decide to close.
  149.           */
  150.  
  151. +        /* if we care, note the client. */
  152. +        if (daemons_max_by_ip || daemons_max_by_ip_read)
  153. +            update_child_status_remote_ip (child_num, current_conn);
  154. +
  155.          while ((r = read_request(current_conn)) != NULL) {
  156.  
  157.          /* ok we've read the request... it's a little too late
  158. @@ -1921,6 +1979,9 @@
  159.           */
  160.          signal (SIGUSR1, (void (*)())just_die);
  161.          }
  162. +        /* if we care, note that the client has left. */
  163. +        if (daemons_max_by_ip || daemons_max_by_ip_read)
  164. +            update_child_status_remote_ip (child_num, (conn_rec *)NULL);
  165.  
  166.          /*
  167.           * Close the connection, being careful to send out whatever is still
  168. --- http_main.h.orig    Sat Apr 26 13:20:06 1997
  169. +++ http_main.h    Fri Feb  6 12:24:09 1998
  170. @@ -97,3 +97,6 @@
  171.  int count_busy_servers ();
  172.  int count_idle_servers ();
  173.  
  174. +void update_child_status_remote_ip (int, conn_rec *);
  175. +int count_connections (conn_rec *, int);
  176. +
  177. --- http_protocol.c.orig    Fri Aug 15 10:08:51 1997
  178. +++ http_protocol.c    Fri Feb  6 15:44:35 1998
  179. @@ -67,6 +67,15 @@
  180.                   * common support code...
  181.                   */
  182.  #include "util_date.h"          /* For parseHTTPdate and BAD_DATE */
  183. +
  184. +#include "scoreboard.h"         /* for limiting connections by IP */
  185. +#ifndef LONG_STRING_LEN
  186. +#define LONG_STRING_LEN 2048
  187. +#endif /* LONG_STRING_LEN */
  188. +extern int daemons_max_by_ip;
  189. +extern int daemons_max_by_ip_read;
  190. +extern void die();
  191. +
  192.  #include <stdarg.h>
  193.  
  194.  #define SET_BYTES_SENT(r) \
  195. @@ -606,7 +615,7 @@
  196.      conn_rec *conn = r->connection;
  197.      int major = 1, minor = 0;    /* Assume HTTP/1.0 if non-"HTTP" protocol*/
  198.      int len;
  199. -    
  200.      /* Read past empty lines until we get a real request line,
  201.       * a read error, the connection closes (EOF), or we timeout.
  202.       *
  203. @@ -762,8 +771,10 @@
  204.  
  205.  request_rec *read_request (conn_rec *conn)
  206.  {
  207. +    char errstr[LONG_STRING_LEN]; int current_connections;
  208.      request_rec *r = (request_rec *)pcalloc (conn->pool, sizeof(request_rec));
  209.  
  210. +    errstr[0] = '\0';
  211.      r->connection = conn;
  212.      conn->server = conn->base_server;
  213.      r->server = conn->server;
  214. @@ -789,6 +800,39 @@
  215.      r->read_length  = 0;
  216.      r->read_body    = REQUEST_NO_BODY;
  217.      
  218. +    if (daemons_max_by_ip && ((current_connections = count_connections(conn,0))
  219. +                              > daemons_max_by_ip))
  220. +    {
  221. +        r->request_time=time(NULL);
  222. +        ap_snprintf(errstr,LONG_STRING_LEN,
  223. +            "client at %s rejected for %s with %d total current connections",
  224. +            conn->remote_ip, conn->server->server_hostname,
  225. +            current_connections);
  226. +    }
  227. +    else if (daemons_max_by_ip_read &&
  228. +             ((current_connections = count_connections(conn,SERVER_BUSY_READ))
  229. +              > daemons_max_by_ip_read))
  230. +    {
  231. +        ap_snprintf(errstr,LONG_STRING_LEN,
  232. +            "client at %s rejected for %s with %d current connections in \
  233. +            read state", conn->remote_ip,
  234. +            conn->server->server_hostname, current_connections);
  235. +    }
  236. +    if (*errstr != '\0') {
  237. +        r->status        = HTTP_OK;
  238. +        r->request_time  = time(NULL);
  239. +        r->proto_num     = 1000;       /* or something */
  240. +        r->assbackwards  = 0;          /* who knows... */
  241. +        r->protocol      = "HTTP/1.0"; /* just not empty */
  242. +        r->the_request   = NULL;
  243. +        r->method        = NULL;
  244. +        r->method_number = M_INVALID;
  245. +        die(LIMIT_CONNECTIONS_BY_IP_ERROR, r);
  246. +        log_transaction(r);
  247. +        log_error(errstr, conn->server);
  248. +        /* return r; */ return NULL;
  249. +    }
  250. +
  251.      r->status = HTTP_REQUEST_TIME_OUT;    /* Until we get a request */
  252.  
  253.      /* Get the request... */
  254. --- httpd.h.orig    Mon Jan  5 15:20:09 1998
  255. +++ httpd.h    Fri Feb  6 13:48:24 1998
  256. @@ -216,6 +216,12 @@
  257.  
  258.  #define DEFAULT_MIN_FREE_DAEMON 5
  259.  
  260. +/* Define default limits for MaxDaemons serving a single address */
  261. +
  262. +#define DEFAULT_MAX_DAEMONS_BY_IP 150
  263. +#define DEFAULT_MAX_DAEMONS_BY_IP_READ 75
  264. +#define LIMIT_CONNECTIONS_BY_IP_ERROR HTTP_SERVICE_UNAVAILABLE
  265. +
  266.  /* Limit on the total --- clients will be locked out if more servers than
  267.   * this are needed.  It is intended solely to keep the server from crashing
  268.   * when things get out of hand.
  269. --- scoreboard.h.orig    Thu Jun 26 18:51:50 1997
  270. +++ scoreboard.h    Fri Feb  6 12:24:10 1998
  271. @@ -89,6 +89,7 @@
  272.      char request[64];    /* We just want an idea... */
  273.      char vhost[32];     /* What virtual host is being accessed? */
  274.  #endif
  275. +    unsigned long remoteip;
  276.  } short_score;
  277.  
  278.  typedef struct
  279.