You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
118 lines
3.5 KiB
118 lines
3.5 KiB
use httparse;
|
|
use log::{error, info, trace};
|
|
use std::io::prelude::*;
|
|
use std::net::{TcpListener, TcpStream};
|
|
|
|
use myip::ThreadPool;
|
|
|
|
fn main() {
|
|
let log_env = env_logger::Env::new().filter_or("MY_LOG_LEVEL", "trace");
|
|
match std::env::var("RUST_LOG_STYLE") {
|
|
Ok(s) if s == "SYSTEMD" => env_logger::builder()
|
|
.format(|buf, record| {
|
|
writeln!(
|
|
buf,
|
|
"<{}>{}: {}",
|
|
match record.level() {
|
|
log::Level::Error => 3,
|
|
log::Level::Warn => 4,
|
|
log::Level::Info => 6,
|
|
log::Level::Debug => 7,
|
|
log::Level::Trace => 7,
|
|
},
|
|
record.target(),
|
|
record.args()
|
|
)
|
|
})
|
|
.init(),
|
|
_ => env_logger::init_from_env(log_env),
|
|
};
|
|
|
|
info!("Replying with most probable peer's ip on port 7878");
|
|
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
|
|
|
|
let pool = ThreadPool::new(4);
|
|
|
|
for stream in listener.incoming() {
|
|
let stream = stream.unwrap();
|
|
|
|
pool.execute(|| {
|
|
handle_connection(stream);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Reply to TCP streem with peer's IP
|
|
// if anyhting goes wrong, just slap 404 on it
|
|
fn handle_connection(mut stream: TcpStream) {
|
|
// make it 8k?
|
|
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
|
|
let mut buffer = [0; 1024];
|
|
stream.read(&mut buffer).unwrap();
|
|
|
|
let get = b"GET / HTTP";
|
|
let err404 = (
|
|
"HTTP/1.1 404 NOT FOUND",
|
|
"<html>\n<head><title>404 Not Found</title></head>\n\
|
|
<body bgcolor=\"white\">\n<center><h1>404 Not Found</h1></center>\n\
|
|
<hr>\n</body>\n</html>"
|
|
.to_string(),
|
|
);
|
|
|
|
trace!("Request: {}", String::from_utf8_lossy(&buffer[..]));
|
|
|
|
let (status_line, contents) = if buffer.starts_with(get) {
|
|
let mut headers = [httparse::EMPTY_HEADER; 30];
|
|
let mut req = httparse::Request::new(&mut headers);
|
|
|
|
match req.parse(&buffer) {
|
|
Ok(_) => {
|
|
let peer_ip =
|
|
pick_ip(req).unwrap_or_else(|| stream.peer_addr().unwrap().ip().to_string());
|
|
("HTTP/1.1 200 OK", peer_ip)
|
|
}
|
|
Err(e) => {
|
|
error!("{:?}", e);
|
|
err404
|
|
}
|
|
}
|
|
} else {
|
|
err404
|
|
};
|
|
|
|
// add date header formatted to rfc2822?
|
|
let response = format!(
|
|
"{}\r\n\
|
|
Access-control-allow-origin: *\r\n\
|
|
Content-type: text/plain; charset=utf-8\r\n\
|
|
Content-Lengh: {}\r\n\r\n\
|
|
{}",
|
|
status_line,
|
|
contents.len(),
|
|
contents
|
|
);
|
|
|
|
stream.write(response.as_bytes()).unwrap();
|
|
stream.flush().unwrap();
|
|
}
|
|
|
|
/// Pick most likely peer ip from request headers
|
|
fn pick_ip(req: httparse::Request) -> Option<String> {
|
|
// https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-
|
|
// CF-Connecting-IP
|
|
// X-Forwarded-For
|
|
let mut ip = None;
|
|
|
|
for header in req.headers {
|
|
let name = header.name.to_lowercase();
|
|
|
|
if name == "CF-Connecting-IP".to_lowercase() {
|
|
ip = Some(String::from_utf8_lossy(header.value).to_string());
|
|
break;
|
|
} else if name == "X-Forwarded-For".to_lowercase() {
|
|
ip = Some(String::from_utf8_lossy(header.value).to_string());
|
|
}
|
|
}
|
|
ip
|
|
}
|