/// <summary> /// Attempts to parse a subnet from CIDR notation in the form of <i>ip-address</i>/<i>prefix</i>, /// where <i>prefix</i> is the network prefix length in bits. /// </summary> /// <param name="input">The input string.</param> /// <param name="cidr">The parsed <see cref="NetworkCidr"/>.</param> /// <returns><c>true</c> if the operation was successful.</returns> public static bool TryParse(string input, out NetworkCidr cidr) { cidr = null; if (string.IsNullOrEmpty(input)) { return(false); } var slashPos = input.IndexOf('/'); if (slashPos <= 0) { return(false); } if (!NetHelper.TryParseIPv4Address(input.Substring(0, slashPos), out var address)) { return(false); } if (!int.TryParse(input.Substring(slashPos + 1), out var prefixLength) || prefixLength < 0 || prefixLength > 32) { return(false); } cidr = new NetworkCidr(); cidr.Initialize(address, prefixLength); return(true); }
/// <summary> /// Parses a subnet from CIDR notation in the form of <i>ip-address</i>/<i>prefix</i>, /// where <i>prefix</i> is the network prefix length in bits. /// </summary> /// <param name="input">The input string.</param> /// <returns>The parsed <see cref="NetworkCidr"/>.</returns> /// <exception cref="ArgumentException">Thrown if the input is not correctly formatted.</exception> public static NetworkCidr Parse(string input) { Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(input), nameof(input)); var slashPos = input.IndexOf('/'); if (slashPos <= 0) { throw new ArgumentException($"Invalid CIDR [{input}].", nameof(input)); } if (!NetHelper.TryParseIPv4Address(input.Substring(0, slashPos), out var address)) { throw new ArgumentException($"Invalid CIDR [{input}].", nameof(input)); } if (!int.TryParse(input.Substring(slashPos + 1), out var prefixLength) || prefixLength < 0 || prefixLength > 32) { throw new ArgumentException($"Invalid CIDR [{input}].", nameof(input)); } var cidr = new NetworkCidr(); cidr.Initialize(address, prefixLength); return(cidr); }
/// <summary> /// Attempts to resolve an IP address or fully qualified domain name /// into host IP addresses. /// </summary> /// <param name="addressOrFQDN">The IP address or FQDN.</param> /// <param name="noCache"> /// Optionally specify that the method is not to answer from the cache, /// even if the cache is enabled. /// </param> /// <returns>An empty result set if the lookup failed.</returns> public async Task <IEnumerable <IPAddress> > LookupAsync(string addressOrFQDN, bool noCache = false) { await SyncContext.Clear; // We can short-circuit things if the parameter is an IP address. if (NetHelper.TryParseIPv4Address(addressOrFQDN, out var address)) { return(new IPAddress[] { address }); } // Try to answer from the cache, if enabled. if (cache != null && !noCache) { lock (cache) { if (cache.TryGetValue(addressOrFQDN, out var answer)) { if (answer.TTD > SysTime.Now) { // The answer hasn't expired yet, so we'll return it. return(answer.Addresses); } } } } // We're going to submit requests to all DNS clients in parallel and // return replies from the first one that answers. var readyEvent = new AsyncAutoResetEvent(); var sync = new object(); var pending = clients.Length; var results = (IList <IPAddress>)null; foreach (var client in clients) { var task = Task.Run( async() => { try { var addresses = await client.Lookup(addressOrFQDN); if (addresses.Count > 0) { lock (sync) { if (results == null) { results = addresses; } } } Interlocked.Decrement(ref pending); readyEvent.Set(); } catch (ResponseException) { // $todo(jefflill): // // I wish the underlying [DnsClient] didn't throw exceptions. Perhaps // I could extend the implementation to implement [TryResolve()]. Interlocked.Decrement(ref pending); readyEvent.Set(); } }); } while (pending > 0) { await readyEvent.WaitAsync(); if (results != null) { return(results); } } return(null); }