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)");
}