]> git.pld-linux.org Git - packages/apache1-mod_bandwidth.git/blob - mod_bandwidth.c
50725cfa06f1a4349e456cbf2157e287 mod_bandwidth.c
[packages/apache1-mod_bandwidth.git] / mod_bandwidth.c
1 /* ====================================================================
2  * Copyright (c) 1995 The Apache Group.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer. 
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * 3. All advertising materials mentioning features or use of this
17  *    software must display the following acknowledgment:
18  *    "This product includes software developed by the Apache Group
19  *    for use in the Apache HTTP server project (http://www.apache.org/)."
20  *
21  * 4. The names "Apache Server" and "Apache Group" must not be used to
22  *    endorse or promote products derived from this software without
23  *    prior written permission.
24  *
25  * 5. Redistributions of any form whatsoever must retain the following
26  *    acknowledgment:
27  *    "This product includes software developed by the Apache Group
28  *    for use in the Apache HTTP server project (http://www.apache.org/)."
29  *
30  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
31  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
34  * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
41  * OF THE POSSIBILITY OF SUCH DAMAGE.
42  * ====================================================================
43  *
44  * This software consists of voluntary contributions made by many
45  * individuals on behalf of the Apache Group and was originally based
46  * on public domain software written at the National Center for
47  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
48  * For more information on the Apache Group and the Apache HTTP server
49  * project, please see <http://www.apache.org/>.
50  *
51  */
52
53 /****************************************************************************
54  * Title       : Bandwidth management
55  * File        : mod_bandwidth.c
56  * Author      : Yann Stettler (stettler@cohprog.com)
57  * Date        : 17 July 1999
58  * Version     : 2.0.3 for Apache 1.3+
59  *
60  * Description :
61  *   Provide bandwidth usage limitation either on the whole server or
62  *   one a per connection basis based on the size of files, directory
63  *   location or remote domain/IP.
64  *
65  * Revision    : 08/04/97 - "-1" value for LargeFileLimit to disable any
66  *                          limits for that kind of files.
67  *               01/26/98 - Include in_domain() and in_ip() in this file and
68  *                          make them static so it will work with Apache 1.3x
69  *               03/29/98 - Change to set bandwidth in VirtualHost directive
70  *               07/17/99 - Minor changes to remove warnings at compil time
71  *                          
72  *                          Allow the use of the network/mask format when
73  *                          setting a bandwidth for a remote host/network.
74  *                          (Thanks to Sami Kuhmonen for the patch) 
75  *
76  *                          New directive BandWidthPulse
77  *
78  *               10/14/00 - Minor bug fixed
79  *               12/15/00 - Bug fix when using mmap
80  *               08/29/01 - Add a directive to change the data directory
81  *                          (Thanks to Awesome Walrus <walrus@amur.ru> )
82  *
83  ***************************************************************************
84  * Copyright (c)1997 Yann Stettler and CohProg SaRL. All rights reserved.
85  * Written for the Apache Group by :
86  *
87  * Yann Stettler
88  * stettler@cohprog.com
89  * http://www.cohprog.com/
90  * http://www.animanga.com/
91  *
92  * Based on the original default_handler module and on bw_module 0.2 from
93  * Etienne BERNARD (eb@via.ecp.fr) at VIA Centrale Reseaux, ECP, France
94  * 
95  * Many thanks to Colba Net Inc (Montreal) for their sponsoring of 
96  * improvements to this module.
97  *
98  ***************************************************************************/
99
100
101 /*
102  * Instruction :
103  * -------------
104  *
105  * Note : this module was writen for Apache 1.3.x and tested on it
106  *
107  * Installation :
108  *
109  * 1) Insert the following line at the end of the "Configuration" file :
110  *    Module bandwidth_module      mod_bandwidth.o
111  *
112  * WARNING : This behaviour is not the same between the various main versions
113  *           of Apache. Please, read the instruction on our website for the
114  *           instructions concerning the latest release : 
115  *           http://www.cohprog.com/v3/bandwidth/doc-en.html
116  *
117  * 2) Run the "Configure" program and re-compile the server with "make".
118  *
119  * 3) Create the following directories with "rwx" permission to everybody :
120  *    (or rwx for the user under which Apache run : Usualy "nobody")
121  *    /tmp/apachebw
122  *    /tmp/apachebw/link
123  *    /tmp/apachebw/master
124  *
125  *  /==== by Awesome Walrus <walrus@amur.ru> =====================\
126  *    Or you may change this name by using BandWidthDataDir global
127  *    configuration directive. See below for details.
128  *  \==== by Awesome Walrus <walrus@amur.ru> =====================/
129  *
130  * Note that if any of those directories doesn't exist, or if they can't
131  * be accessed by the server, the module is totaly disabled except for
132  * logging an error message in the logfile.
133  *
134  * Be careful that on some systems the content of the /tmp directory
135  * is deleted at boot time or every so often by a cronjob. If that the
136  * case, either disable this feature or change the location of the
137  * directories used by the module in the sources bellow.
138  *
139  * Server configuration directive :
140  * --------------------------------
141  *
142  *  /==== by Awesome Walrus <walrus@amur.ru> =====================\
143  * -  BandWidthDataDir
144  *    Syntax  : BandWidthDataDir <directory>
145  *    Default : "/tmp/apachebw"
146  *    Context : server config
147  *
148  *    Sets the name of the directory used by mod_bandwidth to store
149  *    its internal temporary information.
150  *  \==== by Awesome Walrus <walrus@amur.ru> =====================/
151  *
152  * -  BandWidthModule 
153  *    Syntax  : BandWidthModule <On|Off>
154  *    Default : Off
155  *    Context : per server config
156  *
157  *    Enable or disable totaly the whole module. By default, the module is
158  *    disable so it is safe to compile it in the server anyway.
159  *
160  *    PLEASE, NOTE THAT IF YOU SET A BANDWIDTH LIMIT INSIDE A VIRTUALHOST
161  *    BLOCK, YOU ALSO __NEED__ TO PUT THE "BandWidthModule On" DIRECTIVE
162  *    INSIDE THAT VIRTUALHOST BLOCK !
163  *
164  *    IF YOU SET BANDWIDTH LIMITS INSIDE DIRECTORY BLOCKS (OUTSIDE OF
165  *    ANY VIRTUALHOST BLOCK), YOU ONLY NEED TO PUT THE "BandWidthModule On"
166  *    DIRECTIVE ONCE, OUTSIDE OF ANY VIRTUALHOST OR DIRECTORY BLOCK.
167  *
168  * -  BandWidthPulse
169  *    Syntax  : BandWidthPulse <microseconds>
170  *    Default :
171  *    Context : per server config
172  *
173  *    Change the algorithm used to calculate bandwidth and transmit data.
174  *    In normal mode (old mode), the module try to transmit data in packets
175  *    of 1KB. That mean that if the bandwidth available is of 512B, the
176  *    module will transmit 1KB, wait 2 seconds, transmit another 1KB and
177  *    so one.
178  *
179  *    Seting a value with "BandWidthPulse", will change the algorithm so
180  *    that the server will always wait the same amount of time between
181  *    sending packets but the size of the packets will change.
182  *    The value is in microseconds.
183  *    For example, if you set "BandWidthPulse 1000000" (1 sec) and the
184  *    bandwidth available is of 512B, the sever will transmit 512B,
185  *    wait 1 second, transmit 512B and so on.
186  *
187  *    The advantage is a smother flow of data. The disadvantage is
188  *    a bigger overhead of data transmited for packet header.
189  *    Setting too small a value (bellow 1/5 of a sec) is not realy
190  *    useful and will put more load on the system and generate more
191  *    traffic for packet header.
192  *
193  *    Note also that the operating system may do some buffering on
194  *    it's own and so defeat the purpose of setting small values.
195  *
196  *    This may be very useful on especialy crowded network connection :
197  *    In normal mode, several seconds may happen between the sending of
198  *    a full packet. This may lead to timeout or people may believe that
199  *    the connection is hanging. Seting a value of 1000000 (1 sec) would
200  *    guarantee that some data are sent every seconds...
201  *
202  * Directory / Server / Virtual Server configuration directive :
203  * -------------------------------------------------------------
204  *
205  * -  BandWidth
206  *    Syntax  : BandWidth <domain|ip|all> <rate>
207  *    Default : none
208  *    Context : per directory, .htaccess
209  *
210  *    Limit the bandwidth for files in this directory and
211  *    sub-directories based on the remote host <domain> or
212  *    <ip> address or for <all> remote hosts.
213  *
214  *    Ip addresses may now be specified in the network/mask format.
215  *    (Ie: 192.168.0.0/21 )
216  *
217  *    The <rate> is in Bytes/second.
218  *    A <rate> of "0" means no bandwidth limit.
219  *
220  *    Several BandWidth limits can be set for the same
221  *    directory to set different limits for different
222  *    hosts. In this case, the order of the "BandWidth"
223  *    keywords is important as the module will take the
224  *    first entry which matches the client address.
225  *
226  *    Example :
227  *       <Directory /home/www>
228  *       BandWidth ecp.fr 0
229  *       BandWidth 138.195 0
230  *       BandWidth all 1024
231  *       </Directory>
232  *
233  *      This will limit the bandwith for directory /home/www and 
234  *      all it's subdirectories to 1024Bytes/sec, except for 
235  *      *.ecp.fr or 138.195.*.* where no limit is set.
236  *
237  * -  LargeFileLimit
238  *    Syntax  : LargeFileLimit <filesize> <rate>
239  *    Default : none
240  *    Context : per directory, .htaccess
241  *
242  *    Set a maximal <rate> (in bytes/sec) to use when transfering
243  *    a file of <filesize> KBytes or more.
244  *
245  *    Several "LargeFileLimit" can be set for various files sizes
246  *    to create range. The rate used for a given file size will be
247  *    the one of the matching range.
248  *
249  *    A <rate> of "0" mean that there isn't any limit based on
250  *    the size.
251  *
252  *    A <rate> of "-1" mean that there isn't any limit for that type
253  *    of file. It's override even a BandWidth limit. I found this usefull
254  *    to give priority to very small files (html pages, very small pictures)
255  *    while seting limits for larger files... (users with their video files
256  *    can go to hell ! :)
257  *
258  *    Example :
259  *    If the following limits are set :
260  *       LargeFileLimit 200 3072
261  *       LargeFileLimit 1024 2048
262  *
263  *       That's mean that a file of less than 200KBytes won't be
264  *       limited based on his size. A file with a size between
265  *       200KBytes (included) and 1023Kbytes (included) will be
266  *       limited to 3072Bytes/sec and a file of 1024Kbytes or more
267  *       will be limited to 2048Bytes/sec.
268  *
269  * -  MinBandWidth
270  *    Syntax  : MinBandWidth <domain|ip|all> <rate>
271  *    Default : all 256
272  *    Context : per directory, .htaccess
273  *
274  *    Set a minimal bandwidth to use for transfering data. This
275  *    over-ride both BandWidth and LargeFileLimit rules as well
276  *    as the calculated rate based on the number of connections.
277  *
278  *    The first argument is used in the same way as the first
279  *    argument of BandWidth.
280  *
281  *    <rate> is in bytes per second.
282  *
283  *    A rate of "0" explicitly means to use the default minimal
284  *    value (256 Bytes/sec).
285  *
286  *    A rate of "-1" means that the minimal rate is equal to the
287  *    actual rate defined by BandWidth and LargeFileLimit.
288  *    In fact, that means that the final rate won't depend
289  *    of the number of connections but only on what was defined.
290  *
291  *    Example :
292  *    If BandWidth is set to "3072" (3KBytes/sec) and MinBandWidth
293  *    is set to "1024" (1KBytes/sec) that means :
294  *       - if there is one connection, the file will be transfered
295  *         at 3072 Bytes/sec.
296  *       - if there is two connections, each files will be transfered
297  *         at 1536 Bytes/sec. 
298  *       - if there is three or more connections, each files will be
299  *         transfered at 1024 Bytes/sec. (Minimal of 1024 Bytes/sec).
300  *
301  *    If MinBandWidth is set to "-1" that means :
302  *       - if there is one connection, the file will be transfered
303  *         at 3072 Bytes/sec.
304  *       - if there is two or more connections, each files will be
305  *         transfered at 3072 Bytes/sec. In effect, the rate doesn't
306  *         depend anymore on the number of connections but only on
307  *         the configuration values.
308  *
309  *    Note that the total transfer rate will never exceed your physical
310  *    bandwidth limitation.
311  *
312  * Note : If both a "BandWidth" and a "LargeFileLimit" limit apply,
313  *        the lowest one will be used. (But never lower than the
314  *        "MinBandWidth" rate)
315  *
316  *        If both a virtual server limit is defined and another
317  *        apply for a directory under this virtual server, the
318  *        directory limit will over-ride it.
319  *
320  *        If a limit is defined outside a Directory or VirtualHost
321  *        directive, it will act as default on a per virtual server
322  *        basis. (Ie: each virtual server will have that limit,
323  *        _independantly_ of the other servers)
324  *
325  * Implementation notes :
326  * ----------------------
327  * 
328  * 1) This module isn't called when serving a cgi-script. In this
329  *    case, it's the functions in mod_cgi.c that handle the connection.
330  *
331  *    That's not a bug : I didn't want to change the cgi_module and
332  *    I was too lazy to do it anyway.
333  *
334  * 2) The rate of data transmission is only calculated. It isn't
335  *    measured. Which mean that this module calculates the final
336  *    rate that should apply for a file and simply divides this
337  *    number by the number of actual connections subject to the
338  *    same "per directory" directives.
339  * 
340  * 3) On the "+" side, the module regulate the speed taking
341  *    into account the actual time that was needed to send the
342  *    data. Which also mean that if data are read by the client
343  *    at a slowest rate than the limit, no time will be lost and
344  *    data will be sent as fast as the client can read them (but
345  *    no faster than the limited rate :) ...
346  *
347  * 4) Some kind of buffering is done as side effect. Data are
348  *    sent in packet of 1024 Bytes which seems a good value
349  *    for TCP/IP.
350  *    If another packet size is wanted, change the value of
351  *    "#define PACKET" in the codes bellow.
352  *
353  * 5) The default value for MinBandWidth is defined by :
354  *    "#define MIN_BW_DEFAULT" (in Bytes/sec)
355  *
356  * 6) Don't define "BWDEBUG" for a production server :
357  *    this would log a _lot_ of useless informations for
358  *    debuging purpose.
359  *
360  */
361
362 #include <time.h>
363 #include "httpd.h"
364 #include "http_core.h"
365 #include "http_config.h"
366 #include "http_log.h"
367 #include "http_request.h"
368 #include "http_protocol.h"
369 #include "http_main.h"
370
371 #define MIN_BW_DEFAULT  256        /* Minimal bandwidth defaulted to 256Bps */
372 #define PACKET 1024                /* Sent packet of 1024 bytes             */
373
374 /* #define MASTER_DIR  "/tmp/apachebw/master"
375  * #define LINK_DIR    "/tmp/apachebw/link"
376  */
377
378 #define MASTER_DIR  "master"
379 #define LINK_DIR    "link"
380
381 /* Define BWDEBUG for debuging purpose only ! */
382 /* #define BWDEBUG */
383 #ifdef BWDEBUG
384 #undef BWDEBUG
385 #endif
386
387 #define BANDWIDTH_DISABLED             1<<0
388 #define BANDWIDTH_ENABLED              1<<1
389
390 #ifdef USE_MMAP_FILES
391 #include <unistd.h>
392 #include <sys/mman.h>
393
394 /* mmap support for static files based on ideas from John Heidemann's
395  * patch against 1.0.5.  See
396  * <http://www.isi.edu/~johnh/SOFTWARE/APACHE/index.html>.
397  */
398
399 /* Files have to be at least this big before they're mmap()d.  This is to
400  * deal with systems where the expense of doing an mmap() and an munmap()
401  * outweighs the benefit for small files.
402  */
403 #ifndef MMAP_THRESHOLD
404 #ifdef SUNOS4
405 #define MMAP_THRESHOLD          (8*1024)
406 #else
407 #define MMAP_THRESHOLD          0
408 #endif
409 #endif
410 #endif
411
412 /* Limits for bandwidth and minimal bandwidth based on directory */
413 typedef struct {
414   char *from;
415   long rate;
416 } bw_entry;
417
418 /* Limits for bandwidth based on file size */
419 typedef struct {
420   long size;
421   long rate;
422 } bw_sizel;
423
424 /* Per directory configuration structure */
425 typedef struct {
426   array_header *limits;
427   array_header *minlimits;
428   array_header *sizelimits;
429   char  *directory;
430 } bandwidth_config;
431
432 /* Per server configuration structure */
433 typedef struct {
434   int   state;
435 } bandwidth_server_config;
436
437 int bw_handler (request_rec *);
438
439 module bandwidth_module;
440
441 static long int BWPulse=0;
442
443 char bandwidth_data_dir[MAX_STRING_LEN]="/tmp/apachebw";
444
445 /***************************************************************************
446  * Configuration functions                                                 *
447  ***************************************************************************/
448
449 static void *create_bw_config(pool *p, char *path) {
450   bandwidth_config *new=
451         (bandwidth_config *) ap_palloc(p, sizeof(bandwidth_config));
452   new->limits=ap_make_array(p,20,sizeof(bw_entry));
453   new->minlimits=ap_make_array(p,20,sizeof(bw_entry));
454   new->sizelimits=ap_make_array(p,10,sizeof(bw_sizel));
455   new->directory=ap_pstrdup(p, path);
456   return (void *)new; 
457 }
458
459 static void *create_bw_server_config(pool *p, server_rec *s) {
460   bandwidth_server_config *new;
461
462   new = (bandwidth_server_config *)ap_pcalloc(p, sizeof(bandwidth_server_config));
463
464   new->state = BANDWIDTH_DISABLED;
465
466   return (void *)new;
467 }
468
469 /***************************************************************************
470  * Directive functions                                                     *
471  ***************************************************************************/
472
473 static const char *bandwidthmodule(cmd_parms *cmd, bandwidth_config *dconf, int flag) {
474    bandwidth_server_config *sconf;
475
476    sconf = (bandwidth_server_config *)ap_get_module_config(cmd->server->module_config, &bandwidth_module);
477    sconf->state = (flag ? BANDWIDTH_ENABLED : BANDWIDTH_DISABLED);
478
479    return NULL;
480 }
481
482 static const char *set_bandwidth_data_dir(cmd_parms *cmd, void *dummy, char *arg) {
483     arg = ap_os_canonical_filename(cmd->pool, arg);
484
485     if (!ap_is_directory(arg)) {
486         return "BandWidthDataDir must be a valid directory";
487     }
488     ap_cpystrn(bandwidth_data_dir, arg, sizeof(bandwidth_data_dir));
489     return NULL;
490 }
491
492 static const char *setpulse(cmd_parms *cmd, bandwidth_config *dconf, char *pulse) {
493    long int temp;
494
495    if (pulse && *pulse && isdigit(*pulse))
496      temp = atol(pulse);
497    else
498      return "Invalid argument";
499
500    if (temp<0)
501      return "BandWidth must be a number of bytes/s";
502
503    BWPulse=temp;
504
505    return NULL;
506 }
507
508 static const char *bandwidth(cmd_parms *cmd, void *s, char *from, char *bw) {
509   bandwidth_config *conf=(bandwidth_config *)s;
510   bw_entry *a;
511   long int temp;
512
513   if (bw && *bw && isdigit(*bw))
514     temp = atol(bw);
515   else
516     return "Invalid argument";
517
518   if (temp<0)
519     return "BandWidth must be a number of bytes/s";
520
521   a = (bw_entry *)ap_push_array(conf->limits);
522   a->from = ap_pstrdup(cmd->pool,from);
523   a->rate = temp;
524   return NULL;
525 }
526
527 static const char *minbandwidth(cmd_parms *cmd, void *s, char *from, char *bw) {
528   bandwidth_config *conf=(bandwidth_config *)s;
529   bw_entry *a;
530   long int temp;
531
532   if (bw && *bw && (*bw=='-' || isdigit(*bw)))
533     temp = atol(bw);
534   else
535     return "Invalid argument";
536
537   a = (bw_entry *)ap_push_array(conf->minlimits);
538   a->from = ap_pstrdup(cmd->pool,from);
539   a->rate = temp;
540   return NULL;
541 }
542
543 static const char *largefilelimit(cmd_parms *cmd, void *s, char *size, char *bw) {
544   bandwidth_config *conf=(bandwidth_config *)s;
545   bw_sizel *a;
546   long int temp, tsize;
547
548   if (bw && *bw && (*bw=='-' || isdigit(*bw)))
549     temp = atol(bw);
550   else
551     return "Invalid argument";
552
553   if (size && *size && isdigit(*size))
554     tsize = atol(size);
555   else
556     return "Invalid argument";
557
558   /*
559   if (temp<0)
560     return "BandWidth must be a number of bytes/s";
561   */
562
563   if (tsize<0)
564     return "File size must be a number of Kbytes";
565
566   a = (bw_sizel *)ap_push_array(conf->sizelimits);
567   a->size = tsize;
568   a->rate = temp;
569   return NULL;
570 }
571
572 static command_rec bw_cmds[] = {
573 { "BandWidth", bandwidth, NULL, RSRC_CONF | OR_LIMIT, TAKE2,
574     "a domain (or ip, or all for all) and a bandwidth limit (in bytes/s)" },
575 { "MinBandWidth", minbandwidth, NULL, RSRC_CONF | OR_LIMIT, TAKE2,
576     "a domain (or ip, or all for all) and a minimal bandwidth limit (in bytes/s)" },
577 { "LargeFileLimit", largefilelimit, NULL, RSRC_CONF | OR_LIMIT, TAKE2,
578     "a filesize (in Kbytes) and a bandwidth limit (in bytes/s)" },
579 { "BandWidthModule",   bandwidthmodule,  NULL, OR_FILEINFO, FLAG, 
580       "On or Off to enable or disable (default) the whole bandwidth module" },
581 { "BandWidthPulse", setpulse, NULL, OR_FILEINFO, TAKE1,
582     "a number of microseconds" },
583 { "BandWidthDataDir", set_bandwidth_data_dir, NULL, RSRC_CONF, TAKE1,
584     "A writable directory where temporary bandwidth info is to be stored" },
585 { NULL }
586 };
587
588 /***************************************************************************
589  * Internal functions                                                      *
590  ***************************************************************************/
591
592 #ifdef USE_MMAP_FILES
593 struct mmap {
594     void *mm;
595     size_t length;
596 };
597
598 static void mmap_cleanup (void *mmv)
599 {
600     struct mmap *mmd = mmv;
601
602     munmap(mmd->mm, mmd->length);
603 }
604 #endif
605
606 static int in_domain(const char *domain, const char *what) {
607     int dl=strlen(domain);
608     int wl=strlen(what);
609
610     if((wl-dl) >= 0) {
611         if (strcasecmp(domain,&what[wl-dl]) != 0) return 0;
612
613         /* Make sure we matched an *entire* subdomain --- if the user
614          * said 'allow from good.com', we don't want people from nogood.com
615          * to be able to get in.
616          */
617
618         if (wl == dl) return 1; /* matched whole thing */
619         else return (domain[0] == '.' || what[wl - dl - 1] == '.');
620     } else
621         return 0;
622 }
623
624 static int in_ip(char *domain, char *what) {
625
626     /* Check a similar screw case to the one checked above ---
627      * "allow from 204.26.2" shouldn't let in people from 204.26.23
628      */
629     int a, b, c, d, e; 
630     unsigned long in, out, inmask, outmask;
631
632     if (sscanf(domain, "%i.%i.%i.%i/%i", &a, &b, &c, &d, &e) < 5) {
633        int l = strlen(domain);
634        if (strncmp(domain,what,l) != 0) return 0;
635        if (domain[l - 1] == '.') return 1;
636        return (what[l] == '\0' || what[l] == '.');
637     }
638     in = (a<<24)|(b<<16)|(c<<8)|d;
639     inmask = 0xFFFFFFFF ^ ((1<<(32-e))-1);
640
641     if (sscanf(what, "%i.%i.%i.%i", &a, &b, &c, &d) < 4)
642       return 0;
643
644     out = (a<<24)|(b<<16)|(c<<8)|d;
645
646     return ((in&inmask) == (out&inmask));
647 }
648
649 static int is_ip(const char *host)
650 {
651     while ((*host == '.') || (*host=='/') || isdigit(*host))
652         host++;
653     return (*host == '\0');
654 }
655
656 static long get_bw_rate(request_rec *r, array_header *a) {
657   bw_entry *e = (bw_entry *)a->elts;
658   const char *remotehost = NULL;
659   int i;
660
661   remotehost = ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_HOST);
662
663   for (i=0; i< a->nelts; i++) {
664     if (!strcmp(e[i].from,"all"))
665       return e[i].rate;
666
667     if (in_ip(e[i].from, r->connection->remote_ip))
668       return e[i].rate;
669     if ((remotehost!=NULL) && !is_ip(remotehost)) {
670       if (in_domain(e[i].from,remotehost))
671         return e[i].rate;
672     }
673   }
674   return 0;
675 }
676
677 static long get_bw_filesize(request_rec *r, array_header *a, off_t filesize) {
678   bw_sizel *e = (bw_sizel *)a->elts;
679   int i;
680   long int tmpsize=0, tmprate=0;
681
682   if (!filesize)
683     return(0);
684  
685   filesize /= 1024;
686
687   for (i=0; i< a->nelts; i++) {
688     if (e[i].size <= filesize )
689        if (tmpsize <= e[i].size) {
690           tmpsize=e[i].size;
691           tmprate=e[i].rate;
692        }
693   }
694
695   return(tmprate);
696 }
697
698 static int current_connection(char *filename) {
699    struct stat mdata;
700
701    /*
702     * Here, we will do the main work. And that won't take
703     * long !
704     */
705
706     if (stat(filename, &mdata) < 0) 
707        return(1);   /* strange... should not occure */
708
709     return(mdata.st_nlink-1);
710
711     /*
712      * I told you it wouldn't be long... :)
713      */
714 }
715
716 static struct timeval timediff(struct timeval *a, struct timeval *b)
717 {
718    struct timeval rslt, tmp;
719
720    tmp=*a;
721   
722    if ((rslt.tv_usec = tmp.tv_usec - b->tv_usec) < 0) {
723       rslt.tv_usec += 1000000;
724       --(tmp.tv_sec);
725    }
726    if ((rslt.tv_sec = tmp.tv_sec - b->tv_sec) < 0) {
727       rslt.tv_usec=0;
728       rslt.tv_sec=0;
729    }
730    return(rslt);
731 }
732
733 /***************************************************************************
734  * Main function, module core                                              *
735  ***************************************************************************/
736
737 static int handle_bw(request_rec *r) {
738   int rangestatus, errstatus;
739   FILE *f;
740   bandwidth_config *conf =
741      (bandwidth_config *)ap_get_module_config(r->per_dir_config, &bandwidth_module);
742   bandwidth_server_config *sconf =
743      (bandwidth_server_config *)ap_get_module_config(r->server->module_config, &bandwidth_module);
744   long int bw_rate=0, bw_min=0, bw_f_rate=0, cur_rate;
745   int bwlast=0, fd;
746   long int tosend, bytessent, filelength;
747   struct stat fdata;
748   struct timeval opt_time, last_time, now, timespent, timeout;
749   char masterfile[128], filelink[128], *directory;
750
751 #ifdef USE_MMAP_FILES
752     caddr_t mm;
753 #endif
754
755   /* This handler has no use for a request body (yet), but we still
756    * need to read and discard it if the client sent one.
757    */
758   if ((errstatus = ap_discard_request_body(r)) != OK)
759       return errstatus;
760
761   /* Return as fast as possible if request is not a GET or if module not
762      enabled */
763   if (r->method_number != M_GET || sconf->state == BANDWIDTH_DISABLED)
764      return DECLINED;
765
766   if (!conf->directory) {
767      directory=(char *)ap_document_root(r);
768   } else {
769      directory=conf->directory;
770   }
771
772   bw_rate=get_bw_rate(r,conf->limits);
773   bw_min=get_bw_rate(r, conf->minlimits);
774   bw_f_rate=get_bw_filesize(r, conf->sizelimits , r->finfo.st_size);
775
776   if ((bw_rate==0 && bw_f_rate==0) || bw_f_rate < 0 || !directory) return DECLINED;
777
778   if (r->finfo.st_mode == 0 || (r->path_info && *r->path_info)) {
779       ap_log_reason("File does not exist", r->filename, r);
780       return NOT_FOUND;
781   }
782
783   ap_update_mtime (r, r->finfo.st_mtime);
784   ap_set_last_modified(r);
785   ap_set_etag(r);
786   if (((errstatus = ap_meets_conditions(r)) != OK)
787       || (errstatus = ap_set_content_length (r, r->finfo.st_size))) {
788           return errstatus;
789   }
790
791   /* 
792    * Create name of the master file.
793    * Will use the inode and device number of the "limited"
794    * directory.
795    */
796
797   if (stat(directory, &fdata) < -1) {
798      /* Dunno if this may happen... but well... */
799      return DECLINED;
800   }
801   sprintf(masterfile,"%s/%s/%d:%ld",  bandwidth_data_dir, MASTER_DIR, (short int)fdata.st_dev, (long int)fdata.st_ino);
802
803   /*
804    * Check if master file already exist, else create it.
805    * Then we create a hardlink to it using the pid as name.
806    */
807   if ((fd=open(masterfile, O_RDONLY|O_CREAT, 0777)) < 0) {
808      ap_log_printf(r->server, "mod_bandwidth : Can't create/access master file %s",masterfile);
809      return DECLINED;
810   }
811   close(fd);
812
813   sprintf(filelink,"%s/%s/%d", bandwidth_data_dir, LINK_DIR, getpid());
814   if (link(masterfile, filelink) < 0) {
815      ap_log_printf(r->server, "mod_bandwidth : Can't create hard link %s", filelink);
816      return DECLINED;
817   }
818
819   f = ap_pfopen(r->pool, r->filename,"r");
820
821   if (f == NULL) {
822      ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
823                 "file permissions deny server access: %s", r->filename);
824      return FORBIDDEN;
825   }
826
827   /*
828    * Calculate bandwidth for this file based on location limits and
829    * file size limit.
830    *
831    * The following rules applies :
832    * 1) File size limit over-rule location limit if it's slower.
833    * 2) Whatever the resuling limit and number of connections, never
834    *    go bellow the minimal limit for this location.
835    * 3) A file size limit of zero mean that we don't care about the
836    *    size of the file for this purpose.
837    *
838    */
839
840 #ifdef BWDEBUG
841   ap_log_printf(r->server, "mod_bandwidth : Directory : %s Rate : %d Minimum : %d Size rate : %d", directory, bw_rate, bw_min, bw_f_rate);
842 #endif
843
844   if (bw_f_rate && (bw_rate > bw_f_rate || !bw_rate))
845       bw_rate=bw_f_rate;
846
847   if (bw_min < 0)
848      bw_min=bw_rate;
849   else if (! bw_min)
850      bw_min=MIN_BW_DEFAULT;
851
852 #ifdef USE_MMAP_FILES
853   ap_block_alarms();
854   if ((r->finfo.st_size >= MMAP_THRESHOLD)
855       && ( !r->header_only)) {
856     /* we need to protect ourselves in case we die while we've got the
857        * file mmapped */
858       mm = mmap (NULL, r->finfo.st_size, PROT_READ, MAP_PRIVATE,
859                   fileno(f), 0);
860   } else {
861       mm = (caddr_t)-1;
862   }
863
864   if (mm == (caddr_t)-1) {
865       ap_unblock_alarms();
866
867       if (r->finfo.st_size >= MMAP_THRESHOLD) {
868           ap_log_error(APLOG_MARK, APLOG_CRIT, r->server,
869                       "mmap_handler: mmap failed: %s", r->filename);
870       }
871 #endif
872
873   rangestatus = ap_set_byterange(r);
874   ap_send_http_header(r);
875  
876   if (!r->header_only) {
877       if (!rangestatus) {
878           filelength=r->finfo.st_size;
879           bytessent=0;
880           bwlast=0;
881           while(!bwlast) {
882
883              cur_rate=(long int)bw_rate / current_connection(masterfile);
884              if (cur_rate < bw_min)
885                 cur_rate=bw_min;
886
887              if (BWPulse <= 0) {
888                 /*
889                  * I feel rather foolish to use select and use the 
890                  * 1/1000000 of sec for my calculation. But as long as I
891                  * am at it, let's do it well...
892                  *
893                  * Note that my loop don't take into account the time
894                  * spent outside the send_fd_length function... but in
895                  * this case, I feel that I have the moral right to do so :)
896                  */
897
898                 opt_time.tv_sec=(long int) PACKET / cur_rate;
899                 opt_time.tv_usec=(long int)PACKET*1000000/cur_rate-opt_time.tv_sec*1000000;
900
901                 tosend=PACKET;
902                 if (tosend+bytessent >= filelength) {
903                    tosend=filelength-bytessent;
904                    bwlast=1;
905                 }
906              } else {
907                 opt_time.tv_sec=(long int)BWPulse/1000000;
908                 opt_time.tv_usec=BWPulse-opt_time.tv_sec*1000000;
909
910                 tosend=(long int)((double)BWPulse/(double)1000000*(double)cur_rate);
911                 if (tosend+bytessent >= filelength) {
912                    tosend=filelength-bytessent;
913                    bwlast=1;
914                 }
915              }  
916 #ifdef BWDEBUG
917              ap_log_printf(r->server, "mod_bandwidth : Current rate : %d [%d/%d] Min : %d",
918                 cur_rate, bw_rate, current_connection(masterfile), bw_min);
919 #endif
920
921              gettimeofday(&last_time, (struct timezone *) 0);
922
923              ap_send_fd_length(f, r, tosend);
924              bytessent += tosend;
925              if (r->connection->aborted)
926                 break;
927              if (!bwlast) {
928                 /* We sleep... */
929                 gettimeofday(&now, (struct timezone *) 0);
930                 timespent=timediff(&now, &last_time);
931                 timeout=timediff(&opt_time, &timespent);
932
933 #ifdef BWDEBUG
934                     ap_log_printf(r->server, "mod_bandwidth : Sleep : %ld/%ld (Op time : %ld/%ld Spent : %ld/%ld)",
935                       timeout.tv_sec, timeout.tv_usec, opt_time.tv_sec, opt_time.tv_usec,
936                       timespent.tv_sec, timespent.tv_usec);
937 #endif
938
939                 select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout);
940              }
941           }
942       } else {
943           long offset, length;
944           while (ap_each_byterange(r, &offset, &length)) {
945               fseek(f, offset, SEEK_SET);
946
947               filelength=length;
948               bytessent=0;
949               bwlast=0;
950               while(!bwlast) {
951
952                  cur_rate=(long int)bw_rate / current_connection(masterfile);
953                  if (cur_rate < bw_min)
954                     cur_rate=bw_min;
955
956                  if (BWPulse <= 0) {
957                     opt_time.tv_sec=(long int) PACKET / cur_rate;
958                     opt_time.tv_usec=(long int)PACKET*1000000/cur_rate-opt_time.tv_sec*1000000;
959
960                     tosend=PACKET;
961                     if (tosend+bytessent >= filelength) {
962                        tosend=filelength-bytessent;
963                        bwlast=1;
964                     }
965                  } else {
966                     opt_time.tv_sec=(long int)BWPulse/1000000;
967                     opt_time.tv_usec=BWPulse-opt_time.tv_sec*1000000;
968   
969                     tosend=(long int)((double)BWPulse/(double)1000000*(double)cur_rate);
970                     if (tosend+bytessent >= filelength) {
971                        tosend=filelength-bytessent;
972                        bwlast=1;
973                     }
974                  }  
975
976 #ifdef BWDEBUG
977                  ap_log_printf(r->server, "mod_bandwidth : Current rate : %d [%d/%d] Min : %d",
978                    cur_rate, bw_rate, current_connection(masterfile), bw_min);
979 #endif 
980                  gettimeofday(&last_time, (struct timezone *) 0);
981    
982                  ap_send_fd_length(f, r, tosend);
983                  bytessent += tosend;
984                  if (r->connection->aborted)
985                     break;
986                  if (!bwlast) {
987                     /* We sleep... */
988                     gettimeofday(&now, (struct timezone *) 0);
989                     timespent=timediff(&now, &last_time);
990                     timeout=timediff(&opt_time, &timespent);
991
992 #ifdef BWDEBUG
993                     ap_log_printf(r->server, "mod_bandwidth : Sleep : %ld/%ld (Op time : %ld/%ld Spent : %ld/%ld)", 
994                       timeout.tv_sec, timeout.tv_usec, opt_time.tv_sec, opt_time.tv_usec,
995                       timespent.tv_sec, timespent.tv_usec);
996 #endif
997                     select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout);
998                  }
999               }
1000           }
1001       }
1002   }
1003
1004 #ifdef USE_MMAP_FILES
1005     } else {
1006         struct mmap *mmd;
1007
1008         mmd = ap_palloc (r->pool, sizeof (*mmd));
1009         mmd->mm = mm;
1010         mmd->length = r->finfo.st_size;
1011         ap_register_cleanup (r->pool, (void *)mmd, mmap_cleanup, mmap_cleanup);
1012         ap_unblock_alarms();
1013
1014         rangestatus = ap_set_byterange(r);
1015         ap_send_http_header (r);
1016
1017         if (!r->header_only) {
1018            if (!rangestatus) {
1019               filelength=r->finfo.st_size;
1020               bytessent=0;
1021               bwlast=0;
1022               while(!bwlast) {
1023
1024                 cur_rate=(long int)bw_rate / current_connection(masterfile);
1025                 if (cur_rate < bw_min)
1026                    cur_rate=bw_min;
1027
1028                 if (BWPulse <= 0) {
1029                    /*
1030                     * I feel rather foolish to use select and use the 
1031                     * 1/1000000 of sec for my calculation. But as long as I
1032                     * am at it, let's do it well...
1033                     *
1034                     * Note that my loop don't take into account the time
1035                     * spent outside the send_fd_length function... but in
1036                     * this case, I feel that I have the moral right to do so :)
1037                     */
1038
1039                    opt_time.tv_sec=(long int) PACKET / cur_rate;
1040                    opt_time.tv_usec=(long int)PACKET*1000000/cur_rate-opt_time.tv_sec*1000000;
1041
1042                    tosend=PACKET;
1043                    if (tosend+bytessent >= filelength) {
1044                       tosend=filelength-bytessent;
1045                       bwlast=1;
1046                    }
1047                 } else {
1048                    opt_time.tv_sec=(long int)BWPulse/1000000;
1049                    opt_time.tv_usec=BWPulse-opt_time.tv_sec*1000000;
1050
1051                    tosend=(long int)((double)BWPulse/(double)1000000*(double)cur_rate);
1052                    if (tosend+bytessent >= filelength) {
1053                       tosend=filelength-bytessent;
1054                       bwlast=1;
1055                    }
1056                 }
1057
1058 #ifdef BWDEBUG
1059              ap_log_printf(r->server, "mod_bandwidth : Current rate : %d [%d/%d] Min : %d Sending : %d",
1060                 cur_rate, bw_rate, current_connection(masterfile), bw_min, tosend);
1061 #endif
1062                 gettimeofday(&last_time, (struct timezone *) 0);
1063
1064                 ap_send_mmap(mm, r, bytessent, tosend);
1065                 bytessent += tosend;
1066                 if (r->connection->aborted)
1067                    break;
1068                 if (!bwlast) {
1069                    /* We sleep... */
1070                    gettimeofday(&now, (struct timezone *) 0);
1071                    timespent=timediff(&now, &last_time);
1072                    timeout=timediff(&opt_time, &timespent);
1073
1074 #ifdef BWDEBUG
1075                     ap_log_printf(r->server, "mod_bandwidth : Sleep : %ld/%ld (Op time : %ld/%ld Spent : %ld/%ld)",
1076                       timeout.tv_sec, timeout.tv_usec, opt_time.tv_sec, opt_time.tv_usec,
1077                       timespent.tv_sec, timespent.tv_usec);
1078 #endif
1079                    select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout);
1080                 }
1081              }
1082          } else {
1083              long offset, length;
1084              while (ap_each_byterange(r, &offset, &length)) {
1085
1086                 filelength=length;
1087                 bytessent=0;
1088                 bwlast=0;
1089                 while(!bwlast) {
1090
1091                    cur_rate=(long int)bw_rate / current_connection(masterfile);
1092                    if (cur_rate < bw_min)
1093                       cur_rate=bw_min;
1094
1095                    if (BWPulse <= 0) { 
1096                       opt_time.tv_sec=(long int) PACKET / cur_rate;
1097                       opt_time.tv_usec=(long int)PACKET*1000000/cur_rate-opt_time.tv_sec*1000000;
1098    
1099                       tosend=PACKET;
1100                       if (tosend+bytessent >= filelength) {
1101                          tosend=filelength-bytessent;
1102                          bwlast=1;
1103                       }
1104                    } else {
1105                       opt_time.tv_sec=(long int)BWPulse/1000000;
1106                       opt_time.tv_usec=BWPulse-opt_time.tv_sec*1000000;
1107
1108                       tosend=(long int)((double)BWPulse/(double)1000000*(double)cur_rate);
1109                       if (tosend+bytessent >= filelength) {
1110                          tosend=filelength-bytessent;
1111                          bwlast=1;
1112                       }
1113                    }  
1114
1115 #ifdef BWDEBUG
1116                  ap_log_printf(r->server, "mod_bandwidth : Current rate : %d [%d/%d] Min : %d Sending : $d",
1117                    cur_rate, bw_rate, current_connection(masterfile), bw_min , tosend);
1118 #endif 
1119                    gettimeofday(&last_time, (struct timezone *) 0);
1120    
1121                    ap_send_mmap(mm, r, offset+bytessent, tosend);
1122                    bytessent += tosend;
1123                    if (r->connection->aborted)
1124                       break;
1125                    if (!bwlast) {
1126                       /* We sleep... */
1127                       gettimeofday(&now, (struct timezone *) 0);
1128                       timespent=timediff(&now, &last_time);
1129                       timeout=timediff(&opt_time, &timespent);
1130
1131 #ifdef BWDEBUG
1132                     ap_log_printf(r->server, "mod_bandwidth : Sleep : %ld/%ld (Op time : %ld/%ld Spent : %ld/%ld)", 
1133                       timeout.tv_sec, timeout.tv_usec, opt_time.tv_sec, opt_time.tv_usec,
1134                       timespent.tv_sec, timespent.tv_usec);
1135 #endif
1136                       select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &timeout);
1137                    }
1138                 }
1139             }
1140         }
1141     }
1142     }
1143 #endif
1144
1145     ap_pfclose(r->pool, f);
1146
1147     /* Remove the hardlink */
1148      unlink(filelink);
1149
1150     return OK;
1151 }
1152
1153 static handler_rec bw_handlers[] = {
1154 { "*/*", handle_bw },
1155 { NULL }
1156 };
1157
1158 module bandwidth_module = {
1159    STANDARD_MODULE_STUFF,
1160    NULL,                        /* initializer */
1161    create_bw_config,            /* bw config creater */
1162    NULL,                        /* bw merger --- default is to override */
1163    create_bw_server_config,     /* server config */
1164    NULL,                        /* merge server config */
1165    bw_cmds,                     /* command table */
1166    bw_handlers,                 /* handlers */
1167    NULL,                        /* filename translation */
1168    NULL,                        /* check_user_id */
1169    NULL,                        /* check auth */
1170    NULL,                        /* check access */
1171    NULL,                        /* type_checker */
1172    NULL,                        /* fixups */
1173    NULL                         /* logger */
1174 };
This page took 0.283923 seconds and 3 git commands to generate.