incoming ip forwarding and wildcard domain matching
This commit is contained in:
parent
e59dbd28fd
commit
eab6d83a5f
@ -4,13 +4,14 @@ version = "0.1.2"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
openssl = { version = "0.10.66", optional = true }
|
openssl = { version = "0.10.68", optional = true }
|
||||||
rustls = { version = "0.23.14", optional = true }
|
rustls = { version = "0.23.16", optional = true }
|
||||||
rustls-pemfile = { version = "2.2.0", optional = true }
|
rustls-pemfile = { version = "2.2.0", optional = true }
|
||||||
serde_yml = "0.0.12"
|
serde_yml = "0.0.12"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
colog = "1.3.0"
|
colog = "1.3.0"
|
||||||
threadpool = "1.8.1"
|
threadpool = "1.8.1"
|
||||||
|
wildcard_ex = "0.1.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["use-openssl"]
|
default = ["use-openssl"]
|
||||||
|
17
README.md
17
README.md
@ -21,11 +21,12 @@ https_host: localhost:443 # Https server host
|
|||||||
|
|
||||||
threadpool_size: 10 # Threadpool size (count of threads that accept requests) (optional, default - 10)
|
threadpool_size: 10 # Threadpool size (count of threads that accept requests) (optional, default - 10)
|
||||||
connection_timeout: 10 # Read and write timeout of connections in seconds (optional, default - 10)
|
connection_timeout: 10 # Read and write timeout of connections in seconds (optional, default - 10)
|
||||||
|
incoming_ip_forwarding: none # Read IP forwarding on incoming connections (optional, default - none)
|
||||||
|
|
||||||
sites:
|
sites:
|
||||||
- domain: localhost # Site domain
|
- domain: localhost # Site domain (use wildcard matching)
|
||||||
host: localhost:8080 # Http server host
|
host: localhost:8080 # Http server host
|
||||||
ip_forwarding: simple # IP forwarding type (header/simple) (optional, default - header)
|
ip_forwarding: simple # IP forwarding method type (optional, default - header)
|
||||||
enable_keep_alive: true # Enable keep-alive connections (optional, default - true)
|
enable_keep_alive: true # Enable keep-alive connections (optional, default - true)
|
||||||
support_keep_alive: true # Does server supports keep-alive connections (optional, default - true)
|
support_keep_alive: true # Does server supports keep-alive connections (optional, default - true)
|
||||||
# ssl_cert: "/path/to/public/certificate.txt" # Ssl public certificate file (optional)
|
# ssl_cert: "/path/to/public/certificate.txt" # Ssl public certificate file (optional)
|
||||||
@ -34,10 +35,14 @@ sites:
|
|||||||
|
|
||||||
### IP forwaring types
|
### IP forwaring types
|
||||||
|
|
||||||
- Simple:\
|
- None (`none`):\
|
||||||
Appends `ip:port\n` to the request
|
Do nothing
|
||||||
- Header:\
|
- Modern (`modern`):\
|
||||||
Adds header `X-Real-IP: ip:port` to the request
|
Appends encoded to bytes ip to the beginning of the request
|
||||||
|
- Simple (`simple`):\
|
||||||
|
Appends `ip:port\n` to the beginning of the request
|
||||||
|
- Header (`header[:HEADER_NAME]`):\
|
||||||
|
Adds header `HEADER_NAME: ip:port` to the request
|
||||||
|
|
||||||
## How to run
|
## How to run
|
||||||
|
|
||||||
|
5
conf.yml
5
conf.yml
@ -3,11 +3,12 @@ https_host: localhost:443 # Https server host
|
|||||||
|
|
||||||
threadpool_size: 10 # Threadpool size (count of threads that accept requests) (optional, default - 10)
|
threadpool_size: 10 # Threadpool size (count of threads that accept requests) (optional, default - 10)
|
||||||
connection_timeout: 10 # Read and write timeout of connections in seconds (optional, default - 10)
|
connection_timeout: 10 # Read and write timeout of connections in seconds (optional, default - 10)
|
||||||
|
incoming_ip_forwarding: none # Read IP forwarding on incoming connections (optional, default - none)
|
||||||
|
|
||||||
sites:
|
sites:
|
||||||
- domain: localhost # Site domain
|
- domain: localhost # Site domain (use wildcard matching)
|
||||||
host: localhost:8080 # Http server host
|
host: localhost:8080 # Http server host
|
||||||
ip_forwarding: simple # IP forwarding type (header/simple) (optional, default - header)
|
ip_forwarding: simple # IP forwarding method type (optional, default - header)
|
||||||
enable_keep_alive: true # Enable keep-alive connections (optional, default - true)
|
enable_keep_alive: true # Enable keep-alive connections (optional, default - true)
|
||||||
support_keep_alive: true # Does server supports keep-alive connections (optional, default - true)
|
support_keep_alive: true # Does server supports keep-alive connections (optional, default - true)
|
||||||
# ssl_cert: "/path/to/public/certificate.txt" # Ssl public certificate file (optional)
|
# ssl_cert: "/path/to/public/certificate.txt" # Ssl public certificate file (optional)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::{fs, net::TcpStream, sync::Arc, time::Duration};
|
use std::{fs, net::TcpStream, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use serde_yml::{Number, Value};
|
use serde_yml::{Number, Value};
|
||||||
|
use wildcard_ex::is_match_simple;
|
||||||
|
|
||||||
use super::SslCert;
|
use super::SslCert;
|
||||||
|
|
||||||
@ -20,18 +21,26 @@ impl SiteConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone)]
|
||||||
pub enum IpForwarding {
|
pub enum IpForwarding {
|
||||||
Simple,
|
Simple,
|
||||||
Header
|
Header(String),
|
||||||
|
Modern,
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IpForwarding {
|
impl IpForwarding {
|
||||||
pub fn from_name(name: &str) -> Option<IpForwarding> {
|
pub fn from_name(name: &str) -> Option<IpForwarding> {
|
||||||
match name {
|
match name {
|
||||||
|
"none" => Some(IpForwarding::None),
|
||||||
"simple" => Some(IpForwarding::Simple),
|
"simple" => Some(IpForwarding::Simple),
|
||||||
"header" => Some(IpForwarding::Header),
|
"modern" => Some(IpForwarding::Modern),
|
||||||
_ => None
|
"header" => Some(IpForwarding::Header(String::from("X-Real-IP"))),
|
||||||
|
name => if name.starts_with("header:") {
|
||||||
|
Some(IpForwarding::Header(name[7..].to_string()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,7 +51,8 @@ pub struct Config {
|
|||||||
pub http_host: String,
|
pub http_host: String,
|
||||||
pub https_host: String,
|
pub https_host: String,
|
||||||
pub threadpool_size: usize,
|
pub threadpool_size: usize,
|
||||||
pub connection_timeout: Duration
|
pub connection_timeout: Duration,
|
||||||
|
pub incoming_ip_forwarding: IpForwarding
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@ -57,6 +67,10 @@ impl Config {
|
|||||||
.unwrap_or(&Value::Number(Number::from(10))).as_u64()? as usize;
|
.unwrap_or(&Value::Number(Number::from(10))).as_u64()? as usize;
|
||||||
let connection_timeout = Duration::from_secs(doc.get("connection_timeout")
|
let connection_timeout = Duration::from_secs(doc.get("connection_timeout")
|
||||||
.unwrap_or(&Value::Number(Number::from(10))).as_u64()?);
|
.unwrap_or(&Value::Number(Number::from(10))).as_u64()?);
|
||||||
|
let incoming_ip_forwarding = doc.get("incoming_ip_forwarding")
|
||||||
|
.map(|o| o.as_str()).flatten()
|
||||||
|
.map(|o| IpForwarding::from_name(o)).flatten()
|
||||||
|
.unwrap_or(IpForwarding::None);
|
||||||
|
|
||||||
let mut sites: Vec<SiteConfig> = Vec::new();
|
let mut sites: Vec<SiteConfig> = Vec::new();
|
||||||
|
|
||||||
@ -88,7 +102,7 @@ impl Config {
|
|||||||
ip_forwarding: s.get("ip_forwarding")
|
ip_forwarding: s.get("ip_forwarding")
|
||||||
.map(|o| o.as_str()).flatten()
|
.map(|o| o.as_str()).flatten()
|
||||||
.map(|o| IpForwarding::from_name(o)).flatten()
|
.map(|o| IpForwarding::from_name(o)).flatten()
|
||||||
.unwrap_or(IpForwarding::Header),
|
.unwrap_or(IpForwarding::Header("X-Real-IP".to_string())),
|
||||||
};
|
};
|
||||||
|
|
||||||
sites.push(site);
|
sites.push(site);
|
||||||
@ -101,13 +115,14 @@ impl Config {
|
|||||||
http_host,
|
http_host,
|
||||||
https_host,
|
https_host,
|
||||||
threadpool_size,
|
threadpool_size,
|
||||||
connection_timeout
|
connection_timeout,
|
||||||
})
|
incoming_ip_forwarding
|
||||||
|
}.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_site(&self, domain: &str) -> Option<&SiteConfig> {
|
pub fn get_site(&self, domain: &str) -> Option<&SiteConfig> {
|
||||||
for i in self.sites.as_ref() {
|
for i in self.sites.as_ref() {
|
||||||
if i.domain == domain {
|
if is_match_simple(&i.domain, domain) {
|
||||||
return Some(i);
|
return Some(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::{Read, Write},
|
io::{Read, Write}, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, TcpListener, TcpStream}, str::FromStr, sync::Arc, thread, time::Duration
|
||||||
net::{SocketAddr, TcpListener, TcpStream},
|
|
||||||
sync::Arc,
|
|
||||||
thread,
|
|
||||||
time::Duration
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
@ -216,6 +212,48 @@ impl FlowgateServer {
|
|||||||
https: bool,
|
https: bool,
|
||||||
connected: Option<(TcpStream, SiteConfig, bool, String)>
|
connected: Option<(TcpStream, SiteConfig, bool, String)>
|
||||||
) -> Option<(TcpStream, SiteConfig, bool, String)> {
|
) -> Option<(TcpStream, SiteConfig, bool, String)> {
|
||||||
|
let mut addr = addr;
|
||||||
|
|
||||||
|
match &config.incoming_ip_forwarding {
|
||||||
|
IpForwarding::Simple => {
|
||||||
|
let mut header = Vec::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut buf = [0; 1];
|
||||||
|
|
||||||
|
while let Ok(1) = stream.read(&mut buf) {
|
||||||
|
let byte = buf[0];
|
||||||
|
if byte == b'\n' { break }
|
||||||
|
header.push(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = SocketAddr::from_str(&String::from_utf8(header).ok()?).ok()?;
|
||||||
|
},
|
||||||
|
IpForwarding::Modern => {
|
||||||
|
let mut ipver = [0; 1];
|
||||||
|
stream.read(&mut ipver).ok()?;
|
||||||
|
addr = match ipver[0] {
|
||||||
|
0x01 => {
|
||||||
|
let mut octets = [0; 4];
|
||||||
|
stream.read(&mut octets).ok()?;
|
||||||
|
let mut port = [0; 2];
|
||||||
|
stream.read(&mut port).ok()?;
|
||||||
|
let port = u16::from_be_bytes(port);
|
||||||
|
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(octets), port))
|
||||||
|
}, 0x02 => {
|
||||||
|
let mut octets = [0; 16];
|
||||||
|
stream.read(&mut octets).ok()?;
|
||||||
|
let mut port = [0; 2];
|
||||||
|
stream.read(&mut port).ok()?;
|
||||||
|
let port = u16::from_be_bytes(port);
|
||||||
|
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from(octets), port, 0, 0))
|
||||||
|
}, _ => { return None },
|
||||||
|
};
|
||||||
|
},
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
|
||||||
let mut head = Vec::new();
|
let mut head = Vec::new();
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -255,6 +293,12 @@ impl FlowgateServer {
|
|||||||
.map(|l| l.split_once(": ").unwrap())
|
.map(|l| l.split_once(": ").unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
if let IpForwarding::Header(header) = &config.incoming_ip_forwarding {
|
||||||
|
if let Some(ip) = headers.iter().find(|o| o.0 == header).map(|o| o.1) {
|
||||||
|
addr = SocketAddr::from_str(ip).ok()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut connected: (TcpStream, SiteConfig, bool, String) = if connected.is_none() {
|
let mut connected: (TcpStream, SiteConfig, bool, String) = if connected.is_none() {
|
||||||
let mut host = String::new();
|
let mut host = String::new();
|
||||||
let mut keep_alive = false;
|
let mut keep_alive = false;
|
||||||
@ -284,18 +328,19 @@ impl FlowgateServer {
|
|||||||
|
|
||||||
let mut reqbuf: Vec<u8> = Vec::new();
|
let mut reqbuf: Vec<u8> = Vec::new();
|
||||||
|
|
||||||
match connected.1.ip_forwarding {
|
match &connected.1.ip_forwarding {
|
||||||
IpForwarding::Header => {
|
IpForwarding::Header(header) => {
|
||||||
reqbuf.append(&mut status.to_string().as_bytes().to_vec());
|
reqbuf.append(&mut status.to_string().as_bytes().to_vec());
|
||||||
reqbuf.append(&mut b"\r\n".to_vec());
|
reqbuf.append(&mut b"\r\n".to_vec());
|
||||||
for (key, value) in &headers {
|
for (key, value) in &headers {
|
||||||
if *key == "X-Real-IP" { continue }
|
if *key == header { continue }
|
||||||
reqbuf.append(&mut key.to_string().as_bytes().to_vec());
|
reqbuf.append(&mut key.to_string().as_bytes().to_vec());
|
||||||
reqbuf.append(&mut b": ".to_vec());
|
reqbuf.append(&mut b": ".to_vec());
|
||||||
reqbuf.append(&mut value.to_string().as_bytes().to_vec());
|
reqbuf.append(&mut value.to_string().as_bytes().to_vec());
|
||||||
reqbuf.append(&mut b"\r\n".to_vec());
|
reqbuf.append(&mut b"\r\n".to_vec());
|
||||||
}
|
}
|
||||||
reqbuf.append(&mut b"X-Real-IP: ".to_vec());
|
reqbuf.append(&mut header.as_bytes().to_vec());
|
||||||
|
reqbuf.append(&mut b": ".to_vec());
|
||||||
reqbuf.append(&mut addr.to_string().as_bytes().to_vec());
|
reqbuf.append(&mut addr.to_string().as_bytes().to_vec());
|
||||||
reqbuf.append(&mut b"\r\n\r\n".to_vec());
|
reqbuf.append(&mut b"\r\n\r\n".to_vec());
|
||||||
},
|
},
|
||||||
@ -305,6 +350,20 @@ impl FlowgateServer {
|
|||||||
reqbuf.append(&mut head.clone());
|
reqbuf.append(&mut head.clone());
|
||||||
reqbuf.append(&mut b"\r\n\r\n".to_vec());
|
reqbuf.append(&mut b"\r\n\r\n".to_vec());
|
||||||
},
|
},
|
||||||
|
IpForwarding::Modern => {
|
||||||
|
reqbuf.push(if addr.is_ipv4() { 0x01 } else { 0x02 });
|
||||||
|
match addr.ip() {
|
||||||
|
IpAddr::V4(ip) => {
|
||||||
|
reqbuf.append(&mut ip.octets().to_vec());
|
||||||
|
}, IpAddr::V6(ip) => {
|
||||||
|
reqbuf.append(&mut ip.octets().to_vec());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reqbuf.append(&mut addr.port().to_be_bytes().to_vec());
|
||||||
|
reqbuf.append(&mut head.clone());
|
||||||
|
reqbuf.append(&mut b"\r\n\r\n".to_vec());
|
||||||
|
},
|
||||||
|
IpForwarding::None => { }
|
||||||
}
|
}
|
||||||
|
|
||||||
connected.0.write_all(&reqbuf).ok()?;
|
connected.0.write_all(&reqbuf).ok()?;
|
||||||
|
Loading…
Reference in New Issue
Block a user