///|
fn ascii_string(input : Bytes, end : Int) -> Result[String, ProxyError] {
let chars = Array::make(0, ' ')
for i = 0; i < end; i = i + 1 {
if input[i].to_int() > 127 {
return Err(proxy_error(InvalidV1Line, i, "v1 line must be ASCII"))
}
chars.push(input[i].to_int().to_char().unwrap())
}
Ok(String::from_array(chars))
}
///|
fn v1_header(
command : String,
source : String,
destination : String,
source_port : String,
destination_port : String,
) -> Result[ProxyHeader, ProxyError] {
let sp = match parse_port_text(source_port) {
Err(err) => return Err(err)
Ok(v) => v
}
let dp = match parse_port_text(destination_port) {
Err(err) => return Err(err)
Ok(v) => v
}
if command == "TCP4" {
let src = match parse_ipv4_text(source) {
Err(err) => return Err(err)
Ok(v) => v
}
let dst = match parse_ipv4_text(destination) {
Err(err) => return Err(err)
Ok(v) => v
}
ProxyHeader::new(
V1,
Proxy,
Inet,
Stream,
Ipv4({
source_address: src,
destination_address: dst,
source_port: sp,
destination_port: dp,
}),
[],
)
} else if command == "TCP6" {
let src = match parse_ipv6_text(source) {
Err(err) => return Err(err)
Ok(v) => v
}
let dst = match parse_ipv6_text(destination) {
Err(err) => return Err(err)
Ok(v) => v
}
ProxyHeader::new(
V1,
Proxy,
Inet6,
Stream,
Ipv6({
source_address: src,
destination_address: dst,
source_port: sp,
destination_port: dp,
}),
[],
)
} else {
Err(
proxy_error(InvalidV1Line, 6, "v1 command must be TCP4, TCP6, or UNKNOWN"),
)
}
}
///|
pub fn decode_v1(
input : Bytes,
policy : DecodePolicy,
) -> Result[DecodedFrame, ProxyError] {
if policy.expected_protocol == V2Only {
return Err(proxy_error(PolicyViolation, 0, "policy accepts only v2"))
}
let mut end = -1
let mut i = 0
while i < input.length() && i < policy.max_v1_line_bytes {
if input[i] == b'\r' {
if i + 1 >= input.length() {
if i + 1 >= policy.max_v1_line_bytes {
return Err(
proxy_error(
V1LineTooLong,
i,
"CRLF would exceed the configured v1 line bound",
),
)
}
return Err(proxy_error(NeedMoreData, i, "CRLF is incomplete"))
}
if input[i + 1] != b'\n' {
return Err(proxy_error(MissingCrlf, i, "v1 requires CRLF"))
}
if i + 2 > policy.max_v1_line_bytes {
return Err(
proxy_error(V1LineTooLong, i + 2, "v1 line exceeded configured bound"),
)
}
end = i
break
}
if input[i] == b'\n' {
return Err(proxy_error(MissingCrlf, i, "v1 requires CRLF"))
}
i = i + 1
}
if end < 0 {
if input.length() >= policy.max_v1_line_bytes {
Err(
proxy_error(
V1LineTooLong,
policy.max_v1_line_bytes,
"v1 line exceeded configured bound",
),
)
} else {
Err(proxy_error(NeedMoreData, input.length(), "CRLF not received"))
}
} else {
let line = match ascii_string(input, end) {
Err(err) => return Err(err)
Ok(v) => v
}
let parts = line.split(" ").to_array()
if parts.length() < 2 || parts[0].to_owned() != "PROXY" {
return Err(
proxy_error(
InvalidPrefix,
0,
"v1 must start with PROXY followed by one space",
),
)
}
let proto = parts[1].to_owned()
let header = if proto == "UNKNOWN" {
ProxyHeader::new(V1, Proxy, Unspec, Unspec, NoAddress, [])
} else if parts.length() == 6 {
v1_header(
proto,
parts[2].to_owned(),
parts[3].to_owned(),
parts[4].to_owned(),
parts[5].to_owned(),
)
} else {
Err(
proxy_error(InvalidV1Line, 0, "TCP4/TCP6 requires exactly four fields"),
)
}
match header {
Err(err) => Err(err)
Ok(parsed) =>
Ok({
header: parsed,
remaining: Bytes::from_array(input.to_array()[end + 2:]),
consumed: end + 2,
})
}
}
}