socks/
v5.rs

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