]> git.pld-linux.org Git - packages/apache-mod_bw.git/blame - mod_bandwidth.c
- spaces->tabs
[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)
4aa7b840
AM
57 * Date : 12 January 2003
58 * Version : 2.0.5 for Apache 1.3+
15c42696
AM
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> )
4aa7b840
AM
82 * 01/12/03 - Add MaxConnection directive to limit the number
83 * of simultaneous connections.
15c42696
AM
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 *
4aa7b840
AM
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 *
15c42696
AM
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
80596fdf
AM
390#define MASTER_DIR "master"
391#define LINK_DIR "link"
15c42696
AM
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 */
425typedef struct {
426 char *from;
427 long rate;
428} bw_entry;
429
430/* Limits for bandwidth based on file size */
431typedef struct {
432 long size;
433 long rate;
434} bw_sizel;
435
436/* Per directory configuration structure */
437typedef struct {
438 array_header *limits;
439 array_header *minlimits;
440 array_header *sizelimits;
4aa7b840 441 int maxconnection;
15c42696
AM
442 char *directory;
443} bandwidth_config;
444
445/* Per server configuration structure */
446typedef struct {
447 int state;
448} bandwidth_server_config;
449
450int bw_handler (request_rec *);
451
452module bandwidth_module;
453
454static long int BWPulse=0;
455
456char bandwidth_data_dir[MAX_STRING_LEN]="/tmp/apachebw";
457
458/***************************************************************************
459 * Configuration functions *
460 ***************************************************************************/
461
462static 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);
4aa7b840 469 new->maxconnection=0;
15c42696
AM
470 return (void *)new;
471}
472
473static 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
487static 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
496static 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
506static 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)
4aa7b840 515 return "Pulse must be a number of microseconds/s";
15c42696
AM
516
517 BWPulse=temp;
518
519 return NULL;
520}
521
4aa7b840
AM
522static 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
15c42696
AM
539static 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
558static 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
574static 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
603static 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)" },
4aa7b840
AM
610{ "MaxConnection", MaxConnection, NULL, RSRC_CONF | OR_LIMIT, TAKE1,
611 "A number of allowed simultaneous connections" },
15c42696
AM
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
626struct mmap {
627 void *mm;
628 size_t length;
629};
630
631static void mmap_cleanup (void *mmv)
632{
633 struct mmap *mmd = mmv;
634
635 munmap(mmd->mm, mmd->length);
636}
637#endif
638
639static 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
657static 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
682static int is_ip(const char *host)
683{
684 while ((*host == '.') || (*host=='/') || isdigit(*host))
685 host++;
686 return (*host == '\0');
687}
688
689static 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
710static 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
731static 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
749static 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
770static 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;
4aa7b840 778 int nolimit=0, bwlast=0, fd;
15c42696
AM
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
4aa7b840
AM
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 }
15c42696
AM
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
60ab4c72 838 if (stat(directory, &fdata) < 0) {
15c42696
AM
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
4aa7b840
AM
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
15c42696
AM
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);
4aa7b840 873 unlink(filelink);
15c42696
AM
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
4aa7b840
AM
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 }
15c42696
AM
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
4aa7b840
AM
964 if (nolimit)
965 tosend=filelength;
966 else
967 tosend=(long int)((double)BWPulse/(double)1000000*(double)cur_rate);
15c42696
AM
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) {
4aa7b840
AM
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 }
15c42696
AM
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;
4aa7b840
AM
1030
1031 if (nolimit)
1032 tosend=filelength;
1033 else
1034 tosend=(long int)((double)BWPulse/(double)1000000*(double)cur_rate);
15c42696
AM
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
4aa7b840
AM
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 }
15c42696
AM
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
4aa7b840
AM
1121 if (nolimit)
1122 tosend=filelength;
1123 else
1124 tosend=(long int)((double)BWPulse/(double)1000000*(double)cur_rate);
15c42696
AM
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) {
4aa7b840
AM
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 }
15c42696
AM
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
4aa7b840
AM
1186 if (nolimit)
1187 tosend=filelength;
1188 else
1189 tosend=(long int)((double)BWPulse/(double)1000000*(double)cur_rate);
15c42696
AM
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
1234static handler_rec bw_handlers[] = {
1235{ "*/*", handle_bw },
1236{ NULL }
1237};
1238
1239module 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.345294 seconds and 4 git commands to generate.