diff options
Diffstat (limited to 'APACHE_1_3_42/src/modules/proxy/proxy_util.c')
-rw-r--r-- | APACHE_1_3_42/src/modules/proxy/proxy_util.c | 1640 |
1 files changed, 1640 insertions, 0 deletions
diff --git a/APACHE_1_3_42/src/modules/proxy/proxy_util.c b/APACHE_1_3_42/src/modules/proxy/proxy_util.c new file mode 100644 index 0000000000..ef58663da3 --- /dev/null +++ b/APACHE_1_3_42/src/modules/proxy/proxy_util.c @@ -0,0 +1,1640 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Utility routines for Apache proxy */ +#include "mod_proxy.h" +#include "http_main.h" +#include "ap_md5.h" +#include "multithread.h" +#include "http_log.h" +#include "util_uri.h" +#include "util_date.h" /* get ap_checkmask() decl. */ + +static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r); +static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r); +static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r); +static int proxy_match_word(struct dirconn_entry *This, request_rec *r); +static struct per_thread_data *get_per_thread_data(void); +/* already called in the knowledge that the characters are hex digits */ +int ap_proxy_hex2c(const char *x) +{ + int i; +#ifndef CHARSET_EBCDIC + int ch; + + ch = x[0]; + if (ap_isdigit(ch)) + i = ch - '0'; + else if (ap_isupper(ch)) + i = ch - ('A' - 10); + else + i = ch - ('a' - 10); + i <<= 4; + + ch = x[1]; + if (ap_isdigit(ch)) + i += ch - '0'; + else if (ap_isupper(ch)) + i += ch - ('A' - 10); + else + i += ch - ('a' - 10); + return i; +#else /* CHARSET_EBCDIC */ + return (1 == sscanf(x, "%2x", &i)) ? os_toebcdic[i & 0xFF] : 0; +#endif /* CHARSET_EBCDIC */ +} + +void ap_proxy_c2hex(int ch, char *x) +{ +#ifndef CHARSET_EBCDIC + int i; + + x[0] = '%'; + i = (ch & 0xF0) >> 4; + if (i >= 10) + x[1] = ('A' - 10) + i; + else + x[1] = '0' + i; + + i = ch & 0x0F; + if (i >= 10) + x[2] = ('A' - 10) + i; + else + x[2] = '0' + i; +#else /* CHARSET_EBCDIC */ + static const char ntoa[] = {"0123456789ABCDEF"}; + ch = os_toascii[ch & 0xFF]; + x[0] = '%'; + x[1] = ntoa[(ch >> 4) & 0x0F]; + x[2] = ntoa[ch & 0x0F]; + x[3] = '\0'; +#endif /* CHARSET_EBCDIC */ +} + +/* + * canonicalise a URL-encoded string + */ + +/* + * Convert a URL-encoded string to canonical form. + * It decodes characters which need not be encoded, + * and encodes those which must be encoded, and does not touch + * those which must not be touched. + */ +char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t, + enum proxyreqtype isenc) +{ + int i, j, ch; + char *y; + const char *allowed; /* characters which should not be encoded */ + const char *reserved; /* characters which much not be en/de-coded */ + +/* N.B. in addition to :@&=, this allows ';' in an http path + * and '?' in an ftp path -- this may be revised + * + * Also, it makes a '+' character in a search string reserved, as + * it may be form-encoded. (Although RFC 1738 doesn't allow this - + * it only permits ; / ? : @ = & as reserved chars.) + */ + if (t == enc_path) + allowed = "$-_.+!*'(),;:@&="; + else if (t == enc_search) + allowed = "$-_.!*'(),;:@&="; + else if (t == enc_user) + allowed = "$-_.+!*'(),;@&="; + else if (t == enc_fpath) + allowed = "$-_.+!*'(),?:@&="; + else /* if (t == enc_parm) */ + allowed = "$-_.+!*'(),?/:@&="; + + if (t == enc_path) + reserved = "/"; + else if (t == enc_search) + reserved = "+"; + else + reserved = ""; + + y = ap_palloc(p, 3 * len + 1); + + for (i = 0, j = 0; i < len; i++, j++) { +/* always handle '/' first */ + ch = x[i]; + if (strchr(reserved, ch)) { + y[j] = ch; + continue; + } +/* decode it if not already done */ + if (isenc != NOT_PROXY && ch == '%') { + if (!ap_isxdigit(x[i + 1]) || !ap_isxdigit(x[i + 2])) + return NULL; + ch = ap_proxy_hex2c(&x[i + 1]); + i += 2; + if (ch != 0 && strchr(reserved, ch)) { /* keep it encoded */ + ap_proxy_c2hex(ch, &y[j]); + j += 2; + continue; + } + } +/* recode it, if necessary */ + if (!ap_isalnum(ch) && !strchr(allowed, ch)) { + ap_proxy_c2hex(ch, &y[j]); + j += 2; + } + else + y[j] = ch; + } + y[j] = '\0'; + return y; +} + +/* + * Parses network-location. + * urlp on input the URL; on output the path, after the leading / + * user NULL if no user/password permitted + * password holder for password + * host holder for host + * port port number; only set if one is supplied. + * + * Returns an error string. + */ +char * + ap_proxy_canon_netloc(pool *p, char **const urlp, char **userp, + char **passwordp, char **hostp, int *port) +{ + int i; + char *strp, *host, *url = *urlp; + char *user = NULL, *password = NULL; + + if (url[0] != '/' || url[1] != '/') + return "Malformed URL"; + host = url + 2; + url = strchr(host, '/'); + if (url == NULL) + url = ""; + else + *(url++) = '\0'; /* skip seperating '/' */ + + /* find _last_ '@' since it might occur in user/password part */ + strp = strrchr(host, '@'); + + if (strp != NULL) { + *strp = '\0'; + user = host; + host = strp + 1; + +/* find password */ + strp = strchr(user, ':'); + if (strp != NULL) { + *strp = '\0'; + password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, STD_PROXY); + if (password == NULL) + return "Bad %-escape in URL (password)"; + } + + user = ap_proxy_canonenc(p, user, strlen(user), enc_user, STD_PROXY); + if (user == NULL) + return "Bad %-escape in URL (username)"; + } + if (userp != NULL) { + *userp = user; + } + if (passwordp != NULL) { + *passwordp = password; + } + + strp = strrchr(host, ':'); + if (strp != NULL) { + *(strp++) = '\0'; + + for (i = 0; strp[i] != '\0'; i++) + if (!ap_isdigit(strp[i])) + break; + + /* if (i == 0) the no port was given; keep default */ + if (strp[i] != '\0') { + return "Bad port number in URL"; + } + else if (i > 0) { + *port = atoi(strp); + if (*port > 65535) + return "Port number in URL > 65535"; + } + } + ap_str_tolower(host); /* DNS names are case-insensitive */ + if (*host == '\0') + return "Missing host in URL"; +/* check hostname syntax */ + for (i = 0; host[i] != '\0'; i++) + if (!ap_isdigit(host[i]) && host[i] != '.') + break; + /* must be an IP address */ +#if defined(WIN32) || defined(NETWARE) || defined(TPF) || defined(BEOS) + if (host[i] == '\0' && (inet_addr(host) == -1)) +#else + if (host[i] == '\0' && (ap_inet_addr(host) == -1 || inet_network(host) == -1)) +#endif + { + return "Bad IP address in URL"; + } + + *urlp = url; + *hostp = host; + + return NULL; +} + +static const char *const lwday[7] = +{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; + +/* + * If the date is a valid RFC 850 date or asctime() date, then it + * is converted to the RFC 1123 format, otherwise it is not modified. + * This routine is not very fast at doing conversions, as it uses + * sscanf and sprintf. However, if the date is already correctly + * formatted, then it exits very quickly. + */ +const char * + ap_proxy_date_canon(pool *p, const char *x) +{ + int wk, mday, year, hour, min, sec, mon; + char *q, month[4], zone[4], week[4]; + + q = strchr(x, ','); + /* check for RFC 850 date */ + if (q != NULL && q - x > 3 && q[1] == ' ') { + *q = '\0'; + for (wk = 0; wk < 7; wk++) + if (strcmp(x, lwday[wk]) == 0) + break; + *q = ','; + if (wk == 7) + return x; /* not a valid date */ + if (strlen(q) != 24 || + q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' || + q[17] != ':' || strcmp(&q[20], " GMT") != 0) + return x; + if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year, + &hour, &min, &sec, zone) != 7) + return x; + if (year < 70) + year += 2000; + else + year += 1900; + } + else { +/* check for asctime() date */ + if (strlen(x) != 24 || + x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' || + x[16] != ':' || x[19] != ' ' || x[24] != '\0') + return x; + if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour, + &min, &sec, &year) != 7) + return x; + for (wk = 0; wk < 7; wk++) + if (strcmp(week, ap_day_snames[wk]) == 0) + break; + if (wk == 7) + return x; + } + +/* check date */ + for (mon = 0; mon < 12; mon++) + if (strcmp(month, ap_month_snames[mon]) == 0) + break; + if (mon == 12) + return x; + + q = ap_palloc(p, 30); + ap_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", ap_day_snames[wk], mday, + ap_month_snames[mon], year, hour, min, sec); + return q; +} + + +/* + * Reads headers from a buffer and returns an array of headers. + * Returns NULL on file error + * This routine tries to deal with too long lines and continuation lines. + * + * Note: Currently the headers are passed through unmerged. This has to be + * done so that headers which react badly to merging (such as Set-Cookie + * headers, which contain commas within the date field) do not get stuffed + * up. + */ +table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f) +{ + table *resp_hdrs; + int len; + char *value, *end; + char field[MAX_STRING_LEN]; + + resp_hdrs = ap_make_table(r->pool, 20); + + /* + * Read header lines until we get the empty separator line, a read error, + * the connection closes (EOF), or we timeout. + */ + while ((len = ap_getline(buffer, size, f, 1)) > 0) { + + if (!(value = strchr(buffer, ':'))) { /* Find the colon separator */ + + /* + * Buggy MS IIS servers sometimes return invalid headers (an + * extra "HTTP/1.0 200, OK" line sprinkled in between the usual + * MIME headers). Try to deal with it in a sensible way, but log + * the fact. XXX: The mask check is buggy if we ever see an + * HTTP/1.10 + */ + + if (!ap_checkmask(buffer, "HTTP/#.# ###*")) { + /* Nope, it wasn't even an extra HTTP header. Give up. */ + return NULL; + } + + ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, r->server, + "proxy: Ignoring duplicate HTTP status line " + "returned by buggy server %s (%s)", r->uri, r->method); + continue; + } + + *value = '\0'; + ++value; + /* + * XXX: RFC2068 defines only SP and HT as whitespace, this test is + * wrong... and so are many others probably. + */ + while (ap_isspace(*value)) + ++value; /* Skip to start of value */ + + /* should strip trailing whitespace as well */ + for (end = &value[strlen(value) - 1]; end > value && ap_isspace(*end); --end) + *end = '\0'; + + /* make sure we add so as not to destroy duplicated headers */ + ap_table_add(resp_hdrs, buffer, value); + + /* the header was too long; at the least we should skip extra data */ + if (len >= size - 1) { + while ((len = ap_getline(field, MAX_STRING_LEN, f, 1)) + >= MAX_STRING_LEN - 1) { + /* soak up the extra data */ + } + if (len == 0) /* time to exit the larger loop as well */ + break; + } + } + return resp_hdrs; +} + +/* read data from (socket BUFF*) f, write it to: + * - c->fp, if it is open + * - r->connection->client, if nowrite == 0 + */ + +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) +{ + int ok, end_of_chunk; + char *buf; + size_t buf_size; + long remaining = 0; + long total_bytes_rcvd; + register int n = 0, o, w; + conn_rec *con = r->connection; + int alternate_timeouts = 1; /* 1 if we alternate between soft & hard + * timeouts */ + + /* allocate a buffer to store the bytes in */ + /* + * make sure it is at least IOBUFSIZE, as recv_buffer_size may be zero + * for system default + */ + buf_size = MAX(recv_buffer_size, IOBUFSIZE); + buf = ap_palloc(r->pool, buf_size); + + total_bytes_rcvd = 0; + if (c != NULL) + c->written = 0; + +#ifdef CHARSET_EBCDIC + /* The cache copy is ASCII, not EBCDIC, even for text/html) */ + ap_bsetflag(f, B_ASCII2EBCDIC | B_EBCDIC2ASCII, 0); + ap_bsetflag(con->client, B_ASCII2EBCDIC | B_EBCDIC2ASCII, 0); +#endif + + /* + * Since we are reading from one buffer and writing to another, it is + * unsafe to do a soft_timeout here, at least until the proxy has its own + * timeout handler which can set both buffers to EOUT. + */ + + ap_kill_timeout(r); + +#if defined(WIN32) || defined(TPF) || defined(NETWARE) + /* works fine under win32, so leave it */ + ap_hard_timeout("proxy send body", r); + alternate_timeouts = 0; +#else + /* + * CHECKME! Since hard_timeout won't work in unix on sends with partial + * cache completion, we have to alternate between hard_timeout for reads, + * and soft_timeout for send. This is because we need to get a return + * from ap_bwrite to be able to continue caching. BUT, if we *can't* + * continue anyway, just use hard_timeout. (Also, if no cache file is + * written, use hard timeouts) + */ + + if (c == NULL || c->len <= 0 || c->cache_completion == 1.0) { + ap_hard_timeout("proxy send body", r); + alternate_timeouts = 0; + } +#endif + + /* + * Loop and ap_bread() while we can successfully read and write, or + * (after the client aborted) while we can successfully read and finish + * the configured cache_completion. + */ + for (end_of_chunk = ok = 1; ok;) { + if (alternate_timeouts) + ap_hard_timeout("proxy recv body from upstream server", r); + + + /* read a chunked block */ + if (chunked) { + long chunk_start = 0; + n = 0; + + /* start of a new chunk */ + if (end_of_chunk) { + end_of_chunk = 0; + /* get the chunk size from the stream */ + chunk_start = ap_getline(buf, buf_size, f, 0); + if ((chunk_start <= 0) || ((size_t)chunk_start + 1 >= buf_size) || !ap_isxdigit(*buf)) { + n = -1; + } + /* parse the chunk size */ + else { + remaining = ap_get_chunk_size(buf); + if (remaining == 0) { /* Last chunk indicated, get footers */ + /* as we are a proxy, we discard the footers, as the headers + * have already been sent at this point. + */ + if (NULL == ap_proxy_read_headers(r, buf, buf_size, f)) { + n = -1; + } + } + else if (remaining < 0) { + n = -1; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r, + "proxy: remote protocol error, invalid chunk size"); + + } + } + } + + /* read the chunk */ + if (remaining > 0) { + n = ap_bread(f, buf, (int) MIN(buf_size, remaining)); + if (n > -1) { + remaining -= n; + end_of_chunk = (remaining == 0); + } + } + + /* soak up trailing CRLF */ + if (end_of_chunk) { + int ch; /* int because it may hold an EOF */ + /* + * For EBCDIC, the proxy has configured the BUFF layer to + * transparently pass the ascii characters thru (also writing + * an ASCII copy to the cache, where appropriate). + * Therefore, we see here an ASCII-CRLF (\015\012), + * not an EBCDIC-CRLF (\r\n). + */ + if ((ch = ap_bgetc(f)) == EOF) { + /* Protocol error: EOF detected within chunk */ + n = -1; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r, + "proxy: remote protocol error, eof while reading chunked from proxy"); + } + else + { + if (ch == '\015') { /* _ASCII_ CR */ + ch = ap_bgetc(f); + } + if (ch != '\012') { + n = -1; + } + } + } + } + + /* otherwise read block normally */ + else { + if (-1 == len) { + n = ap_bread(f, buf, buf_size); + } + else { + n = ap_bread(f, buf, (int) MIN(buf_size, + (len - total_bytes_rcvd))); + } + } + + + if (alternate_timeouts) + ap_kill_timeout(r); + else + ap_reset_timeout(r); + + if (n == -1) { /* input error */ + if (c != NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error reading from %s", c->url); + c = ap_proxy_cache_error(c); + } + break; + } + if (n == 0) + break; /* EOF */ + o = 0; + total_bytes_rcvd += n; + + /* if we've received everything... */ + /* + * in the case of slow frontends and expensive backends, we want to + * avoid leaving a backend connection hanging while the frontend + * takes it's time to absorb the bytes. so: if we just read the last + * block, we close the backend connection now instead of later - it's + * no longer needed. + */ + if (total_bytes_rcvd == len) { + ap_bclose(f); + f = NULL; + } + + /* Write to cache first. */ + /* + * @@@ XXX FIXME: Assuming that writing the cache file won't time + * out?!!? + */ + if (c != NULL && c->fp != NULL) { + if (ap_bwrite(c->fp, &buf[0], n) != n) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing to %s", c->tempfile); + c = ap_proxy_cache_error(c); + } + else { + c->written += n; + } + } + + /* Write the block to the client, detect aborted transfers */ + while (!nowrite && !con->aborted && n > 0) { + if (alternate_timeouts) + ap_soft_timeout("proxy send body", r); + + w = ap_bwrite(con->client, &buf[o], n); + + if (alternate_timeouts) + ap_kill_timeout(r); + else + ap_reset_timeout(r); + + if (w <= 0) { + if (c != NULL) { + /* + * when a send failure occurs, we need to decide whether + * to continue loading and caching the document, or to + * abort the whole thing + */ + ok = (c->len > 0) && + (c->cache_completion > 0) && + (c->len * c->cache_completion < total_bytes_rcvd); + + if (!ok) { + if (c->fp != NULL) { + ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)); + c->fp = NULL; + } + unlink(c->tempfile); + c = NULL; + } + } + con->aborted = 1; + break; + } + n -= w; + o += w; + } /* while client alive and more data to send */ + + /* if we've received everything, leave now */ + if (total_bytes_rcvd == len) + break; + + } /* loop and ap_bread while "ok" */ + + /* if the backend connection is still open, close it */ + if (f) { + ap_bclose(f); + } + + if (!con->aborted) { + ap_bflush(con->client); + } + + ap_kill_timeout(r); + + r->bytes_sent += total_bytes_rcvd; + + return total_bytes_rcvd; +} + +/* + * Writes response line and headers to the cache file. + * + * If respline is NULL, no response line will be written. + */ +void ap_proxy_write_headers(cache_req *c, const char *respline, table *t) +{ + /* write status line */ + if (respline && c->fp != NULL && + ap_bvputs(c->fp, respline, CRLF, NULL) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing status line to %s", c->tempfile); + c = ap_proxy_cache_error(c); + return; + } + + /* write response headers to the cache file */ + ap_table_do(ap_proxy_send_hdr_line, c, t, NULL); + + /* write terminating CRLF */ + if (c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing CRLF to %s", c->tempfile); + c = ap_proxy_cache_error(c); + } +} + + +/* + * list is a comma-separated list of case-insensitive tokens, with + * optional whitespace around the tokens. + * The return returns 1 if the token val is found in the list, or 0 + * otherwise. + */ +int ap_proxy_liststr(const char *list, const char *key, char **val) +{ + int len, i; + const char *p; + char valbuf[HUGE_STRING_LEN]; + valbuf[sizeof(valbuf) - 1] = 0; /* safety terminating zero */ + + len = strlen(key); + + while (list != NULL) { + p = strchr(list, ','); + if (p != NULL) { + i = p - list; + do + p++; + while (ap_isspace(*p)); + } + else + i = strlen(list); + + while (i > 0 && ap_isspace(list[i - 1])) + i--; + if (i == len && strncasecmp(list, key, len) == 0) { + if (val) { + p = strchr(list, ','); + while (ap_isspace(*list)) { + list++; + } + if ('=' == list[0]) + list++; + while (ap_isspace(*list)) { + list++; + } + strncpy(valbuf, list, MIN(p - list, sizeof(valbuf) - 1)); + *val = valbuf; + } + return 1; + } + list = p; + } + return 0; +} + +#ifdef CASE_BLIND_FILESYSTEM + +/* + * On some platforms, the file system is NOT case sensitive. So, a == A + * need to map to smaller set of characters + */ +void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength) +{ + AP_MD5_CTX context; + unsigned char digest[16]; + char tmp[26]; + int i, k, d; + unsigned int x; + static const char enc_table[32] = "abcdefghijklmnopqrstuvwxyz012345"; + + ap_MD5Init(&context); + ap_MD5Update(&context, (const unsigned char *)it, strlen(it)); + ap_MD5Final(digest, &context); + +/* encode 128 bits as 26 characters, using a modified uuencoding */ +/* the encoding is 5 bytes -> 8 characters + * i.e. 128 bits is 3 x 5 bytes + 1 byte -> 3 * 8 characters + 2 characters + */ + for (i = 0, k = 0; i < 15; i += 5) { + x = (digest[i] << 24) | (digest[i + 1] << 16) | (digest[i + 2] << 8) | digest[i + 3]; + tmp[k++] = enc_table[x >> 27]; + tmp[k++] = enc_table[(x >> 22) & 0x1f]; + tmp[k++] = enc_table[(x >> 17) & 0x1f]; + tmp[k++] = enc_table[(x >> 12) & 0x1f]; + tmp[k++] = enc_table[(x >> 7) & 0x1f]; + tmp[k++] = enc_table[(x >> 2) & 0x1f]; + x = ((x & 0x3) << 8) | digest[i + 4]; + tmp[k++] = enc_table[x >> 5]; + tmp[k++] = enc_table[x & 0x1f]; + } +/* one byte left */ + x = digest[15]; + tmp[k++] = enc_table[x >> 3]; /* use up 5 bits */ + tmp[k++] = enc_table[x & 0x7]; + /* now split into directory levels */ + + for (i = k = d = 0; d < ndepth; ++d) { + memcpy(&val[i], &tmp[k], nlength); + k += nlength; + val[i + nlength] = '/'; + i += nlength + 1; + } + memcpy(&val[i], &tmp[k], 26 - k); + val[i + 26 - k] = '\0'; +} + +#else + +void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength) +{ + AP_MD5_CTX context; + unsigned char digest[16]; + char tmp[22]; + int i, k, d; + unsigned int x; +#if defined(MPE) || (defined(AIX) && defined(__ps2__)) + /* + * Believe it or not, AIX 1.x does not allow you to name a file '@', so + * hack around it in the encoding. + */ + static const char enc_table[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_%"; +#else + static const char enc_table[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@"; +#endif + + ap_MD5Init(&context); + ap_MD5Update(&context, (const unsigned char *)it, strlen(it)); + ap_MD5Final(digest, &context); + +/* encode 128 bits as 22 characters, using a modified uuencoding */ +/* the encoding is 3 bytes -> 4 characters + * i.e. 128 bits is 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters + */ + for (i = 0, k = 0; i < 15; i += 3) { + x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2]; + tmp[k++] = enc_table[x >> 18]; + tmp[k++] = enc_table[(x >> 12) & 0x3f]; + tmp[k++] = enc_table[(x >> 6) & 0x3f]; + tmp[k++] = enc_table[x & 0x3f]; + } +/* one byte left */ + x = digest[15]; + tmp[k++] = enc_table[x >> 2]; /* use up 6 bits */ + tmp[k++] = enc_table[(x << 4) & 0x3f]; + /* now split into directory levels */ + + for (i = k = d = 0; d < ndepth; ++d) { + memcpy(&val[i], &tmp[k], nlength); + k += nlength; + val[i + nlength] = '/'; + i += nlength + 1; + } + memcpy(&val[i], &tmp[k], 22 - k); + val[i + 22 - k] = '\0'; +} + +#endif /* CASE_BLIND_FILESYSTEM */ + +/* + * Converts 16 hex digits to a time integer + */ +int ap_proxy_hex2sec(const char *x) +{ + int i, ch; + unsigned int j; + + for (i = 0, j = 0; i < 16; i++) { + ch = x[i]; + j <<= 4; + if (ap_isdigit(ch)) + j |= ch - '0'; + else if (ap_isupper(ch)) + j |= ch - ('A' - 10); + else + j |= ch - ('a' - 10); + } +/* no longer necessary, as the source hex is 8-byte int */ +/* if (j == 0xffffffff)*/ + /* return -1;*//* so that it works with 8-byte ints */ +/* else */ + return j; +} + +/* + * Converts a time integer to 16 hex digits + */ +void ap_proxy_sec2hex(int t, char *y) +{ + int i, ch; + unsigned int j = t; + + if (-1 == t) { + strcpy(y, "FFFFFFFFFFFFFFFF"); + return; + } + + for (i = 15; i >= 0; i--) { + ch = j & 0xF; + j >>= 4; + if (ch >= 10) + y[i] = ch + ('A' - 10); + else + y[i] = ch + '0'; + } + y[16] = '\0'; +} + + +cache_req *ap_proxy_cache_error(cache_req *c) +{ + if (c != NULL) { + if (c->fp != NULL) { + ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)); + c->fp = NULL; + } + if (c->origfp != NULL) { + ap_pclosef(c->req->pool, ap_bfileno(c->origfp, B_WR)); + c->origfp = NULL; + } + if (c->tempfile) + unlink(c->tempfile); + } + return NULL; +} + +int ap_proxyerror(request_rec *r, int statuscode, const char *message) +{ + ap_table_setn(r->notes, "error-notes", + ap_pstrcat(r->pool, + "The proxy server could not handle the request " + "<EM><A HREF=\"", ap_escape_uri(r->pool, r->uri), + "\">", ap_escape_html(r->pool, r->method), + " ", + ap_escape_html(r->pool, r->uri), "</A></EM>.<P>\n" + "Reason: <STRONG>", + ap_escape_html(r->pool, message), + "</STRONG>", NULL)); + + /* Allow "error-notes" string to be printed by ap_send_error_response() */ + ap_table_setn(r->notes, "verbose-error-to", ap_pstrdup(r->pool, "*")); + + r->status_line = ap_psprintf(r->pool, "%3.3u Proxy Error", statuscode); + return statuscode; +} + +/* + * This routine returns its own error message + */ +const char * + ap_proxy_host2addr(const char *host, struct hostent * reqhp) +{ + int i; + struct hostent *hp; + struct per_thread_data *ptd = get_per_thread_data(); + + for (i = 0; host[i] != '\0'; i++) + if (!ap_isdigit(host[i]) && host[i] != '.') + break; + + if (host[i] != '\0') { + hp = gethostbyname(host); + if (hp == NULL) + return "Host not found"; + } + else { + ptd->ipaddr = ap_inet_addr(host); + hp = gethostbyaddr((char *)&ptd->ipaddr, sizeof(ptd->ipaddr), AF_INET); + if (hp == NULL) { + memset(&ptd->hpbuf, 0, sizeof(ptd->hpbuf)); + ptd->hpbuf.h_name = 0; + ptd->hpbuf.h_addrtype = AF_INET; + ptd->hpbuf.h_length = sizeof(ptd->ipaddr); + ptd->hpbuf.h_addr_list = ptd->charpbuf; + ptd->hpbuf.h_addr_list[0] = (char *)&ptd->ipaddr; + ptd->hpbuf.h_addr_list[1] = 0; + hp = &ptd->hpbuf; + } + } + *reqhp = *hp; + return NULL; +} + +static const char * + proxy_get_host_of_request(request_rec *r) +{ + char *url, *user = NULL, *password = NULL, *err, *host; + int port = -1; + + if (r->hostname != NULL) + return r->hostname; + + /* Set url to the first char after "scheme://" */ + if ((url = strchr(r->uri, ':')) == NULL + || url[1] != '/' || url[2] != '/') + return NULL; + + url = ap_pstrdup(r->pool, &url[1]); /* make it point to "//", which is + * what proxy_canon_netloc expects */ + + err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port); + + if (err != NULL) + ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, r, + "%s", err); + + r->hostname = host; + + return host; /* ought to return the port, too */ +} + +/* Return TRUE if addr represents an IP address (or an IP network address) */ +int ap_proxy_is_ipaddr(struct dirconn_entry *This, pool *p) +{ + const char *addr = This->name; + long ip_addr[4]; + int i, quads; + long bits; + + /* if the address is given with an explicit netmask, use that */ + /* Due to a deficiency in ap_inet_addr(), it is impossible to parse */ + /* "partial" addresses (with less than 4 quads) correctly, i.e. */ + /* 192.168.123 is parsed as 192.168.0.123, which is not what I want. */ + /* I therefore have to parse the IP address manually: */ + /* + * if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) + * == 0) + */ + /* addr and mask were set by proxy_readmask() */ + /* return 1; */ + + /* Parse IP addr manually, optionally allowing */ + /* abbreviated net addresses like 192.168. */ + + /* Iterate over up to 4 (dotted) quads. */ + for (quads = 0; quads < 4 && *addr != '\0'; ++quads) { + char *tmp; + + if (*addr == '/' && quads > 0) /* netmask starts here. */ + break; + + if (!ap_isdigit(*addr)) + return 0; /* no digit at start of quad */ + + ip_addr[quads] = ap_strtol(addr, &tmp, 0); + + if (tmp == addr) /* expected a digit, found something else */ + return 0; + + if (ip_addr[quads] < 0 || ip_addr[quads] > 255) { + /* invalid octet */ + return 0; + } + + addr = tmp; + + if (*addr == '.' && quads != 3) + ++addr; /* after the 4th quad, a dot would be illegal */ + } + + for (This->addr.s_addr = 0, i = 0; i < quads; ++i) + This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); + + if (addr[0] == '/' && ap_isdigit(addr[1])) { /* net mask follows: */ + char *tmp; + + ++addr; + + bits = ap_strtol(addr, &tmp, 0); + + if (tmp == addr) /* expected a digit, found something else */ + return 0; + + addr = tmp; + + if (bits < 0 || bits > 32) /* netmask must be between 0 and 32 */ + return 0; + + } + else { + /* Determine (i.e., "guess") netmask by counting the */ + /* number of trailing .0's; reduce #quads appropriately */ + /* (so that 192.168.0.0 is equivalent to 192.168.) */ + while (quads > 0 && ip_addr[quads - 1] == 0) + --quads; + + /* + * "IP Address should be given in dotted-quad form, optionally + * followed by a netmask (e.g., 192.168.111.0/24)"; + */ + if (quads < 1) + return 0; + + /* every zero-byte counts as 8 zero-bits */ + bits = 8 * quads; + + if (bits != 32) /* no warning for fully qualified IP address */ + fprintf(stderr, "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n", + inet_ntoa(This->addr), bits); + } + + This->mask.s_addr = htonl(INADDR_NONE << (32 - bits)); + + if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) { + fprintf(stderr, "Warning: NetMask and IP-Addr disagree in %s/%ld\n", + inet_ntoa(This->addr), bits); + This->addr.s_addr &= This->mask.s_addr; + fprintf(stderr, " Set to %s/%ld\n", + inet_ntoa(This->addr), bits); + } + + if (*addr == '\0') { + This->matcher = proxy_match_ipaddr; + return 1; + } + else + return (*addr == '\0'); /* okay iff we've parsed the whole string */ +} + +/* Return TRUE if addr represents an IP address (or an IP network address) */ +static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r) +{ + int i; + int ip_addr[4]; + struct in_addr addr; + struct in_addr *ip_list; + char **ip_listptr; + const char *found; + const char *host = proxy_get_host_of_request(r); + + if (host == NULL) /* oops! */ + return 0; + + memset(&addr, '\0', sizeof addr); + memset(ip_addr, '\0', sizeof ip_addr); + + if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) { + for (addr.s_addr = 0, i = 0; i < 4; ++i) + addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i)); + + if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) { +#if DEBUGGING + fprintf(stderr, "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr)); + fprintf(stderr, "%s/", inet_ntoa(This->addr)); + fprintf(stderr, "%s\n", inet_ntoa(This->mask)); +#endif + return 1; + } +#if DEBUGGING + else { + fprintf(stderr, "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr)); + fprintf(stderr, "%s/", inet_ntoa(This->addr)); + fprintf(stderr, "%s\n", inet_ntoa(This->mask)); + } +#endif + } + else { + struct hostent the_host; + + memset(&the_host, '\0', sizeof the_host); + found = ap_proxy_host2addr(host, &the_host); + + if (found != NULL) { +#if DEBUGGING + fprintf(stderr, "2)IP-NoMatch: hostname=%s msg=%s\n", host, found); +#endif + return 0; + } + + if (the_host.h_name != NULL) + found = the_host.h_name; + else + found = host; + + /* Try to deal with multiple IP addr's for a host */ + for (ip_listptr = the_host.h_addr_list; *ip_listptr; ++ip_listptr) { + ip_list = (struct in_addr *)*ip_listptr; + if (This->addr.s_addr == (ip_list->s_addr & This->mask.s_addr)) { +#if DEBUGGING + fprintf(stderr, "3)IP-Match: %s[%s] <-> ", found, inet_ntoa(*ip_list)); + fprintf(stderr, "%s/", inet_ntoa(This->addr)); + fprintf(stderr, "%s\n", inet_ntoa(This->mask)); +#endif + return 1; + } +#if DEBUGGING + else { + fprintf(stderr, "3)IP-NoMatch: %s[%s] <-> ", found, inet_ntoa(*ip_list)); + fprintf(stderr, "%s/", inet_ntoa(This->addr)); + fprintf(stderr, "%s\n", inet_ntoa(This->mask)); + } +#endif + } + } + + return 0; +} + +/* Return TRUE if addr represents a domain name */ +int ap_proxy_is_domainname(struct dirconn_entry *This, pool *p) +{ + char *addr = This->name; + int i; + + /* Domain name must start with a '.' */ + if (addr[0] != '.') + return 0; + + /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ + for (i = 0; ap_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i) + continue; + +#if 0 + if (addr[i] == ':') { + fprintf(stderr, "@@@@ handle optional port in proxy_is_domainname()\n"); + /* @@@@ handle optional port */ + } +#endif + + if (addr[i] != '\0') + return 0; + + /* Strip trailing dots */ + for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) + addr[i] = '\0'; + + This->matcher = proxy_match_domainname; + return 1; +} + +/* Return TRUE if host "host" is in domain "domain" */ +static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r) +{ + const char *host = proxy_get_host_of_request(r); + int d_len = strlen(This->name), h_len; + + if (host == NULL) /* some error was logged already */ + return 0; + + h_len = strlen(host); + + /* @@@ do this within the setup? */ + /* Ignore trailing dots in domain comparison: */ + while (d_len > 0 && This->name[d_len - 1] == '.') + --d_len; + while (h_len > 0 && host[h_len - 1] == '.') + --h_len; + return h_len > d_len + && strncasecmp(&host[h_len - d_len], This->name, d_len) == 0; +} + +/* Return TRUE if addr represents a host name */ +int ap_proxy_is_hostname(struct dirconn_entry *This, pool *p) +{ + struct hostent host; + char *addr = This->name; + int i; + + /* Host names must not start with a '.' */ + if (addr[0] == '.') + return 0; + + /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */ + for (i = 0; ap_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i); + +#if 0 + if (addr[i] == ':') { + fprintf(stderr, "@@@@ handle optional port in proxy_is_hostname()\n"); + /* @@@@ handle optional port */ + } +#endif + + if (addr[i] != '\0' || ap_proxy_host2addr(addr, &host) != NULL) + return 0; + + This->hostentry = ap_pduphostent(p, &host); + + /* Strip trailing dots */ + for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i) + addr[i] = '\0'; + + This->matcher = proxy_match_hostname; + return 1; +} + +/* Return TRUE if host "host" is equal to host2 "host2" */ +static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r) +{ + char *host = This->name; + const char *host2 = proxy_get_host_of_request(r); + int h2_len; + int h1_len; + + if (host == NULL || host2 == NULL) + return 0; /* oops! */ + + h2_len = strlen(host2); + h1_len = strlen(host); + +#if 0 + unsigned long *ip_list; + + /* Try to deal with multiple IP addr's for a host */ + for (ip_list = *This->hostentry->h_addr_list; *ip_list != 0UL; ++ip_list) + if (*ip_list == ? ? ? ? ? ? ? ? ? ? ? ? ?) + return 1; +#endif + + /* Ignore trailing dots in host2 comparison: */ + while (h2_len > 0 && host2[h2_len - 1] == '.') + --h2_len; + while (h1_len > 0 && host[h1_len - 1] == '.') + --h1_len; + return h1_len == h2_len + && strncasecmp(host, host2, h1_len) == 0; +} + +/* Return TRUE if addr is to be matched as a word */ +int ap_proxy_is_word(struct dirconn_entry *This, pool *p) +{ + This->matcher = proxy_match_word; + return 1; +} + +/* Return TRUE if string "str2" occurs literally in "str1" */ +static int proxy_match_word(struct dirconn_entry *This, request_rec *r) +{ + const char *host = proxy_get_host_of_request(r); + return host != NULL && strstr(host, This->name) != NULL; +} + +int ap_proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r) +{ + int i; + + ap_hard_timeout("proxy connect", r); + do { + i = connect(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); +#if defined(WIN32) || defined(NETWARE) + if (i == SOCKET_ERROR) + errno = WSAGetLastError(); +#endif /* WIN32 */ + } while (i == -1 && errno == EINTR); + if (i == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy connect to %s port %d failed", + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port)); + } + ap_kill_timeout(r); + + return i; +} + +/* This function is called by ap_table_do() for all header lines + * (from proxy_http.c and proxy_ftp.c) + * It is passed a cache_req struct pointer and a MIME field and value pair + */ +int ap_proxy_send_hdr_line(void *p, const char *key, const char *value) +{ + cache_req *c = (cache_req *)p; + + if (key == NULL || value == NULL || value[0] == '\0') + return 1; + if (c->fp != NULL && + ap_bvputs(c->fp, key, ": ", value, CRLF, NULL) == -1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error writing header to %s", c->tempfile); + c = ap_proxy_cache_error(c); + return 0; /* no need to continue, it failed already */ + } + return 1; /* tell ap_table_do() to continue calling us + * for more headers */ +} + +/* send a text line to one or two BUFF's; return line length */ +unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache) +{ + unsigned len = ap_bputs(data, client); + if (cache != NULL && cache->fp != NULL) + ap_bputs(data, cache->fp); + return len; +} + +/* do a HTTP/1.1 age calculation */ +time_t ap_proxy_current_age(cache_req *c, const time_t age_value) +{ + time_t apparent_age, corrected_received_age, response_delay, corrected_initial_age, + resident_time, current_age; + + /* Perform an HTTP/1.1 age calculation. (RFC2616 13.2.3) */ + + apparent_age = MAX(0, c->resp_time - c->date); + corrected_received_age = MAX(apparent_age, age_value); + response_delay = c->resp_time - c->req_time; + corrected_initial_age = corrected_received_age + response_delay; + resident_time = time(NULL) - c->resp_time; + current_age = corrected_initial_age + resident_time; + + return (current_age); +} + +/* open a cache file and return a pointer to a BUFF */ +BUFF *ap_proxy_open_cachefile(request_rec *r, char *filename) +{ + BUFF *cachefp = NULL; + int cfd; + + if (filename != NULL) { + cfd = open(filename, O_RDWR | O_BINARY); + if (cfd != -1) { + ap_note_cleanups_for_fd(r->pool, cfd); + cachefp = ap_bcreate(r->pool, B_RD | B_WR); + ap_bpushfd(cachefp, cfd, cfd); + } + else if (errno != ENOENT) + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error opening cache file %s", + filename); + else + ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, r->server, "File %s not found", filename); + + } + return cachefp; +} + +/* create a cache file and return a pointer to a BUFF */ +BUFF *ap_proxy_create_cachefile(request_rec *r, char *filename) +{ + BUFF *cachefp = NULL; + int cfd; + + if (filename != NULL) { + cfd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0622); + if (cfd != -1) { + ap_note_cleanups_for_fd(r->pool, cfd); + cachefp = ap_bcreate(r->pool, B_WR); + ap_bpushfd(cachefp, -1, cfd); + } + else if (errno != ENOENT) + ap_log_rerror(APLOG_MARK, APLOG_ERR, r, + "proxy: error creating cache file %s", + filename); + } + return cachefp; +} + +/* Clear all connection-based headers from headers table */ +void ap_proxy_clear_connection(pool *p, table *headers) +{ + const char *name; + char *next = ap_pstrdup(p, ap_table_get(headers, "Connection")); + + /* Some proxies (Squid, ICS) use the non-standard "Proxy-Connection" header. */ + ap_table_unset(headers, "Proxy-Connection"); + + if (next != NULL) { + while (*next) { + name = next; + while (*next && !ap_isspace(*next) && (*next != ',')) + ++next; + while (*next && (ap_isspace(*next) || (*next == ','))) { + *next = '\0'; + ++next; + } + ap_table_unset(headers, name); + } + ap_table_unset(headers, "Connection"); + } + + /* unset hop-by-hop headers defined in RFC2616 13.5.1 */ + ap_table_unset(headers,"Keep-Alive"); + /* + * XXX: @@@ FIXME: "Proxy-Authenticate" should IMO *not* be stripped + * because in a chain of proxies some "front" proxy might need + * proxy authentication, while a "back-end" proxy which needs none can + * simply pass the "Proxy-Authenticate" back to the client, and pass + * the client's "Proxy-Authorization" to the front-end proxy. + * (See the note in proxy_http.c for the "Proxy-Authorization" case.) + * + * MnKr 04/2002 + */ + ap_table_unset(headers,"Proxy-Authenticate"); + ap_table_unset(headers,"TE"); + ap_table_unset(headers,"Trailer"); + /* it is safe to just chop the transfer-encoding header + * here, because proxy doesn't support any other encodings + * to the backend other than chunked. + */ + ap_table_unset(headers,"Transfer-Encoding"); + ap_table_unset(headers,"Upgrade"); + +} + +/* overlay one table on another + * keys in base will be replaced by keys in overlay + * + * Note: this has to be done in a special way, due + * to some nastiness when it comes to having multiple + * headers in the overlay table. First, we remove all + * the headers in the base table that are found in the + * overlay table, then we simply concatenate the + * tables together. + * + * The base and overlay tables need not be in the same + * pool (and probably won't be). + * + * If the base table is changed in any way through + * being overlayed with the overlay table, this + * function returns a 1. + */ +int ap_proxy_table_replace(table *base, table *overlay) +{ + table_entry *elts = (table_entry *)overlay->a.elts; + int i, q = 0; + const char *val; + + /* remove overlay's keys from base */ + for (i = 0; i < overlay->a.nelts; ++i) { + val = ap_table_get(base, elts[i].key); + if (!val || strcmp(val, elts[i].val)) { + q = 1; + } + if (val) { + ap_table_unset(base, elts[i].key); + } + } + + /* add overlay to base */ + for (i = 0; i < overlay->a.nelts; ++i) { + ap_table_add(base, elts[i].key, elts[i].val); + } + + return q; +} + +/* read the response line + * This function reads a single line of response from the server, + * and returns a status code. + * It also populates the request_rec with the resultant status, and + * returns backasswards status (HTTP/0.9). + */ +int ap_proxy_read_response_line(BUFF *f, request_rec *r, char *buffer, int size, int *backasswards, int *major, int *minor) { + + long len; + + len = ap_getline(buffer, size-1, f, 0); + if (len == -1) { + ap_bclose(f); + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Error reading from remote server"); + } + else if (len == 0) { + ap_bclose(f); + ap_kill_timeout(r); + return ap_proxyerror(r, HTTP_BAD_GATEWAY, + "Document contains no data"); + } + + /* + * Is it an HTTP/1 response? Do some sanity checks on the response. (This + * is buggy if we ever see an HTTP/1.10) + */ + if (ap_checkmask(buffer, "HTTP/#.# ###*")) { + + if (2 != sscanf(buffer, "HTTP/%u.%u", major, minor)) { + /* if no response, default to HTTP/1.1 - is this correct? */ + *major = 1; + *minor = 1; + } + + /* If not an HTTP/1 message */ + if (*major < 1) { + ap_bclose(f); + ap_kill_timeout(r); + return HTTP_BAD_GATEWAY; + } + *backasswards = 0; + + /* there need not be a reason phrase in the response, + * and ap_getline() already deleted trailing whitespace. + * But RFC2616 requires a SP after the Status-Code. Add one: + */ + if (strlen(buffer) < sizeof("HTTP/1.x 200 ")-1) + buffer = ap_pstrcat(r->pool, buffer, " ", NULL); + buffer[12] = '\0'; + r->status = atoi(&buffer[9]); + buffer[12] = ' '; + r->status_line = ap_pstrdup(r->pool, &buffer[9]); + + /* if the response was 100 continue, soak up any headers */ + if (r->status == 100) { + ap_proxy_read_headers(r, buffer, size, f); + } + + } + else { + + /* an http/0.9 response */ + *backasswards = 1; + r->status = 200; + r->status_line = "200 OK"; + *major = 0; + *minor = 9; + + } + + return OK; + +} + + +#if defined WIN32 + +static DWORD tls_index; + +BOOL WINAPI DllMain(HINSTANCE dllhandle, DWORD reason, LPVOID reserved) +{ + LPVOID memptr; + + switch (reason) { + case DLL_PROCESS_ATTACH: + tls_index = TlsAlloc(); + case DLL_THREAD_ATTACH: /* intentional no break */ + TlsSetValue(tls_index, malloc(sizeof(struct per_thread_data))); + break; + case DLL_THREAD_DETACH: + memptr = TlsGetValue(tls_index); + if (memptr) { + free(memptr); + TlsSetValue(tls_index, 0); + } + break; + } + + return TRUE; +} + +#endif + +static struct per_thread_data *get_per_thread_data(void) +{ +#if defined(WIN32) + + return (struct per_thread_data *)TlsGetValue(tls_index); + +#else + + static APACHE_TLS struct per_thread_data sptd; + return &sptd; + +#endif +} |