1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-04-22 13:44:52 +02:00
2023-09-17 21:34:38 +01:00

245 lines
7.0 KiB
Rust

//! PROXY protocol.
#![deny(rust_2018_idioms, nonstandard_style)]
#![warn(future_incompatible)]
// #![warn(missing_docs)]
#![allow(unused)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use std::{
convert::TryFrom as _,
fmt, io,
net::{IpAddr, SocketAddr},
};
use arrayvec::{ArrayString, ArrayVec};
use tokio::io::{AsyncWrite, AsyncWriteExt as _};
pub mod v1;
pub mod v2;
/// PROXY Protocol Version.
#[derive(Debug, Clone, Copy)]
enum Version {
/// Human-readable header format (Version 1)
V1,
/// Binary header format (Version 2)
V2,
}
impl Version {
const fn signature(&self) -> &'static [u8] {
match self {
Version::V1 => v1::SIGNATURE.as_bytes(),
Version::V2 => v2::SIGNATURE.as_slice(),
}
}
const fn v2_hi(&self) -> u8 {
(match self {
Version::V1 => panic!("v1 not supported in PROXY v2"),
Version::V2 => 0x2,
}) << 4
}
}
/// Command
///
/// other values are unassigned and must not be emitted by senders. Receivers
/// must drop connections presenting unexpected values here.
#[derive(Debug, Clone, Copy)]
pub enum Command {
/// \x0 : LOCAL : the connection was established on purpose by the proxy
/// without being relayed. The connection endpoints are the sender and the
/// receiver. Such connections exist when the proxy sends health-checks to the
/// server. The receiver must accept this connection as valid and must use the
/// real connection endpoints and discard the protocol block including the
/// family which is ignored.
Local,
/// \x1 : PROXY : the connection was established on behalf of another node,
/// and reflects the original connection endpoints. The receiver must then use
/// the information provided in the protocol block to get original the address.
Proxy,
}
impl Command {
const fn v2_lo(&self) -> u8 {
match self {
Command::Local => 0x0,
Command::Proxy => 0x1,
}
}
}
/// Address Family.
///
/// maps to the original socket family without necessarily
/// matching the values internally used by the system.
///
/// other values are unspecified and must not be emitted in version 2 of this
/// protocol and must be rejected as invalid by receivers.
#[derive(Debug, Clone, Copy)]
pub enum AddressFamily {
/// 0x0 : AF_UNSPEC : the connection is forwarded for an unknown, unspecified
/// or unsupported protocol. The sender should use this family when sending
/// LOCAL commands or when dealing with unsupported protocol families. The
/// receiver is free to accept the connection anyway and use the real endpoint
/// addresses or to reject it. The receiver should ignore address information.
Unspecified,
/// 0x1 : AF_INET : the forwarded connection uses the AF_INET address family
/// (IPv4). The addresses are exactly 4 bytes each in network byte order,
/// followed by transport protocol information (typically ports).
Inet,
/// 0x2 : AF_INET6 : the forwarded connection uses the AF_INET6 address family
/// (IPv6). The addresses are exactly 16 bytes each in network byte order,
/// followed by transport protocol information (typically ports).
Inet6,
/// 0x3 : AF_UNIX : the forwarded connection uses the AF_UNIX address family
/// (UNIX). The addresses are exactly 108 bytes each.
Unix,
}
impl AddressFamily {
fn v1_str(&self) -> &'static str {
match self {
AddressFamily::Inet => "TCP4",
AddressFamily::Inet6 => "TCP6",
af => panic!("{:?} is not supported in PROXY v1", af),
}
}
const fn v2_hi(&self) -> u8 {
(match self {
AddressFamily::Unspecified => 0x0,
AddressFamily::Inet => 0x1,
AddressFamily::Inet6 => 0x2,
AddressFamily::Unix => 0x3,
}) << 4
}
}
/// Transport Protocol.
///
/// other values are unspecified and must not be emitted in version 2 of this
/// protocol and must be rejected as invalid by receivers.
#[derive(Debug, Clone, Copy)]
pub enum TransportProtocol {
/// 0x0 : UNSPEC : the connection is forwarded for an unknown, unspecified
/// or unsupported protocol. The sender should use this family when sending
/// LOCAL commands or when dealing with unsupported protocol families. The
/// receiver is free to accept the connection anyway and use the real endpoint
/// addresses or to reject it. The receiver should ignore address information.
Unspecified,
/// 0x1 : STREAM : the forwarded connection uses a SOCK_STREAM protocol (eg:
/// TCP or UNIX_STREAM). When used with AF_INET/AF_INET6 (TCP), the addresses
/// are followed by the source and destination ports represented on 2 bytes
/// each in network byte order.
Stream,
/// 0x2 : DGRAM : the forwarded connection uses a SOCK_DGRAM protocol (eg:
/// UDP or UNIX_DGRAM). When used with AF_INET/AF_INET6 (UDP), the addresses
/// are followed by the source and destination ports represented on 2 bytes
/// each in network byte order.
Datagram,
}
impl TransportProtocol {
const fn v2_lo(&self) -> u8 {
match self {
TransportProtocol::Unspecified => 0x0,
TransportProtocol::Stream => 0x1,
TransportProtocol::Datagram => 0x2,
}
}
}
#[derive(Debug)]
enum ProxyProtocolHeader {
V1(v1::Header),
V2(v2::Header),
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Pp2Crc32c {
checksum: u32,
}
impl Pp2Crc32c {
fn to_tlv(&self) -> Tlv {
Tlv {
r#type: 0x03,
value: self.checksum.to_be_bytes().to_vec(),
}
}
}
#[derive(Debug, Clone)]
pub struct Tlv {
r#type: u8,
value: Vec<u8>,
}
impl Tlv {
pub fn new(r#type: u8, value: impl Into<Vec<u8>>) -> Self {
let value = value.into();
assert!(
!value.is_empty(),
"TLV values must have length greater than 1",
);
Self { r#type, value }
}
fn len(&self) -> u16 {
// 1b type + 2b len
// + [len]b value
1 + 2 + (self.value.len() as u16)
}
pub fn write_to(&self, wrt: &mut impl io::Write) -> io::Result<()> {
wrt.write_all(&[self.r#type])?;
wrt.write_all(&(self.value.len() as u16).to_be_bytes())?;
wrt.write_all(&self.value)?;
Ok(())
}
pub fn as_crc32c(&self) -> Option<Pp2Crc32c> {
if self.r#type != 0x03 {
return None;
}
let checksum_bytes = <[u8; 4]>::try_from(self.value.as_slice()).ok()?;
Some(Pp2Crc32c {
checksum: u32::from_be_bytes(checksum_bytes),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tlv_as_crc32c() {
let noop = Tlv::new(0x04, vec![0x00]);
assert_eq!(noop.as_crc32c(), None);
let noop = Tlv::new(0x03, vec![0x08, 0x70, 0x17, 0x7b]);
assert_eq!(
noop.as_crc32c(),
Some(Pp2Crc32c {
checksum: 141563771
})
);
}
}