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