electrum_client/socks/
v4.rs

1use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
2use std::io::{self, Read, Write};
3use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6, TcpStream, ToSocketAddrs};
4
5use super::{TargetAddr, ToTargetAddr};
6
7fn read_response(socket: &mut TcpStream) -> io::Result<SocketAddrV4> {
8    let mut response = [0u8; 8];
9    socket.read_exact(&mut response)?;
10    let mut response = &response[..];
11
12    if response.read_u8()? != 0 {
13        return Err(io::Error::new(
14            io::ErrorKind::InvalidData,
15            "invalid response version",
16        ));
17    }
18
19    match response.read_u8()? {
20        90 => {}
21        91 => {
22            return Err(io::Error::new(
23                io::ErrorKind::Other,
24                "request rejected or failed",
25            ))
26        }
27        92 => {
28            return Err(io::Error::new(
29                io::ErrorKind::PermissionDenied,
30                "request rejected because SOCKS server cannot connect to \
31                                       idnetd on the client",
32            ))
33        }
34        93 => {
35            return Err(io::Error::new(
36                io::ErrorKind::PermissionDenied,
37                "request rejected because the client program and identd \
38                                       report different user-ids",
39            ))
40        }
41        _ => {
42            return Err(io::Error::new(
43                io::ErrorKind::InvalidData,
44                "invalid response code",
45            ))
46        }
47    }
48
49    let port = response.read_u16::<BigEndian>()?;
50    let ip = Ipv4Addr::from(response.read_u32::<BigEndian>()?);
51
52    Ok(SocketAddrV4::new(ip, port))
53}
54
55/// A SOCKS4 client.
56#[derive(Debug)]
57pub struct Socks4Stream {
58    socket: TcpStream,
59    proxy_addr: SocketAddrV4,
60}
61
62impl Socks4Stream {
63    /// Connects to a target server through a SOCKS4 proxy.
64    ///
65    /// # Note
66    ///
67    /// If `target` is a `TargetAddr::Domain`, the domain name will be forwarded
68    /// to the proxy server using the SOCKS4A protocol extension. If the proxy
69    /// server does not support SOCKS4A, consider performing the DNS lookup
70    /// locally and passing a `TargetAddr::Ip`.
71    pub fn connect<T, U>(proxy: T, target: U, userid: &str) -> io::Result<Socks4Stream>
72    where
73        T: ToSocketAddrs,
74        U: ToTargetAddr,
75    {
76        Self::connect_raw(1, proxy, target, userid)
77    }
78
79    fn connect_raw<T, U>(command: u8, proxy: T, target: U, userid: &str) -> io::Result<Socks4Stream>
80    where
81        T: ToSocketAddrs,
82        U: ToTargetAddr,
83    {
84        let mut socket = TcpStream::connect(proxy)?;
85
86        let target = target.to_target_addr()?;
87
88        let mut packet = vec![];
89        let _ = packet.write_u8(4); // version
90        let _ = packet.write_u8(command); // command code
91        match target.to_target_addr()? {
92            TargetAddr::Ip(addr) => {
93                let addr = match addr {
94                    SocketAddr::V4(addr) => addr,
95                    SocketAddr::V6(_) => {
96                        return Err(io::Error::new(
97                            io::ErrorKind::InvalidInput,
98                            "SOCKS4 does not support IPv6",
99                        ));
100                    }
101                };
102                let _ = packet.write_u16::<BigEndian>(addr.port());
103                let _ = packet.write_u32::<BigEndian>((*addr.ip()).into());
104                let _ = packet.write_all(userid.as_bytes());
105                let _ = packet.write_u8(0);
106            }
107            TargetAddr::Domain(ref host, port) => {
108                let _ = packet.write_u16::<BigEndian>(port);
109                let _ = packet.write_u32::<BigEndian>(Ipv4Addr::new(0, 0, 0, 1).into());
110                let _ = packet.write_all(userid.as_bytes());
111                let _ = packet.write_u8(0);
112                packet.extend(host.as_bytes());
113                let _ = packet.write_u8(0);
114            }
115        }
116
117        socket.write_all(&packet)?;
118        let proxy_addr = read_response(&mut socket)?;
119
120        Ok(Socks4Stream { socket, proxy_addr })
121    }
122
123    /// Returns the proxy-side address of the connection between the proxy and
124    /// target server.
125    pub fn proxy_addr(&self) -> SocketAddrV4 {
126        self.proxy_addr
127    }
128
129    /// Returns a shared reference to the inner `TcpStream`.
130    pub fn get_ref(&self) -> &TcpStream {
131        &self.socket
132    }
133
134    /// Returns a mutable reference to the inner `TcpStream`.
135    pub fn get_mut(&mut self) -> &mut TcpStream {
136        &mut self.socket
137    }
138
139    /// Consumes the `Socks4Stream`, returning the inner `TcpStream`.
140    pub fn into_inner(self) -> TcpStream {
141        self.socket
142    }
143}
144
145impl Read for Socks4Stream {
146    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
147        self.socket.read(buf)
148    }
149}
150
151impl Read for &Socks4Stream {
152    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
153        (&self.socket).read(buf)
154    }
155}
156
157impl Write for Socks4Stream {
158    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
159        self.socket.write(buf)
160    }
161
162    fn flush(&mut self) -> io::Result<()> {
163        self.socket.flush()
164    }
165}
166
167impl Write for &Socks4Stream {
168    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
169        (&self.socket).write(buf)
170    }
171
172    fn flush(&mut self) -> io::Result<()> {
173        (&self.socket).flush()
174    }
175}
176
177/// A SOCKS4 BIND client.
178#[derive(Debug)]
179pub struct Socks4Listener(Socks4Stream);
180
181impl Socks4Listener {
182    /// Initiates a BIND request to the specified proxy.
183    ///
184    /// The proxy will filter incoming connections based on the value of
185    /// `target`.
186    pub fn bind<T, U>(proxy: T, target: U, userid: &str) -> io::Result<Socks4Listener>
187    where
188        T: ToSocketAddrs,
189        U: ToTargetAddr,
190    {
191        Socks4Stream::connect_raw(2, proxy, target, userid).map(Socks4Listener)
192    }
193
194    /// The address of the proxy-side TCP listener.
195    ///
196    /// This should be forwarded to the remote process, which should open a
197    /// connection to it.
198    pub fn proxy_addr(&self) -> io::Result<SocketAddr> {
199        if self.0.proxy_addr.ip().octets() != [0, 0, 0, 0] {
200            Ok(SocketAddr::V4(self.0.proxy_addr()))
201        } else {
202            let port = self.0.proxy_addr.port();
203            let peer = match self.0.socket.peer_addr()? {
204                SocketAddr::V4(addr) => SocketAddr::V4(SocketAddrV4::new(*addr.ip(), port)),
205                SocketAddr::V6(addr) => SocketAddr::V6(SocketAddrV6::new(*addr.ip(), port, 0, 0)),
206            };
207            Ok(peer)
208        }
209    }
210
211    /// Waits for the remote process to connect to the proxy server.
212    ///
213    /// The value of `proxy_addr` should be forwarded to the remote process
214    /// before this method is called.
215    pub fn accept(mut self) -> io::Result<Socks4Stream> {
216        self.0.proxy_addr = read_response(&mut self.0.socket)?;
217        Ok(self.0)
218    }
219}
220
221#[cfg(test)]
222mod test {
223    use std::io::{Read, Write};
224    use std::net::{SocketAddr, SocketAddrV4, TcpStream, ToSocketAddrs};
225
226    use super::*;
227
228    fn google_ip() -> SocketAddrV4 {
229        "google.com:80"
230            .to_socket_addrs()
231            .unwrap()
232            .filter_map(|a| match a {
233                SocketAddr::V4(a) => Some(a),
234                SocketAddr::V6(_) => None,
235            })
236            .next()
237            .unwrap()
238    }
239
240    #[test]
241    #[ignore]
242    fn google() {
243        let mut socket = Socks4Stream::connect("127.0.0.1:1080", google_ip(), "").unwrap();
244
245        socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
246        let mut result = vec![];
247        socket.read_to_end(&mut result).unwrap();
248
249        println!("{}", String::from_utf8_lossy(&result));
250        assert!(result.starts_with(b"HTTP/1.0"));
251        assert!(result.ends_with(b"</HTML>\r\n") || result.ends_with(b"</html>"));
252    }
253
254    #[test]
255    #[ignore] // dante doesn't support SOCKS4A
256    fn google_dns() {
257        let mut socket = Socks4Stream::connect("127.0.0.1:8080", "google.com:80", "").unwrap();
258
259        socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
260        let mut result = vec![];
261        socket.read_to_end(&mut result).unwrap();
262
263        println!("{}", String::from_utf8_lossy(&result));
264        assert!(result.starts_with(b"HTTP/1.0"));
265        assert!(result.ends_with(b"</HTML>\r\n") || result.ends_with(b"</html>"));
266    }
267
268    #[test]
269    #[ignore]
270    fn bind() {
271        // First figure out our local address that we'll be connecting from
272        let socket = Socks4Stream::connect("127.0.0.1:1080", google_ip(), "").unwrap();
273        let addr = socket.proxy_addr();
274
275        let listener = Socks4Listener::bind("127.0.0.1:1080", addr, "").unwrap();
276        let addr = listener.proxy_addr().unwrap();
277        let mut end = TcpStream::connect(addr).unwrap();
278        let mut conn = listener.accept().unwrap();
279        conn.write_all(b"hello world").unwrap();
280        drop(conn);
281        let mut result = vec![];
282        end.read_to_end(&mut result).unwrap();
283        assert_eq!(result, b"hello world");
284    }
285}