1 /* $MirOS: src/usr.sbin/httpd/src/modules/proxy/proxy_util.c,v 1.8 2014/03/29 00:28:13 tg Exp $ */
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" must
29 * not be used to endorse or promote products derived from this
30 * software without prior written permission. For written
31 * permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * nor may "Apache" appear in their name, without prior written
35 * permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 *
56 * Portions of this software are based upon public domain software
57 * originally written at the National Center for Supercomputing Applications,
58 * University of Illinois, Urbana-Champaign.
59 */
60
61 /* Utility routines for Apache proxy */
62 #include "mod_proxy.h"
63 #include "http_main.h"
64 #include "ap_md5.h"
65 #include "multithread.h"
66 #include "http_log.h"
67 #include "util_uri.h"
68 #include "util_date.h" /* get ap_checkmask() decl. */
69 #include "sa_len.h"
70
71 __RCSID("$MirOS: src/usr.sbin/httpd/src/modules/proxy/proxy_util.c,v 1.8 2014/03/29 00:28:13 tg Exp $");
72
73 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
74 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
75 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
76 static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
77 static struct per_thread_data *get_per_thread_data(void);
78 /* already called in the knowledge that the characters are hex digits */
ap_proxy_hex2c(const char * x)79 int ap_proxy_hex2c(const char *x)
80 {
81 int i;
82 int ch;
83
84 ch = x[0];
85 if (isdigit((unsigned char)ch))
86 i = ch - '0';
87 else if (ap_isupper(ch))
88 i = ch - ('A' - 10);
89 else
90 i = ch - ('a' - 10);
91 i <<= 4;
92
93 ch = x[1];
94 if (isdigit((unsigned char)ch))
95 i += ch - '0';
96 else if (ap_isupper(ch))
97 i += ch - ('A' - 10);
98 else
99 i += ch - ('a' - 10);
100 return i;
101 }
102
ap_proxy_c2hex(int ch,char * x)103 void ap_proxy_c2hex(int ch, char *x)
104 {
105 int i;
106
107 x[0] = '%';
108 i = (ch & 0xF0) >> 4;
109 if (i >= 10)
110 x[1] = ('A' - 10) + i;
111 else
112 x[1] = '0' + i;
113
114 i = ch & 0x0F;
115 if (i >= 10)
116 x[2] = ('A' - 10) + i;
117 else
118 x[2] = '0' + i;
119 }
120
121 /*
122 * canonicalise a URL-encoded string
123 */
124
125 /*
126 * Convert a URL-encoded string to canonical form.
127 * It decodes characters which need not be encoded,
128 * and encodes those which must be encoded, and does not touch
129 * those which must not be touched.
130 */
ap_proxy_canonenc(pool * p,const char * x,int len,enum enctype t,enum proxyreqtype isenc)131 char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t,
132 enum proxyreqtype isenc)
133 {
134 int i, j, ch;
135 char *y;
136 const char *allowed; /* characters which should not be encoded */
137 const char *reserved; /* characters which much not be en/de-coded */
138
139 /* N.B. in addition to :@&=, this allows ';' in an http path
140 * and '?' in an ftp path -- this may be revised
141 *
142 * Also, it makes a '+' character in a search string reserved, as
143 * it may be form-encoded. (Although RFC 1738 doesn't allow this -
144 * it only permits ; / ? : @ = & as reserved chars.)
145 */
146 if (t == enc_path)
147 allowed = "$-_.+!*'(),;:@&=";
148 else if (t == enc_search)
149 allowed = "$-_.!*'(),;:@&=";
150 else if (t == enc_user)
151 allowed = "$-_.+!*'(),;@&=";
152 else if (t == enc_fpath)
153 allowed = "$-_.+!*'(),?:@&=";
154 else /* if (t == enc_parm) */
155 allowed = "$-_.+!*'(),?/:@&=";
156
157 if (t == enc_path)
158 reserved = "/";
159 else if (t == enc_search)
160 reserved = "+";
161 else
162 reserved = "";
163
164 y = ap_palloc(p, 3 * len + 1);
165
166 for (i = 0, j = 0; i < len; i++, j++) {
167 /* always handle '/' first */
168 ch = x[i];
169 if (strchr(reserved, ch)) {
170 y[j] = ch;
171 continue;
172 }
173 /* decode it if not already done */
174 if (isenc != NOT_PROXY && ch == '%') {
175 if (!ap_isxdigit(x[i + 1]) || !ap_isxdigit(x[i + 2]))
176 return NULL;
177 ch = ap_proxy_hex2c(&x[i + 1]);
178 i += 2;
179 if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */
180 ap_proxy_c2hex(ch, &y[j]);
181 j += 2;
182 continue;
183 }
184 }
185 /* recode it, if necessary */
186 if (!ap_isalnum(ch) && !strchr(allowed, ch)) {
187 ap_proxy_c2hex(ch, &y[j]);
188 j += 2;
189 }
190 else
191 y[j] = ch;
192 }
193 y[j] = '\0';
194 return y;
195 }
196
197 /*
198 * Parses network-location.
199 * urlp on input the URL; on output the path, after the leading /
200 * user NULL if no user/password permitted
201 * password holder for password
202 * host holder for host
203 * port port number; only set if one is supplied.
204 *
205 * Returns an error string.
206 */
207 char *
ap_proxy_canon_netloc(pool * p,char ** const urlp,char ** userp,char ** passwordp,char ** hostp,int * port)208 ap_proxy_canon_netloc(pool *p, char **const urlp, char **userp,
209 char **passwordp, char **hostp, int *port)
210 {
211 int i;
212 char *strp, *host, *url = *urlp;
213 char *user = NULL, *password = NULL;
214 char *t = NULL, *u = NULL, *v = NULL;
215
216 if (url[0] != '/' || url[1] != '/')
217 return "Malformed URL";
218 host = url + 2;
219 url = strchr(host, '/');
220 if (url == NULL)
221 url = "";
222 else
223 *(url++) = '\0'; /* skip seperating '/' */
224
225 /* find _last_ '@' since it might occur in user/password part */
226 strp = strrchr(host, '@');
227
228 if (strp != NULL) {
229 *strp = '\0';
230 user = host;
231 host = strp + 1;
232
233 /* find password */
234 strp = strchr(user, ':');
235 if (strp != NULL) {
236 *strp = '\0';
237 password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, STD_PROXY);
238 if (password == NULL)
239 return "Bad %-escape in URL (password)";
240 }
241
242 user = ap_proxy_canonenc(p, user, strlen(user), enc_user, STD_PROXY);
243 if (user == NULL)
244 return "Bad %-escape in URL (username)";
245 }
246 if (userp != NULL) {
247 *userp = user;
248 }
249 if (passwordp != NULL) {
250 *passwordp = password;
251 }
252
253 v = host;
254 if (*host == '['){
255 u = strrchr(host, ']');
256 if (u){
257 host++;
258 *u = '\0';
259 v = u + 1;
260 }
261 }
262 t = strrchr(v, ':');
263 if (t){
264 *t = '\0';
265 strp = t + 1;
266 }
267 if (strp){
268 for (i=0; strp[i] != '\0'; i++)
269 if (!ap_isdigit(strp[i]))
270 break;
271
272 /* if (i == 0) the no port was given; keep default */
273 if (strp[i] != '\0') {
274 return "Bad port number in URL";
275 }
276 else if (i > 0) {
277 *port = atoi(strp);
278 if (*port > 65535)
279 return "Port number in URL > 65535";
280 }
281 }
282 ap_str_tolower(host); /* DNS names are case-insensitive */
283 if (*host == '\0')
284 return "Missing host in URL";
285 /* check hostname syntax */
286 for (i = 0; host[i] != '\0'; i++)
287 if (!ap_isxdigit(host[i]) && host[i] != '.' && host[i] != ':')
288 break;
289 /* must be an IP address */
290 if (host[i] == '\0') {
291 struct addrinfo hints, *res0;
292 int gai;
293 memset(&hints, 0, sizeof(hints));
294 hints.ai_family = PF_UNSPEC;
295 hints.ai_flags = AI_NUMERICHOST;
296 if ((gai = getaddrinfo(host, NULL, &hints, &res0))) {
297 #if 0
298 return gai_strerror(gai);
299 #else
300 return "Bad IP address in URL";
301 #endif
302 }
303 freeaddrinfo(res0);
304 }
305
306 /* if (strchr(host,'.') == NULL && domain != NULL)
307 host = pstrcat(p, host, domain, NULL);
308 */
309 *urlp = url;
310 *hostp = host;
311
312 return NULL;
313 }
314
315 static const char *const lwday[7] =
316 {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
317
318 /*
319 * If the date is a valid RFC 850 date or asctime() date, then it
320 * is converted to the RFC 1123 format, otherwise it is not modified.
321 * This routine is not very fast at doing conversions, as it uses
322 * sscanf and sprintf. However, if the date is already correctly
323 * formatted, then it exits very quickly.
324 */
325 const char *
ap_proxy_date_canon(pool * p,const char * x)326 ap_proxy_date_canon(pool *p, const char *x)
327 {
328 int wk, mday, year, hour, min, sec, mon;
329 char *q, month[4], zone[4], week[4];
330
331 q = strchr(x, ',');
332 /* check for RFC 850 date */
333 if (q != NULL && q - x > 3 && q[1] == ' ') {
334 *q = '\0';
335 for (wk = 0; wk < 7; wk++)
336 if (strcmp(x, lwday[wk]) == 0)
337 break;
338 *q = ',';
339 if (wk == 7)
340 return x; /* not a valid date */
341 if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
342 q[17] != ':' || strcmp(&q[20], " GMT") != 0)
343 return x;
344 if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
345 &hour, &min, &sec, zone) != 7)
346 return x;
347 if (year < 70)
348 year += 2000;
349 else
350 year += 1900;
351 }
352 else {
353 /* check for acstime() date */
354 if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
355 x[16] != ':' || x[19] != ' ' || x[24] != '\0')
356 return x;
357 if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
358 &min, &sec, &year) != 7)
359 return x;
360 for (wk = 0; wk < 7; wk++)
361 if (strcmp(week, ap_day_snames[wk]) == 0)
362 break;
363 if (wk == 7)
364 return x;
365 }
366
367 /* check date */
368 for (mon = 0; mon < 12; mon++)
369 if (strcmp(month, ap_month_snames[mon]) == 0)
370 break;
371 if (mon == 12)
372 return x;
373
374 q = ap_palloc(p, 30);
375 snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", ap_day_snames[wk], mday,
376 ap_month_snames[mon], year, hour, min, sec);
377 return q;
378 }
379
380
381 /*
382 * Reads headers from a buffer and returns an array of headers.
383 * Returns NULL on file error
384 * This routine tries to deal with too long lines and continuation lines.
385 *
386 * Note: Currently the headers are passed through unmerged. This has to be
387 * done so that headers which react badly to merging (such as Set-Cookie
388 * headers, which contain commas within the date field) do not get stuffed
389 * up.
390 */
ap_proxy_read_headers(request_rec * r,char * buffer,int size,BUFF * f)391 table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f)
392 {
393 table *resp_hdrs;
394 int len;
395 char *value, *end;
396 char field[MAX_STRING_LEN];
397
398 resp_hdrs = ap_make_table(r->pool, 20);
399
400 /*
401 * Read header lines until we get the empty separator line, a read error,
402 * the connection closes (EOF), or we timeout.
403 */
404 while ((len = ap_getline(buffer, size, f, 1)) > 0) {
405
406 if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */
407
408 /*
409 * Buggy MS IIS servers sometimes return invalid headers (an
410 * extra "HTTP/1.0 200, OK" line sprinkled in between the usual
411 * MIME headers). Try to deal with it in a sensible way, but log
412 * the fact. XXX: The mask check is buggy if we ever see an
413 * HTTP/1.10
414 */
415
416 if (!ap_checkmask(buffer, "HTTP/#.# ###*")) {
417 /* Nope, it wasn't even an extra HTTP header. Give up. */
418 return NULL;
419 }
420
421 ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, r->server,
422 "proxy: Ignoring duplicate HTTP status line "
423 "returned by buggy server %s (%s)", r->uri, r->method);
424 continue;
425 }
426
427 *value = '\0';
428 ++value;
429 /*
430 * XXX: RFC2068 defines only SP and HT as whitespace, this test is
431 * wrong... and so are many others probably.
432 */
433 while (ap_isspace(*value))
434 ++value; /* Skip to start of value */
435
436 /* should strip trailing whitespace as well */
437 for (end = &value[strlen(value) - 1]; end > value && ap_isspace(*end); --end)
438 *end = '\0';
439
440 /* make sure we add so as not to destroy duplicated headers */
441 ap_table_add(resp_hdrs, buffer, value);
442
443 /* the header was too long; at the least we should skip extra data */
444 if (len >= size - 1) {
445 while ((len = ap_getline(field, MAX_STRING_LEN, f, 1))
446 >= MAX_STRING_LEN - 1) {
447 /* soak up the extra data */
448 }
449 if (len == 0) /* time to exit the larger loop as well */
450 break;
451 }
452 }
453 return resp_hdrs;
454 }
455
456 /* read data from (socket BUFF*) f, write it to:
457 * - c->fp, if it is open
458 * - r->connection->client, if nowrite == 0
459 */
460
ap_proxy_send_fb(BUFF * f,request_rec * r,cache_req * c,off_t len,int nowrite,int chunked,size_t recv_buffer_size)461 long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c, off_t len, int nowrite, int chunked, size_t recv_buffer_size)
462 {
463 int ok, end_of_chunk;
464 char *buf;
465 size_t buf_size;
466 long remaining = 0;
467 long total_bytes_rcvd;
468 register int n = 0, o, w;
469 conn_rec *con = r->connection;
470 int alternate_timeouts = 1; /* 1 if we alternate between soft & hard
471 * timeouts */
472
473 /* allocate a buffer to store the bytes in */
474 /*
475 * make sure it is at least IOBUFSIZE, as recv_buffer_size may be zero
476 * for system default
477 */
478 buf_size = MAX(recv_buffer_size, IOBUFSIZE);
479 buf = ap_palloc(r->pool, buf_size);
480
481 total_bytes_rcvd = 0;
482 if (c != NULL)
483 c->written = 0;
484
485 /*
486 * Since we are reading from one buffer and writing to another, it is
487 * unsafe to do a soft_timeout here, at least until the proxy has its own
488 * timeout handler which can set both buffers to EOUT.
489 */
490
491 ap_kill_timeout(r);
492
493 /*
494 * CHECKME! Since hard_timeout won't work in unix on sends with partial
495 * cache completion, we have to alternate between hard_timeout for reads,
496 * and soft_timeout for send. This is because we need to get a return
497 * from ap_bwrite to be able to continue caching. BUT, if we *can't*
498 * continue anyway, just use hard_timeout. (Also, if no cache file is
499 * written, use hard timeouts)
500 */
501
502 if (c == NULL || c->len <= 0 || c->cache_completion == 1.0) {
503 ap_hard_timeout("proxy send body", r);
504 alternate_timeouts = 0;
505 }
506
507 /*
508 * Loop and ap_bread() while we can successfully read and write, or
509 * (after the client aborted) while we can successfully read and finish
510 * the configured cache_completion.
511 */
512 for (end_of_chunk = ok = 1; ok;) {
513 if (alternate_timeouts)
514 ap_hard_timeout("proxy recv body from upstream server", r);
515
516
517 /* read a chunked block */
518 if (chunked) {
519 long chunk_start = 0;
520 n = 0;
521
522 /* start of a new chunk */
523 if (end_of_chunk) {
524 end_of_chunk = 0;
525 /* get the chunk size from the stream */
526 chunk_start = ap_getline(buf, buf_size, f, 0);
527 if ((chunk_start <= 0) || ((size_t)chunk_start + 1 >= buf_size) || !ap_isxdigit(*buf)) {
528 n = -1;
529 }
530 /* parse the chunk size */
531 else {
532 remaining = ap_get_chunk_size(buf);
533 if (remaining == 0) { /* Last chunk indicated, get footers */
534 /* as we are a proxy, we discard the footers, as the headers
535 * have already been sent at this point.
536 */
537 if (NULL == ap_proxy_read_headers(r, buf, buf_size, f)) {
538 n = -1;
539 }
540 }
541 else if (remaining < 0) {
542 n = -1;
543 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r,
544 "proxy: remote protocol error, invalid chunk size");
545
546 }
547 }
548 }
549
550 /* read the chunk */
551 if (remaining > 0) {
552 n = ap_bread(f, buf, MIN((int)buf_size, (int)remaining));
553 if (n > -1) {
554 remaining -= n;
555 end_of_chunk = (remaining == 0);
556 }
557 }
558
559 /* soak up trailing CRLF */
560 if (end_of_chunk) {
561 int ch; /* int because it may hold an EOF */
562 /*
563 * For EBCDIC, the proxy has configured the BUFF layer to
564 * transparently pass the ascii characters thru (also writing
565 * an ASCII copy to the cache, where appropriate).
566 * Therefore, we see here an ASCII-CRLF (\015\012),
567 * not an EBCDIC-CRLF (\r\n).
568 */
569 if ((ch = ap_bgetc(f)) == EOF) {
570 /* Protocol error: EOF detected within chunk */
571 n = -1;
572 ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r,
573 "proxy: remote protocol error, eof while reading chunked from proxy");
574 }
575 else
576 {
577 if (ch == '\015') { /* _ASCII_ CR */
578 ch = ap_bgetc(f);
579 }
580 if (ch != '\012') {
581 n = -1;
582 }
583 }
584 }
585 }
586
587 /* otherwise read block normally */
588 else {
589 if (-1 == len) {
590 n = ap_bread(f, buf, buf_size);
591 }
592 else {
593 n = ap_bread(f, buf, MIN((int)buf_size,
594 (int)(len - total_bytes_rcvd)));
595 }
596 }
597
598
599 if (alternate_timeouts)
600 ap_kill_timeout(r);
601 else
602 ap_reset_timeout(r);
603
604 if (n == -1) { /* input error */
605 if (c != NULL) {
606 ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
607 "proxy: error reading from %s", c->url);
608 c = ap_proxy_cache_error(c);
609 }
610 break;
611 }
612 if (n == 0)
613 break; /* EOF */
614 o = 0;
615 total_bytes_rcvd += n;
616
617 /* if we've received everything... */
618 /*
619 * in the case of slow frontends and expensive backends, we want to
620 * avoid leaving a backend connection hanging while the frontend
621 * takes it's time to absorb the bytes. so: if we just read the last
622 * block, we close the backend connection now instead of later - it's
623 * no longer needed.
624 */
625 if (total_bytes_rcvd == len) {
626 ap_bclose(f);
627 f = NULL;
628 }
629
630 /* Write to cache first. */
631 /*
632 * @@@ XXX FIXME: Assuming that writing the cache file won't time
633 * out?!!?
634 */
635 if (c != NULL && c->fp != NULL) {
636 if (ap_bwrite(c->fp, &buf[0], n) != n) {
637 ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
638 "proxy: error writing to %s", c->tempfile);
639 c = ap_proxy_cache_error(c);
640 }
641 else {
642 c->written += n;
643 }
644 }
645
646 /* Write the block to the client, detect aborted transfers */
647 while (!nowrite && !con->aborted && n > 0) {
648 if (alternate_timeouts)
649 ap_soft_timeout("proxy send body", r);
650
651 w = ap_bwrite(con->client, &buf[o], n);
652
653 if (alternate_timeouts)
654 ap_kill_timeout(r);
655 else
656 ap_reset_timeout(r);
657
658 if (w <= 0) {
659 if (c != NULL) {
660 /*
661 * when a send failure occurs, we need to decide whether
662 * to continue loading and caching the document, or to
663 * abort the whole thing
664 */
665 ok = (c->len > 0) &&
666 (c->cache_completion > 0) &&
667 (c->len * c->cache_completion < total_bytes_rcvd);
668
669 if (!ok) {
670 if (c->fp != NULL) {
671 ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));
672 c->fp = NULL;
673 }
674 unlink(c->tempfile);
675 c = NULL;
676 }
677 }
678 con->aborted = 1;
679 break;
680 }
681 n -= w;
682 o += w;
683 } /* while client alive and more data to send */
684
685 /* if we've received everything, leave now */
686 if (total_bytes_rcvd == len)
687 break;
688
689 } /* loop and ap_bread while "ok" */
690
691 /* if the backend connection is still open, close it */
692 if (f) {
693 ap_bclose(f);
694 }
695
696 if (!con->aborted) {
697 ap_bflush(con->client);
698 }
699
700 ap_kill_timeout(r);
701
702 r->bytes_sent += total_bytes_rcvd;
703
704 return total_bytes_rcvd;
705 }
706
707 /*
708 * Writes response line and headers to the cache file.
709 *
710 * If respline is NULL, no response line will be written.
711 */
ap_proxy_write_headers(cache_req * c,const char * respline,table * t)712 void ap_proxy_write_headers(cache_req *c, const char *respline, table *t)
713 {
714 /* write status line */
715 if (respline && c->fp != NULL &&
716 ap_bvputs(c->fp, respline, CRLF, NULL) == -1) {
717 ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
718 "proxy: error writing status line to %s", c->tempfile);
719 c = ap_proxy_cache_error(c);
720 return;
721 }
722
723 /* write response headers to the cache file */
724 ap_table_do(ap_proxy_send_hdr_line, c, t, NULL);
725
726 /* write terminating CRLF */
727 if (c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) {
728 ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
729 "proxy: error writing CRLF to %s", c->tempfile);
730 c = ap_proxy_cache_error(c);
731 }
732 }
733
734
735 /*
736 * list is a comma-separated list of case-insensitive tokens, with
737 * optional whitespace around the tokens.
738 * The return returns 1 if the token val is found in the list, or 0
739 * otherwise.
740 */
ap_proxy_liststr(const char * list,const char * key,char ** val)741 int ap_proxy_liststr(const char *list, const char *key, char **val)
742 {
743 int len, i;
744 const char *p;
745 char valbuf[HUGE_STRING_LEN];
746 valbuf[sizeof(valbuf) - 1] = 0; /* safety terminating zero */
747
748 len = strlen(key);
749
750 while (list != NULL) {
751 p = strchr(list, ',');
752 if (p != NULL) {
753 i = p - list;
754 do
755 p++;
756 while (ap_isspace(*p));
757 }
758 else
759 i = strlen(list);
760
761 while (i > 0 && ap_isspace(list[i - 1]))
762 i--;
763 if (i == len && strncasecmp(list, key, len) == 0) {
764 if (val) {
765 p = strchr(list, ',');
766 while (ap_isspace(*list)) {
767 list++;
768 }
769 if ('=' == list[0])
770 list++;
771 while (ap_isspace(*list)) {
772 list++;
773 }
774 strncpy(valbuf, list, MIN(p - list, sizeof(valbuf) - 1));
775 *val = valbuf;
776 }
777 return 1;
778 }
779 list = p;
780 }
781 return 0;
782 }
783
ap_proxy_hash(const char * it,char * val,int ndepth,int nlength)784 void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength)
785 {
786 AP_MD5_CTX context;
787 unsigned char digest[16];
788 char tmp[22];
789 int i, k, d;
790 unsigned int x;
791 static const char enc_table[64] =
792 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
793
794 ap_MD5Init(&context);
795 ap_MD5Update(&context, (const unsigned char *)it, strlen(it));
796 ap_MD5Final(digest, &context);
797
798 /* encode 128 bits as 22 characters, using a modified uuencoding */
799 /* the encoding is 3 bytes -> 4 characters
800 * i.e. 128 bits is 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters
801 */
802 for (i = 0, k = 0; i < 15; i += 3) {
803 x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2];
804 tmp[k++] = enc_table[x >> 18];
805 tmp[k++] = enc_table[(x >> 12) & 0x3f];
806 tmp[k++] = enc_table[(x >> 6) & 0x3f];
807 tmp[k++] = enc_table[x & 0x3f];
808 }
809 /* one byte left */
810 x = digest[15];
811 tmp[k++] = enc_table[x >> 2]; /* use up 6 bits */
812 tmp[k++] = enc_table[(x << 4) & 0x3f];
813 /* now split into directory levels */
814
815 for (i = k = d = 0; d < ndepth; ++d) {
816 memcpy(&val[i], &tmp[k], nlength);
817 k += nlength;
818 val[i + nlength] = '/';
819 i += nlength + 1;
820 }
821 memcpy(&val[i], &tmp[k], 22 - k);
822 val[i + 22 - k] = '\0';
823 }
824
825 /*
826 * Converts 16 hex digits to a time integer
827 */
ap_proxy_hex2sec(const char * x)828 int ap_proxy_hex2sec(const char *x)
829 {
830 int i, ch;
831 unsigned int j;
832
833 for (i = 0, j = 0; i < 16; i++) {
834 ch = x[i];
835 j <<= 4;
836 if (ap_isdigit(ch))
837 j |= ch - '0';
838 else if (ap_isupper(ch))
839 j |= ch - ('A' - 10);
840 else
841 j |= ch - ('a' - 10);
842 }
843 /* no longer necessary, as the source hex is 8-byte int */
844 /* if (j == 0xffffffff)*/
845 /* return -1;*//* so that it works with 8-byte ints */
846 /* else */
847 return j;
848 }
849
850 /*
851 * Converts a time integer to 16 hex digits
852 */
ap_proxy_sec2hex(int t,char * y,int len)853 int ap_proxy_sec2hex(int t, char *y, int len)
854 {
855 int i, ch;
856 unsigned int j = t;
857
858 if (-1 == t) {
859 if (strlcpy(y, "FFFFFFFFFFFFFFFF", len) > len)
860 return (-1);
861 return (0);
862 }
863
864 if (len < 17)
865 return (-1);
866
867 for (i = 15; i >= 0; i--) {
868 ch = j & 0xF;
869 j >>= 4;
870 if (ch >= 10)
871 y[i] = ch + ('A' - 10);
872 else
873 y[i] = ch + '0';
874 }
875 y[16] = '\0';
876 return (0);
877 }
878
879
ap_proxy_cache_error(cache_req * c)880 cache_req *ap_proxy_cache_error(cache_req *c)
881 {
882 if (c != NULL) {
883 if (c->fp != NULL) {
884 ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));
885 c->fp = NULL;
886 }
887 if (c->origfp != NULL) {
888 ap_pclosef(c->req->pool, ap_bfileno(c->origfp, B_WR));
889 c->origfp = NULL;
890 }
891 if (c->tempfile)
892 unlink(c->tempfile);
893 }
894 return NULL;
895 }
896
ap_proxyerror(request_rec * r,int statuscode,const char * message)897 int ap_proxyerror(request_rec *r, int statuscode, const char *message)
898 {
899 ap_table_setn(r->notes, "error-notes",
900 ap_pstrcat(r->pool,
901 "The proxy server could not handle the request "
902 "<EM><A HREF=\"", ap_escape_uri(r->pool, r->uri),
903 "\">", ap_escape_html(r->pool, r->method),
904 " ",
905 ap_escape_html(r->pool, r->uri), "</A></EM>.<P>\n"
906 "Reason: <STRONG>",
907 ap_escape_html(r->pool, message),
908 "</STRONG>", NULL));
909
910 /* Allow "error-notes" string to be printed by ap_send_error_response() */
911 ap_table_setn(r->notes, "verbose-error-to", ap_pstrdup(r->pool, "*"));
912
913 r->status_line = ap_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
914 return statuscode;
915 }
916
917 /*
918 * This routine returns its own error message
919 */
920 const char *
ap_proxy_host2addr(const char * host,struct hostent * reqhp)921 ap_proxy_host2addr(const char *host, struct hostent * reqhp)
922 {
923 int i;
924 struct hostent *hp;
925 struct per_thread_data *ptd = get_per_thread_data();
926
927 for (i = 0; host[i] != '\0'; i++)
928 if (!isdigit((unsigned char)host[i]) && host[i] != '.')
929 break;
930
931 if (host[i] != '\0') {
932 hp = gethostbyname(host);
933 if (hp == NULL)
934 return "Host not found";
935 }
936 else {
937 ptd->ipaddr = ap_inet_addr(host);
938 hp = gethostbyaddr((char *)&ptd->ipaddr, sizeof(ptd->ipaddr), AF_INET);
939 if (hp == NULL) {
940 memset(&ptd->hpbuf, 0, sizeof(ptd->hpbuf));
941 ptd->hpbuf.h_name = 0;
942 ptd->hpbuf.h_addrtype = AF_INET;
943 ptd->hpbuf.h_length = sizeof(ptd->ipaddr);
944 ptd->hpbuf.h_addr_list = ptd->charpbuf;
945 ptd->hpbuf.h_addr_list[0] = (char *)&ptd->ipaddr;
946 ptd->hpbuf.h_addr_list[1] = 0;
947 hp = &ptd->hpbuf;
948 }
949 }
950 *reqhp = *hp;
951 return NULL;
952 }
953
954 static const char *
proxy_get_host_of_request(request_rec * r)955 proxy_get_host_of_request(request_rec *r)
956 {
957 char *url, *user = NULL, *password = NULL, *err, *host;
958 int port = -1;
959
960 if (r->hostname != NULL)
961 return r->hostname;
962
963 /* Set url to the first char after "scheme://" */
964 if ((url = strchr(r->uri, ':')) == NULL
965 || url[1] != '/' || url[2] != '/')
966 return NULL;
967
968 url = ap_pstrdup(r->pool, &url[1]); /* make it point to "//", which is
969 * what proxy_canon_netloc expects */
970
971 err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
972
973 if (err != NULL)
974 ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r,
975 "%s", err);
976
977 r->hostname = host;
978
979 return host; /* ought to return the port, too */
980 }
981
982 /* Return TRUE if addr represents an IP address (or an IP network address) */
ap_proxy_is_ipaddr(struct dirconn_entry * This,pool * p)983 int ap_proxy_is_ipaddr(struct dirconn_entry *This, pool *p)
984 {
985 const char *addr = This->name;
986 long ip_addr[4];
987 int i, quads;
988 long bits;
989
990 /* if the address is given with an explicit netmask, use that */
991 /* Due to a deficiency in ap_inet_addr(), it is impossible to parse */
992 /* "partial" addresses (with less than 4 quads) correctly, i.e. */
993 /* 192.168.123 is parsed as 192.168.0.123, which is not what I want. */
994 /* I therefore have to parse the IP address manually: */
995 /*
996 * if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr)
997 * == 0)
998 */
999 /* addr and mask were set by proxy_readmask() */
1000 /* return 1; */
1001
1002 /* Parse IP addr manually, optionally allowing */
1003 /* abbreviated net addresses like 192.168. */
1004
1005 /* Iterate over up to 4 (dotted) quads. */
1006 for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
1007 char *tmp;
1008
1009 if (*addr == '/' && quads > 0) /* netmask starts here. */
1010 break;
1011
1012 if (!isdigit((unsigned char)*addr))
1013 return 0; /* no digit at start of quad */
1014
1015 ip_addr[quads] = ap_strtol(addr, &tmp, 0);
1016
1017 if (tmp == addr) /* expected a digit, found something else */
1018 return 0;
1019
1020 if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
1021 /* invalid octet */
1022 return 0;
1023 }
1024
1025 addr = tmp;
1026
1027 if (*addr == '.' && quads != 3)
1028 ++addr; /* after the 4th quad, a dot would be illegal */
1029 }
1030
1031 for (This->addr.s_addr = 0, i = 0; i < quads; ++i)
1032 This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
1033
1034 if (addr[0] == '/' && isdigit((unsigned char)addr[1])) { /* net mask follows: */
1035 char *tmp;
1036
1037 ++addr;
1038
1039 bits = ap_strtol(addr, &tmp, 0);
1040
1041 if (tmp == addr) /* expected a digit, found something else */
1042 return 0;
1043
1044 addr = tmp;
1045
1046 if (bits < 0 || bits > 32) /* netmask must be between 0 and 32 */
1047 return 0;
1048
1049 }
1050 else {
1051 /* Determine (i.e., "guess") netmask by counting the */
1052 /* number of trailing .0's; reduce #quads appropriately */
1053 /* (so that 192.168.0.0 is equivalent to 192.168.) */
1054 while (quads > 0 && ip_addr[quads - 1] == 0)
1055 --quads;
1056
1057 /*
1058 * "IP Address should be given in dotted-quad form, optionally
1059 * followed by a netmask (e.g., 192.168.111.0/24)";
1060 */
1061 if (quads < 1)
1062 return 0;
1063
1064 /* every zero-byte counts as 8 zero-bits */
1065 bits = 8 * quads;
1066
1067 if (bits != 32) /* no warning for fully qualified IP address */
1068 fprintf(stderr, "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n",
1069 inet_ntoa(This->addr), bits);
1070 }
1071
1072 This->mask.s_addr = htonl(INADDR_NONE << (32 - bits));
1073
1074 if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
1075 fprintf(stderr, "Warning: NetMask and IP-Addr disagree in %s/%ld\n",
1076 inet_ntoa(This->addr), bits);
1077 This->addr.s_addr &= This->mask.s_addr;
1078 fprintf(stderr, " Set to %s/%ld\n",
1079 inet_ntoa(This->addr), bits);
1080 }
1081
1082 if (*addr == '\0') {
1083 This->matcher = proxy_match_ipaddr;
1084 return 1;
1085 }
1086 else
1087 return (*addr == '\0'); /* okay iff we've parsed the whole string */
1088 }
1089
1090 /* Return TRUE if addr represents an IP address (or an IP network address) */
proxy_match_ipaddr(struct dirconn_entry * This,request_rec * r)1091 static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
1092 {
1093 int i;
1094 int ip_addr[4];
1095 struct in_addr addr;
1096 struct in_addr *ip_list;
1097 char **ip_listptr;
1098 const char *found;
1099 const char *host = proxy_get_host_of_request(r);
1100
1101 if (host == NULL) /* oops! */
1102 return 0;
1103
1104 memset(&addr, '\0', sizeof addr);
1105 memset(ip_addr, '\0', sizeof ip_addr);
1106
1107 if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
1108 for (addr.s_addr = 0, i = 0; i < 4; ++i)
1109 addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
1110
1111 if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
1112 #if DEBUGGING
1113 fprintf(stderr, "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
1114 fprintf(stderr, "%s/", inet_ntoa(This->addr));
1115 fprintf(stderr, "%s\n", inet_ntoa(This->mask));
1116 #endif
1117 return 1;
1118 }
1119 #if DEBUGGING
1120 else {
1121 fprintf(stderr, "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
1122 fprintf(stderr, "%s/", inet_ntoa(This->addr));
1123 fprintf(stderr, "%s\n", inet_ntoa(This->mask));
1124 }
1125 #endif
1126 }
1127 else {
1128 struct hostent the_host;
1129
1130 memset(&the_host, '\0', sizeof the_host);
1131 found = ap_proxy_host2addr(host, &the_host);
1132
1133 if (found != NULL) {
1134 #if DEBUGGING
1135 fprintf(stderr, "2)IP-NoMatch: hostname=%s msg=%s\n", host, found);
1136 #endif
1137 return 0;
1138 }
1139
1140 if (the_host.h_name != NULL)
1141 found = the_host.h_name;
1142 else
1143 found = host;
1144
1145 /* Try to deal with multiple IP addr's for a host */
1146 for (ip_listptr = the_host.h_addr_list; *ip_listptr; ++ip_listptr) {
1147 ip_list = (struct in_addr *)*ip_listptr;
1148 if (This->addr.s_addr == (ip_list->s_addr & This->mask.s_addr)) {
1149 #if DEBUGGING
1150 fprintf(stderr, "3)IP-Match: %s[%s] <-> ", found, inet_ntoa(*ip_list));
1151 fprintf(stderr, "%s/", inet_ntoa(This->addr));
1152 fprintf(stderr, "%s\n", inet_ntoa(This->mask));
1153 #endif
1154 return 1;
1155 }
1156 #if DEBUGGING
1157 else {
1158 fprintf(stderr, "3)IP-NoMatch: %s[%s] <-> ", found, inet_ntoa(*ip_list));
1159 fprintf(stderr, "%s/", inet_ntoa(This->addr));
1160 fprintf(stderr, "%s\n", inet_ntoa(This->mask));
1161 }
1162 #endif
1163 }
1164 }
1165
1166 return 0;
1167 }
1168
1169 /* Return TRUE if addr represents a domain name */
ap_proxy_is_domainname(struct dirconn_entry * This,pool * p)1170 int ap_proxy_is_domainname(struct dirconn_entry *This, pool *p)
1171 {
1172 char *addr = This->name;
1173 int i;
1174
1175 /* Domain name must start with a '.' */
1176 if (addr[0] != '.')
1177 return 0;
1178
1179 /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
1180 for (i = 0; ap_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i)
1181 continue;
1182
1183 if (addr[i] != '\0')
1184 return 0;
1185
1186 /* Strip trailing dots */
1187 for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)
1188 addr[i] = '\0';
1189
1190 This->matcher = proxy_match_domainname;
1191 return 1;
1192 }
1193
1194 /* Return TRUE if host "host" is in domain "domain" */
proxy_match_domainname(struct dirconn_entry * This,request_rec * r)1195 static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
1196 {
1197 const char *host = proxy_get_host_of_request(r);
1198 int d_len = strlen(This->name), h_len;
1199
1200 if (host == NULL) /* some error was logged already */
1201 return 0;
1202
1203 h_len = strlen(host);
1204
1205 /* @@@ do this within the setup? */
1206 /* Ignore trailing dots in domain comparison: */
1207 while (d_len > 0 && This->name[d_len - 1] == '.')
1208 --d_len;
1209 while (h_len > 0 && host[h_len - 1] == '.')
1210 --h_len;
1211 return h_len > d_len
1212 && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
1213 }
1214
1215 /* Return TRUE if addr represents a host name */
ap_proxy_is_hostname(struct dirconn_entry * This,pool * p)1216 int ap_proxy_is_hostname(struct dirconn_entry *This, pool *p)
1217 {
1218 struct hostent host;
1219 char *addr = This->name;
1220 int i;
1221
1222 /* Host names must not start with a '.' */
1223 if (addr[0] == '.')
1224 return 0;
1225
1226 /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
1227 for (i = 0; ap_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i);
1228
1229 if (addr[i] != '\0' || ap_proxy_host2addr(addr, &host) != NULL)
1230 return 0;
1231
1232 This->hostentry = ap_pduphostent(p, &host);
1233
1234 /* Strip trailing dots */
1235 for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)
1236 addr[i] = '\0';
1237
1238 This->matcher = proxy_match_hostname;
1239 return 1;
1240 }
1241
1242 /* Return TRUE if host "host" is equal to host2 "host2" */
proxy_match_hostname(struct dirconn_entry * This,request_rec * r)1243 static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
1244 {
1245 char *host = This->name;
1246 const char *host2 = proxy_get_host_of_request(r);
1247 int h2_len;
1248 int h1_len;
1249
1250 if (host == NULL || host2 == NULL)
1251 return 0; /* oops! */
1252
1253 h2_len = strlen(host2);
1254 h1_len = strlen(host);
1255
1256 /* Ignore trailing dots in host2 comparison: */
1257 while (h2_len > 0 && host2[h2_len - 1] == '.')
1258 --h2_len;
1259 while (h1_len > 0 && host[h1_len - 1] == '.')
1260 --h1_len;
1261 return h1_len == h2_len
1262 && strncasecmp(host, host2, h1_len) == 0;
1263 }
1264
1265 /* Return TRUE if addr is to be matched as a word */
ap_proxy_is_word(struct dirconn_entry * This,pool * p)1266 int ap_proxy_is_word(struct dirconn_entry *This, pool *p)
1267 {
1268 This->matcher = proxy_match_word;
1269 return 1;
1270 }
1271
1272 /* Return TRUE if string "str2" occurs literally in "str1" */
proxy_match_word(struct dirconn_entry * This,request_rec * r)1273 static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
1274 {
1275 const char *host = proxy_get_host_of_request(r);
1276 return host != NULL && strstr(host, This->name) != NULL;
1277 }
1278
ap_proxy_doconnect(int sock,struct sockaddr * addr,request_rec * r)1279 int ap_proxy_doconnect(int sock, struct sockaddr *addr, request_rec *r)
1280 {
1281 int i;
1282 int salen;
1283 char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
1284 #ifdef NI_WITHSCOPEID
1285 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1286 #else
1287 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1288 #endif
1289
1290 ap_hard_timeout("proxy connect", r);
1291 #ifdef HAVE_SOCKADDR_LEN
1292 salen = addr->sa_len;
1293 #else
1294 switch (addr->sa_family) {
1295 case AF_INET6:
1296 salen = sizeof(struct sockaddr_in6);
1297 break;
1298 default:
1299 salen = sizeof(struct sockaddr_in);
1300 break;
1301 }
1302 #endif
1303 do {
1304 i = connect(sock, addr, salen);
1305 } while (i == -1 && errno == EINTR);
1306 if (i == -1) {
1307 if (getnameinfo(addr, salen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
1308 niflags) != 0) {
1309 memcpy(hbuf, "?", 2);
1310 memcpy(pbuf, "?", 2);
1311 }
1312 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
1313 "proxy connect to %s port %s failed", hbuf, pbuf);
1314 }
1315 ap_kill_timeout(r);
1316
1317 return i;
1318 }
1319
1320 /* This function is called by ap_table_do() for all header lines
1321 * (from proxy_http.c and proxy_ftp.c)
1322 * It is passed a cache_req struct pointer and a MIME field and value pair
1323 */
ap_proxy_send_hdr_line(void * p,const char * key,const char * value)1324 int ap_proxy_send_hdr_line(void *p, const char *key, const char *value)
1325 {
1326 cache_req *c = (cache_req *)p;
1327
1328 if (key == NULL || value == NULL || value[0] == '\0')
1329 return 1;
1330 if (c->fp != NULL &&
1331 ap_bvputs(c->fp, key, ": ", value, CRLF, NULL) == -1) {
1332 ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
1333 "proxy: error writing header to %s", c->tempfile);
1334 c = ap_proxy_cache_error(c);
1335 return 0; /* no need to continue, it failed already */
1336 }
1337 return 1; /* tell ap_table_do() to continue calling us
1338 * for more headers */
1339 }
1340
1341 /* send a text line to one or two BUFF's; return line length */
ap_proxy_bputs2(const char * data,BUFF * client,cache_req * cache)1342 unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache)
1343 {
1344 unsigned len = ap_bputs(data, client);
1345 if (cache != NULL && cache->fp != NULL)
1346 ap_bputs(data, cache->fp);
1347 return len;
1348 }
1349
1350 /* do a HTTP/1.1 age calculation */
ap_proxy_current_age(cache_req * c,const time_t age_value)1351 time_t ap_proxy_current_age(cache_req *c, const time_t age_value)
1352 {
1353 time_t apparent_age, corrected_received_age, response_delay, corrected_initial_age,
1354 resident_time, current_age;
1355
1356 /* Perform an HTTP/1.1 age calculation. (RFC2616 13.2.3) */
1357
1358 apparent_age = MAX(0, c->resp_time - c->date);
1359 corrected_received_age = MAX(apparent_age, age_value);
1360 response_delay = c->resp_time - c->req_time;
1361 corrected_initial_age = corrected_received_age + response_delay;
1362 resident_time = time(NULL) - c->resp_time;
1363 current_age = corrected_initial_age + resident_time;
1364
1365 return (current_age);
1366 }
1367
1368 /* open a cache file and return a pointer to a BUFF */
ap_proxy_open_cachefile(request_rec * r,char * filename)1369 BUFF *ap_proxy_open_cachefile(request_rec *r, char *filename)
1370 {
1371 BUFF *cachefp = NULL;
1372 int cfd;
1373
1374 if (filename != NULL) {
1375 cfd = open(filename, O_RDWR | O_BINARY);
1376 if (cfd != -1) {
1377 ap_note_cleanups_for_fd(r->pool, cfd);
1378 cachefp = ap_bcreate(r->pool, B_RD | B_WR);
1379 ap_bpushfd(cachefp, cfd, cfd);
1380 }
1381 else if (errno != ENOENT)
1382 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
1383 "proxy: error opening cache file %s",
1384 filename);
1385 else
1386 ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "File %s not found", filename);
1387
1388 }
1389 return cachefp;
1390 }
1391
1392 /* create a cache file and return a pointer to a BUFF */
ap_proxy_create_cachefile(request_rec * r,char * filename)1393 BUFF *ap_proxy_create_cachefile(request_rec *r, char *filename)
1394 {
1395 BUFF *cachefp = NULL;
1396 int cfd;
1397
1398 if (filename != NULL) {
1399 cfd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0622);
1400 if (cfd != -1) {
1401 ap_note_cleanups_for_fd(r->pool, cfd);
1402 cachefp = ap_bcreate(r->pool, B_WR);
1403 ap_bpushfd(cachefp, -1, cfd);
1404 }
1405 else if (errno != ENOENT)
1406 ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
1407 "proxy: error creating cache file %s",
1408 filename);
1409 }
1410 return cachefp;
1411 }
1412
1413 /* Clear all connection-based headers from headers table */
ap_proxy_clear_connection(pool * p,table * headers)1414 void ap_proxy_clear_connection(pool *p, table *headers)
1415 {
1416 const char *name;
1417 char *next = ap_pstrdup(p, ap_table_get(headers, "Connection"));
1418
1419 /* Some proxies (Squid, ICS) use the non-standard "Proxy-Connection" header. */
1420 ap_table_unset(headers, "Proxy-Connection");
1421
1422 if (next != NULL) {
1423 while (*next) {
1424 name = next;
1425 while (*next && !ap_isspace(*next) && (*next != ','))
1426 ++next;
1427 while (ap_isspace(*next) || (*next == ',')) {
1428 *next = '\0';
1429 ++next;
1430 }
1431 ap_table_unset(headers, name);
1432 }
1433 ap_table_unset(headers, "Connection");
1434 }
1435
1436 /* unset hop-by-hop headers defined in RFC2616 13.5.1 */
1437 ap_table_unset(headers,"Keep-Alive");
1438 /*
1439 * XXX: @@@ FIXME: "Proxy-Authenticate" should IMO *not* be stripped
1440 * because in a chain of proxies some "front" proxy might need
1441 * proxy authentication, while a "back-end" proxy which needs none can
1442 * simply pass the "Proxy-Authenticate" back to the client, and pass
1443 * the client's "Proxy-Authorization" to the front-end proxy.
1444 * (See the note in proxy_http.c for the "Proxy-Authorization" case.)
1445 *
1446 * MnKr 04/2002
1447 */
1448 ap_table_unset(headers,"Proxy-Authenticate");
1449 ap_table_unset(headers,"TE");
1450 ap_table_unset(headers,"Trailer");
1451 /* it is safe to just chop the transfer-encoding header
1452 * here, because proxy doesn't support any other encodings
1453 * to the backend other than chunked.
1454 */
1455 ap_table_unset(headers,"Transfer-Encoding");
1456 ap_table_unset(headers,"Upgrade");
1457
1458 }
1459
1460 /* overlay one table on another
1461 * keys in base will be replaced by keys in overlay
1462 *
1463 * Note: this has to be done in a special way, due
1464 * to some nastiness when it comes to having multiple
1465 * headers in the overlay table. First, we remove all
1466 * the headers in the base table that are found in the
1467 * overlay table, then we simply concatenate the
1468 * tables together.
1469 *
1470 * The base and overlay tables need not be in the same
1471 * pool (and probably won't be).
1472 *
1473 * If the base table is changed in any way through
1474 * being overlayed with the overlay table, this
1475 * function returns a 1.
1476 */
ap_proxy_table_replace(table * base,table * overlay)1477 int ap_proxy_table_replace(table *base, table *overlay)
1478 {
1479 table_entry *elts = (table_entry *)overlay->a.elts;
1480 int i, q = 0;
1481 const char *val;
1482
1483 /* remove overlay's keys from base */
1484 for (i = 0; i < overlay->a.nelts; ++i) {
1485 val = ap_table_get(base, elts[i].key);
1486 if (!val || strcmp(val, elts[i].val)) {
1487 q = 1;
1488 }
1489 if (val) {
1490 ap_table_unset(base, elts[i].key);
1491 }
1492 }
1493
1494 /* add overlay to base */
1495 for (i = 0; i < overlay->a.nelts; ++i) {
1496 ap_table_add(base, elts[i].key, elts[i].val);
1497 }
1498
1499 return q;
1500 }
1501
1502 /* read the response line
1503 * This function reads a single line of response from the server,
1504 * and returns a status code.
1505 * It also populates the request_rec with the resultant status, and
1506 * returns backasswards status (HTTP/0.9).
1507 */
ap_proxy_read_response_line(BUFF * f,request_rec * r,char * buffer,int size,int * backasswards,int * major,int * minor)1508 int ap_proxy_read_response_line(BUFF *f, request_rec *r, char *buffer, int size, int *backasswards, int *major, int *minor) {
1509
1510 long len;
1511
1512 len = ap_getline(buffer, size-1, f, 0);
1513 if (len == -1) {
1514 ap_bclose(f);
1515 ap_kill_timeout(r);
1516 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1517 "Error reading from remote server");
1518 }
1519 else if (len == 0) {
1520 ap_bclose(f);
1521 ap_kill_timeout(r);
1522 return ap_proxyerror(r, HTTP_BAD_GATEWAY,
1523 "Document contains no data");
1524 }
1525
1526 /*
1527 * Is it an HTTP/1 response? Do some sanity checks on the response. (This
1528 * is buggy if we ever see an HTTP/1.10)
1529 */
1530 if (ap_checkmask(buffer, "HTTP/#.# ###*")) {
1531
1532 if (2 != sscanf(buffer, "HTTP/%u.%u", major, minor)) {
1533 /* if no response, default to HTTP/1.1 - is this correct? */
1534 *major = 1;
1535 *minor = 1;
1536 }
1537
1538 /* If not an HTTP/1 message */
1539 if (*major < 1) {
1540 ap_bclose(f);
1541 ap_kill_timeout(r);
1542 return HTTP_BAD_GATEWAY;
1543 }
1544 *backasswards = 0;
1545
1546 /* there need not be a reason phrase in the response,
1547 * and ap_getline() already deleted trailing whitespace.
1548 * But RFC2616 requires a SP after the Status-Code. Add one:
1549 */
1550 if (strlen(buffer) < sizeof("HTTP/1.x 200 ")-1)
1551 buffer = ap_pstrcat(r->pool, buffer, " ", NULL);
1552 buffer[12] = '\0';
1553 r->status = atoi(&buffer[9]);
1554 buffer[12] = ' ';
1555 r->status_line = ap_pstrdup(r->pool, &buffer[9]);
1556
1557 /* if the response was 100 continue, soak up any headers */
1558 if (r->status == 100) {
1559 ap_proxy_read_headers(r, buffer, size, f);
1560 }
1561
1562 }
1563 else {
1564
1565 /* an http/0.9 response */
1566 *backasswards = 1;
1567 r->status = 200;
1568 r->status_line = "200 OK";
1569 *major = 0;
1570 *minor = 9;
1571
1572 }
1573
1574 return OK;
1575
1576 }
1577
get_per_thread_data(void)1578 static struct per_thread_data *get_per_thread_data(void)
1579 {
1580 static APACHE_TLS struct per_thread_data sptd;
1581 return &sptd;
1582 }
1583