=== modified file 'doc/release-notes/release-3.5.sgml' --- doc/release-notes/release-3.5.sgml 2014-06-04 15:30:16 +0000 +++ doc/release-notes/release-3.5.sgml 2014-06-22 04:45:09 +0000 @@ -26,40 +26,41 @@ Known issues

Although this release is deemed good enough for use in many setups, please note the existence of . Changes since earlier releases of Squid-3.5

The 3.5 change history can be . Major new features since Squid-3.4

Squid 3.5 represents a new feature release above 3.4.

The most important of these new features are: Support libecap v1.0 Authentication helper query extensions Support named services Upgraded squidclient tool Helper support for concurrency channels + Support PROXY protocol Most user-facing changes are reflected in squid.conf (see below). Support libecap v1.0

Details at .

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.

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. Authentication helper query extensions

Details at . @@ -146,71 +147,99 @@ The default is to use X.509 certificate encryption instead.

When performing TLS/SSL server certificates are always verified, the results shown at debug level 3. The encrypted type is displayed at debug level 2 and the connection is used to send and receive the messages regardless of verification results. Helper support for concurrency channels

Helper concurrency greatly reduces the communication lag between Squid and its helpers allowing faster transaction speeds even on sequential helpers.

The Digest authentication, Store-ID, and URL-rewrite helpers packaged with Squid have been updated to support concurrency channels. They will auto-detect the channel-ID field and will produce the appropriate response format. With these helpers concurrency may now be set to 0 or any higher number as desired. +Support PROXY protocol +

More info at + +

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. + +

Squid currently supports receiving version 1 or 2 of the protocol. + A port which has been configured to receive this protocol may only be used to + receive traffic from client software sending in this protocol. + Regular forward-proxy HTTP traffic is not accepted. + +

Squid can be configured by adding an http_port or https_port + with the proxy-surrogate mode flag. The proxy_forwarded_access + must also be configured with src ACLs to whitelist proxies which are + trusted to send correct client details. + +

+ + http_port 3128 proxy-surrogate + proxy_forwarded_access allow localhost + + + Changes to squid.conf since Squid-3.4

There have been changes to Squid's configuration file since Squid-3.4.

Squid supports reading configuration option parameters from external files using the syntax parameters("/path/filename"). For example: acl whitelist dstdomain parameters("/etc/squid/whitelist.txt")

The squid.conf macro ${service_name} is added to provide the service name of the process parsing the config.

There have also been changes to individual directives in the config file. This section gives a thorough account of those changes in three categories:

New tags

collapsed_forwarding

Ported from Squid-2 with no configuration or visible behaviour changes. Collapsing of requests is performed across SMP workers. + proxy_forwarded_access +

Renamed from follow_x_forwarded_for and extended to control more + ways for locating the indirect (original) client IP details. + send_hit

New configuration directive to enable/disable sending cached content based on ACL selection. ACL can be based on client request or cached response details. sslproxy_session_cache_size

New directive which sets the cache size to use for TLS/SSL sessions cache. sslproxy_session_ttl

New directive to specify the time in seconds the TLS/SSL session is valid. store_id_extras

New directive to send additional lookup parameters to the configured Store-ID helper program. It takes a string which may contain logformat %macros.

The Store-ID helper input format is now: [channel-ID] url [extras]

The default value for extras is: "%>a/%>A %un %>rm myip=%la myport=%lp" @@ -294,40 +323,43 @@ Removed tags

cache_dir

COSS storage type is formally replaced by Rock storage type. cache_dns_program

DNS external helper interface has been removed. It was no longer able to provide high performance service and the internal DNS client library with multicast DNS cover all modern use-cases. cache_peer

idle= replaced by standby=.

NOTE that standby connections are started earlier and available in more circumstances than squid-2 idle connections were. They are also spread over all IPs of the peer. dns_children

DNS external helper interface has been removed. + follow_x_forwarded_for +

Renamed proxy_forwarded_access and extended. + Changes to ./configure options since Squid-3.4

There have been some changes to Squid's build configuration since Squid-3.4. This section gives an account of those changes in three categories: New options

There are no new ./configure options in Squid-3.5. === 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-06-21 12:08:24 +0000 @@ -1,40 +1,45 @@ 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 + Documents Proxy Protocol 1.5, for communicating original client IP + details between consenting proxies and servers even when + transparent interception is taking place. + 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/anyp/TrafficMode.h' --- src/anyp/TrafficMode.h 2013-02-04 09:47:50 +0000 +++ src/anyp/TrafficMode.h 2014-06-21 13:06:04 +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 + * - reverse-proxy traffic prohibited + * - intercepted traffic prohibited + */ + 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-06-02 06:23:12 +0000 +++ src/cache_cf.cc 2014-06-21 13:45:59 +0000 @@ -3580,71 +3580,78 @@ debugs(3, 3, portType << "_port: Listen on Host/IP: " << host << " --> " << s->s); } else if ( s->s.GetHostByName(host) ) { /* check/parse for FQDN */ /* dont use ipcache */ s->defaultsite = xstrdup(host); s->s.port(port); if (!Ip::EnableIpv6) s->s.setIPv4(); debugs(3, 3, portType << "_port: found Listen as Host " << s->defaultsite << " on IP: " << s->s); } else { debugs(3, DBG_CRITICAL, "FATAL: " << portType << "_port: failed to resolve Host/IP: " << host); self_destruct(); } } static void parse_port_option(AnyP::PortCfg * s, char *token) { /* modes first */ if (strcmp(token, "accel") == 0) { - if (s->flags.isIntercepted()) { + if (s->flags.isIntercepted() || s->flags.proxySurrogate) { debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: Accelerator mode requires its own port. It cannot be shared with other modes."); self_destruct(); } s->flags.accelSurrogate = true; s->vhost = true; } else if (strcmp(token, "transparent") == 0 || strcmp(token, "intercept") == 0) { - if (s->flags.accelSurrogate || s->flags.tproxyIntercept) { + if (s->flags.accelSurrogate || s->flags.tproxyIntercept || s->flags.proxySurrogate) { debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: 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) { + if (s->flags.natIntercept || s->flags.accelSurrogate || s->flags.proxySurrogate) { debugs(3,DBG_CRITICAL, "FATAL: http(s)_port: 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 (!Ip::Interceptor.ProbeForTproxy(s->s)) { debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: TPROXY support in the system does not work."); self_destruct(); } + } else if (strcmp(token, "proxy-surrogate") == 0) { + if (s->flags.natIntercept || s->flags.accelSurrogate || s->flags.tproxyIntercept) { + debugs(3,DBG_CRITICAL, "FATAL: http(s)_port: proxy-surrogate option requires its own port. It cannot be shared with other modes."); + self_destruct(); + } + s->flags.proxySurrogate = true; + } else if (strncmp(token, "defaultsite=", 12) == 0) { if (!s->flags.accelSurrogate) { debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: 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: http(s)_port: 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: http(s)_port: no-vhost option requires Acceleration mode flag."); } s->vhost = false; } else if (strcmp(token, "vport") == 0) { if (!s->flags.accelSurrogate) { @@ -3853,40 +3860,43 @@ *head = cbdataReference(s); } static void dump_generic_port(StoreEntry * e, const char *n, const AnyP::PortCfg * 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, " proxy-surrogate"); + 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-06-16 16:21:19 +0000 +++ src/cf.data.pre 2014-06-22 04:46:00 +0000 @@ -1073,49 +1073,57 @@ 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: follow_x_forwarded_for +NAME: proxy_forwarded_access 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. +DEFAULT_DOC: indirect client IP will not be accepted. 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. + + The original source details can be relayed in: + HTTP message Forwarded header, or + HTTP message X-Forwarded-For header, or + PROXY protocol connection header. + + Allowing or Denying the X-Forwarded-For or Forwarded headers to + be followed to find the original source of a request. Or permitting + a client proxy to connect using PROXY protocol. 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, 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. 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, @@ -1514,40 +1522,45 @@ probably want to listen on port 80 also, or instead. The -a command line option may be used to specify additional port(s) where Squid listens for proxy request. Such ports will be plain proxy ports with no options. You may specify multiple socket addresses on multiple lines. Modes: intercept Support for IP-Layer interception of outgoing requests without browser settings. NP: disables authentication and IPv6 on the port. tproxy Support Linux TPROXY for spoofing outgoing connections using the client IP address. NP: disables authentication and maybe IPv6 on the port. accel Accelerator / reverse proxy mode + proxy-surrogate + Support for PROXY protocol version 1 or 2 connections. + The proxy_forwarded_access is required to whitelist + downstream proxies which can be trusted. + ssl-bump For each CONNECT request allowed by ssl_bump ACLs, establish secure connection with the client and with the server, decrypt HTTPS messages as they pass through Squid, and treat them as unencrypted HTTP messages, becoming the man-in-the-middle. The ssl_bump option is required to fully enable bumping of CONNECT requests. Omitting the mode flag causes default forward proxy mode to be used. Accelerator Mode Options: defaultsite=domainname What to use for the Host: header if it is not present in a request. Determines what site (not origin server) accelerators should consider the default. no-vhost Disable using HTTP/1.1 Host header for virtual domain support. === modified file 'src/client_side.cc' --- src/client_side.cc 2014-06-18 00:14:50 +0000 +++ src/client_side.cc 2014-06-22 03:56:59 +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 "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" #include "ssl/gadgets.h" @@ -2887,67 +2888,293 @@ 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 = Config.pipeline_max_prefetch + 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 follow_x_forwarded_for ACL tests on the + * client which connected to PROXY protocol port. + */ +void +ConnStateData::proxyProtocolValidateClient() +{ + ACLFilledChecklist ch(Config.accessList.followXFF, NULL, clientConnection->rfc931); + ch.src_addr = clientConnection->remote; + ch.my_addr = clientConnection->local; + // TODO: we should also pass the port details for myportname here. + + if (ch.fastCheck() != ACCESS_ALLOWED) { + // terminate the connection. invalid input. + stopReceiving("PROXY client not permitted by ACLs"); + stopSending("PROXY client not permitted by ACLs"); + } +} + +/** + * Perform cleanup on PROXY protocol errors. + * If header parsing hits a fatal error terminate the connection, + * otherwise wait for more data. + */ +bool +ConnStateData::proxyProtocolError(bool fatalError) +{ + if (fatalError) { + // terminate the connection. invalid input. + stopReceiving("PROXY protocol error"); + stopSending("PROXY protocol error"); + } + return false; +} + +/** + * Parse the connection read buffer for PROXY protocol header. + * Only version 1 header currently supported. + */ +bool +ConnStateData::parseProxyProtocolMagic() +{ + // http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt + + // magic octet prefix for PROXY protocol version 1 + static const SBuf proxy1magic("PROXY "); + + // magic octet prefix for PROXY protocol version 2 + static const SBuf proxy2magic("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 12); + + // detect and parse PROXY protocol version 1 header + if (in.buf.length() > proxy1magic.length() && in.buf.startsWith(proxy1magic)) { + ::Parser::Tokenizer tok(in.buf); + tok.skip(proxy1magic); + + SBuf tcpVersion; + if (!tok.prefix(tcpVersion, CharacterSet::ALPHA+CharacterSet::DIGIT)) + return proxyProtocolError(tok.atEnd()); + + if (!tcpVersion.cmp("UNKNOWN")) { + // skip to first LF (assumes it is part of CRLF) + const SBuf::size_type pos = in.buf.findFirstOf(CharacterSet::LF); + if (pos != SBuf::npos) { + if (in.buf[pos-1] != '\r') + return proxyProtocolError(false); // invalid terminator + // found valid but unusable header + in.buf.consume(pos); + needProxyProtocolHeader_ = false; + return true; + } + // else, no LF found + + // protocol error only if there are more than 107 bytes prefix header + return proxyProtocolError(in.buf.length() > 107); + + } else if (!tcpVersion.cmp("TCP",3)) { + + // skip SP after protocol version + if (!tok.skip(' ')) + return proxyProtocolError(tok.atEnd()); + + SBuf ipa, ipb; + int64_t porta, portb; + const CharacterSet ipChars = CharacterSet("IP Address",".:") + CharacterSet::HEXDIG; + + // parse src-IP SP dst-IP SP src-port SP dst-port CRLF + if (!tok.prefix(ipa, ipChars) || !tok.skip(' ') || + !tok.prefix(ipb, ipChars) || !tok.skip(' ') || + !tok.int64(porta) || !tok.skip(' ') || + !tok.int64(portb) || !tok.skip('\r') || !tok.skip('\n')) + return proxyProtocolError(!tok.atEnd()); + + // XXX parse IP and port strings + Ip::Address originalClient, originalDest; + + if (!originalClient.GetHostByName(ipa.c_str())) + return proxyProtocolError(true); + + if (!originalDest.GetHostByName(ipb.c_str())) + return false; + + if (porta > 0 && porta <= 0xFFFF) // max uint16_t + originalClient.port(static_cast(porta)); + else + return proxyProtocolError(true); + + if (portb > 0 && portb <= 0xFFFF) // max uint16_t + originalDest.port(static_cast(portb)); + else + return proxyProtocolError(true); + + in.buf = tok.remaining(); // sync buffers + needProxyProtocolHeader_ = false; // found successfully + + // we have original client and destination details now + // replace the client connection values + debugs(33, 5, "PROXY protocol on connection " << clientConnection); + clientConnection->local = originalDest; + clientConnection->remote = originalClient; + debugs(33, 5, "PROXY 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; + } + + // detect and parse PROXY protocol version 2 header + } else if (in.buf.length() > proxy2magic.length() && in.buf.startsWith(proxy2magic)) { + + if ((in.buf[0] & 0xF0) != 0x20) // version == 2 is mandatory + proxyProtocolError(true); + + const char command = (in.buf[0] & 0x0F); + if ((command & 0xFE) != 0x00) // values other than 0x0-0x1 are invalid + proxyProtocolError(true); + + const char family = (in.buf[1] & 0xF0) >>4; + if (family > 0x3) // values other than 0x0-0x3 are invalid + proxyProtocolError(true); + + const char proto = (in.buf[1] & 0x0F); + if (proto > 0x2) // values other than 0x0-0x2 are invalid + proxyProtocolError(true); + + const char *clen = in.buf.rawContent() + proxy2magic.length() + 2; + const uint16_t len = ntohs(*(reinterpret_cast(clen))); + + if (in.buf.length() < proxy2magic.length() + 4 + len) + return false; // need more bytes + + in.buf.consume(proxy2magic.length() + 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; + + typedef union proxy_addr { + 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; + + const pax *ipu = reinterpret_cast(extra.rawContent()); + + // replace the client connection values + debugs(33, 5, "PROXY 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)); + 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)); + break; + default: // do nothing + break; + } + debugs(33, 5, "PROXY 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; + + // detect and terminate other protocols + } else if (in.buf.length() >= proxy2magic.length()) { + + // input other than the PROXY header is a protocol error + return proxyProtocolError(true); + } + + // not enough bytes to parse yet. + return false; +} + +/** * 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() { HttpRequestMethod method; 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; /* Begin the parsing */ PROF_start(parseHttpRequest); + + // try to parse the PROXY protocol header magic bytes + if (needProxyProtocolHeader_ && !parseProxyProtocolMagic()) + break; + HttpParserInit(&parser_, in.buf.c_str(), in.buf.length()); /* Process request */ Http::ProtocolVersion http_ver; ClientSocketContext *context = parseHttpRequest(this, &parser_, &method, &http_ver); PROF_stop(parseHttpRequest); /* 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)); @@ -3304,40 +3531,44 @@ typedef CommCbMemFunT 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 = xact->tcpClient->remote; identChecklist.my_addr = xact->tcpClient->local; if (identChecklist.fastCheck() == ACCESS_ALLOWED) Ident::Start(xact->tcpClient, clientIdentDone, this); } #endif clientdbEstablished(clientConnection->remote, 1); flags.readMore = true; + + needProxyProtocolHeader_ = xact->squidPort->flags.proxySurrogate; + if (needProxyProtocolHeader_) + proxyProtocolValidateClient(); // will close the connection on failure } /** Handle a new connection on HTTP socket. */ void httpAccept(const CommAcceptCbParams ¶ms) { MasterXaction::Pointer xact = params.xaction; AnyP::PortCfgPointer s = xact->squidPort; if (!s.valid()) { // it is possible the call or accept() was still queued when the port was reconfigured debugs(33, 2, "HTTP accept failure: port reconfigured."); return; } if (params.flag != Comm::OK) { // Its possible the call was still queued when the client disconnected debugs(33, 2, "httpAccept: " << s->listenConn << ": accept failure: " << xstrerr(params.xerrno)); return; } === modified file 'src/client_side.h' --- src/client_side.h 2014-06-05 14:57:58 +0000 +++ src/client_side.h 2014-06-21 16:00:37 +0000 @@ -372,40 +372,48 @@ Ssl::BumpMode sslBumpMode; ///< ssl_bump decision (Ssl::bumpEnd if n/a). #else bool switchedToHttps() const { return false; } #endif protected: void startDechunkingRequest(); void finishDechunkingRequest(bool withSuccess); void abortChunkedRequestBody(const err_type error); err_type handleChunkedRequestBody(size_t &putSize); void startPinnedConnectionMonitoring(); void clientPinnedConnectionRead(const CommIoCbParams &io); private: int connFinishedWithConn(int size); void clientAfterReadingRequests(); bool concurrentRequestQueueFilled() const; + /* PROXY protocol functionality */ + void proxyProtocolValidateClient(); + bool parseProxyProtocolMagic(); + bool proxyProtocolError(bool isFatal); + + /// whether PROXY protocol header is still expected on this port + bool needProxyProtocolHeader_; + #if USE_AUTH /// some user details that can be used to perform authentication on this connection Auth::UserRequest::Pointer auth_; #endif HttpParser parser_; // XXX: CBDATA plays with public/private and leaves the following 'private' fields all public... :( #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