Skip to content

Commit 2c156ff

Browse files
committed
Remove all io::Error(String) in favor of io::Error(Error)
1 parent 743af0d commit 2c156ff

File tree

5 files changed

+240
-160
lines changed

5 files changed

+240
-160
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
SOCKS proxy support for Rust.
88

9-
Fork of [sfackler/rust-socks](https://github.com/sfackler/rust-socks).
9+
A fork of [sfackler/rust-socks](https://github.com/sfackler/rust-socks).
10+
11+
See [changes](CHANGELOG.md).
1012

1113
## Using
1214

src/error.rs

Lines changed: 144 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,80 @@
1-
// TODO: Replace all string errors with this!
2-
3-
use std::{fmt::Formatter, io};
1+
use std::{fmt::Formatter, io, net::SocketAddrV6, string::FromUtf8Error};
42

53
/// Errors from socks2
64
#[derive(Debug)]
75
#[non_exhaustive]
86
#[allow(missing_docs)]
97
pub enum Error {
10-
// TODO: Add docs for all errors?
11-
InvalidSocksAddress {
12-
addr: String,
13-
},
14-
InvalidPortValue {
15-
addr: String,
16-
port: String,
17-
},
8+
// TargetAddr
9+
/// Domain name and port could not be parsed.
10+
InvalidSocksAddress { addr: String },
11+
/// Port could not be parsed or is over `u16::MAX`.
12+
InvalidPortValue { addr: String, port: String },
13+
14+
// Socks4/Socks5
15+
/// Response from server had an invalid version byte.
16+
InvalidResponseVersion { version: u8 },
17+
/// Unknown response code
18+
UnknownResponseCode { code: u8 },
19+
/// Connection refused or the request was rejected or failed.
20+
ConnectionRefused { code: u8 },
21+
22+
// Socks4
23+
/// Rejected request due to server not connecting to idnetd on the client.
24+
/// Rejected request due to idnetd and client program not having a matching userid.
25+
RejectedRequestID { code: u8 },
26+
/// Socks4 does not support IPv6.
27+
Socks4NoIPv6 { addr: SocketAddrV6 },
28+
29+
// Socks5
30+
/// Domain received from server was not valid utf8.
31+
MalformedDomain { err: FromUtf8Error },
32+
/// Received an invalid address type from the server.
33+
SOCKS5InvalidAddressType { code: u8 },
34+
/// Unknown error from the server.
35+
UnknownServerFailure { code: u8 },
36+
/// Server ruleset does not allow connection.
37+
ServerRefusedByRuleSet {},
38+
/// Network unreachable.
39+
ServerNetworkUnreachable {},
40+
/// Host unreachable.
41+
ServerHostUnreachable {},
42+
/// Time to live expired.
43+
ServerTTLExpired {},
44+
/// Server does not support the sent command.
45+
ServerCmdNotSupported {},
46+
/// Server does not support the address kind.
47+
ServerAddressNotSupported {},
48+
/// Reserved byte from server is invalid.
49+
InvalidReservedByte { byte: u8 },
50+
/// Domains must have a length between 1 and 255 inclusive.
51+
InvalidDomainLength { domain: String, length: usize },
52+
/// No acceptable auth methods.
53+
NoAuthMethods { method: u8 },
54+
/// Unknown auth method.
55+
UnknownAuthMethod { method: u8 },
56+
/// Invalid username.
57+
InvalidUsername { username: String, length: usize },
58+
/// Invalid password.
59+
InvalidPassword { password: (), length: usize },
60+
/// Auth with password failed.
61+
FailedPasswordAuth {},
62+
63+
// UDP
64+
/// Reserved bytes from server is invalid.
65+
InvalidReservedBytes { bytes: u16 },
66+
/// Fragment id from the server is invalid.
67+
InvalidFragmentID { fid: u8 },
68+
/// UDP Bind Client has a limit of 4 GiB for buffers.
1869
/// Only occurs when using `Socks5Datagram` on windows.
19-
WinUDP4GiBLimit {
20-
size: usize,
21-
},
70+
WinUDP4GiBLimit { size: usize },
71+
}
72+
73+
/// Takes an `std::io::Error` and attempts to unwrap it into a `socks2::Error`.
74+
/// Returns a `Some(&socks2::Error)` on success.
75+
#[must_use]
76+
pub fn unwrap_io_to_socks2_error(e: &io::Error) -> Option<&Error> {
77+
e.get_ref().and_then(|i| i.downcast_ref())
2278
}
2379

2480
impl Error {
@@ -39,7 +95,34 @@ impl PartialEq for Error {
3995
};
4096
}
4197

42-
peq!(InvalidSocksAddress, InvalidPortValue, WinUDP4GiBLimit)
98+
peq!(
99+
InvalidSocksAddress,
100+
InvalidPortValue,
101+
InvalidResponseVersion,
102+
UnknownResponseCode,
103+
ConnectionRefused,
104+
RejectedRequestID,
105+
Socks4NoIPv6,
106+
MalformedDomain,
107+
SOCKS5InvalidAddressType,
108+
UnknownServerFailure,
109+
ServerRefusedByRuleSet,
110+
ServerNetworkUnreachable,
111+
ServerHostUnreachable,
112+
ServerTTLExpired,
113+
ServerCmdNotSupported,
114+
ServerAddressNotSupported,
115+
InvalidReservedByte,
116+
InvalidDomainLength,
117+
NoAuthMethods,
118+
UnknownAuthMethod,
119+
InvalidUsername,
120+
InvalidPassword,
121+
FailedPasswordAuth,
122+
InvalidReservedBytes,
123+
InvalidFragmentID,
124+
WinUDP4GiBLimit
125+
)
43126
}
44127
}
45128

@@ -56,6 +139,29 @@ impl From<Error> for io::Error {
56139
from_error!(
57140
(InvalidSocksAddress, InvalidInput),
58141
(InvalidPortValue, InvalidInput),
142+
(InvalidResponseVersion, InvalidData),
143+
(UnknownResponseCode, Other),
144+
(ConnectionRefused, ConnectionRefused),
145+
(RejectedRequestID, PermissionDenied),
146+
(Socks4NoIPv6, InvalidInput),
147+
(MalformedDomain, InvalidData),
148+
(SOCKS5InvalidAddressType, InvalidData),
149+
(UnknownServerFailure, Other),
150+
(ServerRefusedByRuleSet, ConnectionRefused),
151+
(ServerNetworkUnreachable, ConnectionAborted),
152+
(ServerHostUnreachable, ConnectionAborted),
153+
(ServerTTLExpired, Interrupted),
154+
(ServerCmdNotSupported, Unsupported),
155+
(ServerAddressNotSupported, Unsupported),
156+
(InvalidReservedByte, Other),
157+
(InvalidDomainLength, InvalidInput),
158+
(NoAuthMethods, Unsupported),
159+
(UnknownAuthMethod, Unsupported),
160+
(InvalidUsername, InvalidInput),
161+
(InvalidPassword, InvalidInput),
162+
(FailedPasswordAuth, PermissionDenied),
163+
(InvalidReservedBytes, InvalidData),
164+
(InvalidFragmentID, InvalidData),
59165
(WinUDP4GiBLimit, InvalidInput)
60166
)
61167
}
@@ -70,6 +176,29 @@ impl std::fmt::Display for Error {
70176
Self::InvalidPortValue { addr, port } => {
71177
write!(f, "invalid port value '{port}' for '{addr}'")
72178
},
179+
Self::InvalidResponseVersion { version } => write!(f, "invalid response version '{version}'"),
180+
Self::UnknownResponseCode { code } => write!(f, "unknown response code '{code}'"),
181+
Self::ConnectionRefused { code } => write!(f, "connection refused or the request was rejected or failed '{code}'"),
182+
Self::RejectedRequestID { code } => write!(f, "request rejected because of idnetd with code '{code}'"),
183+
Self::Socks4NoIPv6 { addr } => write!(f, "SOCKS4 does not support IPv6 '{addr}'"),
184+
Self::MalformedDomain { err } => write!(f, "malformed domain {err}"),
185+
Self::SOCKS5InvalidAddressType { code } => write!(f, "invalid address type {code}"),
186+
Self::UnknownServerFailure { code } => write!(f, "unknown server failure {code}"),
187+
Self::ServerRefusedByRuleSet {} => write!(f, "connection not allowed by ruleset"),
188+
Self::ServerNetworkUnreachable {} => write!(f, "network unreachable"),
189+
Self::ServerHostUnreachable {} => write!(f, "host unreachable"),
190+
Self::ServerTTLExpired {} => write!(f, "TTL expired"),
191+
Self::ServerCmdNotSupported {} => write!(f, "command not supported"),
192+
Self::ServerAddressNotSupported {} => write!(f, "address kind not supported"),
193+
Self::InvalidReservedByte { byte } => write!(f, "invalid reserved byte '{byte}'"),
194+
Self::InvalidDomainLength { domain, length } => write!(f, "domain '{domain}' with length '{length}' is not between 1-255 inclusive"),
195+
Self::NoAuthMethods { method } => write!(f, "no acceptable authentication methods '{method}'"),
196+
Self::UnknownAuthMethod { method } => write!(f, "unknown authentication method '{method}'"),
197+
Self::InvalidUsername {username, length} => write!(f, "invalid username '{username}' with length '{length}'"),
198+
Self::InvalidPassword {password, length} => write!(f, "invalid password '{password:?}' with length '{length}'"),
199+
Self::FailedPasswordAuth {} => write!(f, "password authentication failed"),
200+
Self::InvalidReservedBytes { bytes } => write!(f, "invalid reserved bytes '{bytes}'"),
201+
Self::InvalidFragmentID {fid} => write!(f, "invalid fragment ID '{fid}'"),
73202
Self::WinUDP4GiBLimit {size} => write!(f, "tried to write '{size}' bytes to UDPSocket, but writev/readv has a 4 GiB limit on windows"),
74203
}
75204
}

src/lib.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub use v5::bind::Socks5Listener;
2424
#[cfg(feature = "udp")]
2525
pub use v5::udp::Socks5Datagram;
2626

27-
pub use error::Error;
27+
pub use error::{unwrap_io_to_socks2_error, Error};
2828

2929
mod error;
3030
#[cfg(feature = "udp")]
@@ -184,18 +184,14 @@ impl ToTargetAddr for &str {
184184
mod test {
185185
use super::*;
186186

187-
fn unwrap_io_error(e: &io::Error) -> Option<&Error> {
188-
e.get_ref().and_then(|i| i.downcast_ref())
189-
}
190-
191187
#[test]
192188
fn domains_to_target_addr() {
193189
assert_eq!(
194190
"localhost:80".to_target_addr().unwrap(),
195191
TargetAddr::Domain("localhost".to_owned(), 80)
196192
);
197193
assert_eq!(
198-
unwrap_io_error(&"localhost:".to_target_addr().unwrap_err()),
194+
unwrap_io_to_socks2_error(&"localhost:".to_target_addr().unwrap_err()),
199195
Some(&Error::InvalidPortValue {
200196
addr: String::new(),
201197
port: String::new()

src/v4.rs

Lines changed: 17 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::Error;
12
use byteorder::{BigEndian, ReadBytesExt};
23
use std::{
34
io::{self, Read},
@@ -11,41 +12,19 @@ fn read_response(socket: &mut TcpStream) -> io::Result<SocketAddrV4> {
1112
socket.read_exact(&mut response)?;
1213
let mut response = &response[..];
1314

14-
if response.read_u8()? != 0 {
15-
return Err(io::Error::new(
16-
io::ErrorKind::InvalidData,
17-
"invalid response version",
18-
));
15+
{
16+
let version = response.read_u8()?;
17+
if version != 0 {
18+
return Err(Error::InvalidResponseVersion { version }.into_io());
19+
}
1920
}
2021

2122
match response.read_u8()? {
2223
90 => {}
23-
91 => {
24-
return Err(io::Error::new(
25-
io::ErrorKind::Other,
26-
"request rejected or failed",
27-
))
28-
}
29-
92 => {
30-
return Err(io::Error::new(
31-
io::ErrorKind::PermissionDenied,
32-
"request rejected because SOCKS server cannot connect to \
33-
idnetd on the client",
34-
))
35-
}
36-
93 => {
37-
return Err(io::Error::new(
38-
io::ErrorKind::PermissionDenied,
39-
"request rejected because the client program and identd \
40-
report different user-ids",
41-
))
42-
}
43-
_ => {
44-
return Err(io::Error::new(
45-
io::ErrorKind::InvalidData,
46-
"invalid response code",
47-
))
48-
}
24+
91 => return Err(Error::ConnectionRefused { code: 91 }.into_io()),
25+
92 => return Err(Error::RejectedRequestID { code: 92 }.into_io()),
26+
93 => return Err(Error::RejectedRequestID { code: 93 }.into_io()),
27+
code => return Err(Error::UnknownResponseCode { code }.into_io()),
4928
}
5029

5130
let port = response.read_u16::<BigEndian>()?;
@@ -58,7 +37,7 @@ fn read_response(socket: &mut TcpStream) -> io::Result<SocketAddrV4> {
5837
pub mod client {
5938
use crate::{
6039
v4::{read_response, NULL_BYTE},
61-
TargetAddr, ToTargetAddr,
40+
Error, TargetAddr, ToTargetAddr,
6241
};
6342
use byteorder::{BigEndian, WriteBytesExt};
6443
use std::{
@@ -85,7 +64,7 @@ pub mod client {
8564
/// locally and passing a `TargetAddr::Ip`.
8665
///
8766
/// # Errors
88-
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
67+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*?)`
8968
pub fn connect<T, U>(proxy: T, target: &U, userid: &str) -> io::Result<Self>
9069
where
9170
T: ToSocketAddrs,
@@ -115,11 +94,8 @@ pub mod client {
11594
TargetAddr::Ip(addr) => {
11695
let addr = match addr {
11796
SocketAddr::V4(addr) => addr,
118-
SocketAddr::V6(_) => {
119-
return Err(io::Error::new(
120-
io::ErrorKind::InvalidInput,
121-
"SOCKS4 does not support IPv6",
122-
));
97+
SocketAddr::V6(addr) => {
98+
return Err(Error::Socks4NoIPv6 { addr }.into_io());
12399
}
124100
};
125101
packet.write_u16::<BigEndian>(addr.port())?;
@@ -223,7 +199,7 @@ pub mod bind {
223199
/// `target`.
224200
///
225201
/// # Errors
226-
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
202+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*?)`
227203
pub fn bind<T, U>(proxy: T, target: &U, userid: &str) -> io::Result<Self>
228204
where
229205
T: ToSocketAddrs,
@@ -238,7 +214,7 @@ pub mod bind {
238214
/// connection to it.
239215
///
240216
/// # Errors
241-
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
217+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*?)`
242218
pub fn proxy_addr(&self) -> io::Result<SocketAddr> {
243219
if self.0.proxy_addr.ip().octets() == [0, 0, 0, 0] {
244220
let port = self.0.proxy_addr.port();
@@ -260,7 +236,7 @@ pub mod bind {
260236
/// before this method is called.
261237
///
262238
/// # Errors
263-
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
239+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*?)`
264240
pub fn accept(mut self) -> io::Result<Socks4Stream> {
265241
self.0.proxy_addr = read_response(&mut self.0.socket)?;
266242
Ok(self.0)

0 commit comments

Comments
 (0)