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