This commit is contained in:
Tim Hess 2019-03-07 09:53:05 -06:00
Родитель ffeeccded1 f0f8e4bd76
Коммит bbd6b12877
20 изменённых файлов: 1228 добавлений и 0 удалений

Просмотреть файл

@ -37,6 +37,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Common.Test", "tes
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Common.Net", "src\Steeltoe.Common.Net\Steeltoe.Common.Net.csproj", "{5C2D53DB-2980-4393-BCA1-B4C51004E4F7}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Common.Net", "src\Steeltoe.Common.Net\Steeltoe.Common.Net.csproj", "{5C2D53DB-2980-4393-BCA1-B4C51004E4F7}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Common.Security", "src\Steeltoe.Common.Security\Steeltoe.Common.Security.csproj", "{8D322AD5-ED49-4D40-952C-B38FD97D21EF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Common.Security.Test", "test\Steeltoe.Common.Security.Test\Steeltoe.Common.Security.Test.csproj", "{6E2CBAC4-3FF1-4DC9-9B9B-D0ADFE80218D}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -71,6 +75,14 @@ Global
{5C2D53DB-2980-4393-BCA1-B4C51004E4F7}.Debug|Any CPU.Build.0 = Debug|Any CPU {5C2D53DB-2980-4393-BCA1-B4C51004E4F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5C2D53DB-2980-4393-BCA1-B4C51004E4F7}.Release|Any CPU.ActiveCfg = Release|Any CPU {5C2D53DB-2980-4393-BCA1-B4C51004E4F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5C2D53DB-2980-4393-BCA1-B4C51004E4F7}.Release|Any CPU.Build.0 = Release|Any CPU {5C2D53DB-2980-4393-BCA1-B4C51004E4F7}.Release|Any CPU.Build.0 = Release|Any CPU
{8D322AD5-ED49-4D40-952C-B38FD97D21EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D322AD5-ED49-4D40-952C-B38FD97D21EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D322AD5-ED49-4D40-952C-B38FD97D21EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D322AD5-ED49-4D40-952C-B38FD97D21EF}.Release|Any CPU.Build.0 = Release|Any CPU
{6E2CBAC4-3FF1-4DC9-9B9B-D0ADFE80218D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6E2CBAC4-3FF1-4DC9-9B9B-D0ADFE80218D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6E2CBAC4-3FF1-4DC9-9B9B-D0ADFE80218D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6E2CBAC4-3FF1-4DC9-9B9B-D0ADFE80218D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -83,6 +95,8 @@ Global
{7FF75564-423A-4EE6-B7AD-9D35C428757F} = {CC77ED1F-BC03-4B9D-A07A-186C9A13042B} {7FF75564-423A-4EE6-B7AD-9D35C428757F} = {CC77ED1F-BC03-4B9D-A07A-186C9A13042B}
{101DF01E-FDE9-4B97-9666-C3777CC308D0} = {CC77ED1F-BC03-4B9D-A07A-186C9A13042B} {101DF01E-FDE9-4B97-9666-C3777CC308D0} = {CC77ED1F-BC03-4B9D-A07A-186C9A13042B}
{5C2D53DB-2980-4393-BCA1-B4C51004E4F7} = {D9798FDE-76F4-4848-8AE0-95249C0101F0} {5C2D53DB-2980-4393-BCA1-B4C51004E4F7} = {D9798FDE-76F4-4848-8AE0-95249C0101F0}
{8D322AD5-ED49-4D40-952C-B38FD97D21EF} = {D9798FDE-76F4-4848-8AE0-95249C0101F0}
{6E2CBAC4-3FF1-4DC9-9B9B-D0ADFE80218D} = {CC77ED1F-BC03-4B9D-A07A-186C9A13042B}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4A85F9DA-2C2D-48E9-A28C-9B35C473C150} SolutionGuid = {4A85F9DA-2C2D-48E9-A28C-9B35C473C150}

Двоичные данные
config/versions-dev.props

Двоичный файл не отображается.

Просмотреть файл

@ -0,0 +1,25 @@
// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Security.Cryptography.X509Certificates;
namespace Steeltoe.Common.Security
{
public class CertificateOptions : ICertificateOptions
{
public string Name { get; set; }
public X509Certificate2 Certificate { get; set; }
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Security.Cryptography.X509Certificates;
namespace Steeltoe.Common.Security
{
public interface ICertificateOptions
{
string Name { get; }
X509Certificate2 Certificate { get; }
}
}

Просмотреть файл

@ -0,0 +1,77 @@
// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
namespace Steeltoe.Common.Security
{
public class PemCertificateProvider : ConfigurationProvider
{
private IConfigurationRoot _certFileProvider;
private IConfigurationRoot _keyFileProvider;
public PemCertificateProvider(IConfigurationRoot certFileProvider, IConfigurationRoot keyFileProvider)
{
_certFileProvider = certFileProvider;
_keyFileProvider = keyFileProvider;
_certFileProvider.GetReloadToken().RegisterChangeCallback(NotifyCertChanged, null);
_keyFileProvider.GetReloadToken().RegisterChangeCallback(NotifyKeyChanged, null);
}
public override void Load()
{
}
public override void Set(string key, string value)
{
throw new InvalidOperationException();
}
public override bool TryGet(string key, out string value)
{
value = _certFileProvider[key];
if (!string.IsNullOrEmpty(value))
{
return true;
}
value = _keyFileProvider[key];
if (!string.IsNullOrEmpty(value))
{
return true;
}
return false;
}
public override IEnumerable<string> GetChildKeys(IEnumerable<string> earlierKeys, string parentPath)
{
return base.GetChildKeys(earlierKeys, parentPath);
}
private void NotifyCertChanged(object state)
{
OnReload();
_certFileProvider.GetReloadToken().RegisterChangeCallback(NotifyCertChanged, null);
}
private void NotifyKeyChanged(object state)
{
OnReload();
_keyFileProvider.GetReloadToken().RegisterChangeCallback(NotifyKeyChanged, null);
}
}
}

Просмотреть файл

@ -0,0 +1,102 @@
// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Microsoft.Extensions.Configuration;
using System.IO;
namespace Steeltoe.Common.Security
{
public class PemCertificateSource : IConfigurationSource
{
private string _certFilePath;
private string _keyFilePath;
public PemCertificateSource(string certFilePath, string keyFilePath)
{
_certFilePath = Path.GetFullPath(certFilePath);
_keyFilePath = Path.GetFullPath(keyFilePath);
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
var certSource = new FileSource("certificate")
{
FileProvider = null,
Path = Path.GetFileName(_certFilePath),
Optional = false,
ReloadOnChange = true,
ReloadDelay = 1000
};
var keySource = new FileSource("privateKey")
{
FileProvider = null,
Path = Path.GetFileName(_keyFilePath),
Optional = false,
ReloadOnChange = true,
ReloadDelay = 1000,
};
var certProvider = new ConfigurationBuilder()
.SetBasePath(Path.GetDirectoryName(_certFilePath))
.Add(certSource)
.Build();
var keyProvider = new ConfigurationBuilder()
.SetBasePath(Path.GetDirectoryName(_keyFilePath))
.Add(keySource)
.Build();
return new PemCertificateProvider(certProvider, keyProvider);
}
}
#pragma warning disable SA1402 // File may only contain a single class
internal class FileSource : FileConfigurationSource
{
internal string Key { get; }
public FileSource(string key)
: base()
{
Key = key;
}
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
EnsureDefaults(builder);
return new FileProvider(this);
}
}
internal class FileProvider : FileConfigurationProvider
{
public FileProvider(FileConfigurationSource source)
: base(source)
{
}
public override void Load(Stream stream)
{
var source = Source as FileSource;
string key = source.Key;
using (var reader = new StreamReader(stream))
{
string value = reader.ReadToEnd();
Data[key] = value;
}
}
}
#pragma warning restore SA1402 // File may only contain a single class
}

Просмотреть файл

@ -0,0 +1,44 @@
// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.FileProviders;
using System;
namespace Steeltoe.Common.Security
{
public static class PemConfigurationExtensions
{
public static IConfigurationBuilder AddPemFiles(this IConfigurationBuilder builder, string certFilePath, string keyFilePath)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (string.IsNullOrEmpty(certFilePath))
{
throw new ArgumentException(nameof(certFilePath));
}
if (string.IsNullOrEmpty(keyFilePath))
{
throw new ArgumentException(nameof(keyFilePath));
}
builder.Add(new PemCertificateSource(certFilePath, keyFilePath));
return builder;
}
}
}

Просмотреть файл

@ -0,0 +1,130 @@
// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;
using System;
using System.IO;
using System.Text;
using MS = System.Security.Cryptography.X509Certificates;
namespace Steeltoe.Common.Security
{
public class PemConfigureCertificateOptions : IConfigureNamedOptions<CertificateOptions>
{
private IConfiguration _config;
private ILogger _logger;
public PemConfigureCertificateOptions(IConfiguration config, ILogger logger = null)
{
if (config == null)
{
throw new ArgumentNullException(nameof(config));
}
_config = config;
_logger = logger;
}
public void Configure(string name, CertificateOptions options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
options.Name = name;
var pemCert = _config["certificate"];
var pemKey = _config["privateKey"];
if (string.IsNullOrEmpty(pemCert) || string.IsNullOrEmpty(pemKey))
{
return;
}
var certBytes = Encoding.Default.GetBytes(pemCert);
var keyBytes = Encoding.Default.GetBytes(pemKey);
X509Certificate cert = ReadCertificate(certBytes);
AsymmetricCipherKeyPair keys = ReadKeys(keyBytes);
var pfxBytes = CreatePfxContainer(cert, keys);
options.Certificate = new MS.X509Certificate2(pfxBytes);
}
public void Configure(CertificateOptions options)
{
Configure(Options.DefaultName, options);
}
internal byte[] CreatePfxContainer(X509Certificate cert, AsymmetricCipherKeyPair keys)
{
var certEntry = new X509CertificateEntry(cert);
var pkcs12Store = new Pkcs12StoreBuilder()
.SetUseDerEncoding(true)
.Build();
var keyEntry = new AsymmetricKeyEntry(keys.Private);
pkcs12Store.SetKeyEntry("ServerInstance", keyEntry, new X509CertificateEntry[] { certEntry });
using (MemoryStream stream = new MemoryStream())
{
pkcs12Store.Save(stream, null, new SecureRandom());
var bytes = stream.ToArray();
return Pkcs12Utilities.ConvertToDefiniteLength(bytes);
}
}
internal AsymmetricCipherKeyPair ReadKeys(byte[] keyBytes)
{
try
{
using (var reader = new StreamReader(new MemoryStream(keyBytes)))
{
return new PemReader(reader).ReadObject() as AsymmetricCipherKeyPair;
}
}
catch (Exception e)
{
_logger?.LogError(e, "Unable to read PEM encoded keys");
}
return null;
}
internal X509Certificate ReadCertificate(byte[] certBytes)
{
try
{
using (var reader = new StreamReader(new MemoryStream(certBytes)))
{
return new PemReader(reader).ReadObject() as X509Certificate;
}
}
catch (Exception e)
{
_logger?.LogError(e, "Unable to read PEM encoded certificate");
}
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,44 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\versions.props" />
<PropertyGroup>
<Description>Steeltoe Common Security Library</Description>
<VersionPrefix>$(SteeltoeVersion)</VersionPrefix>
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
<Authors>Pivotal;dtillman</Authors>
<!--<TargetFramework>netstandard2.0</TargetFramework>-->
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<AssemblyName>Steeltoe.Common.Security</AssemblyName>
<PackageId>Steeltoe.Common.Security</PackageId>
<PackageTags>NET Core;NET Framework</PackageTags>
<PackageIconUrl>https://steeltoe.io/images/transparent.png</PackageIconUrl>
<PackageProjectUrl>https://steeltoe.io</PackageProjectUrl>
<PackageLicenseUrl>http://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
</PropertyGroup>
<PropertyGroup>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\Steeltoe.Common.Security.xml</DocumentationFile>
<NoWarn>SA1101;SA1124;SA1201;SA1309;SA1310;SA1401;SA1600;SA1652;1591</NoWarn>
</PropertyGroup>
<PropertyGroup>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="$(BouncyCastleVersion)" />
<PackageReference Include="Microsoft.Extensions.Options" Version="$(ExtensionsVersion)" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(ExtensionsVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="$(ExtensionsVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="$(ExtensionsVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(ExtensionsVersion)" />
<PackageReference Include="StyleCop.Analyzers" Version="$(StyleCopVersion)">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json">
<Link>stylecop.json</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</AdditionalFiles>
</ItemGroup>
</Project>

Просмотреть файл

@ -19,8 +19,21 @@ namespace Steeltoe.Common.LoadBalancer
{ {
public interface ILoadBalancer public interface ILoadBalancer
{ {
/// <summary>
/// Evaluates a Uri for a host name that can be resolved into a service instance
/// </summary>
/// <param name="request">A Uri containing a service name that can be resolved into one or more service instances</param>
/// <returns>The original Uri, with serviceName replaced by the host:port of a service instance</returns>
Task<Uri> ResolveServiceInstanceAsync(Uri request); Task<Uri> ResolveServiceInstanceAsync(Uri request);
/// <summary>
/// A mechanism for tracking statistics for service instances
/// </summary>
/// <param name="originalUri">The original request Uri</param>
/// <param name="resolvedUri">The Uri resolved by the load balancer</param>
/// <param name="responseTime">The amount of time taken for a remote call to complete</param>
/// <param name="exception">Any exception called during calls to a resolved service instance</param>
/// <returns>A task</returns>
Task UpdateStatsAsync(Uri originalUri, Uri resolvedUri, TimeSpan responseTime, Exception exception); Task UpdateStatsAsync(Uri originalUri, Uri resolvedUri, TimeSpan responseTime, Exception exception);
} }
} }

Просмотреть файл

@ -0,0 +1,34 @@
// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
namespace Steeltoe.Common.Net
{
public class HostInfo
{
public string Hostname { get; set; }
public string IpAddress { get; set; }
public bool Override { get; set; }
public HostInfo()
{
}
public HostInfo(string hostname)
{
Hostname = hostname;
}
}
}

Просмотреть файл

@ -0,0 +1,54 @@
// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
namespace Steeltoe.Common.Net
{
public class InetOptions
{
public const string PREFIX = "spring:cloud:inet";
public string DefaultHostname { get; set; } = "localhost";
public string DefaultIpAddress { get; set; } = "127.0.0.1";
public string IgnoredInterfaces { get; set; }
public bool UseOnlySiteLocalInterfaces { get; set; } = false;
public string PreferredNetworks { get; set; }
internal IEnumerable<string> GetIgnoredInterfaces()
{
if (string.IsNullOrEmpty(IgnoredInterfaces))
{
return new List<string>();
}
return IgnoredInterfaces.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
internal IEnumerable<string> GetPreferredNetworks()
{
if (string.IsNullOrEmpty(PreferredNetworks))
{
return null;
}
return PreferredNetworks.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
}
}

Просмотреть файл

@ -0,0 +1,269 @@
// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text.RegularExpressions;
namespace Steeltoe.Common.Net
{
public class InetUtils
{
private readonly InetOptions _options;
private readonly ILogger _logger;
public InetUtils(InetOptions options, ILogger logger = null)
{
_options = options;
_logger = logger;
}
public HostInfo FindFirstNonLoopbackHostInfo()
{
var address = FindFirstNonLoopbackAddress();
if (address != null)
{
return ConvertAddress(address);
}
HostInfo hostInfo = new HostInfo();
hostInfo.Hostname = _options.DefaultHostname;
hostInfo.IpAddress = _options.DefaultIpAddress;
return hostInfo;
}
public IPAddress FindFirstNonLoopbackAddress()
{
IPAddress result = null;
try
{
int lowest = int.MaxValue;
var ifaces = NetworkInterface.GetAllNetworkInterfaces();
for (int i = 0; i < ifaces.Length; i++)
{
var ifc = ifaces[i];
if (ifc.OperationalStatus == OperationalStatus.Up && !ifc.IsReceiveOnly)
{
_logger?.LogTrace("Testing interface: {name}, {id}", ifc.Name, ifc.Id);
var props = ifc.GetIPProperties();
var ipprops = props.GetIPv4Properties();
if (ipprops.Index < lowest || result == null)
{
lowest = ipprops.Index;
}
else if (result != null)
{
continue;
}
if (!IgnoreInterface(ifc.Name))
{
foreach (var addressInfo in props.UnicastAddresses)
{
var address = addressInfo.Address;
if (IsInet4Address(address)
&& !IsLoopbackAddress(address)
&& IsPreferredAddress(address))
{
_logger?.LogTrace("Found non-loopback interface: {name}", ifc.Name);
result = address;
}
}
}
}
}
}
catch (Exception ex)
{
_logger?.LogError(ex, "Cannot get first non-loopback address");
}
if (result != null)
{
return result;
}
string localHost = GetHostAddress();
if (!string.IsNullOrEmpty(localHost))
{
return IPAddress.Parse(localHost);
}
return null;
}
internal bool IsInet4Address(IPAddress address)
{
return address.AddressFamily == AddressFamily.InterNetwork;
}
internal bool IsLoopbackAddress(IPAddress address)
{
return IPAddress.IsLoopback(address);
}
internal bool IsPreferredAddress(IPAddress address)
{
if (_options.UseOnlySiteLocalInterfaces)
{
bool siteLocalAddress = IsSiteLocalAddress(address);
if (!siteLocalAddress)
{
_logger?.LogTrace("Ignoring address: {address} ", address.ToString());
}
return siteLocalAddress;
}
IEnumerable<string> preferredNetworks = _options.GetPreferredNetworks();
if (preferredNetworks == null)
{
return true;
}
foreach (string regex in preferredNetworks)
{
string hostAddress = address.ToString();
var matcher = new Regex(regex);
if (matcher.IsMatch(hostAddress) || hostAddress.StartsWith(regex))
{
return true;
}
}
_logger?.LogTrace("Ignoring address: {address}", address.ToString());
return false;
}
internal bool IgnoreInterface(string interfaceName)
{
if (string.IsNullOrEmpty(interfaceName))
{
return false;
}
foreach (string regex in _options.GetIgnoredInterfaces())
{
var matcher = new Regex(regex);
if (matcher.IsMatch(interfaceName))
{
_logger?.LogTrace("Ignoring interface: {name}", interfaceName);
return true;
}
}
return false;
}
internal HostInfo ConvertAddress(IPAddress address)
{
HostInfo hostInfo = new HostInfo();
string hostname;
try
{
var hostEntry = Dns.GetHostEntry(address);
hostname = hostEntry.HostName;
}
catch (Exception e)
{
_logger?.LogInformation(e, "Cannot determine local hostname");
hostname = "localhost";
}
hostInfo.Hostname = hostname;
hostInfo.IpAddress = address.ToString();
return hostInfo;
}
internal string ResolveHostAddress(string hostName)
{
string result = null;
try
{
var results = Dns.GetHostAddresses(hostName);
if (results != null && results.Length > 0)
{
foreach (var addr in results)
{
if (addr.AddressFamily.Equals(AddressFamily.InterNetwork))
{
result = addr.ToString();
break;
}
}
}
}
catch (Exception e)
{
_logger?.LogWarning(e, "Unable to resolve host address");
}
return result;
}
internal string ResolveHostName()
{
string result = null;
try
{
result = Dns.GetHostName();
if (!string.IsNullOrEmpty(result))
{
var response = Dns.GetHostEntry(result);
if (response != null)
{
return response.HostName;
}
}
}
catch (Exception e)
{
_logger?.LogWarning(e, "Unable to resolve hostname");
}
return result;
}
internal string GetHostName()
{
return ResolveHostName();
}
internal string GetHostAddress()
{
string hostName = GetHostName();
if (!string.IsNullOrEmpty(hostName))
{
return ResolveHostAddress(hostName);
}
return null;
}
internal bool IsSiteLocalAddress(IPAddress address)
{
string addr = address.ToString();
return addr.StartsWith("10.") ||
addr.StartsWith("172.16.") ||
addr.StartsWith("192.168.");
}
}
}

Просмотреть файл

@ -0,0 +1,104 @@
// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.IO;
using System.Threading;
using Xunit;
namespace Steeltoe.Common.Security.Test
{
public class PemConfigurationExtensionsTest
{
[Fact]
public void AddPemFiles_ThrowsOnNulls()
{
Assert.Throws<ArgumentNullException>(() => PemConfigurationExtensions.AddPemFiles(null, null, null));
Assert.Throws<ArgumentException>(() => PemConfigurationExtensions.AddPemFiles(new ConfigurationBuilder(), null, null));
Assert.Throws<ArgumentException>(() => PemConfigurationExtensions.AddPemFiles(new ConfigurationBuilder(), "foobar", null));
}
[Fact]
public void AddPemFiles_ReadsFiles()
{
var config = new ConfigurationBuilder()
.AddPemFiles("instance.crt", "instance.key")
.Build();
Assert.NotNull(config["certificate"]);
Assert.NotNull(config["privateKey"]);
}
[Fact]
public void AddPemFiles_ReloadsOnChange()
{
var tempFile1 = CreateTempFile("cert");
var tempFile2 = CreateTempFile("key");
var config = new ConfigurationBuilder()
.AddPemFiles(tempFile1, tempFile2)
.Build();
Assert.Equal("cert", config["certificate"]);
Assert.Equal("key", config["privateKey"]);
File.WriteAllText(tempFile1, "cert2");
Thread.Sleep(2000);
Assert.Equal("cert2", config["certificate"]);
Assert.Equal("key", config["privateKey"]);
}
[Fact]
public void AddPemFiles_NotifiesOnChange()
{
var tempFile1 = CreateTempFile("cert");
var tempFile2 = CreateTempFile("key");
var config = new ConfigurationBuilder()
.AddPemFiles(tempFile1, tempFile2)
.Build();
bool changeCalled = false;
var token = config.GetReloadToken();
token.RegisterChangeCallback((o) => changeCalled = true, "state");
Assert.Equal("cert", config["certificate"]);
Assert.Equal("key", config["privateKey"]);
File.WriteAllText(tempFile1, "barfoo");
Thread.Sleep(2000);
Assert.Equal("barfoo", config["certificate"]);
Assert.Equal("key", config["privateKey"]);
Assert.True(changeCalled);
token = config.GetReloadToken();
token.RegisterChangeCallback((o) => changeCalled = true, "state");
changeCalled = false;
File.WriteAllText(tempFile2, "barbar");
Thread.Sleep(2000);
Assert.Equal("barfoo", config["certificate"]);
Assert.Equal("barbar", config["privateKey"]);
Assert.True(changeCalled);
}
private static string CreateTempFile(string contents)
{
var tempFile = Path.GetTempFileName();
File.WriteAllText(tempFile, contents);
return tempFile;
}
}
}

Просмотреть файл

@ -0,0 +1,39 @@
// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Xunit;
namespace Steeltoe.Common.Security.Test
{
public class PemConfigureCertificateOptionsTest
{
[Fact]
public void AddPemFiles_ReadsFiles_CreatesCertificate()
{
var config = new ConfigurationBuilder()
.AddPemFiles("instance.crt", "instance.key")
.Build();
Assert.NotNull(config["certificate"]);
Assert.NotNull(config["privateKey"]);
var pemConfig = new PemConfigureCertificateOptions(config);
CertificateOptions opts = new CertificateOptions();
pemConfig.Configure(opts);
Assert.NotNull(opts.Certificate);
Assert.Equal(Options.DefaultName, opts.Name);
Assert.True(opts.Certificate.HasPrivateKey);
}
}
}

Просмотреть файл

@ -0,0 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\versions.props" />
<PropertyGroup>
<TargetFrameworks>netcoreapp2.0;netcoreapp2.1;net461</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<None Update="instance.crt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="instance.key">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<PropertyGroup>
<NoWarn>SA1101;SA1124;SA1201;SA1309;SA1310;SA1401;SA1600;SA1652;1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Steeltoe.Common.Security\Steeltoe.Common.Security.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(ExtensionsVersion)" />
<PackageReference Include="Microsoft.Extensions.Options" Version="$(ExtensionsVersion)" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(ExtensionsVersion)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
<PackageReference Include="xunit" Version="$(XunitVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitStudioVersion)" />
<DotNetCliToolReference Include="dotnet-xunit" Version="$(XunitVersion)" />
<PackageReference Include="StyleCop.Analyzers" Version="$(StyleCopVersion)">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\stylecop.json">
<Link>stylecop.json</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</AdditionalFiles>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,44 @@
-----BEGIN CERTIFICATE-----
MIIEBzCCAu+gAwIBAgIQSrLsvLyESbpjMDWmkPr+0TANBgkqhkiG9w0BAQsFADAy
MTAwLgYDVQQDEydEaWVnbyBJbnN0YW5jZSBJZGVudGl0eSBJbnRlcm1lZGlhdGUg
Q0EwHhcNMTkwMjI0MjAxMzAwWhcNMTkwMjI1MjAxMzAwWjCByDGBnjA4BgNVBAsT
MW9yZ2FuaXphdGlvbjozOTBjYmI3Zi0wMWYyLTQ2MGUtOTViMi1jNjhmOGQ0YTk0
YjgwMQYDVQQLEypzcGFjZTpjYzY4MmUyNi0yMTU1LTQ2NDktYjk0OC00ZjBkMDJm
ODg3ZGEwLwYDVQQLEyhhcHA6M2ZlYzJmYjktNmIzYi00Yzc1LTkxYTktY2YwNDRi
ZWIxOGU3MSUwIwYDVQQDExxjZmMyYmM0ZC1lODMxLTQ1ODgtNzY0My01NDQ0MIIB
IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt2O/qEPoA0jQFrI23NRIzd3E
FhsNwuTpZ4DoSVIZJQsjG0gjzoj0/YZHp6sbNRlWTHz1eXJ714+0rkYvpYjOp3jo
EOJhsrx3Hs9eN0yDz+YqB+J3DSYt+t+nrae7wDBUAMFZXjqhrDhuFgANjsxF3Xtl
1REgcRIdW9LGydAEAGlyMKAGBmQf9zofYCR+4Sw96KhQXBOTJiI+9p9pw3ndptfB
hYCBdiifcHi6+e3nsOidqjrDP4PA6OfVMCxvzDkJLU2Bq1cjqHFomNB5Tc5AshyA
lvDOi2+xVWBX7Z0+UujlWBYMeQl+FSz4N1cPYQ3SSNCDadkZHx/0f+qvRGX5eQID
AQABo4GBMH8wDgYDVR0PAQH/BAQDAgOoMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr
BgEFBQcDATAfBgNVHSMEGDAWgBTBVXXc35U/Jlb+VYWm3AsyiodCETAtBgNVHREE
JjAkghxjZmMyYmM0ZC1lODMxLTQ1ODgtNzY0My01NDQ0hwQK/0ilMA0GCSqGSIb3
DQEBCwUAA4IBAQAYBwmgU99XJ/bFrkWYXKpb4PZClxbFk8eTGWuFQiREDtum13lU
uxsWGWETLewJ7xEQzoEhWTbhOu8Kc0qILc+/dgoE+ENl59quwpFiWJNAu8gJYLga
8JYaPL2RnCwx+7MBuAWkhNoX9fBWCrNo5NcJpyuaFJZPUCpmspDjQNf53ez/lnoV
8hgCdTcBRFJgmbInSHIyy//k9Tv+6wisdZUsQf7y6O5HByKNNwSFbaEfKrSY38gb
5lc9GBFsHxGgwJhy8y0y9ccMCHCMsEfKIJZPgJvyqjQqR3Y2AZiQCCexTi50f+lW
ul0sbc6nZtCbml31604CjeVYA4jBCV3i6+T6
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDTTCCAjWgAwIBAgIUYnMnLAdY1eziifHiE6IDh958SnwwDQYJKoZIhvcNAQEL
BQAwKjEoMCYGA1UEAxMfRGllZ28gSW5zdGFuY2UgSWRlbnRpdHkgUm9vdCBDQTAe
Fw0xODExMTIyMDE3MzVaFw0yMDExMTEyMDE3MzVaMDIxMDAuBgNVBAMTJ0RpZWdv
IEluc3RhbmNlIElkZW50aXR5IEludGVybWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAKzttSjhIkimTDKEwCRWFeez/S/bFP23QIjHz+Y5
dl7sdX8YXwdfWbqciS2EhJ6s1QCHwMToqLb6czRtzUz1TLdyMs9wuz9XzzgJHdNj
yIb9B5aSbRudhTzGIAkY6HtjSA9lSD7gn66YBt/G5b+YHeZtFzc+Ao+Su5zJZ8AD
7pWm0LP/q2Mahu+Pp3yx6dhkWV4MPDNJH6YBscQEZf08Hhew86b5adxGHmpR6ckU
CpZUGHNOtE386eY6MgoN273cEj0UL7BH+YHGEeKGVrft7QYd5T/k9YC+Z7vmE9kw
Ak5420Z7D3bg06y4qlguYHhqbxrlFW7HHjjrryc0Wfc7VRMCAwEAAaNjMGEwHQYD
VR0OBBYEFMFVddzflT8mVv5VhabcCzKKh0IRMA4GA1UdDwEB/wQEAwICBDAfBgNV
HSMEGDAWgBTFkeVheTtTImn4MXKxnOhKSBHcMzAPBgNVHRMBAf8EBTADAQH/MA0G
CSqGSIb3DQEBCwUAA4IBAQBigPJqRSy13SP+vT9/dWGADoqTZSVE/JDvX4vbKuyg
mOmV4hA3RbDa0vVBzDaMXLMjA4H/RFMCA3j99bec0RZXQT0sCnaYbFIU5msUEiP7
11TY8advyq5GnZaGTXaEGGkmbWTsPKlLulnawtPdEtUQwZxYnh1nc6O0LqmbniwK
zuHG478KmH8r779c6KCiwkA9K6OmMpT6wxJW0N88/tQ6EnJ2VSCGaJDhB+XbwJam
s+pAhGCIOOuCD3fPYlz0QQ2UN/QwRAKDV1QTzGqYBesdZTRZtnDp1q/lZd7ETt8U
iF9yZCtnfUN5lTLNs7ZCxy/3/I/tW90ssLVPY/+tfX/8
-----END CERTIFICATE-----

Просмотреть файл

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAt2O/qEPoA0jQFrI23NRIzd3EFhsNwuTpZ4DoSVIZJQsjG0gj
zoj0/YZHp6sbNRlWTHz1eXJ714+0rkYvpYjOp3joEOJhsrx3Hs9eN0yDz+YqB+J3
DSYt+t+nrae7wDBUAMFZXjqhrDhuFgANjsxF3Xtl1REgcRIdW9LGydAEAGlyMKAG
BmQf9zofYCR+4Sw96KhQXBOTJiI+9p9pw3ndptfBhYCBdiifcHi6+e3nsOidqjrD
P4PA6OfVMCxvzDkJLU2Bq1cjqHFomNB5Tc5AshyAlvDOi2+xVWBX7Z0+UujlWBYM
eQl+FSz4N1cPYQ3SSNCDadkZHx/0f+qvRGX5eQIDAQABAoIBAQCK2Bh4+sCkC/KP
3GmxE3/zbR1SZzUqA0m7NVuod2HWK/Jua1XAvuxNLeb+SIuWzhIKYukvA8BDWee/
sh/MwiFDpkR81AiH3CyLxRBd6a46LtZPlePwrqFNORuoXD/HqE9RKxHQR6+zxh2C
xpN9M6cJoq1cfVUEhmR36sLadIUzEVmGKVUhUv56kdrbwd3AEGrKkcWBl8N8ukCd
1qV4IU7REwPjZBbfXSequ5goSKzzMG9MbKXLAj51btkCnMtLzfHfY3sSyfQIYO62
7KJouNvdg3omNlygBsAuj4hM5nlg9uzuJQLrNEwnqOZXVGvkTXurZ+CrAN13Q5qf
Xj4VyF0BAoGBAMkv+XKxH3Ck6KHsFENR3A2jb6UuvXHW77wzrN+pvXWPBZ0DhmxL
PII/9clBnH4+EzINei4uhs0f0OL8+d0tqoOT0Qz6vOzhFKY/2/Cjk1ifff9uI1i3
BpLjfPJyywy4HiVqjrTuDZjpp4Fj5v8jrI5Eg6Z5Xg4q3+qeAHR22ahpAoGBAOla
c3r048V30LQf45Cg79uZaOtNNxCZwzcX0DZWqih0buwKfCEufhQ2ctpBbi9NeOt3
9KEe2z5PT3B+uNzPBzkUGQ/6A/+aK9JVBEtNWqJI1nR6qMXB5t+d9iQrbIf56NHA
1o3e1sAtXOvkVgh0o/ojwR//N+VMzQaDdlIvbiaRAoGBAJETukK9fRmCoYqaLeZ5
skBXedvYv53Gy6uga+oBgfCzCO43q4iOHH0kWD4fxRS3+KmgVFnXDTf/2GbG2/tl
wc8OGbLNYM1EZdqYtCZsHoXKxVYbevuvR9tGlkRTCR8L6hk7JNtNyppY64R/oQSd
GgKhX3n9jRiUTFHoTBWv2rb5AoGAbUTNjmXdwjm4oJ/OD4tMxaewWX5uqndV0hZ0
iP1L8GWVCzJdrav3nb9hSJIa5kuAs8IX6tpoD2VT7XlpVvwahb/DfJe2B5pJqtPk
jt5J8nPo9+H35aJGWa+98nHjAEklnBKQZR5TsOmM+WiSYKM9pYPYiwMXSWgNGV+1
qAZNrgECgYBoka0S/7jQt9TwQSRAagdmyBIo26JDQ6igqLB/lBrKgYAbqcTRmy2X
bQZH1aCB/3B/NmIsqlzGOl/Uz3V6hW9Ae9X7BrXU4ug7SRH5BSff/eBY2j9YCR3e
z9elUvEYUa59oaBtEJGXCOxjsypdjaR25LnW7/wVI3iBAiw6fAqlsw==
-----END RSA PRIVATE KEY-----

Просмотреть файл

@ -0,0 +1,4 @@
{
"maxParallelThreads": 1,
"parallelizeTestCollections": false
}

Просмотреть файл

@ -0,0 +1,128 @@
// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System.Net;
using Xunit;
namespace Steeltoe.Common.Net.Test
{
public class InetUtilsTest
{
[Fact]
public void TestGetFirstNonLoopbackHostInfo()
{
InetUtils utils = new InetUtils(new InetOptions());
Assert.NotNull(utils.FindFirstNonLoopbackHostInfo());
}
[Fact]
public void TestGetFirstNonLoopbackAddress()
{
InetUtils utils = new InetUtils(new InetOptions());
Assert.NotNull(utils.FindFirstNonLoopbackAddress());
}
[Fact]
public void TestConvert()
{
InetUtils utils = new InetUtils(new InetOptions());
Assert.NotNull(utils.ConvertAddress(Dns.GetHostEntry("localhost").AddressList[0]));
}
[Fact]
public void TestHostInfo()
{
InetUtils utils = new InetUtils(new InetOptions());
HostInfo info = utils.FindFirstNonLoopbackHostInfo();
Assert.NotNull(info.IpAddress);
}
[Fact]
public void TestIgnoreInterface()
{
InetOptions properties = new InetOptions()
{
IgnoredInterfaces = "docker0,veth.*"
};
InetUtils inetUtils = new InetUtils(properties);
Assert.True(inetUtils.IgnoreInterface("docker0"));
Assert.True(inetUtils.IgnoreInterface("vethAQI2QT"));
Assert.False(inetUtils.IgnoreInterface("docker1"));
}
[Fact]
public void TestDefaultIgnoreInterface()
{
InetUtils inetUtils = new InetUtils(new InetOptions());
Assert.False(inetUtils.IgnoreInterface("docker0"));
}
[Fact]
public void TestSiteLocalAddresses()
{
InetOptions properties = new InetOptions()
{
UseOnlySiteLocalInterfaces = true
};
InetUtils utils = new InetUtils(properties);
Assert.True(utils.IsPreferredAddress(IPAddress.Parse("192.168.0.1")));
Assert.False(utils.IsPreferredAddress(IPAddress.Parse("5.5.8.1")));
}
[Fact]
public void TestPreferredNetworksRegex()
{
InetOptions properties = new InetOptions()
{
PreferredNetworks = "192.168.*,10.0.*"
};
InetUtils utils = new InetUtils(properties);
Assert.True(utils.IsPreferredAddress(IPAddress.Parse("192.168.0.1")));
Assert.False(utils.IsPreferredAddress(IPAddress.Parse("5.5.8.1")));
Assert.True(utils.IsPreferredAddress(IPAddress.Parse("10.0.10.1")));
Assert.False(utils.IsPreferredAddress(IPAddress.Parse("10.255.10.1")));
}
[Fact]
public void TestPreferredNetworksSimple()
{
InetOptions properties = new InetOptions()
{
PreferredNetworks = "192,10.0"
};
InetUtils utils = new InetUtils(properties);
Assert.True(utils.IsPreferredAddress(IPAddress.Parse("192.168.0.1")));
Assert.False(utils.IsPreferredAddress(IPAddress.Parse("5.5.8.1")));
Assert.False(utils.IsPreferredAddress(IPAddress.Parse("10.255.10.1")));
Assert.True(utils.IsPreferredAddress(IPAddress.Parse("10.0.10.1")));
}
[Fact]
public void TestPreferredNetworksListIsEmpty()
{
InetOptions properties = new InetOptions();
InetUtils utils = new InetUtils(properties);
Assert.True(utils.IsPreferredAddress(IPAddress.Parse("192.168.0.1")));
Assert.True(utils.IsPreferredAddress(IPAddress.Parse("5.5.8.1")));
Assert.True(utils.IsPreferredAddress(IPAddress.Parse("10.255.10.1")));
Assert.True(utils.IsPreferredAddress(IPAddress.Parse("10.0.10.1")));
}
}
}