ip-reflect/src/main.rs

58 lines
1.8 KiB
Rust

#![warn(clippy::pedantic, clippy::nursery)]
use axum::extract::ConnectInfo;
use axum::handler::get;
use axum::http::{HeaderMap, HeaderValue};
use axum::{Router, Server};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(root));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
Server::bind(&addr)
.serve(app.into_make_service_with_connect_info::<SocketAddr, _>())
.await
.unwrap();
}
#[allow(clippy::unused_async)]
async fn root(header: HeaderMap, ConnectInfo(socket_info): ConnectInfo<SocketAddr>) -> String {
if let Some(Ok(value)) = header.get("Forwarded").map(HeaderValue::to_str) {
let match_str = "or="; // the `for` key is case-insensitive
let maybe_index = value.find(match_str);
if let Some(index) = maybe_index {
let ip_start = value.split_at(index + match_str.len()).1;
let mut chars = ip_start.chars().peekable();
if let Some('"') = chars.peek() {
// We're dealing with an ipv6 address
// skip first character, since we know it's an `"`
chars.next();
let res: String = chars.take_while(|c| *c != '"').collect();
if res.parse::<Ipv6Addr>().is_ok() {
return res;
}
} else {
let res: String = chars.take_while(|c| *c != 'c').collect();
if res.parse::<Ipv4Addr>().is_ok() {
return res;
}
}
}
}
if let Some(Ok(value)) = header.get("X-Forwarded-For").map(HeaderValue::to_str) {
return match value.split_once(',').map(|v| v.0) {
Some(v) => v,
None => value,
}
.to_string();
}
socket_info.ip().to_string()
}