electrum/socks/
v5.rs

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