@@ -2,6 +2,7 @@ pub mod stats;
22
33use std:: fmt:: Debug ;
44use std:: future:: Future ;
5+ use std:: net:: { IpAddr , Ipv4Addr , Ipv6Addr } ;
56use std:: pin:: Pin ;
67use std:: sync:: Arc ;
78use std:: task:: { Context , Poll } ;
@@ -219,6 +220,13 @@ impl<C> Backend<C> {
219220 let original_host = original_host
220221 . or_else ( || request_host_header. clone ( ) )
221222 . unwrap_or_default ( ) ;
223+
224+ anyhow:: ensure!(
225+ is_public_host( & original_host) ,
226+ "private host not allowed: {}" ,
227+ original_host
228+ ) ;
229+
222230 // filter headers
223231 let mut headers = req
224232 . headers
@@ -488,6 +496,54 @@ impl hyper_util::client::legacy::connect::Connection for Connection {
488496 }
489497}
490498
499+ pub fn is_public_host ( host : & str ) -> bool {
500+ // Try to parse as IP address
501+ match host. parse :: < IpAddr > ( ) {
502+ Ok ( ip) => !is_private_ip ( & ip) ,
503+ Err ( _) => true , // Not an IP address, assume it's a hostname
504+ }
505+ }
506+
507+ fn is_private_ip ( ip : & IpAddr ) -> bool {
508+ match ip {
509+ IpAddr :: V4 ( ipv4) => is_private_ipv4 ( ipv4) ,
510+ IpAddr :: V6 ( ipv6) => is_private_ipv6 ( ipv6) ,
511+ }
512+ }
513+
514+ /// Check if an IPv4 address is private
515+ fn is_private_ipv4 ( ip : & Ipv4Addr ) -> bool {
516+ ip. octets ( ) [ 0 ] == 0 // "This network"
517+ || ip. is_private ( )
518+ || ip. is_loopback ( )
519+ || ip. is_link_local ( )
520+ || (
521+ ip. octets ( ) [ 0 ] == 192 && ip. octets ( ) [ 1 ] == 0 && ip. octets ( ) [ 2 ] == 0
522+ && ip. octets ( ) [ 3 ] != 9 && ip. octets ( ) [ 3 ] != 10
523+ )
524+ || ip. is_documentation ( )
525+ || ip. is_broadcast ( )
526+ }
527+
528+ /// Check if an IPv6 address is private
529+ fn is_private_ipv6 ( ip : & Ipv6Addr ) -> bool {
530+ ip. is_unspecified ( )
531+ || ip. is_loopback ( )
532+ || matches ! ( ip. segments( ) , [ 0 , 0 , 0 , 0 , 0 , 0xffff , _, _] )
533+ || matches ! ( ip. segments( ) , [ 0x64 , 0xff9b , 1 , _, _, _, _, _] )
534+ || matches ! ( ip. segments( ) , [ 0x100 , 0 , 0 , 0 , _, _, _, _] )
535+ || ( matches ! ( ip. segments( ) , [ 0x2001 , b, _, _, _, _, _, _] if b < 0x200 )
536+ && !( u128:: from_be_bytes ( ip. octets ( ) ) == 0x2001_0001_0000_0000_0000_0000_0000_0001
537+ || u128:: from_be_bytes ( ip. octets ( ) ) == 0x2001_0001_0000_0000_0000_0000_0000_0002
538+ || matches ! ( ip. segments( ) , [ 0x2001 , 3 , _, _, _, _, _, _] )
539+ || matches ! ( ip. segments( ) , [ 0x2001 , 4 , 0x112 , _, _, _, _, _] )
540+ || matches ! ( ip. segments( ) , [ 0x2001 , b, _, _, _, _, _, _] if ( 0x20 ..=0x3F ) . contains( & b) ) ) )
541+ || matches ! ( ip. segments( ) , [ 0x2002 , _, _, _, _, _, _, _] )
542+ || matches ! ( ip. segments( ) , [ 0x5f00 , ..] )
543+ || ip. is_unique_local ( )
544+ || ip. is_unicast_link_local ( )
545+ }
546+
491547#[ cfg( test) ]
492548mod tests {
493549 use super :: * ;
0 commit comments