15 Commits

Author SHA1 Message Date
4c2711fc81 Release version 0.1.10
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Signed-off-by: Jacob Kiers <code@kiers.eu>
2025-01-09 21:13:07 +01:00
1a9ca771ac Update based on lints
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
Signed-off-by: Jacob Kiers <code@kiers.eu>
2025-01-09 20:56:51 +01:00
b7ec67ed07 Fix SNI header parsing
When a listener is configured to deal with TLS upstreams, we use the SNI
field of the TLS ClientHello message to decide where to send the traffic.

Therefore, a buffer of 1024 bytes was used to temporarily store this
message. However, a TLS ClientHello message can be larger than that, up
to 16K bytes.

So now the first few bytes are read and manually parsed to find out how
long the message is. And then the entire ClientHello message is
retrieved.

So hopefully that will fix the issue causing the ClientHello
determination to fail.

Closes #10

Signed-off-by: Jacob Kiers <code@kiers.eu>
2025-01-09 20:56:51 +01:00
aff46b6bfb Update dependencies to latest version
This also fixes a build failure which would otherwise happen due to the
time crate.

Signed-off-by: Jacob Kiers <code@kiers.eu>
2025-01-09 20:21:34 +01:00
922ea1f030 Add help to main command
Signed-off-by: Jacob Kiers <code@kiers.eu>
2024-12-24 17:22:58 +01:00
6300c43495 Upgrade toolchain to rust 1.79.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Signed-off-by: Jacob Kiers <code@kiers.eu>
2024-06-22 15:44:13 +02:00
c21ff86ee4 Add change log
Signed-off-by: Jacob Kiers <code@kiers.eu>
2024-06-22 15:43:58 +02:00
8d6387773a Add self update functionality
Signed-off-by: Jacob Kiers <code@kiers.eu>
2024-06-22 15:43:58 +02:00
95149ffd9f Update .gitignore
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: Jacob Kiers <code@kiers.eu>
2024-06-22 11:49:12 +02:00
a140748647 Correct attribution to fourth
All checks were successful
continuous-integration/drone/push Build is passing
Search and replace accidentally also renamed the original crate, called
fourth. But attributions should be correct.

Signed-off-by: Jacob Kiers <code@kiers.eu>
2024-06-19 21:59:19 +02:00
ad6955a30d Fix crate name and release v0.1.8
All checks were successful
continuous-integration/drone Build is passing
continuous-integration/drone/tag Build is passing
Signed-off-by: Jacob Kiers <code@kiers.eu>
2024-06-19 21:16:30 +02:00
4592c94586 Reintroduce L4P_CONFIG environment variable
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
This points to a user-configured configuration file.

Closes #5.

Signed-off-by: Jacob Kiers <code@kiers.eu>
2024-02-23 23:53:47 +01:00
6284870059 Rename config::config to config::config_v1
To prevent module inception, which was a clippy warning.

Signed-off-by: Jacob Kiers <code@kiers.eu>
2024-02-23 23:34:19 +01:00
97b4bf6bbe Solve synchronization issue
The async mutex in the previous variant would fail when used in a single
threaded mode, because block_in_place() cannot be used there.

Instead, replace the code with a Arc<RwLock> inside of the
UpstreamAddress to let that class take care of its own mutability.

Signed-off-by: Jacob Kiers <code@kiers.eu>
2024-02-23 23:31:23 +01:00
59c7128f93 Remove kcp support
Signed-off-by: Jacob Kiers <code@kiers.eu>
2024-02-23 22:49:43 +01:00
27 changed files with 1619 additions and 1265 deletions

View File

@@ -1,5 +1,5 @@
local executableName = 'l4p';
local build_image = 'img.kie.rs/jjkiers/rust-cross:rust1.71.1-zig';
local build_image = 'img.kie.rs/jjkiers/rust-crossbuild:rust1.79.0-zig0.11.0-zig';
local archs = [
{ target: 'aarch64-unknown-linux-musl', short: 'arm64-musl' },

2
.gitignore vendored
View File

@@ -1,2 +1,4 @@
/.idea
/.vscode
/target
config.yaml

55
CHANGELOG.md Normal file
View File

@@ -0,0 +1,55 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.1.10] - 2025-01-09
### Fixed
* The ClientHello TLS header is now read in full before it is parsed, solving
an error where there was not enough data to fully read it. In those cases
it was not possible to determine the upstream address and therefore the proxy
would go the the default action instead.
### Changed
* Updated some dependencies to prevent the build from breaking.
## [0.1.9] - 2024-06-22
### Deprecated
The ability to run `l4p` without arguments is now deprecated. Please use
`l4p serve` going forward.
### Added
* Added self update functionality. Just run `l4p update` to use it.
* Now keeping a change log in the `CHANGELOG.md` file.
### Changed
* Updated build pipeline to generate much smaller binaries
-------
## Previous versions
[unreleased]: https://code.kiers.eu/jjkiers/layer4-proxy/compare/v0.1.9...HEAD
[0.1.10]: https://code.kiers.eu/jjkiers/layer4-proxy/compare/v0.1.9...v0.1.10
[0.1.9]: https://code.kiers.eu/jjkiers/layer4-proxy/compare/v0.1.8...v0.1.9
Types of changes:
* `Added` for new features.
* `Changed` for changes in existing functionality.
* `Deprecated` for soon-to-be removed features.
* `Removed` for now removed features.
* `Fixed` for any bug fixes.
* `Security` in case of vulnerabilities.

921
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "layer4-proxy"
version = "0.1.7"
name = "l4p"
version = "0.1.10"
edition = "2021"
authors = ["Jacob Kiers <code@kiers.eu>"]
license = "Apache-2.0"
@@ -25,10 +25,16 @@ byte_string = "1"
bytes = "1.1"
futures = "0.3"
log = "0.4"
pico-args = "0.5.0"
pretty_env_logger = "0.5"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9.21"
time = { version = "0.3.1", features = ["local-offset", "formatting"] }
tls-parser = "0.11"
time = { version = "0.3.37", features = ["local-offset", "formatting"] }
tls-parser = "0.12.2"
tokio = { version = "1.0", features = ["full"] }
url = "2.2.2"
[dependencies.self_update]
version = "0.42.0"
default-features = false
features = ["rustls"]

View File

@@ -4,7 +4,7 @@
![CI](https://drone-ci.kiers.eu/api/badges/jjkiers/layer4-proxy/status.svg)
`l4p` is a layer 4 proxy implemented by Rust to listen on specific ports and transfer TCP/KCP data to remote addresses(only TCP) according to configuration.
`l4p` is a layer 4 proxy implemented by Rust to listen on specific ports and transfer TCP data to remote addresses (only TCP) according to the configuration.
## Features
@@ -57,7 +57,7 @@ For detailed configuration, check [this example](./config.yaml.example).
## Thanks
- [`l4p`](https://crates.io/crates/`l4p`), of which this is a heavily modified fork.
- [`fourth`](https://crates.io/crates/fourth), of which this is a heavily modified fork.
## License

View File

@@ -15,11 +15,6 @@ servers:
listen:
- "127.0.0.1:8081"
default: remote
kcp_server:
protocol: kcp # default TCP
listen:
- "127.0.0.1:8082"
default: echo
upstream:
nginx: "tcp://127.0.0.1:8080"

View File

@@ -1,6 +1,6 @@
use crate::upstreams::ProxyToUpstream;
use crate::upstreams::Upstream;
use log::{debug, warn};
use log::{debug, info, warn};
use serde::Deserialize;
use std::collections::{HashMap, HashSet};
use std::fs::File;
@@ -119,9 +119,10 @@ fn load_config(path: &str) -> Result<ParsedConfigV1, ConfigError> {
if !log_level.eq("disable") {
std::env::set_var("FOURTH_LOG", log_level.clone());
pretty_env_logger::init_custom_env("FOURTH_LOG");
debug!("Set log level to {}", log_level);
}
info!("Using config file: {}", &path);
debug!("Set log level to {}", log_level);
debug!("Config version {}", base.version);
let mut parsed_upstream: HashMap<String, Upstream> = HashMap::new();
@@ -221,7 +222,7 @@ mod tests {
let config = ConfigV1::new("tests/config.yaml").unwrap();
assert_eq!(config.base.version, 1);
assert_eq!(config.base.log.unwrap(), "disable");
assert_eq!(config.base.servers.len(), 5);
assert_eq!(config.base.servers.len(), 3);
assert_eq!(config.base.upstream.len(), 3 + 2); // Add ban and echo upstreams
}
}

View File

@@ -1,3 +1,3 @@
mod config;
pub(crate) use config::ConfigV1;
pub(crate) use config::ParsedConfigV1;
mod config_v1;
pub(crate) use config_v1::ConfigV1;
pub(crate) use config_v1::ParsedConfigV1;

View File

@@ -1,15 +1,52 @@
mod config;
mod plugins;
mod servers;
mod update;
mod upstreams;
use crate::config::ConfigV1;
use crate::servers::Server;
use std::io::{stderr, stdout, Write};
use log::{debug, error};
use pico_args::Arguments;
use std::path::PathBuf;
fn main() {
let mut args = Arguments::from_env();
match args.subcommand().expect("Unexpected error").as_deref() {
Some("serve") => serve(),
Some("update") => update::update(),
Some("help") => {
let _ = print_usage(&mut stdout().lock());
}
Some(cmd) => {
eprintln!("Invalid command: {cmd}");
std::process::exit(1);
}
None => {
eprintln!("Calling l4p without argument is deprecated now. Please use: l4p serve");
let _ = print_usage(&mut stderr().lock());
serve();
}
}
}
fn print_usage(out: &mut dyn Write) -> std::io::Result<()> {
writeln!(
out,
"{} v{}",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION")
)?;
writeln!(out, "Usage:")?;
writeln!(out, "\tupdate\tUpdate l4p to the latest version")?;
writeln!(out, "\tserve\tServe the proxy")?;
writeln!(out, "\thelp\tPrint this message")?;
Ok(())
}
fn serve() {
let config_path = match find_config() {
Ok(p) => p,
Err(paths) => {
@@ -38,20 +75,28 @@ fn main() {
}
fn find_config() -> Result<String, Vec<String>> {
let possible_paths = ["/etc/l4p", ""];
let possible_locations = ["/etc/l4p", ""];
let possible_names = ["l4p.yaml", "config.yaml"];
let mut tried_paths = Vec::<String>::new();
let mut possible_paths = Vec::<PathBuf>::new();
for path in possible_paths
.iter()
.flat_map(|&path| {
possible_names
.iter()
.map(move |&file| PathBuf::new().join(path).join(file))
})
.collect::<Vec<PathBuf>>()
{
if let Ok(env_path) = std::env::var("L4P_CONFIG") {
possible_paths.push(PathBuf::from(env_path));
}
possible_paths.append(
&mut possible_locations
.iter()
.flat_map(|&path| {
possible_names
.iter()
.map(move |&file| PathBuf::new().join(path).join(file))
})
.collect::<Vec<PathBuf>>(),
);
for path in possible_paths {
let path_str = path.to_string_lossy().to_string();
if path.exists() {
return Ok(path_str);

View File

@@ -1,110 +0,0 @@
use std::{io::Write, time::Duration};
use kcp::Kcp;
/// Kcp Delay Config
#[derive(Debug, Clone, Copy)]
pub struct KcpNoDelayConfig {
/// Enable nodelay
pub nodelay: bool,
/// Internal update interval (ms)
pub interval: i32,
/// ACK number to enable fast resend
pub resend: i32,
/// Disable congetion control
pub nc: bool,
}
impl Default for KcpNoDelayConfig {
fn default() -> KcpNoDelayConfig {
KcpNoDelayConfig {
nodelay: false,
interval: 100,
resend: 0,
nc: false,
}
}
}
#[allow(unused)]
impl KcpNoDelayConfig {
/// Get a fastest configuration
///
/// 1. Enable NoDelay
/// 2. Set ticking interval to be 10ms
/// 3. Set fast resend to be 2
/// 4. Disable congestion control
pub fn fastest() -> KcpNoDelayConfig {
KcpNoDelayConfig {
nodelay: true,
interval: 10,
resend: 2,
nc: true,
}
}
/// Get a normal configuration
///
/// 1. Disable NoDelay
/// 2. Set ticking interval to be 40ms
/// 3. Disable fast resend
/// 4. Enable congestion control
pub fn normal() -> KcpNoDelayConfig {
KcpNoDelayConfig {
nodelay: false,
interval: 40,
resend: 0,
nc: false,
}
}
}
/// Kcp Config
#[derive(Debug, Clone, Copy)]
pub struct KcpConfig {
/// Max Transmission Unit
pub mtu: usize,
/// nodelay
pub nodelay: KcpNoDelayConfig,
/// Send window size
pub wnd_size: (u16, u16),
/// Session expire duration, default is 90 seconds
pub session_expire: Duration,
/// Flush KCP state immediately after write
pub flush_write: bool,
/// Flush ACKs immediately after input
pub flush_acks_input: bool,
/// Stream mode
pub stream: bool,
}
impl Default for KcpConfig {
fn default() -> KcpConfig {
KcpConfig {
mtu: 1400,
nodelay: KcpNoDelayConfig::normal(),
wnd_size: (256, 256),
session_expire: Duration::from_secs(90),
flush_write: false,
flush_acks_input: false,
stream: true,
}
}
}
impl KcpConfig {
/// Applies config onto `Kcp`
#[doc(hidden)]
pub fn apply_config<W: Write>(&self, k: &mut Kcp<W>) {
k.set_mtu(self.mtu).expect("invalid MTU");
k.set_nodelay(
self.nodelay.nodelay,
self.nodelay.interval,
self.nodelay.resend,
self.nodelay.nc,
);
k.set_wndsize(self.wnd_size.0, self.wnd_size.1);
}
}

View File

@@ -1,128 +0,0 @@
use std::{
io::{self, ErrorKind},
net::SocketAddr,
sync::Arc,
time::Duration,
};
use byte_string::ByteStr;
use kcp::{Error as KcpError, KcpResult};
use log::{debug, error, trace};
use tokio::{
net::{ToSocketAddrs, UdpSocket},
sync::mpsc,
task::JoinHandle,
time,
};
use crate::plugins::kcp::{config::KcpConfig, session::KcpSessionManager, stream::KcpStream};
#[allow(unused)]
pub struct KcpListener {
udp: Arc<UdpSocket>,
accept_rx: mpsc::Receiver<(KcpStream, SocketAddr)>,
task_watcher: JoinHandle<()>,
}
impl Drop for KcpListener {
fn drop(&mut self) {
self.task_watcher.abort();
}
}
impl KcpListener {
pub async fn bind<A: ToSocketAddrs>(config: KcpConfig, addr: A) -> KcpResult<KcpListener> {
let udp = UdpSocket::bind(addr).await?;
let udp = Arc::new(udp);
let server_udp = udp.clone();
let (accept_tx, accept_rx) = mpsc::channel(1024 /* backlogs */);
let task_watcher = tokio::spawn(async move {
let (close_tx, mut close_rx) = mpsc::channel(64);
let mut sessions = KcpSessionManager::new();
let mut packet_buffer = [0u8; 65536];
loop {
tokio::select! {
conv = close_rx.recv() => {
let conv = conv.expect("close_tx closed unexpectly");
sessions.close_conv(conv);
trace!("session conv: {} removed", conv);
}
recv_res = udp.recv_from(&mut packet_buffer) => {
match recv_res {
Err(err) => {
error!("udp.recv_from failed, error: {}", err);
time::sleep(Duration::from_secs(1)).await;
}
Ok((n, peer_addr)) => {
let packet = &mut packet_buffer[..n];
log::trace!("received peer: {}, {:?}", peer_addr, ByteStr::new(packet));
let mut conv = kcp::get_conv(packet);
if conv == 0 {
// Allocate a conv for client.
conv = sessions.alloc_conv();
debug!("allocate {} conv for peer: {}", conv, peer_addr);
kcp::set_conv(packet, conv);
}
let session = match sessions.get_or_create(&config, conv, &udp, peer_addr, &close_tx) {
Ok((s, created)) => {
if created {
// Created a new session, constructed a new accepted client
let stream = KcpStream::with_session(s.clone());
if let Err(..) = accept_tx.try_send((stream, peer_addr)) {
debug!("failed to create accepted stream due to channel failure");
// remove it from session
sessions.close_conv(conv);
continue;
}
}
s
},
Err(err) => {
error!("failed to create session, error: {}, peer: {}, conv: {}", err, peer_addr, conv);
continue;
}
};
// let mut kcp = session.kcp_socket().lock().await;
// if let Err(err) = kcp.input(packet) {
// error!("kcp.input failed, peer: {}, conv: {}, error: {}, packet: {:?}", peer_addr, conv, err, ByteStr::new(packet));
// }
session.input(packet).await;
}
}
}
}
}
});
Ok(KcpListener {
udp: server_udp,
accept_rx,
task_watcher,
})
}
pub async fn accept(&mut self) -> KcpResult<(KcpStream, SocketAddr)> {
match self.accept_rx.recv().await {
Some(s) => Ok(s),
None => Err(KcpError::IoError(io::Error::new(
ErrorKind::Other,
"accept channel closed unexpectly",
))),
}
}
#[allow(unused)]
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.udp.local_addr()
}
}

View File

@@ -1,14 +0,0 @@
//! Library of KCP on Tokio
pub use self::{
config::{KcpConfig, KcpNoDelayConfig},
listener::KcpListener,
stream::KcpStream,
};
mod config;
mod listener;
mod session;
mod skcp;
mod stream;
mod utils;

View File

@@ -1,256 +0,0 @@
use std::{
collections::{hash_map::Entry, HashMap},
net::SocketAddr,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
time::Duration,
};
use byte_string::ByteStr;
use kcp::KcpResult;
use log::{error, trace};
use tokio::{
net::UdpSocket,
sync::{mpsc, Mutex},
time::{self, Instant},
};
use crate::plugins::kcp::{skcp::KcpSocket, KcpConfig};
pub struct KcpSession {
socket: Mutex<KcpSocket>,
closed: AtomicBool,
session_expire: Duration,
session_close_notifier: Option<mpsc::Sender<u32>>,
input_tx: mpsc::Sender<Vec<u8>>,
}
impl KcpSession {
fn new(
socket: KcpSocket,
session_expire: Duration,
session_close_notifier: Option<mpsc::Sender<u32>>,
input_tx: mpsc::Sender<Vec<u8>>,
) -> KcpSession {
KcpSession {
socket: Mutex::new(socket),
closed: AtomicBool::new(false),
session_expire,
session_close_notifier,
input_tx,
}
}
pub fn new_shared(
socket: KcpSocket,
session_expire: Duration,
session_close_notifier: Option<mpsc::Sender<u32>>,
) -> Arc<KcpSession> {
let is_client = session_close_notifier.is_none();
let (input_tx, mut input_rx) = mpsc::channel(64);
let udp_socket = socket.udp_socket().clone();
let session = Arc::new(KcpSession::new(
socket,
session_expire,
session_close_notifier,
input_tx,
));
{
let session = session.clone();
tokio::spawn(async move {
let mut input_buffer = [0u8; 65536];
let update_timer = time::sleep(Duration::from_millis(10));
tokio::pin!(update_timer);
loop {
tokio::select! {
// recv() then input()
// Drives the KCP machine forward
recv_result = udp_socket.recv(&mut input_buffer), if is_client => {
match recv_result {
Err(err) => {
error!("[SESSION] UDP recv failed, error: {}", err);
}
Ok(n) => {
let input_buffer = &input_buffer[..n];
trace!("[SESSION] UDP recv {} bytes, going to input {:?}", n, ByteStr::new(input_buffer));
let mut socket = session.socket.lock().await;
match socket.input(input_buffer) {
Ok(true) => {
trace!("[SESSION] UDP input {} bytes and waked sender/receiver", n);
}
Ok(false) => {}
Err(err) => {
error!("[SESSION] UDP input {} bytes error: {}, input buffer {:?}", n, err, ByteStr::new(input_buffer));
}
}
}
}
}
// bytes received from listener socket
input_opt = input_rx.recv() => {
if let Some(input_buffer) = input_opt {
let mut socket = session.socket.lock().await;
match socket.input(&input_buffer) {
Ok(..) => {
trace!("[SESSION] UDP input {} bytes from channel {:?}", input_buffer.len(), ByteStr::new(&input_buffer));
}
Err(err) => {
error!("[SESSION] UDP input {} bytes from channel failed, error: {}, input buffer {:?}",
input_buffer.len(), err, ByteStr::new(&input_buffer));
}
}
}
}
// Call update() in period
_ = &mut update_timer => {
let mut socket = session.socket.lock().await;
let is_closed = session.closed.load(Ordering::Acquire);
if is_closed && socket.can_close() {
trace!("[SESSION] KCP session closed");
break;
}
// server socket expires
if !is_client {
// If this is a server stream, close it automatically after a period of time
let last_update_time = socket.last_update_time();
let elapsed = last_update_time.elapsed();
if elapsed > session.session_expire {
if elapsed > session.session_expire * 2 {
// Force close. Client may have already gone.
trace!(
"[SESSION] force close inactive session, conv: {}, last_update: {}s ago",
socket.conv(),
elapsed.as_secs()
);
break;
}
if !is_closed {
trace!(
"[SESSION] closing inactive session, conv: {}, last_update: {}s ago",
socket.conv(),
elapsed.as_secs()
);
session.closed.store(true, Ordering::Release);
}
}
}
match socket.update() {
Ok(next_next) => {
update_timer.as_mut().reset(Instant::from_std(next_next));
}
Err(err) => {
error!("[SESSION] KCP update failed, error: {}", err);
update_timer.as_mut().reset(Instant::now() + Duration::from_millis(10));
}
}
}
}
}
{
// Close the socket.
// Wake all pending tasks and let all send/recv return EOF
let mut socket = session.socket.lock().await;
socket.close();
}
if let Some(ref notifier) = session.session_close_notifier {
let socket = session.socket.lock().await;
let _ = notifier.send(socket.conv()).await;
}
});
}
session
}
pub fn kcp_socket(&self) -> &Mutex<KcpSocket> {
&self.socket
}
pub fn close(&self) {
self.closed.store(true, Ordering::Release);
}
pub async fn input(&self, buf: &[u8]) {
self.input_tx
.send(buf.to_owned())
.await
.expect("input channel closed")
}
}
pub struct KcpSessionManager {
sessions: HashMap<u32, Arc<KcpSession>>,
next_free_conv: u32,
}
impl KcpSessionManager {
pub fn new() -> KcpSessionManager {
KcpSessionManager {
sessions: HashMap::new(),
next_free_conv: 0,
}
}
pub fn close_conv(&mut self, conv: u32) {
self.sessions.remove(&conv);
}
pub fn alloc_conv(&mut self) -> u32 {
loop {
let (mut c, _) = self.next_free_conv.overflowing_add(1);
if c == 0 {
let (nc, _) = c.overflowing_add(1);
c = nc;
}
self.next_free_conv = c;
if self.sessions.get(&self.next_free_conv).is_none() {
let conv = self.next_free_conv;
return conv;
}
}
}
pub fn get_or_create(
&mut self,
config: &KcpConfig,
conv: u32,
udp: &Arc<UdpSocket>,
peer_addr: SocketAddr,
session_close_notifier: &mpsc::Sender<u32>,
) -> KcpResult<(Arc<KcpSession>, bool)> {
match self.sessions.entry(conv) {
Entry::Occupied(occ) => Ok((occ.get().clone(), false)),
Entry::Vacant(vac) => {
let socket = KcpSocket::new(config, conv, udp.clone(), peer_addr, config.stream)?;
let session = KcpSession::new_shared(
socket,
config.session_expire,
Some(session_close_notifier.clone()),
);
trace!("created session for conv: {}, peer: {}", conv, peer_addr);
vac.insert(session.clone());
Ok((session, true))
}
}
}
}

View File

@@ -1,288 +0,0 @@
use std::{
io::{self, ErrorKind, Write},
net::SocketAddr,
sync::Arc,
task::{Context, Poll, Waker},
time::{Duration, Instant},
};
use futures::future;
use kcp::{Error as KcpError, Kcp, KcpResult};
use log::{error, trace};
use tokio::{net::UdpSocket, sync::mpsc};
use crate::plugins::kcp::{utils::now_millis, KcpConfig};
/// Writer for sending packets to the underlying UdpSocket
struct UdpOutput {
socket: Arc<UdpSocket>,
target_addr: SocketAddr,
delay_tx: mpsc::UnboundedSender<Vec<u8>>,
}
impl UdpOutput {
/// Create a new Writer for writing packets to UdpSocket
pub fn new(socket: Arc<UdpSocket>, target_addr: SocketAddr) -> UdpOutput {
let (delay_tx, mut delay_rx) = mpsc::unbounded_channel::<Vec<u8>>();
{
let socket = socket.clone();
tokio::spawn(async move {
while let Some(buf) = delay_rx.recv().await {
if let Err(err) = socket.send_to(&buf, target_addr).await {
error!("[SEND] UDP delayed send failed, error: {}", err);
}
}
});
}
UdpOutput {
socket,
target_addr,
delay_tx,
}
}
}
impl Write for UdpOutput {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match self.socket.try_send_to(buf, self.target_addr) {
Ok(n) => Ok(n),
Err(ref err) if err.kind() == ErrorKind::WouldBlock => {
// send return EAGAIN
// ignored as packet was lost in transmission
trace!(
"[SEND] UDP send EAGAIN, packet.size: {} bytes, delayed send",
buf.len()
);
self.delay_tx
.send(buf.to_owned())
.expect("channel closed unexpectly");
Ok(buf.len())
}
Err(err) => Err(err),
}
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
pub struct KcpSocket {
kcp: Kcp<UdpOutput>,
last_update: Instant,
socket: Arc<UdpSocket>,
flush_write: bool,
flush_ack_input: bool,
sent_first: bool,
pending_sender: Option<Waker>,
pending_receiver: Option<Waker>,
closed: bool,
}
impl KcpSocket {
pub fn new(
c: &KcpConfig,
conv: u32,
socket: Arc<UdpSocket>,
target_addr: SocketAddr,
stream: bool,
) -> KcpResult<KcpSocket> {
let output = UdpOutput::new(socket.clone(), target_addr);
let mut kcp = if stream {
Kcp::new_stream(conv, output)
} else {
Kcp::new(conv, output)
};
c.apply_config(&mut kcp);
// Ask server to allocate one
if conv == 0 {
kcp.input_conv();
}
kcp.update(now_millis())?;
Ok(KcpSocket {
kcp,
last_update: Instant::now(),
socket,
flush_write: c.flush_write,
flush_ack_input: c.flush_acks_input,
sent_first: false,
pending_sender: None,
pending_receiver: None,
closed: false,
})
}
/// Call every time you got data from transmission
pub fn input(&mut self, buf: &[u8]) -> KcpResult<bool> {
match self.kcp.input(buf) {
Ok(..) => {}
Err(KcpError::ConvInconsistent(expected, actual)) => {
trace!(
"[INPUT] Conv expected={} actual={} ignored",
expected,
actual
);
return Ok(false);
}
Err(err) => return Err(err),
}
self.last_update = Instant::now();
if self.flush_ack_input {
self.kcp.flush_ack()?;
}
Ok(self.try_wake_pending_waker())
}
/// Call if you want to send some data
pub fn poll_send(&mut self, cx: &mut Context<'_>, mut buf: &[u8]) -> Poll<KcpResult<usize>> {
if self.closed {
return Ok(0).into();
}
// If:
// 1. Have sent the first packet (asking for conv)
// 2. Too many pending packets
if self.sent_first
&& (self.kcp.wait_snd() >= self.kcp.snd_wnd() as usize || self.kcp.waiting_conv())
{
trace!(
"[SEND] waitsnd={} sndwnd={} excceeded or waiting conv={}",
self.kcp.wait_snd(),
self.kcp.snd_wnd(),
self.kcp.waiting_conv()
);
self.pending_sender = Some(cx.waker().clone());
return Poll::Pending;
}
if !self.sent_first && self.kcp.waiting_conv() && buf.len() > self.kcp.mss() as usize {
buf = &buf[..self.kcp.mss() as usize];
}
let n = self.kcp.send(buf)?;
self.sent_first = true;
self.last_update = Instant::now();
if self.flush_write {
self.kcp.flush()?;
}
Ok(n).into()
}
/// Call if you want to send some data
#[allow(dead_code)]
pub async fn send(&mut self, buf: &[u8]) -> KcpResult<usize> {
future::poll_fn(|cx| self.poll_send(cx, buf)).await
}
#[allow(dead_code)]
pub fn try_recv(&mut self, buf: &mut [u8]) -> KcpResult<usize> {
if self.closed {
return Ok(0);
}
self.kcp.recv(buf)
}
pub fn poll_recv(&mut self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<KcpResult<usize>> {
if self.closed {
return Ok(0).into();
}
match self.kcp.recv(buf) {
Ok(n) => Ok(n).into(),
Err(KcpError::RecvQueueEmpty) => {
self.pending_receiver = Some(cx.waker().clone());
Poll::Pending
}
Err(err) => Err(err).into(),
}
}
#[allow(dead_code)]
pub async fn recv(&mut self, buf: &mut [u8]) -> KcpResult<usize> {
future::poll_fn(|cx| self.poll_recv(cx, buf)).await
}
pub fn flush(&mut self) -> KcpResult<()> {
self.kcp.flush()?;
self.last_update = Instant::now();
Ok(())
}
fn try_wake_pending_waker(&mut self) -> bool {
let mut waked = false;
if self.pending_sender.is_some()
&& self.kcp.wait_snd() < self.kcp.snd_wnd() as usize
&& !self.kcp.waiting_conv()
{
let waker = self.pending_sender.take().unwrap();
waker.wake();
waked = true;
}
if self.pending_receiver.is_some() {
if let Ok(peek) = self.kcp.peeksize() {
if peek > 0 {
let waker = self.pending_receiver.take().unwrap();
waker.wake();
waked = true;
}
}
}
waked
}
pub fn update(&mut self) -> KcpResult<Instant> {
let now = now_millis();
self.kcp.update(now)?;
let next = self.kcp.check(now);
self.try_wake_pending_waker();
Ok(Instant::now() + Duration::from_millis(next as u64))
}
pub fn close(&mut self) {
self.closed = true;
if let Some(w) = self.pending_sender.take() {
w.wake();
}
if let Some(w) = self.pending_receiver.take() {
w.wake();
}
}
pub fn udp_socket(&self) -> &Arc<UdpSocket> {
&self.socket
}
pub fn can_close(&self) -> bool {
self.kcp.wait_snd() == 0
}
pub fn conv(&self) -> u32 {
self.kcp.conv()
}
pub fn peek_size(&self) -> KcpResult<usize> {
self.kcp.peeksize()
}
pub fn last_update_time(&self) -> Instant {
self.last_update
}
}

View File

@@ -1,183 +0,0 @@
use std::{
io::{self, ErrorKind},
net::{IpAddr, SocketAddr},
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use futures::{future, ready};
use kcp::{Error as KcpError, KcpResult};
use log::trace;
use tokio::{
io::{AsyncRead, AsyncWrite, ReadBuf},
net::UdpSocket,
};
use crate::plugins::kcp::{config::KcpConfig, session::KcpSession, skcp::KcpSocket};
pub struct KcpStream {
session: Arc<KcpSession>,
recv_buffer: Vec<u8>,
recv_buffer_pos: usize,
recv_buffer_cap: usize,
}
impl Drop for KcpStream {
fn drop(&mut self) {
self.session.close();
}
}
#[allow(unused)]
impl KcpStream {
pub async fn connect(config: &KcpConfig, addr: SocketAddr) -> KcpResult<KcpStream> {
let udp = match addr.ip() {
IpAddr::V4(..) => UdpSocket::bind("0.0.0.0:0").await?,
IpAddr::V6(..) => UdpSocket::bind("[::]:0").await?,
};
let udp = Arc::new(udp);
let socket = KcpSocket::new(config, 0, udp, addr, config.stream)?;
let session = KcpSession::new_shared(socket, config.session_expire, None);
Ok(KcpStream::with_session(session))
}
pub(crate) fn with_session(session: Arc<KcpSession>) -> KcpStream {
KcpStream {
session,
recv_buffer: Vec::new(),
recv_buffer_pos: 0,
recv_buffer_cap: 0,
}
}
pub fn poll_send(&mut self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<KcpResult<usize>> {
// Mutex doesn't have poll_lock, spinning on it.
let socket = self.session.kcp_socket();
let mut kcp = match socket.try_lock() {
Ok(guard) => guard,
Err(..) => {
cx.waker().wake_by_ref();
return Poll::Pending;
}
};
kcp.poll_send(cx, buf)
}
pub async fn send(&mut self, buf: &[u8]) -> KcpResult<usize> {
future::poll_fn(|cx| self.poll_send(cx, buf)).await
}
pub fn poll_recv(&mut self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<KcpResult<usize>> {
loop {
// Consumes all data in buffer
if self.recv_buffer_pos < self.recv_buffer_cap {
let remaining = self.recv_buffer_cap - self.recv_buffer_pos;
let copy_length = remaining.min(buf.len());
buf.copy_from_slice(
&self.recv_buffer[self.recv_buffer_pos..self.recv_buffer_pos + copy_length],
);
self.recv_buffer_pos += copy_length;
return Ok(copy_length).into();
}
// Mutex doesn't have poll_lock, spinning on it.
let socket = self.session.kcp_socket();
let mut kcp = match socket.try_lock() {
Ok(guard) => guard,
Err(..) => {
cx.waker().wake_by_ref();
return Poll::Pending;
}
};
// Try to read from KCP
// 1. Read directly with user provided `buf`
match ready!(kcp.poll_recv(cx, buf)) {
Ok(n) => {
trace!("[CLIENT] recv directly {} bytes", n);
return Ok(n).into();
}
Err(KcpError::UserBufTooSmall) => {}
Err(err) => return Err(err).into(),
}
// 2. User `buf` too small, read to recv_buffer
let required_size = kcp.peek_size()?;
if self.recv_buffer.len() < required_size {
self.recv_buffer.resize(required_size, 0);
}
match ready!(kcp.poll_recv(cx, &mut self.recv_buffer)) {
Ok(n) => {
trace!("[CLIENT] recv buffered {} bytes", n);
self.recv_buffer_pos = 0;
self.recv_buffer_cap = n;
}
Err(err) => return Err(err).into(),
}
}
}
pub async fn recv(&mut self, buf: &mut [u8]) -> KcpResult<usize> {
future::poll_fn(|cx| self.poll_recv(cx, buf)).await
}
}
impl AsyncRead for KcpStream {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
match ready!(self.poll_recv(cx, buf.initialize_unfilled())) {
Ok(n) => {
buf.advance(n);
Ok(()).into()
}
Err(KcpError::IoError(err)) => Err(err).into(),
Err(err) => Err(io::Error::new(ErrorKind::Other, err)).into(),
}
}
}
impl AsyncWrite for KcpStream {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
match ready!(self.poll_send(cx, buf)) {
Ok(n) => Ok(n).into(),
Err(KcpError::IoError(err)) => Err(err).into(),
Err(err) => Err(io::Error::new(ErrorKind::Other, err)).into(),
}
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
// Mutex doesn't have poll_lock, spinning on it.
let socket = self.session.kcp_socket();
let mut kcp = match socket.try_lock() {
Ok(guard) => guard,
Err(..) => {
cx.waker().wake_by_ref();
return Poll::Pending;
}
};
match kcp.flush() {
Ok(..) => Ok(()).into(),
Err(KcpError::IoError(err)) => Err(err).into(),
Err(err) => Err(io::Error::new(ErrorKind::Other, err)).into(),
}
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Ok(()).into()
}
}

View File

@@ -1,10 +0,0 @@
use std::time::{SystemTime, UNIX_EPOCH};
#[inline]
pub fn now_millis() -> u32 {
let start = SystemTime::now();
let since_the_epoch = start
.duration_since(UNIX_EPOCH)
.expect("time went afterwards");
(since_the_epoch.as_secs() * 1000 + since_the_epoch.subsec_millis() as u64 / 1_000_000) as u32
}

View File

@@ -1 +0,0 @@
//pub mod kcp;

View File

@@ -90,12 +90,6 @@ impl Server {
error!("Failed to start {}: {}", config.name, res.err().unwrap());
}
}
// "kcp" => {
// let res = kcp::proxy(config.clone()).await;
// if res.is_err() {
// error!("Failed to start {}: {}", config.name, res.err().unwrap());
// }
// }
_ => {
error!("Invalid protocol: {}", config.protocol)
}
@@ -113,7 +107,6 @@ impl Server {
#[cfg(test)]
mod tests {
//use crate::plugins::kcp::{KcpConfig, KcpStream};
use std::thread::{self, sleep};
use std::time::Duration;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
@@ -176,27 +169,5 @@ mod tests {
assert_eq!(&buf, &[i]);
}
conn.shutdown().await.unwrap();
// test KCP echo
// let kcp_config = KcpConfig::default();
// let server_addr: SocketAddr = "127.0.0.1:54959".parse().unwrap();
// let mut conn = KcpStream::connect(&kcp_config, server_addr).await.unwrap();
// let mut buf = [0u8; 1];
// for i in 0..=10u8 {
// conn.write(&[i]).await.unwrap();
// conn.read(&mut buf).await.unwrap();
// assert_eq!(&buf, &[i]);
// }
// conn.shutdown().await.unwrap();
//
// // test KCP proxy and close mock server
// let kcp_config = KcpConfig::default();
// let server_addr: SocketAddr = "127.0.0.1:54958".parse().unwrap();
// let mut conn = KcpStream::connect(&kcp_config, server_addr).await.unwrap();
// let mut buf = [0u8; 5];
// conn.write(b"by").await.unwrap();
// conn.read(&mut buf).await.unwrap();
// assert_eq!(&buf, b"hello");
// conn.shutdown().await.unwrap();
}
}

View File

@@ -1,98 +0,0 @@
use crate::config::Upstream;
use crate::plugins::kcp::{KcpConfig, KcpListener, KcpStream};
use crate::servers::{copy, Proxy};
use futures::future::try_join;
use log::{debug, error, warn};
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::io;
use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
use tokio::net::TcpStream;
pub async fn proxy(config: Arc<Proxy>) -> Result<(), Box<dyn std::error::Error>> {
let kcp_config = KcpConfig::default();
let mut listener = KcpListener::bind(kcp_config, config.listen).await?;
let config = config.clone();
loop {
let thread_proxy = config.clone();
match listener.accept().await {
Err(err) => {
error!("Failed to accept connection: {}", err);
return Err(Box::new(err));
}
Ok((stream, peer)) => {
tokio::spawn(async move {
match accept(stream, peer, thread_proxy).await {
Ok(_) => {}
Err(err) => {
error!("Relay thread returned an error: {}", err);
}
};
});
}
}
}
}
async fn accept(
inbound: KcpStream,
peer: SocketAddr,
proxy: Arc<Proxy>,
) -> Result<(), Box<dyn std::error::Error>> {
debug!("New connection from {:?}", peer);
let upstream_name = proxy.default_action.clone();
debug!("Upstream: {}", upstream_name);
let upstream = match proxy.upstream.get(&upstream_name) {
Some(upstream) => upstream,
None => {
warn!(
"No upstream named {:?} on server {:?}",
proxy.default, proxy.name
);
return process(inbound, proxy.upstream.get(&proxy.default_action).unwrap()).await;
// ToDo: Remove unwrap and check default option
}
};
return process(inbound, upstream).await;
}
async fn process(
mut inbound: KcpStream,
upstream: &Upstream,
) -> Result<(), Box<dyn std::error::Error>> {
match upstream {
Upstream::Ban => {
let _ = inbound.shutdown();
}
Upstream::Echo => {
let (mut ri, mut wi) = io::split(inbound);
let inbound_to_inbound = copy(&mut ri, &mut wi);
let bytes_tx = inbound_to_inbound.await;
debug!("Bytes read: {:?}", bytes_tx);
}
Upstream::Custom(custom) => match custom.protocol.as_ref() {
"tcp" => {
let outbound = TcpStream::connect(custom.addr.clone()).await?;
let (mut ri, mut wi) = io::split(inbound);
let (mut ro, mut wo) = io::split(outbound);
let inbound_to_outbound = copy(&mut ri, &mut wo);
let outbound_to_inbound = copy(&mut ro, &mut wi);
let (bytes_tx, bytes_rx) =
try_join(inbound_to_outbound, outbound_to_inbound).await?;
debug!("Bytes read: {:?} write: {:?}", bytes_tx, bytes_rx);
}
_ => {
error!("Reached unknown protocol: {:?}", custom.protocol);
}
},
};
Ok(())
}

View File

@@ -1,3 +1,2 @@
//pub mod kcp;
pub mod tcp;
pub mod tls;

View File

@@ -1,4 +1,4 @@
use crate::servers::protocol::tls::get_sni;
use crate::servers::protocol::tls::determine_upstream_name;
use crate::servers::Proxy;
use log::{debug, error, info, warn};
use std::error::Error;
@@ -35,29 +35,7 @@ async fn accept(inbound: TcpStream, proxy: Arc<Proxy>) -> Result<(), Box<dyn Err
let upstream_name = match proxy.tls {
false => proxy.default_action.clone(),
true => {
let mut hello_buf = [0u8; 1024];
inbound.peek(&mut hello_buf).await?;
let snis = get_sni(&hello_buf);
if snis.is_empty() {
proxy.default_action.clone()
} else {
match proxy.sni.clone() {
Some(sni_map) => {
let mut upstream = proxy.default_action.clone();
for sni in snis {
let m = sni_map.get(&sni);
if m.is_some() {
upstream = m.unwrap().clone();
break;
}
}
upstream
}
None => proxy.default_action.clone(),
}
}
}
true => determine_upstream_name(&inbound, &proxy).await?,
};
debug!("Upstream: {}", upstream_name);

View File

@@ -1,10 +1,14 @@
use log::{debug, warn};
use crate::servers::Proxy;
use log::{debug, error, trace, warn};
use std::error::Error;
use std::sync::Arc;
use tls_parser::{
parse_tls_extensions, parse_tls_raw_record, parse_tls_record_with_header, TlsMessage,
TlsMessageHandshake,
};
use tokio::net::TcpStream;
pub fn get_sni(buf: &[u8]) -> Vec<String> {
fn get_sni(buf: &[u8]) -> Vec<String> {
let mut snis: Vec<String> = Vec::new();
match parse_tls_raw_record(buf) {
Ok((_, ref r)) => match parse_tls_record_with_header(r.data, &r.hdr) {
@@ -53,10 +57,119 @@ pub fn get_sni(buf: &[u8]) -> Vec<String> {
snis
}
pub(crate) async fn determine_upstream_name(
inbound: &TcpStream,
proxy: &Arc<Proxy>,
) -> Result<String, Box<dyn Error>> {
let default_upstream = proxy.default_action.clone();
let mut header = [0u8; 9];
inbound.peek(&mut header).await?;
let required_bytes = client_hello_buffer_size(&header)?;
let mut hello_buf = vec![0; required_bytes];
let read_bytes = inbound.peek(&mut hello_buf).await?;
if read_bytes < required_bytes.into() {
error!("Could not read enough bytes to determine SNI");
return Ok(default_upstream);
}
let snis = get_sni(&hello_buf);
if snis.is_empty() {
return Ok(default_upstream);
} else {
match proxy.sni.clone() {
Some(sni_map) => {
let mut upstream = default_upstream;
for sni in snis {
let m = sni_map.get(&sni);
if m.is_some() {
upstream = m.unwrap().clone();
break;
}
}
Ok(upstream)
}
None => return Ok(default_upstream),
}
}
}
fn client_hello_buffer_size(data: &[u8]) -> Result<usize, String> {
// TLS record header
// -----------------
// byte 0: rec type (should be 0x16 == Handshake)
// byte 1-2: version (should be 0x3000 < v < 0x3003)
// byte 3-4: rec len
if data.len() < 9 {
trace!("Not enough bytes to even check the TLS header.");
return Err("Not enough bytes to even check the TLS header.".into());
}
if data[0] != 0x16 {
trace!("Not a TLS handshake.");
return Err("Not a TLS handshake.".into());
}
// Check the record length
let record_length = ((data[3] as u16) << 8) | (data[4] as u16);
if record_length == 0 || record_length > 16384 {
trace!("Client send invalid header: way too long record header.");
return Err("Client send invalid header: way too long record header.".into());
}
// Handshake record header
// -----------------------
// byte 5: hs msg type (should be 0x01 == client_hello)
// byte 6-8: hs msg len
if data[5] != 0x01 {
trace!("Not a ClientHello message");
return Err("Not a ClientHello message".into());
}
// Check the handshake message length
let handshake_length =
((data[6] as usize) << 16) | ((data[7] as usize) << 8) | (data[8] as usize);
if handshake_length <= 0 || handshake_length > (record_length - 4).into() {
warn!("Invalid client hello length (fragmentation not implemented)");
return Err("Invalid client hello length (fragmentation not implemented)".into());
}
// Calculate the handshake length and return it
Ok(handshake_length + 9)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_too_little_data_end() {
let length = client_hello_buffer_size(&TOO_LITTLE_DATA_END);
if length.is_ok() {
assert!(false);
}
let msg = length.unwrap_err();
dbg!(msg);
assert!(true);
}
#[test]
fn test_too_little_data_start() {
let length = client_hello_buffer_size(&TOO_LITTLE_DATA_START);
assert!(length.is_ok());
assert_eq!(1712, length.unwrap())
}
#[test]
fn test_sni_extract() {
const BUF: [u8; 517] = [
@@ -99,6 +212,454 @@ mod tests {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let sni = get_sni(&BUF);
assert!(sni[0] == *"www.lirui.tech");
assert_eq!(sni[0], *"www.lirui.tech");
}
#[test]
fn test_sni_extract_tiny() {
const BUF: [u8; 1712] = [
0x16, 0x03, 0x01, 0x06, 0xab, 0x01, 0x00, 0x06, // |........|
0xa7, 0x03, 0x03, 0x84, 0x53, 0xb2, 0xd7, 0x37, // |....S..7|
0xcd, 0x27, 0xda, 0xf4, 0x70, 0xd8, 0x78, 0x26, // |.'..p.x&|
0x34, 0x7f, 0xe3, 0xa7, 0x5d, 0xfe, 0x97, 0x29, // |4...]..)|
0x89, 0x29, 0xa2, 0xd8, 0x62, 0x05, 0x7b, 0x13, // |.)..b.{.|
0xcf, 0x4b, 0x13, 0x20, 0x5b, 0x74, 0x4e, 0x23, // |.K. [tN#|
0x90, 0x08, 0x5a, 0x43, 0xbf, 0xe0, 0x0d, 0xeb, // |..ZC....|
0x8a, 0xc8, 0x4d, 0x14, 0x1e, 0x35, 0x43, 0x04, // |..M..5C.|
0x36, 0x32, 0xdc, 0x71, 0xff, 0xcc, 0xb3, 0x5b, // |62.q...[|
0x63, 0x4b, 0x2b, 0xee, 0x00, 0x20, 0xba, 0xba, // |cK+.. ..|
0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0xc0, 0x2b, // |.......+|
0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, // |./.,.0..|
0xcc, 0xa8, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, // |........|
0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0x01, 0x00, // |.../.5..|
0x06, 0x3e, 0x7a, 0x7a, 0x00, 0x00, 0x00, 0x0a, // |.>zz....|
0x00, 0x0c, 0x00, 0x0a, 0xda, 0xda, 0x11, 0xec, // |........|
0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0xff, 0x01, // |........|
0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, // |........|
0x00, 0x00, 0x13, 0x00, 0x11, 0x00, 0x00, 0x0e, // |........|
0x68, 0x61, 0x2e, 0x68, 0x6f, 0x6d, 0x65, 0x2e, // |ha.home.|
0x6b, 0x69, 0x65, 0x2e, 0x72, 0x73, 0xfe, 0x0d, // |kie.rs..|
0x00, 0xba, 0x00, 0x00, 0x01, 0x00, 0x01, 0x11, // |........|
0x00, 0x20, 0xf7, 0xf4, 0x20, 0xc8, 0xb7, 0xeb, // |. .. ...|
0xf1, 0x2d, 0x8b, 0x30, 0x2c, 0xc8, 0x5e, 0xd3, // |.-.0,.^.|
0xa3, 0x02, 0x38, 0xf2, 0x41, 0xf7, 0x3f, 0x2d, // |..8.A.?-|
0xb4, 0xf0, 0xd7, 0x3b, 0xe5, 0x19, 0x3f, 0xc3, // |...;..?.|
0xae, 0x1f, 0x00, 0x90, 0x27, 0x8d, 0x4c, 0xc9, // |....'.L.|
0xb3, 0xd1, 0x63, 0x20, 0xe4, 0x33, 0x18, 0x56, // |..c .3.V|
0xd5, 0x9b, 0xd5, 0xf9, 0xf2, 0x94, 0x1d, 0xe4, // |........|
0xa6, 0x88, 0x47, 0xd2, 0x85, 0x4f, 0xf4, 0x30, // |..G..O.0|
0x22, 0xff, 0x67, 0x80, 0x60, 0x33, 0x17, 0xa0, // |".g.`3..|
0x4f, 0xdb, 0x98, 0x53, 0x00, 0xa4, 0xc8, 0x89, // |O..S....|
0xb8, 0x1b, 0x3f, 0xbd, 0xdf, 0xeb, 0x48, 0x1a, // |..?...H.|
0xa1, 0x33, 0xd7, 0xc1, 0x8d, 0x76, 0xf2, 0xcf, // |.3...v..|
0xbe, 0x30, 0x1d, 0xcd, 0x3a, 0xfe, 0xf1, 0xb0, // |.0..:...|
0x86, 0xbc, 0x28, 0x74, 0x78, 0xa1, 0x9a, 0x60, // |..(tx..`|
0x14, 0xfe, 0x12, 0x92, 0x4d, 0xb5, 0x9e, 0x85, // |....M...|
0x79, 0x62, 0x9c, 0x68, 0x73, 0xc6, 0x0e, 0xe5, // |yb.hs...|
0xad, 0x5b, 0xe2, 0x69, 0x00, 0xc0, 0x26, 0x24, // |.[.i..&$|
0x88, 0xfa, 0x22, 0x29, 0x36, 0x7b, 0x16, 0x59, // |..")6{.Y|
0x48, 0xbe, 0xf9, 0x1c, 0x86, 0x55, 0xcb, 0x67, // |H....U.g|
0xae, 0xb6, 0x7b, 0x69, 0x3e, 0xd0, 0x48, 0x31, // |..{i>.H1|
0x58, 0x8a, 0xd8, 0xba, 0x06, 0x21, 0xf0, 0xd4, // |X....!..|
0x4e, 0xef, 0xcf, 0x67, 0xc5, 0x63, 0x97, 0x59, // |N..g.c.Y|
0x95, 0x12, 0x47, 0x90, 0x00, 0x2d, 0x00, 0x02, // |..G..-..|
0x01, 0x01, 0x00, 0x10, 0x00, 0x0b, 0x00, 0x09, // |........|
0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, // |.http/1.|
0x31, 0x00, 0x0d, 0x00, 0x12, 0x00, 0x10, 0x04, // |1.......|
0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08, // |........|
0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x00, // |........|
0x2b, 0x00, 0x07, 0x06, 0x0a, 0x0a, 0x03, 0x04, // |+.......|
0x03, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, 0x12, // |...#....|
0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, 0x02, 0x00, // |........|
0x02, 0x00, 0x33, 0x04, 0xef, 0x04, 0xed, 0xda, // |..3.....|
0xda, 0x00, 0x01, 0x00, 0x11, 0xec, 0x04, 0xc0, // |........|
0xc6, 0x12, 0x85, 0x0b, 0xba, 0x73, 0x9d, 0x00, // |.....s..|
0x29, 0x08, 0x40, 0x3a, 0xb8, 0xfc, 0x9e, 0x99, // |).@:....|
0x25, 0xbd, 0x60, 0xb6, 0x8a, 0x56, 0x51, 0xac, // |%.`..VQ.|
0x38, 0xa3, 0x15, 0x37, 0x21, 0x80, 0x86, 0x02, // |8..7!...|
0xb2, 0x10, 0x4b, 0x29, 0xeb, 0x37, 0x04, 0x47, // |..K).7.G|
0x16, 0x12, 0x0e, 0x63, 0x2d, 0x32, 0xf6, 0x2a, // |...c-2.*|
0x86, 0x09, 0x7b, 0x41, 0x28, 0x8c, 0xcf, 0xfa, // |..{A(...|
0x08, 0x2e, 0x0c, 0xb2, 0x55, 0xb4, 0xb4, 0xd2, // |....U...|
0x76, 0x38, 0x47, 0x44, 0x78, 0xf0, 0x01, 0xb6, // |v8GDx...|
0xee, 0xf0, 0x1f, 0x4b, 0xc5, 0x6b, 0xb3, 0x93, // |...K.k..|
0x4d, 0xa5, 0x25, 0x29, 0xda, 0x33, 0x1e, 0xc5, // |M.%).3..|
0x15, 0x98, 0xf5, 0x41, 0x3e, 0xd2, 0xf7, 0x82, // |...A>...|
0xd7, 0xbb, 0x56, 0xf0, 0x86, 0x29, 0xa3, 0x56, // |..V..).V|
0x25, 0xdc, 0xaa, 0x03, 0xaa, 0x28, 0xa7, 0x2b, // |%....(.+|
0xc0, 0x41, 0xca, 0x66, 0x3e, 0xcc, 0x21, 0x40, // |.A.f>.!@|
0x60, 0x34, 0x5f, 0x9f, 0x69, 0x37, 0xac, 0x30, // |`4_.i7.0|
0x06, 0x7a, 0xf9, 0x26, 0xfe, 0x3c, 0x13, 0x05, // |.z.&.<..|
0xf6, 0xbe, 0x5f, 0x0c, 0x9a, 0x43, 0x18, 0xa2, // |.._..C..|
0xd9, 0xc5, 0xa0, 0x06, 0x0b, 0x0a, 0x21, 0xf1, // |......!.|
0x6b, 0x12, 0x4a, 0x5d, 0xec, 0xf6, 0x01, 0x30, // |k.J]...0|
0xb6, 0x3b, 0x34, 0x62, 0xcd, 0x5a, 0x6a, 0x26, // |.;4b.Zj&|
0x08, 0x98, 0xc9, 0xd0, 0x8a, 0x49, 0x94, 0x07, // |.....I..|
0x48, 0x45, 0x78, 0x45, 0xae, 0x24, 0x2b, 0x83, // |HExE.$+.|
0xb6, 0x69, 0x6c, 0x20, 0x33, 0xa9, 0xc4, 0x8e, // |.il 3...|
0xe7, 0x1a, 0x90, 0x28, 0xc6, 0x3f, 0x16, 0xf2, // |...(.?..|
0xae, 0x3e, 0x22, 0x17, 0x26, 0x9c, 0x38, 0xf5, // |.>".&.8.|
0x88, 0x60, 0x79, 0x16, 0x28, 0xce, 0x05, 0x72, // |.`y.(..r|
0x2f, 0x64, 0x99, 0xdd, 0x8c, 0x5b, 0xa6, 0xe2, // |/d...[..|
0x65, 0x8a, 0xe2, 0x8d, 0xb6, 0x24, 0x9e, 0x6d, // |e....$.m|
0x5a, 0x70, 0xfd, 0xea, 0xca, 0xec, 0x77, 0x46, // |Zp....wF|
0x20, 0xa8, 0x1f, 0x78, 0xf6, 0x34, 0x52, 0x13, // | ..x.4R.|
0x97, 0xef, 0x60, 0xb9, 0xe5, 0xc6, 0x85, 0xf2, // |..`.....|
0x84, 0x64, 0xdc, 0x08, 0x07, 0xe2, 0x63, 0xa6, // |.d....c.|
0x23, 0x64, 0x54, 0xb8, 0x72, 0xac, 0x23, 0xda, // |#dT.r.#.|
0x8f, 0x73, 0xe4, 0x9b, 0x80, 0x77, 0x66, 0x3f, // |.s...wf?|
0x69, 0x34, 0xc4, 0xfb, 0x45, 0x3d, 0x1c, 0xa7, // |i4..E=..|
0x86, 0x98, 0x2e, 0xb4, 0xe0, 0x84, 0xb6, 0x47, // |.......G|
0x78, 0xeb, 0x2b, 0x10, 0x17, 0x45, 0x8a, 0xcf, // |x.+..E..|
0xea, 0xb5, 0x58, 0x42, 0x93, 0xbe, 0x4b, 0xad, // |..XB..K.|
0xfb, 0x28, 0x11, 0x12, 0xe0, 0x7c, 0x3d, 0x34, // |.(...|=4|
0x8c, 0x82, 0x07, 0x84, 0xda, 0x8b, 0x35, 0x86, // |......5.|
0x37, 0x35, 0x1d, 0x1a, 0xa2, 0xbf, 0x0a, 0xb4, // |75......|
0x8e, 0xf0, 0x91, 0xc4, 0xa8, 0x3f, 0x38, 0x03, // |.....?8.|
0x37, 0xc1, 0x9a, 0x94, 0x43, 0x09, 0x57, 0xee, // |7...C.W.|
0xaa, 0xcb, 0x3d, 0x13, 0xa2, 0x33, 0xd1, 0x04, // |..=..3..|
0x2c, 0x6c, 0xb4, 0x1c, 0x86, 0x07, 0x0c, 0x3c, // |,l.....<|
0x5c, 0xc9, 0x8c, 0xc8, 0x1a, 0x85, 0xa6, 0xdd, // |\.......|
0xd3, 0xc5, 0xae, 0x84, 0x4d, 0xfe, 0xa2, 0x99, // |....M...|
0xd3, 0x0b, 0x1f, 0x43, 0x01, 0xa6, 0x7b, 0xb2, // |...C..{.|
0x5b, 0xd5, 0xa0, 0x3e, 0xd4, 0x6c, 0x65, 0x75, // |[..>.leu|
0x55, 0x28, 0x4d, 0x1c, 0x28, 0x86, 0xda, 0x94, // |U(M.(...|
0xbe, 0x0a, 0x99, 0x61, 0xa4, 0x88, 0xd9, 0x6a, // |...a...j|
0x20, 0x1d, 0x78, 0x45, 0x5f, 0x66, 0xcc, 0x8c, // | .xE_f..|
0xe1, 0xba, 0x4c, 0x51, 0x99, 0x54, 0x27, 0x77, // |..LQ.T'w|
0xb4, 0x84, 0x61, 0x4e, 0xf9, 0x90, 0x6f, 0x19, // |..aN..o.|
0x44, 0x93, 0x27, 0x1d, 0x95, 0x82, 0x74, 0x7f, // |D.'...t.|
0x35, 0xaf, 0x04, 0xe4, 0x58, 0x41, 0x3a, 0x51, // |5...XA:Q|
0x0b, 0x22, 0x45, 0xaf, 0x44, 0x2a, 0xe9, 0xa3, // |."E.D*..|
0x71, 0x65, 0x15, 0x22, 0xea, 0x40, 0x10, 0xaf, // |qe.".@..|
0x5b, 0x27, 0xfc, 0x02, 0x00, 0x23, 0xa3, 0x70, // |['...#.p|
0xa9, 0x6c, 0xa7, 0xf7, 0x29, 0x5c, 0x75, 0x9b, // |.l..)\u.|
0x4c, 0x23, 0x14, 0x51, 0x12, 0x62, 0x71, 0xbb, // |L#.Q.bq.|
0x75, 0x64, 0x65, 0xb3, 0xaa, 0x1e, 0x10, 0x14, // |ude.....|
0xbf, 0xd0, 0x8b, 0xe0, 0xe4, 0x51, 0x6e, 0xa8, // |.....Qn.|
0x1a, 0x95, 0x21, 0xa9, 0x9f, 0xf7, 0x2a, 0xac, // |..!...*.|
0x5c, 0x1c, 0x12, 0xac, 0x9d, 0xac, 0x57, 0x14, // |\.....W.|
0x27, 0xaa, 0xa7, 0xee, 0xc3, 0x9d, 0x63, 0x48, // |'.....cH|
0x0e, 0xd7, 0xf8, 0x92, 0x9f, 0x28, 0xb9, 0x82, // |.....(..|
0x71, 0x99, 0xa1, 0xcb, 0x69, 0x0c, 0x29, 0x7d, // |q...i.)}|
0x67, 0x73, 0xae, 0x9d, 0xd7, 0xc7, 0x51, 0x7a, // |gs....Qz|
0x2c, 0x3a, 0x74, 0x89, 0x7d, 0x76, 0x35, 0xb5, // |,:t.}v5.|
0x97, 0x73, 0x4a, 0xfc, 0x29, 0x9a, 0x1a, 0x06, // |.sJ.)...|
0x2f, 0xd0, 0x89, 0x32, 0xfc, 0x3b, 0x17, 0xec, // |/..2.;..|
0x7a, 0xb5, 0x3c, 0x66, 0x0f, 0x43, 0x55, 0x41, // |z.<f.CUA|
0x49, 0x3f, 0xbf, 0xa1, 0x6f, 0x8a, 0x05, 0x76, // |I?..o..v|
0xd4, 0x02, 0x33, 0x52, 0x78, 0xc8, 0x08, 0xe9, // |..3Rx...|
0x49, 0xb8, 0x42, 0x05, 0xed, 0x34, 0x0a, 0xb1, // |I.B..4..|
0xa8, 0x32, 0x00, 0x6b, 0x00, 0x42, 0x56, 0x8a, // |.2.k.BV.|
0xe9, 0x04, 0x7a, 0xac, 0xc8, 0x72, 0x7f, 0x40, // |..z..r.@|
0x4c, 0xd6, 0xa9, 0x34, 0x0b, 0xc3, 0x63, 0x39, // |L..4..c9|
0x21, 0xbf, 0x04, 0xb0, 0x2b, 0x81, 0xf9, 0x07, // |!...+...|
0xe6, 0x15, 0x92, 0x89, 0x9b, 0x1e, 0xe6, 0x4b, // |.......K|
0x5b, 0x0b, 0x33, 0x5f, 0x89, 0x96, 0xa2, 0x74, // |[.3_...t|
0x41, 0x6b, 0x15, 0xe8, 0x8a, 0x62, 0xf5, 0x1c, // |Ak...b..|
0x37, 0x38, 0x62, 0x77, 0xd4, 0x57, 0x7b, 0x43, // |78bw.W{C|
0x42, 0x4f, 0x01, 0x9c, 0xf2, 0xe0, 0x68, 0xb7, // |BO....h.|
0xf1, 0x66, 0x93, 0xd8, 0x8e, 0x78, 0x80, 0x24, // |.f...x.$|
0x4c, 0x61, 0x11, 0xbb, 0xf2, 0x79, 0xf7, 0x96, // |La...y..|
0x02, 0x80, 0xaa, 0xc7, 0xcd, 0xbb, 0x55, 0x03, // |......U.|
0x22, 0x5e, 0xda, 0xa2, 0x44, 0x7d, 0x82, 0x41, // |"^..D}.A|
0x86, 0x9b, 0x92, 0x0a, 0xd5, 0x7e, 0xf2, 0x78, // |.....~.x|
0x84, 0x50, 0x00, 0x2d, 0x0b, 0xab, 0x92, 0x7a, // |.P.-...z|
0x96, 0x15, 0xcf, 0x5a, 0x34, 0x45, 0x35, 0xa7, // |...Z4E5.|
0x18, 0x61, 0x2b, 0x88, 0x45, 0xaa, 0xd3, 0xe2, // |.a+.E...|
0x54, 0xf9, 0xc7, 0xbb, 0xe7, 0x00, 0x86, 0xbd, // |T.......|
0x8b, 0xbb, 0x6d, 0x3b, 0x0f, 0x8d, 0xfb, 0x4d, // |..m;...M|
0x5d, 0x8b, 0x50, 0x2e, 0x68, 0x74, 0x5d, 0x03, // |].P.ht].|
0x16, 0x2a, 0x49, 0x24, 0x54, 0x5b, 0xa9, 0x34, // |.*I$T[.4|
0x25, 0x17, 0x79, 0xe3, 0xc3, 0x3a, 0x2a, 0x12, // |%.y..:*.|
0x75, 0x64, 0x16, 0xa4, 0xb7, 0x7e, 0x39, 0x5a, // |ud...~9Z|
0x4e, 0x3e, 0x53, 0x2b, 0x49, 0x1b, 0x26, 0xdf, // |N>S+I.&.|
0xfc, 0x29, 0x99, 0xcb, 0xad, 0x29, 0x2c, 0x72, // |.)...),r|
0x3f, 0xa7, 0xcb, 0x45, 0x4c, 0x14, 0xee, 0x46, // |?..EL..F|
0x74, 0x64, 0xdb, 0x4b, 0x4b, 0xa4, 0x35, 0x3c, // |td.KK.5<|
0x91, 0xc4, 0x9b, 0xb0, 0x66, 0xc6, 0x70, 0xb6, // |....f.p.|
0xf2, 0x07, 0x3b, 0xbf, 0x74, 0x72, 0xb4, 0x24, // |..;.tr.$|
0x7e, 0x87, 0xd4, 0x0a, 0x37, 0xd9, 0x49, 0x04, // |~...7.I.|
0x09, 0x36, 0xd1, 0x63, 0x88, 0xe1, 0xe8, 0x08, // |.6.c....|
0xbf, 0x17, 0xc4, 0xcd, 0xcb, 0x3c, 0xef, 0x88, // |.....<..|
0x2c, 0xf6, 0xa3, 0x6d, 0x89, 0x39, 0xc9, 0xfe, // |,..m.9..|
0x97, 0x25, 0xb3, 0x9a, 0x02, 0x40, 0xd4, 0x90, // |.%...@..|
0x28, 0x6a, 0x79, 0xbd, 0x4b, 0x8e, 0x10, 0x18, // |(jy.K...|
0xc9, 0xaf, 0xe9, 0xc0, 0x6e, 0xd5, 0xb1, 0xcf, // |....n...|
0xe8, 0xa4, 0xdc, 0x94, 0x12, 0x82, 0xfb, 0x08, // |........|
0x42, 0xd4, 0x1a, 0x76, 0xa2, 0x4b, 0x3f, 0xc3, // |B..v.K?.|
0xb4, 0x0b, 0xa3, 0x0c, 0xec, 0x19, 0x7c, 0x5f, // |......|_|
0xd5, 0x98, 0x99, 0xf4, 0x1a, 0xca, 0x83, 0xaa, // |........|
0xbd, 0x26, 0x31, 0x95, 0x77, 0x90, 0x43, 0x7a, // |.&1.w.Cz|
0x75, 0x15, 0xcb, 0x68, 0xae, 0x24, 0xc5, 0x1b, // |u..h.$..|
// Cutoff here.
0x8c, 0x49, 0xbe, 0xfc, 0x61, 0x54, 0xd7, 0x18, // |.I..aT..|
0x9d, 0x21, 0x10, 0x14, 0xe2, 0x6d, 0x5b, 0x4b, // |.!...m[K|
0xb0, 0x94, 0xaa, 0x6e, 0xd5, 0x7b, 0xba, 0x6e, // |...n.{.n|
0xe0, 0x03, 0xac, 0x9a, 0xbb, 0xe1, 0x17, 0x9b, // |........|
0x18, 0x0c, 0x33, 0xcc, 0x05, 0x91, 0x1c, 0x43, // |..3....C|
0x37, 0xd2, 0x10, 0xb7, 0xc6, 0xc7, 0x6b, 0xda, // |7.....k.|
0x87, 0x9c, 0xaf, 0x93, 0x52, 0x2f, 0x4c, 0x6e, // |....R/Ln|
0x14, 0xdb, 0x49, 0xbc, 0xeb, 0x96, 0xda, 0xb6, // |..I.....|
0x3b, 0xf8, 0xc0, 0x33, 0xba, 0x15, 0x37, 0x39, // |;..3..79|
0xe7, 0xae, 0xb6, 0x48, 0x3e, 0xd8, 0x57, 0x67, // |...H>.Wg|
0x9c, 0xb6, 0x9c, 0xc0, 0x18, 0x0e, 0x74, 0x67, // |......tg|
0xae, 0x8e, 0xc6, 0x80, 0x7f, 0x81, 0x25, 0xc4, // |......%.|
0xe9, 0x04, 0xe8, 0xd9, 0x98, 0xb6, 0x99, 0x93, // |........|
0xa1, 0xa4, 0x5e, 0x57, 0x74, 0x89, 0x30, 0x38, // |..^Wt.08|
0xa9, 0xbb, 0x99, 0x4a, 0x7e, 0x42, 0x3c, 0xd2, // |...J~B<.|
0x59, 0xb6, 0x49, 0xb0, 0xc7, 0x11, 0x57, 0x03, // |Y.I...W.|
0x6d, 0x23, 0x1b, 0x72, 0xe7, 0x24, 0xdb, 0x75, // |m#.r.$.u|
0x78, 0xd1, 0x38, 0x01, 0x46, 0xb6, 0x8c, 0x1b, // |x.8.F...|
0x41, 0xb4, 0xbd, 0xc1, 0xa2, 0x00, 0x63, 0xa5, // |A.....c.|
0x97, 0x30, 0x5d, 0xbe, 0xd1, 0x37, 0x31, 0xf1, // |.0]..71.|
0xbb, 0xc6, 0xf8, 0x81, 0x35, 0x86, 0x32, 0xa6, // |....5.2.|
0xc3, 0x35, 0x54, 0x45, 0x50, 0xdf, 0x61, 0x46, // |.5TEP.aF|
0x5b, 0x83, 0x6b, 0xac, 0x5c, 0x2d, 0xa2, 0xc3, // |[.k.\-..|
0x2e, 0x71, 0x32, 0x18, 0x41, 0x29, 0x99, 0x66, // |.q2.A).f|
0x8c, 0x50, 0x28, 0x92, 0x45, 0xae, 0x96, 0x38, // |.P(.E..8|
0xa4, 0x83, 0x94, 0x4a, 0x2f, 0x0e, 0x62, 0x13, // |...J/.b.|
0x07, 0x13, 0xc2, 0x0b, 0x84, 0xfd, 0x27, 0xab, // |......'.|
0x6c, 0xb4, 0x69, 0x0d, 0xd2, 0xdb, 0xfb, 0x8e, // |l.i.....|
0xa7, 0x09, 0x65, 0x76, 0x7e, 0x09, 0xa4, 0x7a, // |..ev~..z|
0xe9, 0xfe, 0xec, 0x52, 0x89, 0x7d, 0x07, 0x6f, // |...R.}.o|
0xff, 0xa0, 0xde, 0x8a, 0x42, 0x2d, 0xc3, 0x75, // |....B-.u|
0x05, 0x6d, 0x60, 0x76, 0xce, 0xe1, 0x6c, 0xfd, // |.m`v..l.|
0xae, 0x1f, 0x5e, 0x02, 0x94, 0x39, 0x2a, 0x55, // |..^..9*U|
0x00, 0x1d, 0x00, 0x20, 0x8d, 0x89, 0x9a, 0x19, // |... ....|
0x1d, 0x53, 0x52, 0xd5, 0xc1, 0x3e, 0x3a, 0x1d, // |.SR..>:.|
0x12, 0x15, 0xae, 0x33, 0x2e, 0x54, 0xd1, 0x6f, // |...3.T.o|
0xd6, 0xb1, 0x73, 0xd9, 0x56, 0x98, 0x6f, 0x8f, // |..s.V.o.|
0x7e, 0xf5, 0xd9, 0x75, 0x00, 0x0b, 0x00, 0x02, // |~..u....|
0x01, 0x00, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, // |........|
0x00, 0x00, 0x00, 0x9a, 0x9a, 0x00, 0x01, 0x00, // |........|
];
let sni = get_sni(&BUF);
assert_eq!(sni[0], *"ha.home.kie.rs");
}
#[test]
fn test_too_little_data() {
let sni = get_sni(&TOO_LITTLE_DATA_END);
assert_eq!(0, sni.len());
}
const TOO_LITTLE_DATA_START: [u8; 1392] = [
0x16, 0x03, 0x01, 0x06, 0xab, 0x01, 0x00, 0x06, // |........|
0xa7, 0x03, 0x03, 0x84, 0x53, 0xb2, 0xd7, 0x37, // |....S..7|
0xcd, 0x27, 0xda, 0xf4, 0x70, 0xd8, 0x78, 0x26, // |.'..p.x&|
0x34, 0x7f, 0xe3, 0xa7, 0x5d, 0xfe, 0x97, 0x29, // |4...]..)|
0x89, 0x29, 0xa2, 0xd8, 0x62, 0x05, 0x7b, 0x13, // |.)..b.{.|
0xcf, 0x4b, 0x13, 0x20, 0x5b, 0x74, 0x4e, 0x23, // |.K. [tN#|
0x90, 0x08, 0x5a, 0x43, 0xbf, 0xe0, 0x0d, 0xeb, // |..ZC....|
0x8a, 0xc8, 0x4d, 0x14, 0x1e, 0x35, 0x43, 0x04, // |..M..5C.|
0x36, 0x32, 0xdc, 0x71, 0xff, 0xcc, 0xb3, 0x5b, // |62.q...[|
0x63, 0x4b, 0x2b, 0xee, 0x00, 0x20, 0xba, 0xba, // |cK+.. ..|
0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0xc0, 0x2b, // |.......+|
0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, // |./.,.0..|
0xcc, 0xa8, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, // |........|
0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0x01, 0x00, // |.../.5..|
0x06, 0x3e, 0x7a, 0x7a, 0x00, 0x00, 0x00, 0x0a, // |.>zz....|
0x00, 0x0c, 0x00, 0x0a, 0xda, 0xda, 0x11, 0xec, // |........|
0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0xff, 0x01, // |........|
0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, // |........|
0x00, 0x00, 0x13, 0x00, 0x11, 0x00, 0x00, 0x0e, // |........|
0x68, 0x61, 0x2e, 0x68, 0x6f, 0x6d, 0x65, 0x2e, // |ha.home.|
0x6b, 0x69, 0x65, 0x2e, 0x72, 0x73, 0xfe, 0x0d, // |kie.rs..|
0x00, 0xba, 0x00, 0x00, 0x01, 0x00, 0x01, 0x11, // |........|
0x00, 0x20, 0xf7, 0xf4, 0x20, 0xc8, 0xb7, 0xeb, // |. .. ...|
0xf1, 0x2d, 0x8b, 0x30, 0x2c, 0xc8, 0x5e, 0xd3, // |.-.0,.^.|
0xa3, 0x02, 0x38, 0xf2, 0x41, 0xf7, 0x3f, 0x2d, // |..8.A.?-|
0xb4, 0xf0, 0xd7, 0x3b, 0xe5, 0x19, 0x3f, 0xc3, // |...;..?.|
0xae, 0x1f, 0x00, 0x90, 0x27, 0x8d, 0x4c, 0xc9, // |....'.L.|
0xb3, 0xd1, 0x63, 0x20, 0xe4, 0x33, 0x18, 0x56, // |..c .3.V|
0xd5, 0x9b, 0xd5, 0xf9, 0xf2, 0x94, 0x1d, 0xe4, // |........|
0xa6, 0x88, 0x47, 0xd2, 0x85, 0x4f, 0xf4, 0x30, // |..G..O.0|
0x22, 0xff, 0x67, 0x80, 0x60, 0x33, 0x17, 0xa0, // |".g.`3..|
0x4f, 0xdb, 0x98, 0x53, 0x00, 0xa4, 0xc8, 0x89, // |O..S....|
0xb8, 0x1b, 0x3f, 0xbd, 0xdf, 0xeb, 0x48, 0x1a, // |..?...H.|
0xa1, 0x33, 0xd7, 0xc1, 0x8d, 0x76, 0xf2, 0xcf, // |.3...v..|
0xbe, 0x30, 0x1d, 0xcd, 0x3a, 0xfe, 0xf1, 0xb0, // |.0..:...|
0x86, 0xbc, 0x28, 0x74, 0x78, 0xa1, 0x9a, 0x60, // |..(tx..`|
0x14, 0xfe, 0x12, 0x92, 0x4d, 0xb5, 0x9e, 0x85, // |....M...|
0x79, 0x62, 0x9c, 0x68, 0x73, 0xc6, 0x0e, 0xe5, // |yb.hs...|
0xad, 0x5b, 0xe2, 0x69, 0x00, 0xc0, 0x26, 0x24, // |.[.i..&$|
0x88, 0xfa, 0x22, 0x29, 0x36, 0x7b, 0x16, 0x59, // |..")6{.Y|
0x48, 0xbe, 0xf9, 0x1c, 0x86, 0x55, 0xcb, 0x67, // |H....U.g|
0xae, 0xb6, 0x7b, 0x69, 0x3e, 0xd0, 0x48, 0x31, // |..{i>.H1|
0x58, 0x8a, 0xd8, 0xba, 0x06, 0x21, 0xf0, 0xd4, // |X....!..|
0x4e, 0xef, 0xcf, 0x67, 0xc5, 0x63, 0x97, 0x59, // |N..g.c.Y|
0x95, 0x12, 0x47, 0x90, 0x00, 0x2d, 0x00, 0x02, // |..G..-..|
0x01, 0x01, 0x00, 0x10, 0x00, 0x0b, 0x00, 0x09, // |........|
0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, // |.http/1.|
0x31, 0x00, 0x0d, 0x00, 0x12, 0x00, 0x10, 0x04, // |1.......|
0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08, // |........|
0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x00, // |........|
0x2b, 0x00, 0x07, 0x06, 0x0a, 0x0a, 0x03, 0x04, // |+.......|
0x03, 0x03, 0x00, 0x23, 0x00, 0x00, 0x00, 0x12, // |...#....|
0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, 0x02, 0x00, // |........|
0x02, 0x00, 0x33, 0x04, 0xef, 0x04, 0xed, 0xda, // |..3.....|
0xda, 0x00, 0x01, 0x00, 0x11, 0xec, 0x04, 0xc0, // |........|
0xc6, 0x12, 0x85, 0x0b, 0xba, 0x73, 0x9d, 0x00, // |.....s..|
0x29, 0x08, 0x40, 0x3a, 0xb8, 0xfc, 0x9e, 0x99, // |).@:....|
0x25, 0xbd, 0x60, 0xb6, 0x8a, 0x56, 0x51, 0xac, // |%.`..VQ.|
0x38, 0xa3, 0x15, 0x37, 0x21, 0x80, 0x86, 0x02, // |8..7!...|
0xb2, 0x10, 0x4b, 0x29, 0xeb, 0x37, 0x04, 0x47, // |..K).7.G|
0x16, 0x12, 0x0e, 0x63, 0x2d, 0x32, 0xf6, 0x2a, // |...c-2.*|
0x86, 0x09, 0x7b, 0x41, 0x28, 0x8c, 0xcf, 0xfa, // |..{A(...|
0x08, 0x2e, 0x0c, 0xb2, 0x55, 0xb4, 0xb4, 0xd2, // |....U...|
0x76, 0x38, 0x47, 0x44, 0x78, 0xf0, 0x01, 0xb6, // |v8GDx...|
0xee, 0xf0, 0x1f, 0x4b, 0xc5, 0x6b, 0xb3, 0x93, // |...K.k..|
0x4d, 0xa5, 0x25, 0x29, 0xda, 0x33, 0x1e, 0xc5, // |M.%).3..|
0x15, 0x98, 0xf5, 0x41, 0x3e, 0xd2, 0xf7, 0x82, // |...A>...|
0xd7, 0xbb, 0x56, 0xf0, 0x86, 0x29, 0xa3, 0x56, // |..V..).V|
0x25, 0xdc, 0xaa, 0x03, 0xaa, 0x28, 0xa7, 0x2b, // |%....(.+|
0xc0, 0x41, 0xca, 0x66, 0x3e, 0xcc, 0x21, 0x40, // |.A.f>.!@|
0x60, 0x34, 0x5f, 0x9f, 0x69, 0x37, 0xac, 0x30, // |`4_.i7.0|
0x06, 0x7a, 0xf9, 0x26, 0xfe, 0x3c, 0x13, 0x05, // |.z.&.<..|
0xf6, 0xbe, 0x5f, 0x0c, 0x9a, 0x43, 0x18, 0xa2, // |.._..C..|
0xd9, 0xc5, 0xa0, 0x06, 0x0b, 0x0a, 0x21, 0xf1, // |......!.|
0x6b, 0x12, 0x4a, 0x5d, 0xec, 0xf6, 0x01, 0x30, // |k.J]...0|
0xb6, 0x3b, 0x34, 0x62, 0xcd, 0x5a, 0x6a, 0x26, // |.;4b.Zj&|
0x08, 0x98, 0xc9, 0xd0, 0x8a, 0x49, 0x94, 0x07, // |.....I..|
0x48, 0x45, 0x78, 0x45, 0xae, 0x24, 0x2b, 0x83, // |HExE.$+.|
0xb6, 0x69, 0x6c, 0x20, 0x33, 0xa9, 0xc4, 0x8e, // |.il 3...|
0xe7, 0x1a, 0x90, 0x28, 0xc6, 0x3f, 0x16, 0xf2, // |...(.?..|
0xae, 0x3e, 0x22, 0x17, 0x26, 0x9c, 0x38, 0xf5, // |.>".&.8.|
0x88, 0x60, 0x79, 0x16, 0x28, 0xce, 0x05, 0x72, // |.`y.(..r|
0x2f, 0x64, 0x99, 0xdd, 0x8c, 0x5b, 0xa6, 0xe2, // |/d...[..|
0x65, 0x8a, 0xe2, 0x8d, 0xb6, 0x24, 0x9e, 0x6d, // |e....$.m|
0x5a, 0x70, 0xfd, 0xea, 0xca, 0xec, 0x77, 0x46, // |Zp....wF|
0x20, 0xa8, 0x1f, 0x78, 0xf6, 0x34, 0x52, 0x13, // | ..x.4R.|
0x97, 0xef, 0x60, 0xb9, 0xe5, 0xc6, 0x85, 0xf2, // |..`.....|
0x84, 0x64, 0xdc, 0x08, 0x07, 0xe2, 0x63, 0xa6, // |.d....c.|
0x23, 0x64, 0x54, 0xb8, 0x72, 0xac, 0x23, 0xda, // |#dT.r.#.|
0x8f, 0x73, 0xe4, 0x9b, 0x80, 0x77, 0x66, 0x3f, // |.s...wf?|
0x69, 0x34, 0xc4, 0xfb, 0x45, 0x3d, 0x1c, 0xa7, // |i4..E=..|
0x86, 0x98, 0x2e, 0xb4, 0xe0, 0x84, 0xb6, 0x47, // |.......G|
0x78, 0xeb, 0x2b, 0x10, 0x17, 0x45, 0x8a, 0xcf, // |x.+..E..|
0xea, 0xb5, 0x58, 0x42, 0x93, 0xbe, 0x4b, 0xad, // |..XB..K.|
0xfb, 0x28, 0x11, 0x12, 0xe0, 0x7c, 0x3d, 0x34, // |.(...|=4|
0x8c, 0x82, 0x07, 0x84, 0xda, 0x8b, 0x35, 0x86, // |......5.|
0x37, 0x35, 0x1d, 0x1a, 0xa2, 0xbf, 0x0a, 0xb4, // |75......|
0x8e, 0xf0, 0x91, 0xc4, 0xa8, 0x3f, 0x38, 0x03, // |.....?8.|
0x37, 0xc1, 0x9a, 0x94, 0x43, 0x09, 0x57, 0xee, // |7...C.W.|
0xaa, 0xcb, 0x3d, 0x13, 0xa2, 0x33, 0xd1, 0x04, // |..=..3..|
0x2c, 0x6c, 0xb4, 0x1c, 0x86, 0x07, 0x0c, 0x3c, // |,l.....<|
0x5c, 0xc9, 0x8c, 0xc8, 0x1a, 0x85, 0xa6, 0xdd, // |\.......|
0xd3, 0xc5, 0xae, 0x84, 0x4d, 0xfe, 0xa2, 0x99, // |....M...|
0xd3, 0x0b, 0x1f, 0x43, 0x01, 0xa6, 0x7b, 0xb2, // |...C..{.|
0x5b, 0xd5, 0xa0, 0x3e, 0xd4, 0x6c, 0x65, 0x75, // |[..>.leu|
0x55, 0x28, 0x4d, 0x1c, 0x28, 0x86, 0xda, 0x94, // |U(M.(...|
0xbe, 0x0a, 0x99, 0x61, 0xa4, 0x88, 0xd9, 0x6a, // |...a...j|
0x20, 0x1d, 0x78, 0x45, 0x5f, 0x66, 0xcc, 0x8c, // | .xE_f..|
0xe1, 0xba, 0x4c, 0x51, 0x99, 0x54, 0x27, 0x77, // |..LQ.T'w|
0xb4, 0x84, 0x61, 0x4e, 0xf9, 0x90, 0x6f, 0x19, // |..aN..o.|
0x44, 0x93, 0x27, 0x1d, 0x95, 0x82, 0x74, 0x7f, // |D.'...t.|
0x35, 0xaf, 0x04, 0xe4, 0x58, 0x41, 0x3a, 0x51, // |5...XA:Q|
0x0b, 0x22, 0x45, 0xaf, 0x44, 0x2a, 0xe9, 0xa3, // |."E.D*..|
0x71, 0x65, 0x15, 0x22, 0xea, 0x40, 0x10, 0xaf, // |qe.".@..|
0x5b, 0x27, 0xfc, 0x02, 0x00, 0x23, 0xa3, 0x70, // |['...#.p|
0xa9, 0x6c, 0xa7, 0xf7, 0x29, 0x5c, 0x75, 0x9b, // |.l..)\u.|
0x4c, 0x23, 0x14, 0x51, 0x12, 0x62, 0x71, 0xbb, // |L#.Q.bq.|
0x75, 0x64, 0x65, 0xb3, 0xaa, 0x1e, 0x10, 0x14, // |ude.....|
0xbf, 0xd0, 0x8b, 0xe0, 0xe4, 0x51, 0x6e, 0xa8, // |.....Qn.|
0x1a, 0x95, 0x21, 0xa9, 0x9f, 0xf7, 0x2a, 0xac, // |..!...*.|
0x5c, 0x1c, 0x12, 0xac, 0x9d, 0xac, 0x57, 0x14, // |\.....W.|
0x27, 0xaa, 0xa7, 0xee, 0xc3, 0x9d, 0x63, 0x48, // |'.....cH|
0x0e, 0xd7, 0xf8, 0x92, 0x9f, 0x28, 0xb9, 0x82, // |.....(..|
0x71, 0x99, 0xa1, 0xcb, 0x69, 0x0c, 0x29, 0x7d, // |q...i.)}|
0x67, 0x73, 0xae, 0x9d, 0xd7, 0xc7, 0x51, 0x7a, // |gs....Qz|
0x2c, 0x3a, 0x74, 0x89, 0x7d, 0x76, 0x35, 0xb5, // |,:t.}v5.|
0x97, 0x73, 0x4a, 0xfc, 0x29, 0x9a, 0x1a, 0x06, // |.sJ.)...|
0x2f, 0xd0, 0x89, 0x32, 0xfc, 0x3b, 0x17, 0xec, // |/..2.;..|
0x7a, 0xb5, 0x3c, 0x66, 0x0f, 0x43, 0x55, 0x41, // |z.<f.CUA|
0x49, 0x3f, 0xbf, 0xa1, 0x6f, 0x8a, 0x05, 0x76, // |I?..o..v|
0xd4, 0x02, 0x33, 0x52, 0x78, 0xc8, 0x08, 0xe9, // |..3Rx...|
0x49, 0xb8, 0x42, 0x05, 0xed, 0x34, 0x0a, 0xb1, // |I.B..4..|
0xa8, 0x32, 0x00, 0x6b, 0x00, 0x42, 0x56, 0x8a, // |.2.k.BV.|
0xe9, 0x04, 0x7a, 0xac, 0xc8, 0x72, 0x7f, 0x40, // |..z..r.@|
0x4c, 0xd6, 0xa9, 0x34, 0x0b, 0xc3, 0x63, 0x39, // |L..4..c9|
0x21, 0xbf, 0x04, 0xb0, 0x2b, 0x81, 0xf9, 0x07, // |!...+...|
0xe6, 0x15, 0x92, 0x89, 0x9b, 0x1e, 0xe6, 0x4b, // |.......K|
0x5b, 0x0b, 0x33, 0x5f, 0x89, 0x96, 0xa2, 0x74, // |[.3_...t|
0x41, 0x6b, 0x15, 0xe8, 0x8a, 0x62, 0xf5, 0x1c, // |Ak...b..|
0x37, 0x38, 0x62, 0x77, 0xd4, 0x57, 0x7b, 0x43, // |78bw.W{C|
0x42, 0x4f, 0x01, 0x9c, 0xf2, 0xe0, 0x68, 0xb7, // |BO....h.|
0xf1, 0x66, 0x93, 0xd8, 0x8e, 0x78, 0x80, 0x24, // |.f...x.$|
0x4c, 0x61, 0x11, 0xbb, 0xf2, 0x79, 0xf7, 0x96, // |La...y..|
0x02, 0x80, 0xaa, 0xc7, 0xcd, 0xbb, 0x55, 0x03, // |......U.|
0x22, 0x5e, 0xda, 0xa2, 0x44, 0x7d, 0x82, 0x41, // |"^..D}.A|
0x86, 0x9b, 0x92, 0x0a, 0xd5, 0x7e, 0xf2, 0x78, // |.....~.x|
0x84, 0x50, 0x00, 0x2d, 0x0b, 0xab, 0x92, 0x7a, // |.P.-...z|
0x96, 0x15, 0xcf, 0x5a, 0x34, 0x45, 0x35, 0xa7, // |...Z4E5.|
0x18, 0x61, 0x2b, 0x88, 0x45, 0xaa, 0xd3, 0xe2, // |.a+.E...|
0x54, 0xf9, 0xc7, 0xbb, 0xe7, 0x00, 0x86, 0xbd, // |T.......|
0x8b, 0xbb, 0x6d, 0x3b, 0x0f, 0x8d, 0xfb, 0x4d, // |..m;...M|
0x5d, 0x8b, 0x50, 0x2e, 0x68, 0x74, 0x5d, 0x03, // |].P.ht].|
0x16, 0x2a, 0x49, 0x24, 0x54, 0x5b, 0xa9, 0x34, // |.*I$T[.4|
0x25, 0x17, 0x79, 0xe3, 0xc3, 0x3a, 0x2a, 0x12, // |%.y..:*.|
0x75, 0x64, 0x16, 0xa4, 0xb7, 0x7e, 0x39, 0x5a, // |ud...~9Z|
0x4e, 0x3e, 0x53, 0x2b, 0x49, 0x1b, 0x26, 0xdf, // |N>S+I.&.|
0xfc, 0x29, 0x99, 0xcb, 0xad, 0x29, 0x2c, 0x72, // |.)...),r|
0x3f, 0xa7, 0xcb, 0x45, 0x4c, 0x14, 0xee, 0x46, // |?..EL..F|
0x74, 0x64, 0xdb, 0x4b, 0x4b, 0xa4, 0x35, 0x3c, // |td.KK.5<|
0x91, 0xc4, 0x9b, 0xb0, 0x66, 0xc6, 0x70, 0xb6, // |....f.p.|
0xf2, 0x07, 0x3b, 0xbf, 0x74, 0x72, 0xb4, 0x24, // |..;.tr.$|
0x7e, 0x87, 0xd4, 0x0a, 0x37, 0xd9, 0x49, 0x04, // |~...7.I.|
0x09, 0x36, 0xd1, 0x63, 0x88, 0xe1, 0xe8, 0x08, // |.6.c....|
0xbf, 0x17, 0xc4, 0xcd, 0xcb, 0x3c, 0xef, 0x88, // |.....<..|
0x2c, 0xf6, 0xa3, 0x6d, 0x89, 0x39, 0xc9, 0xfe, // |,..m.9..|
0x97, 0x25, 0xb3, 0x9a, 0x02, 0x40, 0xd4, 0x90, // |.%...@..|
0x28, 0x6a, 0x79, 0xbd, 0x4b, 0x8e, 0x10, 0x18, // |(jy.K...|
0xc9, 0xaf, 0xe9, 0xc0, 0x6e, 0xd5, 0xb1, 0xcf, // |....n...|
0xe8, 0xa4, 0xdc, 0x94, 0x12, 0x82, 0xfb, 0x08, // |........|
0x42, 0xd4, 0x1a, 0x76, 0xa2, 0x4b, 0x3f, 0xc3, // |B..v.K?.|
0xb4, 0x0b, 0xa3, 0x0c, 0xec, 0x19, 0x7c, 0x5f, // |......|_|
0xd5, 0x98, 0x99, 0xf4, 0x1a, 0xca, 0x83, 0xaa, // |........|
0xbd, 0x26, 0x31, 0x95, 0x77, 0x90, 0x43, 0x7a, // |.&1.w.Cz|
0x75, 0x15, 0xcb, 0x68, 0xae, 0x24, 0xc5, 0x1b, // |u..h.$..|
];
const TOO_LITTLE_DATA_END: [u8; 312] = [
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // |........|
0x00, 0x01, 0x00, 0x06, 0x14, 0x49, 0xbc, 0x07, // |.....I..|
0xd1, 0x60, 0x00, 0x00, 0x45, 0x00, 0x01, 0x24, // |.`..E..$|
0x46, 0x04, 0x40, 0x00, 0x7f, 0x06, 0x11, 0x27, // |F.@....'|
0x4d, 0xaf, 0x56, 0xec, 0xc0, 0xa8, 0x3e, 0x65, // |M.V...>e|
0xbf, 0x7f, 0x01, 0xbb, 0xaf, 0x09, 0x43, 0xb4, // |......C.|
0xde, 0x93, 0xcc, 0x66, 0x50, 0x18, 0x02, 0x01, // |...fP...|
0xd3, 0x21, 0x00, 0x00, 0xba, 0x15, 0x37, 0x39, // |.!....79|
0xe7, 0xae, 0xb6, 0x48, 0x3e, 0xd8, 0x57, 0x67, // |...H>.Wg|
0x9c, 0xb6, 0x9c, 0xc0, 0x18, 0x0e, 0x74, 0x67, // |......tg|
0xae, 0x8e, 0xc6, 0x80, 0x7f, 0x81, 0x25, 0xc4, // |......%.|
0xe9, 0x04, 0xe8, 0xd9, 0x98, 0xb6, 0x99, 0x93, // |........|
0xa1, 0xa4, 0x5e, 0x57, 0x74, 0x89, 0x30, 0x38, // |..^Wt.08|
0xa9, 0xbb, 0x99, 0x4a, 0x7e, 0x42, 0x3c, 0xd2, // |...J~B<.|
0x59, 0xb6, 0x49, 0xb0, 0xc7, 0x11, 0x57, 0x03, // |Y.I...W.|
0x6d, 0x23, 0x1b, 0x72, 0xe7, 0x24, 0xdb, 0x75, // |m#.r.$.u|
0x78, 0xd1, 0x38, 0x01, 0x46, 0xb6, 0x8c, 0x1b, // |x.8.F...|
0x41, 0xb4, 0xbd, 0xc1, 0xa2, 0x00, 0x63, 0xa5, // |A.....c.|
0x97, 0x30, 0x5d, 0xbe, 0xd1, 0x37, 0x31, 0xf1, // |.0]..71.|
0xbb, 0xc6, 0xf8, 0x81, 0x35, 0x86, 0x32, 0xa6, // |....5.2.|
0xc3, 0x35, 0x54, 0x45, 0x50, 0xdf, 0x61, 0x46, // |.5TEP.aF|
0x5b, 0x83, 0x6b, 0xac, 0x5c, 0x2d, 0xa2, 0xc3, // |[.k.\-..|
0x2e, 0x71, 0x32, 0x18, 0x41, 0x29, 0x99, 0x66, // |.q2.A).f|
0x8c, 0x50, 0x28, 0x92, 0x45, 0xae, 0x96, 0x38, // |.P(.E..8|
0xa4, 0x83, 0x94, 0x4a, 0x2f, 0x0e, 0x62, 0x13, // |...J/.b.|
0x07, 0x13, 0xc2, 0x0b, 0x84, 0xfd, 0x27, 0xab, // |......'.|
0x6c, 0xb4, 0x69, 0x0d, 0xd2, 0xdb, 0xfb, 0x8e, // |l.i.....|
0xa7, 0x09, 0x65, 0x76, 0x7e, 0x09, 0xa4, 0x7a, // |..ev~..z|
0xe9, 0xfe, 0xec, 0x52, 0x89, 0x7d, 0x07, 0x6f, // |...R.}.o|
0xff, 0xa0, 0xde, 0x8a, 0x42, 0x2d, 0xc3, 0x75, // |....B-.u|
0x05, 0x6d, 0x60, 0x76, 0xce, 0xe1, 0x6c, 0xfd, // |.m`v..l.|
0xae, 0x1f, 0x5e, 0x02, 0x94, 0x39, 0x2a, 0x55, // |..^..9*U|
0x00, 0x1d, 0x00, 0x20, 0x8d, 0x89, 0x9a, 0x19, // |... ....|
0x1d, 0x53, 0x52, 0xd5, 0xc1, 0x3e, 0x3a, 0x1d, // |.SR..>:.|
0x12, 0x15, 0xae, 0x33, 0x2e, 0x54, 0xd1, 0x6f, // |...3.T.o|
0xd6, 0xb1, 0x73, 0xd9, 0x56, 0x98, 0x6f, 0x8f, // |..s.V.o.|
0x7e, 0xf5, 0xd9, 0x75, 0x00, 0x0b, 0x00, 0x02, // |~..u....|
0x01, 0x00, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, // |........|
0x00, 0x00, 0x00, 0x9a, 0x9a, 0x00, 0x01, 0x00, // |........|
];
}

View File

@@ -2,14 +2,17 @@ use log::debug;
use std::fmt::{Display, Formatter};
use std::io::Result;
use std::net::SocketAddr;
use time::{Duration, Instant, OffsetDateTime};
use std::sync::Arc;
use std::sync::RwLock;
use std::time::Instant;
use time::{Duration, OffsetDateTime};
#[derive(Debug, Clone, Default)]
pub(crate) struct UpstreamAddress {
address: String,
resolved_addresses: Vec<SocketAddr>,
resolved_time: Option<Instant>,
ttl: Option<Duration>,
resolved_addresses: Arc<RwLock<Vec<SocketAddr>>>,
resolved_time: Arc<RwLock<Option<Instant>>>,
ttl: Arc<RwLock<Option<Duration>>>,
}
impl Display for UpstreamAddress {
@@ -27,8 +30,10 @@ impl UpstreamAddress {
}
pub fn is_valid(&self) -> bool {
if let Some(resolved) = self.resolved_time {
if let Some(ttl) = self.ttl {
let r = { *self.resolved_time.read().unwrap() };
if let Some(resolved) = r {
if let Some(ttl) = { *self.ttl.read().unwrap() } {
return resolved.elapsed() < ttl;
}
}
@@ -37,7 +42,7 @@ impl UpstreamAddress {
}
fn is_resolved(&self) -> bool {
!self.resolved_addresses.is_empty()
!self.resolved_addresses.read().unwrap().is_empty()
}
fn time_remaining(&self) -> Duration {
@@ -45,17 +50,19 @@ impl UpstreamAddress {
return Duration::seconds(0);
}
self.ttl.unwrap() - self.resolved_time.unwrap().elapsed()
let rt = { *self.resolved_time.read().unwrap() };
let ttl = { *self.ttl.read().unwrap() };
ttl.unwrap() - rt.unwrap().elapsed()
}
pub async fn resolve(&mut self, mode: ResolutionMode) -> Result<Vec<SocketAddr>> {
pub async fn resolve(&self, mode: ResolutionMode) -> Result<Vec<SocketAddr>> {
if self.is_resolved() && self.is_valid() {
debug!(
"Already got address {:?}, still valid for {:.3}s",
&self.resolved_addresses,
self.time_remaining().as_seconds_f64()
);
return Ok(self.resolved_addresses.clone());
return Ok(self.resolved_addresses.read().unwrap().clone());
}
debug!(
@@ -70,8 +77,8 @@ impl UpstreamAddress {
Err(e) => {
debug!("Failed looking up {}: {}", &self.address, &e);
// Protect against DNS flooding. Cache the result for 1 second.
self.resolved_time = Some(Instant::now());
self.ttl = Some(Duration::seconds(3));
*self.resolved_time.write().unwrap() = Some(Instant::now());
*self.ttl.write().unwrap() = Some(Duration::seconds(3));
return Err(e);
}
};
@@ -103,11 +110,11 @@ impl UpstreamAddress {
.expect("Format")
);
self.resolved_addresses = addresses;
self.resolved_time = Some(Instant::now());
self.ttl = Some(Duration::minutes(1));
*self.resolved_addresses.write().unwrap() = addresses.clone();
*self.resolved_time.write().unwrap() = Some(Instant::now());
*self.ttl.write().unwrap() = Some(Duration::minutes(1));
Ok(self.resolved_addresses.clone())
Ok(addresses)
}
}

22
src/update.rs Normal file
View File

@@ -0,0 +1,22 @@
use self_update::cargo_crate_version;
pub(crate) fn update() {
println!("Updating to the latest version...");
let backend = self_update::backends::gitea::Update::configure()
.with_host("https://code.kiers.eu")
.repo_owner("jjkiers")
.repo_name("layer4-proxy")
.bin_name("l4p")
.show_download_progress(true)
.current_version(cargo_crate_version!())
.build()
.expect("Should initialize correctly.");
let status = backend.update_extended();
match status {
Err(e) => eprintln!("Error updating: {e}"),
Ok(_) => (),
}
}

View File

@@ -7,36 +7,25 @@ use serde::Deserialize;
use std::net::SocketAddr;
use tokio::io;
use tokio::net::TcpStream;
use tokio::sync::Mutex;
#[derive(Debug, Default)]
struct Addr(Mutex<UpstreamAddress>);
impl Clone for Addr {
fn clone(&self) -> Self {
tokio::task::block_in_place(|| Self(Mutex::new(self.0.blocking_lock().clone())))
}
}
#[derive(Debug, Clone, Deserialize, Default)]
pub struct ProxyToUpstream {
pub addr: String,
pub protocol: String,
#[serde(skip_deserializing)]
addresses: Addr,
addresses: UpstreamAddress,
}
impl ProxyToUpstream {
pub async fn resolve_addresses(&self) -> std::io::Result<Vec<SocketAddr>> {
let mut addr = self.addresses.0.lock().await;
addr.resolve((*self.protocol).into()).await
self.addresses.resolve((*self.protocol).into()).await
}
pub fn new(address: String, protocol: String) -> Self {
Self {
addr: address.clone(),
protocol,
addresses: Addr(Mutex::new(UpstreamAddress::new(address))),
addresses: UpstreamAddress::new(address),
}
}

View File

@@ -19,16 +19,6 @@ servers:
listen:
- "0.0.0.0:54956"
default: echo
kcp_server:
protocol: kcp
listen:
- "127.0.0.1:54958"
default: tester
kcp_echo_server:
protocol: kcp
listen:
- "127.0.0.1:54959"
default: echo
upstream:
web: "tcp://127.0.0.1:8080"