Skip to content

Commit 21c31ad

Browse files
committed
add alpn and authority TLVs
1 parent d336fb3 commit 21c31ad

File tree

3 files changed

+118
-13
lines changed

3 files changed

+118
-13
lines changed

actix-proxy-protocol/examples/proxy-server.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ async fn wrap_with_proxy_protocol_v2(mut stream: TcpStream) -> io::Result<()> {
7676

7777
proxy_header.add_typed_tlv(tlv::UniqueId::new("4269")); // UNIQUE_ID
7878
proxy_header.add_typed_tlv(tlv::Noop::new("NOOP m8")); // NOOP
79-
proxy_header.add_crc23c_checksum_tlv();
79+
proxy_header.add_typed_tlv(tlv::Authority::new("localhost")); // NOOP
80+
proxy_header.add_typed_tlv(tlv::Alpn::new("http/1.1")); // NOOP
81+
proxy_header.add_crc23c_checksum();
8082

8183
proxy_header.write_to_tokio(&mut upstream).await?;
8284

actix-proxy-protocol/src/tlv.rs

Lines changed: 109 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
1-
use std::{borrow::Cow, convert::TryFrom};
1+
use std::{borrow::Cow, convert::TryFrom, str};
2+
3+
const PP2_TYPE_ALPN: u8 = 0x01;
4+
const PP2_TYPE_AUTHORITY: u8 = 0x02;
5+
const PP2_TYPE_CRC32C: u8 = 0x03; // done
6+
const PP2_TYPE_NOOP: u8 = 0x04; // done
7+
const PP2_TYPE_UNIQUE_ID: u8 = 0x05; // done
8+
const PP2_TYPE_SSL: u8 = 0x20;
9+
const PP2_SUBTYPE_SSL_VERSION: u8 = 0x21;
10+
const PP2_SUBTYPE_SSL_CN: u8 = 0x22;
11+
const PP2_SUBTYPE_SSL_CIPHER: u8 = 0x23;
12+
const PP2_SUBTYPE_SSL_SIG_ALG: u8 = 0x24;
13+
const PP2_SUBTYPE_SSL_KEY_ALG: u8 = 0x25;
14+
const PP2_TYPE_NETNS: u8 = 0x30;
215

316
pub trait Tlv: Sized {
417
const TYPE: u8;
@@ -16,13 +29,90 @@ pub trait Tlv: Sized {
1629
}
1730
}
1831

32+
/// Application-Layer Protocol Negotiation (ALPN). It is a byte sequence defining
33+
/// the upper layer protocol in use over the connection. The most common use case
34+
/// will be to pass the exact copy of the ALPN extension of the Transport Layer
35+
/// Security (TLS) protocol as defined by RFC7301 [9].
36+
#[derive(Debug, Clone, PartialEq, Eq)]
37+
pub struct Alpn {
38+
alpn: Vec<u8>,
39+
}
40+
41+
impl Alpn {
42+
///
43+
///
44+
/// # Panics
45+
/// Panics if `alpn` is empty (i.e., has length of 0).
46+
pub fn new(alpn: impl Into<Vec<u8>>) -> Self {
47+
let alpn = alpn.into();
48+
49+
assert!(!alpn.is_empty(), "ALPN TLV value cannot be empty");
50+
51+
Self { alpn }
52+
}
53+
}
54+
55+
impl Tlv for Alpn {
56+
const TYPE: u8 = PP2_TYPE_ALPN;
57+
58+
fn try_from_value(value: &[u8]) -> Option<Self> {
59+
Some(Self {
60+
alpn: value.to_owned(),
61+
})
62+
}
63+
64+
fn value_bytes(&self) -> Cow<'_, [u8]> {
65+
Cow::Borrowed(&self.alpn)
66+
}
67+
}
68+
69+
/// Contains the host name value passed by the client, as an UTF8-encoded string.
70+
/// In case of TLS being used on the client connection, this is the exact copy of
71+
/// the "server_name" extension as defined by RFC3546 [10], section 3.1, often
72+
/// referred to as "SNI". There are probably other situations where an authority
73+
/// can be mentioned on a connection without TLS being involved at all.
74+
#[derive(Debug, Clone, PartialEq, Eq)]
75+
pub struct Authority {
76+
authority: String,
77+
}
78+
79+
impl Authority {
80+
/// A UTF-8
81+
///
82+
/// # Panics
83+
/// Panics if `authority` is an empty string.
84+
pub fn new(authority: impl Into<String>) -> Self {
85+
let authority = authority.into();
86+
87+
assert!(!authority.is_empty(), "Authority TLV value cannot be empty");
88+
89+
Self { authority }
90+
}
91+
}
92+
93+
impl Tlv for Authority {
94+
const TYPE: u8 = PP2_TYPE_AUTHORITY;
95+
96+
fn try_from_value(value: &[u8]) -> Option<Self> {
97+
Some(Self {
98+
authority: str::from_utf8(value).ok()?.to_owned(),
99+
})
100+
}
101+
102+
fn value_bytes(&self) -> Cow<'_, [u8]> {
103+
Cow::Borrowed(&self.authority.as_bytes())
104+
}
105+
}
106+
107+
/// The value of the type PP2_TYPE_CRC32C is a 32-bit number storing the CRC32c
108+
/// checksum of the PROXY protocol header.
19109
#[derive(Debug, Clone, Default, PartialEq, Eq)]
20110
pub struct Crc32c {
21111
pub(crate) checksum: u32,
22112
}
23113

24114
impl Tlv for Crc32c {
25-
const TYPE: u8 = 0x03;
115+
const TYPE: u8 = PP2_TYPE_CRC32C;
26116

27117
fn try_from_value(value: &[u8]) -> Option<Self> {
28118
let checksum_bytes = <[u8; 4]>::try_from(value).ok()?;
@@ -37,7 +127,10 @@ impl Tlv for Crc32c {
37127
}
38128
}
39129

40-
#[derive(Debug, Clone, Default, PartialEq, Eq)]
130+
/// The TLV of this type should be ignored when parsed. The value is zero or more
131+
/// bytes. Can be used for data padding or alignment. Note that it can be used
132+
/// to align only by 3 or more bytes because a TLV can not be smaller than that.
133+
#[derive(Debug, Clone, PartialEq, Eq)]
41134
pub struct Noop {
42135
value: Vec<u8>,
43136
}
@@ -46,7 +139,7 @@ impl Noop {
46139
///
47140
///
48141
/// # Panics
49-
/// Panics if value is empty (i.e., has length of 0).
142+
/// Panics if `value` is empty (i.e., has length of 0).
50143
pub fn new(value: impl Into<Vec<u8>>) -> Self {
51144
let value = value.into();
52145

@@ -57,7 +150,7 @@ impl Noop {
57150
}
58151

59152
impl Tlv for Noop {
60-
const TYPE: u8 = 0x04;
153+
const TYPE: u8 = PP2_TYPE_NOOP;
61154

62155
fn try_from_value(value: &[u8]) -> Option<Self> {
63156
Some(Self {
@@ -70,7 +163,13 @@ impl Tlv for Noop {
70163
}
71164
}
72165

73-
#[derive(Debug, Clone, Default, PartialEq, Eq)]
166+
/// The value of the type PP2_TYPE_UNIQUE_ID is an opaque byte sequence of up to
167+
/// 128 bytes generated by the upstream proxy that uniquely identifies the
168+
/// connection.
169+
///
170+
/// The unique ID can be used to easily correlate connections across multiple
171+
/// layers of proxies, without needing to look up IP addresses and port numbers.
172+
#[derive(Debug, Clone, PartialEq, Eq)]
74173
pub struct UniqueId {
75174
value: Vec<u8>,
76175
}
@@ -79,9 +178,9 @@ impl UniqueId {
79178
///
80179
///
81180
/// # Panics
82-
/// Panics if value is empty (i.e., has length of 0).
83-
pub fn new(value: impl Into<Vec<u8>>) -> Self {
84-
let value = value.into();
181+
/// Panics if `value` is empty (i.e., has length of 0).
182+
pub fn new(id: impl Into<Vec<u8>>) -> Self {
183+
let value = id.into();
85184

86185
assert!(!value.is_empty(), "UniqueId TLV `value` cannot be empty");
87186

@@ -90,7 +189,7 @@ impl UniqueId {
90189
}
91190

92191
impl Tlv for UniqueId {
93-
const TYPE: u8 = 0x05;
192+
const TYPE: u8 = PP2_TYPE_UNIQUE_ID;
94193

95194
fn try_from_value(value: &[u8]) -> Option<Self> {
96195
Some(Self {

actix-proxy-protocol/src/v2.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,12 @@ impl Header {
132132
self.tlvs.iter().any(|&(typ, _)| typ == T::TYPE)
133133
}
134134

135+
/// Calculates and adds a crc32c TLV to the PROXY header.
136+
///
137+
/// Uses method defined in spec.
138+
///
135139
/// If this is not called last thing it will be wrong.
136-
pub fn add_crc23c_checksum_tlv(&mut self) {
140+
pub fn add_crc23c_checksum(&mut self) {
137141
// don't add a checksum if it is already set
138142
if self.has_tlv::<Crc32c>() {
139143
return;
@@ -285,7 +289,7 @@ mod tests {
285289
);
286290

287291
// add crc32c TLV to header
288-
header.add_crc23c_checksum_tlv();
292+
header.add_crc23c_checksum();
289293

290294
assert_eq!(header.v2_len(), 12 + 7);
291295
assert_eq!(header.to_vec(), exp);

0 commit comments

Comments
 (0)