Skip to content

Commit e278a1b

Browse files
committed
fixup: remove all heap allocations
1 parent e5f6359 commit e278a1b

File tree

1 file changed

+57
-26
lines changed

1 file changed

+57
-26
lines changed

lightning-net-tokio/src/lib.rs

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,13 @@ async fn socks5_connect(
513513
const SOCKS5_REQUEST_REPLY_MAX_LEN: usize = 1 /* VER */ + 1 /* CMD for request, REP for reply */ + 1 /* RSV */
514514
+ 1 /* ATYP */ + 1 /* HOSTNAME len */ + 255 /* HOSTNAME */ + 2 /* PORT */;
515515
const SOCKS5_REQUEST_REPLY_MIN_LEN: usize = 1 /* VER */ + 1 /* CMD for request, REP for reply */ + 1 /* RSV */
516-
+ 1 /* ATYP */ + 4 /* IPV4 ADDR */ + 2 /* PORT */;
516+
+ 1 /* ATYP */ + 1 /* HOSTNAME len */ + 1 /* HOSTNAME */ + 2 /* PORT */;
517+
const SOCKS5_IPV4_REQUEST_LEN: usize = 1 /* VER */ + 1 /* CMD */ + 1 /* RSV */ + 1 /* ATYP */
518+
+ 4 /* IPV4 DST.ADDR */ + 2 /* DST.PORT */;
519+
const SOCKS5_IPV6_REQUEST_LEN: usize = 1 /* VER */ + 1 /* CMD */ + 1 /* RSV */ + 1 /* ATYP */
520+
+ 16 /* IPV4 DST.ADDR */ + 2 /* DST.PORT */;
521+
const SOCKS5_HOSTNAME_REQUEST_STATIC_FIELDS_LEN: usize = 1 /* VER */ + 1 /* CMD */ + 1 /* RSV */ + 1 /* ATYP */
522+
+ 1 /* HOSTNAME len */ + 2 /* DST.PORT */;
517523

518524
let method_selection_message = [VERSION, NMETHODS, NO_AUTH];
519525
let mut tcp_stream = TcpStream::connect(&socks5_proxy_addr).await.map_err(|_| ())?;
@@ -525,48 +531,73 @@ async fn socks5_connect(
525531
return Err(());
526532
}
527533

528-
let mut socks5_request: Vec<u8> = Vec::with_capacity(SOCKS5_REQUEST_REPLY_MAX_LEN);
534+
let mut socks5_request = [0u8; SOCKS5_REQUEST_REPLY_MAX_LEN];
535+
let request_size;
529536

530-
socks5_request.push(VERSION);
531-
socks5_request.push(CMD_CONNECT);
532-
socks5_request.push(RSV);
537+
socks5_request[0..3].copy_from_slice(&[VERSION, CMD_CONNECT, RSV]);
533538

534539
match addr {
535540
SocketAddress::TcpIpV4 { addr, port } => {
536-
socks5_request.push(ATYP_IPV4);
537-
socks5_request.extend_from_slice(&addr);
538-
socks5_request.extend_from_slice(&port.to_be_bytes());
541+
socks5_request[3] = ATYP_IPV4;
542+
socks5_request[4..8].copy_from_slice(&addr);
543+
socks5_request[8..10].copy_from_slice(&port.to_be_bytes());
544+
request_size = SOCKS5_IPV4_REQUEST_LEN;
539545
},
540546
SocketAddress::TcpIpV6 { addr, port } => {
541-
socks5_request.push(ATYP_IPV6);
542-
socks5_request.extend_from_slice(&addr);
543-
socks5_request.extend_from_slice(&port.to_be_bytes());
547+
socks5_request[3] = ATYP_IPV6;
548+
socks5_request[4..20].copy_from_slice(&addr);
549+
socks5_request[20..22].copy_from_slice(&port.to_be_bytes());
550+
request_size = SOCKS5_IPV6_REQUEST_LEN;
544551
},
545552
ref onion_v3 @ SocketAddress::OnionV3 { port, .. } => {
546553
let onion_v3_url = onion_v3.to_string();
547554
let hostname = onion_v3_url.split_once(':').ok_or(())?.0.as_bytes();
548-
socks5_request.push(ATYP_DOMAINNAME);
549-
socks5_request.extend_from_slice(&[hostname.len() as u8]);
550-
socks5_request.extend_from_slice(hostname);
551-
socks5_request.extend_from_slice(&port.to_be_bytes());
555+
socks5_request[3] = ATYP_DOMAINNAME;
556+
socks5_request[4] = hostname.len() as u8;
557+
let port_index = 5 + hostname.len();
558+
socks5_request[5..port_index].copy_from_slice(hostname);
559+
socks5_request[port_index..port_index + 2].copy_from_slice(&port.to_be_bytes());
560+
request_size = SOCKS5_HOSTNAME_REQUEST_STATIC_FIELDS_LEN + hostname.len();
552561
},
553562
SocketAddress::Hostname { hostname, port } => {
554-
socks5_request.push(ATYP_DOMAINNAME);
555-
socks5_request.extend_from_slice(&[hostname.len() as u8]);
556-
socks5_request.extend_from_slice(hostname.as_bytes());
557-
socks5_request.extend_from_slice(&port.to_be_bytes());
563+
socks5_request[3] = ATYP_DOMAINNAME;
564+
socks5_request[4] = hostname.len();
565+
let port_index = 5 + hostname.len() as usize;
566+
socks5_request[5..port_index].copy_from_slice(hostname.as_bytes());
567+
socks5_request[port_index..port_index + 2].copy_from_slice(&port.to_be_bytes());
568+
request_size = SOCKS5_HOSTNAME_REQUEST_STATIC_FIELDS_LEN + hostname.len() as usize;
558569
},
559570
SocketAddress::OnionV2 { .. } => return Err(()),
560571
};
561572

562-
tcp_stream.write_all(&socks5_request).await.map_err(|_| ())?;
573+
tcp_stream.write_all(&socks5_request[..request_size]).await.map_err(|_| ())?;
574+
575+
let mut buffer = [0u8; SOCKS5_REQUEST_REPLY_MAX_LEN];
576+
// First pull some bytes into the buffer
577+
let mut total_read = tcp_stream.read(&mut buffer).await.map_err(|_| ())?;
578+
// Then make sure we've reached EOF, otherwise keep appending until the max size of the buffer
579+
while total_read < SOCKS5_REQUEST_REPLY_MAX_LEN {
580+
let n_read = match tcp_stream.try_read(&mut buffer[total_read..]) {
581+
Ok(n) => n,
582+
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => 0,
583+
Err(_e) => return Err(()),
584+
};
585+
if n_read == 0 {
586+
break;
587+
}
588+
total_read += n_read;
589+
}
590+
// If we've filled the buffer, return `Err` if we receive more bytes than the max allowed by the RFC
591+
if total_read == SOCKS5_REQUEST_REPLY_MAX_LEN {
592+
match tcp_stream.try_read(&mut [0u8; 1]) {
593+
Ok(0) => (),
594+
Ok(1..) => return Err(()),
595+
Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => (),
596+
Err(_e) => return Err(()),
597+
}
598+
}
563599

564-
let mut buffer: Vec<u8> = Vec::with_capacity(SOCKS5_REQUEST_REPLY_MAX_LEN);
565-
let n_read = tcp_stream.read_to_end(&mut buffer).await.map_err(|_| ())?;
566-
if n_read < SOCKS5_REQUEST_REPLY_MIN_LEN
567-
|| n_read > SOCKS5_REQUEST_REPLY_MAX_LEN
568-
|| buffer[..3] != [VERSION, SUCCEEDED, RSV]
569-
{
600+
if total_read < SOCKS5_REQUEST_REPLY_MIN_LEN || buffer[..3] != [VERSION, SUCCEEDED, RSV] {
570601
return Err(());
571602
}
572603

0 commit comments

Comments
 (0)