@@ -57,6 +57,9 @@ pub async fn serve_on(
5757) -> Result < ( ) , Report < ProxyError > > {
5858 let is_loopback = is_loopback ( cfg. listen . ip ( ) ) ;
5959 log:: info!( "listening on {}" , cfg. listen) ;
60+ for ( host, ip) in & cfg. resolve {
61+ log:: info!( "--resolve pin: {host} -> {ip}" ) ;
62+ }
6063 loop {
6164 let ( client, peer) = match listener. accept ( ) . await {
6265 Ok ( pair) => pair,
@@ -161,7 +164,7 @@ async fn handle_connection(
161164 }
162165 // Stray absolute-form plain HTTP.
163166 if is_loopback {
164- blind_forward_http ( client, & head) . await
167+ blind_forward_http ( client, & head, & cfg . resolve ) . await
165168 } else {
166169 respond_status_line ( & mut client, StatusCode :: FORBIDDEN ) . await
167170 }
@@ -196,7 +199,26 @@ async fn handle_connect(
196199 }
197200
198201 // No match on loopback: connect upstream FIRST, then reply 200 (else 502).
199- blind_tunnel ( client, & host, port) . await
202+ blind_tunnel ( client, & host, port, & cfg. resolve ) . await
203+ }
204+
205+ /// Opens an upstream TCP connection, honoring a `--resolve` pin for `host`.
206+ ///
207+ /// When `host` (matched case-insensitively) has a pin, the socket dials that IP
208+ /// instead of resolving the name via DNS. The TLS SNI / `Host` set by the caller
209+ /// are unaffected, so the certificate still validates against the hostname.
210+ async fn connect_upstream (
211+ host : & str ,
212+ port : u16 ,
213+ resolve : & HashMap < String , IpAddr > ,
214+ ) -> std:: io:: Result < TcpStream > {
215+ if !resolve. is_empty ( )
216+ && let Some ( ip) = resolve. get ( & host. to_ascii_lowercase ( ) )
217+ {
218+ log:: debug!( "--resolve {host}:{port} -> {ip}:{port}" ) ;
219+ return TcpStream :: connect ( ( * ip, port) ) . await ;
220+ }
221+ TcpStream :: connect ( ( host, port) ) . await
200222}
201223
202224/// Connects to the upstream first; on success replies `200` then pipes bytes
@@ -205,8 +227,9 @@ async fn blind_tunnel(
205227 mut client : TcpStream ,
206228 host : & str ,
207229 port : u16 ,
230+ resolve : & HashMap < String , IpAddr > ,
208231) -> Result < ( ) , Report < ProxyError > > {
209- let mut upstream = match TcpStream :: connect ( ( host, port) ) . await {
232+ let mut upstream = match connect_upstream ( host, port, resolve ) . await {
210233 Ok ( stream) => stream,
211234 Err ( err) => {
212235 log:: warn!( "blind tunnel to {host}:{port} failed: {err}" ) ;
@@ -267,6 +290,7 @@ async fn serve_pac(client: &mut TcpStream, pac: &str) -> Result<(), Report<Proxy
267290async fn blind_forward_http (
268291 mut client : TcpStream ,
269292 head : & RequestHead ,
293+ resolve : & HashMap < String , IpAddr > ,
270294) -> Result < ( ) , Report < ProxyError > > {
271295 let Ok ( uri) = head. target . parse :: < Uri > ( ) else {
272296 return respond_status_line ( & mut client, StatusCode :: BAD_REQUEST ) . await ;
@@ -275,7 +299,7 @@ async fn blind_forward_http(
275299 return respond_status_line ( & mut client, StatusCode :: BAD_REQUEST ) . await ;
276300 } ;
277301 let port = uri. port_u16 ( ) . unwrap_or ( 80 ) ;
278- let mut upstream = match TcpStream :: connect ( ( host, port) ) . await {
302+ let mut upstream = match connect_upstream ( host, port, resolve ) . await {
279303 Ok ( stream) => stream,
280304 Err ( err) => {
281305 log:: warn!( "plain-HTTP forward to {host}:{port} failed: {err}" ) ;
@@ -432,14 +456,9 @@ async fn proxy_to_upstream(
432456
433457 // Dial the `--resolve` pin when the upstream host has one; the SNI/`Host`
434458 // (set above) stay the hostname, so the certificate still validates.
435- let tcp = match resolve. get ( upstream_host) {
436- Some ( ip) => {
437- log:: debug!( "--resolve {upstream_host} -> {ip}" ) ;
438- TcpStream :: connect ( ( * ip, upstream_port) ) . await
439- }
440- None => TcpStream :: connect ( ( upstream_host, upstream_port) ) . await ,
441- }
442- . change_context ( ProxyError :: Server ) ?;
459+ let tcp = connect_upstream ( upstream_host, upstream_port, resolve)
460+ . await
461+ . change_context ( ProxyError :: Server ) ?;
443462
444463 let response = if outcome. scheme_is_tls {
445464 let connector = TlsConnector :: from ( client_config ( insecure) ) ;
0 commit comments