Skip to content

Commit 6cf7a22

Browse files
committed
[puter] add wisp v1 fallback support
1 parent ba136bc commit 6cf7a22

3 files changed

Lines changed: 72 additions & 31 deletions

File tree

server/src/config.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ use wisp_mux::{
2525
};
2626

2727
use crate::{
28-
handle::wisp::utils::get_certificates_from_paths, puter::PuterPasswordProtocolExtensionBuilder,
29-
CLI, CONFIG, RESOLVER,
28+
CLI, CONFIG, RESOLVER, handle::wisp::utils::get_certificates_from_paths, puter::{PuterPasswordProtocolExtension, PuterPasswordProtocolExtensionBuilder}
3029
};
3130

3231
pub const VERSION_STRING: &str = concat!(
@@ -290,6 +289,8 @@ struct ConfigCache {
290289
pub blocked_udp_hosts: RegexSet,
291290

292291
pub socks5_server: Option<SocketAddr>,
292+
293+
pub puter_auth_server: Option<Url>,
293294
}
294295

295296
lazy_static! {
@@ -319,6 +320,8 @@ lazy_static! {
319320
blocked_udp_hosts: RegexSet::new(&CONFIG.stream.block_udp_hosts).unwrap(),
320321

321322
socks5_server: CONFIG.stream.socks5_server.as_ref().map(|x| x.to_socket_addrs().expect("failed to resolve socks5 server").next().expect("failed to resolve socks5 server")),
323+
324+
puter_auth_server: CONFIG.wisp.puter_auth_server.as_ref().map(|x| Url::parse(x).expect("failed to parse puter auth server")),
322325
}
323326
};
324327
}
@@ -406,6 +409,11 @@ impl WispConfig {
406409
self.extensions.contains(&ProtocolExtension::Wispnet)
407410
}
408411

412+
#[doc(hidden)]
413+
pub fn puter_auth_server(&self) -> Option<&Url> {
414+
CONFIG_CACHE.puter_auth_server.as_ref()
415+
}
416+
409417
#[doc(hidden)]
410418
pub async fn to_opts(&self) -> anyhow::Result<(Option<WispV2Handshake>, u32)> {
411419
if self.wisp_v2 {
@@ -432,7 +440,8 @@ impl WispConfig {
432440
.context("failed to parse puter auth server")?,
433441
true,
434442
),
435-
))
443+
));
444+
required_extensions.push(PuterPasswordProtocolExtension::ID);
436445
}
437446

438447
match self.auth_extension {

server/src/puter.rs

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,36 @@ use wisp_mux::{
1414

1515
use crate::REQWEST_CLIENT;
1616

17+
#[derive(Serialize)]
18+
struct AuthRequest {
19+
token: String,
20+
}
21+
#[derive(Deserialize)]
22+
struct AuthResponse {
23+
#[serde(default)]
24+
allow: bool,
25+
}
26+
27+
pub async fn verify_relay_token(endpoint: &Url, token: &str) -> anyhow::Result<bool> {
28+
let origin = endpoint.origin().ascii_serialization();
29+
30+
let res = REQWEST_CLIENT
31+
.request(Method::POST, endpoint.clone())
32+
.header("Content-Type", "application/json")
33+
.header("Origin", origin)
34+
.json(&AuthRequest {
35+
token: token.to_string(),
36+
})
37+
.send()
38+
.await
39+
.context("failed to ask auth server for auth")?
40+
.json::<AuthResponse>()
41+
.await
42+
.context("auth server gave invalid response")?;
43+
44+
Ok(res.allow)
45+
}
46+
1747
/// ID of Puter password protocol extension.
1848
pub const PUTER_PASSWORD_PROTOCOL_EXTENSION_ID: u8 = 0x02;
1949

@@ -55,15 +85,6 @@ impl PuterPasswordProtocolExtension {
5585
pub const ID: u8 = PUTER_PASSWORD_PROTOCOL_EXTENSION_ID;
5686
}
5787

58-
#[derive(Serialize)]
59-
struct AuthRequest {
60-
token: String,
61-
}
62-
#[derive(Deserialize)]
63-
struct AuthResponse {
64-
allow: bool,
65-
}
66-
6788
#[async_trait]
6889
impl ProtocolExtension for PuterPasswordProtocolExtension {
6990
fn get_id(&self) -> u8 {
@@ -81,26 +102,12 @@ impl ProtocolExtension for PuterPasswordProtocolExtension {
81102
chosen_password,
82103
..
83104
} => {
84-
let origin = endpoint.origin().ascii_serialization();
85-
86-
let res = REQWEST_CLIENT
87-
.request(Method::POST, endpoint.clone())
88-
.header("Content-Type", "application/json")
89-
.header("Origin", origin)
90-
.json(&AuthRequest {
91-
token: chosen_password.clone(),
92-
})
93-
.send()
105+
if verify_relay_token(endpoint, &chosen_password)
94106
.await
95-
.context("failed to ask auth server for auth")
107+
.context("failed to verify relay token")
96108
.map_err(|x| WispError::ExtensionImplError(x.into()))?
97-
.json::<AuthResponse>()
98-
.await
99-
.context("auth server gave invalid response")
100-
.map_err(|x| WispError::ExtensionImplError(x.into()))?;
101-
102-
if res.allow {
103-
Ok(None)
109+
{
110+
Ok(None)
104111
} else {
105112
Ok(Some((
106113
CloseReason::ExtensionsPasswordAuthFailed,

server/src/route.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::{
2020
config::SocketTransport,
2121
generate_stats,
2222
listener::{ServerStream, ServerStreamExt, ServerStreamRead, ServerStreamWrite},
23+
puter::verify_relay_token,
2324
stream::WebSocketStreamWrapper,
2425
upgrade::{is_upgrade_request, upgrade},
2526
util_chain::{chain, Chain},
@@ -140,7 +141,31 @@ where
140141
};
141142

142143
let ws_protocol = headers.get(SEC_WEBSOCKET_PROTOCOL);
143-
let req_path = req.uri().path().to_string();
144+
let mut req_path = req.uri().path().to_string();
145+
146+
if let Some(server) = CONFIG.wisp.puter_auth_server() {
147+
let endpoint = CONFIG.wisp.prefix.clone() + "/";
148+
if req_path != endpoint {
149+
let trimmed = req_path.strip_prefix("/").unwrap_or(&req_path);
150+
151+
let Some(token_loc) = trimmed.find('/') else {
152+
debug!("sent non_ws_response to http client [no token found]");
153+
return non_ws_resp();
154+
};
155+
156+
if !verify_relay_token(server, &trimmed[..token_loc])
157+
.await
158+
.context("failed to verify relay token")?
159+
{
160+
debug!("sent non_ws_response to http client [token invalid]");
161+
return Ok(Response::builder()
162+
.status(StatusCode::UNAUTHORIZED)
163+
.body(Body::new(CONFIG.server.non_ws_response.as_bytes().into()))?);
164+
}
165+
166+
req_path = trimmed[token_loc..].to_string();
167+
}
168+
}
144169

145170
if req_path.ends_with(&(CONFIG.wisp.prefix.clone() + "/")) {
146171
let has_ws_protocol = ws_protocol.is_some();

0 commit comments

Comments
 (0)