1use nu_protocol::{Record, Span, Value};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::net::IpAddr;
5
6pub fn resolve_trusted_ip(
10 headers: &http::header::HeaderMap,
11 remote_ip: Option<IpAddr>,
12 trusted_proxies: &[ipnet::IpNet],
13) -> Option<IpAddr> {
14 if trusted_proxies.is_empty() {
16 return remote_ip;
17 }
18
19 let remote_is_trusted = remote_ip
22 .map(|ip| trusted_proxies.iter().any(|net| net.contains(&ip)))
23 .unwrap_or(true);
24
25 if !remote_is_trusted {
26 return remote_ip;
27 }
28
29 let xff = match headers.get("x-forwarded-for") {
31 Some(v) => v.to_str().ok()?,
32 None => return remote_ip,
33 };
34
35 let ips: Vec<&str> = xff.split(',').map(|s| s.trim()).collect();
37
38 let mut leftmost_ip = None;
39 for ip_str in ips.into_iter().rev() {
40 if let Ok(ip) = ip_str.parse::<IpAddr>() {
41 leftmost_ip = Some(ip);
42 if !trusted_proxies.iter().any(|net| net.contains(&ip)) {
44 return Some(ip);
45 }
46 }
47 }
48
49 leftmost_ip.or(remote_ip)
51}
52
53#[derive(Clone, Debug, Serialize, Deserialize)]
54pub struct Request {
55 pub proto: String,
56 #[serde(with = "http_serde::method")]
57 pub method: http::method::Method,
58 #[serde(skip_serializing_if = "Option::is_none")]
59 pub authority: Option<String>,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub remote_ip: Option<std::net::IpAddr>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub remote_port: Option<u16>,
64 #[serde(skip_serializing_if = "Option::is_none")]
66 pub trusted_ip: Option<std::net::IpAddr>,
67 #[serde(with = "http_serde::header_map")]
68 pub headers: http::header::HeaderMap,
69 #[serde(with = "http_serde::uri")]
70 pub uri: http::Uri,
71 pub path: String,
72 pub query: HashMap<String, String>,
73}
74
75pub fn request_to_value(request: &Request, span: Span) -> Value {
76 let mut record = Record::new();
77
78 record.push("proto", Value::string(request.proto.clone(), span));
79 record.push("method", Value::string(request.method.to_string(), span));
80 record.push("uri", Value::string(request.uri.to_string(), span));
81 record.push("path", Value::string(request.path.clone(), span));
82
83 if let Some(authority) = &request.authority {
84 record.push("authority", Value::string(authority.clone(), span));
85 }
86
87 if let Some(remote_ip) = &request.remote_ip {
88 record.push("remote_ip", Value::string(remote_ip.to_string(), span));
89 }
90
91 if let Some(remote_port) = &request.remote_port {
92 record.push("remote_port", Value::int(*remote_port as i64, span));
93 }
94
95 if let Some(trusted_ip) = &request.trusted_ip {
96 record.push("trusted_ip", Value::string(trusted_ip.to_string(), span));
97 }
98
99 let mut headers_record = Record::new();
101 for (key, value) in request.headers.iter() {
102 headers_record.push(
103 key.to_string(),
104 Value::string(value.to_str().unwrap_or_default().to_string(), span),
105 );
106 }
107 record.push("headers", Value::record(headers_record, span));
108
109 let mut query_record = Record::new();
111 for (key, value) in &request.query {
112 query_record.push(key.clone(), Value::string(value.clone(), span));
113 }
114 record.push("query", Value::record(query_record, span));
115
116 Value::record(record, span)
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 fn headers_with_xff(xff: &str) -> http::header::HeaderMap {
124 let mut headers = http::header::HeaderMap::new();
125 headers.insert("x-forwarded-for", xff.parse().unwrap());
126 headers
127 }
128
129 fn parse_cidr(s: &str) -> ipnet::IpNet {
130 s.parse().unwrap()
131 }
132
133 #[test]
134 fn test_no_trusted_proxies_returns_remote_ip() {
135 let headers = http::header::HeaderMap::new();
136 let remote: IpAddr = "1.2.3.4".parse().unwrap();
137 let result = resolve_trusted_ip(&headers, Some(remote), &[]);
138 assert_eq!(result, Some(remote));
139 }
140
141 #[test]
142 fn test_remote_not_trusted_returns_remote_ip() {
143 let headers = headers_with_xff("5.6.7.8");
144 let remote: IpAddr = "1.2.3.4".parse().unwrap();
145 let trusted = vec![parse_cidr("10.0.0.0/8")];
146 let result = resolve_trusted_ip(&headers, Some(remote), &trusted);
147 assert_eq!(result, Some(remote));
148 }
149
150 #[test]
151 fn test_xff_extracts_client_ip() {
152 let headers = headers_with_xff("5.6.7.8, 10.0.0.1");
155 let remote: IpAddr = "10.0.0.2".parse().unwrap();
156 let trusted = vec![parse_cidr("10.0.0.0/8")];
157 let result = resolve_trusted_ip(&headers, Some(remote), &trusted);
158 assert_eq!(result, Some("5.6.7.8".parse().unwrap()));
159 }
160
161 #[test]
162 fn test_xff_stops_at_first_untrusted() {
163 let headers = headers_with_xff("1.1.1.1, 5.6.7.8, 10.0.0.1");
166 let remote: IpAddr = "10.0.0.2".parse().unwrap();
167 let trusted = vec![parse_cidr("10.0.0.0/8")];
168 let result = resolve_trusted_ip(&headers, Some(remote), &trusted);
169 assert_eq!(result, Some("5.6.7.8".parse().unwrap()));
171 }
172
173 #[test]
174 fn test_all_xff_trusted_returns_leftmost() {
175 let headers = headers_with_xff("10.0.0.5, 10.0.0.1");
177 let remote: IpAddr = "10.0.0.2".parse().unwrap();
178 let trusted = vec![parse_cidr("10.0.0.0/8")];
179 let result = resolve_trusted_ip(&headers, Some(remote), &trusted);
180 assert_eq!(result, Some("10.0.0.5".parse().unwrap()));
181 }
182
183 #[test]
184 fn test_no_xff_header_returns_remote() {
185 let headers = http::header::HeaderMap::new();
186 let remote: IpAddr = "10.0.0.2".parse().unwrap();
187 let trusted = vec![parse_cidr("10.0.0.0/8")];
188 let result = resolve_trusted_ip(&headers, Some(remote), &trusted);
189 assert_eq!(result, Some(remote));
190 }
191
192 #[test]
193 fn test_multiple_trusted_cidrs() {
194 let headers = headers_with_xff("5.6.7.8, 192.168.1.1");
195 let remote: IpAddr = "10.0.0.2".parse().unwrap();
196 let trusted = vec![parse_cidr("10.0.0.0/8"), parse_cidr("192.168.0.0/16")];
197 let result = resolve_trusted_ip(&headers, Some(remote), &trusted);
198 assert_eq!(result, Some("5.6.7.8".parse().unwrap()));
199 }
200
201 #[test]
202 fn test_ipv6_support() {
203 let headers = headers_with_xff("2001:db8::1, ::ffff:10.0.0.1");
204 let remote: IpAddr = "::ffff:10.0.0.2".parse().unwrap();
205 let trusted = vec![parse_cidr("::ffff:10.0.0.0/104")];
206 let result = resolve_trusted_ip(&headers, Some(remote), &trusted);
207 assert_eq!(result, Some("2001:db8::1".parse().unwrap()));
208 }
209
210 #[test]
211 fn test_unix_socket_with_xff() {
212 let headers = headers_with_xff("5.6.7.8, 10.0.0.1");
214 let trusted = vec![parse_cidr("10.0.0.0/8")];
215 let result = resolve_trusted_ip(&headers, None, &trusted);
216 assert_eq!(result, Some("5.6.7.8".parse().unwrap()));
217 }
218
219 #[test]
220 fn test_unix_socket_no_xff() {
221 let headers = http::header::HeaderMap::new();
223 let trusted = vec![parse_cidr("10.0.0.0/8")];
224 let result = resolve_trusted_ip(&headers, None, &trusted);
225 assert_eq!(result, None);
226 }
227
228 #[test]
229 fn test_trust_all_uses_leftmost_xff() {
230 let headers = headers_with_xff("38.147.250.103");
233 let trusted = vec![parse_cidr("0.0.0.0/0")];
234 let result = resolve_trusted_ip(&headers, None, &trusted);
235 assert_eq!(result, Some("38.147.250.103".parse().unwrap()));
236 }
237}