bool Filter::readProxyHeader()

in source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc [261:375]


bool Filter::readProxyHeader(int fd) {
  while (buf_off_ < MAX_PROXY_PROTO_LEN_V2) {
    int bytes_avail;
    auto& os_syscalls = Api::OsSysCallsSingleton::get();

    if (os_syscalls.ioctl(fd, FIONREAD, &bytes_avail).rc_ < 0) {
      throw EnvoyException("failed to read proxy protocol (no bytes avail)");
    }

    if (bytes_avail == 0) {
      return false;
    }

    bytes_avail = std::min(size_t(bytes_avail), MAX_PROXY_PROTO_LEN_V2 - buf_off_);

    const Api::SysCallSizeResult result =
        os_syscalls.recv(fd, buf_ + buf_off_, bytes_avail, MSG_PEEK);
    ssize_t nread = result.rc_;

    if (nread < 1) {
      throw EnvoyException("failed to read proxy protocol (no bytes read)");
    }

    if (buf_off_ + nread >= PROXY_PROTO_V2_HEADER_LEN) {
      const char* sig = PROXY_PROTO_V2_SIGNATURE;
      if (!memcmp(buf_, sig, PROXY_PROTO_V2_SIGNATURE_LEN)) {
        header_version_ = V2;
      } else if (memcmp(buf_, PROXY_PROTO_V1_SIGNATURE, PROXY_PROTO_V1_SIGNATURE_LEN)) {
        // It is not v2, and can't be v1, so no sense hanging around: it is invalid
        throw EnvoyException("failed to read proxy protocol (exceed max v1 header len)");
      }
    }

    if (header_version_ == V2) {
      const int ver_cmd = buf_[PROXY_PROTO_V2_SIGNATURE_LEN];
      if (((ver_cmd & 0xf0) >> 4) != PROXY_PROTO_V2_VERSION) {
        throw EnvoyException("Unsupported V2 proxy protocol version");
      }
      if (buf_off_ < PROXY_PROTO_V2_HEADER_LEN) {
        ssize_t exp = PROXY_PROTO_V2_HEADER_LEN - buf_off_;
        const Api::SysCallSizeResult read_result = os_syscalls.recv(fd, buf_ + buf_off_, exp, 0);
        if (read_result.rc_ != exp) {
          throw EnvoyException("failed to read proxy protocol (remote closed)");
        }
        buf_off_ += read_result.rc_;
        nread -= read_result.rc_;
      }
      ssize_t addr_len = lenV2Address(buf_);
      uint8_t upper_byte = buf_[PROXY_PROTO_V2_HEADER_LEN - 2];
      uint8_t lower_byte = buf_[PROXY_PROTO_V2_HEADER_LEN - 1];
      ssize_t hdr_addr_len = (upper_byte << 8) + lower_byte;
      if (hdr_addr_len < addr_len) {
        throw EnvoyException("failed to read proxy protocol (insufficient data)");
      }
      if (ssize_t(buf_off_) + nread >= PROXY_PROTO_V2_HEADER_LEN + addr_len) {
        ssize_t missing = (PROXY_PROTO_V2_HEADER_LEN + addr_len) - buf_off_;
        const Api::SysCallSizeResult read_result =
            os_syscalls.recv(fd, buf_ + buf_off_, missing, 0);
        if (read_result.rc_ != missing) {
          throw EnvoyException("failed to read proxy protocol (remote closed)");
        }
        buf_off_ += read_result.rc_;
        parseV2Header(buf_);
        // The TLV remain, they are read/discard in parseExtensions() which is called from the
        // parent (if needed).
        return true;
      } else {
        const Api::SysCallSizeResult result = os_syscalls.recv(fd, buf_ + buf_off_, nread, 0);
        nread = result.rc_;
        if (nread < 0) {
          throw EnvoyException("failed to read proxy protocol (remote closed)");
        }
        buf_off_ += nread;
      }
    } else {
      // continue searching buf_ from where we left off
      for (; search_index_ < buf_off_ + nread; search_index_++) {
        if (buf_[search_index_] == '\n' && buf_[search_index_ - 1] == '\r') {
          if (search_index_ == 1) {
            // This could be the binary protocol. It cannot be the ascii protocol
            header_version_ = InProgress;
          } else {
            header_version_ = V1;
            search_index_++;
          }
          break;
        }
      }

      // If we bailed on the first char, we might be v2, but are for sure not v1. Thus we
      // can read up to min(PROXY_PROTO_V2_HEADER_LEN, bytes_avail). If we bailed after first
      // char, but before we hit \r\n, read up to search_index_. We're asking only for
      // bytes we've already seen so there should be no block or fail
      size_t ntoread;
      if (header_version_ == InProgress) {
        ntoread = bytes_avail;
      } else {
        ntoread = search_index_ - buf_off_;
      }

      const Api::SysCallSizeResult result = os_syscalls.recv(fd, buf_ + buf_off_, ntoread, 0);
      nread = result.rc_;
      ASSERT(size_t(nread) == ntoread);

      buf_off_ += nread;

      if (header_version_ == V1) {
        parseV1Header(buf_, buf_off_);
        return true;
      }
    }
  }

  throw EnvoyException("failed to read proxy protocol (exceed max v2 header len)");
}