Skip to content

Commit a935f6c

Browse files
committed
feat: client context, udp tunnel
1 parent 632f0bf commit a935f6c

File tree

11 files changed

+804
-128
lines changed

11 files changed

+804
-128
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ license = "MIT"
99
repository = "https://github.com/PocketRelay/PocketArkClientShared"
1010

1111
[dependencies]
12+
# Shared UDP tunnel protocol
13+
pocket-relay-udp-tunnel = { version = "0" }
14+
1215
# Logging
1316
log = "0.4"
1417

cspell.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"userWords": [
3+
"reqwest",
4+
"serverinstanceinfo",
5+
"valu",
6+
"trialservicename",
7+
"defaultdnsaddress",
8+
"redirector",
9+
"gosredirector",
10+
"seqno"
11+
]
12+
}

src/api.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ struct ServerDetails {
9292
ident: Option<String>,
9393
/// Association token if the server supports providing one
9494
association: Option<String>,
95+
/// Tunnel port if the server provides one
96+
tunnel_port: Option<u16>,
9597
}
9698

9799
/// Data from completing a lookup contains the resolved address
@@ -100,11 +102,13 @@ struct ServerDetails {
100102
#[derive(Debug, Clone)]
101103
pub struct LookupData {
102104
/// Server url
103-
pub url: Arc<Url>,
105+
pub url: Url,
104106
/// The server version
105107
pub version: Version,
106108
/// Association token if the server supports providing one
107-
pub association: Arc<Option<String>>,
109+
pub association: Option<String>,
110+
/// Tunnel port if the server provides one
111+
pub tunnel_port: Option<u16>,
108112
}
109113

110114
/// Errors that can occur while looking up a server
@@ -223,9 +227,10 @@ pub async fn lookup_server(
223227
}
224228

225229
Ok(LookupData {
226-
url: Arc::new(url),
230+
url,
227231
version: details.version,
228-
association: Arc::new(details.association),
232+
association: details.association,
233+
tunnel_port: details.tunnel_port,
229234
})
230235
}
231236

@@ -476,11 +481,11 @@ pub async fn proxy_http_request(
476481
/// ## Arguments
477482
/// * `http_client` - The HTTP client to connect with
478483
/// * `base_url` - The server base URL (Connection URL)
479-
/// * `association` - Optional association token
484+
/// * `association` - Association token
480485
pub async fn create_server_tunnel(
481-
http_client: reqwest::Client,
486+
http_client: &reqwest::Client,
482487
base_url: &Url,
483-
association: Option<&String>,
488+
association: &str,
484489
) -> Result<Upgraded, ServerStreamError> {
485490
// Create the upgrade endpoint URL
486491
let endpoint_url: Url = base_url
@@ -496,12 +501,10 @@ pub async fn create_server_tunnel(
496501
.collect();
497502

498503
// Include association token
499-
if let Some(association) = association {
500-
headers.insert(
501-
HeaderName::from_static(headers::ASSOCIATION),
502-
HeaderValue::from_str(association).expect("Invalid association token"),
503-
);
504-
}
504+
headers.insert(
505+
HeaderName::from_static(headers::ASSOCIATION),
506+
HeaderValue::from_str(association).expect("Invalid association token"),
507+
);
505508

506509
// Send the HTTP request and get its response
507510
let response = http_client

src/ctx.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//! Shared context state that the app should store and pass to the
2+
//! various servers when they are started
3+
4+
use url::Url;
5+
6+
use crate::api::AuthToken;
7+
8+
/// Shared context
9+
pub struct ClientContext {
10+
/// HTTP client for the client to make requests with
11+
pub http_client: reqwest::Client,
12+
/// Base URL of the connected server
13+
pub base_url: Url,
14+
/// Optional association token
15+
pub association: Option<String>,
16+
/// Optional tunnel port for tunnel V2 if available
17+
pub tunnel_port: Option<u16>,
18+
/// Authentication token
19+
pub token: AuthToken,
20+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub use semver::Version;
1414
pub use url::Url;
1515

1616
pub mod api;
17+
pub mod ctx;
1718
pub mod servers;
1819
pub mod ssl;
1920
pub mod update;

src/servers/blaze.rs

Lines changed: 20 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,53 @@
11
//! Server connected to by BlazeSDK clients (Majority of the game traffic)
22
33
use super::{spawn_server_task, BLAZE_PORT};
4-
use crate::api::{create_server_stream, AuthToken};
4+
use crate::{api::create_server_stream, ctx::ClientContext};
55
use log::{debug, error};
66
use std::{net::Ipv4Addr, sync::Arc};
77
use tokio::{
88
io::copy_bidirectional,
99
net::{TcpListener, TcpStream},
1010
};
11-
use url::Url;
1211

1312
/// Starts the blaze server
1413
///
1514
/// ## Arguments
16-
/// * `http_client` - The HTTP client passed around for connection upgrades
17-
/// * `base_url` - The server base URL to connect clients to
18-
/// * `association` - Optional client association
19-
pub async fn start_blaze_server(
20-
http_client: reqwest::Client,
21-
base_url: Arc<Url>,
22-
association: Arc<Option<String>>,
23-
token: AuthToken,
24-
) -> std::io::Result<()> {
15+
/// * `ctx` - The client context
16+
pub async fn start_blaze_server(ctx: Arc<ClientContext>) -> std::io::Result<()> {
2517
// Bind the local socket for accepting connections
2618
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, BLAZE_PORT)).await?;
2719

2820
// Accept connections
2921
loop {
3022
let (client_stream, _) = listener.accept().await?;
3123

32-
spawn_server_task(handle(
33-
client_stream,
34-
http_client.clone(),
35-
base_url.clone(),
36-
association.clone(),
37-
token.clone(),
38-
));
24+
spawn_server_task(handle(client_stream, ctx.clone()));
3925
}
4026
}
4127

4228
/// Handler for processing BlazeSDK client connections
4329
///
4430
/// ## Arguments
4531
/// * `client_stream` - The client stream to read and write from
46-
/// * `http_client` - The HTTP client passed around for connection upgrades
47-
/// * `base_url` - The server base URL to connect clients to
48-
/// * `association` - Client association token if supported
49-
/// * `token` - Client authentication token
50-
async fn handle(
51-
mut client_stream: TcpStream,
52-
http_client: reqwest::Client,
53-
base_url: Arc<Url>,
54-
association: Arc<Option<String>>,
55-
token: AuthToken,
56-
) {
32+
/// * `ctx` - The client context
33+
async fn handle(mut client_stream: TcpStream, ctx: Arc<ClientContext>) {
5734
debug!("Starting blaze connection");
5835

5936
// Create a stream to the Pocket Relay server
60-
let mut server_stream =
61-
match create_server_stream(http_client, &base_url, Option::as_ref(&association), token)
62-
.await
63-
{
64-
Ok(stream) => stream,
65-
Err(err) => {
66-
error!("Failed to create server stream: {}", err);
67-
return;
68-
}
69-
};
37+
let mut server_stream = match create_server_stream(
38+
ctx.http_client.clone(),
39+
&ctx.base_url,
40+
Option::as_ref(&ctx.association),
41+
ctx.token.clone(),
42+
)
43+
.await
44+
{
45+
Ok(stream) => stream,
46+
Err(err) => {
47+
error!("Failed to create server stream: {}", err);
48+
return;
49+
}
50+
};
7051

7152
debug!("Blaze connection linked");
7253

src/servers/http.rs

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
//! is only capable of communicating over SSLv3
44
55
use super::{spawn_server_task, HTTP_PORT};
6-
use crate::api::{headers::X_TOKEN, proxy_http_request, AuthToken};
6+
use crate::{
7+
api::{headers::X_TOKEN, proxy_http_request},
8+
ctx::ClientContext,
9+
};
710
use anyhow::Context;
811
use hyper::{
912
body::HttpBody, header::HeaderValue, http::uri::PathAndQuery, server::conn::Http,
@@ -14,7 +17,6 @@ use openssl::ssl::{Ssl, SslContext};
1417
use std::{convert::Infallible, net::Ipv4Addr, pin::Pin, sync::Arc};
1518
use tokio::net::{TcpListener, TcpStream};
1619
use tokio_openssl::SslStream;
17-
use url::Url;
1820

1921
/// Starts the HTTP proxy server
2022
///
@@ -24,29 +26,23 @@ use url::Url;
2426
/// * `context` - The SSL context to use when accepting clients
2527
/// * `token` - The authentication token
2628
pub async fn start_http_server(
27-
http_client: reqwest::Client,
28-
base_url: Arc<Url>,
29+
ctx: Arc<ClientContext>,
2930
ssl_context: SslContext,
30-
token: AuthToken,
31-
) -> anyhow::Result<()> {
31+
) -> std::io::Result<()> {
3232
// Bind the local tcp socket for accepting connections
33-
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, HTTP_PORT))
34-
.await
35-
.context("Failed to bind listener")?;
33+
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, HTTP_PORT)).await?;
3634

3735
// Accept connections
3836
loop {
3937
let (stream, _) = listener.accept().await?;
4038

41-
let ssl = Ssl::new(&ssl_context).context("Failed to get ssl instance")?;
42-
let stream = SslStream::new(ssl, stream).context("Failed to create ssl stream")?;
39+
let ssl = Ssl::new(&ssl_context).map_err(std::io::Error::other)?;
40+
let stream = SslStream::new(ssl, stream).map_err(std::io::Error::other)?;
4341

44-
let http_client = http_client.clone();
45-
let base_url = base_url.clone();
46-
let token = token.clone();
42+
let ctx = ctx.clone();
4743

4844
spawn_server_task(async move {
49-
if let Err(err) = serve_connection(stream, http_client, base_url, token).await {
45+
if let Err(err) = serve_connection(stream, ctx).await {
5046
error!("Error while redirecting: {}", err);
5147
}
5248
});
@@ -57,23 +53,14 @@ pub async fn start_http_server(
5753
/// completes the accept stream process
5854
pub async fn serve_connection(
5955
mut stream: SslStream<TcpStream>,
60-
http_client: reqwest::Client,
61-
base_url: Arc<Url>,
62-
token: AuthToken,
56+
ctx: Arc<ClientContext>,
6357
) -> anyhow::Result<()> {
6458
Pin::new(&mut stream).accept().await?;
6559

6660
Http::new()
6761
.serve_connection(
6862
stream,
69-
service_fn(move |request| {
70-
handle(
71-
request,
72-
http_client.clone(),
73-
base_url.clone(),
74-
token.clone(),
75-
)
76-
}),
63+
service_fn(move |request| handle(request, ctx.clone())),
7764
)
7865
.await
7966
.context("Serve error")?;
@@ -90,9 +77,7 @@ pub async fn serve_connection(
9077
/// * `base_url` - The server base URL (Connection URL)
9178
async fn handle(
9279
mut request: Request<Body>,
93-
http_client: reqwest::Client,
94-
base_url: Arc<Url>,
95-
token: AuthToken,
80+
ctx: Arc<ClientContext>,
9681
) -> Result<Response<Body>, Infallible> {
9782
let path_and_query = request
9883
.uri()
@@ -107,7 +92,7 @@ async fn handle(
10792
let path_and_query = path_and_query.strip_prefix('/').unwrap_or(path_and_query);
10893

10994
// Create the new url from the path
110-
let url = match base_url.join(path_and_query) {
95+
let url = match ctx.base_url.join(path_and_query) {
11196
Ok(value) => value,
11297
Err(err) => {
11398
error!("Failed to create HTTP proxy URL: {}", err);
@@ -134,11 +119,11 @@ async fn handle(
134119
let mut headers = request.headers().clone();
135120
headers.insert(
136121
X_TOKEN,
137-
HeaderValue::from_str(&token).expect("Invalid token"),
122+
HeaderValue::from_str(&ctx.token).expect("Invalid token"),
138123
);
139124

140125
// Proxy the request to the server
141-
let response = match proxy_http_request(&http_client, url, method, body, headers).await {
126+
let response = match proxy_http_request(&ctx.http_client, url, method, body, headers).await {
142127
Ok(value) => value,
143128
Err(err) => {
144129
error!("Failed to proxy HTTP request: {}", err);

src/servers/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod http;
99
pub mod qos;
1010
pub mod redirector;
1111
pub mod tunnel;
12+
pub mod udp_tunnel;
1213

1314
/// The port the Redirector server will bind to
1415
pub const REDIRECTOR_PORT: u16 = 42230;
@@ -18,7 +19,6 @@ pub const BLAZE_PORT: u16 = 42128;
1819
pub const QOS_PORT: u16 = 42130;
1920
/// The port the HTTP server will bind to
2021
pub const HTTP_PORT: u16 = 443;
21-
2222
/// The port used for the host socket
2323
pub const TUNNEL_HOST_PORT: u16 = 42132;
2424
/// Port that the OS may choose

src/servers/redirector.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,16 @@ use tokio_openssl::SslStream;
2020
///
2121
/// ## Arguments
2222
/// * `context` - The SSL context to use when accepting clients
23-
pub async fn start_redirector_server(ssl_context: SslContext) -> anyhow::Result<()> {
23+
pub async fn start_redirector_server(ssl_context: SslContext) -> std::io::Result<()> {
2424
// Bind the local tcp socket for accepting connections
25-
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, REDIRECTOR_PORT))
26-
.await
27-
.context("Failed to bind listener")?;
25+
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, REDIRECTOR_PORT)).await?;
2826

2927
// Accept connections
3028
loop {
3129
let (stream, _) = listener.accept().await?;
3230

33-
let ssl = Ssl::new(&ssl_context).context("Failed to get ssl instance")?;
34-
let stream = SslStream::new(ssl, stream).context("Failed to create ssl stream")?;
31+
let ssl = Ssl::new(&ssl_context).map_err(std::io::Error::other)?;
32+
let stream = SslStream::new(ssl, stream).map_err(std::io::Error::other)?;
3533

3634
spawn_server_task(async move {
3735
if let Err(err) = serve_connection(stream).await {

0 commit comments

Comments
 (0)