///|
pub fn v2_signature() -> Bytes {
Bytes::from_array([
b'\r', b'\n', b'\r', b'\n', b'\x00', b'\r', b'\n', b'Q', b'U', b'I', b'T', b'\n',
])
}
///|
fn parse_v2_command(value : Int) -> Result[Command, ProxyError] {
if value == 0 {
Ok(Local)
} else if value == 1 {
Ok(Proxy)
} else {
Err(proxy_error(InvalidCommand, 12, "v2 command nibble must be 0 or 1"))
}
}
///|
fn parse_v2_family(value : Int) -> Result[AddressFamily, ProxyError] {
if value == 0 {
Ok(Unspec)
} else if value == 1 {
Ok(Inet)
} else if value == 2 {
Ok(Inet6)
} else if value == 3 {
Ok(Unix)
} else {
Err(proxy_error(InvalidFamily, 13, "unsupported v2 address family"))
}
}
///|
fn parse_v2_transport(value : Int) -> Result[Transport, ProxyError] {
if value == 0 {
Ok(Unspec)
} else if value == 1 {
Ok(Stream)
} else if value == 2 {
Ok(Datagram)
} else {
Err(proxy_error(InvalidTransport, 13, "unsupported v2 transport"))
}
}
///|
fn parse_v2_proxy_body(
family : AddressFamily,
transport : Transport,
body : Bytes,
policy : DecodePolicy,
) -> Result[(AddressBlock, Array[RawTlv]), ProxyError] {
if family == Unspec {
if transport != Unspec {
return Err(
proxy_error(
InvalidFamilyTransport,
13,
"UNSPEC family requires UNSPEC transport",
),
)
}
if !policy.allow_unspec {
return Err(
proxy_error(PolicyViolation, 13, "UNSPEC is disabled by policy"),
)
}
let raw = if body.length() == 0 { NoAddress } else { RawUnsupported(body) }
return Ok((raw, []))
}
if transport == Unspec {
return Err(
proxy_error(
InvalidFamilyTransport,
13,
"non-UNSPEC family requires STREAM or DGRAM",
),
)
}
let needed = if family == Inet {
12
} else if family == Inet6 {
36
} else {
216
}
if body.length() < needed {
return Err(
proxy_error(
TruncatedAddress,
16,
"v2 address block is shorter than its family minimum",
),
)
}
let address = if family == Inet {
let src = match
Ipv4Address::new(Bytes::from_array(body.to_array()[0:4]).to_fixedarray()) {
Err(err) => return Err(err)
Ok(v) => v
}
let dst = match
Ipv4Address::new(Bytes::from_array(body.to_array()[4:8]).to_fixedarray()) {
Err(err) => return Err(err)
Ok(v) => v
}
let sp = (body[8].to_int() << 8) | body[9].to_int()
let dp = (body[10].to_int() << 8) | body[11].to_int()
Ipv4({
source_address: src,
destination_address: dst,
source_port: sp,
destination_port: dp,
})
} else if family == Inet6 {
let src = match
Ipv6Address::new(Bytes::from_array(body.to_array()[0:16]).to_fixedarray()) {
Err(err) => return Err(err)
Ok(v) => v
}
let dst = match
Ipv6Address::new(
Bytes::from_array(body.to_array()[16:32]).to_fixedarray(),
) {
Err(err) => return Err(err)
Ok(v) => v
}
let sp = (body[32].to_int() << 8) | body[33].to_int()
let dp = (body[34].to_int() << 8) | body[35].to_int()
Ipv6({
source_address: src,
destination_address: dst,
source_port: sp,
destination_port: dp,
})
} else {
UnixAddress({
source: Bytes::from_array(body.to_array()[0:108]),
destination: Bytes::from_array(body.to_array()[108:216]),
})
}
let tlv_data = Bytes::from_array(body.to_array()[needed:])
match
parse_tlvs(
tlv_data,
offset=16 + needed,
preserve_unknown=policy.preserve_unknown_tlvs,
) {
Err(err) => Err(err)
Ok(tlvs) => Ok((address, tlvs))
}
}
///|
pub fn decode_v2(
input : Bytes,
policy : DecodePolicy,
) -> Result[DecodedFrame, ProxyError] {
if policy.expected_protocol == V1Only {
return Err(proxy_error(PolicyViolation, 0, "policy accepts only v1"))
}
if input.length() < 16 {
return Err(
proxy_error(NeedMoreData, input.length(), "v2 fixed header is incomplete"),
)
}
if !bytes_equal_constant_time(
Bytes::from_array(input.to_array()[0:12]),
v2_signature(),
) {
return Err(proxy_error(InvalidV2Signature, 0, "v2 signature differs"))
}
let version = input[12].to_int() >> 4
if version != 2 {
return Err(
proxy_error(UnsupportedVersion, 12, "v2 version nibble must be 2"),
)
}
let command = match parse_v2_command(input[12].to_int() & 15) {
Err(err) => return Err(err)
Ok(v) => v
}
let length = (input[14].to_int() << 8) | input[15].to_int()
if length > policy.max_v2_payload_bytes {
return Err(
proxy_error(
HeaderTooLarge,
14,
"declared v2 payload exceeds configured bound",
),
)
}
let total = 16 + length
if input.length() < total {
return Err(
proxy_error(
NeedMoreData,
input.length(),
"declared v2 header body is incomplete",
),
)
}
let whole = Bytes::from_array(input.to_array()[0:total])
let body = Bytes::from_array(input.to_array()[16:total])
let header = if command == Local {
if !policy.allow_local_command {
Err(
proxy_error(PolicyViolation, 12, "LOCAL command is disabled by policy"),
)
} else {
// RFC wire fields after the LOCAL command are ignored. Normalize them so
// reserved family/transport nibbles cannot influence the returned model.
ProxyHeader::new(V2, Local, Unspec, Unspec, NoAddress, [])
}
} else {
let family = match parse_v2_family(input[13].to_int() >> 4) {
Err(err) => return Err(err)
Ok(v) => v
}
let transport = match parse_v2_transport(input[13].to_int() & 15) {
Err(err) => return Err(err)
Ok(v) => v
}
match parse_v2_proxy_body(family, transport, body, policy) {
Err(err) => Err(err)
Ok((address, tlvs)) =>
ProxyHeader::new(V2, Proxy, family, transport, address, tlvs)
}
}
match header {
Err(err) => Err(err)
Ok(parsed) => {
let has_crc = find_tlv(parsed.tlvs, TLV_CRC32C) is Some(_)
if policy.require_crc32c && !has_crc {
return Err(proxy_error(MissingCrc32c, 16, "policy requires CRC32C"))
}
if policy.verify_crc32c {
if has_crc {
match verify_proxy_crc32c(whole) {
Err(err) => return Err(err)
Ok(_) => ()
}
}
}
Ok({
header: parsed,
remaining: Bytes::from_array(input.to_array()[total:]),
consumed: total,
})
}
}
}