chuck 96/10/01 00:11:48
Modified: src/modules/proxy Makefile mod_proxy.c Added: src/modules/proxy mod_proxy.h proxy_cache.c proxy_connect.c proxy_ftp.c proxy_http.c proxy_util.c Log: Phase II - The Great Proxy Reorganization Layout with protocol abstraction, daemon gc in mind. Revision Changes Path 1.3 +15 -11 apache/src/modules/proxy/Makefile Index: Makefile =================================================================== RCS file: /export/home/cvs/apache/src/modules/proxy/Makefile,v retrieving revision 1.2 retrieving revision 1.3 diff -C3 -r1.2 -r1.3 *** Makefile 1996/09/29 13:58:55 1.2 --- Makefile 1996/10/01 07:11:41 1.3 *************** *** 50,56 **** # # Makefile for the Apache proxy library # ! # $Id: Makefile,v 1.2 1996/09/29 13:58:55 chuck Exp $ # SHELL = /bin/sh --- 50,56 ---- # # Makefile for the Apache proxy library # ! # $Id: Makefile,v 1.3 1996/10/01 07:11:41 chuck Exp $ # SHELL = /bin/sh *************** *** 59,70 **** LIB=libproxy.a ! # define -DEXPLAIN if you want verbose debugging output CFLAGS=-I. -I$(INCDIR) $(AUX_CFLAGS) # Internal stuff, should not need changing. ! OBJS=mod_proxy.o ! PROXYSRC=mod_proxy.c default: $(LIB) --- 59,73 ---- LIB=libproxy.a ! # AUX_CFLAGS comes from higher level Makefile CFLAGS=-I. -I$(INCDIR) $(AUX_CFLAGS) # Internal stuff, should not need changing. ! OBJS=mod_proxy.o proxy_cache.o proxy_connect.o proxy_ftp.o proxy_http.o \ ! proxy_util.o ! ! PROXYSRC=mod_proxy.c proxy_cache.c proxy_connect.c proxy_ftp.c proxy_http.c \ ! proxy_util.c default: $(LIB) *************** *** 74,86 **** $(RANLIB) $@ # dependencies ! mod_proxy.o: $(INCDIR)/http_log.h ! mod_proxy.o: $(INCDIR)/http_main.h ! mod_proxy.o: $(INCDIR)/http_protocol.h ! mod_proxy.o: $(INCDIR)/http_config.h ! mod_proxy.o: $(INCDIR)/httpd.h ! mod_proxy.o: $(INCDIR)/md5.h ! mod_proxy.o: $(INCDIR)/explain.h # various forms of cleanup tidy: --- 77,90 ---- $(RANLIB) $@ # dependencies ! mod_proxy.o proxy_cache.o proxy_connect.o proxy_ftp.o proxy_http.o \ ! proxy_util.o: mod_proxy.h ! mod_proxy.o: $(INCDIR)/httpd.h $(INCDIR)/http_config.h ! proxy_cache.o: $(INCDIR)/httpd.h $(INCDIR)/http_config.h $(INCDIR)/md5.h ! proxy_connect.o: $(INCDIR)/httpd.h $(INCDIR)/http_config.h ! proxy_ftp.o: $(INCDIR)/httpd.h $(INCDIR)/http_config.h ! proxy_http.o: $(INCDIR)/httpd.h $(INCDIR)/http_config.h ! proxy_util.o: $(INCDIR)/httpd.h $(INCDIR)/http_config.h $(INCDIR)/md5.h # various forms of cleanup tidy: 1.3 +51 -3052 apache/src/modules/proxy/mod_proxy.c Index: mod_proxy.c =================================================================== RCS file: /export/home/cvs/apache/src/modules/proxy/mod_proxy.c,v retrieving revision 1.2 retrieving revision 1.3 diff -C3 -r1.2 -r1.3 *** mod_proxy.c 1996/09/29 14:10:58 1.2 --- mod_proxy.c 1996/10/01 07:11:42 1.3 *************** *** 50,197 **** * */ ! /* $Id: mod_proxy.c,v 1.2 1996/09/29 14:10:58 chuck Exp $ */ ! /* ! Note that the Explain() stuff is not yet complete. ! Also note numerous FIXMEs and CHECKMEs which should be eliminated. ! ! If TESTING is set, then garbage collection doesn't delete ... probably a good ! idea when hacking. ! ! This code is still experimental! ! ! Things to do: ! ! 1. Make it garbage collect in the background, not while someone is waiting for ! a response! ! ! 2. Check the logic thoroughly. ! ! 3. Empty directories are only removed the next time round (but this does avoid ! two passes). Consider doing them the first time round. ! ! Ben Laurie <[EMAIL PROTECTED]> 30 Mar 96 ! ! More things to do: ! ! 0. Massive code cleanup & break into multiple files; link as a lib ! ! 1. add PASV mode for ftp now that it works ! ! 2. Add gopher & WAIS ! ! 3. Various other fixups to insure no NULL strings parsed, etc. ! ! 4. NoProxy directive for excluding sites to proxy ! ! 5. Imply NoCache * if cache directory is not configured, to enable proxy ! without cache (and avoid SIGSEGV) ! ! 6. Implement protocol handler struct a la Apache module handlers ! ! 7. Use a cache expiry database for more efficient GC ! ! 8. Handle multiple IPs for doconnect() ! ! 9. Bulletproof GC against SIGALRM ! ! Chuck Murcko <[EMAIL PROTECTED]> 28 Sep 96 ! ! */ ! ! #define TESTING 0 ! #undef EXPLAIN ! ! #include "httpd.h" ! #include "http_config.h" ! #include "http_log.h" ! #include "http_main.h" ! #include "http_protocol.h" ! ! #include "md5.h" ! ! #include <utime.h> ! ! #include "explain.h" ! ! DEF_Explain ! ! #define SEC_ONE_DAY 86400 /* one day, in seconds */ ! #define SEC_ONE_HR 3600 /* one hour, in seconds */ ! ! #define DEFAULT_FTP_DATA_PORT 20 ! #define DEFAULT_FTP_PORT 21 ! #define DEFAULT_GOPHER_PORT 70 ! #define DEFAULT_NNTP_PORT 119 ! #define DEFAULT_WAIS_PORT 210 ! #define DEFAULT_HTTPS_PORT 443 ! #define DEFAULT_SNEWS_PORT 563 ! #define DEFAULT_PROSPERO_PORT 1525 /* WARNING: conflict w/Oracle */ ! ! /* Some WWW schemes and their default ports; this is basically /etc/services */ ! static struct ! { ! const char *scheme; ! int port; ! } defports[]={ ! { "ftp", DEFAULT_FTP_PORT}, ! { "gopher", DEFAULT_GOPHER_PORT}, ! { "http", DEFAULT_PORT}, ! { "nntp", DEFAULT_NNTP_PORT}, ! { "wais", DEFAULT_WAIS_PORT}, ! { "https", DEFAULT_HTTPS_PORT}, ! { "snews", DEFAULT_SNEWS_PORT}, ! { "prospero", DEFAULT_PROSPERO_PORT}, ! { NULL, -1} /* unknown port */ ! }; ! ! ! /* static information about a remote proxy */ ! struct proxy_remote ! { ! const char *scheme; /* the schemes handled by this proxy, or '*' */ ! const char *protocol; /* the scheme used to talk to this proxy */ ! const char *hostname; /* the hostname of this proxy */ ! int port; /* the port for this proxy */ ! }; ! ! struct proxy_alias { ! char *real; ! char *fake; ! }; ! ! struct nocache_entry { ! char *name; ! }; ! ! #define DEFAULT_CACHE_SPACE 5 ! #define DEFAULT_CACHE_MAXEXPIRE SEC_ONE_DAY ! #define DEFAULT_CACHE_EXPIRE SEC_ONE_HR ! #define DEFAULT_CACHE_LMFACTOR (0.1) ! ! /* static information about the local cache */ ! struct cache_conf ! { ! const char *root; /* the location of the cache directory */ ! int space; /* Maximum cache size (in 1024 bytes) */ ! int maxexpire; /* Maximum time to keep cached files in secs */ ! int defaultexpire; /* default time to keep cached file in secs */ ! double lmfactor; /* factor for estimating expires date */ ! int gcinterval; /* garbage collection interval, in seconds */ ! int dirlevels; /* Number of levels of subdirectories */ ! int dirlength; /* Length of subdirectory names */ ! }; ! ! typedef struct ! { ! ! struct cache_conf cache; /* cache configuration */ ! array_header *proxies; ! array_header *aliases; ! array_header *nocaches; ! int req; /* true if proxy requests are enabled */ ! } proxy_server_conf; /* * A Web proxy module. Stages: --- 50,58 ---- * */ ! /* $Id: mod_proxy.c,v 1.3 1996/10/01 07:11:42 chuck Exp $ */ ! #include "mod_proxy.h" /* * A Web proxy module. Stages: *************** *** 203,252 **** * handler: handle proxy requests */ - struct hdr_entry - { - char *field; - char *value; - }; - - /* caching information about a request */ - struct cache_req - { - request_rec *req; /* the request */ - char *url; /* the URL requested */ - char *filename; /* name of the cache file, or NULL if no cache */ - char *tempfile; /* name of the temporary file, of NULL if not caching */ - time_t ims; /* if-modified-since date of request; -1 if no header */ - BUFF *fp; /* the cache file descriptor if the file is cached - and may be returned, or NULL if the file is - not cached (or must be reloaded) */ - time_t expire; /* calculated expire date of cached entity */ - time_t lmod; /* last-modified date of cached entity */ - time_t date; /* the date the cached file was last touched */ - int version; /* update count of the file */ - unsigned int len; /* content length */ - char *protocol; /* Protocol, and major/minor number, e.g. HTTP/1.1 */ - int status; /* the status of the cached file */ - char *resp_line; /* the whole status like (protocol, code + message) */ - array_header *hdrs; /* the HTTP headers of the file */ - }; - - - extern module proxy_module; - - - static int http_canon(request_rec *r, char *url, const char *scheme, - int def_port); - static int ftp_canon(request_rec *r, char *url); - - static int http_handler(request_rec *r, struct cache_req *c, char *url, - const char *proxyhost, int proxyport); - static int ftp_handler(request_rec *r, struct cache_req *c, char *url); - - static int connect_handler(request_rec *r, struct cache_req *c, char *url); - - static BUFF *cache_error(struct cache_req *r); - /* -------------------------------------------------------------- */ /* Translate the URL into a 'filename' */ --- 64,69 ---- *************** *** 332,2270 **** proxy_fixup(request_rec *r) { char *url, *p; ! int i; ! ! if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; ! ! url = &r->filename[6]; ! /* lowercase the scheme */ ! p = strchr(url, ':'); ! if (p == NULL || p == url) return BAD_REQUEST; ! for (i=0; i != p - url; i++) url[i] = tolower(url[i]); ! ! /* canonicalise each specific scheme */ ! if (strncmp(url, "http:", 5) == 0) ! return http_canon(r, url+5, "http", DEFAULT_PORT); ! else if (strncmp(url, "ftp:", 4) == 0) return ftp_canon(r, url+4); ! else return OK; /* otherwise; we've done the best we can */ ! } ! ! /* already called in the knowledge that the characters are hex digits */ ! static int ! hex2c(const char *x) ! { ! int i, ch; ! ! ch = x[0]; ! if (isdigit(ch)) i = ch - '0'; ! else if (isupper(ch)) i = ch - ('A' - 10); ! else i = ch - ('a' - 10); ! i <<= 4; ! ! ch = x[1]; ! if (isdigit(ch)) i += ch - '0'; ! else if (isupper(ch)) i += ch - ('A' - 10); ! else i += ch - ('a' - 10); ! return i; ! } ! ! ! static void ! c2hex(int ch, char *x) ! { ! 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; ! } ! ! /* ! * canonicalise a URL-encoded string ! */ ! ! enum enctype { enc_path, enc_search, enc_user, enc_fpath, enc_parm }; ! ! /* ! * Decodes a '%' escaped string, and returns the number of characters ! */ ! static int ! decodeenc(char *x) ! { ! int i, j, ch; ! ! if (x[0] == '\0') return 0; /* special case for no characters */ ! for (i=0, j=0; x[i] != '\0'; i++, j++) ! { ! /* decode it if not already done */ ! ch = x[i]; ! if ( ch == '%' && isxdigit(x[i+1]) && isxdigit(x[i+2])) ! { ! ch = hex2c(&x[i+1]); ! i += 2; ! } ! x[j] = ch; ! } ! x[j] = '\0'; ! return j; ! } ! ! ! /* ! * 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. ! */ ! static char * ! canonenc(pool *p, const char *x, int len, enum enctype t, int isenc) ! { ! int i, j, ispath, 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 = palloc(p, 3*len+1); ! ispath = (t == enc_path); ! ! for (i=0, j=0; i < len; i++, j++) ! { ! /* always handle '/' first */ ! ch = x[i]; ! if (ind(reserved, ch) != -1) ! { ! y[j] = ch; ! continue; ! } ! /* decode it if not already done */ ! if (isenc && ch == '%') ! { ! if (!isxdigit(x[i+1]) || !isxdigit(x[i+2])) ! return NULL; ! ch = hex2c(&x[i+1]); ! i += 2; ! if (ch != 0 && ind(reserved, ch) != -1) ! { /* keep it encoded */ ! c2hex(ch, &y[j]); ! j += 2; ! continue; ! } ! } ! /* recode it, if necessary */ ! if (!isalnum(ch) && ind(allowed, ch) == -1) ! { ! 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. ! */ ! static char * ! canon_netloc(pool *pool, char **const urlp, char **userp, char **passwordp, ! char **hostp, int *port) ! { ! int i; ! char *p, *host, *url=*urlp; ! ! if (url[0] != '/' || url[1] != '/') return "Malformed URL"; ! host = url + 2; ! url = strchr(host, '/'); ! if (url == NULL) ! url = ""; ! else ! *(url++) = '\0'; /* skip seperating '/' */ ! ! if (userp != NULL) ! { ! char *user=NULL, *password = NULL; ! p = strchr(host, '@'); ! ! if (p != NULL) ! { ! *p = '\0'; ! user = host; ! host = p + 1; ! ! /* find password */ ! p = strchr(user, ':'); ! if (p != NULL) ! { ! *p = '\0'; ! password = canonenc(pool, p+1, strlen(p+1), enc_user, 1); ! if (password == NULL) ! return "Bad %-escape in URL (password)"; ! } ! ! user = canonenc(pool, user, strlen(user), enc_user, 1); ! if (user == NULL) return "Bad %-escape in URL (username)"; ! } ! *userp = user; ! *passwordp = password; ! } ! ! p = strchr(host, ':'); ! if (p != NULL) ! { ! *(p++) = '\0'; ! ! for (i=0; p[i] != '\0'; i++) ! if (!isdigit(p[i])) break; ! ! if (i == 0 || p[i] != '\0') ! return "Bad port number in URL"; ! *port = atoi(p); ! if (*port > 65535) return "Port number in URL > 65535"; ! } ! 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 (!isdigit(host[i]) && host[i] != '.') ! break; ! /* must be an IP address */ ! if (host[i] == '\0' && (inet_addr(host) == -1 || inet_network(host) == -1)) ! return "Bad IP address in URL"; ! ! *urlp = url; ! *hostp = host; ! ! return NULL; ! } ! ! /* ! * checks an encoded ftp string for bad characters, namely, CR, LF or ! * non-ascii character ! */ ! static int ! ftp_check_string(const char *x) ! { ! int i, ch; ! ! for (i=0; x[i] != '\0'; i++) ! { ! ch = x[i]; ! if ( ch == '%' && isxdigit(x[i+1]) && isxdigit(x[i+2])) ! { ! ch = hex2c(&x[i+1]); ! i += 2; ! } ! if (ch == '\015' || ch == '\012' || (ch & 0x80)) return 0; ! } ! return 1; ! } ! ! /* ! * Canonicalise ftp URLs. ! */ ! static int ! ftp_canon(request_rec *r, char *url) ! { ! char *user, *password, *host, *path, *parms, *p, sport[7]; ! const char *err; ! int port; ! ! port = DEFAULT_FTP_PORT; ! err = canon_netloc(r->pool, &url, &user, &password, &host, &port); ! if (err) return BAD_REQUEST; ! if (user != NULL && !ftp_check_string(user)) return BAD_REQUEST; ! if (password != NULL && !ftp_check_string(password)) return BAD_REQUEST; ! ! /* now parse path/parameters args, according to rfc1738 */ ! /* N.B. if this isn't a true proxy request, then the URL path ! * (but not query args) has already been decoded. ! * This gives rise to the problem of a ; being decoded into the ! * path. ! */ ! p = strchr(url, ';'); ! if (p != NULL) ! { ! *(p++) = '\0'; ! parms = canonenc(r->pool, p, strlen(p), enc_parm, r->proxyreq); ! if (parms == NULL) return BAD_REQUEST; ! } else ! parms = ""; ! ! path = canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq); ! if (path == NULL) return BAD_REQUEST; ! if (!ftp_check_string(path)) return BAD_REQUEST; ! ! if (!r->proxyreq && r->args != NULL) ! { ! if (p != NULL) ! { ! p = canonenc(r->pool, r->args, strlen(r->args), enc_parm, 1); ! if (p == NULL) return BAD_REQUEST; ! parms = pstrcat(r->pool, parms, "?", p, NULL); ! } ! else ! { ! p = canonenc(r->pool, r->args, strlen(r->args), enc_path, 1); ! if (p == NULL) return BAD_REQUEST; ! path = pstrcat(r->pool, path, "?", p, NULL); ! } ! r->args = NULL; ! } ! ! /* now, rebuild URL */ ! ! if (port != DEFAULT_FTP_PORT) sprintf(sport, ":%d", port); ! else sport[0] = '\0'; ! ! r->filename = pstrcat(r->pool, "proxy:ftp://", (user != NULL) ? user : "", ! (password != NULL) ? ":" : "", ! (password != NULL) ? password : "", ! (user != NULL) ? "@" : "", host, sport, "/", path, ! (parms[0] != '\0') ? ";" : "", parms, NULL); ! ! return OK; ! } ! ! ! /* ! * Canonicalise http-like URLs. ! * scheme is the scheme for the URL ! * url is the URL starting with the first '/' ! * def_port is the default port for this scheme. ! */ ! static int ! http_canon(request_rec *r, char *url, const char *scheme, int def_port) ! { ! char *host, *path, *search, *p, sport[7]; ! const char *err; ! int port; ! ! /* do syntatic check. ! * We break the URL into host, port, path, search ! */ ! port = def_port; ! err = canon_netloc(r->pool, &url, NULL, NULL, &host, &port); ! if (err) return BAD_REQUEST; ! ! /* now parse path/search args, according to rfc1738 */ ! /* N.B. if this isn't a true proxy request, then the URL _path_ ! * has already been decoded ! */ ! if (r->proxyreq) ! { ! p = strchr(url, '?'); ! if (p != NULL) *(p++) = '\0'; ! } else ! p = r->args; ! ! /* process path */ ! path = canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq); ! if (path == NULL) return BAD_REQUEST; ! ! /* process search */ ! if (p != NULL) ! { ! search = p; ! if (search == NULL) return BAD_REQUEST; ! } else ! search = NULL; ! ! if (port != def_port) sprintf(sport, ":%d", port); ! else sport[0] = '\0'; ! ! r->filename = pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/", ! path, (search) ? "?" : "", (search) ? search : "", NULL); ! return OK; ! } ! ! static const char *lwday[7]= ! {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; ! static const char *wday[7]= ! {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; ! static const char *months[12]= ! {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", ! "Dec"}; ! ! /* ! * 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. ! */ ! static char * ! date_canon(pool *p, 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 (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 acstime() date */ ! if (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, wday[wk]) == 0) break; ! if (wk == 7) return x; ! } ! ! /* check date */ ! for (mon=0; mon < 12; mon++) if (strcmp(month, months[mon]) == 0) break; ! if (mon == 12) return x; ! /* ! * it doesn't do any harm to convert an invalid date from one format to ! * another ! */ ! #if 0 ! if (hour > 23 || min > 60 || sec > 62 || mday == 0 || mday > 31) return x; ! if (mday == 31 && (mon == 1 || mon == 3 || mon == 5 || mon == 8 || mon == 10)) ! return x; ! if (mday > 29 && mon == 1) return x; ! if (mday == 29 && mon == 1) ! if (year%4 != 0 || (year%100 == 0 && year%400 != 0)) return x; ! #endif ! ! if (strlen(x) < 31) x = palloc(p, 31); ! sprintf(x, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", wday[wk], mday, ! months[mon], year, hour, min, sec); ! return x; ! } ! ! ! /* -------------------------------------------------------------- */ ! /* Invoke handler */ ! ! /* Utility routines */ ! ! /* ! * Reads headers from a connection and returns an array of headers. ! * Returns NULL on file error ! */ ! static array_header * ! read_headers(pool *pool, char *buffer, int size, BUFF *f) ! { ! int gotcr, len, i, j; ! array_header *resp_hdrs; ! struct hdr_entry *hdr; ! char *p; ! ! resp_hdrs = make_array(pool, 10, sizeof(struct hdr_entry)); ! hdr = NULL; ! ! gotcr = 1; ! for (;;) ! { ! len = bgets(buffer, size, f); ! if (len == -1) return NULL; ! if (len == 0) break; ! if (buffer[len-1] == '\n') ! { ! buffer[--len] = '\0'; ! i = 1; ! } else ! i = 0; ! ! if (!gotcr || buffer[0] == ' ' || buffer[0] == '\t') ! { ! /* a continuation header */ ! if (hdr == NULL) ! { ! /* error!! */ ! if (!i) ! { ! i = bskiplf(f); ! if (i == -1) return NULL; ! } ! gotcr = 1; ! continue; ! } ! hdr->value = pstrcat(pool, hdr->value, buffer, NULL); ! } ! else if (gotcr && len == 0) break; ! else ! { ! p = strchr(buffer, ':'); ! if (p == NULL) ! { ! /* error!! */ ! if (!gotcr) ! { ! i = bskiplf(f); ! if (i == -1) return NULL; ! } ! gotcr = 1; ! hdr = NULL; ! continue; ! } ! hdr = push_array(resp_hdrs); ! *(p++) = '\0'; ! hdr->field = pstrdup(pool, buffer); ! while (*p == ' ' || *p == '\t') p++; ! hdr->value = pstrdup(pool, p); ! gotcr = i; ! } ! } ! ! hdr = (struct hdr_entry *)resp_hdrs->elts; ! for (i=0; i < resp_hdrs->nelts; i++) ! { ! p = hdr[i].value; ! j = strlen(p); ! while (j > 0 && (p[j-1] == ' ' || p[j-1] == '\t')) j--; ! p[j] = '\0'; ! } ! ! return resp_hdrs; ! } ! ! static long int ! send_dir(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c, char *url) ! { ! char buf[IOBUFSIZE]; ! char buf2[IOBUFSIZE]; ! char *filename; ! char urlptr[100]; ! long total_bytes_sent; ! register int n, o, w; ! conn_rec *con = r->connection; ! ! sprintf(buf,"<HTML><HEAD><TITLE>%s</TITLE></HEAD><BODY><H1>Directory %s</H1><HR><PRE>", url, url); ! bwrite(con->client, buf, strlen(buf)); ! if (f2 != NULL) bwrite(f2, buf, strlen(buf)); ! total_bytes_sent=strlen(buf); ! while(!con->aborted) ! { ! n = bgets(buf, IOBUFSIZE, f); ! if (n == -1) /* input error */ ! { ! if (f2 != NULL) f2 = cache_error(c); ! break; ! } ! if (n == 0) break; /* EOF */ ! if(buf[0]=='l') ! { ! char *link; ! ! link=strstr(buf, " -> "); ! filename=link; ! do filename--; while (filename[0]!=' '); ! *(filename++)=0; ! *(link++)=0; ! sprintf(urlptr, "%s%s%s",url,(url[strlen(url)-1]=='/' ? "" : "/"), filename); ! sprintf(buf2, "%s <A HREF=\"%s\">%s %s</A>\015\012", buf, urlptr, filename, link); ! strcpy(buf, buf2); ! n=strlen(buf); ! } ! else if(buf[0]=='d' || buf[0]=='-' || buf[0]=='l') ! { ! filename=strrchr(buf, ' '); ! *(filename++)=0; ! filename[strlen(filename)-1]=0; ! /* Special handling for '.' and '..' */ ! if (!strcmp(filename, ".")) ! { ! sprintf(urlptr, "%s",url); ! sprintf(buf2, "%s <A HREF=\"%s\">%s</A>\015\012", buf, urlptr, filename); ! } ! else if (!strcmp(filename, "..")) ! { ! char temp[200]; ! char newpath[200]; ! char *method, *host, *path, *file, *newfile; ! ! strcpy(temp,url); ! method=temp; ! ! host=strchr(method,':'); ! if (host == NULL) host=""; ! else *(host++)=0; ! host++; host++; ! ! path=strchr(host,'/'); ! if (path == NULL) path=""; ! else *(path++)=0; ! ! strcpy(newpath,path); ! newfile=strrchr(newpath,'/'); ! if (newfile) *(newfile)=0; ! else newpath[0]=0; ! ! sprintf(urlptr,"%s://%s/%s",method,host,newpath); ! sprintf(buf2, "%s <A HREF=\"%s\">%s</A>\015\012", buf, urlptr, filename); ! } ! else ! { ! sprintf(urlptr, "%s%s%s",url,(url[strlen(url)-1]=='/' ? "" : "/"), filename); ! sprintf(buf2, "%s <A HREF=\"%s\">%s</A>\015\012", buf, urlptr, filename); ! } ! strcpy(buf, buf2); ! n=strlen(buf); ! } ! ! o=0; ! total_bytes_sent += n; ! ! if (f2 != NULL) ! if (bwrite(f2, buf, n) != n) f2 = cache_error(c); ! ! while(n && !r->connection->aborted) { ! w = bwrite(con->client, &buf[o], n); ! if (w <= 0) ! break; ! reset_timeout(r); /* reset timeout after successfule write */ ! n-=w; ! o+=w; ! } ! } ! sprintf(buf,"</PRE><HR><I><A HREF=\"http://www.apache.org\">%s</A></I></BODY></HTML>", SERVER_VERSION); ! bwrite(con->client, buf, strlen(buf)); ! if (f2 != NULL) bwrite(f2, buf, strlen(buf)); ! total_bytes_sent+=strlen(buf); ! bflush(con->client); ! ! return total_bytes_sent; ! } ! ! static long int ! send_fb(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c) ! { ! char buf[IOBUFSIZE]; ! long total_bytes_sent; ! register int n,o,w; ! conn_rec *con = r->connection; ! ! total_bytes_sent = 0; ! while (!con->aborted) { ! n = bread(f, buf, IOBUFSIZE); ! if (n == -1) /* input error */ ! { ! if (f2 != NULL) f2 = cache_error(c); ! break; ! } ! if (n == 0) break; /* EOF */ ! o=0; ! total_bytes_sent += n; ! ! if (f2 != NULL) ! if (bwrite(f2, buf, n) != n) f2 = cache_error(c); ! ! while(n && !r->connection->aborted) { ! w = bwrite(con->client, &buf[o], n); ! if (w <= 0) ! break; ! reset_timeout(r); /* reset timeout after successfule write */ ! n-=w; ! o+=w; ! } ! } ! bflush(con->client); ! ! return total_bytes_sent; ! } ! ! /* ! * Read a header from the array, returning the first entry ! */ ! static struct hdr_entry * ! get_header(array_header *hdrs_arr, const char *name) ! { ! struct hdr_entry *hdrs; ! int i; ! ! hdrs = (struct hdr_entry *)hdrs_arr->elts; ! for (i = 0; i < hdrs_arr->nelts; i++) ! if (hdrs[i].field != NULL && strcasecmp(name, hdrs[i].field) == 0) ! return &hdrs[i]; ! ! return NULL; ! } ! ! #define HDR_APP (0) ! #define HDR_REP (1) ! ! /* ! * Add to the header reply, either concatenating, or replacing existin ! * headers. It stores the pointers provided, so make sure the data ! * is not subsequently overwritten ! */ ! static struct hdr_entry * ! add_header(array_header *hdrs_arr, char *field, char *value, ! int rep) ! { ! int i; ! struct hdr_entry *hdrs; ! ! hdrs = (struct hdr_entry *)hdrs_arr->elts; ! if (rep) ! for (i = 0; i < hdrs_arr->nelts; i++) ! if (hdrs[i].field != NULL && strcasecmp(field, hdrs[i].field) == 0) ! { ! hdrs[i].value = value; ! return hdrs; ! } ! ! hdrs = push_array(hdrs_arr); ! hdrs->field = field; ! hdrs->value = value; ! ! return hdrs; ! } ! ! #ifdef NEEDED ! static void ! del_header(array_header *hdrs_arr, const char *field) ! { ! int i; ! struct hdr_entry *hdrs; ! ! hdrs = (struct hdr_entry *)hdrs_arr->elts; ! ! for (i = 0; i < hdrs_arr->nelts; i++) ! if (hdrs[i].field != NULL && strcasecmp(field, hdrs[i].field) == 0) ! hdrs[i].value = NULL; ! } ! #endif ! ! /* ! * Sends response line and headers ! */ ! static void ! send_headers(BUFF *fp, const char *respline, array_header *hdrs_arr) ! { ! struct hdr_entry *hdrs; ! int i; ! ! hdrs = (struct hdr_entry *)hdrs_arr->elts; ! ! bputs(respline, fp); ! bputs("\015\012", fp); ! for (i = 0; i < hdrs_arr->nelts; i++) ! { ! if (hdrs[i].field == NULL) continue; ! bvputs(fp, hdrs[i].field, ": ", hdrs[i].value, "\015\012", NULL); ! } ! ! bputs("\015\012", fp); ! } ! ! ! /* ! * 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. ! */ ! static int ! liststr(const char *list, const char *val) ! { ! int len, i; ! const char *p; ! ! len = strlen(val); ! ! while (list != NULL) ! { ! p = strchr(list, ','); ! if (p != NULL) ! { ! i = p - list; ! do p++; while (isspace(*p)); ! } ! else ! i = strlen(list); ! ! while (i > 0 && isspace(list[i-1])) i--; ! if (i == len && strncasecmp(list, val, len) == 0) return 1; ! list = p; ! } ! return 0; ! } ! ! /* number of characters in the hash */ ! #define HASH_LEN (22*2) ! ! static void ! hash(const char *it, char *val,int ndepth,int nlength) ! { ! MD5_CTX context; ! unsigned char digest[16]; ! char tmp[22]; ! int i, k, d; ! unsigned int x; ! static const char table[64]= ! "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@"; ! ! MD5Init(&context); ! MD5Update(&context, (const unsigned char *)it, strlen(it)); ! 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++] = table[x >> 18]; ! tmp[k++] = table[(x >> 12) & 0x3f]; ! tmp[k++] = table[(x >> 6) & 0x3f]; ! tmp[k++] = table[x & 0x3f]; ! } ! /* one byte left */ ! x = digest[15]; ! tmp[k++] = table[x >> 2]; /* use up 6 bits */ ! tmp[k++] = table[(x << 4) & 0x3f]; ! /* now split into directory levels */ ! ! for(i=k=d=0 ; d < ndepth ; ++d) ! { ! strncpy(&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'; ! } ! ! /* ! * Compare a string to a mask ! * Mask characters: ! * @ - uppercase letter ! * # - lowercase letter ! * & - hex digit ! * # - digit ! * * - swallow remaining characters ! * <x> - exact match for any other character ! */ ! static int ! checkmask(const char *data, const char *mask) ! { ! int i, ch, d; ! ! for (i=0; mask[i] != '\0' && mask[i] != '*'; i++) ! { ! ch = mask[i]; ! d = data[i]; ! if (ch == '@') ! { ! if (!isupper(d)) return 0; ! } else if (ch == '$') ! { ! if (!islower(d)) return 0; ! } else if (ch == '#') ! { ! if (!isdigit(d)) return 0; ! } else if (ch == '&') ! { ! if (!isxdigit(d)) return 0; ! } else if (ch != d) return 0; ! } ! ! if (mask[i] == '*') return 1; ! else return (data[i] == '\0'); ! } ! ! /* ! * This routine converts a tm structure into the number of seconds ! * since 1st January 1970 UT ! * ! * The return value is a non-negative integer on success or -1 if the ! * input date is out of the domain Thu, 01 Jan 1970 00:00:00 to ! * Tue, 19 Jan 2038 03:14:07 inclusive ! * ! * Notes ! * This routine has been tested on 1000000 valid dates generated ! * at random by gmtime(). ! * ! * This routine is very fast, much faster than mktime(). ! */ ! static int ! tm2sec(const struct tm *t) ! { ! int days, year; ! static const int dayoffs[12]= ! {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275}; ! ! year = t->tm_year; ! /* shift new year to 1st March; which is where it should be */ ! if (t->tm_mon < 2) year--; /* now years and months since 1st March 1900 */ ! days = t->tm_mday - 1 + dayoffs[t->tm_mon]; ! ! /* find the number of days since 1st March 1900 (in the Gregorian calendar) */ ! days += year * 365 + year/4 - year/100 + (year/100 + 3)/4; ! days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */ ! ! days = ((days * 24 + t->tm_hour) * 60 + t->tm_min) * 60 + t->tm_sec; ! if (year < 69 || year > 138 || days < 0) /* must have overflowed */ ! return -1; ! else ! return days; ! } ! ! /* ! * Parses a standard HTTP date. ! * ! * The restricted HTTP syntax is ! * rfc1123-date = day "," SP 2DIGIT SP date SP time SP "GMT" ! * date = 2DIGIT SP month SP 4DIGIT ! * time = 2DIGIT ":" 2DIGIT ":" 2DIGIT ! * ! * day = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun" ! * ! * month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | ! * "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec" ! * ! * The spec is not clear as to whether the day and months are ! * case-sensitive or not. This code assumes they are. ! * ! * It fills in the year, month, mday, hour, min, sec and is_dst fields of ! * date. It does not set the wday or yday fields. ! * On failure is sets the year to 0. ! * ! * It also returns the number of seconds since 1 Jan 1970 UT, or ! * -1 if this would be out of range or if the date is invalid. ! * ! * Notes ! * This routine has been tested on 100000 valid dates generated ! * at random by strftime(). ! * ! * This routine is very fast; it would be 10x slower if it ! * used sscanf. ! * ! * From Andrew Daviel <[EMAIL PROTECTED]> 29 Jul 96: ! * ! * Expanded to include RFC850 date (used by Netscape) ! * rfc850-date = weekday "," SP 2DIGIT "-" month "-" 2DIGIT SP time SP "GMT" ! * Netscape also appends "; length nnnn" to If-Modified-Since; allow this ! * ! */ ! static int ! parsedate(const char *date, struct tm *d) ! { ! int mint, mon, year; ! char* comma; ! int lday; ! struct tm x; ! const int months[12]={ ! ('J' << 16) | ( 'a' << 8) | 'n', ('F' << 16) | ( 'e' << 8) | 'b', ! ('M' << 16) | ( 'a' << 8) | 'r', ('A' << 16) | ( 'p' << 8) | 'r', ! ('M' << 16) | ( 'a' << 8) | 'y', ('J' << 16) | ( 'u' << 8) | 'n', ! ('J' << 16) | ( 'u' << 8) | 'l', ('A' << 16) | ( 'u' << 8) | 'g', ! ('S' << 16) | ( 'e' << 8) | 'p', ('O' << 16) | ( 'c' << 8) | 't', ! ('N' << 16) | ( 'o' << 8) | 'v', ('D' << 16) | ( 'e' << 8) | 'c'}; ! if (d == NULL) d = &x; ! ! d->tm_year = 0; /* bad date */ ! comma = index(date,',') ; ! lday = (comma-date) ; ! ! if( lday >= 6 && lday <= 8) { /* RFC850 */ ! date = comma - 3 ; ! if (!checkmask(date, "day, [EMAIL PROTECTED] ##:##:## GMT") && ! !checkmask(date, "day, [EMAIL PROTECTED] ##:##:## GMT;*")) return -1; ! } else { /* RFC1123 */ ! if (!checkmask(date, "@$$, ## @$$ #### ##:##:## GMT") && ! !checkmask(date, "@$$, ## @$$ #### ##:##:## GMT;*")) return -1; ! } ! ! /* we don't test the weekday */ ! d->tm_mday = (date[5] - '0') * 10 + (date[6] - '0'); ! if (d->tm_mday == 0 || d->tm_mday > 31) return -1; ! ! mint = (date[8] << 16) | (date[9] << 8) | date[10]; ! for (mon=0; mon < 12; mon++) if (mint == months[mon]) break; ! if (mon == 12) return -1; ! ! d->tm_mon = mon; ! if( lday >= 6 && lday <= 8) { /* RFC850 */ ! year = 1900 + date[12] * 10 + date[13] - ('0' * 11) ; ! d->tm_hour = date[15] * 10 + date[16] - '0' * 11; ! d->tm_min = date[18] * 10 + date[19] - '0' * 11; ! d->tm_sec = date[21] * 10 + date[22] - '0' * 11; ! } else { /* RFC1123 */ ! year = date[12] * 1000 + date[13] * 100 + date[14] * 10 + date[15] - ! ('0' * 1111); ! d->tm_hour = date[17] * 10 + date[18] - '0' * 11; ! d->tm_min = date[20] * 10 + date[21] - '0' * 11; ! d->tm_sec = date[23] * 10 + date[24] - '0' * 11; ! } ! ! if (d->tm_hour > 23 || d->tm_min > 59 || d->tm_sec > 61) return -1; ! ! if (d->tm_mday == 31 && (mon == 1 || mon == 3 || mon == 5 || mon == 8 || ! mon == 10)) return -1; ! if (d->tm_mday > 29 && mon == 1) return -1; ! if (d->tm_mday == 29 && mon == 1) ! if (year%4 != 0 || (year%100 == 0 && year%400 != 0)) return -1; ! ! d->tm_year = year - 1900; ! d->tm_isdst = 0; ! return tm2sec(d); ! } ! ! /* ! * Converts 8 hex digits to a time integer ! */ ! static int ! hex2sec(const char *x) ! { ! int i, ch; ! unsigned int j; ! ! for (i=0, j=0; i < 8; i++) ! { ! ch = x[i]; ! j <<= 4; ! if (isdigit(ch)) j |= ch - '0'; ! else if (isupper(ch)) j |= ch - ('A' - 10); ! else j |= ch - ('a' - 10); ! } ! if (j == 0xffffffff) return -1; /* so that it works with 8-byte ints */ ! else return j; ! } ! ! /* ! * Converts a time integer to 8 hex digits ! */ ! static void ! sec2hex(int t, char *y) ! { ! int i, ch; ! unsigned int j=t; ! ! for (i=7; i >= 0; i--) ! { ! ch = j & 0xF; ! j >>= 4; ! if (ch >= 10) y[i] = ch + ('A' - 10); ! else y[i] = ch + '0'; ! } ! y[8] = '\0'; ! } ! ! ! static void ! log_uerror(const char *routine, const char *file, const char *err, ! server_rec *s) ! { ! char *p, *q; ! ! q = get_time(); ! p = strerror(errno); ! ! if (err != NULL) ! { ! fprintf(s->error_log, "[%s] %s\n", q, err); ! if (file != NULL) ! fprintf(s->error_log, "- %s: %s: %s\n", routine, file, p); ! else ! fprintf(s->error_log, "- %s: %s\n", routine, p); ! } else ! { ! if (file != NULL) ! fprintf(s->error_log, "[%s] %s: %s: %s\n", q, routine, file, p); ! else ! fprintf(s->error_log, "[%s] %s: %s\n", q, routine, p); ! } ! ! fflush(s->error_log); ! } ! ! struct gc_ent ! { ! unsigned long int len; ! time_t expire; ! char file[HASH_LEN+1]; ! ! }; ! ! static int ! gcdiff(const void *ap, const void *bp) ! { ! const struct gc_ent *a=*(struct gc_ent **)ap, *b=*(struct gc_ent **)bp; ! ! if (a->expire > b->expire) return 1; ! else if (a->expire < b->expire) return -1; ! else return 0; ! } ! ! static int curbytes, cachesize, every; ! static unsigned long int curblocks; ! static time_t now, expire; ! static char *filename; ! ! static int sub_garbage_coll(request_rec *r,array_header *files, ! const char *cachedir,const char *cachesubdir); ! ! static void garbage_coll(request_rec *r) ! { ! const char *cachedir; ! void *sconf = r->server->module_config; ! proxy_server_conf *pconf = ! (proxy_server_conf *)get_module_config(sconf, &proxy_module); ! const struct cache_conf *conf=&pconf->cache; ! array_header *files; ! struct stat buf; ! struct gc_ent *fent,**elts; ! int i; ! static time_t lastcheck=-1; /* static data!!! */ ! ! cachedir = conf->root; ! cachesize = conf->space; ! every = conf->gcinterval; ! ! if (cachedir == NULL || every == -1) return; ! now = time(NULL); ! if (now != -1 && lastcheck != -1 && now < lastcheck + every) return; ! ! block_alarms(); /* avoid SIGALRM on big cache cleanup */ ! ! filename = palloc(r->pool, strlen(cachedir) + HASH_LEN + 2); ! strcpy(filename, cachedir); ! strcat(filename, "/.time"); ! if (stat(filename, &buf) == -1) /* does not exist */ ! { ! if (errno != ENOENT) ! { ! log_uerror("stat", filename, NULL, r->server); ! return; ! } ! if (creat(filename, 0666) == -1) ! { ! if (errno != EEXIST) ! log_uerror("creat", filename, NULL, r->server); ! else ! lastcheck = now; /* someone else got in there */ ! return; ! } ! } else ! { ! lastcheck = buf.st_mtime; /* save the time */ ! if (now < lastcheck + every) return; ! if (utime(filename, NULL) == -1) ! log_uerror("utimes", filename, NULL, r->server); ! } ! files = make_array(r->pool, 100, sizeof(struct gc_ent *)); ! curblocks = 0; ! curbytes = 0; ! ! sub_garbage_coll(r,files,cachedir,"/"); ! ! if (curblocks < cachesize || curblocks + curbytes <= cachesize) ! return; ! ! qsort(files->elts, files->nelts, sizeof(struct gc_ent *), gcdiff); ! ! elts = (struct gc_ent **)files->elts; ! for (i=0; i < files->nelts; i++) ! { ! fent = elts[i]; ! sprintf(filename, "%s%s", cachedir, fent->file); ! Explain3("GC Unlinking %s (expiry %ld, now %ld)",filename,fent->expire,now); ! #if TESTING ! fprintf(stderr,"Would unlink %s\n",filename); ! #else ! if (unlink(filename) == -1) ! { ! if (errno != ENOENT) ! log_uerror("unlink", filename, NULL, r->server); ! } ! else ! #endif ! { ! curblocks -= fent->len >> 10; ! curbytes -= fent->len & 0x3FF; ! if (curbytes < 0) ! { ! curbytes += 1024; ! curblocks--; ! } ! if (curblocks < cachesize || curblocks + curbytes <= cachesize) ! break; ! } ! } ! unblock_alarms(); ! } ! ! static int sub_garbage_coll(request_rec *r,array_header *files, ! const char *cachebasedir,const char *cachesubdir) ! { ! char line[27]; ! char cachedir[HUGE_STRING_LEN]; ! struct stat buf; ! int fd,i; ! DIR *dir; ! #if defined(NEXT) ! struct DIR_TYPE *ent; ! #else ! struct dirent *ent; ! #endif ! struct gc_ent *fent; ! int nfiles=0; ! ! sprintf(cachedir,"%s%s",cachebasedir,cachesubdir); ! Explain1("GC Examining directory %s",cachedir); ! dir = opendir(cachedir); ! if (dir == NULL) ! { ! log_uerror("opendir", cachedir, NULL, r->server); ! return 0; ! } ! ! while ((ent = readdir(dir)) != NULL) ! { ! if (ent->d_name[0] == '.') continue; ! sprintf(filename, "%s%s", cachedir, ent->d_name); ! Explain1("GC Examining file %s",filename); ! /* is it a temporary file? */ ! if (strncmp(ent->d_name, "tmp", 3) == 0) ! { ! /* then stat it to see how old it is; delete temporary files > 1 day old */ ! if (stat(filename, &buf) == -1) ! { ! if (errno != ENOENT) ! log_uerror("stat", filename, NULL, r->server); ! } else if (now != -1 && buf.st_atime < now - SEC_ONE_DAY && ! buf.st_mtime < now - SEC_ONE_DAY) ! { ! Explain1("GC unlink %s",filename); ! #if TESTING ! fprintf(stderr,"Would unlink %s\n",filename); ! #else ! unlink(filename); ! #endif ! } ! continue; ! } ! ++nfiles; ! /* is it another file? */ ! /* FIXME: Shouldn't any unexpected files be deleted? */ ! /* if (strlen(ent->d_name) != HASH_LEN) continue; */ ! ! /* read the file */ ! fd = open(filename, O_RDONLY); ! if (fd == -1) ! { ! if (errno != ENOENT) log_uerror("open", filename,NULL, r->server); ! continue; ! } ! if (fstat(fd, &buf) == -1) ! { ! log_uerror("fstat", filename, NULL, r->server); ! close(fd); ! continue; ! } ! if(S_ISDIR(buf.st_mode)) ! { ! char newcachedir[HUGE_STRING_LEN]; ! close(fd); ! sprintf(newcachedir,"%s%s/",cachesubdir,ent->d_name); ! if(!sub_garbage_coll(r,files,cachebasedir,newcachedir)) ! { ! sprintf(newcachedir,"%s%s",cachedir,ent->d_name); ! #if TESTING ! fprintf(stderr,"Would remove directory %s\n",newcachedir); ! #else ! rmdir(newcachedir); ! #endif ! --nfiles; ! } ! continue; ! } ! ! i = read(fd, line, 26); ! if (i == -1) ! { ! log_uerror("read", filename, NULL, r->server); ! close(fd); ! continue; ! } ! close(fd); ! line[i] = '\0'; ! expire = hex2sec(line+18); ! if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&&") || expire == -1) ! { ! /* bad file */ ! if (now != -1 && buf.st_atime > now + SEC_ONE_DAY && ! buf.st_mtime > now + SEC_ONE_DAY) ! { ! log_error("proxy: deleting bad cache file", r->server); ! #if TESTING ! fprintf(stderr,"Would unlink bad file %s\n",filename); ! #else ! unlink(filename); ! #endif ! } ! continue; ! } ! ! /* ! * we need to calculate an 'old' factor, and remove the 'oldest' files ! * so that the space requirement is met; sort by the expires date of the ! * file. ! * ! */ ! /* FIXME: We should make the array an array of gc_ents, not gc_ent *s ! */ ! fent = palloc(r->pool, sizeof(struct gc_ent)); ! fent->len = buf.st_size; ! fent->expire = expire; ! strcpy(fent->file,cachesubdir); ! strcat(fent->file, ent->d_name); ! *(struct gc_ent **)push_array(files) = fent; ! ! /* accumulate in blocks, to cope with directories > 4Gb */ ! curblocks += buf.st_size >> 10; /* Kbytes */ ! curbytes += buf.st_size & 0x3FF; ! if (curbytes >= 1024) ! { ! curbytes -= 1024; ! curblocks++; ! } ! } ! ! closedir(dir); ! ! return nfiles; ! ! } ! ! /* ! * read a cache file; ! * returns 1 on success, ! * 0 on failure (bad file or wrong URL) ! * -1 on UNIX error ! */ ! static int ! rdcache(pool *pool, BUFF *cachefp, struct cache_req *c) ! { ! char urlbuff[1034], *p; ! int len; ! /* read the data from the cache file */ ! /* format ! * date SP lastmod SP expire SP count SP content-length CRLF ! * dates are stored as hex seconds since 1970 ! */ ! len = bgets(urlbuff, 1034, cachefp); ! if (len == -1) return -1; ! if (len == 0 || urlbuff[len-1] != '\n') return 0; ! urlbuff[len-1] = '\0'; ! ! if (!checkmask(urlbuff, "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&")) ! return 0; ! ! c->date = hex2sec(urlbuff); ! c->lmod = hex2sec(urlbuff+9); ! c->expire = hex2sec(urlbuff+18); ! c->version = hex2sec(urlbuff+27); ! c->len = hex2sec(urlbuff+36); ! ! /* check that we have the same URL */ ! len = bgets(urlbuff, 1034, cachefp); ! if (len == -1) return -1; ! if (len == 0 || strncmp(urlbuff, "X-URL: ", 7) != 0 || ! urlbuff[len-1] != '\n') ! return 0; ! urlbuff[len-1] = '\0'; ! if (strcmp(urlbuff+7, c->url) != 0) return 0; ! ! /* What follows is the message */ ! len = bgets(urlbuff, 1034, cachefp); ! if (len == -1) return -1; ! if (len == 0 || urlbuff[len-1] != '\n') return 0; ! urlbuff[--len] = '\0'; ! ! c->resp_line = pstrdup(pool, urlbuff); ! p = strchr(urlbuff, ' '); ! if (p == NULL) return 0; ! ! c->status = atoi(p); ! c->hdrs = read_headers(pool, urlbuff, 1034, cachefp); ! if (c->hdrs == NULL) return -1; ! if (c->len != -1) /* add a content-length header */ ! { ! struct hdr_entry *q; ! q = get_header(c->hdrs, "Content-Length"); ! if (q == NULL) ! { ! p = palloc(pool, 15); ! sprintf(p, "%u", c->len); ! add_header(c->hdrs, "Content-Length", p, HDR_REP); ! } ! } ! return 1; ! } ! ! ! /* ! * Call this to test for a resource in the cache ! * Returns DECLINED if we need to check the remote host ! * or an HTTP status code if successful ! * ! * Functions: ! * if URL is cached then ! * if cached file is not expired then ! * if last modified after if-modified-since then send body ! * else send 304 Not modified ! * else ! * if last modified after if-modified-since then add ! * last modified date to request ! */ ! static int ! cache_check(request_rec *r, char *url, struct cache_conf *conf, ! struct cache_req **cr) ! { ! char hashfile[33], *imstr, *pragma, *p, *auth; ! struct cache_req *c; ! time_t now; ! BUFF *cachefp; ! int cfd, i; ! const long int zero=0L; ! void *sconf = r->server->module_config; ! proxy_server_conf *pconf = ! (proxy_server_conf *)get_module_config(sconf, &proxy_module); ! ! c = pcalloc(r->pool, sizeof(struct cache_req)); ! *cr = c; ! c->req = r; ! c->url = pstrdup(r->pool, url); ! ! /* get the If-Modified-Since date of the request */ ! c->ims = -1; ! imstr = table_get(r->headers_in, "If-Modified-Since"); ! if (imstr != NULL) ! { ! /* this may modify the value in the original table */ ! imstr = date_canon(r->pool, imstr); ! c->ims = parsedate(imstr, NULL); ! if (c->ims == -1) /* bad or out of range date; remove it */ ! table_set(r->headers_in, "If-Modified-Since", NULL); ! } ! ! /* find the filename for this cache entry */ ! hash(url, hashfile,pconf->cache.dirlevels,pconf->cache.dirlength); ! if (conf->root != NULL) ! c->filename = pstrcat(r->pool, conf->root, "/", hashfile, NULL); ! else ! c->filename = NULL; ! ! cachefp = NULL; ! /* find out about whether the request can access the cache */ ! pragma = table_get(r->headers_in, "Pragma"); ! auth = table_get(r->headers_in, "Authorization"); ! Explain5("Request for %s, pragma=%s, auth=%s, ims=%ld, imstr=%s",url, ! pragma,auth,c->ims,imstr); ! if (c->filename != NULL && r->method_number == M_GET && ! strlen(url) < 1024 && !liststr(pragma, "no-cache") && auth == NULL) ! { ! Explain1("Check file %s",c->filename); ! cfd = open(c->filename, O_RDWR); ! if (cfd != -1) ! { ! note_cleanups_for_fd(r->pool, cfd); ! cachefp = bcreate(r->pool, B_RD | B_WR); ! bpushfd(cachefp, cfd, cfd); ! } else if (errno != ENOENT) ! log_uerror("open", c->filename, "proxy: error opening cache file", ! r->server); ! else ! Explain1("File %s not found",c->filename); ! } ! ! if (cachefp != NULL) ! { ! i = rdcache(r->pool, cachefp, c); ! if (i == -1) ! log_uerror("read", c->filename, "proxy: error reading cache file", ! r->server); ! else if (i == 0) ! log_error("proxy: bad cache file", r->server); ! if (i != 1) ! { ! pclosef(r->pool, cachefp->fd); ! cachefp = NULL; ! } ! } ! if (cachefp == NULL) ! c->hdrs = make_array(r->pool, 2, sizeof(struct hdr_entry)); ! /* FIXME: Shouldn't we check the URL somewhere? */ ! now = time(NULL); ! /* Ok, have we got some un-expired data? */ ! if (cachefp != NULL && c->expire != -1 && now < c->expire) ! { ! Explain0("Unexpired data available"); ! /* check IMS */ ! if (c->lmod != -1 && c->ims != -1 && c->ims >= c->lmod) ! { ! /* has the cached file changed since this request? */ ! if (c->date == -1 || c->date > c->ims) ! { ! /* No, but these header values may have changed, so we send them with the ! * 304 response ! */ ! /* CHECKME: surely this was wrong? (Ben) ! p = table_get(r->headers_in, "Expires"); ! */ ! p = table_get(c->hdrs, "Expires"); ! if (p != NULL) table_set(r->headers_out, "Expires", p); ! } ! pclosef(r->pool, cachefp->fd); ! Explain0("Use local copy, cached file hasn't changed"); ! return USE_LOCAL_COPY; ! } ! ! /* Ok, has been modified */ ! Explain0("Local copy modified, send it"); ! r->status_line = strchr(c->resp_line, ' ') + 1; ! r->status = c->status; ! soft_timeout ("send", r); ! if (!r->assbackwards) ! send_headers(r->connection->client, c->resp_line, c->hdrs); ! bsetopt(r->connection->client, BO_BYTECT, &zero); ! r->sent_bodyct = 1; ! if (!r->header_only) send_fb (cachefp, r, NULL, NULL); ! pclosef(r->pool, cachefp->fd); ! return OK; ! } ! ! /* if we already have data and a last-modified date, and it is not a head ! * request, then add an If-Modified-Since ! */ ! ! if (cachefp != NULL && c->lmod != -1 && !r->header_only) ! { ! /* ! * use the later of the one from the request and the last-modified date ! * from the cache ! */ ! if (c->ims == -1 || c->ims < c->lmod) ! { ! struct hdr_entry *q; ! ! q = get_header(c->hdrs, "Last-Modified"); ! ! if (q != NULL && q->value != NULL) ! table_set(r->headers_in, "If-Modified-Since", ! (char *)q->value); ! } ! } ! c->fp = cachefp; ! ! Explain0("Local copy not present or expired. Declining."); ! ! return DECLINED; ! } ! ! /* ! * Having read the response from the client, decide what to do ! * If the response is not cachable, then delete any previously cached ! * response, and copy data from remote server to client. ! * Functions: ! * parse dates ! * check for an uncachable response ! * calculate an expiry date, if one is not provided ! * if the remote file has not been modified, then return the document ! * from the cache, maybe updating the header line ! * otherwise, delete the old cached file and open a new temporary file ! */ ! static int ! cache_update(struct cache_req *c, array_header *resp_hdrs, ! const char *protocol, int nocache) ! { ! request_rec *r=c->req; ! char *p; ! int i; ! struct hdr_entry *expire, *dates, *lmods, *clen; ! time_t expc, date, lmod, now; ! char buff[46]; ! void *sconf = r->server->module_config; ! proxy_server_conf *conf = ! (proxy_server_conf *)get_module_config(sconf, &proxy_module); ! const long int zero=0L; ! ! c->tempfile = NULL; ! ! /* we've received the response */ ! /* read expiry date; if a bad date, then leave it so the client can ! * read it ! */ ! expire = get_header(resp_hdrs, "Expires"); ! if (expire != NULL) expc = parsedate(expire->value, NULL); ! else expc = -1; ! ! /* ! * read the last-modified date; if the date is bad, then delete it ! */ ! lmods = get_header(resp_hdrs, "Last-Modified"); ! if (lmods != NULL) ! { ! lmod = parsedate(lmods->value, NULL); ! if (lmod == -1) ! { ! /* kill last modified date */ ! lmods->value = NULL; ! lmods = NULL; ! } ! } else ! lmod = -1; ! ! /* ! * what responses should we not cache? ! * Unknown status responses and those known to be uncacheable ! * 304 response when we have no valid cache file, or ! * 200 response from HTTP/1.0 and up without a Last-Modified header, or ! * HEAD requests, or ! * requests with an Authorization header, or ! * protocol requests nocache (e.g. ftp with user/password) ! */ ! if ((r->status != 200 && r->status != 301 && r->status != 304) || ! (expire != NULL && expc == -1) || ! (r->status == 304 && c->fp == NULL) || ! (r->status == 200 && lmods == NULL && ! strncmp(protocol, "HTTP/1.", 7) == 0) || ! r->header_only || ! table_get(r->headers_in, "Authorization") != NULL || ! nocache) ! { ! Explain1("Response is not cacheable, unlinking %s",c->filename); ! /* close the file */ ! if (c->fp != NULL) ! { ! pclosef(r->pool, c->fp->fd); ! c->fp = NULL; ! } ! /* delete the previously cached file */ ! unlink(c->filename); ! return DECLINED; /* send data to client but not cache */ ! } ! ! /* otherwise, we are going to cache the response */ ! /* ! * Read the date. Generate one if one is not supplied ! */ ! dates = get_header(resp_hdrs, "Date"); ! if (dates != NULL) date = parsedate(dates->value, NULL); ! else date = -1; ! ! now = time(NULL); ! ! if (date == -1) /* No, or bad date */ ! { ! /* no date header! */ ! /* add one; N.B. use the time _now_ rather than when we were checking the cache ! */ ! date = now; ! p = gm_timestr_822(r->pool, now); ! dates = add_header(resp_hdrs, "Date", p, HDR_REP); ! Explain0("Added date header"); ! } ! ! /* check last-modified date */ ! if (lmod != -1 && lmod > date) ! /* if its in the future, then replace by date */ ! { ! lmod = date; ! lmods->value = dates->value; ! Explain0("Last modified is in the future, replacing with now"); ! } ! /* if the response did not contain the header, then use the cached version */ ! if (lmod == -1 && c->fp != NULL) ! { ! lmod = c->lmod; ! Explain0("Reusing cached last modified"); ! } ! ! /* we now need to calculate the expire data for the object. */ ! if (expire == NULL && c->fp != NULL) /* no expiry data sent in response */ ! { ! expire = get_header(c->hdrs, "Expires"); ! if (expire != NULL) expc = parsedate(expire->value, NULL); ! } ! /* so we now have the expiry date */ ! /* if no expiry date then ! * if lastmod ! * expiry date = now + min((date - lastmod) * factor, maxexpire) ! * else ! * expire date = now + defaultexpire ! */ ! Explain1("Expiry date is %ld",expc); ! if (expc == -1) ! { ! if (lmod != -1) ! { ! double x = (double)(date - lmod)*conf->cache.lmfactor; ! double maxex=conf->cache.maxexpire; ! if (x > maxex) x = maxex; ! expc = now + (int)x; ! } else ! expc = now + conf->cache.defaultexpire; ! Explain1("Expiry date calculated %ld",expc); ! } ! ! /* get the content-length header */ ! clen = get_header(c->hdrs, "Content-Length"); ! if (clen == NULL) c->len = -1; ! else c->len = atoi(clen->value); ! ! sec2hex(date, buff); ! buff[8] = ' '; ! sec2hex(lmod, buff+9); ! buff[17] = ' '; ! sec2hex(expc, buff+18); ! buff[26] = ' '; ! sec2hex(c->version++, buff+27); ! buff[35] = ' '; ! sec2hex(c->len, buff+36); ! buff[44] = '\n'; ! buff[45] = '\0'; ! ! /* if file not modified */ ! if (r->status == 304) ! { ! if (c->ims != -1 && lmod != -1 && lmod <= c->ims) ! { ! /* set any changed headers somehow */ ! /* update dates and version, but not content-length */ ! if (lmod != c->lmod || expc != c->expire || date != c->date) ! { ! off_t curpos=lseek(c->fp->fd, 0, SEEK_SET); ! if (curpos == -1) ! log_uerror("lseek", c->filename, ! "proxy: error seeking on cache file",r->server); ! else if (write(c->fp->fd, buff, 35) == -1) ! log_uerror("write", c->filename, ! "proxy: error updating cache file", r->server); ! } ! pclosef(r->pool, c->fp->fd); ! Explain0("Remote document not modified, use local copy"); ! /* CHECKME: Is this right? Shouldn't we check IMS again here? */ ! return USE_LOCAL_COPY; ! } else ! { ! /* return the whole document */ ! Explain0("Remote document updated, sending"); ! r->status_line = strchr(c->resp_line, ' ') + 1; ! r->status = c->status; ! soft_timeout ("send", r); ! if (!r->assbackwards) ! send_headers(r->connection->client, c->resp_line, c->hdrs); ! bsetopt(r->connection->client, BO_BYTECT, &zero); ! r->sent_bodyct = 1; ! if (!r->header_only) send_fb (c->fp, r, NULL, NULL); ! /* set any changed headers somehow */ ! /* update dates and version, but not content-length */ ! if (lmod != c->lmod || expc != c->expire || date != c->date) ! { ! off_t curpos=lseek(c->fp->fd, 0, SEEK_SET); ! ! if (curpos == -1) ! log_uerror("lseek", c->filename, ! "proxy: error seeking on cache file",r->server); ! else if (write(c->fp->fd, buff, 35) == -1) ! log_uerror("write", c->filename, ! "proxy: error updating cache file", r->server); ! } ! pclosef(r->pool, c->fp->fd); ! return OK; ! } ! } ! /* new or modified file */ ! if (c->fp != NULL) ! { ! pclosef(r->pool, c->fp->fd); ! c->fp->fd = -1; ! } ! c->version = 0; ! sec2hex(0, buff+27); ! buff[35] = ' '; ! ! /* open temporary file */ ! #define TMPFILESTR "/tmpXXXXXX" ! c->tempfile=palloc(r->pool,strlen(conf->cache.root)+sizeof TMPFILESTR-1); ! strcpy(c->tempfile,conf->cache.root); ! /* ! p = strrchr(c->tempfile, '/'); ! if (p == NULL) return DECLINED; ! strcpy(p, TMPFILESTR); ! */ ! strcat(c->tempfile,TMPFILESTR); ! #undef TMPFILESTR ! p = mktemp(c->tempfile); ! if (p == NULL) return DECLINED; ! ! Explain1("Create temporary file %s",c->tempfile); ! ! i = open(c->tempfile, O_WRONLY | O_CREAT | O_EXCL, 0622); ! if (i == -1) ! { ! log_uerror("open", c->tempfile, "proxy: error creating cache file", ! r->server); ! return DECLINED; ! } ! note_cleanups_for_fd(r->pool, i); ! c->fp = bcreate(r->pool, B_WR); ! bpushfd(c->fp, -1, i); ! ! if (bvputs(c->fp, buff, "X-URL: ", c->url, "\n", NULL) == -1) ! { ! log_uerror("write", c->tempfile, "proxy: error writing cache file", ! r->server); ! pclosef(r->pool, c->fp->fd); ! unlink(c->tempfile); ! c->fp = NULL; ! } ! return DECLINED; ! } ! ! static void ! cache_tidy(struct cache_req *c) ! { ! server_rec *s=c->req->server; ! long int bc; ! ! if (c->fp == NULL) return; ! ! bgetopt(c->req->connection->client, BO_BYTECT, &bc); ! ! if (c->len != -1) ! { ! /* file lengths don't match; don't cache it */ ! if (bc != c->len) ! { ! pclosef(c->req->pool, c->fp->fd); /* no need to flush */ ! unlink(c->tempfile); ! return; ! } ! } else ! { ! /* update content-length of file */ ! char buff[9]; ! off_t curpos; ! ! c->len = bc; ! bflush(c->fp); ! sec2hex(c->len, buff); ! curpos = lseek(c->fp->fd, 36, SEEK_SET); ! if (curpos == -1) ! log_uerror("lseek", c->tempfile, ! "proxy: error seeking on cache file", s); ! else if (write(c->fp->fd, buff, 8) == -1) ! log_uerror("write", c->tempfile, ! "proxy: error updating cache file", s); ! } ! ! if (bflush(c->fp) == -1) ! { ! log_uerror("write", c->tempfile, "proxy: error writing to cache file", ! s); ! pclosef(c->req->pool, c->fp->fd); ! unlink(c->tempfile); ! return; ! } ! ! if (pclosef(c->req->pool, c->fp->fd) == -1) ! { ! log_uerror("close", c->tempfile, "proxy: error closing cache file", s); ! unlink(c->tempfile); ! return; ! } ! ! if (unlink(c->filename) == -1 && errno != ENOENT) ! { ! log_uerror("unlink", c->filename, ! "proxy: error deleting old cache file", s); ! } else ! { ! char *p; ! proxy_server_conf *conf= ! (proxy_server_conf *)get_module_config(s->module_config,&proxy_module); ! ! for(p=c->filename+strlen(conf->cache.root)+1 ; ; ) ! { ! p=strchr(p,'/'); ! if(!p) ! break; ! *p='\0'; ! if(mkdir(c->filename,S_IREAD|S_IWRITE|S_IEXEC) < 0 && errno != EEXIST) ! log_uerror("mkdir",c->filename,"proxy: error creating cache directory",s); ! *p='/'; ! ++p; ! } ! #ifdef __EMX__ ! /* Under OS/2 use rename. */ ! if (rename(c->tempfile, c->filename) == -1) ! log_uerror("rename", c->filename, "proxy: error renaming cache file", s); ! } ! #else ! ! if (link(c->tempfile, c->filename) == -1) ! log_uerror("link", c->filename, "proxy: error linking cache file", s); ! } ! if (unlink(c->tempfile) == -1) ! log_uerror("unlink", c->tempfile, "proxy: error deleting temp file",s); ! #endif ! garbage_coll(c->req); ! } ! static BUFF * ! cache_error(struct cache_req *c) ! { ! log_uerror("write", c->tempfile, "proxy: error writing to cache file", ! c->req->server); ! pclosef(c->req->pool, c->fp->fd); ! c->fp = NULL; ! unlink(c->tempfile); ! return NULL; } static int proxy_handler(request_rec *r) { --- 149,175 ---- proxy_fixup(request_rec *r) { char *url, *p; ! int i; ! if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; ! url = &r->filename[6]; ! /* lowercase the scheme */ ! p = strchr(url, ':'); ! if (p == NULL || p == url) return BAD_REQUEST; ! for (i=0; i != p - url; i++) url[i] = tolower(url[i]); ! /* canonicalise each specific scheme */ ! if (strncmp(url, "http:", 5) == 0) ! return proxy_http_canon(r, url+5, "http", DEFAULT_PORT); ! else if (strncmp(url, "ftp:", 4) == 0) ! return proxy_ftp_canon(r, url+4); ! else return OK; /* otherwise; we've done the best we can */ } + /* -------------------------------------------------------------- */ + /* Invoke handler */ + static int proxy_handler(request_rec *r) { *************** *** 2286,2292 **** p = strchr(url, ':'); if (p == NULL) return BAD_REQUEST; ! rc = cache_check(r, url, &conf->cache, &cr); if (rc != DECLINED) return rc; *p = '\0'; --- 191,197 ---- p = strchr(url, ':'); if (p == NULL) return BAD_REQUEST; ! rc = proxy_cache_check(r, url, &conf->cache, &cr); if (rc != DECLINED) return rc; *p = '\0'; *************** *** 2305,2311 **** { /* we only know how to handle communication to a proxy via http */ if (strcmp(ents[i].protocol, "http") == 0) ! rc = http_handler(r, cr, url, ents[i].hostname, ents[i].port); else rc = DECLINED; /* an error or success */ --- 210,217 ---- { /* we only know how to handle communication to a proxy via http */ if (strcmp(ents[i].protocol, "http") == 0) ! rc = proxy_http_handler(r, cr, url, ents[i].hostname, ! ents[i].port); else rc = DECLINED; /* an error or success */ *************** *** 2319,3247 **** * give up?? */ /* handle the scheme */ ! if (r->method_number == M_CONNECT) return connect_handler(r, cr, url); ! if (strcmp(scheme, "http") == 0) return http_handler(r, cr, url, NULL, 0); ! if (strcmp(scheme, "ftp") == 0) return ftp_handler(r, cr, url); else return NOT_IMPLEMENTED; } ! ! static int ! proxyerror(request_rec *r, const char *message) ! { ! r->status = SERVER_ERROR; ! r->status_line = "500 Proxy Error"; ! r->content_type = "text/html"; ! ! send_http_header(r); ! rvputs(r, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\015\012\ ! <html><head><title>Proxy Error</title><head>\015\012<body><h1>Proxy Error\ ! </h1>\015\012The proxy server could not handle this request.\ ! \015\012<p>\015\012Reason: <b>", message, "</b>\015\012</body><html>\015\012", ! NULL); ! return OK; ! } ! ! /* ! * This routine returns its own error message ! */ ! static const char * ! host2addr(const char *host, struct in_addr *addr) ! { ! int i; ! unsigned long ipaddr; ! ! for (i=0; host[i] != '\0'; i++) ! if (!isdigit(host[i]) && host[i] != '.') ! break; ! ! if (host[i] != '\0') ! { ! struct hostent *hp; ! ! hp = gethostbyname(host); ! if (hp == NULL) return "Host not found"; ! memcpy(addr, hp->h_addr, sizeof(struct in_addr)); ! } else ! { ! if ((ipaddr = inet_addr(host)) == -1) ! return "Bad IP address"; ! memcpy(addr, &ipaddr, sizeof(unsigned long)); ! } ! return NULL; ! } ! ! /* ! * Returns the ftp status code; ! * or -1 on I/O error, 0 on data error ! */ ! int ! ftp_getrc(BUFF *f) ! { ! int i, len, status; ! char linebuff[100], buff[5]; ! ! len = bgets(linebuff, 100, f); ! if (len == -1) return -1; ! /* check format */ ! if (len < 5 || !isdigit(linebuff[0]) || !isdigit(linebuff[1]) || ! !isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-')) ! return 0; ! status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0'; ! ! if (linebuff[len-1] != '\n') ! { ! i = bskiplf(f); ! if (i != 1) return i; ! } ! ! /* skip continuation lines */ ! if (linebuff[3] == '-') ! { ! memcpy(buff, linebuff, 3); ! buff[3] = ' '; ! do ! { ! len = bgets(linebuff, 100, f); ! if (len == -1) return -1; ! if (len < 5) return 0; ! if (linebuff[len-1] != '\n') ! { ! i = bskiplf(f); ! if (i != 1) return i; ! } ! } while (memcmp(linebuff, buff, 4) != 0); ! } ! ! return status; ! } ! ! static int ! doconnect(int sock, struct sockaddr_in *addr, request_rec *r) ! { ! int i; ! ! hard_timeout ("proxy connect", r); ! do i = connect(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_in)); ! while (i == -1 && errno == EINTR); ! if (i == -1) log_uerror("connect", NULL, NULL, r->server); ! kill_timeout(r); ! ! return i; ! } ! ! /* ! * Handles direct access of ftp:// URLs ! * Original (Non-PASV) version from ! * Troy Morrison <[EMAIL PROTECTED]> ! */ ! static int ! ftp_handler(request_rec *r, struct cache_req *c, char *url) ! { ! char *host, *path, *p, *user, *password, *parms; ! const char *err; ! int port, userlen, passlen, i, len, sock, dsock, csd, rc, nocache; ! struct sockaddr_in server; ! struct hdr_entry *hdr; ! array_header *resp_hdrs; ! BUFF *f, *cache, *data; ! pool *pool=r->pool; ! const int one=1; ! const long int zero=0L; ! ! /* This appears to fix a bug(?) that generates an "Address family not ! supported by protocol" error in doconnect() later (along with ! making sure server.sin_family = AF_INET - cdm)*/ ! memset(&server, 0, sizeof(struct sockaddr_in)); ! ! /* we only support GET and HEAD */ ! if (r->method_number != M_GET) return NOT_IMPLEMENTED; ! ! host = pstrdup(r->pool, url+6); ! /* We break the URL into host, port, path-search */ ! port = DEFAULT_FTP_PORT; ! path = strchr(host, '/'); ! if (path == NULL) path = ""; ! else *(path++) = '\0'; ! ! user = password = NULL; ! nocache = 0; ! passlen=0; /* not actually needed, but it shuts the compiler up */ ! p = strchr(host, '@'); ! if (p != NULL) ! { ! (*p++) = '\0'; ! user = host; ! host = p; ! /* find password */ ! p = strchr(user, ':'); ! if (p != NULL) ! { ! *(p++) = '\0'; ! password = p; ! passlen = decodeenc(password); ! } ! userlen = decodeenc(user); ! nocache = 1; /* don't cache when a username is supplied */ ! } else ! { ! user = "anonymous"; ! userlen = 9; ! ! password = "[EMAIL PROTECTED]"; ! passlen = strlen(password); ! } ! ! p = strchr(host, ':'); ! if (p != NULL) ! { ! *(p++) = '\0'; ! port = atoi(p); ! } ! ! Explain2("FTP: connect to %s:%d",host,port); ! ! parms = strchr(path, ';'); ! if (parms != NULL) *(parms++) = '\0'; ! ! server.sin_family = AF_INET; ! server.sin_port = htons(port); ! err = host2addr(host, &server.sin_addr); ! if (err != NULL) return proxyerror(r, err); /* give up */ ! ! sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); ! if (sock == -1) ! { ! log_uerror("socket", NULL, "proxy: error creating socket", r->server); ! return SERVER_ERROR; ! } ! note_cleanups_for_fd(pool, sock); ! ! if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, ! sizeof(int)) == -1) ! { ! log_uerror("setsockopt", NULL, "proxy: error setting reuseaddr option", ! r->server); ! pclosef(pool, sock); ! return SERVER_ERROR; ! } ! ! i = doconnect(sock, &server, r); ! if (i == -1) return proxyerror(r, "Could not connect to remote machine"); ! ! f = bcreate(pool, B_RDWR); ! bpushfd(f, sock, sock); ! /* shouldn't we implement telnet control options here? */ ! ! /* possible results: 120, 220, 421 */ ! hard_timeout ("proxy ftp", r); ! i = ftp_getrc(f); ! Explain1("FTP: returned status %d", i); ! if (i == -1) return proxyerror(r, "Error reading from remote server"); ! if (i != 220) return BAD_GATEWAY; ! ! Explain0("FTP: connected."); ! ! bputs("USER ", f); ! bwrite(f, user, userlen); ! bputs("\015\012", f); ! bflush(f); /* capture any errors */ ! Explain1("FTP: USER %s",user); ! ! /* possible results; 230, 331, 332, 421, 500, 501, 530 */ ! /* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */ ! i = ftp_getrc(f); ! Explain1("FTP: returned status %d",i); ! if (i == -1) return proxyerror(r, "Error sending to remote server"); ! if (i == 530) return FORBIDDEN; ! else if (i != 230 && i != 331) return BAD_GATEWAY; ! ! if (i == 331) /* send password */ ! { ! if (password == NULL) return FORBIDDEN; ! bputs("PASS ", f); ! bwrite(f, password, passlen); ! bputs("\015\012", f); ! bflush(f); ! Explain1("FTP: PASS %s",password); ! /* possible results 202, 230, 332, 421, 500, 501, 503, 530 */ ! i = ftp_getrc(f); ! Explain1("FTP: returned status %d",i); ! if (i == -1) return proxyerror(r, "Error sending to remote server"); ! if (i == 332 || i == 530) return FORBIDDEN; ! else if (i != 230 && i != 202) return BAD_GATEWAY; ! } ! ! /* set the directory */ ! /* this is what we must do if we don't know the OS type of the remote ! * machine ! */ ! for (;;) ! { ! p = strchr(path, '/'); ! if (p == NULL) break; ! *p = '\0'; ! ! len = decodeenc(path); ! bputs("CWD ", f); ! bwrite(f, path, len); ! bputs("\015\012", f); ! bflush(f); ! Explain1("FTP: CWD %s",path); ! /* responses: 250, 421, 500, 501, 502, 530, 550 */ ! /* 1,3 error, 2 success, 4,5 failure */ ! i = ftp_getrc(f); ! Explain1("FTP: returned status %d",i); ! if (i == -1) return proxyerror(r, "Error sending to remote server"); ! else if (i == 550) return NOT_FOUND; ! else if (i != 250) return BAD_GATEWAY; ! ! path = p + 1; ! } ! ! if (parms != NULL && strncmp(parms, "type=", 5) == 0) ! { ! parms += 5; ! if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') || ! parms[1] != '\0') parms = ""; ! } ! else parms = ""; ! ! /* changed to make binary transfers the default */ ! ! if (parms[0] != 'a') ! { ! /* set type to image */ ! /* TM - Added \015\012 to the end of TYPE I, otherwise it hangs the ! connection */ ! bputs("TYPE I\015\012", f); ! bflush(f); ! Explain0("FTP: TYPE I"); ! /* responses: 200, 421, 500, 501, 504, 530 */ ! i = ftp_getrc(f); ! Explain1("FTP: returned status %d",i); ! if (i == -1) return proxyerror(r, "Error sending to remote server"); ! else if (i != 200 && i != 504) return BAD_GATEWAY; ! /* Allow not implemented */ ! else if (i == 504) parms[0] = '\0'; ! } ! ! /* set up data connection */ ! len = sizeof(struct sockaddr_in); ! if (getsockname(sock, (struct sockaddr *)&server, &len) < 0) ! { ! log_uerror("getsockname", NULL,"proxy: error getting socket address", ! r->server); ! pclosef(pool, sock); ! return SERVER_ERROR; ! } ! ! dsock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); ! if (dsock == -1) ! { ! log_uerror("socket", NULL, "proxy: error creating socket", r->server); ! pclosef(pool, sock); ! return SERVER_ERROR; ! } ! note_cleanups_for_fd(pool, dsock); ! ! if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one, ! sizeof(int)) == -1) ! { ! log_uerror("setsockopt", NULL, "proxy: error setting reuseaddr option", ! r->server); ! pclosef(pool, dsock); ! pclosef(pool, sock); ! return SERVER_ERROR; ! } ! ! if (bind(dsock, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == ! -1) ! { ! char buff[22]; ! ! sprintf(buff, "%s:%d", inet_ntoa(server.sin_addr), server.sin_port); ! log_uerror("bind", buff, "proxy: error binding to ftp data socket", ! r->server); ! pclosef(pool, sock); ! pclosef(pool, dsock); ! } ! listen(dsock, 2); /* only need a short queue */ ! ! /* set request */ ! len = decodeenc(path); ! ! /* TM - if len == 0 then it must be a directory (you can't RETR nothing) */ ! ! if(len==0) parms="d"; ! else ! { ! bputs("SIZE ", f); ! bwrite(f, path, len); ! bputs("\015\012", f); ! bflush(f); ! Explain1("FTP: SIZE %s",path); ! i = ftp_getrc(f); ! Explain1("FTP: returned status %d", i); ! if (i != 500) /* Size command not recognized */ ! { ! if (i==550) /* Not a regular file */ ! { ! Explain0("FTP: SIZE shows this is a directory"); ! parms="d"; ! bputs("CWD ", f); ! bwrite(f, path, len); ! bputs("\015\012", f); ! bflush(f); ! Explain1("FTP: CWD %s",path); ! i = ftp_getrc(f); ! Explain1("FTP: returned status %d", i); ! if (i == -1) return proxyerror(r, "Error sending to remote server"); ! else if (i == 550) return NOT_FOUND; ! else if (i != 250) return BAD_GATEWAY; ! path=""; len=0; ! } ! } ! } ! ! if (parms[0] == 'd') ! { ! if (len != 0) bputs("LIST ", f); ! else bputs("LIST -lag", f); ! Explain1("FTP: LIST %s",(len==0 ? "" : path)); ! } ! else ! { ! bputs("RETR ", f); ! Explain1("FTP: RETR %s",path); ! } ! bwrite(f, path, len); ! bputs("\015\012", f); ! bflush(f); ! /* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, 550 ! NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, 530 */ ! rc = ftp_getrc(f); ! Explain1("FTP: returned status %d",rc); ! if (rc == -1) return proxyerror(r, "Error sending to remote server"); ! if (rc == 550) ! { ! Explain0("FTP: RETR failed, trying LIST instead"); ! parms="d"; ! bputs("CWD ", f); ! bwrite(f, path, len); ! bputs("\015\012", f); ! bflush(f); ! Explain1("FTP: CWD %s", path); ! rc = ftp_getrc(f); ! Explain1("FTP: returned status %d", rc); ! if (rc == -1) return proxyerror(r, "Error sending to remote server"); ! if (rc == 550) return NOT_FOUND; ! if (rc != 250) return BAD_GATEWAY; ! ! bputs("LIST -lag\015\012", f); ! bflush(f); ! Explain0("FTP: LIST -lag"); ! rc = ftp_getrc(f); ! Explain1("FTP: returned status %d", rc); ! if (rc == -1) return proxyerror(r, "Error sending to remote server"); ! } ! if (rc != 125 && rc != 150 && rc != 226 && rc != 250) return BAD_GATEWAY; ! kill_timeout(r); ! ! r->status = 200; ! r->status_line = "200 OK"; ! ! resp_hdrs = make_array(pool, 2, sizeof(struct hdr_entry)); ! if (parms[0] == 'd') ! add_header(resp_hdrs, "Content-Type", "text/html", HDR_REP); ! else ! { ! find_ct(r); ! if(r->content_type != NULL) ! { ! add_header(resp_hdrs, "Content-Type", r->content_type, HDR_REP); ! Explain1("FTP: Content-Type set to %s",r->content_type); ! } ! } ! i = cache_update(c, resp_hdrs, "FTP", nocache); ! if (i != DECLINED) ! { ! pclosef(pool, dsock); ! pclosef(pool, sock); ! return i; ! } ! cache = c->fp; ! ! /* wait for connection */ ! hard_timeout ("proxy ftp data connect", r); ! len = sizeof(struct sockaddr_in); ! do csd = accept(dsock, (struct sockaddr *)&server, &len); ! while (csd == -1 && errno == EINTR); /* SHUDDER on SOCKS - cdm */ ! if (csd == -1) ! { ! log_uerror("accept", NULL, "proxy: failed to accept data connection", ! r->server); ! pclosef(pool, dsock); ! pclosef(pool, sock); ! cache_error(c); ! return BAD_GATEWAY; ! } ! note_cleanups_for_fd(pool, csd); ! data = bcreate(pool, B_RDWR); ! bpushfd(data, csd, -1); ! kill_timeout(r); ! ! hard_timeout ("proxy receive", r); ! /* send response */ ! /* write status line */ ! if (!r->assbackwards) ! rvputs(r, SERVER_PROTOCOL, " ", r->status_line, "\015\012", NULL); ! if (cache != NULL) ! if (bvputs(cache, SERVER_PROTOCOL, " ", r->status_line, "\015\012", ! NULL) == -1) ! cache = cache_error(c); ! ! /* send headers */ ! len = resp_hdrs->nelts; ! hdr = (struct hdr_entry *)resp_hdrs->elts; ! for (i=0; i < len; i++) ! { ! if (hdr[i].field == NULL || hdr[i].value == NULL || ! hdr[i].value[0] == '\0') continue; ! if (!r->assbackwards) ! rvputs(r, hdr[i].field, ": ", hdr[i].value, "\015\012", NULL); ! if (cache != NULL) ! if (bvputs(cache, hdr[i].field, ": ", hdr[i].value, "\015\012", ! NULL) == -1) ! cache = cache_error(c); ! } ! ! if (!r->assbackwards) rputs("\015\012", r); ! if (cache != NULL) ! if (bputs("\015\012", cache) == -1) cache = cache_error(c); ! ! bsetopt(r->connection->client, BO_BYTECT, &zero); ! r->sent_bodyct = 1; ! /* send body */ ! if (!r->header_only) ! { ! if (parms[0] != 'd') send_fb(data, r, cache, c); ! else send_dir(data, r, cache, c, url); ! ! if (rc == 125 || rc == 150) rc = ftp_getrc(f); ! if (rc != 226 && rc != 250) cache_error(c); ! } ! else ! { ! /* abort the transfer */ ! bputs("ABOR\015\012", f); ! bflush(f); ! pclosef(pool, csd); ! Explain0("FTP: ABOR"); ! /* responses: 225, 226, 421, 500, 501, 502 */ ! i = ftp_getrc(f); ! Explain1("FTP: returned status %d",i); ! } ! ! cache_tidy(c); ! ! /* finish */ ! bputs("QUIT\015\012", f); ! bflush(f); ! Explain0("FTP: QUIT"); ! /* responses: 221, 500 */ ! ! pclosef(pool, csd); ! pclosef(pool, dsock); ! pclosef(pool, sock); ! ! return OK; ! } ! ! /* ! * This handles Netscape CONNECT method secure proxy requests. ! * A connection is opened to the specified host and data is ! * passed through between the WWW site and the browser. ! * ! * This code is based on the INTERNET-DRAFT document ! * "Tunneling SSL Through a WWW Proxy" currently at ! * http://www.mcom.com/newsref/std/tunneling_ssl.html. ! * ! * FIXME: this is bad, because it does its own socket I/O ! * instead of using the I/O in buff.c. However, ! * the I/O in buff.c blocks on reads, and because ! * this function doesn't know how much data will ! * be sent either way (or when) it can't use blocking ! * I/O. This may be very implementation-specific ! * (to Linux). Any suggestions? ! * FIXME: this doesn't log the number of bytes sent, but ! * that may be okay, since the data is supposed to ! * be transparent. In fact, this doesn't log at all ! * yet. 8^) ! * FIXME: doesn't check any headers initally sent from the ! * client. ! * FIXME: should allow authentication, but hopefully the ! * generic proxy authentication is good enough. ! * FIXME: no check for r->assbackwards, whatever that is. ! */ ! ! static int ! connect_handler(request_rec *r, struct cache_req *c, char *url) ! { ! struct sockaddr_in server; ! const char *host, *err; ! char *p; ! int port, sock; ! char buffer[HUGE_STRING_LEN]; ! int nbytes, i; ! fd_set fds; ! ! memset(&server, '\0', sizeof(server)); ! server.sin_family=AF_INET; ! ! /* Break the URL into host:port pairs */ ! ! host = url; ! p = strchr(url, ':'); ! if (p==NULL) port = DEFAULT_HTTPS_PORT; ! else ! { ! port = atoi(p+1); ! *p='\0'; ! } ! ! switch (port) ! { ! case DEFAULT_HTTPS_PORT: ! case DEFAULT_SNEWS_PORT: ! break; ! default: ! return HTTP_SERVICE_UNAVAILABLE; ! } ! ! Explain2("CONNECT to %s on port %d", host, port); ! ! server.sin_port = htons(port); ! err = host2addr(host, &server.sin_addr); ! if (err != NULL) return proxyerror(r, err); /* give up */ ! ! sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); ! if (sock == -1) ! { ! log_error("proxy: error creating socket", r->server); ! return SERVER_ERROR; ! } ! note_cleanups_for_fd(r->pool, sock); ! ! i = doconnect(sock, &server, r); ! if (i == -1 ) ! return proxyerror(r, "Could not connect to remote machine"); ! ! Explain0("Returning 200 OK Status"); ! ! rvputs(r, "HTTP/1.0 200 Connection established\015\012", NULL); ! rvputs(r, "Proxy-agent: ", SERVER_VERSION, "\015\012\015\012", NULL); ! bflush(r->connection->client); ! ! while (1) /* Infinite loop until error (one side closes the connection) */ ! { ! FD_ZERO(&fds); ! FD_SET(sock, &fds); ! FD_SET(r->connection->client->fd, &fds); ! ! Explain0("Going to sleep (select)"); ! i = select((r->connection->client->fd > sock ? ! r->connection->client->fd+1 : ! #ifdef HPUX ! sock+1), (int*)&fds, NULL, NULL, NULL); ! #else ! sock+1), &fds, NULL, NULL, NULL); ! #endif ! Explain1("Woke from select(), i=%d",i); ! ! if (i) ! { ! if (FD_ISSET(sock, &fds)) ! { ! Explain0("sock was set"); ! if((nbytes=read(sock,buffer,HUGE_STRING_LEN))!=0) ! { ! if(nbytes==-1) break; ! if(write(r->connection->client->fd, buffer, nbytes)==EOF)break; ! Explain1("Wrote %d bytes to client", nbytes); ! } ! else break; ! } ! else if (FD_ISSET(r->connection->client->fd, &fds)) ! { ! Explain0("client->fd was set"); ! if((nbytes=read(r->connection->client->fd,buffer, ! HUGE_STRING_LEN))!=0) ! { ! if(nbytes==-1) break; ! if(write(sock,buffer,nbytes)==EOF) break; ! Explain1("Wrote %d bytes to server", nbytes); ! } ! else break; ! } ! else break; /* Must be done waiting */ ! } ! else break; ! } ! ! pclosef(r->pool,sock); ! ! return OK; ! } ! ! /* ! * This handles http:// URLs, and other URLs using a remote proxy over http ! * If proxyhost is NULL, then contact the server directly, otherwise ! * go via the proxy. ! * Note that if a proxy is used, then URLs other than http: can be accessed, ! * also, if we have trouble which is clearly specific to the proxy, then ! * we return DECLINED so that we can try another proxy. (Or the direct ! * route.) ! */ ! static int ! http_handler(request_rec *r, struct cache_req *c, char *url, ! const char *proxyhost, int proxyport) ! { ! char *p; ! const char *err, *host; ! int port, i, sock, len; ! array_header *reqhdrs_arr, *resp_hdrs; ! table_entry *reqhdrs; ! struct sockaddr_in server; ! BUFF *f, *cache; ! struct hdr_entry *hdr; ! char buffer[HUGE_STRING_LEN], inprotocol[9], outprotocol[9]; ! pool *pool=r->pool; ! const long int zero=0L; ! ! void *sconf = r->server->module_config; ! proxy_server_conf *conf = ! (proxy_server_conf *)get_module_config(sconf, &proxy_module); ! struct nocache_entry *ent=(struct nocache_entry *)conf->nocaches->elts; ! int nocache = 0; ! ! memset(&server, '\0', sizeof(server)); ! server.sin_family = AF_INET; ! ! if (proxyhost != NULL) ! { ! server.sin_port = htons(proxyport); ! err = host2addr(proxyhost, &server.sin_addr); ! if (err != NULL) return DECLINED; /* try another */ ! host = proxyhost; ! } else ! { ! url += 7; /* skip http:// */ ! /* We break the URL into host, port, path-search */ ! port = DEFAULT_PORT; ! p = strchr(url, '/'); ! if (p == NULL) ! { ! host = pstrdup(pool, url); ! url = "/"; ! } else ! { ! char *q = palloc(pool, p-url+1); ! memcpy(q, url, p-url); ! q[p-url] = '\0'; ! url = p; ! host = q; ! } ! ! p = strchr(host, ':'); ! if (p != NULL) ! { ! *(p++) = '\0'; ! port = atoi(p); ! } ! server.sin_port = htons(port); ! err = host2addr(host, &server.sin_addr); ! if (err != NULL) return proxyerror(r, err); /* give up */ ! } ! ! sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); ! if (sock == -1) ! { ! log_error("proxy: error creating socket", r->server); ! return SERVER_ERROR; ! } ! note_cleanups_for_fd(pool, sock); ! ! i = doconnect(sock, &server, r); ! if (i == -1) ! { ! if (proxyhost != NULL) return DECLINED; /* try again another way */ ! else return proxyerror(r, "Could not connect to remote machine"); ! } ! ! f = bcreate(pool, B_RDWR); ! bpushfd(f, sock, sock); ! ! hard_timeout ("proxy send", r); ! bvputs(f, r->method, " ", url, " HTTP/1.0\015\012", NULL); ! ! reqhdrs_arr = table_elts (r->headers_in); ! reqhdrs = (table_entry *)reqhdrs_arr->elts; ! for (i=0; i < reqhdrs_arr->nelts; i++) ! { ! if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL) continue; ! if (!strcasecmp(reqhdrs[i].key, "Connection")) continue; ! bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, "\015\012", NULL); ! } ! ! bputs("\015\012", f); ! /* send the request data, if any. N.B. should we trap SIGPIPE ? */ ! ! if (should_client_block(r)) ! { ! while ((i = read_client_block (r, buffer, HUGE_STRING_LEN))) ! bwrite(f, buffer, i); ! } ! bflush(f); ! kill_timeout(r); ! ! hard_timeout ("proxy receive", r); ! ! len = bgets(buffer, HUGE_STRING_LEN-1, f); ! if (len == -1 || len == 0) ! { ! pclosef(pool, sock); ! return proxyerror(r, "Error reading from remote server"); ! } ! ! /* Is it an HTTP/1 response? */ ! if (checkmask(buffer, "HTTP/#.# ### *")) ! { ! /* If not an HTTP/1 messsage or if the status line was > 8192 bytes */ ! if (buffer[5] != '1' || buffer[len-1] != '\n') ! { ! pclosef(pool, sock); ! return BAD_GATEWAY; ! } ! buffer[--len] = '\0'; ! memcpy(inprotocol, buffer, 8); ! inprotocol[8] = '\0'; ! ! /* we use the same protocol on output as on input */ ! strcpy(outprotocol, inprotocol); ! buffer[12] = '\0'; ! r->status = atoi(&buffer[9]); ! buffer[12] = ' '; ! r->status_line = pstrdup(pool, &buffer[9]); ! ! /* read the headers. */ ! /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */ ! /* Also, take care with headers with multiple occurences. */ ! ! resp_hdrs = read_headers(pool, buffer, HUGE_STRING_LEN, f); ! } else ! { ! /* an http/0.9 response */ ! strcpy(inprotocol, "HTTP/0.9"); ! strcpy(outprotocol, "HTTP/1.0"); ! r->status = 200; ! r->status_line = "200 OK"; ! ! /* no headers */ ! resp_hdrs = make_array(pool, 2, sizeof(struct hdr_entry)); ! } ! ! kill_timeout(r); ! ! /* ! * HTTP/1.0 requires us to accept 3 types of dates, but only generate ! * one type ! */ ! ! len = resp_hdrs->nelts; ! hdr = (struct hdr_entry *)resp_hdrs->elts; ! for (i=0; i < len; i++) ! { ! if (hdr[i].value[0] == '\0') continue; ! p = hdr[i].field; ! if (strcasecmp(p, "Date") == 0 || ! strcasecmp(p, "Last-Modified") == 0 || ! strcasecmp(p, "Expires") == 0) ! hdr[i].value = date_canon(pool, hdr[i].value); ! } ! ! /* check if NoCache directive on this host */ ! for (i=0; i < conf->nocaches->nelts; i++) ! { ! if (ent[i].name[0] == '*' || (ent[i].name != NULL && ! strstr(host, ent[i].name) != NULL)) ! nocache = 1; ! } ! ! i = cache_update(c, resp_hdrs, inprotocol, nocache); ! if (i != DECLINED) ! { ! pclosef(pool, sock); ! return i; ! } ! ! cache = c->fp; ! ! hard_timeout ("proxy receive", r); ! ! /* write status line */ ! if (!r->assbackwards) ! rvputs(r, "HTTP/1.0 ", r->status_line, "\015\012", NULL); ! if (cache != NULL) ! if (bvputs(cache, outprotocol, " ", r->status_line, "\015\012", NULL) ! == -1) ! cache = cache_error(c); ! ! /* send headers */ ! len = resp_hdrs->nelts; ! for (i=0; i < len; i++) ! { ! if (hdr[i].field == NULL || hdr[i].value == NULL || ! hdr[i].value[0] == '\0') continue; ! if (!r->assbackwards) ! rvputs(r, hdr[i].field, ": ", hdr[i].value, "\015\012", NULL); ! if (cache != NULL) ! if (bvputs(cache, hdr[i].field, ": ", hdr[i].value, "\015\012", ! NULL) == -1) ! cache = cache_error(c); ! } ! ! if (!r->assbackwards) rputs("\015\012", r); ! if (cache != NULL) ! if (bputs("\015\012", cache) == -1) cache = cache_error(c); ! ! bsetopt(r->connection->client, BO_BYTECT, &zero); ! r->sent_bodyct = 1; ! /* Is it an HTTP/0.9 respose? If so, send the extra data */ ! if (strcmp(inprotocol, "HTTP/0.9") == 0) ! { ! bwrite(r->connection->client, buffer, len); ! if (cache != NULL) ! if (bwrite(f, buffer, len) != len) cache = cache_error(c); ! } ! ! /* send body */ ! /* if header only, then cache will be NULL */ ! /* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */ ! if (!r->header_only) send_fb(f, r, cache, c); ! ! cache_tidy(c); ! ! pclosef(pool, sock); ! ! return OK; ! } ! ! static handler_rec proxy_handlers[] = { ! { "proxy-server", proxy_handler }, ! { NULL } ! }; ! static void * create_proxy_config(pool *p, server_rec *s) --- 225,241 ---- * give up?? */ /* handle the scheme */ ! if (r->method_number == M_CONNECT) ! return proxy_connect_handler(r, cr, url); ! if (strcmp(scheme, "http") == 0) ! return proxy_http_handler(r, cr, url, NULL, 0); ! if (strcmp(scheme, "ftp") == 0) ! return proxy_ftp_handler(r, cr, url); else return NOT_IMPLEMENTED; } ! /* -------------------------------------------------------------- */ ! /* Setup configurable data */ static void * create_proxy_config(pool *p, server_rec *s) *************** *** 3453,3464 **** return NULL; } static command_rec proxy_cmds[] = { { "ProxyRequests", set_proxy_req, NULL, RSRC_CONF, FLAG, "on if the true proxy requests should be accepted"}, ! { "ProxyRemote", add_proxy, NULL, RSRC_CONF, TAKE2, "a scheme, partial URL or '*' and a proxy server"}, ! { "ProxyPass", add_pass, NULL, RSRC_CONF, TAKE2, "a virtual path and a URL"}, { "CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1, "The directory to store cache files"}, --- 447,463 ---- return NULL; } + static handler_rec proxy_handlers[] = { + { "proxy-server", proxy_handler }, + { NULL } + }; + static command_rec proxy_cmds[] = { { "ProxyRequests", set_proxy_req, NULL, RSRC_CONF, FLAG, "on if the true proxy requests should be accepted"}, ! { "ProxyRemote", add_proxy, NULL, RSRC_CONF, TAKE2, "a scheme, partial URL or '*' and a proxy server"}, ! { "ProxyPass", add_pass, NULL, RSRC_CONF, TAKE2, "a virtual path and a URL"}, { "CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1, "The directory to store cache files"}, *************** *** 3467,3473 **** { "CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF, TAKE1, "The maximum time in hours to cache a document"}, { "CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF, TAKE1, ! "The default time in hours to cache a document"}, { "CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF, TAKE1, "The factor used to estimate Expires date from LastModified date"}, { "CacheGcInterval", set_cache_gcint, NULL, RSRC_CONF, TAKE1, --- 466,472 ---- { "CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF, TAKE1, "The maximum time in hours to cache a document"}, { "CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF, TAKE1, ! "The default time in hours to cache a document"}, { "CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF, TAKE1, "The factor used to estimate Expires date from LastModified date"}, { "CacheGcInterval", set_cache_gcint, NULL, RSRC_CONF, TAKE1, *************** *** 3481,3501 **** { NULL } }; - module proxy_module = { STANDARD_MODULE_STUFF, ! NULL, /* initializer */ ! NULL, /* create per-directory config structure */ ! NULL, /* merge per-directory config structures */ ! create_proxy_config, /* create per-server config structure */ ! NULL, /* merge per-server config structures */ ! proxy_cmds, /* command table */ ! proxy_handlers, /* handlers */ ! proxy_trans, /* translate_handler */ ! NULL, /* check_user_id */ ! NULL, /* check auth */ ! NULL, /* check access */ ! NULL, /* type_checker */ ! proxy_fixup, /* pre-run fixups */ ! NULL /* logger */ }; --- 480,500 ---- { NULL } }; module proxy_module = { STANDARD_MODULE_STUFF, ! NULL, /* initializer */ ! NULL, /* create per-directory config structure */ ! NULL, /* merge per-directory config structures */ ! create_proxy_config, /* create per-server config structure */ ! NULL, /* merge per-server config structures */ ! proxy_cmds, /* command table */ ! proxy_handlers, /* handlers */ ! proxy_trans, /* translate_handler */ ! NULL, /* check_user_id */ ! NULL, /* check auth */ ! NULL, /* check access */ ! NULL, /* type_checker */ ! proxy_fixup, /* pre-run fixups */ ! NULL /* logger */ }; +