///|
pub(all) enum ProxyVersion {
V1
V2
} derive(Eq, Debug)
///|
pub(all) enum Command {
Local
Proxy
} derive(Eq, Debug)
///|
pub(all) enum AddressFamily {
Unspec
Inet
Inet6
Unix
} derive(Eq, Debug)
///|
pub(all) enum Transport {
Unspec
Stream
Datagram
} derive(Eq, Debug)
///|
pub(all) struct Ipv4Address {
octets : FixedArray[Byte]
} derive(Debug)
///|
pub(all) struct Ipv6Address {
octets : FixedArray[Byte]
} derive(Debug)
///|
pub fn Ipv4Address::new(
octets : FixedArray[Byte],
) -> Result[Ipv4Address, ProxyError] {
if octets.length() != 4 {
Err(proxy_error(InvalidIpv4, 0, "IPv4 address must have exactly 4 octets"))
} else {
Ok({ octets, })
}
}
///|
pub fn Ipv6Address::new(
octets : FixedArray[Byte],
) -> Result[Ipv6Address, ProxyError] {
if octets.length() != 16 {
Err(proxy_error(InvalidIpv6, 0, "IPv6 address must have exactly 16 octets"))
} else {
Ok({ octets, })
}
}
///|
pub(all) struct Ipv4Endpoints {
source_address : Ipv4Address
destination_address : Ipv4Address
source_port : Int
destination_port : Int
} derive(Debug)
///|
pub(all) struct Ipv6Endpoints {
source_address : Ipv6Address
destination_address : Ipv6Address
source_port : Int
destination_port : Int
} derive(Debug)
///|
pub(all) struct UnixEndpoints {
source : Bytes
destination : Bytes
} derive(Debug)
///|
pub(all) enum AddressBlock {
NoAddress
Ipv4(Ipv4Endpoints)
Ipv6(Ipv6Endpoints)
UnixAddress(UnixEndpoints)
RawUnsupported(Bytes)
} derive(Debug)
///|
pub(all) struct RawTlv {
type_code : Byte
value : Bytes
} derive(Debug)
///|
pub(all) struct ProxyHeader {
version : ProxyVersion
command : Command
family : AddressFamily
transport : Transport
address : AddressBlock
tlvs : Array[RawTlv]
} derive(Debug)
///|
pub(all) struct DecodedFrame {
header : ProxyHeader
remaining : Bytes
consumed : Int
} derive(Debug)
///|
fn valid_port(port : Int) -> Bool {
port >= 0 && port <= 65535
}
///|
pub fn ProxyHeader::new(
version : ProxyVersion,
command : Command,
family : AddressFamily,
transport : Transport,
address : AddressBlock,
tlvs : Array[RawTlv],
) -> Result[ProxyHeader, ProxyError] {
if command == Local && version == V1 {
return Err(proxy_error(PolicyViolation, 0, "v1 has no LOCAL command"))
}
if command == Local {
return Ok({
version,
command,
family: Unspec,
transport: Unspec,
address: NoAddress,
tlvs: [],
})
}
if (family == Unspec && transport != Unspec) ||
(family == Inet && transport == Unspec) ||
(family == Inet6 && transport == Unspec) ||
(family == Unix && transport == Unspec) {
return Err(
proxy_error(
InvalidFamilyTransport,
0,
"address family requires STREAM or DGRAM",
),
)
}
match (family, address) {
(Unspec, NoAddress) =>
Ok({ version, command, family, transport, address, tlvs })
(Inet, Ipv4(endpoints)) =>
if valid_port(endpoints.source_port) &&
valid_port(endpoints.destination_port) {
Ok({ version, command, family, transport, address, tlvs })
} else {
Err(proxy_error(InvalidPort, 0, "port must be in 0..65535"))
}
(Inet6, Ipv6(endpoints)) =>
if valid_port(endpoints.source_port) &&
valid_port(endpoints.destination_port) {
Ok({ version, command, family, transport, address, tlvs })
} else {
Err(proxy_error(InvalidPort, 0, "port must be in 0..65535"))
}
(Unix, UnixAddress(endpoints)) =>
if endpoints.source.length() == 108 &&
endpoints.destination.length() == 108 {
Ok({ version, command, family, transport, address, tlvs })
} else {
Err(proxy_error(InvalidLength, 0, "UNIX fields must be 108 bytes each"))
}
(Unspec, RawUnsupported(_)) =>
Ok({ version, command, family, transport, address, tlvs })
_ =>
Err(
proxy_error(
InvalidFamilyTransport,
0,
"family and address block disagree",
),
)
}
}