Updated patch. I believe this covers everything so far, including the
16-bit alignment and segmented TCP packet issues.

Amos

=== modified file 'doc/release-notes/release-3.5.sgml'
--- doc/release-notes/release-3.5.sgml  2014-08-11 16:09:06 +0000
+++ doc/release-notes/release-3.5.sgml  2014-08-12 14:53:59 +0000
@@ -27,40 +27,41 @@
 <p>
 Although this release is deemed good enough for use in many setups, please 
note the existence of 
 <url 
url="http://bugs.squid-cache.org/buglist.cgi?query_format=advanced&amp;product=Squid&amp;bug_status=UNCONFIRMED&amp;bug_status=NEW&amp;bug_status=ASSIGNED&amp;bug_status=REOPENED&amp;version=3.5";
 name="open bugs against Squid-3.5">.
 
 <sect1>Changes since earlier releases of Squid-3.5
 <p>
 The 3.5 change history can be <url 
url="http://www.squid-cache.org/Versions/v3/3.5/changesets/"; name="viewed 
here">.
 
 
 <sect>Major new features since Squid-3.4
 <p>Squid 3.5 represents a new feature release above 3.4.
 
 <p>The most important of these new features are:
 <itemize>
        <item>Support libecap v1.0
        <item>Authentication helper query extensions
        <item>Support named services
        <item>Upgraded squidclient tool
        <item>Helper support for concurrency channels
        <item>Native FTP Relay
+       <item>Receive PROXY protocol, Versions 1 & 2
 </itemize>
 
 Most user-facing changes are reflected in squid.conf (see below).
 
 
 <sect1>Support libecap v1.0
 <p>Details at <url url="http://wiki.squid-cache.org/Features/BLAH";>.
 
 <p>The new libecap version allows Squid to better check the version of
   the eCAP adapter being loaded as well as the version of the eCAP library
   being used.
 
 <p>Squid-3.5 can support eCAP adapters built with libecap v1.0,
    but no longer supports adapters built with earlier libecap versions
    due to API changes.
 
 
 <sect1>Authentication helper query extensions
 <p>Details at <url url="http://www.squid-cache.org/Doc/config/auth_param/";>.
 
@@ -188,72 +189,111 @@
 
 <p>FTP Relay highlights:</p>
 
 <itemize>
     <item>Added ftp_port directive telling Squid to relay native FTP commands.
     <item>Active and passive FTP support on the user-facing side; require
          passive connections to come from the control connection source IP
          address.
     <item>IPv6 support (EPSV and, on the user-facing side, EPRT).
     <item>Intelligent adaptation of relayed FTP FEAT responses.
     <item>Relaying of multi-line FTP control responses using various formats.
     <item>Support relaying of FTP MLSD and MLST commands (RFC 3659).
     <item>Several Microsoft FTP server compatibility features.
     <item>ICAP/eCAP support (at individual FTP command/response level).
     <item>Optional "current FTP directory" tracking with the assistance of
          injected (by Squid) PWD commands (cannot be 100% reliable due to
          symbolic links and such, but is helpful in some common use cases).
     <item>No caching support -- no reliable Request URIs for that (see above).
 </itemize>
 
+<sect1>Receive PROXY protocol, Versions 1 & 2
+<p>More info at <url 
url="http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt";>
+
+<p>PROXY protocol provides a simple way for proxies and tunnels of any kind to
+   relay the original client source details without having to alter or 
understand
+   the protocol being relayed on the connection.
+
+<p>Squid currently supports receiving HTTP traffic from a client proxy using 
this protocol.
+   An http_port which has been configured to receive this protocol may only be 
used to
+   receive traffic from client software sending in this protocol.
+   HTTP traffic without the PROXY header is not accepted on such a port.
+
+<p>The <em>accel</em> and <em>intercept</em> options are still used to 
identify the
+   traffic syntax being delivered by the client proxy.
+
+<p>Squid can be configured by adding an <em>http_port</em>
+   with the <em>require-proxy-header</em> mode flag. The 
<em>proxy_protocol_access</em>
+   must also be configured with <em>src</em> ACLs to whitelist proxies which 
are
+   trusted to send correct client details.
+
+<p>Forward-proxy traffic from a client proxy:
+<verbatim>
+ http_port 3128 require-proxy-header
+ proxy_protocol_access allow localhost
+</verbatim>
+
+<p>Intercepted traffic from a client proxy or tunnel:
+<verbatim>
+ http_port 3128 intercept require-proxy-header
+ proxy_protocol_access allow localhost
+</verbatim>
+
+<p><em>Known Issue:</em>
+   Use of <em>require-proxy-header</em> on <em>https_port</em> is not 
supported.
+
 
 <sect>Changes to squid.conf since Squid-3.4
 <p>
 There have been changes to Squid's configuration file since Squid-3.4.
 
 <p>Squid supports reading configuration option parameters from external
    files using the syntax <em>parameters("/path/filename")</em>. For example:
 <verb>
     acl whitelist dstdomain parameters("/etc/squid/whitelist.txt")
 </verb>
 
 <p>The squid.conf macro ${service_name} is added to provide the service name
    of the process parsing the config.
 
 <p>There have also been changes to individual directives in the config file.
 
 This section gives a thorough account of those changes in three categories:
 
 <itemize>
        <item><ref id="newtags" name="New tags">
        <item><ref id="modifiedtags" name="Changes to existing tags">
        <item><ref id="removedtags" name="Removed tags">
 </itemize>
 <p>
 
 <sect1>New tags<label id="newtags">
 <p>
 <descrip>
        <tag>collapsed_forwarding</tag>
        <p>Ported from Squid-2 with no configuration or visible behaviour 
changes.
            Collapsing of requests is performed across SMP workers.
 
+       <tag>proxy_protocol_access</tag>
+       <p>New directive to control which clients are permitted to open PROXY
+          protocol connections on a port flagged with 
<em>require-proxy-header</em>.
+
        <tag>send_hit</tag>
        <p>New configuration directive to enable/disable sending cached content
           based on ACL selection. ACL can be based on client request or cached
           response details.
 
        <tag>sslproxy_session_cache_size</tag>
        <p>New directive which sets the cache size to use for TLS/SSL sessions 
cache.
 
        <tag>sslproxy_session_ttl</tag>
        <p>New directive to specify the time in seconds the TLS/SSL session is 
valid.
 
        <tag>store_id_extras</tag>
        <p>New directive to send additional lookup parameters to the configured
           Store-ID helper program. It takes a string which may contain 
logformat %macros.
        <p>The Store-ID helper input format is now:
        <verb>
          [channel-ID] url [extras]
        </verb>
        <p>The default value for extras is: "%&gt;a/%>A %un %>rm myip=%la 
myport=%lp"
 
@@ -318,40 +358,42 @@
        <p>These connections differ from HTTP persistent connections in that 
they
           have not been used for HTTP messaging (and may never be). They may be
           turned into persistent connections after their first use subject to 
the
           same keep-alive critera any HTTP connection is checked for.
 
        <tag>forward_max_tries</tag>
        <p>Default value increased to <em>25 destinations</em> to allow better
           contact and IPv4 failover with domains using long lists of IPv6
           addresses.
 
        <tag>ftp_epsv</tag>
        <p>Converted into an Access List with allow/deny value driven by ACLs
           using Squid standard first line wins matching basis.
        <p>The old values of <em>on</em> and <em>off</em> imply <em>allow 
all</em>
            and <em>deny all</em> respectively and are now deprecated.
           Do not combine use of on/off values with ACL configuration.
 
        <tag>http_port</tag>
        <p><em>protocol=</em> option altered to accept protocol version details.
           Currently supported values are: HTTP, HTTP/1.1, HTTPS, HTTPS/1.1
+       <p><em>New option <em>require-proxy-header</em> to mark ports receiving 
PROXY
+          protocol version 1 or 2 traffic.
 
        <tag>https_port</tag>
        <p><em>protocol=</em> option altered to accept protocol version details.
           Currently supported values are: HTTP, HTTP/1.1, HTTPS, HTTPS/1.1
 
        <tag>logformat</tag>
        <p>New format code <em>%credentials</em> to log the client credentials
           token.
        <p>New format code <em>%tS</em> to log transaction start time in
           "seconds.milliseconds" format, similar to the existing access.log
           "current time" field (%ts.%03tu) which logs the corresponding
           transaction finish time.
 
 </descrip>
 
 <sect1>Removed tags<label id="removedtags">
 <p>
 <descrip>
        <tag>cache_dir</tag>
        <p><em>COSS</em> storage type is formally replaced by Rock storage type.

=== modified file 'doc/rfc/1-index.txt'
--- doc/rfc/1-index.txt 2014-06-09 01:38:06 +0000
+++ doc/rfc/1-index.txt 2014-07-25 09:18:15 +0000
@@ -1,40 +1,43 @@
 draft-ietf-radext-digest-auth-06.txt
        RADIUS Extension for Digest Authentication
        A proposed extension to Radius for Digest authentication
        via RADIUS servers.
 
 draft-cooper-webi-wpad-00.txt
 draft-ietf-svrloc-wpad-template-00.txt
        Web Proxy Auto-Discovery Protocol -- WPAD
        documents how MSIE and several other browsers automatically
        find their proxy settings from DHCP and/or DNS
 
 draft-forster-wrec-wccp-v1-00.txt
        WCCP 1.0
 
 draft-wilson-wccp-v2-12-oct-2001.txt
        WCCP 2.0
 
 draft-vinod-carp-v1-03.txt
        Microsoft CARP peering algorithm
 
+proxy-protocol.txt
+       The PROXY protocol, Versions 1 & 2
+
 rfc0959.txt
        FTP
 
 rfc1035.txt
        DNS for IPv4
 
 rfc1157.txt
        A Simple Network Management Protocol (SNMP)
        SNMP v1 Specification. SNMP v2 is documented in several RFCs,
        namely, 1902,1903,1904,1905,1906,1907.
 
 rfc1738.txt
        Uniform Resource Locators (URL)
        (updated by RFC 3986, but not obsoleted)
 
 rfc1902.txt
        Structure of Managament Information (SMI) for SNMPv2
        Management information is viewed as a collection of managed objects,
        the Management Information Base (MIB). MIB modules are
        written using an adapted subset of OSI's Abstract Syntax

=== modified file 'src/Makefile.am'
--- src/Makefile.am     2014-08-07 17:31:05 +0000
+++ src/Makefile.am     2014-08-12 14:52:27 +0000
@@ -1618,40 +1618,41 @@
        parser/libsquid-parser.la \
        base/libbase.la \
        libsquid.la \
        ip/libip.la \
        fs/libfs.la \
        comm/libcomm.la \
        eui/libeui.la \
        icmp/libicmp.la icmp/libicmp-core.la \
        log/liblog.la \
        format/libformat.la \
        $(REPL_OBJS) \
        $(DISK_LIBS) \
        $(DISK_OS_LIBS) \
        $(ADAPTATION_LIBS) \
        $(ESI_LIBS) \
        $(SSL_LIBS) \
        anyp/libanyp.la \
        ipc/libipc.la \
        mgr/libmgr.la \
        $(SNMP_LIBS) \
+       parser/libsquid-parser.la \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
        $(top_builddir)/lib/libmiscutil.la \
        $(NETTLELIB) \
        $(REGEXLIB) \
        $(SQUID_CPPUNIT_LIBS) \
        $(SQUID_CPPUNIT_LA) \
        $(SSLLIB) \
        $(KRB5LIBS) \
        $(COMPAT_LIB) \
        $(XTRA_LIBS)
 tests_testCacheManager_LDFLAGS = $(LIBADD_DL)
 tests_testCacheManager_DEPENDENCIES = \
        $(REPL_OBJS) \
        $(SQUID_CPPUNIT_LA)
 
 tests_testDiskIO_SOURCES = \
        CacheDigest.h \
        tests/stub_CacheDigest.cc \
        cbdata.cc \
@@ -2048,40 +2049,41 @@
        http/libsquid-http.la \
        ftp/libftp.la \
        ident/libident.la \
        acl/libacls.la \
        acl/libstate.la \
        acl/libapi.la \
        parser/libsquid-parser.la \
        base/libbase.la \
        libsquid.la \
        ip/libip.la \
        fs/libfs.la \
        anyp/libanyp.la \
        icmp/libicmp.la icmp/libicmp-core.la \
        comm/libcomm.la \
        log/liblog.la \
        format/libformat.la \
        $(REPL_OBJS) \
        $(ADAPTATION_LIBS) \
        $(ESI_LIBS) \
        $(SSL_LIBS) \
+       parser/libsquid-parser.la \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
        $(top_builddir)/lib/libmiscutil.la \
        $(DISK_LIBS) \
        $(DISK_OS_LIBS) \
        ipc/libipc.la \
        mgr/libmgr.la \
        $(SNMP_LIBS) \
        $(NETTLELIB) \
        $(REGEXLIB) \
        $(SQUID_CPPUNIT_LIBS) \
        $(SQUID_CPPUNIT_LA) \
        $(SSLLIB) \
        $(KRB5LIBS) \
        $(COMPAT_LIB) \
        $(XTRA_LIBS)
 tests_testEvent_LDFLAGS = $(LIBADD_DL)
 tests_testEvent_DEPENDENCIES = \
        $(REPL_OBJS) \
        $(SQUID_CPPUNIT_LA)
@@ -2300,40 +2302,41 @@
        http/libsquid-http.la \
        ftp/libftp.la \
        ident/libident.la \
        acl/libacls.la \
        acl/libstate.la \
        acl/libapi.la \
        parser/libsquid-parser.la \
        base/libbase.la \
        libsquid.la \
        ip/libip.la \
        fs/libfs.la \
        anyp/libanyp.la \
        icmp/libicmp.la icmp/libicmp-core.la \
        comm/libcomm.la \
        log/liblog.la \
        format/libformat.la \
        $(REPL_OBJS) \
        $(ADAPTATION_LIBS) \
        $(ESI_LIBS) \
        $(SSL_LIBS) \
+       parser/libsquid-parser.la \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
        $(top_builddir)/lib/libmiscutil.la \
        $(DISK_LIBS) \
        $(DISK_OS_LIBS) \
        ipc/libipc.la \
        mgr/libmgr.la \
        $(SNMP_LIBS) \
        $(NETTLELIB) \
        $(REGEXLIB) \
        $(SQUID_CPPUNIT_LIBS) \
        $(SQUID_CPPUNIT_LA) \
        $(SSLLIB) \
        $(KRB5LIBS) \
        $(COMPAT_LIB) \
        $(XTRA_LIBS)
 tests_testEventLoop_LDFLAGS = $(LIBADD_DL)
 tests_testEventLoop_DEPENDENCIES = \
        $(REPL_OBJS) \
        $(SQUID_CPPUNIT_LA)
@@ -2550,40 +2553,41 @@
        acl/libapi.la \
        parser/libsquid-parser.la \
        libsquid.la \
        ip/libip.la \
        fs/libfs.la \
        anyp/libanyp.la \
        icmp/libicmp.la icmp/libicmp-core.la \
        comm/libcomm.la \
        log/liblog.la \
        format/libformat.la \
        $(REPL_OBJS) \
        $(DISK_LIBS) \
        $(DISK_OS_LIBS) \
        $(ADAPTATION_LIBS) \
        $(ESI_LIBS) \
        $(SSL_LIBS) \
        ipc/libipc.la \
        base/libbase.la \
        mgr/libmgr.la \
        $(SNMP_LIBS) \
+       parser/libsquid-parser.la \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
        $(top_builddir)/lib/libmiscutil.la \
        $(NETTLELIB) \
        $(REGEXLIB) \
        $(SQUID_CPPUNIT_LIBS) \
        $(SQUID_CPPUNIT_LA) \
        $(SSLLIB) \
        $(KRB5LIBS) \
        $(COMPAT_LIB) \
        $(XTRA_LIBS)
 tests_test_http_range_LDFLAGS = $(LIBADD_DL)
 tests_test_http_range_DEPENDENCIES = \
        $(SQUID_CPPUNIT_LA)
 
 tests_testHttpParser_SOURCES = \
        Debug.h \
        HttpParser.cc \
        HttpParser.h \
        MemBuf.cc \
@@ -2842,40 +2846,41 @@
        acl/libstate.la \
        acl/libapi.la \
        parser/libsquid-parser.la \
        libsquid.la \
        ip/libip.la \
        fs/libfs.la \
        $(SSL_LIBS) \
        ipc/libipc.la \
        base/libbase.la \
        mgr/libmgr.la \
        anyp/libanyp.la \
        $(SNMP_LIBS) \
        icmp/libicmp.la icmp/libicmp-core.la \
        comm/libcomm.la \
        log/liblog.la \
        format/libformat.la \
        http/libsquid-http.la \
        $(REPL_OBJS) \
        $(ADAPTATION_LIBS) \
        $(ESI_LIBS) \
+       parser/libsquid-parser.la \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
        $(top_builddir)/lib/libmiscutil.la \
        $(DISK_OS_LIBS) \
        $(NETTLELIB) \
        $(REGEXLIB) \
        $(SQUID_CPPUNIT_LIBS) \
        $(SQUID_CPPUNIT_LA) \
        $(SSLLIB) \
        $(KRB5LIBS) \
        $(COMPAT_LIB) \
        $(XTRA_LIBS)
 tests_testHttpRequest_LDFLAGS = $(LIBADD_DL)
 tests_testHttpRequest_DEPENDENCIES = \
        $(REPL_OBJS) \
        $(SQUID_CPPUNIT_LA)
 
 ## why so many sources? well httpHeaderTools requites ACLChecklist & friends.
 ## first line - what we are testing.
 tests_testStore_SOURCES= \
@@ -3688,40 +3693,41 @@
        acl/libstate.la \
        acl/libapi.la \
        parser/libsquid-parser.la \
        base/libbase.la \
        libsquid.la \
        ip/libip.la \
        fs/libfs.la \
        $(SSL_LIBS) \
        ipc/libipc.la \
        mgr/libmgr.la \
        $(SNMP_LIBS) \
        icmp/libicmp.la icmp/libicmp-core.la \
        comm/libcomm.la \
        log/liblog.la \
        $(DISK_OS_LIBS) \
        format/libformat.la \
        $(REGEXLIB) \
        $(REPL_OBJS) \
        $(ADAPTATION_LIBS) \
        $(ESI_LIBS) \
+       parser/libsquid-parser.la \
        $(top_builddir)/lib/libmisccontainers.la \
        $(top_builddir)/lib/libmiscencoding.la \
        $(top_builddir)/lib/libmiscutil.la \
        $(NETTLELIB) \
        $(COMPAT_LIB) \
        $(SQUID_CPPUNIT_LIBS) \
        $(SQUID_CPPUNIT_LA) \
        $(SSLLIB) \
        $(KRB5LIBS) \
        $(COMPAT_LIB) \
        $(XTRA_LIBS)
 tests_testURL_LDFLAGS = $(LIBADD_DL)
 tests_testURL_DEPENDENCIES = \
        $(REPL_OBJS) \
        $(SQUID_CPPUNIT_LA)
 
 tests_testSBuf_SOURCES= \
        tests/testSBuf.h \
        tests/testSBuf.cc \
        tests/testMain.cc \

=== modified file 'src/SquidConfig.h'
--- src/SquidConfig.h   2014-08-11 16:09:06 +0000
+++ src/SquidConfig.h   2014-08-12 14:52:27 +0000
@@ -372,40 +372,43 @@
 #if USE_HTTP_VIOLATIONS
         acl_access *brokenPosts;
 #endif
         acl_access *redirector;
         acl_access *store_id;
         acl_access *reply;
         AclAddress *outgoing_address;
 #if USE_HTCP
 
         acl_access *htcp;
         acl_access *htcp_clr;
 #endif
 
 #if USE_OPENSSL
         acl_access *ssl_bump;
 #endif
 #if FOLLOW_X_FORWARDED_FOR
         acl_access *followXFF;
 #endif /* FOLLOW_X_FORWARDED_FOR */
 
+        /// acceptible PROXY protocol clients
+        acl_access *proxyProtocol;
+
         /// spoof_client_ip squid.conf acl.
         /// nil unless configured
         acl_access* spoof_client_ip;
 
         acl_access *ftp_epsv;
     } accessList;
     AclDenyInfoList *denyInfoList;
 
     struct {
         size_t list_width;
         int list_wrap;
         char *anon_user;
         int passive;
         int epsv_all;
         int epsv;
         int eprt;
         int sanitycheck;
         int telnet;
     } Ftp;
     RefreshPattern *Refresh;

=== modified file 'src/anyp/TrafficMode.h'
--- src/anyp/TrafficMode.h      2013-02-04 09:47:50 +0000
+++ src/anyp/TrafficMode.h      2014-07-25 06:12:42 +0000
@@ -8,40 +8,50 @@
  * Set of 'mode' flags defining types of trafic which can be received.
  *
  * Use to determine the processing steps which need to be applied
  * to this traffic under any special circumstances which may apply.
  */
 class TrafficMode
 {
 public:
     TrafficMode() : accelSurrogate(false), natIntercept(false), 
tproxyIntercept(false), tunnelSslBumping(false) {}
     TrafficMode(const TrafficMode &rhs) { operator =(rhs); }
     TrafficMode &operator =(const TrafficMode &rhs) { memcpy(this, &rhs, 
sizeof(TrafficMode)); return *this; }
 
     /** marks HTTP accelerator (reverse/surrogate proxy) traffic
      *
      * Indicating the following are required:
      *  - URL translation from relative to absolute form
      *  - restriction to origin peer relay recommended
      */
     bool accelSurrogate;
 
+    /** marks ports receiving PROXY protocol traffic
+     *
+     * Indicating the following are required:
+     *  - PROXY protocol magic header
+     *  - src/dst IP retrieved from magic PROXY header
+     *  - indirect client IP trust verification is mandatory
+     *  - TLS is not supported
+     */
+    bool proxySurrogate;
+
     /** marks NAT intercepted traffic
      *
      * Indicating the following are required:
      *  - NAT lookups
      *  - URL translation from relative to absolute form
      *  - Same-Origin verification is mandatory
      *  - destination pinning is recommended
      *  - authentication prohibited
      */
     bool natIntercept;
 
     /** marks TPROXY intercepted traffic
      *
      * Indicating the following are required:
      *  - src/dst IP inversion must be performed
      *  - client IP should be spoofed if possible
      *  - URL translation from relative to absolute form
      *  - Same-Origin verification is mandatory
      *  - destination pinning is recommended
      *  - authentication prohibited

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc     2014-08-10 23:18:33 +0000
+++ src/cache_cf.cc     2014-08-12 15:44:59 +0000
@@ -3603,45 +3603,57 @@
     } else if (strcmp(token, "transparent") == 0 || strcmp(token, "intercept") 
== 0) {
         if (s->flags.accelSurrogate || s->flags.tproxyIntercept) {
             debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": Intercept 
mode requires its own interception port. It cannot be shared with other 
modes.");
             self_destruct();
         }
         s->flags.natIntercept = true;
         Ip::Interceptor.StartInterception();
         /* Log information regarding the port modes under interception. */
         debugs(3, DBG_IMPORTANT, "Starting Authentication on port " << s->s);
         debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s 
<< " (interception enabled)");
     } else if (strcmp(token, "tproxy") == 0) {
         if (s->flags.natIntercept || s->flags.accelSurrogate) {
             debugs(3,DBG_CRITICAL, "FATAL: " << cfg_directive << ": TPROXY 
option requires its own interception port. It cannot be shared with other 
modes.");
             self_destruct();
         }
         s->flags.tproxyIntercept = true;
         Ip::Interceptor.StartTransparency();
         /* Log information regarding the port modes under transparency. */
         debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s 
<< " (TPROXY enabled)");
 
+        if (s->flags.proxySurrogate) {
+            debugs(3, DBG_IMPORTANT, "Disabling TPROXY Spoofing on port " << 
s->s << " (require-proxy-header enabled)");
+        }
+
         if (!Ip::Interceptor.ProbeForTproxy(s->s)) {
             debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": TPROXY 
support in the system does not work.");
             self_destruct();
         }
 
+    } else if (strcmp(token, "require-proxy-header") == 0) {
+        s->flags.proxySurrogate = true;
+        if (s->flags.tproxyIntercept) {
+            // receiving is still permitted, so we do not unset the TPROXY flag
+            // spoofing access control override takes care of the spoof 
disable later
+            debugs(3, DBG_IMPORTANT, "Disabling TPROXY Spoofing on port " << 
s->s << " (require-proxy-header enabled)");
+        }
+
     } else if (strncmp(token, "defaultsite=", 12) == 0) {
         if (!s->flags.accelSurrogate) {
             debugs(3, DBG_CRITICAL, "FATAL: " << cfg_directive << ": 
defaultsite option requires Acceleration mode flag.");
             self_destruct();
         }
         safe_free(s->defaultsite);
         s->defaultsite = xstrdup(token + 12);
     } else if (strcmp(token, "vhost") == 0) {
         if (!s->flags.accelSurrogate) {
             debugs(3, DBG_CRITICAL, "WARNING: " << cfg_directive << ": vhost 
option is deprecated. Use 'accel' mode flag instead.");
         }
         s->flags.accelSurrogate = true;
         s->vhost = true;
     } else if (strcmp(token, "no-vhost") == 0) {
         if (!s->flags.accelSurrogate) {
             debugs(3, DBG_IMPORTANT, "ERROR: " << cfg_directive << ": no-vhost 
option requires Acceleration mode flag.");
         }
         s->vhost = false;
     } else if (strcmp(token, "vport") == 0) {
         if (!s->flags.accelSurrogate) {
@@ -3809,90 +3821,102 @@
         self_destruct();
         return;
     }
 
     char *token = ConfigParser::NextToken();
 
     if (!token) {
         self_destruct();
         return;
     }
 
     AnyP::PortCfgPointer s = new AnyP::PortCfg();
     s->transport = parsePortProtocol(protoName); // default; protocol=... 
overwrites
     parsePortSpecification(s, token);
 
     /* parse options ... */
     while ((token = ConfigParser::NextToken())) {
         parse_port_option(s, token);
     }
 
-#if USE_OPENSSL
     if (s->transport.protocol == AnyP::PROTO_HTTPS) {
+#if USE_OPENSSL
         /* ssl-bump on https_port configuration requires either tproxy or 
intercept, and vice versa */
         const bool hijacked = s->flags.isIntercepted();
         if (s->flags.tunnelSslBumping && !hijacked) {
             debugs(3, DBG_CRITICAL, "FATAL: ssl-bump on https_port requires 
tproxy/intercept which is missing.");
             self_destruct();
         }
         if (hijacked && !s->flags.tunnelSslBumping) {
             debugs(3, DBG_CRITICAL, "FATAL: tproxy/intercept on https_port 
requires ssl-bump which is missing.");
             self_destruct();
         }
+#endif
+        if (s->flags.proxySurrogate) {
+            debugs(3,DBG_CRITICAL, "FATAL: https_port: require-proxy-header 
option is not supported on HTTPS ports.");
+            self_destruct();
+        }
     } else if (protoName.cmp("FTP") == 0) {
         /* ftp_port does not support ssl-bump */
         if (s->flags.tunnelSslBumping) {
             debugs(3, DBG_CRITICAL, "FATAL: ssl-bump is not supported for 
ftp_port.");
             self_destruct();
         }
+        if (s->flags.proxySurrogate) {
+            // Passive FTP data channel does not work without deep protocol 
inspection in the frontend.
+            debugs(3,DBG_CRITICAL, "FATAL: require-proxy-header option is not 
supported on ftp_port.");
+            self_destruct();
+        }
     }
-#endif
 
     if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && s->s.isAnyAddr()) {
         // clone the port options from *s to *(s->next)
         s->next = s->clone();
         s->next->s.setIPv4();
         debugs(3, 3, AnyP::UriScheme(s->transport.protocol).c_str() << "_port: 
clone wildcard address for split-stack: " << s->s << " and " << s->next->s);
     }
 
     while (*head != NULL)
         head = &((*head)->next);
 
     *head = s;
 }
 
 static void
 dump_generic_port(StoreEntry * e, const char *n, const AnyP::PortCfgPointer &s)
 {
     char buf[MAX_IPSTRLEN];
 
     storeAppendPrintf(e, "%s %s",
                       n,
                       s->s.toUrl(buf,MAX_IPSTRLEN));
 
     // MODES and specific sub-options.
     if (s->flags.natIntercept)
         storeAppendPrintf(e, " intercept");
 
     else if (s->flags.tproxyIntercept)
         storeAppendPrintf(e, " tproxy");
 
+    else if (s->flags.proxySurrogate)
+        storeAppendPrintf(e, " require-proxy-header");
+
     else if (s->flags.accelSurrogate) {
         storeAppendPrintf(e, " accel");
 
         if (s->vhost)
             storeAppendPrintf(e, " vhost");
 
         if (s->vport < 0)
             storeAppendPrintf(e, " vport");
         else if (s->vport > 0)
             storeAppendPrintf(e, " vport=%d", s->vport);
 
         if (s->defaultsite)
             storeAppendPrintf(e, " defaultsite=%s", s->defaultsite);
 
         // TODO: compare against prefix of 'n' instead of assuming http_port
         if (s->transport.protocol != AnyP::PROTO_HTTP)
             storeAppendPrintf(e, " protocol=%s", 
AnyP::UriScheme(s->transport.protocol).c_str());
 
         if (s->allow_direct)
             storeAppendPrintf(e, " allow-direct");

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre     2014-08-11 16:09:06 +0000
+++ src/cf.data.pre     2014-08-12 14:52:27 +0000
@@ -1134,79 +1134,127 @@
 acl localnet src 172.16.0.0/12 # RFC1918 possible internal network
 acl localnet src 192.168.0.0/16        # RFC1918 possible internal network
 acl localnet src fc00::/7       # RFC 4193 local private network range
 acl localnet src fe80::/10      # RFC 4291 link-local (directly plugged) 
machines
 
 acl SSL_ports port 443
 acl Safe_ports port 80         # http
 acl Safe_ports port 21         # ftp
 acl Safe_ports port 443                # https
 acl Safe_ports port 70         # gopher
 acl Safe_ports port 210                # wais
 acl Safe_ports port 1025-65535 # unregistered ports
 acl Safe_ports port 280                # http-mgmt
 acl Safe_ports port 488                # gss-http
 acl Safe_ports port 591                # filemaker
 acl Safe_ports port 777                # multiling http
 acl CONNECT method CONNECT
 NOCOMMENT_END
 DOC_END
 
+NAME: proxy_protocol_access
+TYPE: acl_access
+LOC: Config.accessList.proxyProtocol
+DEFAULT: none
+DEFAULT_DOC: all TCP connections to ports with require-proxy-header will be 
denied
+DOC_START
+       Determine which client proxies can be trusted to provide correct
+       information regarding real client IP address using PROXY protocol.
+
+       Requests may pass through a chain of several other proxies
+       before reaching us. The original source details may by sent in:
+               * HTTP message Forwarded header, or
+               * HTTP message X-Forwarded-For header, or
+               * PROXY protocol connection header.
+
+       This directive is solely for validating new PROXY protocol
+       connections received from a port flagged with require-proxy-header.
+       It is checked only once after TCP connection setup.
+
+       A deny match results in TCP connection closure.
+
+       An allow match is required for Squid to permit the corresponding
+       TCP connection, before Squid even looks for HTTP request headers.
+       If there is an allow match, Squid starts using PROXY header information
+       to determine the source address of the connection for all future ACL
+       checks, logging, etc.
+
+       SECURITY CONSIDERATIONS:
+
+               Any host from which we accept client IP details can place
+               incorrect information in the relevant 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.
+
+       This clause only supports fast acl types.
+       See http://wiki.squid-cache.org/SquidFaq/SquidAcl for details.
+DOC_END
+
 NAME: follow_x_forwarded_for
 TYPE: acl_access
 IFDEF: FOLLOW_X_FORWARDED_FOR
 LOC: Config.accessList.followXFF
 DEFAULT_IF_NONE: deny all
 DEFAULT_DOC: X-Forwarded-For header will be ignored.
 DOC_START
-       Allowing or Denying the X-Forwarded-For header to be followed to
-       find the original source of a request.
+       Determine which client proxies can be trusted to provide correct
+       information regarding real client IP address.
 
        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.
+       before reaching us. The original source details may by sent in:
+               * HTTP message Forwarded header, or
+               * HTTP message X-Forwarded-For header, or
+               * PROXY protocol connection header.
+
+       PROXY protocol connections are controlled by the proxy_protocol_access
+       directive which is checked before this.
 
        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, 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. For the purpose of ACL used in the
-       follow_x_forwarded_for directive the src ACL type always matches
-       the address we are testing and srcdomain matches its rDNS.
+       directive, then we trust the information it provides regarding
+       the IP of the client it received from (if any).
+
+       For the purpose of ACLs used in this directive the src ACL type always
+       matches the address we are testing and srcdomain matches its rDNS.
+
+       On each HTTP request Squid checks for X-Forwarded-For header fields.
+       If found the header values are iterated in reverse order and an allow
+       match is required for Squid to continue on to the next value.
+       The verification ends when a value receives a deny match, cannot be
+       tested, or there are no more values to test.
+       NOTE: Squid does not yet follow the Forwarded HTTP header.
 
        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, ICAP, delay
        pools and logging, depending on the acl_uses_indirect_client,
        icap_uses_indirect_client, delay_pool_uses_indirect_client, 
        log_uses_indirect_client and tproxy_uses_indirect_client options.
 
        This clause only supports fast acl types.
        See http://wiki.squid-cache.org/SquidFaq/SquidAcl for details.
 
        SECURITY CONSIDERATIONS:
 
-               Any host for which we follow the X-Forwarded-For header
-               can place incorrect information in the header, and Squid
+               Any host from which we accept client IP details can place
+               incorrect information in the relevant 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
@@ -1761,40 +1809,45 @@
                            always      disable always PMTU discovery.
 
                        In many setups of transparently intercepting proxies
                        Path-MTU discovery can not work on traffic towards the
                        clients. This is the case when the intercepting device
                        does not fully track connections and fails to forward
                        ICMP must fragment messages to the cache server. If you
                        have such setup and experience that certain clients
                        sporadically hang or never complete requests set
                        disable-pmtu-discovery option to 'transparent'.
 
           name=        Specifies a internal name for the port. Defaults to
                        the port specification (port or addr:port)
 
           tcpkeepalive[=idle,interval,timeout]
                        Enable TCP keepalive probes of idle connections.
                        In seconds; idle is the initial time before TCP starts
                        probing the connection, interval how often to probe, and
                        timeout the time before giving up.
 
+          require-proxy-header
+                       Require PROXY protocol version 1 or 2 connections.
+                       The proxy_protocol_access is required to whitelist
+                       downstream proxies which can be trusted.
+
        If you run Squid on a dual-homed machine with an internal
        and an external interface we recommend you to specify the
        internal address:port in http_port. This way Squid will only be
        visible on the internal address.
 
 NOCOMMENT_START
 
 # Squid normally listens to port 3128
 http_port @DEFAULT_HTTP_PORT@
 NOCOMMENT_END
 DOC_END
 
 NAME: https_port
 IFDEF: USE_OPENSSL
 TYPE: PortCfg
 DEFAULT: none
 LOC: HttpsPortList
 DOC_START
        Usage:  [ip:]port cert=certificate.pem [key=key.pem] [mode] [options...]
 

=== modified file 'src/client_side.cc'
--- src/client_side.cc  2014-08-11 16:09:06 +0000
+++ src/client_side.cc  2014-08-19 07:29:11 +0000
@@ -102,40 +102,41 @@
 #include "fd.h"
 #include "fde.h"
 #include "fqdncache.h"
 #include "FwdState.h"
 #include "globals.h"
 #include "http.h"
 #include "HttpHdrContRange.h"
 #include "HttpHeaderTools.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 #include "ident/Config.h"
 #include "ident/Ident.h"
 #include "internal.h"
 #include "ipc/FdNotes.h"
 #include "ipc/StartListening.h"
 #include "log/access_log.h"
 #include "Mem.h"
 #include "MemBuf.h"
 #include "MemObject.h"
 #include "mime_header.h"
+#include "parser/Tokenizer.h"
 #include "profiler/Profiler.h"
 #include "rfc1738.h"
 #include "servers/forward.h"
 #include "SquidConfig.h"
 #include "SquidTime.h"
 #include "StatCounters.h"
 #include "StatHist.h"
 #include "Store.h"
 #include "TimeOrTag.h"
 #include "tools.h"
 #include "URL.h"
 
 #if USE_AUTH
 #include "auth/UserRequest.h"
 #endif
 #if USE_DELAY_POOLS
 #include "ClientInfo.h"
 #endif
 #if USE_OPENSSL
 #include "ssl/context_storage.h"
@@ -2282,40 +2283,42 @@
 
 #if THIS_VIOLATES_HTTP_SPECS_ON_URL_TRANSFORMATION
 
     if ((t = strchr(url, '#')))        /* remove HTML anchors */
         *t = '\0';
 
 #endif
 
     debugs(33,5, HERE << "repare absolute URL from " <<
            (csd->transparent()?"intercept":(csd->port->flags.accelSurrogate ? 
"accel":"")));
     /* Rewrite the URL in transparent or accelerator mode */
     /* NP: there are several cases to traverse here:
      *  - standard mode (forward proxy)
      *  - transparent mode (TPROXY)
      *  - transparent mode with failures
      *  - intercept mode (NAT)
      *  - intercept mode with failures
      *  - accelerator mode (reverse proxy)
      *  - internal URL
      *  - mixed combos of the above with internal URL
+     *  - remote interception with PROXY protocol
+     *  - remote reverse-proxy with PROXY protocol
      */
     if (csd->transparent()) {
         /* intercept or transparent mode, properly working with no failures */
         prepareTransparentURL(csd, http, url, req_hdr);
 
     } else if (internalCheck(url)) {
         /* internal URL mode */
         /* prepend our name & port */
         http->uri = xstrdup(internalLocalUri(NULL, url));
         // We just re-wrote the URL. Must replace the Host: header.
         //  But have not parsed there yet!! flag for local-only handling.
         http->flags.internal = true;
 
     } else if (csd->port->flags.accelSurrogate || csd->switchedToHttps()) {
         /* accelerator mode */
         prepareAcceleratedURL(csd, http, url, req_hdr);
     }
 
     if (!http->uri) {
         /* No special rewrites have been applied above, use the
@@ -2657,41 +2660,42 @@
     request->flags.accelerated = http->flags.accel;
     request->flags.sslBumped=conn->switchedToHttps();
     request->flags.ignoreCc = conn->port->ignore_cc;
     // TODO: decouple http->flags.accel from request->flags.sslBumped
     request->flags.noDirect = (request->flags.accelerated && 
!request->flags.sslBumped) ?
                               !conn->port->allow_direct : 0;
 #if USE_AUTH
     if (request->flags.sslBumped) {
         if (conn->getAuth() != NULL)
             request->auth_user_request = conn->getAuth();
     }
 #endif
 
     /** \par
      * If transparent or interception mode is working clone the transparent 
and interception flags
      * from the port settings to the request.
      */
     if (http->clientConnection != NULL) {
         request->flags.intercepted = ((http->clientConnection->flags & 
COMM_INTERCEPTION) != 0);
         request->flags.interceptTproxy = ((http->clientConnection->flags & 
COMM_TRANSPARENT) != 0 ) ;
-        if (request->flags.interceptTproxy) {
+        static const bool proxyProtocolPort = (conn->port != NULL) ? 
conn->port->flags.proxySurrogate : false;
+        if (request->flags.interceptTproxy && !proxyProtocolPort) {
             if (Config.accessList.spoof_client_ip) {
                 ACLFilledChecklist *checklist = 
clientAclChecklistCreate(Config.accessList.spoof_client_ip, http);
                 request->flags.spoofClientIp = (checklist->fastCheck() == 
ACCESS_ALLOWED);
                 delete checklist;
             } else
                 request->flags.spoofClientIp = true;
         } else
             request->flags.spoofClientIp = false;
     }
 
     if (internalCheck(request->urlpath.termedBuf())) {
         if (internalHostnameIs(request->GetHost()) && request->port == 
getMyPort()) {
             debugs(33, 2, "internal URL found: " << request->url.getScheme() 
<< "://" << request->GetHost() <<
                    ':' << request->port);
             http->flags.internal = true;
         } else if (Config.onoff.global_internal_static && 
internalStaticCheck(request->urlpath.termedBuf())) {
             debugs(33, 2, "internal URL found: " << request->url.getScheme() 
<< "://" << request->GetHost() <<
                    ':' << request->port << " (global_internal_static on)");
             request->SetHost(internalHostname());
             request->port = getMyPort();
@@ -2878,64 +2882,334 @@
 bool
 ConnStateData::concurrentRequestQueueFilled() const
 {
     const int existingRequestCount = getConcurrentRequestCount();
 
     // default to the configured pipeline size.
     // add 1 because the head of pipeline is counted in concurrent requests 
and not prefetch queue
     const int concurrentRequestLimit = pipelinePrefetchMax() + 1;
 
     // when queue filled already we cant add more.
     if (existingRequestCount >= concurrentRequestLimit) {
         debugs(33, 3, clientConnection << " max concurrent requests reached (" 
<< concurrentRequestLimit << ")");
         debugs(33, 5, clientConnection << " deferring new request until one is 
done");
         return true;
     }
 
     return false;
 }
 
 /**
+ * Perform proxy_protocol_access ACL tests on the client which
+ * connected to PROXY protocol port to see if we trust the
+ * sender enough to accept their PROXY header claim.
+ */
+bool
+ConnStateData::proxyProtocolValidateClient()
+{
+    if (!Config.accessList.proxyProtocol)
+        return proxyProtocolError("PROXY client not permitted by default ACL");
+
+    ACLFilledChecklist ch(Config.accessList.proxyProtocol, NULL, 
clientConnection->rfc931);
+    ch.src_addr = clientConnection->remote;
+    ch.my_addr = clientConnection->local;
+    ch.conn(this);
+
+    if (ch.fastCheck() != ACCESS_ALLOWED)
+        return proxyProtocolError("PROXY client not permitted by ACLs");
+
+    return true;
+}
+
+/**
+ * Perform cleanup on PROXY protocol errors.
+ * If header parsing hits a fatal error terminate the connection,
+ * otherwise wait for more data.
+ */
+bool
+ConnStateData::proxyProtocolError(const char *msg)
+{
+    if (msg) {
+        // This is important to know, but maybe not so much that flooding the 
log is okay.
+#if QUIET_PROXY_PROTOCOL
+        // display the first of every 32 occurances at level 1, the others at 
level 2.
+        static uint8_t hide = 0;
+        debugs(33, (hide++ % 32 == 0 ? DBG_IMPORTANT : 2), msg << " from " << 
clientConnection);
+#else
+        debugs(33, DBG_IMPORTANT, msg << " from " << clientConnection);
+#endif
+        mustStop(msg);
+    }
+    return false;
+}
+
+/// magic octet prefix for PROXY protocol version 1
+static const SBuf Proxy1p0magic("PROXY ", 6);
+
+/// magic octet prefix for PROXY protocol version 2
+static const SBuf 
Proxy2p0magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12);
+
+/**
+ * Test the connection read buffer for PROXY protocol header.
+ * Version 1 and 2 header currently supported.
+ */
+bool
+ConnStateData::parseProxyProtocolHeader()
+{
+    // http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
+
+    // detect and parse PROXY/2.0 protocol header
+    if (in.buf.startsWith(Proxy2p0magic))
+        return parseProxy2p0();
+
+    // detect and parse PROXY/1.0 protocol header
+    if (in.buf.startsWith(Proxy1p0magic))
+         return parseProxy1p0();
+
+    // detect and terminate other protocols
+    if (in.buf.length() >= Proxy2p0magic.length()) {
+        // PROXY/1.0 magic is shorter, so we know that
+        // the input does not start with any PROXY magic
+        return proxyProtocolError("PROXY protocol error: invalid header");
+    }
+
+    // TODO: detect short non-magic prefixes earlier to avoid
+    // waiting for more data which may never come
+
+    // not enough bytes to parse yet.
+    return false;
+}
+
+/// parse the PROXY/1.0 protocol header from the connection read buffer
+bool
+ConnStateData::parseProxy1p0()
+{
+    ::Parser::Tokenizer tok(in.buf);
+    tok.skip(Proxy1p0magic);
+
+    // skip to first LF (assumes it is part of CRLF)
+    static const CharacterSet lineContent = 
CharacterSet::LF.complement("non-LF");
+    SBuf line;
+    if (tok.prefix(line, lineContent, 107-Proxy1p0magic.length())) {
+        if (tok.skip('\n')) {
+            // found valid header
+            in.buf = tok.remaining();
+            needProxyProtocolHeader_ = false;
+            // reset the tokenizer to work on found line only.
+            tok.reset(line);
+        } else
+            return false; // no LF yet
+
+    } else // protocol error only if there are more than 107 bytes prefix 
header
+        return proxyProtocolError(in.buf.length() > 107? "PROXY/1.0 error: 
missing CRLF" : NULL);
+
+    static const SBuf unknown("UNKNOWN"), tcpName("TCP");
+    if (tok.skip(tcpName)) {
+
+        // skip TCP/IP version number
+        static const CharacterSet tcpVersions("TCP-version","46");
+        if(!tok.skipOne(tcpVersions))
+            return proxyProtocolError("PROXY/1.0 error: missing TCP version");
+
+        // skip SP after protocol version
+        if (!tok.skip(' '))
+            return proxyProtocolError("PROXY/1.0 error: missing SP");
+
+        SBuf ipa, ipb;
+        int64_t porta, portb;
+        static const CharacterSet ipChars = CharacterSet("IP Address",".:") + 
CharacterSet::HEXDIG;
+
+        // parse:  src-IP SP dst-IP SP src-port SP dst-port CR
+        // leave the LF until later.
+        const bool correct = tok.prefix(ipa, ipChars) && tok.skip(' ') &&
+                             tok.prefix(ipb, ipChars) && tok.skip(' ') &&
+                             tok.int64(porta) && tok.skip(' ') &&
+                             tok.int64(portb) &&
+                             tok.skip('\r');
+        if (!correct)
+            return proxyProtocolError("PROXY/1.0 error: invalid syntax");
+
+        // parse IP and port strings
+        Ip::Address originalClient, originalDest;
+
+        if (!originalClient.GetHostByName(ipa.c_str()))
+            return proxyProtocolError("PROXY/1.0 error: invalid src-IP 
address");
+
+        if (!originalDest.GetHostByName(ipb.c_str()))
+            return proxyProtocolError("PROXY/1.0 error: invalid dst-IP 
address");
+
+        if (porta > 0 && porta <= 0xFFFF) // max uint16_t
+            originalClient.port(static_cast<uint16_t>(porta));
+        else
+            return proxyProtocolError("PROXY/1.0 error: invalid src port");
+
+        if (portb > 0 && portb <= 0xFFFF) // max uint16_t
+            originalDest.port(static_cast<uint16_t>(portb));
+        else
+            return proxyProtocolError("PROXY/1.0 error: invalid dst port");
+
+        // we have original client and destination details now
+        // replace the client connection values
+        debugs(33, 5, "PROXY/1.0 protocol on connection " << clientConnection);
+        clientConnection->local = originalDest;
+        clientConnection->remote = originalClient;
+        clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY 
spoofing of this new IP.
+        debugs(33, 5, "PROXY/1.0 upgrade: " << clientConnection);
+
+        // repeat fetch ensuring the new client FQDN can be logged
+        if (Config.onoff.log_fqdn)
+            fqdncache_gethostbyaddr(clientConnection->remote, 
FQDN_LOOKUP_IF_MISS);
+
+        return true;
+
+    } else if (tok.skip(unknown)) {
+        // found valid but unusable header
+        return true;
+
+    } else
+        return proxyProtocolError("PROXY/1.0 error: invalid protocol family");
+
+    return false;
+}
+
+/// parse the PROXY/2.0 protocol header from the connection read buffer
+bool
+ConnStateData::parseProxy2p0()
+{
+    static const SBuf::size_type prefixLen = Proxy2p0magic.length();
+    if (in.buf.length() < prefixLen + 4)
+        return false; // need more bytes
+
+    if ((in.buf[prefixLen] & 0xF0) != 0x20) // version == 2 is mandatory
+        return proxyProtocolError("PROXY/2.0 error: invalid version");
+
+    const char command = (in.buf[prefixLen] & 0x0F);
+    if ((command & 0xFE) != 0x00) // values other than 0x0-0x1 are invalid
+        return proxyProtocolError("PROXY/2.0 error: invalid command");
+
+    const char family = (in.buf[prefixLen+1] & 0xF0) >>4;
+    if (family > 0x3) // values other than 0x0-0x3 are invalid
+        return proxyProtocolError("PROXY/2.0 error: invalid family");
+
+    const char proto = (in.buf[prefixLen+1] & 0x0F);
+    if (proto > 0x2) // values other than 0x0-0x2 are invalid
+        return proxyProtocolError("PROXY/2.0 error: invalid protocol type");
+
+    const char *clen = in.buf.rawContent() + prefixLen + 2;
+    uint16_t len;
+    memcpy(&len, clen, sizeof(len));
+    len = ntohs(len);
+
+    if (in.buf.length() < prefixLen + 4 + len)
+        return false; // need more bytes
+
+    in.buf.consume(prefixLen + 4); // 4 being the extra bytes
+    const SBuf extra = in.buf.consume(len);
+    needProxyProtocolHeader_ = false; // found successfully
+
+    // LOCAL connections do nothing with the extras
+    if (command == 0x00/* LOCAL*/)
+        return true;
+
+    union pax {
+        struct {        /* for TCP/UDP over IPv4, len = 12 */
+            struct in_addr src_addr;
+            struct in_addr dst_addr;
+            uint16_t src_port;
+            uint16_t dst_port;
+        } ipv4_addr;
+        struct {        /* for TCP/UDP over IPv6, len = 36 */
+             struct in6_addr src_addr;
+             struct in6_addr dst_addr;
+             uint16_t src_port;
+             uint16_t dst_port;
+        } ipv6_addr;
+#if NOT_SUPPORTED
+        struct {        /* for AF_UNIX sockets, len = 216 */
+             uint8_t src_addr[108];
+             uint8_t dst_addr[108];
+        } unix_addr;
+#endif
+    };
+
+    pax ipu;
+    memcpy(&ipu, extra.rawContent(), sizeof(pax));
+
+    // replace the client connection values
+    debugs(33, 5, "PROXY/2.0 protocol on connection " << clientConnection);
+    switch (family)
+    {
+    case 0x1: // IPv4
+        clientConnection->local = ipu.ipv4_addr.dst_addr;
+        clientConnection->local.port(ntohs(ipu.ipv4_addr.dst_port));
+        clientConnection->remote = ipu.ipv4_addr.src_addr;
+        clientConnection->remote.port(ntohs(ipu.ipv4_addr.src_port));
+        clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY 
spoofing of this new IP.
+        break;
+    case 0x2: // IPv6
+        clientConnection->local = ipu.ipv6_addr.dst_addr;
+        clientConnection->local.port(ntohs(ipu.ipv6_addr.dst_port));
+        clientConnection->remote = ipu.ipv6_addr.src_addr;
+        clientConnection->remote.port(ntohs(ipu.ipv6_addr.src_port));
+        clientConnection->flags ^= COMM_TRANSPARENT; // prevent TPROXY 
spoofing of this new IP.
+        break;
+    default: // do nothing
+        break;
+    }
+    debugs(33, 5, "PROXY/2.0 upgrade: " << clientConnection);
+
+    // repeat fetch ensuring the new client FQDN can be logged
+    if (Config.onoff.log_fqdn)
+        fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);
+
+    return true;
+}
+
+/**
  * Attempt to parse one or more requests from the input buffer.
  * If a request is successfully parsed, even if the next request
  * is only partially parsed, it will return TRUE.
  */
 bool
 ConnStateData::clientParseRequests()
 {
     bool parsed_req = false;
 
     debugs(33, 5, HERE << clientConnection << ": attempting to parse");
 
     // Loop while we have read bytes that are not needed for producing the body
     // On errors, bodyPipe may become nil, but readMore will be cleared
     while (!in.buf.isEmpty() && !bodyPipe && flags.readMore) {
         connStripBufferWhitespace(this);
 
         /* Don't try to parse if the buffer is empty */
         if (in.buf.isEmpty())
             break;
 
         /* Limit the number of concurrent requests */
         if (concurrentRequestQueueFilled())
             break;
 
+        // try to parse the PROXY protocol header magic bytes
+        if (needProxyProtocolHeader_ && !parseProxyProtocolHeader())
+            break;
+
         Http::ProtocolVersion http_ver;
         ClientSocketContext *context = parseOneRequest(http_ver);
 
         /* partial or incomplete request */
         if (!context) {
             // TODO: why parseHttpRequest can just return parseHttpRequestAbort
             // (which becomes context) but checkHeaderLimits cannot?
             checkHeaderLimits();
             break;
         }
 
         /* status -1 or 1 */
         if (context) {
             debugs(33, 5, HERE << clientConnection << ": parsed a request");
             AsyncCall::Pointer timeoutCall = commCbCall(5, 4, 
"clientLifetimeTimeout",
                                              
CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http));
             commSetConnTimeout(clientConnection, Config.Timeout.lifetime, 
timeoutCall);
 
             processParsedRequest(context, http_ver);
 
@@ -3235,88 +3509,93 @@
 #if USE_OPENSSL
         sslBumpMode(Ssl::bumpEnd),
         switchedToHttps_(false),
         sslServerBump(NULL),
 #endif
         stoppedSending_(NULL),
         stoppedReceiving_(NULL)
 {
     pinning.host = NULL;
     pinning.port = -1;
     pinning.pinned = false;
     pinning.auth = false;
     pinning.zeroReply = false;
     pinning.peer = NULL;
 
     // store the details required for creating more MasterXaction objects as 
new requests come in
     clientConnection = xact->tcpClient;
     port = xact->squidPort;
     log_addr = xact->tcpClient->remote;
     log_addr.applyMask(Config.Addrs.client_netmask);
-
     flags.readMore = true; // kids may overwrite
 }
 
 void
 ConnStateData::start()
 {
     BodyProducer::start();
     HttpControlMsgSink::start();
 
-    // ensure a buffer is present for this connection
-    in.maybeMakeSpaceAvailable();
-
     if (port->disable_pmtu_discovery != DISABLE_PMTU_OFF &&
             (transparent() || port->disable_pmtu_discovery == 
DISABLE_PMTU_ALWAYS)) {
 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
         int i = IP_PMTUDISC_DONT;
         if (setsockopt(clientConnection->fd, SOL_IP, IP_MTU_DISCOVER, &i, 
sizeof(i)) < 0)
             debugs(33, 2, "WARNING: Path MTU discovery disabling failed on " 
<< clientConnection << " : " << xstrerror());
 #else
         static bool reported = false;
 
         if (!reported) {
             debugs(33, DBG_IMPORTANT, "NOTICE: Path MTU discovery disabling is 
not supported on your platform.");
             reported = true;
         }
 #endif
     }
 
+    // ensure a buffer is present for this connection
+    in.maybeMakeSpaceAvailable();
+
     typedef CommCbMemFunT<ConnStateData, CommCloseCbParams> Dialer;
     AsyncCall::Pointer call = JobCallback(33, 5, Dialer, this, 
ConnStateData::connStateClosed);
     comm_add_close_handler(clientConnection->fd, call);
 
     if (Config.onoff.log_fqdn)
         fqdncache_gethostbyaddr(clientConnection->remote, FQDN_LOOKUP_IF_MISS);
 
 #if USE_IDENT
     if (Ident::TheConfig.identLookup) {
         ACLFilledChecklist identChecklist(Ident::TheConfig.identLookup, NULL, 
NULL);
         identChecklist.src_addr = clientConnection->remote;
         identChecklist.my_addr = clientConnection->local;
         if (identChecklist.fastCheck() == ACCESS_ALLOWED)
             Ident::Start(clientConnection, clientIdentDone, this);
     }
 #endif
 
     clientdbEstablished(clientConnection->remote, 1);
 
+    needProxyProtocolHeader_ = port->flags.proxySurrogate;
+    if (needProxyProtocolHeader_) {
+        if (!proxyProtocolValidateClient()) // will close the connection on 
failure
+            return;
+    }
+
 #if USE_DELAY_POOLS
     fd_table[clientConnection->fd].clientInfo = NULL;
 
     if (Config.onoff.client_db) {
         /* it was said several times that client write limiter does not work 
if client_db is disabled */
 
         ClientDelayPools& pools(Config.ClientDelay.pools);
         ACLFilledChecklist ch(NULL, NULL, NULL);
 
         // TODO: we check early to limit error response bandwith but we
         // should recheck when we can honor delay_pool_uses_indirect
         // TODO: we should also pass the port details for myportname here.
         ch.src_addr = clientConnection->remote;
         ch.my_addr = clientConnection->local;
 
         for (unsigned int pool = 0; pool < pools.size(); ++pool) {
 
             /* pools require explicit 'allow' to assign a client into them */
             if (pools[pool].access) {
                 ch.accessList = pools[pool].access;
@@ -3499,41 +3778,41 @@
     debugs(83, 3, "clientNegotiateSSL: FD " << fd << " negotiated cipher " <<
            SSL_get_cipher(ssl));
 
     client_cert = SSL_get_peer_certificate(ssl);
 
     if (client_cert != NULL) {
         debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
                " client certificate: subject: " <<
                X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
 
         debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
                " client certificate: issuer: " <<
                X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
 
         X509_free(client_cert);
     } else {
         debugs(83, 5, "clientNegotiateSSL: FD " << fd <<
                " has no certificate.");
     }
 
-    conn->readSomeData();
+    AsyncJob::Start(conn);
 }
 
 /**
  * If SSL_CTX is given, starts reading the SSL handshake.
  * Otherwise, calls switchToHttps to generate a dynamic SSL_CTX.
  */
 static void
 httpsEstablish(ConnStateData *connState,  SSL_CTX *sslContext, Ssl::BumpMode 
bumpMode)
 {
     SSL *ssl = NULL;
     assert(connState);
     const Comm::ConnectionPointer &details = connState->clientConnection;
 
     if (sslContext && !(ssl = httpsCreate(details, sslContext)))
         return;
 
     typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
     AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
                                      connState, ConnStateData::requestTimeout);
     commSetConnTimeout(details, Config.Timeout.request, timeoutCall);

=== modified file 'src/client_side.h'
--- src/client_side.h   2014-08-10 02:28:33 +0000
+++ src/client_side.h   2014-08-12 14:55:00 +0000
@@ -420,40 +420,50 @@
     virtual ClientSocketContext *parseOneRequest(Http::ProtocolVersion &ver) = 
0;
 
     /// start processing a freshly parsed request
     virtual void processParsedRequest(ClientSocketContext *context, const 
Http::ProtocolVersion &ver) = 0;
 
     /// returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
     virtual int pipelinePrefetchMax() const;
 
     /// timeout to use when waiting for the next request
     virtual time_t idleTimeout() const = 0;
 
     BodyPipe::Pointer bodyPipe; ///< set when we are reading request body
 
 private:
     int connFinishedWithConn(int size);
     void clientAfterReadingRequests();
     bool concurrentRequestQueueFilled() const;
 
     void pinNewConnection(const Comm::ConnectionPointer &pinServer, 
HttpRequest *request, CachePeer *aPeer, bool auth);
 
+    /* PROXY protocol functionality */
+    bool proxyProtocolValidateClient();
+    bool parseProxyProtocolHeader();
+    bool parseProxy1p0();
+    bool parseProxy2p0();
+    bool proxyProtocolError(const char *reason);
+
+    /// whether PROXY protocol header is still expected
+    bool needProxyProtocolHeader_;
+
 #if USE_AUTH
     /// some user details that can be used to perform authentication on this 
connection
     Auth::UserRequest::Pointer auth_;
 #endif
 
 #if USE_OPENSSL
     bool switchedToHttps_;
     /// The SSL server host name appears in CONNECT request or the server ip 
address for the intercepted requests
     String sslConnectHostOrIp; ///< The SSL server host name as passed in the 
CONNECT request
     String sslCommonName; ///< CN name for SSL certificate generation
     String sslBumpCertKey; ///< Key to use to store/retrieve generated 
certificate
 
     /// HTTPS server cert. fetching state for bump-ssl-server-first
     Ssl::ServerBump *sslServerBump;
     Ssl::CertSignAlgorithm signAlgorithm; ///< The signing algorithm to use
 #endif
 
     /// the reason why we no longer write the response or nil
     const char *stoppedSending_;
     /// the reason why we no longer read the request or nil

Reply via email to