1pub mod packet;
8
9use bytes::{Bytes, BytesMut};
10use prost::Message as _;
11use tokio::io::{AsyncReadExt, AsyncWriteExt};
12use tokio::net::TcpStream;
13use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
14
15use crate::error::{Error, Result};
16use crate::guid::Guid;
17use crate::proto;
18use packet::{Packet, PacketFlags, PacketType};
19
20pub const HANDSHAKE_SIGNATURE: u32 = 0x6873_7562;
23
24fn handshake_packet_id() -> Guid {
27 Guid::from_parts([1, 0, 0, 0])
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34#[repr(i32)]
35pub enum EncryptionMode {
36 Disabled = 0,
37 Optional = 1,
38 Required = 2,
39}
40
41pub const DEFAULT_CONNECT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30);
47
48pub const DEFAULT_MAX_MESSAGE_SIZE: u64 = 512 * 1024 * 1024;
59
60#[derive(Debug)]
65pub struct Bus {
66 pub reader: BusReader,
67 pub writer: BusWriter,
68 pub connection_id: Guid,
70}
71
72#[derive(Debug)]
73pub struct BusReader {
74 stream: OwnedReadHalf,
75 buffer: BytesMut,
76 max_message_size: u64,
77 max_part_count: u32,
78}
79
80#[derive(Debug)]
81pub struct BusWriter {
82 stream: OwnedWriteHalf,
83 buffer: BytesMut,
84}
85
86impl Bus {
87 pub async fn connect(address: &str) -> Result<Self> {
89 Self::connect_with(address, DEFAULT_MAX_MESSAGE_SIZE, DEFAULT_CONNECT_TIMEOUT).await
90 }
91
92 pub async fn connect_with(
98 address: &str,
99 max_message_size: u64,
100 connect_timeout: std::time::Duration,
101 ) -> Result<Self> {
102 tokio::time::timeout(
103 connect_timeout,
104 Self::connect_inner(address, max_message_size),
105 )
106 .await
107 .map_err(|_| Error::Connect {
108 address: address.to_owned(),
109 source: std::io::Error::new(
110 std::io::ErrorKind::TimedOut,
111 format!("no handshake within {connect_timeout:?}"),
112 ),
113 })?
114 }
115
116 async fn connect_inner(address: &str, max_message_size: u64) -> Result<Self> {
117 let stream = TcpStream::connect(address)
118 .await
119 .map_err(|source| Error::Connect {
120 address: address.to_owned(),
121 source,
122 })?;
123 stream.set_nodelay(true)?;
127
128 let (read_half, write_half) = stream.into_split();
129 let mut bus = Self {
130 reader: BusReader {
131 stream: read_half,
132 buffer: BytesMut::with_capacity(64 * 1024),
133 max_message_size,
134 max_part_count: packet::DEFAULT_MAX_PART_COUNT,
135 },
136 writer: BusWriter {
137 stream: write_half,
138 buffer: BytesMut::with_capacity(64 * 1024),
139 },
140 connection_id: Guid::random(),
141 };
142 bus.handshake().await?;
143 Ok(bus)
144 }
145
146 async fn handshake(&mut self) -> Result<()> {
151 let handshake = proto::bus::THandshake {
152 connection_id: self.connection_id.to_proto(),
153 encryption_mode: Some(EncryptionMode::Disabled as i32),
154 ..Default::default()
155 };
156
157 let mut part = Vec::with_capacity(4 + handshake.encoded_len());
158 part.extend_from_slice(&HANDSHAKE_SIGNATURE.to_le_bytes());
159 handshake
160 .encode(&mut part)
161 .expect("a Vec never runs out of room");
162
163 self.writer
164 .send(&Packet::message(
165 handshake_packet_id(),
166 vec![Some(Bytes::from(part))],
167 PacketFlags::NONE,
168 ))
169 .await?;
170
171 let reply = self.reader.receive().await?;
172 if reply.packet_type != PacketType::Message {
173 return Err(Error::Protocol(format!(
174 "handshake reply is a {:?} packet, expected a message",
175 reply.packet_type
176 )));
177 }
178 if reply.id != handshake_packet_id() {
179 return Err(Error::Protocol(format!(
180 "handshake reply has packet id {}, expected {}",
181 reply.id,
182 handshake_packet_id()
183 )));
184 }
185 let [Some(payload)] = reply.parts.as_slice() else {
186 return Err(Error::Protocol(format!(
187 "handshake reply has {} parts, expected exactly one",
188 reply.parts.len()
189 )));
190 };
191 if payload.len() < 4 {
192 return Err(Error::Protocol(
193 "handshake reply is too short to hold its signature".to_owned(),
194 ));
195 }
196 let signature = u32::from_le_bytes(payload[0..4].try_into().unwrap());
197 if signature != HANDSHAKE_SIGNATURE {
198 return Err(Error::Protocol(format!(
199 "handshake reply signature is {signature:#010x}, expected {HANDSHAKE_SIGNATURE:#010x}"
200 )));
201 }
202
203 let peer =
204 proto::bus::THandshake::decode(&payload[4..]).map_err(|source| Error::Decode {
205 message: "THandshake",
206 source,
207 })?;
208 if peer.encryption_mode == Some(EncryptionMode::Required as i32) {
209 return Err(Error::Protocol(
210 "the proxy requires encryption, which this crate does not implement yet".to_owned(),
211 ));
212 }
213
214 Ok(())
215 }
216}
217
218impl BusWriter {
219 pub async fn send(&mut self, message: &Packet) -> Result<()> {
224 self.buffer.clear();
225 packet::encode(message, &mut self.buffer)?;
226 self.stream.write_all(&self.buffer).await?;
227 self.stream.flush().await?;
228 Ok(())
229 }
230
231 pub async fn shutdown(&mut self) -> Result<()> {
232 self.stream.shutdown().await?;
233 Ok(())
234 }
235}
236
237impl BusReader {
238 pub async fn receive(&mut self) -> Result<Packet> {
240 loop {
241 if let Some(message) =
242 packet::decode_with(&mut self.buffer, self.max_message_size, self.max_part_count)?
243 {
244 return Ok(message);
245 }
246 let read = self.stream.read_buf(&mut self.buffer).await?;
247 if read == 0 {
248 return Err(Error::Io(std::io::Error::new(
249 std::io::ErrorKind::UnexpectedEof,
250 "the proxy closed the connection",
251 )));
252 }
253 }
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use super::*;
260 use tokio::net::TcpListener;
261
262 async fn handshake_stub(reply: impl Fn(Packet) -> Option<Packet> + Send + 'static) -> String {
265 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
266 let address = listener.local_addr().unwrap().to_string();
267 tokio::spawn(async move {
268 let (stream, _) = listener.accept().await.unwrap();
269 let (mut read_half, mut write_half) = stream.into_split();
270 let mut buffer = BytesMut::new();
271 loop {
272 match packet::decode(&mut buffer, DEFAULT_MAX_MESSAGE_SIZE) {
273 Ok(Some(request)) => {
274 if let Some(response) = reply(request) {
275 let mut out = BytesMut::new();
276 packet::encode(&response, &mut out).unwrap();
277 let _ = write_half.write_all(&out).await;
278 let _ = write_half.flush().await;
279 }
280 return;
281 }
282 Ok(None) => {}
283 Err(_) => return,
284 }
285 if read_half.read_buf(&mut buffer).await.unwrap_or(0) == 0 {
286 return;
287 }
288 }
289 });
290 address
291 }
292
293 fn handshake_bytes(id: Guid) -> Vec<u8> {
295 let handshake = proto::bus::THandshake {
296 connection_id: Guid::random().to_proto(),
297 encryption_mode: Some(0),
298 ..Default::default()
299 };
300 let mut part = Vec::new();
301 part.extend_from_slice(&HANDSHAKE_SIGNATURE.to_le_bytes());
302 handshake.encode(&mut part).unwrap();
303 let reply = Packet::message(id, vec![Some(Bytes::from(part))], PacketFlags::NONE);
304 let mut out = BytesMut::new();
305 packet::encode(&reply, &mut out).unwrap();
306 out.to_vec()
307 }
308
309 fn handshake_reply(mode: EncryptionMode) -> Packet {
310 let handshake = proto::bus::THandshake {
311 connection_id: Guid::random().to_proto(),
312 encryption_mode: Some(mode as i32),
313 ..Default::default()
314 };
315 let mut part = Vec::new();
316 part.extend_from_slice(&HANDSHAKE_SIGNATURE.to_le_bytes());
317 handshake.encode(&mut part).unwrap();
318 Packet::message(
319 handshake_packet_id(),
320 vec![Some(Bytes::from(part))],
321 PacketFlags::NONE,
322 )
323 }
324
325 #[tokio::test]
326 async fn the_client_speaks_first_and_its_handshake_is_well_formed() {
327 let (sender, receiver) = tokio::sync::oneshot::channel();
328 let sender = std::sync::Mutex::new(Some(sender));
329 let address = handshake_stub(move |request| {
330 if let Some(sender) = sender.lock().unwrap().take() {
331 let _ = sender.send(request.clone());
332 }
333 Some(handshake_reply(EncryptionMode::Disabled))
334 })
335 .await;
336
337 Bus::connect(&address)
338 .await
339 .expect("the handshake should succeed");
340
341 let request = receiver.await.unwrap();
342 assert_eq!(request.packet_type, PacketType::Message);
343 assert_eq!(
344 request.id,
345 handshake_packet_id(),
346 "the handshake packet id is 1-0-0-0"
347 );
348 assert_eq!(request.parts.len(), 1);
349
350 let payload = request.parts[0].as_ref().unwrap();
351 assert_eq!(&payload[0..4], b"bush", "the signature spells bush");
355 assert_eq!(HANDSHAKE_SIGNATURE, 0x6873_7562);
356 assert_eq!(
357 request.id.0,
358 [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
359 "the handshake packet id is the GUID 1-0-0-0"
360 );
361 let handshake = proto::bus::THandshake::decode(&payload[4..]).unwrap();
362 assert_eq!(handshake.encryption_mode, Some(0), "encryption is disabled");
363 }
364
365 #[tokio::test]
366 async fn a_peer_that_requires_encryption_is_refused_not_downgraded() {
367 let address = handshake_stub(|_| Some(handshake_reply(EncryptionMode::Required))).await;
368 let error = Bus::connect(&address).await.unwrap_err();
369 assert!(
370 error.to_string().contains("requires encryption"),
371 "unexpected error: {error}"
372 );
373 }
374
375 #[tokio::test]
376 async fn a_handshake_with_the_wrong_signature_is_refused() {
377 let address = handshake_stub(|_| {
378 Some(Packet::message(
379 handshake_packet_id(),
380 vec![Some(Bytes::from_static(b"junk-and-more-junk"))],
381 PacketFlags::NONE,
382 ))
383 })
384 .await;
385 let error = Bus::connect(&address).await.unwrap_err();
386 assert!(
387 error.to_string().contains("signature"),
388 "unexpected error: {error}"
389 );
390 }
391
392 #[tokio::test]
393 async fn a_handshake_with_the_wrong_packet_id_is_refused() {
394 let address = handshake_stub(|_| {
395 let mut reply = handshake_reply(EncryptionMode::Disabled);
396 reply.id = Guid::from_parts([7, 0, 0, 0]);
397 Some(reply)
398 })
399 .await;
400 let error = Bus::connect(&address).await.unwrap_err();
401 assert!(
402 error.to_string().contains("packet id"),
403 "unexpected error: {error}"
404 );
405 }
406
407 #[tokio::test]
408 async fn a_closed_connection_is_an_error_not_a_hang() {
409 let address = handshake_stub(|_| None).await;
410 let error = Bus::connect(&address).await.unwrap_err();
411 assert!(
412 error.to_string().contains("closed the connection"),
413 "unexpected error: {error}"
414 );
415 }
416
417 #[tokio::test]
420 async fn a_silent_peer_does_not_hang_the_connect() {
421 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
422 let address = listener.local_addr().unwrap().to_string();
423 let _accepting = tokio::spawn(async move {
425 let _held = listener.accept().await;
426 std::future::pending::<()>().await;
427 });
428
429 let started = std::time::Instant::now();
430 let error = Bus::connect_with(
431 &address,
432 DEFAULT_MAX_MESSAGE_SIZE,
433 std::time::Duration::from_millis(200),
434 )
435 .await
436 .unwrap_err();
437
438 assert!(
439 started.elapsed() < std::time::Duration::from_secs(5),
440 "it waited too long"
441 );
442 assert!(
443 error.to_string().contains("no handshake within"),
444 "unexpected error: {error}"
445 );
446 }
447
448 #[tokio::test]
455 async fn the_reader_applies_its_own_size_ceiling() {
456 let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
457 let address = listener.local_addr().unwrap().to_string();
458
459 tokio::spawn(async move {
460 let (stream, _) = listener.accept().await.unwrap();
461 let (mut read_half, mut write_half) = stream.into_split();
462 let mut buffer = BytesMut::new();
463 loop {
466 if let Ok(Some(request)) = packet::decode(&mut buffer, DEFAULT_MAX_MESSAGE_SIZE) {
467 let _ = write_half.write_all(&handshake_bytes(request.id)).await;
468 break;
469 }
470 if read_half.read_buf(&mut buffer).await.unwrap_or(0) == 0 {
471 return;
472 }
473 }
474 let big = Packet::message(
475 Guid::random(),
476 vec![Some(Bytes::from(vec![0u8; 128 * 1024]))],
477 PacketFlags::NONE,
478 );
479 let mut out = BytesMut::new();
480 packet::encode(&big, &mut out).unwrap();
481 let _ = write_half.write_all(&out).await;
482 std::future::pending::<()>().await;
483 });
484
485 let mut bus = Bus::connect_with(&address, 4096, DEFAULT_CONNECT_TIMEOUT)
487 .await
488 .expect("the handshake itself is small");
489 let error = bus.reader.receive().await.unwrap_err();
490 assert!(
491 error.to_string().contains("more than the 4096"),
492 "the reader ignored its ceiling: {error}"
493 );
494 }
495
496 #[tokio::test]
497 async fn connecting_to_a_closed_port_reports_the_address() {
498 let error = Bus::connect("127.0.0.1:1").await.unwrap_err();
500 assert!(
501 error.to_string().contains("127.0.0.1:1"),
502 "unexpected error: {error}"
503 );
504 }
505}