1use std::fmt;
20use std::future::Future;
21use std::io;
22use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
23use std::pin::Pin;
24use std::str::FromStr;
25use std::sync::Arc;
26use std::time::Duration;
27
28use thiserror::Error;
29use tokio::net::TcpStream;
30
31use crate::socks5::{self, Socks5Error, Socks5Proxy};
32
33pub const MAX_DOMAIN_LEN: usize = 255;
35
36pub const DEFAULT_MINECRAFT_PORT: u16 = 25565;
38
39#[derive(Debug, Clone, PartialEq, Eq, Hash)]
41pub enum TargetAddr {
42 Ip(SocketAddr),
44 Domain(String, u16),
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Error)]
54pub enum AddrError {
55 #[error("host is empty")]
57 EmptyHost,
58 #[error("port is missing")]
60 MissingPort,
61 #[error("invalid port {0:?}")]
63 InvalidPort(String),
64 #[error("invalid IPv6 address {0:?}")]
66 InvalidIpv6(String),
67 #[error("domain name is {0} bytes long, the maximum is {MAX_DOMAIN_LEN}")]
69 DomainTooLong(usize),
70 #[error("domain name contains the invalid character {0:?}")]
74 InvalidDomainChar(char),
75}
76
77impl TargetAddr {
78 pub fn new(host: &str, port: u16) -> Result<Self, AddrError> {
83 let unbracketed = host
84 .strip_prefix('[')
85 .and_then(|h| h.strip_suffix(']'))
86 .unwrap_or(host);
87 if let Ok(ip) = unbracketed.parse::<IpAddr>() {
88 return Ok(Self::Ip(SocketAddr::new(ip, port)));
89 }
90 if unbracketed.len() != host.len() {
91 return Err(AddrError::InvalidIpv6(host.to_owned()));
92 }
93 validate_domain(host)?;
94 Ok(Self::Domain(host.to_owned(), port))
95 }
96
97 pub fn parse_with_default_port(s: &str, default_port: u16) -> Result<Self, AddrError> {
107 parse_target(s, Some(default_port))
108 }
109
110 pub fn port(&self) -> u16 {
112 match self {
113 Self::Ip(addr) => addr.port(),
114 Self::Domain(_, port) => *port,
115 }
116 }
117
118 pub fn host(&self) -> String {
120 match self {
121 Self::Ip(addr) => addr.ip().to_string(),
122 Self::Domain(host, _) => host.clone(),
123 }
124 }
125
126 pub fn is_local(&self) -> bool {
133 match self {
134 Self::Ip(addr) => is_local_ip(addr.ip()),
135 Self::Domain(host, _) => {
136 let host = host.strip_suffix('.').unwrap_or(host).to_ascii_lowercase();
137 host == "localhost" || host.ends_with(".localhost")
138 }
139 }
140 }
141}
142
143impl From<SocketAddr> for TargetAddr {
144 fn from(addr: SocketAddr) -> Self {
145 Self::Ip(addr)
146 }
147}
148
149impl FromStr for TargetAddr {
150 type Err = AddrError;
151
152 fn from_str(s: &str) -> Result<Self, Self::Err> {
155 parse_target(s, None)
156 }
157}
158
159impl fmt::Display for TargetAddr {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161 match self {
162 Self::Ip(addr) => write!(f, "{addr}"),
163 Self::Domain(host, port) => write!(f, "{host}:{port}"),
164 }
165 }
166}
167
168fn parse_target(s: &str, default_port: Option<u16>) -> Result<TargetAddr, AddrError> {
169 let s = s.trim();
170 let parse_port = |p: &str| {
171 p.parse::<u16>()
172 .map_err(|_| AddrError::InvalidPort(p.to_owned()))
173 };
174 let need_default = || default_port.ok_or(AddrError::MissingPort);
175
176 if let Some(rest) = s.strip_prefix('[') {
177 let (inside, after) = rest
178 .split_once(']')
179 .ok_or_else(|| AddrError::InvalidIpv6(s.to_owned()))?;
180 let ip: Ipv6Addr = inside
181 .parse()
182 .map_err(|_| AddrError::InvalidIpv6(inside.to_owned()))?;
183 let port = match after {
184 "" => need_default()?,
185 _ => match after.strip_prefix(':') {
186 Some(p) => parse_port(p)?,
187 None => return Err(AddrError::InvalidIpv6(s.to_owned())),
188 },
189 };
190 return Ok(TargetAddr::Ip(SocketAddr::new(IpAddr::V6(ip), port)));
191 }
192 if let Ok(ip) = s.parse::<Ipv6Addr>() {
194 return Ok(TargetAddr::Ip(SocketAddr::new(
195 IpAddr::V6(ip),
196 need_default()?,
197 )));
198 }
199 let (host, port) = match s.rsplit_once(':') {
200 Some((host, port)) => (host, parse_port(port)?),
201 None => (s, need_default()?),
202 };
203 if host.is_empty() {
204 return Err(AddrError::EmptyHost);
205 }
206 if let Ok(ip) = host.parse::<Ipv4Addr>() {
207 return Ok(TargetAddr::Ip(SocketAddr::new(IpAddr::V4(ip), port)));
208 }
209 validate_domain(host)?;
210 Ok(TargetAddr::Domain(host.to_owned(), port))
211}
212
213pub(crate) fn validate_domain(host: &str) -> Result<(), AddrError> {
214 if host.is_empty() {
215 return Err(AddrError::EmptyHost);
216 }
217 if host.len() > MAX_DOMAIN_LEN {
218 return Err(AddrError::DomainTooLong(host.len()));
219 }
220 match host
221 .chars()
222 .find(|c| !(c.is_ascii_alphanumeric() || matches!(c, '-' | '.' | '_')))
223 {
224 Some(c) => Err(AddrError::InvalidDomainChar(c)),
225 None => Ok(()),
226 }
227}
228
229fn is_local_ip(ip: IpAddr) -> bool {
230 match ip {
231 IpAddr::V4(v4) => is_local_v4(v4),
232 IpAddr::V6(v6) => {
233 if let Some(v4) = v6.to_ipv4_mapped() {
234 return is_local_v4(v4);
235 }
236 v6.is_loopback() || v6.is_unique_local() || v6.is_unicast_link_local()
237 }
238 }
239}
240
241fn is_local_v4(v4: Ipv4Addr) -> bool {
242 v4.is_loopback() || v4.is_private() || v4.is_link_local()
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub enum Route {
248 Proxy,
251 PreferProxy,
253 Direct,
255}
256
257pub type RouteFn = dyn Fn(&TargetAddr) -> Route + Send + Sync;
259
260#[derive(Clone, Default)]
262pub enum ProxyPolicy {
263 #[default]
266 PreferProxy,
267 RequireProxy,
269 RequireProxyExceptLocal,
272 Custom(Arc<RouteFn>),
274}
275
276impl ProxyPolicy {
277 pub fn custom(f: impl Fn(&TargetAddr) -> Route + Send + Sync + 'static) -> Self {
279 Self::Custom(Arc::new(f))
280 }
281
282 pub fn route(&self, target: &TargetAddr) -> Route {
284 match self {
285 Self::PreferProxy => Route::PreferProxy,
286 Self::RequireProxy => Route::Proxy,
287 Self::RequireProxyExceptLocal if target.is_local() => Route::Direct,
288 Self::RequireProxyExceptLocal => Route::Proxy,
289 Self::Custom(f) => f(target),
290 }
291 }
292}
293
294impl fmt::Debug for ProxyPolicy {
295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296 match self {
297 Self::PreferProxy => f.write_str("PreferProxy"),
298 Self::RequireProxy => f.write_str("RequireProxy"),
299 Self::RequireProxyExceptLocal => f.write_str("RequireProxyExceptLocal"),
300 Self::Custom(_) => f.write_str("Custom(..)"),
301 }
302 }
303}
304
305pub type ResolveFuture<'a> = Pin<Box<dyn Future<Output = io::Result<Vec<SocketAddr>>> + Send + 'a>>;
307
308pub trait Resolve: Send + Sync {
313 fn resolve<'a>(&'a self, host: &'a str, port: u16) -> ResolveFuture<'a>;
315}
316
317#[derive(Debug, Clone, Copy, Default)]
319pub struct SystemResolver;
320
321impl Resolve for SystemResolver {
322 fn resolve<'a>(&'a self, host: &'a str, port: u16) -> ResolveFuture<'a> {
323 Box::pin(async move { Ok(tokio::net::lookup_host((host, port)).await?.collect()) })
324 }
325}
326
327#[derive(Debug, Clone, Copy, PartialEq, Eq)]
329pub struct Timeouts {
330 pub resolve: Duration,
332 pub connect: Duration,
334 pub handshake: Duration,
336}
337
338impl Default for Timeouts {
339 fn default() -> Self {
340 Self {
341 resolve: Duration::from_secs(10),
342 connect: Duration::from_secs(10),
343 handshake: Duration::from_secs(15),
344 }
345 }
346}
347
348#[derive(Debug, Clone, Copy, PartialEq, Eq)]
350pub enum TimeoutStage {
351 Resolve,
353 Connect,
355 Handshake,
357}
358
359impl fmt::Display for TimeoutStage {
360 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
361 f.write_str(match self {
362 Self::Resolve => "DNS resolution",
363 Self::Connect => "TCP connect",
364 Self::Handshake => "SOCKS5 handshake",
365 })
366 }
367}
368
369#[derive(Debug, Error)]
371pub enum ConnectError {
372 #[error("the proxy policy requires a proxy to connect to {0}, but none is configured")]
374 ProxyRequired(TargetAddr),
375 #[error("could not resolve {host}: {source}")]
377 Resolve {
378 host: String,
380 #[source]
382 source: io::Error,
383 },
384 #[error("{0} resolved to no addresses")]
386 NoAddresses(String),
387 #[error("{0} timed out")]
389 Timeout(TimeoutStage),
390 #[error("could not connect to {addr}: {source}")]
392 Connect {
393 addr: SocketAddr,
395 #[source]
397 source: io::Error,
398 },
399 #[error("SOCKS5 proxy {proxy}: {source}")]
401 Socks5 {
402 proxy: TargetAddr,
404 #[source]
406 source: Socks5Error,
407 },
408 #[error(transparent)]
410 Io(#[from] io::Error),
411}
412
413#[derive(Clone)]
417pub struct Connector {
418 proxy: Option<Socks5Proxy>,
419 policy: ProxyPolicy,
420 timeouts: Timeouts,
421 resolver: Arc<dyn Resolve>,
422}
423
424impl Default for Connector {
425 fn default() -> Self {
426 Self::new()
427 }
428}
429
430impl fmt::Debug for Connector {
431 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432 f.debug_struct("Connector")
433 .field("proxy", &self.proxy)
434 .field("policy", &self.policy)
435 .field("timeouts", &self.timeouts)
436 .finish_non_exhaustive()
437 }
438}
439
440impl Connector {
441 pub fn new() -> Self {
444 Self {
445 proxy: None,
446 policy: ProxyPolicy::default(),
447 timeouts: Timeouts::default(),
448 resolver: Arc::new(SystemResolver),
449 }
450 }
451
452 pub fn with_proxy(mut self, proxy: Socks5Proxy) -> Self {
454 self.proxy = Some(proxy);
455 self
456 }
457
458 pub fn with_optional_proxy(mut self, proxy: Option<Socks5Proxy>) -> Self {
460 self.proxy = proxy;
461 self
462 }
463
464 pub fn with_policy(mut self, policy: ProxyPolicy) -> Self {
466 self.policy = policy;
467 self
468 }
469
470 pub fn with_timeouts(mut self, timeouts: Timeouts) -> Self {
472 self.timeouts = timeouts;
473 self
474 }
475
476 pub fn with_resolver(mut self, resolver: Arc<dyn Resolve>) -> Self {
478 self.resolver = resolver;
479 self
480 }
481
482 pub fn proxy(&self) -> Option<&Socks5Proxy> {
484 self.proxy.as_ref()
485 }
486
487 pub fn policy(&self) -> &ProxyPolicy {
489 &self.policy
490 }
491
492 pub fn plan(&self, target: &TargetAddr) -> Option<bool> {
495 match (self.policy.route(target), self.proxy.is_some()) {
496 (Route::Proxy, true) | (Route::PreferProxy, true) => Some(true),
497 (Route::Proxy, false) => None,
498 (Route::PreferProxy, false) | (Route::Direct, _) => Some(false),
499 }
500 }
501
502 pub async fn connect(&self, target: &TargetAddr) -> Result<TcpStream, ConnectError> {
507 let stream = match (self.plan(target), &self.proxy) {
508 (None, _) => return Err(ConnectError::ProxyRequired(target.clone())),
509 (Some(true), Some(proxy)) => self.connect_via_proxy(proxy, target).await?,
510 (Some(_), _) => self.connect_addr(target).await?,
511 };
512 stream.set_nodelay(true)?;
513 Ok(stream)
514 }
515
516 async fn connect_via_proxy(
517 &self,
518 proxy: &Socks5Proxy,
519 target: &TargetAddr,
520 ) -> Result<TcpStream, ConnectError> {
521 let mut stream = self.connect_addr(proxy.addr()).await?;
522 stream.set_nodelay(true)?;
524 let negotiation = socks5::handshake(&mut stream, target, proxy.auth());
525 match tokio::time::timeout(self.timeouts.handshake, negotiation).await {
526 Err(_) => Err(ConnectError::Timeout(TimeoutStage::Handshake)),
527 Ok(Err(source)) => Err(ConnectError::Socks5 {
528 proxy: proxy.addr().clone(),
529 source,
530 }),
531 Ok(Ok(_bound)) => Ok(stream),
532 }
533 }
534
535 async fn connect_addr(&self, addr: &TargetAddr) -> Result<TcpStream, ConnectError> {
537 let candidates = match addr {
538 TargetAddr::Ip(ip) => vec![*ip],
539 TargetAddr::Domain(host, port) => {
540 let lookup = self.resolver.resolve(host, *port);
541 let addrs = match tokio::time::timeout(self.timeouts.resolve, lookup).await {
542 Err(_) => return Err(ConnectError::Timeout(TimeoutStage::Resolve)),
543 Ok(Err(source)) => {
544 return Err(ConnectError::Resolve {
545 host: host.clone(),
546 source,
547 });
548 }
549 Ok(Ok(addrs)) => addrs,
550 };
551 if addrs.is_empty() {
552 return Err(ConnectError::NoAddresses(host.clone()));
553 }
554 addrs
555 }
556 };
557 let mut last_err = None;
558 for candidate in candidates {
559 match tokio::time::timeout(self.timeouts.connect, TcpStream::connect(candidate)).await {
560 Ok(Ok(stream)) => return Ok(stream),
561 Ok(Err(source)) => {
562 last_err = Some(ConnectError::Connect {
563 addr: candidate,
564 source,
565 })
566 }
567 Err(_) => last_err = Some(ConnectError::Timeout(TimeoutStage::Connect)),
568 }
569 }
570 Err(last_err.unwrap_or(ConnectError::Timeout(TimeoutStage::Connect)))
572 }
573}
574
575#[cfg(test)]
576mod tests {
577 use super::*;
578
579 fn ip(s: &str) -> TargetAddr {
580 TargetAddr::Ip(s.parse().unwrap())
581 }
582
583 #[test]
584 fn parse_targets() {
585 assert_eq!("1.2.3.4:25565".parse(), Ok(ip("1.2.3.4:25565")));
586 assert_eq!("[::1]:80".parse(), Ok(ip("[::1]:80")));
587 assert_eq!(
588 "mc.example.net:25570".parse(),
589 Ok(TargetAddr::Domain("mc.example.net".into(), 25570))
590 );
591 assert_eq!(
592 "mc.example.net".parse::<TargetAddr>(),
593 Err(AddrError::MissingPort)
594 );
595 assert_eq!(
596 TargetAddr::parse_with_default_port("mc.example.net", 25565),
597 Ok(TargetAddr::Domain("mc.example.net".into(), 25565))
598 );
599 assert_eq!(
600 TargetAddr::parse_with_default_port("::1", 25565),
601 Ok(ip("[::1]:25565"))
602 );
603 assert_eq!(
604 TargetAddr::parse_with_default_port("[fe80::1]", 1),
605 Ok(ip("[fe80::1]:1"))
606 );
607 assert_eq!(
608 "host:99999".parse::<TargetAddr>(),
609 Err(AddrError::InvalidPort("99999".into()))
610 );
611 assert_eq!(":25565".parse::<TargetAddr>(), Err(AddrError::EmptyHost));
612 assert!(matches!(
613 "[::1".parse::<TargetAddr>(),
614 Err(AddrError::InvalidIpv6(_))
615 ));
616 assert!(matches!(
617 "[nope]:1".parse::<TargetAddr>(),
618 Err(AddrError::InvalidIpv6(_))
619 ));
620 assert_eq!(
621 "bad host:1".parse::<TargetAddr>(),
622 Err(AddrError::InvalidDomainChar(' '))
623 );
624 assert_eq!(
625 "b\u{fc}cher.de:1".parse::<TargetAddr>(),
626 Err(AddrError::InvalidDomainChar('\u{fc}'))
627 );
628 let long = format!("{}:1", "a".repeat(256));
629 assert_eq!(
630 long.parse::<TargetAddr>(),
631 Err(AddrError::DomainTooLong(256))
632 );
633 }
634
635 #[test]
636 fn new_and_display() {
637 assert_eq!(TargetAddr::new("::1", 5), Ok(ip("[::1]:5")));
638 assert_eq!(TargetAddr::new("[::1]", 5), Ok(ip("[::1]:5")));
639 assert_eq!(TargetAddr::new("10.0.0.1", 5), Ok(ip("10.0.0.1:5")));
640 assert!(TargetAddr::new("[x]", 5).is_err());
641 assert_eq!(
642 TargetAddr::new("a.b", 7).unwrap().to_string(),
643 "a.b:7".to_owned()
644 );
645 assert_eq!(ip("[::1]:5").to_string(), "[::1]:5");
646 assert_eq!(ip("[::1]:5").host(), "::1");
647 }
648
649 #[test]
650 fn locality() {
651 for local in [
652 "127.0.0.1:1",
653 "10.1.2.3:1",
654 "172.16.0.1:1",
655 "10.0.0.10:25570",
656 "169.254.1.1:1",
657 "[::1]:1",
658 "[fd00::1]:1",
659 "[fe80::1]:1",
660 "[::ffff:10.0.0.1]:1",
661 ] {
662 assert!(ip(local).is_local(), "{local}");
663 }
664 for public in [
665 "8.8.8.8:1",
666 "100.64.0.1:1",
667 "172.32.0.1:1",
668 "[2001:db8::1]:1",
669 "[::ffff:8.8.8.8]:1",
670 ] {
671 assert!(!ip(public).is_local(), "{public}");
672 }
673 assert!(TargetAddr::new("localhost", 1).unwrap().is_local());
674 assert!(TargetAddr::new("LocalHost.", 1).unwrap().is_local());
675 assert!(TargetAddr::new("a.localhost", 1).unwrap().is_local());
676 assert!(!TargetAddr::new("lan-server", 1).unwrap().is_local());
677 assert!(!TargetAddr::new("localhost.example", 1).unwrap().is_local());
678 }
679
680 #[test]
681 fn policies() {
682 let lan = ip("10.0.0.10:25570");
683 let public = TargetAddr::new("mc.example.net", 25565).unwrap();
684 assert_eq!(ProxyPolicy::PreferProxy.route(&public), Route::PreferProxy);
685 assert_eq!(ProxyPolicy::RequireProxy.route(&lan), Route::Proxy);
686 assert_eq!(
687 ProxyPolicy::RequireProxyExceptLocal.route(&lan),
688 Route::Direct
689 );
690 assert_eq!(
691 ProxyPolicy::RequireProxyExceptLocal.route(&public),
692 Route::Proxy
693 );
694 let custom = ProxyPolicy::custom(|t| {
695 if t.port() == 25570 {
696 Route::Direct
697 } else {
698 Route::Proxy
699 }
700 });
701 assert_eq!(custom.route(&lan), Route::Direct);
702 assert_eq!(custom.route(&public), Route::Proxy);
703 assert_eq!(format!("{custom:?}"), "Custom(..)");
704 }
705
706 #[test]
707 fn plan() {
708 let lan = ip("10.0.0.10:25570");
709 let public = ip("8.8.8.8:25565");
710 let proxy: Socks5Proxy = "socks5h://127.0.0.1:1080".parse().unwrap();
711
712 let none = Connector::new().with_policy(ProxyPolicy::RequireProxyExceptLocal);
713 assert_eq!(none.plan(&lan), Some(false));
714 assert_eq!(none.plan(&public), None);
715
716 let some = none.clone().with_proxy(proxy.clone());
717 assert_eq!(some.plan(&lan), Some(false));
718 assert_eq!(some.plan(&public), Some(true));
719
720 let prefer = Connector::new();
721 assert_eq!(prefer.plan(&public), Some(false));
722 assert_eq!(prefer.clone().with_proxy(proxy).plan(&public), Some(true));
723 }
724}