Skip to content

Commit 2279f13

Browse files
Make DNS lookup threadsafe, also still work if IPv4 resolution fails
1 parent f7ed37d commit 2279f13

File tree

1 file changed

+39
-36
lines changed

1 file changed

+39
-36
lines changed

fCraft/Network/HttpUtil.cs

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,53 @@
11
// Part of fCraft | Copyright 2009-2015 Matvei Stefarov <me@matvei.org> | BSD-3 | See LICENSE.txt //Copyright (c) 2011-2013 Jon Baker, Glenn Marien and Lao Tszy <Jonty800@gmail.com> //Copyright (c) <2012-2014> <LeChosenOne, DingusBungus> | ProCraft Copyright 2014-2016 Joseph Beauvais <123DMWM@gmail.com>
2-
32
using System;
43
using System.Collections.Generic;
54
using System.Linq;
65
using System.Net;
76
using System.Net.Sockets;
87
using JetBrains.Annotations;
98

10-
namespace fCraft
11-
{
9+
namespace fCraft {
1210
/// <summary> Static class for assisting with making web requests. </summary>
13-
public static class HttpUtil
14-
{
11+
public static class HttpUtil {
12+
1513
// Dns lookup, to make sure that IPv4 is preferred for connections
16-
static readonly Dictionary<string, IPAddress> TargetAddresses = new Dictionary<string, IPAddress>();
14+
static readonly Dictionary<string, IPAddress> cache = new Dictionary<string, IPAddress>();
1715
static DateTime nextDnsLookup = DateTime.MinValue;
1816
static readonly TimeSpan DnsRefreshInterval = TimeSpan.FromMinutes(30);
17+
static readonly object dnsLock = new object();
1918

20-
static IPAddress RefreshTargetAddress([NotNull] Uri requestUri)
21-
{
22-
if (requestUri == null) throw new ArgumentNullException("requestUri");
23-
24-
string hostName = requestUri.Host.ToLowerInvariant();
25-
IPAddress targetAddress;
26-
if (!TargetAddresses.TryGetValue(hostName, out targetAddress) || DateTime.UtcNow >= nextDnsLookup) {
27-
IPAddress[] allAddresses = null;
19+
static IPAddress LookupIPv4([NotNull] Uri uri) {
20+
if (uri == null) throw new ArgumentNullException("requestUri");
21+
string hostName = uri.Host.ToLowerInvariant();
22+
IPAddress ip;
23+
24+
lock (dnsLock) {
25+
if (cache.TryGetValue(hostName, out ip) && DateTime.UtcNow < nextDnsLookup) return ip;
26+
}
27+
28+
try {
29+
// Perform a DNS lookup on given host. Throws SocketException if no host found.
30+
IPAddress[] allIPs = null;
2831
try {
29-
// Perform a DNS lookup on given host. Throws SocketException if no host found.
30-
try {
31-
allAddresses = Dns.GetHostAddresses(requestUri.Host);
32-
} catch {
33-
return null;
34-
}
35-
// Find a suitable IPv4 address. Throws InvalidOperationException if none found.
36-
targetAddress = allAddresses.First(ip => ip.AddressFamily == AddressFamily.InterNetwork);
37-
} catch (SocketException ex) {
38-
Logger.Log(LogType.Error,
39-
"HttpUtil.RefreshTargetAddress: Error looking up server URL: {0}", ex);
40-
} catch (InvalidOperationException) {
41-
Logger.Log(LogType.Warning, "HttpUtil.RefreshTargetAddress: {0} does not have an IPv4 address!",
42-
requestUri.Host);
43-
} catch (UriFormatException) {
44-
Logger.Log(LogType.Warning, "Invalid URI: The hostname could not be parsed.");
45-
32+
allIPs = Dns.GetHostAddresses(uri.Host);
33+
} catch {
34+
return null;
4635
}
47-
TargetAddresses[hostName] = targetAddress;
48-
nextDnsLookup = DateTime.UtcNow + DnsRefreshInterval;
36+
37+
// Find a suitable IPv4 address. Throws InvalidOperationException if none found.
38+
ip = allIPs.First(address => address.AddressFamily == AddressFamily.InterNetwork);
39+
} catch (SocketException ex) {
40+
Logger.Log(LogType.Error, "HttpUtil.LookupIPv4: Error looking up server URL: {0}", ex);
41+
} catch (InvalidOperationException) {
42+
Logger.Log(LogType.Warning, "HttpUtil.LookupIPv4: {0} does not have an IPv4 address!", uri.Host);
43+
} catch (UriFormatException) {
44+
Logger.Log(LogType.Warning, "Invalid URI: The hostname could not be parsed.");
45+
4946
}
50-
return targetAddress;
47+
48+
lock (dnsLock) { cache[hostName] = ip; }
49+
nextDnsLookup = DateTime.UtcNow + DnsRefreshInterval;
50+
return ip;
5151
}
5252

5353

@@ -61,9 +61,12 @@ public static HttpWebRequest CreateRequest([NotNull] Uri uri, TimeSpan timeout)
6161
req.ServicePoint.BindIPEndPointDelegate = Server.BindIPEndPointCallback;
6262
req.Timeout = (int)timeout.TotalMilliseconds;
6363
req.UserAgent = Updater.UserAgent;
64-
64+
6565
if (uri.Scheme == "http") {
66-
req.Proxy = new WebProxy("http://" + RefreshTargetAddress(uri) + ":" + uri.Port);
66+
IPAddress ipv4 = LookupIPv4(uri);
67+
if (ipv4 != null) {
68+
req.Proxy = new WebProxy("http://" + LookupIPv4(uri) + ":" + uri.Port);
69+
}
6770
}
6871
return req;
6972
}

0 commit comments

Comments
 (0)