diff --git a/src/ObjectValidation CountryValidator/README.md b/src/ObjectValidation CountryValidator/README.md
index a808e08..7ad4418 100644
--- a/src/ObjectValidation CountryValidator/README.md
+++ b/src/ObjectValidation CountryValidator/README.md
@@ -24,6 +24,7 @@ enumerable lengths
- XRechnung routing validation
- European VAT ID validation
- XML validation
+- Hostname or IP address validation
- Conditional value requirements
- Enumeration value validation
- Validation references
@@ -81,6 +82,7 @@ The **ObjectValidation-CountryValidator** extension is licensed using the
| XRechnung routing validation | `XRechnungRouteAttribute` |
| European VAT ID validation | `EuVatIdAttribute` |
| XML validation | `XmlAttribute` |
+| Hostname or IP address validation | `HostAttribute` |
| Conditional value requirement | `RequiredIfAttribute` |
| Allowed/denied values | `AllowedValuesAttribute`, `DeniedValuesAttribute` |
| Enumeration value | (none - using the type) |
@@ -298,6 +300,7 @@ These item validation adapters exist:
| `CompareAttribute` | `ItemCompareAttribute` |
| `CreditCardAttribute` | `ItemCreditCardAttribute` |
| `EmailAddressAttribute` | `ItemEmailAddressAttribute` |
+| `HostAttribute` | `ItemHostAttribute` |
| `MaxLengthAttribute` | `ItemMaxLengthAttribute` |
| `MinLengthAttribute` | `ItemMinLengthAttribute` |
| `NoValidationAttribute` | `ItemNoValidationAttribute` |
diff --git a/src/ObjectValidation/AbaValidation.cs b/src/ObjectValidation/AbaValidation.cs
index 93535ab..f9eca72 100644
--- a/src/ObjectValidation/AbaValidation.cs
+++ b/src/ObjectValidation/AbaValidation.cs
@@ -93,25 +93,25 @@ public static AbaFormats GetAbaFormat(string aba)
///
/// MICR format normalizing regular expression
///
- [GeneratedRegex(@"[^\d]", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"[^\d]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex NormalizingMicr_Generated();
///
/// Fraction format normalizing regular expression
///
- [GeneratedRegex(@"[^\d|\-|/]", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"[^\d|\-|/]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex NormalizingFraction_Generated();
///
/// MICR syntax validating regular expression ($1 is the FED routing symbol, $2 the ABA institution identifier, and $3 the check digit)
///
- [GeneratedRegex(@"^(\d{4})(\d{4})(\d)$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(\d{4})(\d{4})(\d)$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex MicrSyntax_Generated();
///
/// Fraction syntax validating regular expression ($1 is the ABA prefix, $2 the ABA institution identifier, and $3 the FED routing symbol)
///
- [GeneratedRegex(@"^(\d{1,2})\-(\d{4})/?(\d{4})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(\d{1,2})\-(\d{4})/?(\d{4})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex FractionSyntax_Generated();
}
}
diff --git a/src/ObjectValidation/CountryCodes.cs b/src/ObjectValidation/CountryCodes.cs
index 2280267..3ce1542 100644
--- a/src/ObjectValidation/CountryCodes.cs
+++ b/src/ObjectValidation/CountryCodes.cs
@@ -1,6 +1,4 @@
-using System.Collections.Frozen;
-
-namespace wan24.ObjectValidation
+namespace wan24.ObjectValidation
{
///
/// Country ISO 3166-1 alpha-2 codes
diff --git a/src/ObjectValidation/CurrencyCodes.cs b/src/ObjectValidation/CurrencyCodes.cs
index 82c2b57..e3559b9 100644
--- a/src/ObjectValidation/CurrencyCodes.cs
+++ b/src/ObjectValidation/CurrencyCodes.cs
@@ -1,6 +1,4 @@
-using System.Collections.Frozen;
-
-namespace wan24.ObjectValidation
+namespace wan24.ObjectValidation
{
///
/// Currency ISO 4217 codes
diff --git a/src/ObjectValidation/EuVatId.cs b/src/ObjectValidation/EuVatId.cs
index 71842d7..6a1b2ae 100644
--- a/src/ObjectValidation/EuVatId.cs
+++ b/src/ObjectValidation/EuVatId.cs
@@ -65,175 +65,175 @@ public static partial class EuVatId
///
/// VAT ID syntax regular expression for AT
///
- [GeneratedRegex(@"^(ATU)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(ATU)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex AT_Generated();
///
/// VAT ID syntax regular expression for BE
///
- [GeneratedRegex(@"^(BE)(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(BE)(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex BE_Generated();
///
/// VAT ID syntax regular expression for BG
///
- [GeneratedRegex(@"^(BG)(\d{9,10})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(BG)(\d{9,10})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex BG_Generated();
///
/// VAT ID syntax regular expression for CY
///
- [GeneratedRegex(@"^(CY)(\d{8}[A-Z])$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(CY)(\d{8}[A-Z])$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex CY_Generated();
///
/// VAT ID syntax regular expression for CZ
///
- [GeneratedRegex(@"^(CZ)(\d{8,10})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(CZ)(\d{8,10})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex CZ_Generated();
///
/// VAT ID syntax regular expression for DE
///
- [GeneratedRegex(@"^(DE)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(DE)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex DE_Generated();
///
/// VAT ID syntax regular expression for DK
///
- [GeneratedRegex(@"^(DK)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(DK)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex DK_Generated();
///
/// VAT ID syntax regular expression for EE
///
- [GeneratedRegex(@"^(EE)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(EE)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex EE_Generated();
///
/// VAT ID syntax regular expression for ES
///
- [GeneratedRegex(@"^(ES)([A-Z|\d]\d{7}[A-Z|\d])$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(ES)([A-Z|\d]\d{7}[A-Z|\d])$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex ES_Generated();
///
/// VAT ID syntax regular expression for FI
///
- [GeneratedRegex(@"^(FI)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(FI)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex FI_Generated();
///
/// VAT ID syntax regular expression for FR
///
- [GeneratedRegex(@"^(FR)([A-Z|\d]{2}\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(FR)([A-Z|\d]{2}\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex FR_Generated();
///
/// VAT ID syntax regular expression for GR
///
- [GeneratedRegex(@"^(GR)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(GR)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex GR_Generated();
///
/// VAT ID syntax regular expression for HR
///
- [GeneratedRegex(@"^(HR)(\d{11})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(HR)(\d{11})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex HR_Generated();
///
/// VAT ID syntax regular expression for HU
///
- [GeneratedRegex(@"^(HU)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(HU)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex HU_Generated();
///
/// VAT ID syntax regular expression for IE
///
- [GeneratedRegex(@"^(IE)((\d[A-Z|\d]\d{5}[A-Z])|(\d{7}[A-W][A-I]))$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(IE)((\d[A-Z|\d]\d{5}[A-Z])|(\d{7}[A-W][A-I]))$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex IE_Generated();
///
/// VAT ID syntax regular expression for IT
///
- [GeneratedRegex(@"^(IT)(\d{11})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(IT)(\d{11})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex IT_Generated();
///
/// VAT ID syntax regular expression for LT
///
- [GeneratedRegex(@"^(LT)(\d{9}|\d{12})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(LT)(\d{9}|\d{12})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex LT_Generated();
///
/// VAT ID syntax regular expression for LU
///
- [GeneratedRegex(@"^(LU)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(LU)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex LU_Generated();
///
/// VAT ID syntax regular expression for LV
///
- [GeneratedRegex(@"^(LV)(\d{11})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(LV)(\d{11})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex LV_Generated();
///
/// VAT ID syntax regular expression for MT
///
- [GeneratedRegex(@"^(MT)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(MT)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex MT_Generated();
///
/// VAT ID syntax regular expression for NL
///
- [GeneratedRegex(@"^(NL)([\d|A-Z|\+|\*]{12})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(NL)([\d|A-Z|\+|\*]{12})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex NL_Generated();
///
/// VAT ID syntax regular expression for PL
///
- [GeneratedRegex(@"^(PL)(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(PL)(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex PL_Generated();
///
/// VAT ID syntax regular expression for PT
///
- [GeneratedRegex(@"^(PT)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(PT)(\d{9})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex PT_Generated();
///
/// VAT ID syntax regular expression for RO
///
- [GeneratedRegex(@"^(RO)([1-9]\d{0,9})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(RO)([1-9]\d{0,9})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex RO_Generated();
///
/// VAT ID syntax regular expression for SE
///
- [GeneratedRegex(@"^(SE)(\d{10}01)$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(SE)(\d{10}01)$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex SE_Generated();
///
/// VAT ID syntax regular expression for SI
///
- [GeneratedRegex(@"^(SI)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(SI)(\d{8})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex SI_Generated();
///
/// VAT ID syntax regular expression for SK
///
- [GeneratedRegex(@"^(SK)(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(SK)(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex SK_Generated();
///
/// VAT ID syntax regular expression for XI
///
- [GeneratedRegex(@"^(XI)(\d{9}|\d{12}|(GD\d{3})|(HA\d{3}))$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^(XI)(\d{9}|\d{12}|(GD\d{3})|(HA\d{3}))$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex XI_Generated();
///
/// Normalizing regular expression
///
- [GeneratedRegex(@"[^\d|A-Z]", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"[^\d|A-Z]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex Normalizing_Generated();
}
}
diff --git a/src/ObjectValidation/HostAttribute.cs b/src/ObjectValidation/HostAttribute.cs
index 21cf72a..f64ba98 100644
--- a/src/ObjectValidation/HostAttribute.cs
+++ b/src/ObjectValidation/HostAttribute.cs
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
+using System.Diagnostics.Contracts;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
@@ -34,6 +35,36 @@ public class HostAttribute() : ValidationAttributeBase()
///
public int PingTimeout { get; set; } = 300;
+ ///
+ /// Use a cache for results?
+ ///
+ public bool UseCache { get; set; } = true;
+
+ ///
+ /// Check if a hostname/IP exists using a cache (may throw; for implementing a cache, this method needs to be overridden)
+ ///
+ /// Hostname
+ /// IP address
+ /// IP status
+ /// If exists
+ protected virtual bool CheckIfExistsCache(string? hostName, IPAddress? ip, out IPStatus status)
+ {
+ if (hostName is not null)
+ {
+ IPHostEntry hostEntry = Dns.GetHostEntry(hostName);
+ status = IPStatus.Unknown;
+ return hostEntry.AddressList.Length > 0 || hostEntry.Aliases.Length > 0;
+ }
+ Contract.Assert(ip is not null);
+ using Ping ping = new();
+ PingReply pong = ping.Send(ip, PingTimeout, RandomNumberGenerator.GetBytes(count: 32), new()
+ {
+ DontFragment = true
+ });
+ status = pong.Status;
+ return pong.Status == IPStatus.Success;
+ }
+
///
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
@@ -47,15 +78,16 @@ public class HostAttribute() : ValidationAttributeBase()
if (!CheckIfExists) return null;
try
{
- Dns.GetHostEntry(str);
- return null;
+ return CheckIfExistsCache(str, ip: null, out _)
+ ? null
+ : this.CreateValidationResult($"Hostname DNS lookup failed", validationContext);
}
catch (Exception ex)
{
return this.CreateValidationResult($"Hostname DNS lookup failed: {ex.Message ?? ex.GetType().ToString()}", validationContext);
}
case UriHostNameType.IPv4:
- if(!IPAddress.TryParse(str, out ip)) return this.CreateValidationResult($"Host IPv4 address parsing failed", validationContext);
+ if (!IPAddress.TryParse(str, out ip)) return this.CreateValidationResult($"Host IPv4 address parsing failed", validationContext);
if (ip.AddressFamily != AddressFamily.InterNetwork) return this.CreateValidationResult($"Detected host IPv4 address parsed to IPv6", validationContext);
break;
case UriHostNameType.IPv6:
@@ -66,18 +98,13 @@ public class HostAttribute() : ValidationAttributeBase()
return this.CreateValidationResult($"Hostname or IP address value invalid ({type})", validationContext);
}
if (!CheckIfExists) return null;
- using Ping ping = new();
try
{
- PingReply pong = ping.Send(ip, PingTimeout, RandomNumberGenerator.GetBytes(count: 32), new()
- {
- DontFragment = true
- });
- return pong.Status == IPStatus.Success
+ return CheckIfExistsCache(hostName: null, ip, out IPStatus status)
? null
- : this.CreateValidationResult($"Ping to {ip} failed: {pong.Status}", validationContext);
+ : this.CreateValidationResult($"Ping to {ip} failed: {status}", validationContext);
}
- catch(Exception ex)
+ catch (Exception ex)
{
return this.CreateValidationResult($"Ping to {ip} failed exceptional: {ex.Message ?? ex.GetType().ToString()}", validationContext);
}
diff --git a/src/ObjectValidation/LuhnChecksum.cs b/src/ObjectValidation/LuhnChecksum.cs
index dbae836..7e79bab 100644
--- a/src/ObjectValidation/LuhnChecksum.cs
+++ b/src/ObjectValidation/LuhnChecksum.cs
@@ -30,7 +30,7 @@ public static bool Validate(string value)
///
/// Normalizing regular expression
///
- [GeneratedRegex(@"[^\d]", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"[^\d]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex Normalizing_Generated();
}
}
diff --git a/src/ObjectValidation/ObjectValidation.csproj b/src/ObjectValidation/ObjectValidation.csproj
index f174134..7795223 100644
--- a/src/ObjectValidation/ObjectValidation.csproj
+++ b/src/ObjectValidation/ObjectValidation.csproj
@@ -22,7 +22,7 @@
LICENSE
True
README.md
- 2.7.0
+ 2.8.0
embedded
true
Debug;Release;Trunk
diff --git a/src/ObjectValidation/SwiftValidation.cs b/src/ObjectValidation/SwiftValidation.cs
index 3cc8e72..d5c64bc 100644
--- a/src/ObjectValidation/SwiftValidation.cs
+++ b/src/ObjectValidation/SwiftValidation.cs
@@ -79,25 +79,25 @@ public static bool ValidateIban(string iban)
///
/// Normalizing regular expression
///
- [GeneratedRegex(@"[\d|A-Z]", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"[\d|A-Z]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex Normalizing_Generated();
///
/// IBAN syntax regular expression ($1 is the country, $2 the checksum, $3 the bank ID and $4 the account ID)
///
- [GeneratedRegex(@"^([A-Z]{2})(\d{2})(\d{8})(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^([A-Z]{2})(\d{2})(\d{8})(\d{10})$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex IbanSyntax_Generated();
///
/// IBAN checksum calculation regular expression
///
- [GeneratedRegex(@"[A-Z]", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"[A-Z]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex IbanChecksum_Generated();
///
/// BIC syntax regular expression
///
- [GeneratedRegex(@"^[A-Z|\d]{4}[A-Z]{2}[A-Z|\d]{2}([A-Z|\d]{3})?$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^[A-Z|\d]{4}[A-Z]{2}[A-Z|\d]{2}([A-Z|\d]{3})?$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex BicSyntax_Generated();
}
}
diff --git a/src/ObjectValidation/XRechnungRouting.cs b/src/ObjectValidation/XRechnungRouting.cs
index 2e12665..51c977d 100644
--- a/src/ObjectValidation/XRechnungRouting.cs
+++ b/src/ObjectValidation/XRechnungRouting.cs
@@ -48,25 +48,25 @@ public static bool Validate(string route)
///
/// Normalizing regular expression
///
- [GeneratedRegex(@"[^\d|A-Z|-]", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"[^\d|A-Z|-]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex Normalizing_Generated();
///
/// Syntax regular expression
///
- [GeneratedRegex(@"^\d{2}(\d(\d{2}(\d{3})?)?)?-[A-Z|\d]{1,30}-\d{2}$", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"^\d{2}(\d(\d{2}(\d{3})?)?)?-[A-Z|\d]{1,30}-\d{2}$", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex Syntax_Generated();
///
/// Checksum calculation regular expression
///
- [GeneratedRegex(@"[A-Z]", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"[A-Z]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex ChecksumCalculation_Generated();
///
/// Checksum normalizing regular expression
///
- [GeneratedRegex(@"[^\d|A-Z]", RegexOptions.Compiled | RegexOptions.Singleline)]
+ [GeneratedRegex(@"[^\d|A-Z]", RegexOptions.Compiled | RegexOptions.Singleline, 3000)]
private static partial Regex ChecksumNormalizing_Generated();
}
}