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