--- bootstrap.sh	2005-09-28 15:48:10.015438476 +0200
+++ bootstrap.sh	2005-09-28 15:51:59.854322630 +0200
@@ -66,6 +66,9 @@
 amver=`find_version automake ${amversions}`
 acver=`find_version autoconf ${acversions}`
 
+# Set environment variable to tell automake which autoconf to use.
+AUTOCONF="autoconf${acver}" ; export AUTOCONF
+
 # Bootstrap the autotool subsystems
 bootstrap aclocal$amver
 bootstrap autoheader$acver

--- configure.in	2005-09-28 15:48:29.056963262 +0200
+++ configure.in	2005-09-28 15:51:59.860322131 +0200
@@ -1200,6 +1200,24 @@
   fi
 ])
 
+follow_xff=1
+AC_ARG_ENABLE(follow-x-forwarded-for,
+[  --enable-follow-x-forwarded-for
+                          Enable support for following the X-Forwarded-For
+                          HTTP header to try to find the IP address of the
+                          original or indirect client when a request has
+                          been forwarded through other proxies.],
+[ if test "$enableval" = "yes" ; then
+    echo "follow X-Forwarded-For enabled"
+    follow_xff=1
+  fi
+])
+if test $follow_xff = 1; then
+    AC_DEFINE(FOLLOW_X_FORWARDED_FOR, 1, [Enable following X-Forwarded-For headers])
+else
+    AC_DEFINE(FOLLOW_X_FORWARDED_FOR, 0)
+fi
+
 # Force some compilers to use ANSI features
 #
 case "$host" in

--- src/acl.c	2005-09-28 15:48:57.963723584 +0200
+++ src/acl.c	2005-09-28 15:51:59.867321548 +0200
@@ -2162,6 +2162,11 @@
     cbdataLock(A);
     if (request != NULL) {
 	checklist->request = requestLink(request);
+#if FOLLOW_X_FORWARDED_FOR
+	if (Config.onoff.acl_uses_indirect_client) {
+	    checklist->src_addr = request->indirect_client_addr;
+	} else
+#endif /* FOLLOW_X_FORWARDED_FOR */
 	checklist->src_addr = request->client_addr;
 	checklist->my_addr = request->my_addr;
 	checklist->my_port = request->my_port;

--- src/cf.data.pre	2005-09-28 15:49:58.931999119 +0200
+++ src/cf.data.pre	2005-09-28 15:51:59.876320799 +0200
@@ -2174,6 +2174,92 @@
 NOCOMMENT_END
 DOC_END
 
+NAME: follow_x_forwarded_for
+TYPE: acl_access
+IFDEF: FOLLOW_X_FORWARDED_FOR
+LOC: Config.accessList.followXFF
+DEFAULT: none
+DEFAULT_IF_NONE: deny all
+DOC_START
+	Allowing or Denying the X-Forwarded-For header to be followed to
+	find the original source of a request.
+
+	Requests may pass through a chain of several other proxies
+	before reaching us.  The X-Forwarded-For header will contain a
+	comma-separated list of the IP addresses in the chain, with the
+	rightmost address being the most recent.
+
+	If a request reaches us from a source that is allowed by this
+	configuration item, then we consult the X-Forwarded-For header
+	to see where that host received the request from.  If the
+	X-Forwarded-For header contains multiple addresses, and if
+	acl_uses_indirect_client is on, then we continue backtracking
+	until we reach an address for which we are not allowed to
+	follow the X-Forwarded-For header, or until we reach the first
+	address in the list.  (If acl_uses_indirect_client is off, then
+	it's impossible to backtrack through more than one level of
+	X-Forwarded-For addresses.)
+
+	The end result of this process is an IP address that we will
+	refer to as the indirect client address.  This address may
+	be treated as the client address for access control, delay
+	pools and logging, depending on the acl_uses_indirect_client,
+	delay_pool_uses_indirect_client and log_uses_indirect_client
+	options.
+
+	SECURITY CONSIDERATIONS:
+
+		Any host for which we follow the X-Forwarded-For header
+		can place incorrect information in the header, and Squid
+		will use the incorrect information as if it were the
+		source address of the request.  This may enable remote
+		hosts to bypass any access control restrictions that are
+		based on the client's source addresses.
+
+	For example:
+
+		acl localhost src 127.0.0.1
+		acl my_other_proxy srcdomain .proxy.example.com
+		follow_x_forwarded_for allow localhost
+		follow_x_forwarded_for allow my_other_proxy
+DOC_END
+
+NAME: acl_uses_indirect_client
+COMMENT: on|off
+TYPE: onoff
+IFDEF: FOLLOW_X_FORWARDED_FOR
+DEFAULT: on
+LOC: Config.onoff.acl_uses_indirect_client
+DOC_START
+	Controls whether the indirect client address
+	(see follow_x_forwarded_for) is used instead of the
+	direct client address in acl matching.
+DOC_END
+
+NAME: delay_pool_uses_indirect_client
+COMMENT: on|off
+TYPE: onoff
+IFDEF: FOLLOW_X_FORWARDED_FOR && DELAY_POOLS
+DEFAULT: on
+LOC: Config.onoff.delay_pool_uses_indirect_client
+DOC_START
+	Controls whether the indirect client address
+	(see follow_x_forwarded_for) is used instead of the
+	direct client address in delay pools.
+DOC_END
+
+NAME: log_uses_indirect_client
+COMMENT: on|off
+TYPE: onoff
+IFDEF: FOLLOW_X_FORWARDED_FOR
+DEFAULT: on
+LOC: Config.onoff.log_uses_indirect_client
+DOC_START
+	Controls whether the indirect client address
+	(see follow_x_forwarded_for) is used instead of the
+	direct client address in the access log.
+DOC_END
+
 NAME: http_access
 TYPE: acl_access
 LOC: Config.accessList.http

--- src/client_side.c.orig	2005-09-28 15:50:34.252261723 +0200
+++ src/client_side.c	2005-09-29 10:55:47.006272632 +0200
@@ -84,6 +84,7 @@
 static const char *const crlf = "\r\n";
 
 #define FAILURE_MODE_TIME 300
+#define DANSGUARDIAN_IP_FORWARD
 
 /* Local functions */
 
@@ -110,6 +111,11 @@
 #if USE_IDENT
 static IDCB clientIdentDone;
 #endif
+#if FOLLOW_X_FORWARDED_FOR
+static void clientFollowXForwardedForStart(void *data);
+static void clientFollowXForwardedForNext(void *data);
+static void clientFollowXForwardedForDone(int answer, void *data);
+#endif /* FOLLOW_X_FORWARDED_FOR */
 static int clientOnlyIfCached(clientHttpRequest * http);
 static STCB clientSendMoreData;
 static STCB clientCacheHit;
@@ -180,10 +186,179 @@
     return ch;
 }
 
+#if FOLLOW_X_FORWARDED_FOR
+/*
+ * clientFollowXForwardedForStart() copies the X-Forwarded-For
+ * header into x_forwarded_for_iterator and passes control to
+ * clientFollowXForwardedForNext().
+ *
+ * clientFollowXForwardedForNext() checks the indirect_client_addr
+ * against the followXFF ACL and passes the result to
+ * clientFollowXForwardedForDone().
+ *
+ * clientFollowXForwardedForDone() either grabs the next address
+ * from the tail of x_forwarded_for_iterator and loops back to
+ * clientFollowXForwardedForNext(), or cleans up and passes control to
+ * clientAccessCheck().
+ */
+
+static void
+clientFollowXForwardedForStart(void *data)
+{
+    clientHttpRequest *http = data;
+    request_t *request = http->request;
+    if (Config.accessList.followXFF
+	&& httpHeaderHas(&request->header, HDR_X_FORWARDED_FOR))
+    {
+	request->x_forwarded_for_iterator = httpHeaderGetList(
+			&request->header, HDR_X_FORWARDED_FOR);
+	debug(33, 5) ("clientFollowXForwardedForStart: indirect_client_addr=%s XFF='%s'\n",
+			inet_ntoa(request->indirect_client_addr),
+			strBuf(request->x_forwarded_for_iterator));
+	clientFollowXForwardedForNext(http);
+    } else {
+	/* not configured to follow X-Forwarded-For, or nothing to follow */
+	debug(33, 5) ("clientFollowXForwardedForStart: nothing to do\n");
+	clientFollowXForwardedForDone(-1, http);
+    }
+}
+
+static void
+clientFollowXForwardedForNext(void *data)
+{
+    clientHttpRequest *http = data;
+    request_t *request = http->request;
+    debug(33, 5) ("clientFollowXForwardedForNext: indirect_client_addr=%s XFF='%s'\n",
+		    inet_ntoa(request->indirect_client_addr),
+		    strBuf(request->x_forwarded_for_iterator));
+    if (strLen(request->x_forwarded_for_iterator) != 0) {
+	/* check the acl to see whether to believe the X-Forwarded-For header */
+	http->acl_checklist = clientAclChecklistCreate(
+			Config.accessList.followXFF, http);
+	aclNBCheck(http->acl_checklist, clientFollowXForwardedForDone, http);
+    } else {
+	/* nothing left to follow */
+	debug(33, 5) ("clientFollowXForwardedForNext: nothing more to do\n");
+	clientFollowXForwardedForDone(-1, http);
+    }
+}
+
+static void
+clientFollowXForwardedForDone(int answer, void *data)
+{
+    clientHttpRequest *http = data;
+    request_t *request = http->request;
+    /*
+     * answer should be be ACCESS_ALLOWED or ACCESS_DENIED if we are
+     * called as a result of ACL checks, or -1 if we are called when
+     * there's nothing left to do.
+     */
+    if (answer == ACCESS_ALLOWED) {
+	/*
+	 * The IP address currently in request->indirect_client_addr
+	 * is trusted to use X-Forwarded-For.  Remove the last
+	 * comma-delimited element from x_forwarded_for_iterator and use
+	 * it to to replace indirect_client_addr, then repeat the cycle.
+	 */
+	const char *p;
+	const char *asciiaddr;
+	int l;
+	struct in_addr addr;
+	debug(33, 5) ("clientFollowXForwardedForDone: indirect_client_addr=%s is trusted\n",
+			inet_ntoa(request->indirect_client_addr));
+	p = strBuf(request->x_forwarded_for_iterator);
+	l = strLen(request->x_forwarded_for_iterator);
+
+	/*
+	 * XXX x_forwarded_for_iterator should really be a list of
+	 * IP addresses, but it's a String instead.  We have to
+	 * walk backwards through the String, biting off the last
+	 * comma-delimited part each time.  As long as the data is in
+	 * a String, we should probably implement and use a variant of
+	 * strListGetItem() that walks backwards instead of forwards
+	 * through a comma-separated list.  But we don't even do that;
+	 * we just do the work in-line here.
+	 */
+	/* skip trailing space and commas */
+	while (l > 0 && (p[l-1] == ',' || xisspace(p[l-1])))
+	    l--;
+	strCut(request->x_forwarded_for_iterator, l);
+	/* look for start of last item in list */
+	while (l > 0 && ! (p[l-1] == ',' || xisspace(p[l-1])))
+	    l--;
+	asciiaddr = p+l;
+	if (inet_aton(asciiaddr, &addr) == 0) {
+	    /* the address is not well formed; do not use it */
+	    debug(33, 3) ("clientFollowXForwardedForDone: malformed address '%s'\n",
+			    asciiaddr);
+	    goto done;
+	}
+	debug(33, 3) ("clientFollowXForwardedForDone: changing indirect_client_addr from %s to '%s'\n",
+		    inet_ntoa(request->indirect_client_addr),
+		    asciiaddr);
+	request->indirect_client_addr = addr;
+	strCut(request->x_forwarded_for_iterator, l);
+	if (! Config.onoff.acl_uses_indirect_client) {
+	    /*
+	     * If acl_uses_indirect_client is off, then it's impossible
+	     * to follow more than one level of X-Forwarded-For.
+	     */
+	    goto done;
+	}
+	clientFollowXForwardedForNext(http);
+	return;
+    } else if (answer == ACCESS_DENIED) {
+	debug(33, 5) ("clientFollowXForwardedForDone: indirect_client_addr=%s not trusted\n",
+			inet_ntoa(request->indirect_client_addr));
+    } else {
+	debug(33, 5) ("clientFollowXForwardedForDone: indirect_client_addr=%s nothing more to do\n",
+			inet_ntoa(request->indirect_client_addr));
+    }
+done:
+    /* clean up, and pass control to clientAccessCheck */
+    debug(33, 6) ("clientFollowXForwardedForDone: cleanup\n");
+    if (Config.onoff.log_uses_indirect_client) {
+	/*
+	 * Ensure that the access log shows the indirect client
+	 * instead of the direct client.
+	 */
+	ConnStateData *conn = http->conn;
+	conn->log_addr = request->indirect_client_addr;
+	conn->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
+	debug(33, 3) ("clientFollowXForwardedForDone: setting log_addr=%s\n",
+			inet_ntoa(conn->log_addr));
+    }
+    stringClean(&request->x_forwarded_for_iterator);
+    request->flags.done_follow_x_forwarded_for = 1;
+    http->acl_checklist = NULL; /* XXX do we need to aclChecklistFree() ? */
+    clientAccessCheck(http);
+}
+#endif /* FOLLOW_X_FORWARDED_FOR */
+
 void
 clientAccessCheck(void *data)
 {
     clientHttpRequest *http = data;
+#if FOLLOW_X_FORWARDED_FOR
+    if (! http->request->flags.done_follow_x_forwarded_for
+        && httpHeaderHas(&http->request->header, HDR_X_FORWARDED_FOR))
+    {
+	/*
+	 * There's an X-ForwardedFor header and we haven't yet tried
+	 * to follow it to find the indirect_client_addr.  Follow it now.
+	 * clientFollowXForwardedForDone() will eventually pass control
+	 * back to us.
+	 *
+	 * XXX perhaps our caller should have called
+	 *     clientFollowXForwardedForStart instead.  Then we wouldn't
+	 *     need to do this little dance transferring control over
+	 *     there and then back here, and we wouldn't need the
+	 *     done_follow_x_forwarded_for flag.
+	 */
+	clientFollowXForwardedForStart(data);
+	return;
+    }
+#endif /* FOLLOW_X_FORWARDED_FOR */
     if (checkAccelOnly(http)) {
 	/* deny proxy requests in accel_only mode */
 	debug(33, 1) ("clientAccessCheck: proxy request denied in accel_only mode\n");
@@ -355,6 +530,9 @@
 	new_request->http_ver = old_request->http_ver;
 	httpHeaderAppend(&new_request->header, &old_request->header);
 	new_request->client_addr = old_request->client_addr;
+#if FOLLOW_X_FORWARDED_FOR
+	new_request->indirect_client_addr = old_request->indirect_client_addr;
+#endif /* FOLLOW_X_FORWARDED_FOR */
 	new_request->my_addr = old_request->my_addr;
 	new_request->my_port = old_request->my_port;
 	new_request->flags.redirected = 1;
@@ -823,6 +1001,7 @@
     clientHttpRequest **H;
     ConnStateData *conn = http->conn;
     StoreEntry *e;
+	 String s;
     request_t *request = http->request;
     MemObject *mem = NULL;
     debug(33, 3) ("httpRequestFree: %s\n", storeUrl(http->entry));
@@ -846,6 +1025,14 @@
 	    http->al.http.content_type = strBuf(mem->reply->content_type);
 	}
 	http->al.cache.caddr = conn->log_addr;
+#ifdef DANSGUARDIAN_IP_FORWARD
+	if (httpHeaderHas(&request->header, HDR_X_FORWARDED_FOR)) {
+		s = httpHeaderGetList(&request->header, HDR_X_FORWARDED_FOR);
+		if (! inet_pton (AF_INET,strBuf(s),&http->al.cache.caddr))
+			http->al.cache.caddr = conn->log_addr;
+	}
+#endif
+
 	http->al.cache.size = http->out.size;
 	http->al.cache.code = http->log_type;
 	http->al.cache.msec = tvSubMsec(http->start, current_time);
@@ -3120,6 +3307,9 @@
 	    safe_free(http->log_uri);
 	    http->log_uri = xstrdup(urlCanonicalClean(request));
 	    request->client_addr = conn->peer.sin_addr;
+#if FOLLOW_X_FORWARDED_FOR
+	    request->indirect_client_addr = request->client_addr;
+#endif /* FOLLOW_X_FORWARDED_FOR */
 	    request->my_addr = conn->me.sin_addr;
 	    request->my_port = ntohs(conn->me.sin_port);
 	    request->http_ver = http->http_ver;

--- src/delay_pools.c	2005-09-28 16:16:34.667037053 +0200
+++ src/delay_pools.c	2005-09-28 16:17:28.338582747 +0200
@@ -318,6 +318,11 @@
     r = http->request;
 
     memset(&ch, '\0', sizeof(ch));
+#if FOLLOW_X_FORWARDED_FOR
+    if (Config.onoff.delay_pool_uses_indirect_client) {
+  ch.src_addr = r->indirect_client_addr;
+    } else
+#endif /* FOLLOW_X_FORWARDED_FOR */
     ch.src_addr = r->client_addr;
     ch.my_addr = r->my_addr;
     ch.my_port = r->my_port;

--- src/structs.h	2005-09-28 16:18:20.156285179 +0200
+++ src/structs.h	2005-09-28 15:59:57.408699579 +0200
@@ -604,6 +604,11 @@
 	int vary_ignore_expire;
 	int pipeline_prefetch;
 	int request_entities;
+#if FOLLOW_X_FORWARDED_FOR
+	int acl_uses_indirect_client;
+	int delay_pool_uses_indirect_client;
+	int log_uses_indirect_client;
+#endif /* FOLLOW_X_FORWARDED_FOR */
 	int detect_broken_server_pconns;
 	int balance_on_multiple_ip;
 	int relaxed_header_parser;
@@ -630,6 +635,9 @@
 	acl_access *reply;
 	acl_address *outgoing_address;
 	acl_tos *outgoing_tos;
+#if FOLLOW_X_FORWARDED_FOR
+	acl_access *followXFF;
+#endif /* FOLLOW_X_FORWARDED_FOR */
     } accessList;
     acl_deny_info_list *denyInfoList;
     struct _authConfig {
@@ -1619,6 +1627,11 @@
     unsigned int internal:1;
     unsigned int body_sent:1;
     unsigned int reset_tcp:1;
+#if FOLLOW_X_FORWARDED_FOR
+    /* XXX this flag could be eliminated;
+     * see comments in clientAccessCheck */
+    unsigned int done_follow_x_forwarded_for;
+#endif /* FOLLOW_X_FORWARDED_FOR */
 };
 
 struct _link_list {
@@ -1665,6 +1678,9 @@
     int max_forwards;
     /* these in_addr's could probably be sockaddr_in's */
     struct in_addr client_addr;
+#if FOLLOW_X_FORWARDED_FOR
+    struct in_addr indirect_client_addr; /* after following X-Forwarded-For */
+#endif /* FOLLOW_X_FORWARDED_FOR */
     struct in_addr my_addr;
     unsigned short my_port;
     HttpHeader header;
@@ -1676,6 +1692,11 @@
     const char *vary_headers;	/* Used when varying entities are detected. Changes how the store key is calculated */
     BODY_HANDLER *body_reader;
     void *body_reader_data;
+#if FOLLOW_X_FORWARDED_FOR
+	/* XXX a list of IP addresses would be a better data structure
+	* than this String */
+	String x_forwarded_for_iterator;
+#endif /* FOLLOW_X_FORWARDED_FOR */
 };
 
 struct _cachemgr_passwd {

