socks5_client/
lib.rs

1// Set of libraries for privacy-preserving networking apps
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2019-2023 by
6//     Dr. Maxim Orlovsky <orlovsky@cyphernet.org>
7//
8// Copyright 2022-2023 Cyphernet DAO, Switzerland
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14//     http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22#![cfg_attr(docsrs, feature(doc_auto_cfg))]
23
24//! Pure rust SOCKS5 protocol implementation for a client with zero dependencies.
25//!
26//! The library is a part of [rust cyphernet suite](https://github.com/Cyphernet-DAO/rust-cyphernet)
27//! and used by it for generic implementation of noise protocol framework abstracted
28//! from the underlying curve.
29
30#[macro_use]
31extern crate amplify;
32
33mod encoding;
34mod error;
35
36use std::io;
37
38use cypheraddr::{Host, HostName, NetAddr};
39pub use error::ServerError;
40
41use crate::encoding::{Encoding, EncodingError, DOMAIN, IPV4, IPV6};
42
43#[derive(Debug, Display, Error, From)]
44#[display(inner)]
45pub enum Error {
46    #[from]
47    Server(ServerError),
48    #[from]
49    Encoding(EncodingError),
50
51    /// invalid server reply
52    InvalidReply,
53
54    /// not supported SOCKS protocol version {0}
55    VersionNotSupported(u8),
56
57    /// server requires authentication with an unsupported method
58    AuthRequired,
59
60    /// SOCKS5 connection is established, the handshake is complete
61    Completed,
62
63    /// connection is closed due to a failure
64    Closed,
65}
66
67#[derive(Debug)]
68pub enum Socks5 {
69    Initial(NetAddr<HostName>, bool),
70    Connected(NetAddr<HostName>),
71    Awaiting,
72    Reading(u8, u8),
73    Active(NetAddr<HostName>),
74    Rejected(ServerError, u8, u8),
75    Failed(Error),
76}
77
78impl Socks5 {
79    pub fn with(addr: impl Into<NetAddr<HostName>>, force_proxy: bool) -> Self {
80        Self::Initial(addr.into(), force_proxy)
81    }
82
83    pub fn advance(&mut self, input: &[u8]) -> Result<Vec<u8>, Error> {
84        match self {
85            Socks5::Initial(addr, false) if !addr.requires_proxy() => {
86                *self = Socks5::Active(addr.clone());
87                Ok(vec![])
88            }
89            Socks5::Initial(addr, _) => {
90                debug_assert!(input.is_empty());
91                let out = vec![0x05, 0x01, 0x00];
92                *self = Socks5::Connected(addr.clone());
93                Ok(out)
94            }
95            Socks5::Connected(addr) => {
96                debug_assert_eq!(input.len(), 2);
97                if input[0] != 0x05 {
98                    *self = Socks5::Failed(Error::VersionNotSupported(input[0]));
99                    return Err(Error::VersionNotSupported(input[0]));
100                }
101                if input[1] != 0x00 {
102                    *self = Socks5::Failed(Error::AuthRequired);
103                    return Err(Error::AuthRequired);
104                }
105
106                let mut out = vec![0x05, 0x01, 0x00];
107                addr.encode(&mut out)?;
108                *self = Socks5::Awaiting;
109                Ok(out)
110            }
111            Socks5::Awaiting => {
112                debug_assert_eq!(input.len(), 5);
113                if input[0] != 0x05 {
114                    *self = Socks5::Failed(Error::VersionNotSupported(input[0]));
115                    return Err(Error::VersionNotSupported(input[0]));
116                }
117                if input[1] != 0x00 {
118                    let err = ServerError::from(input[1]);
119                    *self = Socks5::Rejected(err, input[3], input[4]);
120                } else {
121                    *self = Socks5::Reading(input[3], input[4]);
122                }
123                Ok(vec![])
124            }
125            Socks5::Reading(code1, code2) => {
126                let mut vec = Vec::with_capacity(input.len() + 2);
127                vec.extend_from_slice(&[*code1, *code2]);
128                vec.extend_from_slice(input);
129                let mut cursor = io::Cursor::new(vec);
130                let addr = NetAddr::<HostName>::decode(&mut cursor)?;
131                *self = Socks5::Active(addr);
132                Ok(vec![])
133            }
134            Socks5::Active(_) => Err(Error::Completed),
135            Socks5::Rejected(_, _, _) | Socks5::Failed(_) => Err(Error::Closed),
136        }
137    }
138
139    pub fn next_read_len(&self) -> usize {
140        match self {
141            Socks5::Initial(_, _) => 0,
142            Socks5::Connected(_) => 2,
143            Socks5::Awaiting => 5,
144            Socks5::Reading(ty, _) | Socks5::Rejected(_, ty, _) if *ty == IPV4 => 5,
145            Socks5::Reading(ty, _) | Socks5::Rejected(_, ty, _) if *ty == IPV6 => 17,
146            Socks5::Reading(ty, len) | Socks5::Rejected(_, ty, len) if *ty == DOMAIN => {
147                *len as usize + 1
148            }
149            Socks5::Reading(_, _) | Socks5::Rejected(_, _, _) => 1,
150            Socks5::Active(_) | Socks5::Failed(_) => 0,
151        }
152    }
153}