diff --git a/forwardproxy.go b/forwardproxy.go index e41a557..8cf121b 100644 --- a/forwardproxy.go +++ b/forwardproxy.go @@ -533,15 +533,32 @@ match: // This is net.Dial's default behavior: if the host resolves to multiple IP addresses, // Dial will try each IP address in order until one succeeds + + var lastDialErr error + var hadAllowedIP bool + for _, ip := range IPs { if !h.hostIsAllowed(host, ip) { continue } + hadAllowedIP = true + conn, err = h.dialContext(ctx, network, net.JoinHostPort(ip.String(), port)) if err == nil { return conn, nil } + + lastDialErr = err + } + + // If at least one IP address was permitted by ACL but dialing all of them failed, + // return Bad Gateway. Otherwise the host did not have any allowed IPs and is forbidden. + if hadAllowedIP { + return nil, caddyhttp.Error( + http.StatusBadGateway, + fmt.Errorf("failed to connect to %s after trying allowed IP addresses: %v", host, lastDialErr), + ) } return nil, caddyhttp.Error(http.StatusForbidden, fmt.Errorf("no allowed IP addresses for %s", host)) diff --git a/forwardproxy_test.go b/forwardproxy_test.go index beb1158..3b46df3 100644 --- a/forwardproxy_test.go +++ b/forwardproxy_test.go @@ -216,6 +216,19 @@ func TestGETNoAuth(t *testing.T) { } } +func TestGETAllowedIPDialFailureReturnsBadGateway(t *testing.T) { + const useTLS = true + for _, httpProxyVer := range testHTTPProxyVersions { + response, err := getViaProxy("198.51.100.1:12345", "/", caddyForwardProxy.addr, httpProxyVer, credentialsEmpty, useTLS) + if err != nil { + t.Fatal(err) + } + if response.StatusCode != http.StatusBadGateway { + t.Fatalf("Expected response 502 StatusBadGateway for allowed host with dial failure, got %d", response.StatusCode) + } + } +} + func TestGETAuthCorrect(t *testing.T) { const useTLS = true for _, httpProxyVer := range testHTTPProxyVersions {