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-
32using System ;
43using System . Collections . Generic ;
54using System . Linq ;
65using System . Net ;
76using System . Net . Sockets ;
87using 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