Skip to content

Alos-no/Alos.Email.Validation

Repository files navigation

Alos.Email.Validation

A comprehensive .NET library for email validation and anti-abuse protection. Includes provider-specific normalization, relay service blocking, disposable domain detection, and MX record verification.

NuGet

Features

  • Email Normalization - Provider-specific rules for Gmail, Outlook, ProtonMail, iCloud, and more
  • Relay Service Detection - Block Apple Hide My Email, Firefox Relay, DuckDuckGo, SimpleLogin, etc.
  • Disposable Domain Detection - 38,000+ domains from disposable-email-domains
  • MX Record Verification - DNS lookup to verify domain can receive email
  • Custom Blocklist/Allowlist - Override built-in lists with your own domains
  • Auto-Update - Background service to keep blocklists current
  • Thread-Safe - All operations are safe for concurrent access

Installation

dotnet add package Alos.Email.Validation

Quick Start

// Program.cs
builder.Services.AddEmailValidation(builder.Configuration);

// In your service
public class MyService
{
    private readonly IEmailValidationService _emailValidation;

    public MyService(IEmailValidationService emailValidation)
    {
        _emailValidation = emailValidation;
    }

    public async Task<bool> RegisterUserAsync(string email)
    {
        var result = await _emailValidation.ValidateAsync(email);

        if (!result.IsValid)
        {
            // Handle validation error
            switch (result.Error)
            {
                case EmailValidationError.InvalidFormat:
                    // Email format is invalid
                    break;
                case EmailValidationError.RelayService:
                    // Email uses a relay service (Apple, Firefox, etc.)
                    break;
                case EmailValidationError.Disposable:
                    // Email uses a disposable domain
                    break;
                case EmailValidationError.InvalidDomain:
                    // Domain has no MX records
                    break;
            }
            return false;
        }

        // Normalize email before storing (prevents alias duplicates)
        var normalizedEmail = _emailValidation.Normalize(email);

        // Continue with registration...
        return true;
    }
}

Configuration

Basic Setup

// Using IConfiguration (recommended)
builder.Services.AddEmailValidation(builder.Configuration);

// Or programmatic configuration
builder.Services.AddEmailValidation(options =>
{
    options.CustomBlocklist = ["spammer.com", "badactor.net"];
    options.CustomAllowlist = ["legitimate-service.com"];
});

Configuration Options

{
  "EmailValidation": {
    "EnableAutoUpdate": true,
    "UpdateInterval": "1.00:00:00",
    "CustomBlocklist": ["spammer.com", "badactor.net"],
    "CustomAllowlist": ["legitimate-service.com"],
    "CustomBlocklistUrls": ["https://example.com/my-blocklist.txt"],
    "CustomAllowlistUrls": ["https://example.com/my-allowlist.txt"]
  }
}
Option Type Default Description
BlocklistDirectory string? Cross-platform default Path to blocklist files. Defaults to LocalApplicationData/alos/email-blocklists.
EnableAutoUpdate bool false Enable background blocklist updates from GitHub.
UpdateInterval TimeSpan 24:00:00 Interval between update checks.
BlocklistUrl string GitHub URL URL for primary blocklist download.
AllowlistUrl string GitHub URL URL for primary allowlist download.
CustomBlocklistUrls List<string> [] Additional URLs for custom blocklists (fetched by auto-updater).
CustomAllowlistUrls List<string> [] Additional URLs for custom allowlists (fetched by auto-updater).
CustomBlocklist List<string> [] Inline domains to add to blocklist.
CustomAllowlist List<string> [] Inline domains to add to allowlist (overrides blocklist).

Auto-Update Setup

Enable automatic blocklist updates from GitHub:

builder.Services.AddEmailValidationWithAutoUpdate(builder.Configuration);

This registers a background service that periodically downloads the latest blocklists. Requires BlocklistDirectory to be set and EnableAutoUpdate to be true.

Email Normalization

The library normalizes email addresses using provider-specific rules to prevent alias-based duplicate accounts:

Provider Normalization Example
Gmail Remove dots and +suffix j.doe+spam@gmail.comjdoe@gmail.com
ProtonMail Remove dots, hyphens, underscores, and +suffix j.doe-test_name+spam@proton.mejdoetestname@proton.me
Yahoo Remove -suffix (hyphen-based aliases, no plus support) john-shopping@yahoo.comjohn@yahoo.com
Fastmail Remove +suffix; subdomain addressing alias@user.fastmail.comuser@fastmail.com
Outlook/Hotmail Remove +suffix only john+tag@outlook.comjohn@outlook.com
iCloud Remove +suffix only user+tag@icloud.comuser@icloud.com
Yandex Remove +suffix only user+tag@yandex.ruuser@yandex.ru
GMX/mail.com Remove +suffix only user+tag@gmx.comuser@gmx.com
Tuta No normalization (no alias support) user+tag@tuta.comuser+tag@tuta.com
AOL No normalization (unclear plus support) user+tag@aol.comuser+tag@aol.com
QQ Mail No normalization (manual aliases only) user+tag@qq.comuser+tag@qq.com
NetEase No normalization (manual aliases only) user+tag@163.comuser+tag@163.com
Sina/Sohu/Aliyun No normalization user+tag@sina.comuser+tag@sina.com
Other Lowercase only (default) User+Spam@Example.comuser+spam@example.com

Provider-Specific Notes:

  • ProtonMail ignores dots, hyphens, and underscores as a security measure against impersonation attacks
  • Yahoo uses hyphen-based aliases (nickname-keyword@yahoo.com), not plus addressing
  • Fastmail supports both plus addressing (user+tag@fastmail.com) and subdomain addressing (anything@user.fastmail.com)
  • Plus addressing providers: Yandex, GMX, mail.com (200+ domains), Runbox, Mailfence, Rambler, Rackspace
  • No plus addressing: Tuta/Tutanota, AOL/AIM (unclear support)
  • Chinese providers: QQ Mail (qq.com, foxmail.com), NetEase (163.com, 126.com, yeah.net), Sina, Sohu, Aliyun use manual alias systems and do not support plus addressing
  • Unknown providers: By default, plus suffix is preserved (conservative). Use stripPlusForUnknownProviders: true for aggressive anti-abuse mode.
var normalized = _emailValidation.Normalize("J.Doe+Spam@GMAIL.com");
// Returns: "jdoe@gmail.com"

var protonNormalized = _emailValidation.Normalize("J.Doe-Test_Name+Tag@Proton.Me");
// Returns: "jdoetestname@proton.me"

var yahooNormalized = _emailValidation.Normalize("John-Shopping@Yahoo.com");
// Returns: "john@yahoo.com"

var fastmailSubdomain = _emailValidation.Normalize("Alias@User.Fastmail.Com");
// Returns: "user@fastmail.com"

var tutaNormalized = _emailValidation.Normalize("User+Tag@Tuta.com");
// Returns: "user+tag@tuta.com" (Tuta doesn't support plus addressing)

// Chinese providers: plus suffix preserved (no subaddressing support)
var qqNormalized = _emailValidation.Normalize("User+Tag@QQ.com");
// Returns: "user+tag@qq.com"

var neteaseNormalized = _emailValidation.Normalize("User+Tag@163.com");
// Returns: "user+tag@163.com"

// Unknown providers: default (conservative) preserves plus suffix
var unknownDefault = _emailValidation.Normalize("User+Spam@Company.com");
// Returns: "user+spam@company.com"

// Unknown providers: aggressive mode strips plus suffix
var unknownAggressive = _emailValidation.Normalize("User+Spam@Company.com", stripPlusForUnknownProviders: true);
// Returns: "user@company.com"

Relay Service Detection

Blocks email relay/forwarding services that enable unlimited alias creation:

  • Apple - Hide My Email (privaterelay.appleid.com)
  • Mozilla - Firefox Relay (mozmail.com)
  • DuckDuckGo - Email Protection (duck.com)
  • SimpleLogin - Proton-owned (simplelogin.com, slmails.com, etc.)
  • Proton Pass - (passmail.net)
  • addy.io - AnonAddy (addy.io, anonaddy.com, etc.)
  • Fastmail - Masked Email (fastmail.com)
  • Cloaked - (cloaked.id, myclkd.email)
  • Burner Mail - (nicoric.com)
  • GitHub - (users.noreply.github.com)

Includes wildcard matching for subdomain-based services (e.g., alias@username.anonaddy.com).

bool isRelay = _emailValidation.IsRelayService("user@duck.com");
// Returns: true

Disposable Domain Detection

Detects 38,000+ disposable/temporary email domains using the community-maintained disposable-email-domains list.

bool isDisposable = _emailValidation.IsDisposable("user@mailinator.com");
// Returns: true

Custom Lists

Override built-in detection with custom blocklists and allowlists:

builder.Services.AddEmailValidation(options =>
{
    // Block additional domains
    options.CustomBlocklist = ["internal-temp.com", "competitor.com"];

    // Allow domains that appear on blocklist but are legitimate for your use case
    options.CustomAllowlist = ["temp-looks-suspicious-but-legit.com"];
});

Precedence: Allowlist always takes precedence over blocklist.

URL-Based Custom Lists

For organization-specific or third-party blocklists that you want to keep synchronized, use URL-based configuration:

builder.Services.AddEmailValidation(options =>
{
    options.EnableAutoUpdate = true;

    // Add custom blocklist URLs (fetched alongside primary lists)
    options.CustomBlocklistUrls = [
        "https://example.com/corporate-blocklist.txt",
        "https://internal.company.com/banned-domains.conf"
    ];

    // Add custom allowlist URLs (override false positives)
    options.CustomAllowlistUrls = [
        "https://example.com/trusted-partners.txt"
    ];
});

The auto-updater downloads all URLs and saves them to BlocklistDirectory with naming conventions:

  • custom_blocklist_000.conf, custom_blocklist_001.conf, etc.
  • custom_allowlist_000.conf, custom_allowlist_001.conf, etc.

Local File-Based Custom Lists

You can also place custom list files directly in the BlocklistDirectory. Use the naming convention:

  • custom_blocklist_*.conf - Custom blocklist files
  • custom_allowlist_*.conf - Custom allowlist files

File format (one domain per line, # for comments):

# Corporate blocklist - Updated 2024-01
spammer.com
badactor.net
another-blocked.com

All files matching the naming convention are loaded and merged at startup and on hot reload.

Hot Reload

Blocklists can be reloaded at runtime without restarting the application:

// Inject the checker
private readonly IDisposableEmailDomainChecker _checker;

// Reload from disk (thread-safe)
_checker.ReloadFromDisk("/var/lib/alos/email-blocklists");

The auto-update background service calls this automatically after downloading new lists.

API Reference

IEmailValidationService

public interface IEmailValidationService
{
    // Full validation pipeline (format, relay, disposable, MX)
    Task<EmailValidationResult> ValidateAsync(string email, CancellationToken ct = default);

    // Check if domain is a relay service (synchronous)
    bool IsRelayService(string email);

    // Check if domain is disposable (synchronous)
    bool IsDisposable(string email);

    // Normalize email using provider-specific rules
    string Normalize(string email);
}

EmailValidationResult

public sealed record EmailValidationResult
{
    public bool IsValid { get; init; }
    public EmailValidationError? Error { get; init; }
}

public enum EmailValidationError
{
    InvalidFormat,   // RFC 5322 format violation
    RelayService,    // Apple, Firefox, DuckDuckGo, etc.
    Disposable,      // Mailinator, Guerrilla Mail, etc.
    InvalidDomain    // No MX records
}

Static Helpers

For scenarios where DI is not available:

// Normalize without DI
string normalized = EmailNormalizer.Normalize("j.doe+spam@gmail.com");
string? domain = EmailNormalizer.ExtractDomain("user@example.com");

// Check relay service without DI
bool isRelay = RelayServiceBlocklist.IsRelayService("duck.com");

Framework Support

  • .NET 8.0
  • .NET 9.0
  • .NET 10.0

Dependencies

  • DnsClient - MX record lookups
  • Microsoft.Extensions.* - Configuration, DI, Hosting, Logging

Thread Safety

All services are registered as singletons and are thread-safe:

  • IEmailValidationService - Thread-safe validation
  • IDisposableEmailDomainChecker - Uses ReaderWriterLockSlim for concurrent reads during hot reload
  • IMxRecordValidator - Thread-safe DNS lookups

Testing

The library includes comprehensive test coverage:

  • 475+ unit tests - Core validation logic, provider-specific normalization
  • 80+ integration tests - File I/O, DI registration, hot reload, thread safety
# Run all tests
dotnet test

# Run unit tests only
dotnet test tests/Alos.Email.Validation.Tests/

# Run integration tests only
dotnet test tests/Alos.Email.Validation.IntegrationTests/

License

Apache License 2.0 - See LICENSE file for details.

Credits

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages