7

what exactly I want :

public static CustomDnsResolver : Dns 
{
   .....
}

public static void Main(string[] args) 
{
    var httpClient = new HttpClient();
    httpClient.Dns(new CustomDnsResolver());
}

basically I just want to use my custom DNS resolver in HttpClient instead of System default, Is there any way to achieve it?

Dylech30th
  • 157
  • 2
  • 10

3 Answers3

5

The use case you have is exactly why Microsoft build the HttpClient stack. It allow you to put your business logic in layered class with the help of HttpMessageHandler class. You can find some sample in ms docs or visualstudiomagazine

void Main()
{
    var dnsHandler = new DnsHandler(new CustomDnsResolver());
    var client = new HttpClient(dnsHandler);
    var html = client.GetStringAsync("http://google.com").Result;
}

public class DnsHandler : HttpClientHandler
{
    private readonly CustomDnsResolver _dnsResolver;

    public DnsHandler(CustomDnsResolver dnsResolver)
    {
        _dnsResolver = dnsResolver;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var host = request.RequestUri.Host;
        var ip = _dnsResolver.Resolve(host);

        var builder = new UriBuilder(request.RequestUri);
        builder.Host = ip;

        request.RequestUri = builder.Uri;

        return base.SendAsync(request, cancellationToken);
    }
}

public class CustomDnsResolver
{
    public string Resolve(string host)
    {
        return "127.0.0.1";
    }
}
Kalten
  • 4,092
  • 23
  • 32
  • 4
    This solution doesn't work if you try to fetch data from https site. – Mohammad Nikravan Aug 21 '20 at 02:08
  • You can override certificate validation to workaroud this. – Kalten Aug 21 '20 at 10:53
  • you can override the ServerCertificateCustomValidationCallback of HttpClientHandler to custom certificate validation – Dylech30th Aug 31 '20 at 18:45
  • 2
    This doesn't answer the question. You cannot just say, "override certificate validation", what is expected is that we set the hostname correctly so SSL works but avoid the DNS lookup. – Luke Briner Mar 07 '22 at 14:34
  • This answer is true for a set of constrains. I don't propose you the universal answer. If it can help some people as is. I'm still happy. If someone else can propose a better answer, I will thanks him for that. – Kalten Mar 08 '22 at 18:27
  • Indeed, we can walkaround the certificate validation via other ways. But actually we've met another question, when server side accepts ONLY request from hostname instead of IP, this solution fails. :-( – Leo Li Oct 11 '22 at 03:34
3

Not sure if this works for all cases, but it worked brilliantly for my case, even when using HTTPS. So what you do is you replace the host in the URL with the actual IP-address that your custom resolver has resolved, and then you simply add a "host" header with the host name. Like this:

        var requestUri = new Uri("https://123.123.123.123/some/path");
        using var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
        request.Headers.TryAddWithoutValidation("host", "www.host-name.com");
        using var response = await httpClient.SendAsync(request);

I hope this helps as this is far by the first time I've run into this issue and I've never been able to solve it before now.

Hans Olav
  • 349
  • 3
  • 5
  • This will work for https only if you also override certificate errors as mentioned in the comment by Dylech30th on the other answer. – Ross Presser May 01 '22 at 15:17
  • Hm... I actually use this is production today and it works like a charm. I don't override certificate errors in any way. I use the SocketsHttpHandler and run in .NET 6. How can that be? – Hans Olav May 02 '22 at 21:16
  • After some experimentation, your code **does work** as long as there is a valid certificate for www.host-name.com available for the web server at 123.123.123.123 to use for TLS negotiation. If there is no such certificate at the address, you'll get an error. Example here https://pastebin.com/vDjGBfHC – Ross Presser May 03 '22 at 20:41
  • 2
    I've done some digging in on the behavioral change of the SocketsHttpHandler, as you can see in https://github.com/dotnet/runtime/issues/45144, the actual reason is that the server requires SNI validation, before .NET 5, the host header will not be used as the SNI automatically, but this behavior changed in .NET 5 where it now forces host header as the SNI if it is set. – Dylech30th May 28 '22 at 18:15
1

Consider using SocketsHttpHandler.ConnectCallback.

huang
  • 919
  • 11
  • 22