dgaudet 97/07/19 01:58:37
Modified: htdocs/manual new_features_1_3.html htdocs/manual/mod mod_proxy.html src CHANGES src/modules/proxy mod_proxy.c mod_proxy.h proxy_ftp.c proxy_http.c proxy_util.c Log: NoProxy and ProxyDomain directives. Submitted by: Martin Kraemer <[EMAIL PROTECTED]> Reviewed by: Dean Gaudet Revision Changes Path 1.9 +4 -0 apache/htdocs/manual/new_features_1_3.html Index: new_features_1_3.html =================================================================== RCS file: /export/home/cvs/apache/htdocs/manual/new_features_1_3.html,v retrieving revision 1.8 retrieving revision 1.9 diff -C3 -r1.8 -r1.9 *** new_features_1_3.html 1997/07/18 21:01:31 1.8 --- new_features_1_3.html 1997/07/19 08:58:31 1.9 *************** *** 105,110 **** --- 105,114 ---- before any requests are handled. This allows the module to set up anything that need to be done once per processes. For example, connections to databases. + + <li><strong><a href="mod/mod_proxy.html#noproxy">NoProxy</a></strong> + and <strong><a href="mod/mod_proxy.html#proxydomain">ProxyDomain</a> + </strong> directives added to proxy, useful for intranets. </ul> 1.26 +148 -0 apache/htdocs/manual/mod/mod_proxy.html Index: mod_proxy.html =================================================================== RCS file: /export/home/cvs/apache/htdocs/manual/mod/mod_proxy.html,v retrieving revision 1.25 retrieving revision 1.26 diff -C3 -r1.25 -r1.26 *** mod_proxy.html 1997/07/06 17:19:18 1.25 --- mod_proxy.html 1997/07/19 08:58:32 1.26 *************** *** 42,47 **** --- 42,49 ---- <li><a href="#proxyremote">ProxyRemote</a> <li><a href="#proxypass">ProxyPass</a> <li><a href="#proxyblock">ProxyBlock</a> + <li><a href="#noproxy">NoProxy</a> + <li><a href="#proxydomain">ProxyDomain</a> <li><a href="#cacheroot">CacheRoot</a> <li><a href="#cachesize">CacheSize</a> <li><a href="#cachemaxexpire">CacheMaxExpire</a> *************** *** 150,155 **** --- 152,284 ---- blocks connections to all sites. + <A name="noproxy"><h2>NoProxy</h2></A> + <strong>Syntax:</strong> NoProxy { <A HREF="#domain"><em><Domain></em></A> + | <A HREF="#subnet"><em><SubNet></em></A> + | <A HREF="#ipaddr"><em><IpAddr></em></A> + | <A HREF="#hostname"><em><Hostname></em></A> + } <br> + <strong>Context:</strong> server config<br> + <strong>Status:</strong> Base<br> + <strong>Module:</strong> mod_proxy<br> + <strong>Compatibility:</strong> NoProxy is only available in a patch to + Apache 1.2.1 and later.<p> + + This directive is only useful for apache proxy servers within intranets. + The NoProxy directive specifies a list of subnets, IP addresses, hosts + and/or domains, separated by spaces. A request to a host which matches + one or more of these is always served directly, without forwarding to + the configured ProxyRemote proxy server(s).<br>Example: + + <pre> + ProxyRemote * http://firewall.mycompany.com:81 + NoProxy .mycompany.com 192.168.112.0/21 + </pre> + The arguments to the NoProxy directive are one of the following type list: + <DL> + <!-- ===================== Domain ======================= --> + <A NAME="domain"> + <DT><EM>Domain</EM> + <DD>A <EM>Domain</EM> is a partially qualified DNS domain name, preceded + by a period. + It represents a list of hosts which logically belong to the same DNS + domain or zone (i.e. the suffixes of the hostnames are all ending in + <EM>Domain</EM>).<BR> + Examples: <SAMP>.com</SAMP> <SAMP>.apache.org.</SAMP> <SAMP>.sni.de</SAMP><BR> + To distinguish <EM>Domain</EM>s from <A HREF="#hostname"><EM>Hostname</EM></A>s (both + syntactically and semantically; a DNS domain can have a DNS A record, + too!), <EM>Domain</EM>s are always written + with a leading period.<BR> + Note: Domain name comparisons are done without regard to the case, + and <EM>Domain</EM>s are always assumed to be anchored in the root + of the DNS tree, therefore two domains <SAMP>.MyDomain.com</SAMP> and + <SAMP>.mydomain.com.</SAMP> (note the trailing period) are + considered equal. Since a domain comparison does not involve a DNS + lookup, it is much more efficient than subnet comparison. + + <!-- ===================== SubNet ======================= --> + <A NAME="subnet"> + <DT><EM>SubNet</EM> + <DD>A <EM>SubNet</EM> is a partially qualified internet address in + numeric (dotted quad) form, optionally followed by a slash and the + netmask, specified as the number of significant bits in the + <EM>SubNet</EM>. It is used to represent a subnet of hosts which can + be reached over a common network interface. In the absence of the + explicit net mask it is assumed that omitted (or zero valued) + trailing digits specify the mask. (In this case, the netmask can + only be multiples of 8 bits wide.)<BR> + Examples: + <DL> + <DT><SAMP>192.168</SAMP> or <SAMP>192.168.0.0</SAMP> + <DD>the subnet 192.168.0.0 with a netmask of 16 valid bits + (sometimes used in the netmask form <SAMP>255.255.0.0</SAMP>) + <DT><SAMP>139.25.112.0/21</SAMP> + <DD>the subnet <SAMP>139.25.112.0/21</SAMP> with a netmask of 21 + valid bits (also used in the form 255.255.248.0) + </DL> + As a degenerate case, a <EM>SubNet</EM> with 32 valid bits is the + equivalent to an <EM>IPAddr</EM>, while a <EM>SubNet</EM> with zero + valid bits (e.g., 0.0.0.0/0) is the same as the constant + <EM>_Default_</EM>, matching any IP address. + + <!-- ===================== IPAddr ======================= --> + <A NAME="ipaddr"> + <DT><EM>IPAddr</EM> + <DD>A <EM>IPAddr</EM> represents a fully qualified internet address in + numeric (dotted quad) form. Usually, this address represents a + host, but there need not necessarily be a DNS domain name + connected with the address.<BR> + Example: 139.25.113.10<BR> + Note: An <EM>IPAddr</EM> does not need to be resolved by the DNS system, so + it can result in more effective apache performance.<BR> + <p><strong>See Also:</strong> + <a href="../dns-caveats.html">DNS Issues</a></p> + + <!-- ===================== Hostname ======================= --> + <A NAME="hostname"> + <DT><EM>Hostname</EM> + <DD>A <EM>Hostname</EM> is a fully qualified DNS domain name which can + be resolved to one or more <A HREF="#ipaddr"><EM>IPAddrs</EM></A> via the DNS domain name service. + It represents a logical host (in contrast to <A HREF="#domain"><EM>Domain</EM></A>s, see + above) and must be resolvable to at least one <A HREF="#ipaddr"><EM>IPAddr</EM></A> (or + often to a list of hosts with different <A HREF="#ipaddr"><EM>IPAddr</EM></A>'s).<BR> + Examples: <SAMP>prep.ai.mit.edu</SAMP> + <SAMP>www.apache.org.</SAMP><BR> + Note: In many situations, it is more effective to specify an + <A HREF="#ipaddr"><EM>IPAddr</EM></A> in place of a <EM>Hostname</EM> since a DNS lookup + can be avoided. Name resolution in Apache can take a remarkable deal + of time when the connection to the name server uses a slow PPP + link.<BR> + Note: <EM>Hostname</EM> comparisons are done without regard to the case, + and <EM>Hostname</EM>s are always assumed to be anchored in the root + of the DNS tree, therefore two hosts <SAMP>WWW.MyDomain.com</SAMP> + and <SAMP>www.mydomain.com.</SAMP> (note the trailing period) are + considered equal.<BR> + <p><strong>See Also:</strong> + <a href="../dns-caveats.html">DNS Issues</a></p> + </DL> + + <A name="proxydomain"><h2>ProxyDomain</h2></A> + <strong>Syntax:</strong> ProxyDomain <em><Domain></em><br> + <strong>Context:</strong> server config<br> + <strong>Status:</strong> Base<br> + <strong>Module:</strong> mod_proxy<br> + <strong>Compatibility:</strong> ProxyDomain is only available in a patch to + Apache 1.2.1 and later.<p> + + This directive is only useful for apache proxy servers within intranets. + The ProxyDomain directive specifies the default domain which the apache + proxy server will belong to. If a request to a host without a domain name + is encountered, a redirection response to the same host + with the configured <em>Domain</em> appended will be generated. + <br>Example: + + <pre> + ProxyRemote * http://firewall.mycompany.com:81 + NoProxy .mycompany.com 192.168.112.0/21 + ProxyDomain .mycompany.com + </pre> + <A name="cacheroot"><h2>CacheRoot</h2></A> <strong>Syntax:</strong> CacheRoot <em><directory></em><br> <strong>Context:</strong> server config<br> *************** *** 299,304 **** --- 428,434 ---- <li><a href="#startup">Why does Apache start more slowly when using the proxy module?</a> <li><a href="#socks">Can I use the Apache proxy module with my SOCKS proxy?</a> + <li><a href="#intranet">What other functions are useful for an intranet proxy server?</a> </ul> <h2><a name="access">Controlling access to your proxy</a></h2> *************** *** 359,364 **** --- 489,512 ---- Remember that you'll also have to grant access to your Apache proxy machine by permitting connections on the appropriate ports in your SOCKS daemon's configuration.<p> + + <h2><a name="intranet">What other functions are useful for an intranet proxy server?</a></h2> + + <p>An Apache proxy server situated in an intranet needs to forward external + requests through the company's firewall. However, when it has to access + resources within the intranet, it can bypass the firewall when accessing + hosts. The <A HREF="#noproxy">NoProxy</A> directive is useful for specifying + which hosts belong to the intranet and should be accessed directly.</p> + + <p>Users within an intranet tend to omit the local domain name from their + WWW requests, thus requesting "http://somehost/" instead of + "http://somehost.my.dom.ain/". Some commercial proxy servers let them get + away with this and simply serve the request, implying a configured + local domain. When the <A HREF="#proxydomain">ProxyDomain</A> directive + is used and the server is <A HREF="#proxyrequests">configured for + proxy service</A>, Apache can return a redirect response and send the client + to the correct, fully qualified, server address. This is the preferred method + since the user's bookmark files will then contain fully qualified hosts.</p> <!--#include virtual="footer.html" --> </BODY> 1.346 +5 -0 apache/src/CHANGES Index: CHANGES =================================================================== RCS file: /export/home/cvs/apache/src/CHANGES,v retrieving revision 1.345 retrieving revision 1.346 diff -C3 -r1.345 -r1.346 *** CHANGES 1997/07/19 06:20:42 1.345 --- CHANGES 1997/07/19 08:58:33 1.346 *************** *** 1,5 **** --- 1,10 ---- Changes with Apache 1.3 + *) Added NoProxy directive to avoid using ProxyRemote for selected + addresses. Added ProxyDomain directive to cause unqualified + names to be qualified by redirection. + [Martin Kraemer <[EMAIL PROTECTED]>] + *) Support Proxy Authentication, and don't pass the Proxy-Authorize header to the remote host in the proxy. [Sameer Parekh and Wallace] 1.16 +182 -1 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.15 retrieving revision 1.16 diff -C3 -r1.15 -r1.16 *** mod_proxy.c 1997/07/17 22:28:01 1.15 --- mod_proxy.c 1997/07/19 08:58:34 1.16 *************** *** 51,56 **** --- 51,57 ---- */ #include "mod_proxy.h" + #include "http_log.h" /* Some WWW schemes and their default ports; this is basically /etc/services */ /* This will become global when the protocol abstraction comes */ *************** *** 186,191 **** --- 187,262 ---- } + + /* Send a redirection if the request contains a hostname which is not */ + /* fully qualified, i.e. doesn't have a domain name appended. Some proxy */ + /* servers like Netscape's allow this and access hosts from the local */ + /* domain in this case. I think it is better to redirect to a FQDN, since */ + /* these will later be found in the bookmarks files. */ + /* The "ProxyDomain" directive determines what domain will be appended */ + int + proxy_needsdomain(request_rec *r, const char *url, const char *domain) + { + char *scheme = pstrdup (r->pool, url); + char *url_copy, *user = NULL, *password = NULL, *path, *err, *host; + int port = -1; + + /* We only want to worry about GETs */ + if (r->method_number != M_GET) return DECLINED; + + /* Set url to the first char after "scheme://" */ + if ((url_copy = strchr(scheme,':')) == NULL + || url_copy[1] != '/' || url_copy[2] != '/') + return DECLINED; + + *url_copy++ = '\0'; /* delimit scheme, make url_copy point to "//", which is what proxy_canon_netloc expects */ + + /* Save the path - it will be re-appended for the redirection later */ + path = strchr(&url_copy[2], '/'); + if (path != NULL) + ++path; /* leading '/' will be overwritten by proxy_canon_netloc */ + else + path = ""; + + /* Split request into user, password, host, port */ + err = proxy_canon_netloc(r->pool, &url_copy, &user, &password, &host, &port); + + if (err != NULL) + { + log_error(err, r->server); + return DECLINED; + } + + /* If host does contain a dot already, or it is "localhost", decline */ + if (strchr (host, '.') != NULL || strcasecmp(host, "localhost") == 0) + return DECLINED; /* host name has a dot already */ + else + { + char *nuri; + char *ref = table_get(r->headers_in, "Referer"); + char strport[10]; + + /* now, rebuild URL */ + if (port == -1) + strcpy (strport, ""); + else + ap_snprintf(strport, sizeof(strport), ":%d", port); + + /* Reassemble the request, but insert the domain after the host name */ + nuri = pstrcat(r->pool, scheme, "://", (user != NULL) ? user : "", + (password != NULL) ? ":" : "", + (password != NULL) ? password : "", + (user != NULL) ? "@" : "", + host, domain, strport, "/", path, + NULL); + + table_set(r->headers_out, "Location", nuri); + log_error(pstrcat(r->pool, "Domain missing: ", r->uri, " sent to ", nuri, + ref ? " from " : NULL, ref, NULL), r->server); + return REDIRECT; + } + } + /* -------------------------------------------------------------- */ /* Invoke handler */ *************** *** 200,205 **** --- 271,277 ---- struct proxy_remote *ents=(struct proxy_remote *)proxies->elts; int i, rc; struct cache_req *cr; + int direct_connect = 0; if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; *************** *** 213,224 **** rc = proxy_cache_check(r, url, &conf->cache, &cr); if (rc != DECLINED) return rc; *p = '\0'; scheme = pstrdup(r->pool, url); *p = ':'; ! /* firstly, try a proxy */ for (i=0; i < proxies->nelts; i++) { p = strchr(ents[i].scheme, ':'); /* is it a partial URL? */ --- 285,328 ---- rc = proxy_cache_check(r, url, &conf->cache, &cr); if (rc != DECLINED) return rc; + /* If the host doesn't have a domain name, add one and redirect. */ + if (conf->domain != NULL + && proxy_needsdomain(r, url, conf->domain) == REDIRECT) + return REDIRECT; + *p = '\0'; scheme = pstrdup(r->pool, url); *p = ':'; ! /* Check URI's destination host against NoProxy hosts */ ! /* Bypass ProxyRemote server lookup if configured as NoProxy */ ! /* we only know how to handle communication to a proxy via http */ ! /*if (strcmp(scheme, "http") == 0)*/ ! { ! int i; ! struct dirconn_entry *list=(struct dirconn_entry*)conf->dirconn->elts; ! ! /* if (*++p == '/' && *++p == '/') */ ! ! for (direct_connect=i=0; i < conf->dirconn->nelts && !direct_connect; i++) ! { ! direct_connect = list[i].matcher (&list[i], r); ! /*log_error("URI and NoProxy:", r->server);*/ ! /*log_error(r->uri, r->server);*/ ! /*log_error(list[i].name, r->server);*/ ! } ! #if DEBUGGING ! { ! char msg[256]; ! sprintf (msg, (direct_connect)?"NoProxy for %s":"UseProxy for %s", r->uri); ! log_error(msg, r->server); ! } ! #endif ! } ! ! /* firstly, try a proxy, unless a NoProxy directive is active */ + if (!direct_connect) for (i=0; i < proxies->nelts; i++) { p = strchr(ents[i].scheme, ':'); /* is it a partial URL? */ *************** *** 264,270 **** --- 368,376 ---- ps->proxies = make_array(p, 10, sizeof(struct proxy_remote)); ps->aliases = make_array(p, 10, sizeof(struct proxy_alias)); ps->noproxies = make_array(p, 10, sizeof(struct noproxy_entry)); + ps->dirconn = make_array(p, 10, sizeof(struct dirconn_entry)); ps->nocaches = make_array(p, 10, sizeof(struct nocache_entry)); + ps->domain = NULL; ps->req = 0; ps->cache.root = NULL; *************** *** 366,371 **** --- 472,548 ---- return NULL; } + /* Similar to set_proxy_exclude(), but defining directly connected hosts, + * which should never be accessed via the configured ProxyRemote servers + */ + static const char * + set_proxy_dirconn(cmd_parms *parms, void *dummy, char *arg) + { + server_rec *s = parms->server; + proxy_server_conf *conf = + get_module_config (s->module_config, &proxy_module); + struct dirconn_entry *New; + struct dirconn_entry *list=(struct dirconn_entry*)conf->dirconn->elts; + int found = 0; + int i; + + /* Don't duplicate entries */ + for (i=0; i < conf->dirconn->nelts; i++) + { + if (strcasecmp(arg, list[i].name) == 0) + found = 1; + } + + if (!found) + { + New = push_array (conf->dirconn); + New->name = arg; + + if (proxy_is_ipaddr(New)) + { + #if DEBUGGING + fprintf(stderr,"Parsed addr %s\n", inet_ntoa(New->addr)); + fprintf(stderr,"Parsed mask %s\n", inet_ntoa(New->mask)); + #endif + } + else if (proxy_is_domainname(New)) + { + str_tolower(New->name); + #if DEBUGGING + fprintf(stderr,"Parsed domain %s\n", New->name); + #endif + } + else if (proxy_is_hostname(New)) + { + str_tolower(New->name); + #if DEBUGGING + fprintf(stderr,"Parsed host %s\n", New->name); + #endif + } + else + { + proxy_is_word(New); + #if DEBUGGING + fprintf(stderr,"Parsed word %s\n", New->name); + #endif + } + } + return NULL; + } + + static const char * + set_proxy_domain(cmd_parms *parms, void *dummy, char *arg) + { + proxy_server_conf *psf = + get_module_config (parms->server->module_config, &proxy_module); + + if (arg[0] != '.') + return "Domain name must start with a dot."; + + psf->domain = arg; + return NULL; + } + static const char * set_proxy_req(cmd_parms *parms, void *dummy, int flag) { *************** *** 519,524 **** --- 696,705 ---- "a virtual path and a URL"}, { "ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, ITERATE, "A list of names, hosts or domains to which the proxy will not connect" }, + { "NoProxy", set_proxy_dirconn, NULL, RSRC_CONF, ITERATE, + "A list of domains, hosts, or subnets to which the proxy will connect directly" }, + { "ProxyDomain", set_proxy_domain, NULL, RSRC_CONF, TAKE1, + "The default intranet domain name (in absence of a domain in the URL)" }, { "CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1, "The directory to store cache files"}, { "CacheSize", set_cache_size, NULL, RSRC_CONF, TAKE1, 1.13 +13 -0 apache/src/modules/proxy/mod_proxy.h Index: mod_proxy.h =================================================================== RCS file: /export/home/cvs/apache/src/modules/proxy/mod_proxy.h,v retrieving revision 1.12 retrieving revision 1.13 diff -C3 -r1.12 -r1.13 *** mod_proxy.h 1997/06/15 19:22:47 1.12 --- mod_proxy.h 1997/07/19 08:58:35 1.13 *************** *** 151,156 **** --- 151,163 ---- char *fake; }; + struct dirconn_entry { + char *name; + struct in_addr addr,mask; + struct hostent hostlist; + int (*matcher)(struct dirconn_entry *This, request_rec *r); + }; + struct noproxy_entry { char *name; struct in_addr addr; *************** *** 185,191 **** --- 192,200 ---- array_header *proxies; array_header *aliases; array_header *noproxies; + array_header *dirconn; array_header *nocaches; + char *domain; /* domain name to use in absence of a domain name in the request */ int req; /* true if proxy requests are enabled */ } proxy_server_conf; *************** *** 269,274 **** --- 278,287 ---- BUFF *proxy_cache_error(struct cache_req *r); int proxyerror(request_rec *r, const char *message); const char *proxy_host2addr(const char *host, struct hostent *reqhp); + int proxy_is_ipaddr(struct dirconn_entry *This); + int proxy_is_domainname(struct dirconn_entry *This); + int proxy_is_hostname(struct dirconn_entry *This); + int proxy_is_word(struct dirconn_entry *This); int proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r); int proxy_garbage_init(server_rec *, pool *); 1.24 +12 -0 apache/src/modules/proxy/proxy_ftp.c Index: proxy_ftp.c =================================================================== RCS file: /export/home/cvs/apache/src/modules/proxy/proxy_ftp.c,v retrieving revision 1.23 retrieving revision 1.24 diff -C3 -r1.23 -r1.24 *** proxy_ftp.c 1997/06/16 19:32:53 1.23 --- proxy_ftp.c 1997/07/19 08:58:35 1.24 *************** *** 508,513 **** --- 508,524 ---- return SERVER_ERROR; } + #ifdef SINIX_D_RESOLVER_BUG + { struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list; + + for ( ; ip_addr->s_addr != 0; ++ip_addr) { + memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr)); + i = proxy_doconnect(sock, &server, r); + if (i == 0) + break; + } + } + #else j = 0; while (server_hp.h_addr_list[j] != NULL) { memcpy(&server.sin_addr, server_hp.h_addr_list[j], *************** *** 517,522 **** --- 528,534 ---- break; j++; } + #endif if (i == -1) return proxyerror(r, "Could not connect to remote machine"); 1.22 +12 -0 apache/src/modules/proxy/proxy_http.c Index: proxy_http.c =================================================================== RCS file: /export/home/cvs/apache/src/modules/proxy/proxy_http.c,v retrieving revision 1.21 retrieving revision 1.22 diff -C3 -r1.21 -r1.22 *** proxy_http.c 1997/07/19 06:20:49 1.21 --- proxy_http.c 1997/07/19 08:58:35 1.22 *************** *** 227,232 **** --- 227,243 ---- note_cleanups_for_socket(pool, sock); + #ifdef SINIX_D_RESOLVER_BUG + { struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list; + + for ( ; ip_addr->s_addr != 0; ++ip_addr) { + memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr)); + i = proxy_doconnect(sock, &server, r); + if (i == 0) + break; + } + } + #else j = 0; while (server_hp.h_addr_list[j] != NULL) { memcpy(&server.sin_addr, server_hp.h_addr_list[j], *************** *** 236,241 **** --- 247,253 ---- break; j++; } + #endif if (i == -1) { if (proxyhost != NULL) return DECLINED; /* try again another way */ 1.20 +357 -0 apache/src/modules/proxy/proxy_util.c Index: proxy_util.c =================================================================== RCS file: /export/home/cvs/apache/src/modules/proxy/proxy_util.c,v retrieving revision 1.19 retrieving revision 1.20 diff -C3 -r1.19 -r1.20 *** proxy_util.c 1997/06/16 15:16:28 1.19 --- proxy_util.c 1997/07/19 08:58:35 1.20 *************** *** 56,61 **** --- 56,67 ---- #include "http_main.h" #include "md5.h" #include "multithread.h" + #include "http_log.h" + + 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); /* already called in the knowledge that the characters are hex digits */ int *************** *** 245,250 **** --- 251,259 ---- return "Bad IP address in URL"; } + /* if (strchr(host,'.') == NULL && domain != NULL) + host = pstrcat(pool, host, domain, NULL); + */ *urlp = url; *hostp = host; *************** *** 796,801 **** --- 805,1158 ---- } memcpy(reqhp, hp, sizeof(struct hostent)); return NULL; + } + + 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 = pstrdup(r->pool, &url[1]); /* make it point to "//", which is what proxy_canon_netloc expects */ + + err = proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port); + + if (err != NULL) + log_error(err, r->server); + + 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 + proxy_is_ipaddr(struct dirconn_entry *This) + { + const char *addr = This->name; + unsigned long ip_addr[4]; + int i,quads; + unsigned long bits; + + /* if the address is given with an explicit netmask, use that */ + /* Due to a deficiency in 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. */ + + /* quads = sscanf(what, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], + &ip_addr[2], &ip_addr[3]); + commented out: use of strtok() allows arbitrary base, like in: + 139.25.113.10 == 0x8b.0x19.0x71.0x0a + (yes, inet_addr() can parse that, too!) + */ + + /* 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 (!isdigit(*addr)) + return 0; /* no digit at start of quad */ + + ip_addr[quads] = strtoul(addr, &tmp, 0); + + if (tmp == addr) /* expected a digit, found something else */ + 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] == '/' && isdigit(addr[1])) /* net mask follows: */ + { + char *tmp; + + ++addr; + + bits = strtoul(addr, &tmp, 0); + + if (tmp == addr) /* expected a digit, found something else */ + return 0; + + addr = tmp; + + if (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; + const char *found; + const char *host = proxy_get_host_of_request(r); + + 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 = 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_list = (struct in_addr *) *the_host.h_addr_list; ip_list->s_addr != 0UL; ++ip_list) + 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 + } + + /* Use net math to determine if a host lies in a subnet */ + /*return This->addr.s_addr == (r->connection->remote_addr.sin_addr.s_addr & This->mask.s_addr);*/ + return 0; + } + + /* Return TRUE if addr represents a domain name */ + int + proxy_is_domainname(struct dirconn_entry *This) + { + 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; isalnum(addr[i]) || addr[i]=='-' || addr[i]=='.'; ++i) + ; + + if (addr[i] == ':') + { + fprintf(stderr,"@@@@ handle optional port in proxy_is_domainname()\n"); + /* @@@@ handle optional port */ + } + + 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 + proxy_is_hostname(struct dirconn_entry *This) + { + 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; isalnum(addr[i]) || addr[i]=='-' || addr[i]=='.'; ++i) + ; + + if (addr[i] == ':') + { + fprintf(stderr,"@@@@ handle optional port in proxy_is_hostname()\n"); + /* @@@@ handle optional port */ + } + + if (addr[i] != '\0' || proxy_host2addr(addr, &This->hostlist) != NULL) + return 0; + + /* 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; + char *host2 = proxy_get_host_of_request(r); + int h2_len=strlen(host2); + int 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->hostlist.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 + proxy_is_word(struct dirconn_entry *This) + { + 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) + { + char *host = proxy_get_host_of_request(r); + return host != NULL && strstr(host, This->name) != NULL; } int