Skip to content

Commit bd2639e

Browse files
committed
vey-proxy: fix the encoding of CONNECT-UDP CAPSULE data format
1 parent 6723740 commit bd2639e

3 files changed

Lines changed: 62 additions & 26 deletions

File tree

vey-proxy/CHANGELOG

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Release notes for `vey-proxy`, ordered from newest to oldest.
55
v1.13.9:
66
- BUG FIX: fix deadlock in hickory resolver when response contains only CNAME record
77
- BUG FIX: fix udp packet copy when uses socks_proxy server
8+
- BUG FIX: fix the encoding of CONNECT-UDP CAPSULE data format
89
- BUG FIX: fix continent rules match in route_geoip escaper
910
- BUG FIX: fix parsing of IMAP command UNSUBSCRIBE
1011
- BUG FIX: fix parsing of malformed BER integer in LDAP response

vey-proxy/src/module/http_connect_udp/recv.rs

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -200,19 +200,7 @@ impl HttpConnectUdpRecvBuffer {
200200

201201
fn parse_header(&mut self) -> Result<(), HttpConnectUdpRecvError> {
202202
let left_data = &self.buffer[self.parse_start..self.read_start];
203-
204-
// Context ID
205203
let mut offset = 0;
206-
match VarInt::parse(left_data) {
207-
Some(data) => {
208-
let context_id = data.value();
209-
if context_id != 0 {
210-
return Err(HttpConnectUdpRecvError::InvalidContextId(context_id));
211-
}
212-
offset += data.encoded_len();
213-
}
214-
None => return Ok(()),
215-
}
216204

217205
// Capsule Type
218206
match VarInt::parse(&left_data[offset..]) {
@@ -227,19 +215,39 @@ impl HttpConnectUdpRecvBuffer {
227215
}
228216

229217
// Capsule Length
218+
let capsule_len = match VarInt::parse(&left_data[offset..]) {
219+
Some(data) => {
220+
let capsule_length = data.value();
221+
offset += data.encoded_len();
222+
capsule_length
223+
}
224+
None => return Ok(()),
225+
};
226+
227+
// Context ID
230228
if let Some(data) = VarInt::parse(&left_data[offset..]) {
231-
let capsule_length = data.value();
232-
if capsule_length > self.max_packet_size as u64 {
233-
return Err(HttpConnectUdpRecvError::InvalidPacketSize(capsule_length));
229+
let context_id = data.value();
230+
if context_id != 0 {
231+
return Err(HttpConnectUdpRecvError::InvalidContextId(context_id));
232+
}
233+
let context_id_len = data.encoded_len();
234+
if context_id_len > capsule_len as usize {
235+
return Err(HttpConnectUdpRecvError::InvalidPacketSize(capsule_len));
236+
}
237+
let datagram_len = capsule_len as usize - context_id_len;
238+
if datagram_len > self.max_packet_size {
239+
return Err(HttpConnectUdpRecvError::InvalidPacketSize(
240+
datagram_len as u64,
241+
));
234242
}
235-
let datagram_len = capsule_length as usize;
236-
offset += data.encoded_len();
237-
let datagram = Datagram {
243+
244+
offset += context_id_len;
245+
246+
self.datagram = Some(Datagram {
238247
length: datagram_len,
239248
start: self.parse_start + offset,
240249
left: datagram_len,
241-
};
242-
self.datagram = Some(datagram);
250+
});
243251
}
244252

245253
Ok(())
@@ -257,9 +265,9 @@ mod tests {
257265
fn capsule(payload: &[u8]) -> Vec<u8> {
258266
let mut encoder = VarIntEncoder::default();
259267
let mut buf = Vec::with_capacity(payload.len() + 6);
260-
buf.push(0); // Context ID
261268
buf.push(0); // Capsule Type: Datagram
262-
buf.extend_from_slice(encoder.encode_u16(payload.len() as u16));
269+
buf.extend_from_slice(encoder.encode_u16(payload.len() as u16 + 1));
270+
buf.push(0); // Context ID
263271
buf.extend_from_slice(payload);
264272
buf
265273
}
@@ -361,7 +369,7 @@ mod tests {
361369

362370
#[tokio::test]
363371
async fn reject_non_zero_context_id() {
364-
let data = [1, 0, 0];
372+
let data = [0, 1, 1]; // Capsule Type: 0 (Datagram), Capsule Length: 1, Context ID: 1
365373
let mut reader = MockIoBuilder::new().read(&data).build();
366374
let mut buffer = HttpConnectUdpRecvBuffer::new(8, 128);
367375

@@ -371,7 +379,7 @@ mod tests {
371379

372380
#[tokio::test]
373381
async fn reject_non_datagram_capsule_type() {
374-
let data = [0, 1, 0];
382+
let data = [1, 0]; // Capsule Type: 1 (non-datagram), Capsule Length: 0
375383
let mut reader = MockIoBuilder::new().read(&data).build();
376384
let mut buffer = HttpConnectUdpRecvBuffer::new(8, 128);
377385

vey-proxy/src/module/http_connect_udp/send.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ impl HttpConnectUdpSendBuffer {
3434
return;
3535
}
3636
self.buffer.reserve(packet.len() + 2 + 4);
37-
self.buffer.push(0); // Context ID
3837
self.buffer.push(0); // Capsule Type: Datagram
3938
self.buffer
40-
.extend_from_slice(self.len_encoder.encode_u16(packet.len() as u16));
39+
.extend_from_slice(self.len_encoder.encode_u16(packet.len() as u16 + 1));
40+
self.buffer.push(0); // Context ID
4141
self.buffer.extend_from_slice(packet);
4242
}
4343

@@ -91,3 +91,30 @@ impl HttpConnectUdpSendBuffer {
9191
}
9292
}
9393
}
94+
95+
#[cfg(test)]
96+
mod tests {
97+
use super::*;
98+
99+
#[test]
100+
fn test_send_buffer_format() {
101+
let mut send_buf = HttpConnectUdpSendBuffer::new(128);
102+
let payload = b"hello";
103+
send_buf.push_packet(payload);
104+
105+
// Expected format:
106+
// Capsule Type: 0 (1 byte)
107+
// Capsule Length: payload.len() + 1 = 6 (1 byte VarInt)
108+
// Context ID: 0 (1 byte)
109+
// Payload: "hello" (5 bytes)
110+
let expected = vec![0, 6, 0, b'h', b'e', b'l', b'l', b'o'];
111+
assert_eq!(send_buf.buffer, expected);
112+
}
113+
114+
#[test]
115+
fn test_send_buffer_oversized_packet_dropped() {
116+
let mut send_buf = HttpConnectUdpSendBuffer::new(4);
117+
send_buf.push_packet(b"oversized");
118+
assert!(send_buf.buffer.is_empty());
119+
}
120+
}

0 commit comments

Comments
 (0)