#[cfg(unix)]
use std::path::PathBuf;
use std::{
error,
fmt::{self, Display},
net::SocketAddr,
str::FromStr,
time::Duration,
};
use base64::{decode_config, encode_config, URL_SAFE_NO_PAD};
use log::error;
use url::{self, Url};
use crate::{
crypto::v1::{openssl_bytes_to_key, CipherKind},
plugin::PluginConfig,
relay::socks5::Address,
};
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ServerType {
Local,
Server,
}
impl ServerType {
pub fn is_local(self) -> bool {
self == ServerType::Local
}
pub fn is_server(self) -> bool {
self == ServerType::Server
}
}
#[derive(Clone, Copy, Debug)]
pub enum Mode {
TcpOnly = 0x01,
TcpAndUdp = 0x03,
UdpOnly = 0x02,
}
impl Mode {
pub fn enable_udp(self) -> bool {
matches!(self, Mode::UdpOnly | Mode::TcpAndUdp)
}
pub fn enable_tcp(self) -> bool {
matches!(self, Mode::TcpOnly | Mode::TcpAndUdp)
}
pub fn merge(&self, mode: Mode) -> Mode {
let me = *self as u8;
let fm = mode as u8;
match me | fm {
0x01 => Mode::TcpOnly,
0x02 => Mode::UdpOnly,
0x03 => Mode::TcpAndUdp,
_ => unreachable!(),
}
}
}
impl fmt::Display for Mode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Mode::TcpOnly => f.write_str("tcp_only"),
Mode::TcpAndUdp => f.write_str("tcp_and_udp"),
Mode::UdpOnly => f.write_str("udp_only"),
}
}
}
impl FromStr for Mode {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"tcp_only" => Ok(Mode::TcpOnly),
"tcp_and_udp" => Ok(Mode::TcpAndUdp),
"udp_only" => Ok(Mode::UdpOnly),
_ => Err(()),
}
}
}
#[derive(Clone, Debug)]
pub struct ServerConfig {
addr: ServerAddr,
password: String,
method: CipherKind,
enc_key: Box<[u8]>,
timeout: Option<Duration>,
plugin: Option<PluginConfig>,
plugin_addr: Option<ServerAddr>,
remarks: Option<String>,
id: Option<String>,
mode: Mode,
}
impl ServerConfig {
pub fn new<A, P>(addr: A, password: P, method: CipherKind) -> ServerConfig
where
A: Into<ServerAddr>,
P: Into<String>,
{
let password = password.into();
let mut enc_key = vec![0u8; method.key_len()].into_boxed_slice();
openssl_bytes_to_key(password.as_bytes(), &mut enc_key);
ServerConfig {
addr: addr.into(),
password,
method,
enc_key,
timeout: None,
plugin: None,
plugin_addr: None,
remarks: None,
id: None,
mode: Mode::TcpOnly,
}
}
pub fn set_method<P>(&mut self, method: CipherKind, password: P)
where
P: Into<String>,
{
self.method = method;
self.password = password.into();
let mut enc_key = vec![0u8; method.key_len()].into_boxed_slice();
openssl_bytes_to_key(self.password.as_bytes(), &mut enc_key);
self.enc_key = enc_key;
}
pub fn set_plugin(&mut self, p: PluginConfig) {
self.plugin = Some(p);
}
pub fn set_addr<A>(&mut self, a: A)
where
A: Into<ServerAddr>,
{
self.addr = a.into();
}
pub fn addr(&self) -> &ServerAddr {
&self.addr
}
pub fn key(&self) -> &[u8] {
&self.enc_key.as_ref()
}
pub fn password(&self) -> &str {
&self.password.as_str()
}
pub fn method(&self) -> CipherKind {
self.method
}
pub fn plugin(&self) -> Option<&PluginConfig> {
self.plugin.as_ref()
}
pub fn set_plugin_addr(&mut self, a: ServerAddr) {
self.plugin_addr = Some(a);
}
pub fn plugin_addr(&self) -> Option<&ServerAddr> {
self.plugin_addr.as_ref()
}
pub fn external_addr(&self) -> &ServerAddr {
self.plugin_addr.as_ref().unwrap_or(&self.addr)
}
pub fn set_timeout(&mut self, timeout: Duration) {
self.timeout = Some(timeout);
}
pub fn timeout(&self) -> Option<Duration> {
self.timeout
}
pub fn remarks(&self) -> Option<&str> {
self.remarks.as_ref().map(AsRef::as_ref)
}
pub fn set_remarks<S>(&mut self, remarks: S)
where
S: Into<String>,
{
self.remarks = Some(remarks.into());
}
pub fn id(&self) -> Option<&str> {
self.id.as_ref().map(AsRef::as_ref)
}
pub fn set_id<S>(&mut self, id: S)
where
S: Into<String>,
{
self.id = Some(id.into())
}
pub fn mode(&self) -> Mode {
self.mode
}
pub fn set_mode(&mut self, mode: Mode) {
self.mode = mode;
}
pub fn to_qrcode_url(&self) -> String {
let param = format!("{}:{}@{}", self.method(), self.password(), self.addr());
format!("ss://{}", encode_config(¶m, URL_SAFE_NO_PAD))
}
pub fn to_url(&self) -> String {
let user_info = format!("{}:{}", self.method(), self.password());
let encoded_user_info = encode_config(&user_info, URL_SAFE_NO_PAD);
let mut url = format!("ss://{}@{}", encoded_user_info, self.addr());
if let Some(c) = self.plugin() {
let mut plugin = c.plugin.clone();
if let Some(ref opt) = c.plugin_opts {
plugin += ";";
plugin += opt;
}
let plugin_param = [("plugin", &plugin)];
url += "/?";
url += &serde_urlencoded::to_string(&plugin_param).unwrap();
}
url
}
pub fn from_url(encoded: &str) -> Result<ServerConfig, UrlParseError> {
let parsed = Url::parse(encoded).map_err(UrlParseError::from)?;
if parsed.scheme() != "ss" {
return Err(UrlParseError::InvalidScheme);
}
let user_info = parsed.username();
let account = match decode_config(user_info, URL_SAFE_NO_PAD) {
Ok(account) => match String::from_utf8(account) {
Ok(ac) => ac,
Err(..) => {
return Err(UrlParseError::InvalidAuthInfo);
}
},
Err(err) => {
error!("Failed to parse UserInfo with Base64, err: {}", err);
return Err(UrlParseError::InvalidUserInfo);
}
};
let host = match parsed.host_str() {
Some(host) => host,
None => return Err(UrlParseError::MissingHost),
};
let port = parsed.port().unwrap_or(8388);
let addr = format!("{}:{}", host, port);
let mut sp2 = account.splitn(2, ':');
let (method, pwd) = match (sp2.next(), sp2.next()) {
(Some(m), Some(p)) => (m, p),
_ => return Err(UrlParseError::InvalidUserInfo)
};
let addr = match addr.parse::<ServerAddr>() {
Ok(a) => a,
Err(err) => {
error!("Failed to parse \"{}\" to ServerAddr, err: {:?}", addr, err);
return Err(UrlParseError::InvalidServerAddr);
}
};
let mut svrconfig = ServerConfig::new(addr, pwd.to_owned(), method.parse().unwrap());
if let Some(q) = parsed.query() {
let query = match serde_urlencoded::from_bytes::<Vec<(String, String)>>(q.as_bytes()) {
Ok(q) => q,
Err(err) => {
error!("Failed to parse QueryString, err: {}", err);
return Err(UrlParseError::InvalidQueryString);
}
};
for (key, value) in query {
if key != "plugin" {
continue;
}
let mut vsp = value.splitn(2, ';');
match vsp.next() {
None => {}
Some(p) => {
let plugin = PluginConfig {
plugin: p.to_owned(),
plugin_opts: vsp.next().map(ToOwned::to_owned),
plugin_args: Vec::new(),
};
svrconfig.set_plugin(plugin);
}
}
}
}
Ok(svrconfig)
}
pub fn is_basic(&self) -> bool {
self.remarks.is_none() && self.id.is_none()
}
}
#[derive(Debug, Clone)]
pub enum UrlParseError {
ParseError(url::ParseError),
InvalidScheme,
InvalidUserInfo,
MissingHost,
InvalidAuthInfo,
InvalidServerAddr,
InvalidQueryString,
}
impl From<url::ParseError> for UrlParseError {
fn from(err: url::ParseError) -> UrlParseError {
UrlParseError::ParseError(err)
}
}
impl fmt::Display for UrlParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
UrlParseError::ParseError(ref err) => fmt::Display::fmt(err, f),
UrlParseError::InvalidScheme => write!(f, "URL must have \"ss://\" scheme"),
UrlParseError::InvalidUserInfo => write!(f, "invalid user info"),
UrlParseError::MissingHost => write!(f, "missing host"),
UrlParseError::InvalidAuthInfo => write!(f, "invalid authentication info"),
UrlParseError::InvalidServerAddr => write!(f, "invalid server address"),
UrlParseError::InvalidQueryString => write!(f, "invalid query string"),
}
}
}
impl error::Error for UrlParseError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match *self {
UrlParseError::ParseError(ref err) => Some(err as &dyn error::Error),
UrlParseError::InvalidScheme => None,
UrlParseError::InvalidUserInfo => None,
UrlParseError::MissingHost => None,
UrlParseError::InvalidAuthInfo => None,
UrlParseError::InvalidServerAddr => None,
UrlParseError::InvalidQueryString => None,
}
}
}
impl FromStr for ServerConfig {
type Err = UrlParseError;
fn from_str(s: &str) -> Result<ServerConfig, Self::Err> {
ServerConfig::from_url(s)
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ServerAddr {
SocketAddr(SocketAddr),
DomainName(String, u16),
}
impl ServerAddr {
pub fn host(&self) -> String {
match *self {
ServerAddr::SocketAddr(ref s) => s.ip().to_string(),
ServerAddr::DomainName(ref dm, _) => dm.clone(),
}
}
pub fn port(&self) -> u16 {
match *self {
ServerAddr::SocketAddr(ref s) => s.port(),
ServerAddr::DomainName(_, p) => p,
}
}
}
#[derive(Debug)]
pub struct ServerAddrError;
impl FromStr for ServerAddr {
type Err = ServerAddrError;
fn from_str(s: &str) -> Result<ServerAddr, ServerAddrError> {
match s.parse::<SocketAddr>() {
Ok(addr) => Ok(ServerAddr::SocketAddr(addr)),
Err(..) => {
let mut sp = s.split(':');
match (sp.next(), sp.next()) {
(Some(dn), Some(port)) => {
if dn.is_empty() {
return Err(ServerAddrError);
}
match port.parse::<u16>() {
Ok(port) => Ok(ServerAddr::DomainName(dn.to_owned(), port)),
Err(..) => Err(ServerAddrError),
}
}
_ => Err(ServerAddrError),
}
}
}
}
}
impl Display for ServerAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ServerAddr::SocketAddr(ref a) => write!(f, "{}", a),
ServerAddr::DomainName(ref d, port) => write!(f, "{}:{}", d, port),
}
}
}
impl From<SocketAddr> for ServerAddr {
fn from(addr: SocketAddr) -> ServerAddr {
ServerAddr::SocketAddr(addr)
}
}
impl<I: Into<String>> From<(I, u16)> for ServerAddr {
fn from((dname, port): (I, u16)) -> ServerAddr {
ServerAddr::DomainName(dname.into(), port)
}
}
impl From<Address> for ServerAddr {
fn from(addr: Address) -> ServerAddr {
match addr {
Address::SocketAddress(sa) => ServerAddr::SocketAddr(sa),
Address::DomainNameAddress(dn, port) => ServerAddr::DomainName(dn, port),
}
}
}
impl From<&Address> for ServerAddr {
fn from(addr: &Address) -> ServerAddr {
match *addr {
Address::SocketAddress(sa) => ServerAddr::SocketAddr(sa),
Address::DomainNameAddress(ref dn, port) => ServerAddr::DomainName(dn.clone(), port),
}
}
}
impl From<ServerAddr> for Address {
fn from(addr: ServerAddr) -> Address {
match addr {
ServerAddr::SocketAddr(sa) => Address::SocketAddress(sa),
ServerAddr::DomainName(dn, port) => Address::DomainNameAddress(dn, port),
}
}
}
impl From<&ServerAddr> for Address {
fn from(addr: &ServerAddr) -> Address {
match *addr {
ServerAddr::SocketAddr(sa) => Address::SocketAddress(sa),
ServerAddr::DomainName(ref dn, port) => Address::DomainNameAddress(dn.clone(), port),
}
}
}
#[derive(Debug, Clone)]
pub enum ManagerAddr {
SocketAddr(SocketAddr),
DomainName(String, u16),
#[cfg(unix)]
UnixSocketAddr(PathBuf),
}
#[derive(Debug)]
pub struct ManagerAddrError;
impl FromStr for ManagerAddr {
type Err = ManagerAddrError;
fn from_str(s: &str) -> Result<ManagerAddr, ManagerAddrError> {
match s.find(':') {
Some(pos) => {
match s.parse::<SocketAddr>() {
Ok(saddr) => Ok(ManagerAddr::SocketAddr(saddr)),
Err(..) => {
let (sdomain, sport) = s.split_at(pos);
let (sdomain, sport) = (sdomain.trim(), sport[1..].trim());
match sport.parse::<u16>() {
Ok(port) => Ok(ManagerAddr::DomainName(sdomain.to_owned(), port)),
Err(..) => Err(ManagerAddrError),
}
}
}
}
#[cfg(unix)]
None => {
Ok(ManagerAddr::UnixSocketAddr(PathBuf::from(s)))
}
#[cfg(not(unix))]
None => Err(ManagerAddrError),
}
}
}
impl Display for ManagerAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ManagerAddr::SocketAddr(ref saddr) => fmt::Display::fmt(saddr, f),
ManagerAddr::DomainName(ref dname, port) => write!(f, "{}:{}", dname, port),
#[cfg(unix)]
ManagerAddr::UnixSocketAddr(ref path) => fmt::Display::fmt(&path.display(), f),
}
}
}
impl From<SocketAddr> for ManagerAddr {
fn from(addr: SocketAddr) -> ManagerAddr {
ManagerAddr::SocketAddr(addr)
}
}
impl<'a> From<(&'a str, u16)> for ManagerAddr {
fn from((dname, port): (&'a str, u16)) -> ManagerAddr {
ManagerAddr::DomainName(dname.to_owned(), port)
}
}
impl From<(String, u16)> for ManagerAddr {
fn from((dname, port): (String, u16)) -> ManagerAddr {
ManagerAddr::DomainName(dname, port)
}
}
#[cfg(unix)]
impl From<PathBuf> for ManagerAddr {
fn from(p: PathBuf) -> ManagerAddr {
ManagerAddr::UnixSocketAddr(p)
}
}