Skip to content

Commit a6e7abb

Browse files
committed
Add support for TLS clients over :5811
1 parent 034fccc commit a6e7abb

File tree

6 files changed

+181
-8
lines changed

6 files changed

+181
-8
lines changed

Cargo.lock

+97
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ edition = "2018"
88

99
[dependencies]
1010
async-std = { version = "1.6.3", features = ["unstable"] }
11-
async-tungstenite = "0.8.0"
11+
async-tungstenite = { version = "0.8.0", features = ["async-tls"] }
1212
serde_json = "1.0.57"
1313
proto = { path = "../proto" }
1414
futures-util = "0.3.5"
@@ -24,3 +24,5 @@ rmp-serde = "0.14.4"
2424
chrono = "0.4.15"
2525
serde = { version = "1.0.116", features = ["derive"] }
2626
fern = { version = "0.6.0", features = ["colored"] }
27+
async-tls = "0.10.0"
28+
rustls = "0.18.1"

server/persisted_entries.bin

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

server/src/net.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ use futures::prelude::*;
66
use futures_util::core_reexport::pin::Pin;
77
use futures_util::core_reexport::task::{Context, Poll};
88
use proto::prelude::*;
9+
use async_tls::server::TlsStream;
10+
11+
type MaybeTlsStream = WebSocketStream<async_tungstenite::stream::Stream<TcpStream, TlsStream<TcpStream>>>;
912

1013
pub struct NTSocket {
11-
sock: WebSocketStream<TcpStream>,
14+
sock: MaybeTlsStream,
1215
}
1316

1417
impl NTSocket {
15-
pub fn new(sock: WebSocketStream<TcpStream>) -> NTSocket {
18+
pub fn new(sock: MaybeTlsStream) -> NTSocket {
1619
NTSocket { sock }
1720
}
1821
}

server/src/server.rs

+49-5
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,26 @@ use crate::net::NTSocket;
44
use async_std::net::TcpListener;
55
use async_std::sync::{channel, Arc, Mutex, Sender};
66
use async_std::task;
7+
use async_std::io;
78
use async_tungstenite::tungstenite::handshake::server::{Request, Response};
89
use async_tungstenite::tungstenite::http::{HeaderValue, StatusCode};
910
use itertools::Itertools;
1011
use proto::prelude::{DataType, MessageBody, NTBinaryMessage, NTMessage, NTTextMessage};
1112
use std::collections::HashMap;
1213
use std::ops::DerefMut;
14+
use crate::persist::restore_persistent;
15+
use proto::prelude::publish::SetFlags;
1316

1417
mod broadcast;
1518
use broadcast::*;
1619

1720
mod loop_;
18-
use crate::persist::restore_persistent;
1921
use loop_::*;
20-
use proto::prelude::publish::SetFlags;
22+
23+
mod tls;
24+
use tls::*;
25+
use async_tungstenite::stream::Stream;
26+
use std::time::Duration;
2127

2228
pub static MAX_BATCHING_SIZE: usize = 5;
2329

@@ -39,9 +45,46 @@ async fn tcp_loop(state: Arc<Mutex<NTServer>>, tx: Sender<ServerMessage>) -> any
3945
let listener = TcpListener::bind("0.0.0.0:5810").await?;
4046

4147
while let Ok((sock, addr)) = listener.accept().await {
42-
log::info!("TCP connection at {}", addr);
48+
log::info!("Unsecure TCP connection at {}", addr);
49+
let cid = rand::random::<u32>();
50+
let sock = async_tungstenite::accept_hdr_async(Stream::Plain(sock), |req: &Request, mut res: Response| {
51+
let ws_proto = req.headers().iter().find(|(hdr, _)| **hdr == "Sec-WebSocket-Protocol");
52+
53+
match ws_proto.map(|(_, s)| s.to_str().unwrap()) {
54+
Some("networktables.first.wpi.edu") => {
55+
res.headers_mut().insert("Sec-WebSocket-Protocol", HeaderValue::from_static("networktables.first.wpi.edu"));
56+
Ok(res)
57+
}
58+
_ => {
59+
log::error!("Rejecting client that did not specify correct subprotocol");
60+
Err(Response::builder()
61+
.status(StatusCode::BAD_REQUEST)
62+
.body(Some("Protocol 'networktables.first.wpi.edu' required to communicate with this server".to_string()))
63+
.unwrap())
64+
}
65+
}
66+
}).await;
67+
68+
if let Ok(sock) = sock {
69+
log::info!("Client assigned CID {}", cid);
70+
let client = ConnectedClient::new(NTSocket::new(sock), tx.clone(), cid);
71+
state.lock().await.clients.insert(cid, client);
72+
task::spawn(update_new_client(cid, state.clone()));
73+
}
74+
}
75+
Ok(())
76+
}
77+
78+
async fn tls_loop(state: Arc<Mutex<NTServer>>, tx: Sender<ServerMessage>) -> anyhow::Result<()> {
79+
let listener = TcpListener::bind("0.0.0.0:5811").await?;
80+
let acceptor = generate_acceptor();
81+
82+
while let Ok((sock, addr)) = listener.accept().await {
83+
log::info!("Secure TCP connection at {}", addr);
4384
let cid = rand::random::<u32>();
44-
let sock = async_tungstenite::accept_hdr_async(sock, |req: &Request, mut res: Response| {
85+
let sock = acceptor.accept(sock).await?;
86+
log::info!("TLS handshake completed");
87+
let sock = async_tungstenite::accept_hdr_async(Stream::Tls(sock), |req: &Request, mut res: Response| {
4588
let ws_proto = req.headers().iter().find(|(hdr, _)| **hdr == "Sec-WebSocket-Protocol");
4689

4790
match ws_proto.map(|(_, s)| s.to_str().unwrap()) {
@@ -121,7 +164,8 @@ impl NTServer {
121164

122165
let (tx, rx) = channel(32);
123166

124-
task::spawn(tcp_loop(_self.clone(), tx));
167+
task::spawn(tcp_loop(_self.clone(), tx.clone()));
168+
task::spawn(tls_loop(_self.clone(), tx));
125169
task::spawn(channel_loop(_self.clone(), rx));
126170
task::spawn(broadcast_loop(_self.clone()));
127171
task::spawn(flush_persistent_loop(_self.clone()));

server/src/server/tls.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use rustls::internal::pemfile::{pkcs8_private_keys, certs};
2+
use rustls::{PrivateKey, Certificate, ServerConfig, NoClientAuth};
3+
use std::io::BufReader;
4+
use std::fs::File;
5+
use async_tls::TlsAcceptor;
6+
7+
fn load_key() -> Vec<PrivateKey> {
8+
pkcs8_private_keys(&mut BufReader::new(File::open("./NT4.key").unwrap()))
9+
.unwrap()
10+
}
11+
12+
fn load_cert() -> Vec<Certificate> {
13+
certs(&mut BufReader::new(File::open("./NT4.crt.sgn").unwrap()))
14+
.unwrap()
15+
}
16+
17+
pub fn generate_acceptor() -> TlsAcceptor {
18+
let mut keys = load_key();
19+
let cert = load_cert();
20+
21+
let mut config = ServerConfig::new(NoClientAuth::new());
22+
config.set_single_cert(cert, keys.remove(0))
23+
.unwrap();
24+
25+
TlsAcceptor::from(config)
26+
}

0 commit comments

Comments
 (0)