]> git.pld-linux.org Git - packages/apache-mod_bw.git/blame - mod_bandwidth.c
- mv apache-mod_bandwitdh.spec apache-mod_bandwidth.spec (typo - #636)
[packages/apache-mod_bw.git] / mod_bandwidth.c
CommitLineData
15c42696
AM
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 */
413typedef struct {
414 char *from;
415 long rate;
416} bw_entry;
417
418/* Limits for bandwidth based on file size */
419typedef struct {
420 long size;
421 long rate;
422} bw_sizel;
423
424/* Per directory configuration structure */
425typedef 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 */
433typedef struct {
434 int state;
435} bandwidth_server_config;
436
437int bw_handler (request_rec *);
438
439module bandwidth_module;
440
441static long int BWPulse=0;
442
443char bandwidth_data_dir[MAX_STRING_LEN]="/tmp/apachebw";
444
445/***************************************************************************
446 * Configuration functions *
447 ***************************************************************************/
448
449static 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
459static 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
473static 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
482static 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
492static 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
508static 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
527static 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
543static 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
572static 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
593struct mmap {
594 void *mm;
595 size_t length;
596};
597
598static void mmap_cleanup (void *mmv)
599{
600 struct mmap *mmd = mmv;
601
602 munmap(mmd->mm, mmd->length);
603}
604#endif
605
606static 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
624static 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
649static int is_ip(const char *host)
650{
651 while ((*host == '.') || (*host=='/') || isdigit(*host))
652 host++;
653 return (*host == '\0');
654}
655
656static 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
677static 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
698static 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
716static 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
737static 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
1153static handler_rec bw_handlers[] = {
1154{ "*/*", handle_bw },
1155{ NULL }
1156};
1157
1158module 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.189902 seconds and 4 git commands to generate.