initial commit

This commit is contained in:
Tove 2025-10-12 20:38:46 +02:00
commit 99713a31d1
Signed by: TudbuT
GPG key ID: B3CF345217F202D3
6 changed files with 177 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View file

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "tcpudp"
version = "0.1.0"

6
Cargo.toml Normal file
View file

@ -0,0 +1,6 @@
[package]
name = "tcpudp"
version = "0.1.0"
edition = "2024"
[dependencies]

54
src/main.rs Normal file
View file

@ -0,0 +1,54 @@
mod tcp_to_udp;
mod udp_to_tcp;
use std::env::args;
static ARGS: &str = "args: <listen: 'udp'/'tcp'> <tcp port> <udp port>";
pub struct Ports {
pub tcp: u16,
pub udp: u16,
}
impl Ports {
fn get(mut rest_of_args: impl Iterator<Item = String>) -> Ports {
Ports {
tcp: rest_of_args
.next()
.and_then(|x| x.parse::<u16>().ok())
.expect(ARGS),
udp: rest_of_args
.next()
.and_then(|x| x.parse::<u16>().ok())
.expect(ARGS),
}
}
}
enum Mode {
UdpToTcp(Ports),
TcpToUdp(Ports),
}
impl Mode {
fn get() -> Mode {
let mut args = args().skip(1);
match args.next().expect(ARGS).as_str() {
"udp" => Mode::UdpToTcp(Ports::get(args)),
"tcp" => Mode::TcpToUdp(Ports::get(args)),
_ => panic!("{}", ARGS),
}
}
fn run(self) {
match self {
Self::UdpToTcp(ports) => ports.udp_to_tcp(),
Self::TcpToUdp(ports) => ports.tcp_to_udp(),
}
}
}
fn main() {
let mode = Mode::get();
mode.run();
}

50
src/tcp_to_udp.rs Normal file
View file

@ -0,0 +1,50 @@
use std::{
io::{Read, Write},
net::{TcpListener, UdpSocket},
thread,
};
use crate::Ports;
impl Ports {
pub fn tcp_to_udp(self) {
// TCP server, UDP client
let tcp_listener = TcpListener::bind(("::0", self.tcp)).unwrap();
loop {
let mut tcp = tcp_listener.accept().unwrap().0;
thread::spawn(move || {
let udp = UdpSocket::bind(("127.0.0.1", 0)).unwrap();
udp.connect(("127.0.0.1", self.udp)).unwrap();
{
let (mut tcp, udp) = (tcp.try_clone().unwrap(), udp.try_clone().unwrap());
thread::spawn(move || {
let mut buf = [0u8; 10 * 1024];
loop {
let len = udp.recv(&mut buf).unwrap();
if tcp
.write_all(&(len as u32).to_be_bytes())
.and_then(|_| tcp.write_all(&buf[..len]))
.is_err()
{
break;
}
}
});
}
let mut buf4 = [0u8; 4];
let mut buf = [0u8; 10 * 1024];
loop {
if tcp.read_exact(&mut buf4).is_err() {
break;
}
let len = u32::from_be_bytes(buf4) as usize;
if tcp.read_exact(&mut buf[..len]).is_err() {
break;
}
udp.send(&buf[..len]).unwrap();
}
});
}
}
}

59
src/udp_to_tcp.rs Normal file
View file

@ -0,0 +1,59 @@
use std::{
collections::HashMap,
io::{Read, Write},
net::{Shutdown, SocketAddr, TcpStream, UdpSocket},
thread,
};
use crate::Ports;
impl Ports {
pub fn udp_to_tcp(self) {
// UDP server, TCP client
let udp_listener = UdpSocket::bind(("::0", self.udp)).unwrap();
let mut buf = [0u8; 1024 * 10];
let mut map: HashMap<SocketAddr, TcpStream> = HashMap::new();
loop {
let (len, addr) = udp_listener.recv_from(&mut buf).unwrap();
let mut tcp_socket = map.get(&addr);
if tcp_socket.is_none() {
let tcp = TcpStream::connect(("127.0.0.1", self.tcp)).unwrap();
{
let (mut tcp, addr, udp) = (
tcp.try_clone().unwrap(),
addr,
udp_listener.try_clone().unwrap(),
);
thread::spawn(move || {
let mut buf4 = [0u8; 4];
let mut buf = [0u8; 1024 * 10];
loop {
if tcp.read_exact(&mut buf4).is_err() {
break;
}
let len = u32::from_be_bytes(buf4) as usize;
if tcp.read_exact(&mut buf[..len]).is_err() {
break;
}
udp.send_to(&buf[..len], addr).unwrap();
}
});
}
map.insert(addr, tcp);
tcp_socket = map.get(&addr);
}
let Some(mut tcp_socket) = tcp_socket else {
unreachable!()
};
if tcp_socket
.write_all(&(len as u32).to_be_bytes())
.and_then(|_| tcp_socket.write_all(&buf[..len]))
.is_err()
{
let _ = tcp_socket.shutdown(Shutdown::Both);
map.remove(&addr);
}
}
}
}