Skip to content

Commit d448f0d

Browse files
committed
Connect with a timeout will try all socket addresses
1 parent bf17166 commit d448f0d

File tree

5 files changed

+71
-36
lines changed

5 files changed

+71
-36
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ use socks2::Socks4Stream;
3333
use socks2::Socks5Stream;
3434
use std::io::Write;
3535

36-
let mut connection = Socks4Stream::connect(PROXY, &TARGET, "userid").unwrap();
36+
let mut connection = Socks4Stream::connect(PROXY, &TARGET, "userid", None).unwrap();
3737
let buf = [126_u8; 50]
3838
connection.write(&buf);
3939

40-
let mut connection = Socks5Stream::connect(PROXY, &TARGET).unwrap();
40+
let mut connection = Socks5Stream::connect(PROXY, &TARGET, None).unwrap();
4141
let buf = [126_u8; 50]
4242
connection.write(&buf);
4343
```
@@ -53,11 +53,11 @@ socks2 = { version = "0.4", default-features = false, features = ["bind"] }
5353
use socks2::Socks4Listener;
5454
use socks2::Socks5Listener;
5555

56-
let mut connection = Socks4Listener::bin(PROXY, &TARGET, "userid")
56+
let mut connection = Socks4Listener::bind(PROXY, &TARGET, "userid", None)
5757
.unwrap()
5858
.accept();
5959

60-
let mut connection = Socks5Listener::bind(PROXY, &TARGET)
60+
let mut connection = Socks5Listener::bind(PROXY, &TARGET, None)
6161
.unwrap()
6262
.accept();
6363
```
@@ -73,7 +73,7 @@ socks2 = { version = "0.4", default-features = false, features = ["udp"] }
7373
use socks2::Socks5Datagram;
7474
use std::io::Write;
7575

76-
let mut connection = Socks5Datagram::bind(PROXY, &TARGET).unwrap();
76+
let mut connection = Socks5Datagram::bind(PROXY, &TARGET, None).unwrap();
7777
let buf = [126_u8; 50]
7878
connection.send_to(&buf, &OTHER_ADDR);
7979
```

src/error.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ pub enum Error {
1616
InvalidPortValue { addr: String, port: String },
1717

1818
// Socks4/Socks5
19-
/// Could not resolve the first socket address.
20-
NoResolveSocketAddr {},
19+
/// Could not resolve any of the socket address.
20+
NoResolveSocketAddrs {},
2121
/// Response from server had an invalid version byte.
2222
InvalidResponseVersion { version: u8 },
2323
/// Unknown response code
@@ -113,7 +113,7 @@ impl PartialEq for Error {
113113
peq!(
114114
InvalidSocksAddress,
115115
InvalidPortValue,
116-
NoResolveSocketAddr,
116+
NoResolveSocketAddrs,
117117
InvalidResponseVersion,
118118
UnknownResponseCode,
119119
ConnectionRefused,
@@ -155,7 +155,7 @@ impl From<Error> for io::Error {
155155
from_error!(
156156
(InvalidSocksAddress, InvalidInput),
157157
(InvalidPortValue, InvalidInput),
158-
(NoResolveSocketAddr, InvalidInput),
158+
(NoResolveSocketAddrs, InvalidInput),
159159
(InvalidResponseVersion, InvalidData),
160160
(UnknownResponseCode, Other),
161161
(ConnectionRefused, ConnectionRefused),
@@ -193,7 +193,7 @@ impl core::fmt::Display for Error {
193193
Self::InvalidPortValue { addr, port } => {
194194
write!(f, "invalid port value '{port}' for '{addr}'")
195195
},
196-
Self::NoResolveSocketAddr {} => write!(f, "could not resolve a socket address"),
196+
Self::NoResolveSocketAddrs {} => write!(f, "could not resolve a socket address"),
197197
Self::InvalidResponseVersion { version } => write!(f, "invalid response version '{version}'"),
198198
Self::UnknownResponseCode { code } => write!(f, "unknown response code '{code}'"),
199199
Self::ConnectionRefused { code } => write!(f, "connection refused or the request was rejected or failed '{code}'"),

src/lib.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111
extern crate alloc;
1212

1313
use alloc::vec;
14+
use core::time::Duration;
1415
use std::{
1516
io,
16-
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs},
17+
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, TcpStream, ToSocketAddrs},
1718
};
1819

1920
#[cfg(feature = "client")]
@@ -193,6 +194,30 @@ impl ToTargetAddr for &str {
193194
}
194195
}
195196

197+
fn tcp_stream_connect<T>(proxy: T, connect_timeout: Option<Duration>) -> io::Result<TcpStream>
198+
where
199+
T: ToSocketAddrs,
200+
{
201+
match connect_timeout {
202+
None => TcpStream::connect(proxy),
203+
Some(t) => {
204+
// Timeout is applied to every SocketAddr try.
205+
let mut addrs = proxy.to_socket_addrs()?;
206+
let mut last_err = None;
207+
for addr in &mut addrs {
208+
match TcpStream::connect_timeout(&addr, t) {
209+
Ok(t) => return Ok(t),
210+
Err(err) => {
211+
last_err = Some(err);
212+
continue;
213+
}
214+
}
215+
}
216+
Err(last_err.unwrap_or_else(|| Error::NoResolveSocketAddrs {}.into_io()))
217+
}
218+
}
219+
}
220+
196221
#[cfg(test)]
197222
#[allow(clippy::unwrap_used)]
198223
mod test {

src/v4.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ fn read_response(socket: &mut TcpStream) -> io::Result<SocketAddrV4> {
3838
#[cfg(feature = "client")]
3939
pub mod client {
4040
use crate::{
41+
tcp_stream_connect,
4142
v4::{read_response, NULL_BYTE},
4243
Error, TargetAddr, ToTargetAddr,
4344
};
@@ -59,13 +60,16 @@ pub mod client {
5960
impl Socks4Stream {
6061
/// Connects to a target server through a SOCKS4 proxy.
6162
///
62-
/// # Note
63-
///
63+
/// # Notes
6464
/// If `target` is a `TargetAddr::Domain`, the domain name will be forwarded
6565
/// to the proxy server using the SOCKS4A protocol extension. If the proxy
6666
/// server does not support SOCKS4A, consider performing the DNS lookup
6767
/// locally and passing a `TargetAddr::Ip`.
6868
///
69+
/// When using `connect_timeout` the duration will apply to every socket address
70+
/// tried. Only the last connection error will be returned or
71+
/// `io::Error(Error::NoResolveSocketAddrs)`.
72+
///
6973
/// # Errors
7074
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*?)`
7175
pub fn connect<T, U>(
@@ -92,18 +96,7 @@ pub mod client {
9296
T: ToSocketAddrs,
9397
U: ToTargetAddr,
9498
{
95-
let mut socket = match connect_timeout {
96-
None => TcpStream::connect(proxy)?,
97-
// TODO: Connect timeout for each address until one works?
98-
Some(t) => TcpStream::connect_timeout(
99-
&proxy
100-
.to_socket_addrs()?
101-
.next()
102-
.ok_or_else(|| Error::NoResolveSocketAddr {}.into_io())?,
103-
t,
104-
)?,
105-
};
106-
99+
let mut socket = tcp_stream_connect(proxy, connect_timeout)?;
107100
let target = target.to_target_addr()?;
108101

109102
let mut packet = vec![];
@@ -218,6 +211,9 @@ pub mod bind {
218211
/// The proxy will filter incoming connections based on the value of
219212
/// `target`.
220213
///
214+
/// # Notes
215+
/// See `Socks4Stream::connect()`.
216+
///
221217
/// # Errors
222218
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*?)`
223219
pub fn bind<T, U>(

src/v5.rs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ impl Authentication<'_> {
130130
#[cfg(feature = "client")]
131131
pub mod client {
132132
use crate::{
133+
tcp_stream_connect,
133134
v5::{read_response, write_addr, Authentication, MAX_ADDR_LEN},
134135
Error, TargetAddr, ToTargetAddr,
135136
};
@@ -150,6 +151,14 @@ pub mod client {
150151
impl Socks5Stream {
151152
/// Connects to a target server through a SOCKS5 proxy.
152153
///
154+
/// # Notes
155+
/// If `target` is a `TargetAddr::Domain`, the domain name will be forwarded
156+
/// to the proxy server to be resolved there.
157+
///
158+
/// When using `connect_timeout` the duration will apply to every socket address
159+
/// tried. Only the last connection error will be returned or
160+
/// `io::Error(Error::NoResolveSocketAddrs)`.
161+
///
153162
/// # Errors
154163
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*?)`
155164
pub fn connect<T, U>(
@@ -167,6 +176,9 @@ pub mod client {
167176
/// Connects to a target server through a SOCKS5 proxy using given
168177
/// username and password.
169178
///
179+
/// # Notes
180+
/// See `Socks5Stream::connect()`.
181+
///
170182
/// # Errors
171183
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*?)`
172184
pub fn connect_with_password<T, U>(
@@ -195,17 +207,7 @@ pub mod client {
195207
T: ToSocketAddrs,
196208
U: ToTargetAddr,
197209
{
198-
let mut socket = match connect_timeout {
199-
None => TcpStream::connect(proxy)?,
200-
// TODO: Connect timeout for each address until one works?
201-
Some(t) => TcpStream::connect_timeout(
202-
&proxy
203-
.to_socket_addrs()?
204-
.next()
205-
.ok_or_else(|| Error::NoResolveSocketAddr {}.into_io())?,
206-
t,
207-
)?,
208-
};
210+
let mut socket = tcp_stream_connect(proxy, connect_timeout)?;
209211

210212
let target = target.to_target_addr()?;
211213

@@ -390,6 +392,9 @@ pub mod bind {
390392
/// The proxy will filter incoming connections based on the value of
391393
/// `target`.
392394
///
395+
/// # Notes
396+
/// See `Socks5Stream::connect()`.
397+
///
393398
/// # Errors
394399
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*?)`
395400
pub fn bind<T, U>(
@@ -410,6 +415,9 @@ pub mod bind {
410415
/// The proxy will filter incoming connections based on the value of
411416
/// `target`.
412417
///
418+
/// # Notes
419+
/// See `Socks5Stream::connect()`.
420+
///
413421
/// # Errors
414422
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*?)`
415423
pub fn bind_with_password<T, U>(
@@ -481,6 +489,9 @@ pub mod udp {
481489
/// Creates a UDP socket bound to the specified address which will have its
482490
/// traffic routed through the specified proxy.
483491
///
492+
/// # Notes
493+
/// See `Socks5Stream::connect()`.
494+
///
484495
/// # Errors
485496
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*?)`
486497
pub fn bind<T, U>(proxy: T, addr: U, connect_timeout: Option<Duration>) -> io::Result<Self>
@@ -495,6 +506,9 @@ pub mod udp {
495506
/// traffic routed through the specified proxy. The given username and password
496507
/// is used to authenticate to the SOCKS proxy.
497508
///
509+
/// # Notes
510+
/// See `Socks5Stream::connect()`.
511+
///
498512
/// # Errors
499513
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*?)`
500514
pub fn bind_with_password<T, U>(

0 commit comments

Comments
 (0)