]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Merge from trunk
authorAmos Jeffries <squid3@treenet.co.nz>
Tue, 2 Sep 2014 01:08:58 +0000 (18:08 -0700)
committerAmos Jeffries <squid3@treenet.co.nz>
Tue, 2 Sep 2014 01:08:58 +0000 (18:08 -0700)
1  2 
doc/release-notes/release-3.5.sgml
src/cache_cf.cc
src/cf.data.pre
src/client_side.cc
src/client_side.h

Simple merge
diff --cc src/cache_cf.cc
Simple merge
diff --cc src/cf.data.pre
Simple merge
index 206b607d22b0168eb860007ffc9b1d467d34832b,9b2a8ffd3594fc85ef3d623bc6ad1b985ad34856..8e842e3b8536444b4f4ce879f3f6863c57ebeb1a
@@@ -2898,276 -2893,10 +2897,276 @@@ ConnStateData::concurrentRequestQueueFi
      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.
+  * Returns true after completing parsing of at least one request [header]. That
+  * includes cases where parsing ended with an error (e.g., a huge request).
   */
  bool
  ConnStateData::clientParseRequests()
          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");
+         if (ClientSocketContext *context = parseOneRequest(http_ver)) {
+             debugs(33, 5, clientConnection << ": done parsing a request");
              AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "clientLifetimeTimeout",
                                               CommTimeoutCbPtrFun(clientLifetimeTimeout, context->http));
              commSetConnTimeout(clientConnection, Config.Timeout.lifetime, timeoutCall);
Simple merge