/// <summary> /// Attempts a lookup from cache. /// </summary> /// <param name="uri"></param> /// <param name="startQuery"></param> /// <param name="preferIPv6"></param> /// <returns></returns> private static SIPEndPoint SIPLookupFromCache( SIPURI uri, QueryType startQuery, bool preferIPv6) { SIPEndPoint result = null; QueryType queryType = startQuery; string host = uri.MAddrOrHostAddress; int port = SIPConstants.GetDefaultPort(uri.Protocol); if (ushort.TryParse(uri.HostPort, out var uriPort)) { port = uriPort; } bool isDone = false; while (!isDone) { switch (queryType) { case QueryType.SRV: var srvProtocol = SIPServices.GetSRVProtocolForSIPURI(uri); string serviceHost = DnsQueryExtensions.ConcatServiceName(uri.MAddrOrHostAddress, uri.Scheme.ToString(), srvProtocol.ToString()); var srvResult = _lookupClient.QueryCache(serviceHost, QueryType.SRV); (var srvHost, var srvPort) = GetHostAndPortFromSrvResult(srvResult); if (srvHost != null) { host = srvHost; port = srvPort != 0 ? srvPort : port; } queryType = preferIPv6 ? QueryType.AAAA : QueryType.A; break; case QueryType.AAAA: var aaaaResult = _lookupClient.QueryCache(host, UseANYLookups ? QueryType.ANY : QueryType.AAAA, QueryClass.IN); if (aaaaResult?.Answers?.Count > 0) { result = GetFromLookupResult(uri.Protocol, aaaaResult.Answers.OrderByDescending(x => x.RecordType).First(), port); isDone = true; } else { queryType = QueryType.A; } break; default: // A record lookup. var aResult = _lookupClient.QueryCache(host, QueryType.A, QueryClass.IN); if (aResult != null) { if (aResult.Answers?.Count > 0) { result = GetFromLookupResult(uri.Protocol, aResult.Answers.First(), port); } else { // We got a result back but it was empty indicating an unresolvable host or // some other DNS error condition. result = SIPEndPoint.Empty; } } isDone = true; break; } } return(result); }
/// <summary> /// Attempts a lookup from DNS. /// </summary> /// <param name="uri"></param> /// <param name="startQuery"></param> /// <param name="preferIPv6"></param> /// <param name="ct"></param> /// <returns></returns> private static async Task <SIPEndPoint> SIPLookupAsync( SIPURI uri, QueryType startQuery, bool preferIPv6, CancellationToken ct) { SIPEndPoint result = null; QueryType queryType = startQuery; string host = uri.MAddrOrHostAddress; int port = SIPConstants.GetDefaultPort(uri.Protocol); if (ushort.TryParse(uri.HostPort, out var uriPort)) { port = uriPort; } bool isDone = false; while (!isDone && !ct.IsCancellationRequested) { switch (queryType) { case QueryType.SRV: try { var srvProtocol = SIPServices.GetSRVProtocolForSIPURI(uri); //string serviceHost = DnsQueryExtensions.ConcatResolveServiceName(uri.MAddrOrHostAddress, uri.Scheme.ToString(), srvProtocol.ToString()); //var srvResult = await _lookupClient.QueryAsync(serviceHost, QueryType.SRV, QueryClass.IN, ct).ConfigureAwait(false); var srvResult = await _lookupClient.ResolveServiceAsync(uri.MAddrOrHostAddress, uri.Scheme.ToString(), srvProtocol.ToString()); (var srvHost, var srvPort) = GetHostAndPortFromSrvResult(srvResult); if (srvHost != null) { host = srvHost; port = srvPort != 0 ? srvPort : port; } } catch (Exception srvExcp) { logger.LogWarning($"SIPDNS exception on SRV lookup. {srvExcp.Message}."); } queryType = preferIPv6 ? QueryType.AAAA : QueryType.A; break; case QueryType.AAAA: try { var aaaaResult = await _lookupClient .QueryAsync(host, UseANYLookups?QueryType.ANY : QueryType.AAAA, QueryClass.IN, ct) .ConfigureAwait(false); if (aaaaResult?.Answers?.Count > 0) { result = GetFromLookupResult(uri.Protocol, aaaaResult.Answers.AddressRecords().OrderByDescending(x => x.RecordType).First(), port); isDone = true; } else { queryType = QueryType.A; } } catch (Exception srvExcp) { logger.LogWarning($"SIPDNS exception on AAAA lookup. {srvExcp.Message}."); queryType = QueryType.A; } break; default: // A record lookup. try { var aResult = await _lookupClient.QueryAsync(host, QueryType.A, QueryClass.IN, ct) .ConfigureAwait(false); if (aResult != null) { if (aResult.Answers?.Count > 0) { result = GetFromLookupResult(uri.Protocol, aResult.Answers.AddressRecords().First(), port); } else { // We got a result back but it was empty indicating an unresolvable host or // some other DNS error condition. result = SIPEndPoint.Empty; } } } catch (Exception srvExcp) { logger.LogWarning($"SIPDNS exception on A lookup. {srvExcp.Message}."); result = SIPEndPoint.Empty; } isDone = true; break; } } return(result); }
/// <summary> /// Resolve method that performs either an A or AAAA record lookup. If required /// a SRV record lookup will be performed prior to the A or AAAA lookup. /// </summary> /// <param name="uri">The SIP URI to lookup.</param> /// <param name="queryType">Whether the address lookup should be A or AAAA.</param> /// <returns>A SIPEndPoint or null.</returns> private static async Task <SIPEndPoint> Resolve(SIPURI uri, QueryType queryType) { if (uri == null || String.IsNullOrWhiteSpace(uri.MAddrOrHostAddress)) { throw new ArgumentNullException("uri", "SIP DNS resolve was supplied an empty input."); } if (!ushort.TryParse(uri.HostPort, out var uriPort)) { uriPort = SIPConstants.DEFAULT_SIP_PORT; } if (IPAddress.TryParse(uri.MAddrOrHostAddress, out var ipAddress)) { // Target is already an IP address, no DNS lookup required. return(new SIPEndPoint(uri.Protocol, ipAddress, uriPort)); } else { if (!uri.MAddrOrHostAddress.Contains(".") || uri.MAddrOrHostAddress.EndsWith(MDNS_TLD)) { AddressFamily family = (queryType == QueryType.AAAA) ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork; // The lookup is for a local network host. Use the OS DNS logic as the // main DNS client can be configured to use external DNS servers that won't // be able to lookup this hostname. IPHostEntry hostEntry = null; try { hostEntry = Dns.GetHostEntry(uri.MAddrOrHostAddress); } catch (SocketException) { // Socket exception gets thrown for failed lookups, } if (hostEntry != null) { var addressList = hostEntry.AddressList; if (addressList?.Length == 0) { logger.LogWarning($"Operating System DNS lookup failed for {uri.MAddrOrHostAddress}."); return(null); } else { if (addressList.Any(x => x.AddressFamily == family)) { var addressResult = addressList.First(x => x.AddressFamily == family); return(new SIPEndPoint(uri.Protocol, addressResult, uriPort)); } else { // Didn't get a result for the preferred address family so just use the // first available result. var addressResult = addressList.First(); return(new SIPEndPoint(uri.Protocol, addressResult, uriPort)); } } } else { return(null); } } else { if (uri.HostPort != null) { // Explicit port means no SRV lookup. return(HostQuery(uri.Protocol, uri.MAddrOrHostAddress, uriPort, queryType)); } else { // No explicit port so use a SRV -> (A | AAAA -> A | ANY) record lookup. var srvProtocol = SIPServices.GetSRVProtocolForSIPURI(uri); try { ServiceHostEntry srvResult = null; var srvResults = await _lookupClient.ResolveServiceAsync(uri.MAddrOrHostAddress, uri.Scheme.ToString(), srvProtocol.ToString()).ConfigureAwait(false); if (srvResults == null || srvResults.Count() == 0) { logger.LogWarning($"SIP DNS SRV lookup returned no results for {uri}."); } else { srvResult = srvResults.OrderBy(y => y.Priority).ThenByDescending(w => w.Weight).FirstOrDefault(); } string host = uri.MAddrOrHostAddress; // If no SRV results then fallback is to lookup the hostname directly. int port = SIPConstants.DEFAULT_SIP_PORT; // If no SRV results then fallback is to use the default port. if (srvResult != null) { host = srvResult.HostName; port = srvResult.Port; } return(HostQuery(uri.Protocol, host, port, queryType)); } catch (Exception e) { logger.LogWarning($"SIP DNS SRV lookup failure for {uri}. {e?.InnerException?.Message}"); } } } return(null); } }