This commit is contained in:
Connection Refused
2019-12-02 19:51:12 +08:00
commit b2ea730984
229 changed files with 86605 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
using System;
using System.Runtime.InteropServices;
namespace DnsClient
{
internal class DisposableIntPtr : IDisposable
{
public IntPtr Ptr => _ptr;
public bool IsValid { get; private set; } = true;
private IntPtr _ptr;
private DisposableIntPtr()
{
}
public static DisposableIntPtr Alloc(int size)
{
var ptr = new DisposableIntPtr();
try
{
ptr._ptr = Marshal.AllocHGlobal(size);
}
catch (OutOfMemoryException)
{
ptr.IsValid = false;
}
return ptr;
}
public void Dispose()
{
Marshal.FreeHGlobal(_ptr);
}
}
}

View File

@@ -0,0 +1,7 @@
internal static partial class Interop
{
internal static class Libraries
{
internal const string IpHlpApi = "iphlpapi.dll";
}
}

View File

@@ -0,0 +1,189 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
namespace System.IO
{
/// <summary>
/// Helper for reading config files where each row is a key-value data pair.
/// The input key-values must not have any whitespace within them.
/// Keys are only matched if they begin a line, with no preceding whitespace.
/// </summary>
internal struct RowConfigReader
{
private readonly string _buffer;
private readonly StringComparison _comparisonKind;
private int _currentIndex;
/// <summary>
/// Constructs a new RowConfigReader which reads from the given string.
/// <param name="buffer">The string to parse through.</param>
/// </summary>
public RowConfigReader(string buffer)
{
_buffer = buffer;
_comparisonKind = StringComparison.Ordinal;
_currentIndex = 0;
}
/// <summary>
/// Constructs a new RowConfigReader which reads from the given string.
/// <param name="buffer">The string to parse through.</param>
/// <param name="comparisonKind">The comparison kind to use.</param>
/// </summary>
public RowConfigReader(string buffer, StringComparison comparisonKind)
{
_buffer = buffer;
_comparisonKind = comparisonKind;
_currentIndex = 0;
}
/// <summary>
/// Gets the next occurrence of the given key, from the current position of the reader,
/// or throws if no occurrence of the key exists in the remainder of the string.
/// </summary>
public string GetNextValue(string key)
{
string value;
if (!TryGetNextValue(key, out value))
{
throw new InvalidOperationException("Couldn't get next value with key " + key);
}
else
{
return value;
}
}
/// <summary>
/// Tries to get the next occurrence of the given key from the current position of the reader.
/// If successful, returns true and stores the result in 'value'. Otherwise, returns false.
/// </summary>
public bool TryGetNextValue(string key, out string value)
{
Debug.Assert(_buffer != null);
if (_currentIndex >= _buffer.Length)
{
value = null;
return false;
}
// First, find the key, by repeatedly searching for occurrences.
// We only match an occurrence if it starts a line, by itself, with no preceding whitespace.
int keyIndex;
if (!TryFindNextKeyOccurrence(key, _currentIndex, out keyIndex))
{
value = null;
return false;
}
// Next, we will take the end of the line, and look backwards for the start of the value.
// NOTE: This assumes that the "value" does not have any whitespace in it, nor is there any
// after. This is the format of most "row-based" config files in /proc/net, etc.
int afterKey = keyIndex + key.Length;
int endOfValue;
int endOfLine = _buffer.IndexOf(Environment.NewLine, afterKey, _comparisonKind);
if (endOfLine == -1)
{
// There may not be a newline after this key, if we've reached the end of the file.
endOfLine = _buffer.Length - 1;
endOfValue = endOfLine;
}
else
{
endOfValue = endOfLine - 1;
}
int lineLength = endOfLine - keyIndex; // keyIndex is the start of the line.
int whitespaceBeforeValue = _buffer.LastIndexOf('\t', endOfLine, lineLength);
if (whitespaceBeforeValue == -1)
{
whitespaceBeforeValue = _buffer.LastIndexOf(' ', endOfLine, lineLength); // try space as well
}
int valueIndex = whitespaceBeforeValue + 1; // Get the first character after the whitespace.
int valueLength = endOfValue - whitespaceBeforeValue;
if (valueIndex <= keyIndex || valueIndex == -1 || valueLength == 0)
{
// No value found after the key.
value = null;
return false;
}
value = _buffer.Substring(valueIndex, valueLength); // Grab the whole value string.
_currentIndex = endOfLine + 1;
return true;
}
private bool TryFindNextKeyOccurrence(string key, int startIndex, out int keyIndex)
{
// Loop until end of file is reached, or a match is found.
while (true)
{
keyIndex = _buffer.IndexOf(key, startIndex, _comparisonKind);
if (keyIndex == -1)
{
// Reached end of string with no match.
return false;
}
// Check If the match is at the beginning of the string, or is preceded by a newline.
else if (keyIndex == 0
|| (keyIndex >= Environment.NewLine.Length && _buffer.Substring(keyIndex - Environment.NewLine.Length, Environment.NewLine.Length) == Environment.NewLine))
{
// Check if the match is followed by whitespace, meaning it is not part of a larger word.
if (HasFollowingWhitespace(keyIndex, key.Length))
{
return true;
}
}
startIndex = startIndex + key.Length;
}
}
private bool HasFollowingWhitespace(int keyIndex, int length)
{
return (keyIndex + length < _buffer.Length)
&& (_buffer[keyIndex + length] == ' ' || _buffer[keyIndex + length] == '\t');
}
/// <summary>
/// Gets the next occurrence of the key in the string, and parses it as an Int32.
/// Throws if the key is not found in the remainder of the string, or if the key
/// cannot be successfully parsed into an Int32.
/// </summary>
/// <remarks>
/// This is mainly provided as a helper because most Linux config/info files
/// store integral data.
/// </remarks>
public int GetNextValueAsInt32(string key)
{
// PERF: We don't need to allocate a new string here, we can parse an Int32 "in-place" in the existing string.
string value = GetNextValue(key);
int result;
if (int.TryParse(value, out result))
{
return result;
}
else
{
throw new InvalidOperationException("Unable to parse value " + value + " of key " + key + " as an Int32.");
}
}
/// <summary>
/// Reads the value of the first occurrence of the given key contained in the string given.
/// </summary>
/// <param name="data">The key-value row configuration string.</param>
/// <param name="key">The key to find.</param>
/// <returns>The value of the row containing the first occurrence of the key.</returns>
public static string ReadFirstValueFromString(string data, string key)
{
return new RowConfigReader(data).GetNextValue(key);
}
}
}

View File

@@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.IO;
using System.Net;
// TODO: Remove if fixed
// This code is from https://github.com/dotnet/corefx
// Will be removed whenever the bugs reading network information on linux are fixed and
// I can use the Managed version.
namespace DnsClient.Linux
{
internal static partial class StringParsingHelpers
{
internal static string ParseDnsSuffixFromResolvConfFile(string filePath)
{
string data = File.ReadAllText(filePath);
RowConfigReader rcr = new RowConfigReader(data);
string dnsSuffix;
return rcr.TryGetNextValue("search", out dnsSuffix) ? dnsSuffix : string.Empty;
}
internal static List<IPAddress> ParseDnsAddressesFromResolvConfFile(string filePath)
{
// Parse /etc/resolv.conf for all of the "nameserver" entries.
// These are the DNS servers the machine is configured to use.
// On OSX, this file is not directly used by most processes for DNS
// queries/routing, but it is automatically generated instead, with
// the machine's DNS servers listed in it.
string data = File.ReadAllText(filePath);
RowConfigReader rcr = new RowConfigReader(data);
List<IPAddress> addresses = new List<IPAddress>();
string addressString = null;
while (rcr.TryGetNextValue("nameserver", out addressString))
{
IPAddress parsedAddress;
if (IPAddress.TryParse(addressString, out parsedAddress))
{
addresses.Add(parsedAddress);
}
}
return addresses;
}
}
}

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Runtime.InteropServices;
#if !NET45
namespace DnsClient.Windows.IpHlpApi
{
internal class FixedNetworkInformation
{
private FixedNetworkInformation()
{
}
public ICollection<IPAddress> DnsAddresses { get; private set; }
public string DomainName { get; private set; }
public string HostName { get; private set; }
public static FixedNetworkInformation GetFixedInformation()
{
var info = new FixedNetworkInformation();
uint size = 0;
DisposableIntPtr buffer = null;
Interop.IpHlpApi.FIXED_INFO fixedInfo = new Interop.IpHlpApi.FIXED_INFO();
uint result = Interop.IpHlpApi.GetNetworkParams(IntPtr.Zero, ref size);
while (result == Interop.IpHlpApi.ERROR_BUFFER_OVERFLOW)
{
using (buffer = DisposableIntPtr.Alloc((int)size))
{
if (buffer.IsValid)
{
result = Interop.IpHlpApi.GetNetworkParams(buffer.Ptr, ref size);
if (result == Interop.IpHlpApi.ERROR_SUCCESS)
{
fixedInfo = Marshal.PtrToStructure<Interop.IpHlpApi.FIXED_INFO>(buffer.Ptr);
}
else
{
throw new Win32Exception((int)result);
}
var dnsAddresses = new List<IPAddress>();
Interop.IpHlpApi.IP_ADDR_STRING addr = fixedInfo.DnsServerList;
IPAddress ip;
if (IPAddress.TryParse(addr.IpAddress, out ip))
{
dnsAddresses.Add(ip);
while (addr.Next != IntPtr.Zero)
{
addr = Marshal.PtrToStructure<Interop.IpHlpApi.IP_ADDR_STRING>(addr.Next);
if (IPAddress.TryParse(addr.IpAddress, out ip))
{
dnsAddresses.Add(ip);
}
}
}
info.HostName = fixedInfo.hostName;
info.DomainName = fixedInfo.domainName;
info.DnsAddresses = dnsAddresses.ToArray();
return info;
}
else
{
throw new OutOfMemoryException();
}
}
}
return info;
}
}
}
#endif

View File

@@ -0,0 +1,70 @@
using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;
internal static partial class Interop
{
internal static partial class IpHlpApi
{
internal const uint ERROR_SUCCESS = 0;
internal const uint ERROR_INVALID_FUNCTION = 1;
internal const uint ERROR_NO_SUCH_DEVICE = 2;
internal const uint ERROR_INVALID_DATA = 13;
internal const uint ERROR_INVALID_PARAMETER = 87;
internal const uint ERROR_BUFFER_OVERFLOW = 111;
internal const uint ERROR_INSUFFICIENT_BUFFER = 122;
internal const uint ERROR_NO_DATA = 232;
internal const uint ERROR_IO_PENDING = 997;
internal const uint ERROR_NOT_FOUND = 1168;
[DllImport(Libraries.IpHlpApi)]
internal extern static uint GetAdaptersAddresses(
AddressFamily family,
uint flags,
IntPtr pReserved,
IntPtr adapterAddresses,
ref uint outBufLen);
[DllImport(Libraries.IpHlpApi, ExactSpelling = true)]
internal extern static uint GetNetworkParams(IntPtr pFixedInfo, ref uint pOutBufLen);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct FIXED_INFO
{
public const int MAX_HOSTNAME_LEN = 128;
public const int MAX_DOMAIN_NAME_LEN = 128;
public const int MAX_SCOPE_ID_LEN = 256;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_HOSTNAME_LEN + 4)]
public string hostName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_DOMAIN_NAME_LEN + 4)]
public string domainName;
public IntPtr currentDnsServer; // IpAddressList*
public IP_ADDR_STRING DnsServerList;
public uint nodeType;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_SCOPE_ID_LEN + 4)]
public string scopeId;
public bool enableRouting;
public bool enableProxy;
public bool enableDns;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct IP_ADDR_STRING
{
public IntPtr Next; // struct _IpAddressList*
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string IpAddress;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string IpMask;
public uint Context;
}
}
}