1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
use byteorder::{ByteOrder, BigEndian}; use std::borrow::Cow; use std::str::FromStr; use std::net; /// Module contains implementation of /// the socks5 proxification protocol /// with no authentification required /// when establishing a connection /// between a client and a socks5 server pub mod no_auth; pub use no_auth::TcpNoAuth; /// The Socks5 protocol command representation #[repr(C)] pub enum Command { TcpConnectionEstablishment = 1, TcpPortBinding, UdpPortBinding } /// Represents a destination address of /// a service, to which a user wants to /// connect through a socks5 proxy. /// It is a good solution, but not /// the fastest, so it will be rewritten in the /// future in preference to a dispatch mechanism pub enum Destination { /// Represents an IPv4 address Ipv4Addr(std::net::Ipv4Addr), /// Represents a domain name DomainName(Cow<'static, str>), /// Represents an IPv6 address Ipv6Addr(std::net::Ipv6Addr) } impl Destination { /// Returns the length in bytes /// of the destination, represented as a buffer pub fn len_as_buffer(&self) -> usize { match self { Destination::Ipv4Addr(_) => 4 + 1, Destination::DomainName(name) => name.len() + 2, Destination::Ipv6Addr(_) => 16 + 1 } } /// Extends buffer with a buffer /// representation of a Destination /// (See the Socks5 wiki for more information). /// /// Note: /// I wanted to make this function generic, such as /// it would have the signature like that: /// ``` /// fn extend_buffer(buf: impl AsMut<u8>) /// ``` /// but it was preffered to don't to it, because the sense of this /// flexibility will lead to longer compilation time, and that /// is totally okay in most of cases, but the function is not even /// `pub(crate)`, so the choice is obvious /// fn extend_buffer(&mut self, buf: &mut [u8]) -> Result<(), ()> { match self { Destination::Ipv4Addr(addr) => { // If the destination is an IPv4 address, then // the first byte of the buffer will // contain `0x01` buf[0] = 0x01; // Then we need represent the IPv4 // address as a buffer (in the network byte order) // and copy it to our buffer `buf` BigEndian::write_u32(&mut buf[1..5], addr.clone().into()); }, Destination::DomainName(name) => { // If the destination is a domain name, then // the first byte of the buffer will // contain `0x03` buf[0] = 0x03; // Then we need to compute the length // of the domain name and store it // as a next byte in the buffer. // The length cannot be larger than // the maxumim value of a byte (0xFF or 255), // so we need to make sure of it if name.len() > 255 { return Err(()) } // Storing the length buf[1] = name.len() as u8; // Then the socks5 protocol requires us to // represent the domain name address as // a buffer and copy it to our buffer `buf` buf[2..].clone_from_slice(name.as_bytes()); }, Destination::Ipv6Addr(addr) => { // If the destination is an IPv6 address, then // the first byte of the buffer will // contain `0x04` buf[0] = 0x04; // Then we need represent the IPv4 // address as a buffer (in the network byte order) // and copy it to our buffer `buf` BigEndian::write_u128(&mut buf[1..17], addr.clone().into()); } } Ok(()) } } impl FromStr for Destination { type Err = (); /// Parses a socks5 destination. /// The parsing algorithm is simpler than /// you can think of: /// At first, it tries to parse an IPv4 /// from the string. /// If succeed, returns an IPv4 destination representation, /// If not, it tries to parse an IPv6 /// from the string. /// Then, if succeed, returns an IPv6 destination representation. /// Finally, if not, it tries to parse a domain name /// from the string and returns a domain name destination /// if succeed, unless `Err` fn from_str(s: &str) -> Result<Destination, Self::Err> { // Trying to parse an ipv4 address from the string // (Actually, not the best code, but better that // multiple calls of `.map` or nested matched for ex.) let result = s.parse::<net::Ipv4Addr>(); if result.is_ok() { return Ok(Destination::Ipv4Addr(result.unwrap())) } // Trying to parse an IPv6 address from the string let result = s.parse::<net::Ipv6Addr>(); if result.is_ok() { return Ok(Destination::Ipv6Addr(result.unwrap())) } // Trying to parse a domain name webpki::DNSNameRef::try_from_ascii_str(s) .map_err(|_| ())?; Ok(Destination::DomainName(Cow::Owned(s.to_owned()))) } }