feat(shadowsocks): ServerConfig::new returns Result

- fix #1770
- allow users to check errors instead of just panic on wrong passwords
This commit is contained in:
zonyitoo
2024-11-17 01:18:05 +08:00
parent c52b77d112
commit c85a74194f
15 changed files with 116 additions and 109 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "shadowsocks-service"
version = "1.21.3"
version = "1.22.0"
authors = ["Shadowsocks Contributors"]
description = "shadowsocks is a fast tunnel proxy that helps you bypass firewalls."
repository = "https://github.com/shadowsocks/shadowsocks-rust"
@@ -215,7 +215,7 @@ serde = { version = "1.0", features = ["derive"] }
json5 = "0.4"
bson = { version = "2.13.0", optional = true }
shadowsocks = { version = "1.21.1", path = "../shadowsocks", default-features = false }
shadowsocks = { version = "1.22.0", path = "../shadowsocks", default-features = false }
# Just for the ioctl call macro
[target.'cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "openbsd"))'.dependencies]

View File

@@ -1935,7 +1935,17 @@ impl Config {
}
};
let mut nsvr = ServerConfig::new(addr, password, method);
let mut nsvr = match ServerConfig::new(addr, password, method) {
Ok(svr) => svr,
Err(serr) => {
let err = Error::new(
ErrorKind::Malformed,
"server config create failed",
Some(format!("{}", serr)),
);
return Err(err);
}
};
nsvr.set_source(server_source);
nsvr.set_mode(global_mode);
@@ -2055,7 +2065,17 @@ impl Config {
}
};
let mut nsvr = ServerConfig::new(addr, password, method);
let mut nsvr = match ServerConfig::new(addr, password, method) {
Ok(svr) => svr,
Err(serr) => {
let err = Error::new(
ErrorKind::Malformed,
"server config create failed",
Some(format!("{}", serr)),
);
return Err(err);
}
};
nsvr.set_source(server_source);
// Extensible Identity Header, Users

View File

@@ -479,7 +479,13 @@ impl Manager {
None => return Ok(AddResponse("method is required")),
};
let mut svr_cfg = ServerConfig::new(addr, req.password.clone(), method);
let mut svr_cfg = match ServerConfig::new(addr, req.password.clone(), method) {
Ok(svr_cfg) => svr_cfg,
Err(err) => {
error!("failed to create ServerConfig, error: {}", err);
return Ok(AddResponse(format!("invalid server")));
}
};
if let Some(ref plugin) = req.plugin {
let p = PluginConfig {

View File

@@ -1,6 +1,6 @@
[package]
name = "shadowsocks"
version = "1.21.1"
version = "1.22.0"
authors = ["Shadowsocks Contributors"]
description = "shadowsocks is a fast tunnel proxy that helps you bypass firewalls."
repository = "https://github.com/shadowsocks/shadowsocks-rust"

View File

@@ -4,7 +4,6 @@
use std::path::PathBuf;
use std::{
collections::HashMap,
error,
fmt::{self, Debug, Display},
net::SocketAddr,
str::{self, FromStr},
@@ -381,6 +380,18 @@ pub enum ServerSource {
OnlineConfig, //< Created from online configuration (SIP008)
}
/// Errors when creating a new ServerConfig
#[derive(Debug, Clone, Error)]
pub enum ServerConfigError {
/// Invalid base64 encoding of password
#[error("invalid key encoding for {0}, {1}")]
InvalidKeyEncoding(CipherKind, base64::DecodeError),
/// Key length mismatch
#[error("invalid key length for {0}, expecting {1} bytes, but found {2} bytes")]
InvalidKeyLength(CipherKind, usize, usize),
}
/// Configuration for a server
#[derive(Clone, Debug)]
pub struct ServerConfig {
@@ -426,29 +437,23 @@ pub struct ServerConfig {
}
#[inline]
fn make_derived_key(method: CipherKind, password: &str, enc_key: &mut [u8]) {
fn make_derived_key(method: CipherKind, password: &str, enc_key: &mut [u8]) -> Result<(), ServerConfigError> {
#[cfg(feature = "aead-cipher-2022")]
if method.is_aead_2022() {
// AEAD 2022 password is a base64 form of enc_key
match AEAD2022_PASSWORD_BASE64_ENGINE.decode(password) {
Ok(v) => {
if v.len() != enc_key.len() {
panic!(
"{} is expecting a {} bytes key, but password: {} ({} bytes after decode)",
method,
enc_key.len(),
password,
v.len()
);
return Err(ServerConfigError::InvalidKeyLength(method, enc_key.len(), v.len()));
}
enc_key.copy_from_slice(&v);
}
Err(err) => {
panic!("{method} password {password} is not base64 encoded, error: {err}");
return Err(ServerConfigError::InvalidKeyEncoding(method, err));
}
}
return;
return Ok(());
}
cfg_if! {
@@ -462,6 +467,8 @@ fn make_derived_key(method: CipherKind, password: &str, enc_key: &mut [u8]) {
unreachable!("{method} don't know how to make a derived key");
}
}
Ok(())
}
/// Check if method supports Extended Identity Header
@@ -476,7 +483,7 @@ pub fn method_support_eih(method: CipherKind) -> bool {
)
}
fn password_to_keys<P>(method: CipherKind, password: P) -> (String, Box<[u8]>, Vec<Bytes>)
fn password_to_keys<P>(method: CipherKind, password: P) -> Result<(String, Box<[u8]>, Vec<Bytes>), ServerConfigError>
where
P: Into<String>,
{
@@ -491,7 +498,7 @@ where
warn!("method \"none\" doesn't need a password, which should be set as an empty String, but password.len() = {}", password.len());
}
return (password, Vec::new().into_boxed_slice(), Vec::new());
return Ok((password, Vec::new().into_boxed_slice(), Vec::new()));
}
#[cfg(feature = "stream-cipher")]
@@ -499,7 +506,7 @@ where
// TABLE cipher doesn't need key derivation.
// Reference implemenation: shadowsocks-libev, shadowsocks (Python)
let enc_key = password.clone().into_bytes().into_boxed_slice();
return (password, enc_key, Vec::new());
return Ok((password, enc_key, Vec::new()));
}
#[allow(unreachable_patterns)]
@@ -518,7 +525,7 @@ where
let upsk = split_iter.next().expect("uPSK");
let mut enc_key = vec![0u8; method.key_len()].into_boxed_slice();
make_derived_key(method, upsk, &mut enc_key);
make_derived_key(method, upsk, &mut enc_key)?;
for ipsk in split_iter {
match USER_KEY_BASE64_ENGINE.decode(ipsk) {
@@ -533,25 +540,25 @@ where
identity_keys.reverse();
return (upsk.to_owned(), enc_key, identity_keys);
return Ok((upsk.to_owned(), enc_key, identity_keys));
}
let mut enc_key = vec![0u8; method.key_len()].into_boxed_slice();
make_derived_key(method, &password, &mut enc_key);
make_derived_key(method, &password, &mut enc_key)?;
(password, enc_key, Vec::new())
Ok((password, enc_key, Vec::new()))
}
impl ServerConfig {
/// Create a new `ServerConfig`
pub fn new<A, P>(addr: A, password: P, method: CipherKind) -> ServerConfig
pub fn new<A, P>(addr: A, password: P, method: CipherKind) -> Result<ServerConfig, ServerConfigError>
where
A: Into<ServerAddr>,
P: Into<String>,
{
let (password, enc_key, identity_keys) = password_to_keys(method, password);
let (password, enc_key, identity_keys) = password_to_keys(method, password)?;
ServerConfig {
Ok(ServerConfig {
addr: addr.into(),
password,
method,
@@ -566,21 +573,23 @@ impl ServerConfig {
mode: Mode::TcpAndUdp, // Server serves TCP & UDP by default
weight: ServerWeight::new(),
source: ServerSource::Default,
}
})
}
/// Set encryption method
pub fn set_method<P>(&mut self, method: CipherKind, password: P)
pub fn set_method<P>(&mut self, method: CipherKind, password: P) -> Result<(), ServerConfigError>
where
P: Into<String>,
{
self.method = method;
let (password, enc_key, identity_keys) = password_to_keys(method, password);
let (password, enc_key, identity_keys) = password_to_keys(method, password)?;
self.password = password;
self.enc_key = enc_key;
self.identity_keys = Arc::new(identity_keys);
Ok(())
}
/// Set plugin
@@ -912,7 +921,7 @@ impl ServerConfig {
return Err(UrlParseError::InvalidMethod);
}
};
let mut svrconfig = ServerConfig::new(addr, pwd, method);
let mut svrconfig = ServerConfig::new(addr, pwd, method)?;
if let Some(q) = parsed.query() {
let query = match serde_urlencoded::from_bytes::<Vec<(String, String)>>(q.as_bytes()) {
@@ -961,52 +970,26 @@ impl ServerConfig {
}
/// Shadowsocks URL parsing Error
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Error)]
pub enum UrlParseError {
ParseError(url::ParseError),
#[error("{0}")]
ParseError(#[from] url::ParseError),
#[error("URL must have \"ss://\" scheme")]
InvalidScheme,
#[error("unknown encryption method")]
InvalidMethod,
#[error("invalid user info")]
InvalidUserInfo,
#[error("missing host")]
MissingHost,
#[error("invalid authentication info")]
InvalidAuthInfo,
#[error("invalid server address")]
InvalidServerAddr,
#[error("invalid query string")]
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::InvalidMethod => write!(f, "unknown encryption method"),
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::InvalidMethod => None,
UrlParseError::InvalidUserInfo => None,
UrlParseError::MissingHost => None,
UrlParseError::InvalidAuthInfo => None,
UrlParseError::InvalidServerAddr => None,
UrlParseError::InvalidQueryString => None,
}
}
#[error("{0}")]
ServerConfigError(#[from] ServerConfigError),
}
impl FromStr for ServerConfig {

View File

@@ -90,7 +90,7 @@ async fn tcp_tunnel_example(
password: &str,
method: CipherKind,
) -> io::Result<()> {
let svr_cfg_server = ServerConfig::new(server_addr, password, method);
let svr_cfg_server = ServerConfig::new(server_addr, password, method).unwrap();
let svr_cfg_local = svr_cfg_server.clone();
let ctx_server = Context::new_shared(ServerType::Server);

View File

@@ -32,7 +32,7 @@ use tokio::{
async fn tcp_tunnel_tfo() {
let _ = env_logger::try_init();
let svr_cfg = ServerConfig::new(("127.0.0.1", 41000), "", CipherKind::NONE);
let svr_cfg = ServerConfig::new(("127.0.0.1", 41000), "", CipherKind::NONE).unwrap();
let svr_cfg_client = svr_cfg.clone();
tokio::spawn(async move {

View File

@@ -60,7 +60,7 @@ async fn udp_tunnel_echo(
password: &str,
method: CipherKind,
) -> io::Result<()> {
let svr_cfg_server = ServerConfig::new(server_addr, password, method);
let svr_cfg_server = ServerConfig::new(server_addr, password, method).unwrap();
let svr_cfg_local = svr_cfg_server.clone();
let ctx_server = Context::new_shared(ServerType::Server);