Don't rewrite request remote. Add 'Request::real_ip()'.

Resolves #479.
This commit is contained in:
Alexander Mielczarek 2017-12-24 16:12:21 -06:00 committed by Sergio Benitez
parent 2f7961f410
commit c2899b2391
3 changed files with 29 additions and 102 deletions

View File

@ -1,5 +1,5 @@
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::net::SocketAddr; use std::net::{IpAddr, SocketAddr};
use std::fmt; use std::fmt;
use std::str; use std::str;
@ -192,6 +192,34 @@ impl<'r> Request<'r> {
self.remote = Some(address); self.remote = Some(address);
} }
/// Returns the ip address "X-Real-IP" header if found.
///
/// # Example
///
/// Get the value of the ip out of the "X-Real-IP" header.
///
/// ```rust
/// # use rocket::Request;
/// # use rocket::http::{Header, Method};
/// # use std::net::{SocketAddr, IpAddr, Ipv4Addr};
///
/// # Request::example(Method::Get, "/uri", |mut request| {
/// request.add_header(Header::new("X-Real-IP", "8.8.8.8"));
/// let ip = request.real_ip().expect("X-Real-IP not set");
/// assert_eq!(ip, "8.8.8.8".parse::<Ipv4Addr>().unwrap());
/// # });
/// ```
#[inline(always)]
pub fn real_ip(&self) -> Option<IpAddr> {
self.headers()
.get_one("X-Real-IP")
.and_then(|ip| {
ip.parse()
.map_err(|_| warn_!("'X-Real-IP' header is malformed: {}", ip))
.ok()
})
}
/// Returns a [`HeaderMap`](/rocket/http/struct.HeaderMap.html) of all of /// Returns a [`HeaderMap`](/rocket/http/struct.HeaderMap.html) of all of
/// the headers in `self`. /// the headers in `self`.
/// ///

View File

@ -1,7 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::str::from_utf8_unchecked; use std::str::from_utf8_unchecked;
use std::cmp::min; use std::cmp::min;
use std::net::SocketAddr;
use std::io::{self, Write}; use std::io::{self, Write};
use std::mem; use std::mem;
@ -173,26 +172,9 @@ impl Rocket {
/// Preprocess the request for Rocket things. Currently, this means: /// Preprocess the request for Rocket things. Currently, this means:
/// ///
/// * Rewriting the method in the request if _method form field exists. /// * Rewriting the method in the request if _method form field exists.
/// * Rewriting the remote IP if the 'X-Real-IP' header is set.
/// ///
/// Keep this in-sync with derive_form when preprocessing form fields. /// Keep this in-sync with derive_form when preprocessing form fields.
fn preprocess_request(&self, req: &mut Request, data: &Data) { fn preprocess_request(&self, req: &mut Request, data: &Data) {
// Rewrite the remote IP address. The request must already have an
// address associated with it to do this since we need to know the port.
if let Some(current) = req.remote() {
let ip = req.headers()
.get_one("X-Real-IP")
.and_then(|ip| {
ip.parse()
.map_err(|_| warn_!("'X-Real-IP' header is malformed: {}", ip))
.ok()
});
if let Some(ip) = ip {
req.set_remote(SocketAddr::new(ip, current.port()));
}
}
// Check if this is a form and if the form contains the special _method // Check if this is a form and if the form contains the special _method
// field which we use to reinterpret the request's method. // field which we use to reinterpret the request's method.
let data_len = data.peek().len(); let data_len = data.peek().len();

View File

@ -1,83 +0,0 @@
#![feature(plugin, decl_macro, custom_derive)]
#![plugin(rocket_codegen)]
extern crate rocket;
use std::net::SocketAddr;
#[get("/")]
fn get_ip(remote: SocketAddr) -> String {
remote.to_string()
}
mod remote_rewrite_tests {
use super::*;
use rocket::local::Client;
use rocket::http::{Header, Status};
use std::net::SocketAddr;
const KNOWN_IP: &'static str = "127.0.0.1:8000";
fn check_ip(header: Option<Header<'static>>, ip: Option<String>) {
let addr: SocketAddr = KNOWN_IP.parse().unwrap();
let c = Client::new(rocket::ignite().mount("/", routes![get_ip])).unwrap();
let mut response = match header {
Some(header) => c.get("/").header(header).remote(addr).dispatch(),
None => c.get("/").remote(addr).dispatch()
};
assert_eq!(response.status(), Status::Ok);
let body = response.body_string();
match ip {
Some(ip) => assert_eq!(body, Some(format!("{}:{}", ip, addr.port()))),
None => assert_eq!(body, Some(KNOWN_IP.into()))
}
}
#[test]
fn x_real_ip_rewrites() {
let ip = "8.8.8.8";
check_ip(Some(Header::new("X-Real-IP", ip)), Some(ip.to_string()));
let ip = "129.120.111.200";
check_ip(Some(Header::new("X-Real-IP", ip)), Some(ip.to_string()));
}
#[test]
fn x_real_ip_rewrites_ipv6() {
let ip = "2001:db8:0:1:1:1:1:1";
check_ip(Some(Header::new("X-Real-IP", ip)), Some(format!("[{}]", ip)));
let ip = "2001:db8::2:1";
check_ip(Some(Header::new("X-Real-IP", ip)), Some(format!("[{}]", ip)));
}
#[test]
fn uncased_header_rewrites() {
let ip = "8.8.8.8";
check_ip(Some(Header::new("x-REAL-ip", ip)), Some(ip.to_string()));
let ip = "1.2.3.4";
check_ip(Some(Header::new("x-real-ip", ip)), Some(ip.to_string()));
}
#[test]
fn no_header_no_rewrite() {
check_ip(Some(Header::new("real-ip", "?")), None);
check_ip(None, None);
}
#[test]
fn bad_header_doesnt_rewrite() {
let ip = "092348092348";
check_ip(Some(Header::new("X-Real-IP", ip)), None);
let ip = "1200:100000:0120129";
check_ip(Some(Header::new("X-Real-IP", ip)), None);
let ip = "192.168.1.900";
check_ip(Some(Header::new("X-Real-IP", ip)), None);
}
}