Compare commits

...

3 Commits

Author SHA1 Message Date
884018a90d Add readme 2022-08-30 15:26:23 +02:00
53ad29727e Can specify tokens in a file 2022-08-30 15:17:13 +02:00
dde219a717 Close Websocket when upstream TCP connection is closed 2022-08-30 15:09:39 +02:00
4 changed files with 52 additions and 2 deletions

22
README.MD Normal file
View File

@@ -0,0 +1,22 @@
# TCP over HTTP
This project aims to provide an easy-to-setup TCP forwarding solution:
```
|--------| |--------| |--------| | -------|
| | | Client | | Server | | |
| Client | -- TCP xx -- | | -- HTTP 80 / 443 -- | | -- TCP xx -- | Server |
| | | Relay | | Relay | | |
|--------|   |--------| |--------| |--------|
```
This project can be used especially to bypass firewalls that blocks traffics
from ports others than the 80 / 443 duo.
This repository contains two binaries:
* `tpc_relay_server`: The server relay
* `tcp_relay_client`: The client relay
The clients relay authenticates itself to the server using a token.
A single server - client relay pair can relay multiple ports simultaneously from the same machine.

View File

@@ -8,6 +8,10 @@ pub struct Args {
#[clap(short, long)] #[clap(short, long)]
pub tokens: Vec<String>, pub tokens: Vec<String>,
/// Access tokens stored in a file, one token per line
#[clap(long)]
pub tokens_file: Option<String>,
/// Forwarded ports /// Forwarded ports
#[clap(short, long)] #[clap(short, long)]
pub ports: Vec<u16>, pub ports: Vec<u16>,

View File

@@ -35,14 +35,22 @@ pub async fn config_route(req: HttpRequest, data: Data<Arc<Args>>) -> impl Respo
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
let args: Args = Args::parse(); let mut args: Args = Args::parse();
let args = Arc::new(args);
if args.ports.is_empty() { if args.ports.is_empty() {
log::error!("No port to forward!"); log::error!("No port to forward!");
std::process::exit(2); std::process::exit(2);
} }
// Read tokens from file, if any
if let Some(file) = &args.tokens_file {
std::fs::read_to_string(file)
.expect("Failed to read tokens file!")
.split('\n')
.filter(|l| !l.is_empty())
.for_each(|t| args.tokens.push(t.to_string()));
}
if args.tokens.is_empty() { if args.tokens.is_empty() {
log::error!("No tokens specified!"); log::error!("No tokens specified!");
std::process::exit(3); std::process::exit(3);
@@ -50,6 +58,7 @@ async fn main() -> std::io::Result<()> {
log::info!("Starting relay on http://{}", args.listen_address); log::info!("Starting relay on http://{}", args.listen_address);
let args = Arc::new(args);
let args_clone = args.clone(); let args_clone = args.clone();
HttpServer::new(move || { HttpServer::new(move || {
App::new() App::new()

View File

@@ -4,6 +4,7 @@ use std::time::{Duration, Instant};
use actix::{Actor, ActorContext, AsyncContext, Handler, Message, StreamHandler}; use actix::{Actor, ActorContext, AsyncContext, Handler, Message, StreamHandler};
use actix_web::{Error, HttpRequest, HttpResponse, web}; use actix_web::{Error, HttpRequest, HttpResponse, web};
use actix_web_actors::ws; use actix_web_actors::ws;
use actix_web_actors::ws::{CloseCode, CloseReason};
use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
use tokio::net::TcpStream; use tokio::net::TcpStream;
@@ -20,6 +21,9 @@ const CLIENT_TIMEOUT: Duration = Duration::from_secs(60);
#[rtype(result = "bool")] #[rtype(result = "bool")]
pub struct DataForWebSocket(Vec<u8>); pub struct DataForWebSocket(Vec<u8>);
#[derive(Message)]
#[rtype(result = "()")]
pub struct TCPReadEndClosed;
/// Define HTTP actor /// Define HTTP actor
struct RelayWS { struct RelayWS {
@@ -72,6 +76,7 @@ impl Actor for RelayWS {
Ok(l) => { Ok(l) => {
if l == 0 { if l == 0 {
log::info!("Got empty read. Closing read end..."); log::info!("Got empty read. Closing read end...");
addr.do_send(TCPReadEndClosed);
return; return;
} }
@@ -129,6 +134,16 @@ impl Handler<DataForWebSocket> for RelayWS {
} }
} }
impl Handler<TCPReadEndClosed> for RelayWS {
type Result = ();
fn handle(&mut self, _msg: TCPReadEndClosed, ctx: &mut Self::Context) -> Self::Result {
ctx.close(Some(CloseReason {
code: CloseCode::Away,
description: Some("TCP read end closed.".to_string()),
}));
}
}
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct WebSocketQuery { pub struct WebSocketQuery {