@@ -21,9 +21,9 @@ use std::os::fd::AsRawFd;
2121#[ cfg( target_os = "linux" ) ]
2222use socket2:: { Domain , Protocol , Socket , Type } ;
2323
24- #[ cfg( target_os = "linux" ) ]
25- use std:: net:: Ipv4Addr ;
2624use std:: net:: SocketAddr ;
25+ #[ cfg( target_os = "linux" ) ]
26+ use std:: net:: { Ipv4Addr , Ipv6Addr } ;
2727use std:: sync:: { Arc , OnceLock } ;
2828use std:: time:: Duration ;
2929use tokio:: net:: { TcpListener , TcpStream } ;
@@ -295,35 +295,44 @@ pub fn get_client() -> &'static Client<
295295}
296296
297297/// Try to bind to an available port in the given range (up to 16 attempts)
298- async fn bind_to_available_port ( start : u16 , end : u16 , bind_addr : [ u8 ; 4 ] ) -> Result < TcpListener > {
298+ async fn bind_to_available_port ( start : u16 , end : u16 , ip : std :: net :: IpAddr ) -> Result < TcpListener > {
299299 let mut rng = rand:: thread_rng ( ) ;
300300
301301 for _ in 0 ..16 {
302302 let port = rng. gen_range ( start..=end) ;
303- match bind_ipv4_listener ( bind_addr, port) . await {
303+ let addr = std:: net:: SocketAddr :: new ( ip, port) ;
304+ match bind_listener ( addr) . await {
304305 Ok ( listener) => {
305- debug ! ( "Successfully bound to port {}" , port) ;
306+ debug ! ( "Successfully bound to {}:{}" , ip , port) ;
306307 return Ok ( listener) ;
307308 }
308309 Err ( _) => continue ,
309310 }
310311 }
311312 anyhow:: bail!(
312- "No available port found after 16 attempts in range {}-{}" ,
313+ "No available port found after 16 attempts in range {}-{} on {} " ,
313314 start,
314- end
315+ end,
316+ ip
315317 )
316318}
317319
318- async fn bind_ipv4_listener ( bind_addr : [ u8 ; 4 ] , port : u16 ) -> Result < TcpListener > {
320+ async fn bind_listener ( addr : std :: net :: SocketAddr ) -> Result < TcpListener > {
319321 #[ cfg( target_os = "linux" ) ]
320322 {
321323 // Setup a raw socket to set IP_FREEBIND for specific non-loopback addresses
322- let ip = Ipv4Addr :: from ( bind_addr) ;
323- let is_specific_non_loopback =
324- ip != Ipv4Addr :: new ( 127 , 0 , 0 , 1 ) && ip != Ipv4Addr :: new ( 0 , 0 , 0 , 0 ) ;
324+ let is_specific_non_loopback = match addr. ip ( ) {
325+ std:: net:: IpAddr :: V4 ( ip) => {
326+ ip != Ipv4Addr :: new ( 127 , 0 , 0 , 1 ) && ip != Ipv4Addr :: new ( 0 , 0 , 0 , 0 )
327+ }
328+ std:: net:: IpAddr :: V6 ( ip) => ip != Ipv6Addr :: LOCALHOST && ip != Ipv6Addr :: UNSPECIFIED ,
329+ } ;
325330 if is_specific_non_loopback {
326- let sock = Socket :: new ( Domain :: IPV4 , Type :: STREAM , Some ( Protocol :: TCP ) ) ?;
331+ let domain = match addr {
332+ std:: net:: SocketAddr :: V4 ( _) => Domain :: IPV4 ,
333+ std:: net:: SocketAddr :: V6 ( _) => Domain :: IPV6 ,
334+ } ;
335+ let sock = Socket :: new ( domain, Type :: STREAM , Some ( Protocol :: TCP ) ) ?;
327336 // Enabling FREEBIND for non-local address binding before interface configuration
328337 unsafe {
329338 let yes: libc:: c_int = 1 ;
@@ -341,35 +350,31 @@ async fn bind_ipv4_listener(bind_addr: [u8; 4], port: u16) -> Result<TcpListener
341350 ) ;
342351 }
343352 }
344-
353+ sock . set_reuse_address ( true ) ? ;
345354 sock. set_nonblocking ( true ) ?;
346- let addr = SocketAddr :: from ( ( ip, port) ) ;
347355 sock. bind ( & addr. into ( ) ) ?;
348- sock. listen ( 1024 ) ?; // OS default backlog
356+ sock. listen ( 128 ) ?;
349357 let std_listener: std:: net:: TcpListener = sock. into ( ) ;
350358 std_listener. set_nonblocking ( true ) ?;
351359 return Ok ( TcpListener :: from_std ( std_listener) ?) ;
352360 }
353361 }
354- // Fallback: normal async bind if the conditions aren't met
355- let listener = TcpListener :: bind ( SocketAddr :: from ( ( bind_addr, port) ) ) . await ?;
356- Ok ( listener)
362+
363+ TcpListener :: bind ( addr) . await . map_err ( Into :: into)
357364}
358365
359366pub struct ProxyServer {
360- http_port : Option < u16 > ,
361- https_port : Option < u16 > ,
367+ http_bind : Option < std :: net :: SocketAddr > ,
368+ https_bind : Option < std :: net :: SocketAddr > ,
362369 rule_engine : Arc < RuleEngine > ,
363370 cert_manager : Arc < CertificateManager > ,
364- bind_address : [ u8 ; 4 ] ,
365371}
366372
367373impl ProxyServer {
368374 pub fn new (
369- http_port : Option < u16 > ,
370- https_port : Option < u16 > ,
375+ http_bind : Option < std :: net :: SocketAddr > ,
376+ https_bind : Option < std :: net :: SocketAddr > ,
371377 rule_engine : RuleEngine ,
372- bind_address : Option < [ u8 ; 4 ] > ,
373378 ) -> Self {
374379 let cert_manager = CertificateManager :: new ( ) . expect ( "Failed to create certificate manager" ) ;
375380
@@ -378,22 +383,21 @@ impl ProxyServer {
378383 init_client_with_ca ( ca_cert_der) ;
379384
380385 ProxyServer {
381- http_port ,
382- https_port ,
386+ http_bind ,
387+ https_bind ,
383388 rule_engine : Arc :: new ( rule_engine) ,
384389 cert_manager : Arc :: new ( cert_manager) ,
385- bind_address : bind_address. unwrap_or ( [ 127 , 0 , 0 , 1 ] ) ,
386390 }
387391 }
388392
389393 pub async fn start ( & mut self ) -> Result < ( u16 , u16 ) > {
390- let http_listener = if let Some ( port) = self . http_port {
391- bind_ipv4_listener ( self . bind_address , port) . await ?
394+ // Bind HTTP listener
395+ let http_listener = if let Some ( addr) = self . http_bind {
396+ bind_listener ( addr) . await ?
392397 } else {
393- // No port specified, find available port in 8000-8999 range
394- let listener = bind_to_available_port ( 8000 , 8999 , self . bind_address ) . await ?;
395- self . http_port = Some ( listener. local_addr ( ) ?. port ( ) ) ;
396- listener
398+ // No address specified, find available port in 8000-8999 range on localhost
399+ bind_to_available_port ( 8000 , 8999 , std:: net:: IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) )
400+ . await ?
397401 } ;
398402
399403 let http_port = http_listener. local_addr ( ) ?. port ( ) ;
@@ -429,14 +433,13 @@ impl ProxyServer {
429433
430434 // IPv6-specific listener not required; IPv4 listener suffices for jail routing
431435
432- // Start HTTPS proxy
433- let https_listener = if let Some ( port ) = self . https_port {
434- bind_ipv4_listener ( self . bind_address , port ) . await ?
436+ // Bind HTTPS listener
437+ let https_listener = if let Some ( addr ) = self . https_bind {
438+ bind_listener ( addr ) . await ?
435439 } else {
436- // No port specified, find available port in 8000-8999 range
437- let listener = bind_to_available_port ( 8000 , 8999 , self . bind_address ) . await ?;
438- self . https_port = Some ( listener. local_addr ( ) ?. port ( ) ) ;
439- listener
440+ // No address specified, find available port in 8000-8999 range on localhost
441+ bind_to_available_port ( 8000 , 8999 , std:: net:: IpAddr :: V4 ( Ipv4Addr :: LOCALHOST ) )
442+ . await ?
440443 } ;
441444
442445 let https_port = https_listener. local_addr ( ) ?. port ( ) ;
@@ -693,17 +696,19 @@ mod tests {
693696 let engine = V8JsRuleEngine :: new ( js. to_string ( ) ) . unwrap ( ) ;
694697 let rule_engine = RuleEngine :: from_trait ( Box :: new ( engine) , None ) ;
695698
696- let proxy = ProxyServer :: new ( Some ( 8080 ) , Some ( 8443 ) , rule_engine, None ) ;
699+ let http_bind = Some ( "127.0.0.1:8080" . parse ( ) . unwrap ( ) ) ;
700+ let https_bind = Some ( "127.0.0.1:8443" . parse ( ) . unwrap ( ) ) ;
701+ let proxy = ProxyServer :: new ( http_bind, https_bind, rule_engine) ;
697702
698- assert_eq ! ( proxy. http_port , Some ( 8080 ) ) ;
699- assert_eq ! ( proxy. https_port , Some ( 8443 ) ) ;
703+ assert_eq ! ( proxy. http_bind . map ( |s| s . port ( ) ) , Some ( 8080 ) ) ;
704+ assert_eq ! ( proxy. https_bind . map ( |s| s . port ( ) ) , Some ( 8443 ) ) ;
700705 }
701706
702707 #[ tokio:: test]
703708 async fn test_proxy_server_auto_port ( ) {
704709 let engine = V8JsRuleEngine :: new ( "true" . to_string ( ) ) . unwrap ( ) ;
705710 let rule_engine = RuleEngine :: from_trait ( Box :: new ( engine) , None ) ;
706- let mut proxy = ProxyServer :: new ( None , None , rule_engine, None ) ;
711+ let mut proxy = ProxyServer :: new ( None , None , rule_engine) ;
707712
708713 let ( http_port, https_port) = proxy. start ( ) . await . unwrap ( ) ;
709714
0 commit comments