Skip to main content

electrum_client/socks/
v5.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
4use std::cmp;
5use std::io::{self, Read, Write};
6use std::net::{
7    Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, TcpStream, ToSocketAddrs, UdpSocket,
8};
9use std::ptr;
10use std::time::Duration;
11
12use super::writev::WritevExt;
13use super::{TargetAddr, ToTargetAddr};
14
15const MAX_ADDR_LEN: usize = 260;
16
17fn read_addr<R: Read>(socket: &mut R) -> io::Result<TargetAddr> {
18    match socket.read_u8()? {
19        1 => {
20            let ip = Ipv4Addr::from(socket.read_u32::<BigEndian>()?);
21            let port = socket.read_u16::<BigEndian>()?;
22            Ok(TargetAddr::Ip(SocketAddr::V4(SocketAddrV4::new(ip, port))))
23        }
24        3 => {
25            let len = socket.read_u8()?;
26            let mut domain = vec![0; len as usize];
27            socket.read_exact(&mut domain)?;
28            let domain = String::from_utf8(domain)
29                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
30            let port = socket.read_u16::<BigEndian>()?;
31            Ok(TargetAddr::Domain(domain, port))
32        }
33        4 => {
34            let mut ip = [0; 16];
35            socket.read_exact(&mut ip)?;
36            let ip = Ipv6Addr::from(ip);
37            let port = socket.read_u16::<BigEndian>()?;
38            Ok(TargetAddr::Ip(SocketAddr::V6(SocketAddrV6::new(
39                ip, port, 0, 0,
40            ))))
41        }
42        _ => Err(io::Error::other("unsupported address type")),
43    }
44}
45
46fn read_response(socket: &mut TcpStream) -> io::Result<TargetAddr> {
47    if socket.read_u8()? != 5 {
48        return Err(io::Error::new(
49            io::ErrorKind::InvalidData,
50            "invalid response version",
51        ));
52    }
53
54    match socket.read_u8()? {
55        0 => {}
56        1 => return Err(io::Error::other("general SOCKS server failure")),
57        2 => return Err(io::Error::other("connection not allowed by ruleset")),
58        3 => return Err(io::Error::other("network unreachable")),
59        4 => return Err(io::Error::other("host unreachable")),
60        5 => return Err(io::Error::other("connection refused")),
61        6 => return Err(io::Error::other("TTL expired")),
62        7 => return Err(io::Error::other("command not supported")),
63        8 => return Err(io::Error::other("address kind not supported")),
64        _ => return Err(io::Error::other("unknown error")),
65    }
66
67    if socket.read_u8()? != 0 {
68        return Err(io::Error::new(
69            io::ErrorKind::InvalidData,
70            "invalid reserved byte",
71        ));
72    }
73
74    read_addr(socket)
75}
76
77fn write_addr(mut packet: &mut [u8], target: &TargetAddr) -> io::Result<usize> {
78    let start_len = packet.len();
79    match *target {
80        TargetAddr::Ip(SocketAddr::V4(addr)) => {
81            packet.write_u8(1).unwrap();
82            packet.write_u32::<BigEndian>((*addr.ip()).into()).unwrap();
83            packet.write_u16::<BigEndian>(addr.port()).unwrap();
84        }
85        TargetAddr::Ip(SocketAddr::V6(addr)) => {
86            packet.write_u8(4).unwrap();
87            packet.write_all(&addr.ip().octets()).unwrap();
88            packet.write_u16::<BigEndian>(addr.port()).unwrap();
89        }
90        TargetAddr::Domain(ref domain, port) => {
91            packet.write_u8(3).unwrap();
92            if domain.len() > u8::MAX as usize {
93                return Err(io::Error::new(
94                    io::ErrorKind::InvalidInput,
95                    "domain name too long",
96                ));
97            }
98            packet.write_u8(domain.len() as u8).unwrap();
99            packet.write_all(domain.as_bytes()).unwrap();
100            packet.write_u16::<BigEndian>(port).unwrap();
101        }
102    }
103
104    Ok(start_len - packet.len())
105}
106
107/// Authentication methods
108#[derive(Debug)]
109enum Authentication<'a> {
110    Password {
111        username: &'a str,
112        password: &'a str,
113    },
114    None,
115}
116
117impl Authentication<'_> {
118    fn id(&self) -> u8 {
119        match *self {
120            Authentication::Password { .. } => 2,
121            Authentication::None => 0,
122        }
123    }
124
125    fn is_no_auth(&self) -> bool {
126        matches!(*self, Authentication::None)
127    }
128}
129
130/// A SOCKS5 client.
131#[derive(Debug)]
132pub struct Socks5Stream {
133    socket: TcpStream,
134    proxy_addr: TargetAddr,
135}
136
137impl Socks5Stream {
138    /// Connects to a target server through a SOCKS5 proxy.
139    pub fn connect<T, U>(proxy: T, target: U, timeout: Option<Duration>) -> io::Result<Socks5Stream>
140    where
141        T: ToSocketAddrs,
142        U: ToTargetAddr,
143    {
144        Self::connect_raw(1, proxy, target, &Authentication::None, timeout)
145    }
146
147    /// Connects to a target server through a SOCKS5 proxy using given
148    /// username and password.
149    pub fn connect_with_password<T, U>(
150        proxy: T,
151        target: U,
152        username: &str,
153        password: &str,
154        timeout: Option<Duration>,
155    ) -> io::Result<Socks5Stream>
156    where
157        T: ToSocketAddrs,
158        U: ToTargetAddr,
159    {
160        let auth = Authentication::Password { username, password };
161        Self::connect_raw(1, proxy, target, &auth, timeout)
162    }
163
164    fn connect_raw<T, U>(
165        command: u8,
166        proxy: T,
167        target: U,
168        auth: &Authentication,
169        timeout: Option<Duration>,
170    ) -> io::Result<Socks5Stream>
171    where
172        T: ToSocketAddrs,
173        U: ToTargetAddr,
174    {
175        let mut socket = if let Some(timeout) = timeout {
176            let addr = proxy.to_socket_addrs()?.next().unwrap();
177            TcpStream::connect_timeout(&addr, timeout)?
178        } else {
179            TcpStream::connect(proxy)?
180        };
181
182        socket.set_read_timeout(timeout)?;
183        socket.set_write_timeout(timeout)?;
184
185        let target = target.to_target_addr()?;
186
187        let packet_len = if auth.is_no_auth() { 3 } else { 4 };
188        let packet = [
189            5,                                     // protocol version
190            if auth.is_no_auth() { 1 } else { 2 }, // method count
191            auth.id(),                             // method
192            0,                                     // no auth (always offered)
193        ];
194        socket.write_all(&packet[..packet_len])?;
195
196        let mut buf = [0; 2];
197        socket.read_exact(&mut buf)?;
198        let response_version = buf[0];
199        let selected_method = buf[1];
200
201        if response_version != 5 {
202            return Err(io::Error::new(
203                io::ErrorKind::InvalidData,
204                "invalid response version",
205            ));
206        }
207
208        if selected_method == 0xff {
209            return Err(io::Error::other("no acceptable auth methods"));
210        }
211
212        if selected_method != auth.id() && selected_method != Authentication::None.id() {
213            return Err(io::Error::other("unknown auth method"));
214        }
215
216        match *auth {
217            Authentication::Password { username, password } if selected_method == auth.id() => {
218                Self::password_authentication(&mut socket, username, password)?
219            }
220            _ => (),
221        }
222
223        let mut packet = [0; MAX_ADDR_LEN + 3];
224        packet[0] = 5; // protocol version
225        packet[1] = command; // command
226        packet[2] = 0; // reserved
227        let len = write_addr(&mut packet[3..], &target)?;
228        socket.write_all(&packet[..len + 3])?;
229
230        let proxy_addr = read_response(&mut socket)?;
231
232        Ok(Socks5Stream { socket, proxy_addr })
233    }
234
235    fn password_authentication(
236        socket: &mut TcpStream,
237        username: &str,
238        password: &str,
239    ) -> io::Result<()> {
240        if username.is_empty() || username.len() > 255 {
241            return Err(io::Error::new(
242                io::ErrorKind::InvalidInput,
243                "invalid username",
244            ));
245        };
246        if password.is_empty() || password.len() > 255 {
247            return Err(io::Error::new(
248                io::ErrorKind::InvalidInput,
249                "invalid password",
250            ));
251        }
252
253        let mut packet = [0; 515];
254        let packet_size = 3 + username.len() + password.len();
255        packet[0] = 1; // version
256        packet[1] = username.len() as u8;
257        packet[2..2 + username.len()].copy_from_slice(username.as_bytes());
258        packet[2 + username.len()] = password.len() as u8;
259        packet[3 + username.len()..packet_size].copy_from_slice(password.as_bytes());
260        socket.write_all(&packet[..packet_size])?;
261
262        let mut buf = [0; 2];
263        socket.read_exact(&mut buf)?;
264        if buf[0] != 1 {
265            return Err(io::Error::new(
266                io::ErrorKind::InvalidData,
267                "invalid response version",
268            ));
269        }
270        if buf[1] != 0 {
271            return Err(io::Error::new(
272                io::ErrorKind::PermissionDenied,
273                "password authentication failed",
274            ));
275        }
276
277        Ok(())
278    }
279
280    /// Returns the proxy-side address of the connection between the proxy and
281    /// target server.
282    pub fn proxy_addr(&self) -> &TargetAddr {
283        &self.proxy_addr
284    }
285
286    /// Returns a shared reference to the inner `TcpStream`.
287    pub fn get_ref(&self) -> &TcpStream {
288        &self.socket
289    }
290
291    /// Returns a mutable reference to the inner `TcpStream`.
292    pub fn get_mut(&mut self) -> &mut TcpStream {
293        &mut self.socket
294    }
295
296    /// Consumes the `Socks5Stream`, returning the inner `TcpStream`.
297    pub fn into_inner(self) -> TcpStream {
298        self.socket
299    }
300}
301
302impl Read for Socks5Stream {
303    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
304        self.socket.read(buf)
305    }
306}
307
308impl Read for &Socks5Stream {
309    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
310        (&self.socket).read(buf)
311    }
312}
313
314impl Write for Socks5Stream {
315    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
316        self.socket.write(buf)
317    }
318
319    fn flush(&mut self) -> io::Result<()> {
320        self.socket.flush()
321    }
322}
323
324impl Write for &Socks5Stream {
325    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
326        (&self.socket).write(buf)
327    }
328
329    fn flush(&mut self) -> io::Result<()> {
330        (&self.socket).flush()
331    }
332}
333
334/// A SOCKS5 BIND client.
335#[derive(Debug)]
336pub struct Socks5Listener(Socks5Stream);
337
338impl Socks5Listener {
339    /// Initiates a BIND request to the specified proxy.
340    ///
341    /// The proxy will filter incoming connections based on the value of
342    /// `target`.
343    pub fn bind<T, U>(proxy: T, target: U, timeout: Option<Duration>) -> io::Result<Socks5Listener>
344    where
345        T: ToSocketAddrs,
346        U: ToTargetAddr,
347    {
348        Socks5Stream::connect_raw(2, proxy, target, &Authentication::None, timeout)
349            .map(Socks5Listener)
350    }
351    /// Initiates a BIND request to the specified proxy using given username
352    /// and password.
353    ///
354    /// The proxy will filter incoming connections based on the value of
355    /// `target`.
356    pub fn bind_with_password<T, U>(
357        proxy: T,
358        target: U,
359        username: &str,
360        password: &str,
361        timeout: Option<Duration>,
362    ) -> io::Result<Socks5Listener>
363    where
364        T: ToSocketAddrs,
365        U: ToTargetAddr,
366    {
367        let auth = Authentication::Password { username, password };
368        Socks5Stream::connect_raw(2, proxy, target, &auth, timeout).map(Socks5Listener)
369    }
370
371    /// The address of the proxy-side TCP listener.
372    ///
373    /// This should be forwarded to the remote process, which should open a
374    /// connection to it.
375    pub fn proxy_addr(&self) -> &TargetAddr {
376        &self.0.proxy_addr
377    }
378
379    /// Waits for the remote process to connect to the proxy server.
380    ///
381    /// The value of `proxy_addr` should be forwarded to the remote process
382    /// before this method is called.
383    pub fn accept(mut self) -> io::Result<Socks5Stream> {
384        self.0.proxy_addr = read_response(&mut self.0.socket)?;
385        Ok(self.0)
386    }
387}
388
389/// A SOCKS5 UDP client.
390#[derive(Debug)]
391pub struct Socks5Datagram {
392    socket: UdpSocket,
393    // keeps the session alive
394    stream: Socks5Stream,
395}
396
397impl Socks5Datagram {
398    /// Creates a UDP socket bound to the specified address which will have its
399    /// traffic routed through the specified proxy.
400    pub fn bind<T, U>(proxy: T, addr: U, timeout: Option<Duration>) -> io::Result<Socks5Datagram>
401    where
402        T: ToSocketAddrs,
403        U: ToSocketAddrs,
404    {
405        Self::bind_internal(proxy, addr, &Authentication::None, timeout)
406    }
407    /// Creates a UDP socket bound to the specified address which will have its
408    /// traffic routed through the specified proxy. The given username and password
409    /// is used to authenticate to the SOCKS proxy.
410    pub fn bind_with_password<T, U>(
411        proxy: T,
412        addr: U,
413        username: &str,
414        password: &str,
415        timeout: Option<Duration>,
416    ) -> io::Result<Socks5Datagram>
417    where
418        T: ToSocketAddrs,
419        U: ToSocketAddrs,
420    {
421        let auth = Authentication::Password { username, password };
422        Self::bind_internal(proxy, addr, &auth, timeout)
423    }
424
425    fn bind_internal<T, U>(
426        proxy: T,
427        addr: U,
428        auth: &Authentication,
429        timeout: Option<Duration>,
430    ) -> io::Result<Socks5Datagram>
431    where
432        T: ToSocketAddrs,
433        U: ToSocketAddrs,
434    {
435        // we don't know what our IP is from the perspective of the proxy, so
436        // don't try to pass `addr` in here.
437        let dst = TargetAddr::Ip(SocketAddr::V4(SocketAddrV4::new(
438            Ipv4Addr::new(0, 0, 0, 0),
439            0,
440        )));
441        let stream = Socks5Stream::connect_raw(3, proxy, dst, auth, timeout)?;
442
443        let socket = UdpSocket::bind(addr)?;
444        socket.connect(&stream.proxy_addr)?;
445
446        Ok(Socks5Datagram { socket, stream })
447    }
448
449    /// Like `UdpSocket::send_to`.
450    ///
451    /// # Note
452    ///
453    /// The SOCKS protocol inserts a header at the beginning of the message. The
454    /// header will be 10 bytes for an IPv4 address, 22 bytes for an IPv6
455    /// address, and 7 bytes plus the length of the domain for a domain address.
456    pub fn send_to<A>(&self, buf: &[u8], addr: A) -> io::Result<usize>
457    where
458        A: ToTargetAddr,
459    {
460        let addr = addr.to_target_addr()?;
461
462        let mut header = [0; MAX_ADDR_LEN + 3];
463        // first two bytes are reserved at 0
464        // third byte is the fragment id at 0
465        let len = write_addr(&mut header[3..], &addr)?;
466
467        self.socket.writev([&header[..len + 3], buf])
468    }
469
470    /// Like `UdpSocket::recv_from`.
471    pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, TargetAddr)> {
472        let mut header = [0; MAX_ADDR_LEN + 3];
473        let len = self.socket.readv([&mut header, buf])?;
474
475        let overflow = len.saturating_sub(header.len());
476
477        let header_len = cmp::min(header.len(), len);
478        let mut header = &mut &header[..header_len];
479
480        if header.read_u16::<BigEndian>()? != 0 {
481            return Err(io::Error::new(
482                io::ErrorKind::InvalidData,
483                "invalid reserved bytes",
484            ));
485        }
486        if header.read_u8()? != 0 {
487            return Err(io::Error::new(
488                io::ErrorKind::InvalidData,
489                "invalid fragment id",
490            ));
491        }
492        let addr = read_addr(&mut header)?;
493
494        unsafe {
495            ptr::copy(buf.as_ptr(), buf.as_mut_ptr().add(header.len()), overflow);
496        }
497        buf[..header.len()].copy_from_slice(header);
498
499        Ok((header.len() + overflow, addr))
500    }
501
502    /// Returns the address of the proxy-side UDP socket through which all
503    /// messages will be routed.
504    pub fn proxy_addr(&self) -> &TargetAddr {
505        &self.stream.proxy_addr
506    }
507
508    /// Returns a shared reference to the inner socket.
509    pub fn get_ref(&self) -> &UdpSocket {
510        &self.socket
511    }
512
513    /// Returns a mutable reference to the inner socket.
514    pub fn get_mut(&mut self) -> &mut UdpSocket {
515        &mut self.socket
516    }
517}
518
519#[cfg(test)]
520mod test {
521    use std::io::{Read, Write};
522    use std::net::{TcpStream, ToSocketAddrs, UdpSocket};
523
524    use super::*;
525
526    const SOCKS_PROXY_NO_AUTH_ONLY: &str = "127.0.0.1:1080";
527    const SOCKS_PROXY_PASSWD_ONLY: &str = "127.0.0.1:1081";
528
529    #[test]
530    #[ignore]
531    fn google_no_auth() {
532        let addr = "google.com:80".to_socket_addrs().unwrap().next().unwrap();
533        let socket = Socks5Stream::connect(SOCKS_PROXY_NO_AUTH_ONLY, addr, None).unwrap();
534        google(socket);
535    }
536
537    #[test]
538    #[ignore]
539    fn google_with_password() {
540        let addr = "google.com:80".to_socket_addrs().unwrap().next().unwrap();
541        let socket = Socks5Stream::connect_with_password(
542            SOCKS_PROXY_PASSWD_ONLY,
543            addr,
544            "testuser",
545            "testpass",
546            None,
547        )
548        .unwrap();
549        google(socket);
550    }
551
552    fn google(mut socket: Socks5Stream) {
553        socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
554        let mut result = vec![];
555        socket.read_to_end(&mut result).unwrap();
556
557        println!("{}", String::from_utf8_lossy(&result));
558        assert!(result.starts_with(b"HTTP/1.0"));
559        assert!(result.ends_with(b"</HTML>\r\n") || result.ends_with(b"</html>"));
560    }
561
562    #[test]
563    #[ignore]
564    fn google_dns() {
565        let mut socket =
566            Socks5Stream::connect(SOCKS_PROXY_NO_AUTH_ONLY, "google.com:80", None).unwrap();
567
568        socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
569        let mut result = vec![];
570        socket.read_to_end(&mut result).unwrap();
571
572        println!("{}", String::from_utf8_lossy(&result));
573        assert!(result.starts_with(b"HTTP/1.0"));
574        assert!(result.ends_with(b"</HTML>\r\n") || result.ends_with(b"</html>"));
575    }
576
577    #[test]
578    #[ignore]
579    fn bind_no_auth() {
580        let addr = find_address();
581        let listener = Socks5Listener::bind(SOCKS_PROXY_NO_AUTH_ONLY, addr, None).unwrap();
582        bind(listener);
583    }
584
585    #[test]
586    #[ignore]
587    fn bind_with_password_supported_but_no_auth_used() {
588        let addr = find_address();
589        let listener = Socks5Listener::bind_with_password(
590            SOCKS_PROXY_NO_AUTH_ONLY,
591            addr,
592            "unused_and_invalid_username",
593            "unused_and_invalid_password",
594            None,
595        )
596        .unwrap();
597        bind(listener);
598    }
599
600    #[test]
601    #[ignore]
602    fn bind_with_password() {
603        let addr = find_address();
604        let listener = Socks5Listener::bind_with_password(
605            "127.0.0.1:1081",
606            addr,
607            "testuser",
608            "testpass",
609            None,
610        )
611        .unwrap();
612        bind(listener);
613    }
614
615    fn bind(listener: Socks5Listener) {
616        let addr = listener.proxy_addr().clone();
617        let mut end = TcpStream::connect(addr).unwrap();
618        let mut conn = listener.accept().unwrap();
619        conn.write_all(b"hello world").unwrap();
620        drop(conn);
621        let mut result = vec![];
622        end.read_to_end(&mut result).unwrap();
623        assert_eq!(result, b"hello world");
624    }
625
626    // First figure out our local address that we'll be connecting from
627    fn find_address() -> TargetAddr {
628        let socket =
629            Socks5Stream::connect(SOCKS_PROXY_NO_AUTH_ONLY, "google.com:80", None).unwrap();
630        socket.proxy_addr().to_owned()
631    }
632
633    #[test]
634    #[ignore]
635    fn associate_no_auth() {
636        let socks =
637            Socks5Datagram::bind(SOCKS_PROXY_NO_AUTH_ONLY, "127.0.0.1:15410", None).unwrap();
638        associate(socks, "127.0.0.1:15411");
639    }
640
641    #[test]
642    #[ignore]
643    fn associate_with_password() {
644        let socks = Socks5Datagram::bind_with_password(
645            SOCKS_PROXY_PASSWD_ONLY,
646            "127.0.0.1:15414",
647            "testuser",
648            "testpass",
649            None,
650        )
651        .unwrap();
652        associate(socks, "127.0.0.1:15415");
653    }
654
655    fn associate(socks: Socks5Datagram, socket_addr: &str) {
656        let socket = UdpSocket::bind(socket_addr).unwrap();
657
658        socks.send_to(b"hello world!", socket_addr).unwrap();
659        let mut buf = [0; 13];
660        let (len, addr) = socket.recv_from(&mut buf).unwrap();
661        assert_eq!(len, 12);
662        assert_eq!(&buf[..12], b"hello world!");
663
664        socket.send_to(b"hello world!", addr).unwrap();
665
666        let len = socks.recv_from(&mut buf).unwrap().0;
667        assert_eq!(len, 12);
668        assert_eq!(&buf[..12], b"hello world!");
669    }
670
671    #[test]
672    #[ignore]
673    fn associate_long() {
674        let socks =
675            Socks5Datagram::bind(SOCKS_PROXY_NO_AUTH_ONLY, "127.0.0.1:15412", None).unwrap();
676        let socket_addr = "127.0.0.1:15413";
677        let socket = UdpSocket::bind(socket_addr).unwrap();
678
679        let mut msg = vec![];
680        for i in 0..(MAX_ADDR_LEN + 100) {
681            msg.push(i as u8);
682        }
683
684        socks.send_to(&msg, socket_addr).unwrap();
685        let mut buf = vec![0; msg.len() + 1];
686        let (len, addr) = socket.recv_from(&mut buf).unwrap();
687        assert_eq!(len, msg.len());
688        assert_eq!(msg, &buf[..msg.len()]);
689
690        socket.send_to(&msg, addr).unwrap();
691
692        let mut buf = vec![0; msg.len() + 1];
693        let len = socks.recv_from(&mut buf).unwrap().0;
694        assert_eq!(len, msg.len());
695        assert_eq!(msg, &buf[..msg.len()]);
696    }
697
698    #[test]
699    #[ignore]
700    fn incorrect_password() {
701        let addr = "google.com:80".to_socket_addrs().unwrap().next().unwrap();
702        let err = Socks5Stream::connect_with_password(
703            SOCKS_PROXY_PASSWD_ONLY,
704            addr,
705            "testuser",
706            "invalid",
707            None,
708        )
709        .unwrap_err();
710
711        assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
712        assert_eq!(err.to_string(), "password authentication failed");
713    }
714
715    #[test]
716    #[ignore]
717    fn auth_method_not_supported() {
718        let addr = "google.com:80".to_socket_addrs().unwrap().next().unwrap();
719        let err = Socks5Stream::connect(SOCKS_PROXY_PASSWD_ONLY, addr, None).unwrap_err();
720
721        assert_eq!(err.kind(), io::ErrorKind::Other);
722        assert_eq!(err.to_string(), "no acceptable auth methods");
723    }
724
725    #[test]
726    #[ignore]
727    fn username_and_password_length() {
728        let addr = "google.com:80".to_socket_addrs().unwrap().next().unwrap();
729
730        let err = Socks5Stream::connect_with_password(
731            SOCKS_PROXY_PASSWD_ONLY,
732            addr,
733            &string_of_size(1),
734            &string_of_size(1),
735            None,
736        )
737        .unwrap_err();
738        assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
739        assert_eq!(err.to_string(), "password authentication failed");
740
741        let err = Socks5Stream::connect_with_password(
742            SOCKS_PROXY_PASSWD_ONLY,
743            addr,
744            &string_of_size(255),
745            &string_of_size(255),
746            None,
747        )
748        .unwrap_err();
749        assert_eq!(err.kind(), io::ErrorKind::PermissionDenied);
750        assert_eq!(err.to_string(), "password authentication failed");
751
752        let err = Socks5Stream::connect_with_password(
753            SOCKS_PROXY_PASSWD_ONLY,
754            addr,
755            &string_of_size(0),
756            &string_of_size(255),
757            None,
758        )
759        .unwrap_err();
760        assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
761        assert_eq!(err.to_string(), "invalid username");
762
763        let err = Socks5Stream::connect_with_password(
764            SOCKS_PROXY_PASSWD_ONLY,
765            addr,
766            &string_of_size(256),
767            &string_of_size(255),
768            None,
769        )
770        .unwrap_err();
771        assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
772        assert_eq!(err.to_string(), "invalid username");
773
774        let err = Socks5Stream::connect_with_password(
775            SOCKS_PROXY_PASSWD_ONLY,
776            addr,
777            &string_of_size(255),
778            &string_of_size(0),
779            None,
780        )
781        .unwrap_err();
782        assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
783        assert_eq!(err.to_string(), "invalid password");
784
785        let err = Socks5Stream::connect_with_password(
786            SOCKS_PROXY_PASSWD_ONLY,
787            addr,
788            &string_of_size(255),
789            &string_of_size(256),
790            None,
791        )
792        .unwrap_err();
793        assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
794        assert_eq!(err.to_string(), "invalid password");
795    }
796
797    fn string_of_size(size: usize) -> String {
798        (0..size).map(|_| 'x').collect()
799    }
800}