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

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
}