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"
|
||||
|
||||
[dependencies]
|
||||
openssl = { version = "0.10.66", optional = true }
|
||||
rustls = { version = "0.23.14", optional = true }
|
||||
openssl = { version = "0.10.68", optional = true }
|
||||
rustls = { version = "0.23.16", optional = true }
|
||||
rustls-pemfile = { version = "2.2.0", optional = true }
|
||||
serde_yml = "0.0.12"
|
||||
log = "0.4.22"
|
||||
colog = "1.3.0"
|
||||
threadpool = "1.8.1"
|
||||
wildcard_ex = "0.1.2"
|
||||
|
||||
[features]
|
||||
default = ["use-openssl"]
|
||||
|
21
README.md
21
README.md
@ -16,16 +16,17 @@ TODO:
|
||||
|
||||
Default `conf.yml`:
|
||||
```yml
|
||||
http_host: localhost:80 # Http server host
|
||||
https_host: localhost:443 # Https server host
|
||||
http_host: localhost:80 # Http server host
|
||||
https_host: localhost:443 # Https server host
|
||||
|
||||
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)
|
||||
incoming_ip_forwarding: none # Read IP forwarding on incoming connections (optional, default - none)
|
||||
|
||||
sites:
|
||||
- domain: localhost # Site domain
|
||||
- domain: localhost # Site domain (use wildcard matching)
|
||||
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)
|
||||
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)
|
||||
@ -34,10 +35,14 @@ sites:
|
||||
|
||||
### IP forwaring types
|
||||
|
||||
- Simple:\
|
||||
Appends `ip:port\n` to the request
|
||||
- Header:\
|
||||
Adds header `X-Real-IP: ip:port` to the request
|
||||
- None (`none`):\
|
||||
Do nothing
|
||||
- Modern (`modern`):\
|
||||
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
|
||||
|
||||
|
7
conf.yml
7
conf.yml
@ -3,12 +3,13 @@ https_host: localhost:443 # Https server host
|
||||
|
||||
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)
|
||||
incoming_ip_forwarding: none # Read IP forwarding on incoming connections (optional, default - none)
|
||||
|
||||
sites:
|
||||
- domain: localhost # Site domain
|
||||
- domain: localhost # Site domain (use wildcard matching)
|
||||
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)
|
||||
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_key: "/path/to/private/key.txt" # Ssl private key file (optional)
|
||||
# ssl_key: "/path/to/private/key.txt" # Ssl private key file (optional)
|
@ -1,6 +1,7 @@
|
||||
use std::{fs, net::TcpStream, sync::Arc, time::Duration};
|
||||
|
||||
use serde_yml::{Number, Value};
|
||||
use wildcard_ex::is_match_simple;
|
||||
|
||||
use super::SslCert;
|
||||
|
||||
@ -20,18 +21,26 @@ impl SiteConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone)]
|
||||
pub enum IpForwarding {
|
||||
Simple,
|
||||
Header
|
||||
Header(String),
|
||||
Modern,
|
||||
None
|
||||
}
|
||||
|
||||
impl IpForwarding {
|
||||
pub fn from_name(name: &str) -> Option<IpForwarding> {
|
||||
match name {
|
||||
"none" => Some(IpForwarding::None),
|
||||
"simple" => Some(IpForwarding::Simple),
|
||||
"header" => Some(IpForwarding::Header),
|
||||
_ => None
|
||||
"modern" => Some(IpForwarding::Modern),
|
||||
"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 https_host: String,
|
||||
pub threadpool_size: usize,
|
||||
pub connection_timeout: Duration
|
||||
pub connection_timeout: Duration,
|
||||
pub incoming_ip_forwarding: IpForwarding
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@ -57,6 +67,10 @@ impl Config {
|
||||
.unwrap_or(&Value::Number(Number::from(10))).as_u64()? as usize;
|
||||
let connection_timeout = Duration::from_secs(doc.get("connection_timeout")
|
||||
.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();
|
||||
|
||||
@ -88,7 +102,7 @@ impl Config {
|
||||
ip_forwarding: s.get("ip_forwarding")
|
||||
.map(|o| o.as_str()).flatten()
|
||||
.map(|o| IpForwarding::from_name(o)).flatten()
|
||||
.unwrap_or(IpForwarding::Header),
|
||||
.unwrap_or(IpForwarding::Header("X-Real-IP".to_string())),
|
||||
};
|
||||
|
||||
sites.push(site);
|
||||
@ -101,13 +115,14 @@ impl Config {
|
||||
http_host,
|
||||
https_host,
|
||||
threadpool_size,
|
||||
connection_timeout
|
||||
})
|
||||
connection_timeout,
|
||||
incoming_ip_forwarding
|
||||
}.clone())
|
||||
}
|
||||
|
||||
pub fn get_site(&self, domain: &str) -> Option<&SiteConfig> {
|
||||
for i in self.sites.as_ref() {
|
||||
if i.domain == domain {
|
||||
if is_match_simple(&i.domain, domain) {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,5 @@
|
||||
use std::{
|
||||
io::{Read, Write},
|
||||
net::{SocketAddr, TcpListener, TcpStream},
|
||||
sync::Arc,
|
||||
thread,
|
||||
time::Duration
|
||||
io::{Read, Write}, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, TcpListener, TcpStream}, str::FromStr, sync::Arc, thread, time::Duration
|
||||
};
|
||||
|
||||
use log::info;
|
||||
@ -216,6 +212,48 @@ impl FlowgateServer {
|
||||
https: bool,
|
||||
connected: 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();
|
||||
|
||||
{
|
||||
@ -254,6 +292,12 @@ impl FlowgateServer {
|
||||
.filter(|l| l.contains(": "))
|
||||
.map(|l| l.split_once(": ").unwrap())
|
||||
.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 host = String::new();
|
||||
@ -281,21 +325,22 @@ impl FlowgateServer {
|
||||
.map(|o| o.1.parse().ok())
|
||||
.flatten()
|
||||
.unwrap_or(0usize);
|
||||
|
||||
|
||||
let mut reqbuf: Vec<u8> = Vec::new();
|
||||
|
||||
match connected.1.ip_forwarding {
|
||||
IpForwarding::Header => {
|
||||
match &connected.1.ip_forwarding {
|
||||
IpForwarding::Header(header) => {
|
||||
reqbuf.append(&mut status.to_string().as_bytes().to_vec());
|
||||
reqbuf.append(&mut b"\r\n".to_vec());
|
||||
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 b": ".to_vec());
|
||||
reqbuf.append(&mut value.to_string().as_bytes().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 b"\r\n\r\n".to_vec());
|
||||
},
|
||||
@ -305,6 +350,20 @@ impl FlowgateServer {
|
||||
reqbuf.append(&mut head.clone());
|
||||
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()?;
|
||||
|
Loading…
Reference in New Issue
Block a user