Skip to content

Commit 78c7a3e

Browse files
committed
Added Errors sections to docs and started replacing all string errors
1 parent ed67a0e commit 78c7a3e

File tree

5 files changed

+181
-36
lines changed

5 files changed

+181
-36
lines changed

src/error.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// TODO: Replace all string errors with this!
2+
3+
use std::{fmt::Formatter, io};
4+
5+
/// Errors from socks2
6+
#[derive(Debug)]
7+
#[non_exhaustive]
8+
#[allow(missing_docs)]
9+
pub enum Error {
10+
// TODO: Add docs for all errors?
11+
InvalidSocksAddress { addr: String },
12+
InvalidPortValue { addr: String, port: String },
13+
WinUDP4GiBLimit { size: usize },
14+
}
15+
16+
// impl Error {
17+
// pub(crate) fn into_io(self) -> io::Error {
18+
// self.into()
19+
// }
20+
// }
21+
22+
impl PartialEq for Error {
23+
fn eq(&self, other: &Self) -> bool {
24+
macro_rules! peq {
25+
($($s:ident),+) => {
26+
match (self, other) {
27+
$((Self::$s { .. }, Self::$s { .. }) => true,)+
28+
_ => false,
29+
}
30+
};
31+
}
32+
33+
peq!(InvalidSocksAddress, InvalidPortValue, WinUDP4GiBLimit)
34+
}
35+
}
36+
37+
impl From<Error> for io::Error {
38+
fn from(value: Error) -> Self {
39+
macro_rules! from_error {
40+
($(($s:ident, $k:ident)),+) => {
41+
match value {
42+
$(Error::$s { .. } => io::Error::new(io::ErrorKind::$k, value),)+
43+
}
44+
};
45+
}
46+
47+
from_error!(
48+
(InvalidSocksAddress, InvalidInput),
49+
(InvalidPortValue, InvalidInput),
50+
(WinUDP4GiBLimit, InvalidInput)
51+
)
52+
}
53+
}
54+
55+
impl std::error::Error for Error {}
56+
57+
impl std::fmt::Display for Error {
58+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
59+
match self {
60+
Self::InvalidSocksAddress { addr } => write!(f, "invalid socket address '{addr}'"),
61+
Self::InvalidPortValue { addr, port } => {
62+
write!(f, "invalid port value '{port}' for '{addr}'")
63+
},
64+
Self::WinUDP4GiBLimit {size} => write!(f, "tried to write '{size}' bytes to UDPSocket, but writev/readv has a 4 GiB limit on windows"),
65+
}
66+
}
67+
}

src/io_ext.rs

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,24 @@ mod imp {
6666
#[cfg(windows)]
6767
mod imp {
6868
use super::{io, IOVecExt, UdpSocket, VEC_SIZE};
69+
use crate::Error;
6970
use std::{os::windows::io::AsRawSocket, ptr};
7071
use windows_sys::Win32::Networking::WinSock::{WSARecv, WSASend, WSABUF};
7172

7273
impl IOVecExt for UdpSocket {
7374
fn writev(&self, bufs: [&[u8]; VEC_SIZE]) -> io::Result<usize> {
7475
let bufs_lens: [u32; VEC_SIZE] = [
75-
bufs[0].len().try_into().map_err(|_e| {
76-
io::Error::new(
77-
io::ErrorKind::InvalidInput,
78-
"can only write up to 4294967295 bytes (4GB) on a UDPSocket using writev",
79-
)
76+
bufs[0].len().try_into().map_err(|_| {
77+
Error::WinUDP4GiBLimit {
78+
size: bufs[0].len(),
79+
}
80+
.into()
8081
})?,
81-
bufs[1].len().try_into().map_err(|_e| {
82-
io::Error::new(
83-
io::ErrorKind::InvalidInput,
84-
"can only write up to 4294967295 bytes (4GB) on a UDPSocket using writev",
85-
)
82+
bufs[1].len().try_into().map_err(|_| {
83+
Error::WinUDP4GiBLimit {
84+
size: bufs[1].len(),
85+
}
86+
.into()
8687
})?,
8788
];
8889

@@ -125,17 +126,17 @@ mod imp {
125126

126127
fn readv(&self, bufs: [&mut [u8]; VEC_SIZE]) -> io::Result<usize> {
127128
let bufs_lens: [u32; VEC_SIZE] = [
128-
bufs[0].len().try_into().map_err(|_e| {
129-
io::Error::new(
130-
io::ErrorKind::InvalidInput,
131-
"can only write up to 4294967295 bytes (4GB) on a UDPSocket using readv",
132-
)
129+
bufs[0].len().try_into().map_err(|_| {
130+
Error::WinUDP4GiBLimit {
131+
size: bufs[0].len(),
132+
}
133+
.into()
133134
})?,
134135
bufs[1].len().try_into().map_err(|_e| {
135-
io::Error::new(
136-
io::ErrorKind::InvalidInput,
137-
"can only write up to 4294967295 bytes (4GB) on a UDPSocket using readv",
138-
)
136+
Error::WinUDP4GiBLimit {
137+
size: bufs[1].len(),
138+
}
139+
.into()
139140
})?,
140141
];
141142

src/lib.rs

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! SOCKS proxy clients
2+
23
#![deny(clippy::all)]
3-
/*** TODO ***/
4-
#![allow(clippy::missing_errors_doc)]
5-
// #![deny(clippy::unwrap_used)] // TODO: This library should try to never panic!
6-
/*** END TODO ***/
4+
#![deny(clippy::unwrap_used)]
5+
#![deny(clippy::expect_used)]
6+
#![warn(missing_docs)]
77

88
use std::{
99
io,
@@ -24,6 +24,9 @@ pub use v5::bind::Socks5Listener;
2424
#[cfg(feature = "udp")]
2525
pub use v5::udp::Socks5Datagram;
2626

27+
pub use error::Error;
28+
29+
mod error;
2730
#[cfg(feature = "udp")]
2831
mod io_ext;
2932
#[cfg(any(feature = "client", feature = "bind"))]
@@ -80,6 +83,9 @@ impl Iterator for Iter {
8083
/// A trait for objects that can be converted to `TargetAddr`.
8184
pub trait ToTargetAddr {
8285
/// Converts the value of `self` to a `TargetAddr`.
86+
///
87+
/// # Errors
88+
/// - `std::io::ErrorKind::*`
8389
fn to_target_addr(&self) -> io::Result<TargetAddr>;
8490
}
8591

@@ -148,26 +154,56 @@ impl ToTargetAddr for &str {
148154
// split the string by ':' and convert the second part to u16
149155
let mut parts_iter = self.rsplitn(2, ':');
150156
let Some(port_str) = parts_iter.next() else {
151-
return Err(io::Error::new(
152-
io::ErrorKind::InvalidInput,
153-
"invalid socket address",
154-
));
157+
return Err(Error::InvalidSocksAddress {
158+
addr: (*self).to_string(),
159+
}
160+
.into());
155161
};
156162

157163
let Some(host) = parts_iter.next() else {
158-
return Err(io::Error::new(
159-
io::ErrorKind::InvalidInput,
160-
"invalid socket address",
161-
));
164+
return Err(Error::InvalidSocksAddress {
165+
addr: (*self).to_string(),
166+
}
167+
.into());
162168
};
163169

164170
let Some(port): Option<u16> = port_str.parse().ok() else {
165-
return Err(io::Error::new(
166-
io::ErrorKind::InvalidInput,
167-
"invalid port value",
168-
));
171+
return Err(Error::InvalidPortValue {
172+
addr: (*self).to_string(),
173+
port: port_str.to_string(),
174+
}
175+
.into());
169176
};
170177

171178
(host, port).to_target_addr()
172179
}
173180
}
181+
182+
#[cfg(test)]
183+
#[allow(clippy::unwrap_used)]
184+
mod test {
185+
use super::*;
186+
187+
fn unwrap_io_error(e: &io::Error) -> Option<&Error> {
188+
e.get_ref().and_then(|i| i.downcast_ref())
189+
}
190+
191+
#[test]
192+
fn domains_to_target_addr() {
193+
assert_eq!(
194+
"localhost:80".to_target_addr().unwrap(),
195+
TargetAddr::Domain("localhost".to_owned(), 80)
196+
);
197+
assert_eq!(
198+
unwrap_io_error(&"localhost:".to_target_addr().unwrap_err()),
199+
Some(&Error::InvalidPortValue {
200+
addr: String::new(),
201+
port: String::new()
202+
})
203+
);
204+
assert_eq!(
205+
"github.com:443".to_target_addr().unwrap(),
206+
TargetAddr::Domain("github.com".to_owned(), 443)
207+
);
208+
}
209+
}

src/v4.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ pub mod client {
8383
/// to the proxy server using the SOCKS4A protocol extension. If the proxy
8484
/// server does not support SOCKS4A, consider performing the DNS lookup
8585
/// locally and passing a `TargetAddr::Ip`.
86+
///
87+
/// # Errors
88+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
8689
pub fn connect<T, U>(proxy: T, target: &U, userid: &str) -> io::Result<Self>
8790
where
8891
T: ToSocketAddrs,
@@ -218,6 +221,9 @@ pub mod bind {
218221
///
219222
/// The proxy will filter incoming connections based on the value of
220223
/// `target`.
224+
///
225+
/// # Errors
226+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
221227
pub fn bind<T, U>(proxy: T, target: &U, userid: &str) -> io::Result<Self>
222228
where
223229
T: ToSocketAddrs,
@@ -230,6 +236,9 @@ pub mod bind {
230236
///
231237
/// This should be forwarded to the remote process, which should open a
232238
/// connection to it.
239+
///
240+
/// # Errors
241+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
233242
pub fn proxy_addr(&self) -> io::Result<SocketAddr> {
234243
if self.0.proxy_addr.ip().octets() == [0, 0, 0, 0] {
235244
let port = self.0.proxy_addr.port();
@@ -249,6 +258,9 @@ pub mod bind {
249258
///
250259
/// The value of `proxy_addr` should be forwarded to the remote process
251260
/// before this method is called.
261+
///
262+
/// # Errors
263+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
252264
pub fn accept(mut self) -> io::Result<Socks4Stream> {
253265
self.0.proxy_addr = read_response(&mut self.0.socket)?;
254266
Ok(self.0)
@@ -257,6 +269,7 @@ pub mod bind {
257269
}
258270

259271
#[cfg(test)]
272+
#[allow(clippy::unwrap_used)]
260273
mod test {
261274
use std::{
262275
io::{Read, Write},

src/v5.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,9 @@ pub mod client {
168168

169169
impl Socks5Stream {
170170
/// Connects to a target server through a SOCKS5 proxy.
171+
///
172+
/// # Errors
173+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
171174
pub fn connect<T, U>(proxy: T, target: &U) -> io::Result<Self>
172175
where
173176
T: ToSocketAddrs,
@@ -178,6 +181,9 @@ pub mod client {
178181

179182
/// Connects to a target server through a SOCKS5 proxy using given
180183
/// username and password.
184+
///
185+
/// # Errors
186+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
181187
pub fn connect_with_password<T, U>(
182188
proxy: T,
183189
target: &U,
@@ -386,6 +392,9 @@ pub mod bind {
386392
///
387393
/// The proxy will filter incoming connections based on the value of
388394
/// `target`.
395+
///
396+
/// # Errors
397+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
389398
pub fn bind<T, U>(proxy: T, target: &U) -> io::Result<Self>
390399
where
391400
T: ToSocketAddrs,
@@ -398,6 +407,9 @@ pub mod bind {
398407
///
399408
/// The proxy will filter incoming connections based on the value of
400409
/// `target`.
410+
///
411+
/// # Errors
412+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
401413
pub fn bind_with_password<T, U>(
402414
proxy: T,
403415
target: &U,
@@ -425,6 +437,9 @@ pub mod bind {
425437
///
426438
/// The value of `proxy_addr` should be forwarded to the remote process
427439
/// before this method is called.
440+
///
441+
/// # Errors
442+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
428443
pub fn accept(mut self) -> io::Result<Socks5Stream> {
429444
self.0.proxy_addr = read_response(&mut self.0.socket)?;
430445
Ok(self.0)
@@ -457,16 +472,23 @@ pub mod udp {
457472
impl Socks5Datagram {
458473
/// Creates a UDP socket bound to the specified address which will have its
459474
/// traffic routed through the specified proxy.
475+
///
476+
/// # Errors
477+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
460478
pub fn bind<T, U>(proxy: T, addr: U) -> io::Result<Self>
461479
where
462480
T: ToSocketAddrs,
463481
U: ToSocketAddrs,
464482
{
465483
Self::bind_internal(proxy, addr, &Authentication::None)
466484
}
485+
467486
/// Creates a UDP socket bound to the specified address which will have its
468487
/// traffic routed through the specified proxy. The given username and password
469488
/// is used to authenticate to the SOCKS proxy.
489+
///
490+
/// # Errors
491+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
470492
pub fn bind_with_password<T, U>(
471493
proxy: T,
472494
addr: U,
@@ -503,10 +525,12 @@ pub mod udp {
503525
/// Like `UdpSocket::send_to`.
504526
///
505527
/// # Note
506-
///
507528
/// The SOCKS protocol inserts a header at the beginning of the message. The
508529
/// header will be 10 bytes for an IPv4 address, 22 bytes for an IPv6
509530
/// address, and 7 bytes plus the length of the domain for a domain address.
531+
///
532+
/// # Errors
533+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
510534
pub fn send_to<A>(&self, buf: &[u8], addr: &A) -> io::Result<usize>
511535
where
512536
A: ToTargetAddr,
@@ -522,6 +546,9 @@ pub mod udp {
522546
}
523547

524548
/// Like `UdpSocket::recv_from`.
549+
///
550+
/// # Errors
551+
/// - `io::Error(std::io::ErrorKind::*, socks2::Error::*)`
525552
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, TargetAddr)> {
526553
let mut header = [0; MAX_ADDR_LEN + 3];
527554
let len = self.socket.readv([&mut header, buf])?;
@@ -574,6 +601,7 @@ pub mod udp {
574601
}
575602

576603
#[cfg(test)]
604+
#[allow(clippy::unwrap_used)]
577605
mod test {
578606
use std::{
579607
io::{Read, Write},

0 commit comments

Comments
 (0)