A professional, high-performance SMTP server library for .NET with minimal dependencies. Features include authentication, TLS/SSL support, rate limiting, and extensible architecture.
$ dotnet add package ZetianA professional, high-performance SMTP server library for .NET with minimal dependencies. Build custom SMTP servers with ease using a fluent API and extensible architecture.
dotnet add package Zetian
using Zetian.Server;
// Create a basic SMTP server
using var server = SmtpServerBuilder.CreateBasic();
server.MessageReceived += (sender, e) =>
{
Console.WriteLine($"From: {e.Message.From}");
Console.WriteLine($"Subject: {e.Message.Subject}");
};
await server.StartAsync();
Console.WriteLine($"Server running on {server.Endpoint}");
using Zetian.Server;
using var server = new SmtpServerBuilder()
.Port(587)
.RequireAuthentication()
.AllowPlainTextAuthentication()
.SimpleAuthentication("user", "password")
.Build();
await server.StartAsync();
using Zetian.Server;
using var server = new SmtpServerBuilder()
.Port(587)
.Certificate("certificate.pfx", "password")
.RequireSecureConnection()
.Build();
await server.StartAsync();
using Zetian.Models;
using Zetian.Server;
var server = new SmtpServerBuilder()
.Port(587)
.ServerName("My SMTP Server")
.MaxMessageSizeMB(25)
.MaxConnections(50)
.RequireAuthentication()
.AuthenticationHandler(async (user, pass) =>
{
// Custom authentication logic
return await ValidateUser(user, pass)
? AuthenticationResult.Succeed(user)
: AuthenticationResult.Fail();
})
// New features
.EnableSmtpUtf8()
.WithFileMessageStore(@"C:\smtp_messages") // Protocol-level storage
.WithSenderDomainWhitelist("trusted1.com", "trusted2.com") // Protocol-level filtering
.WithSenderDomainBlacklist("spam.com", "junk.org")
.Build();// Save messages to file system with date structure (Protocol-level)
using Zetian.Server;
using Zetian.Extensions;
var server = new SmtpServerBuilder()
.Port(25)
.WithFileMessageStore(@"C:\mail", createDateFolders: true) // Saves at protocol level
.Build();
// Or use the extension method (Event-based)
server.SaveMessagesToDirectory(@"C:\mail"); // Saves via event handler
// Or use custom message store
using Zetian.Server;
using Zetian.Abstractions;
public class MongoMessageStore : IMessageStore
{
public async Task<bool> SaveAsync(ISmtpSession session, ISmtpMessage message, CancellationToken cancellationToken)
{
// Save to MongoDB
return true;
}
}
var server = new SmtpServerBuilder()
.MessageStore(new MongoMessageStore())
.Build();// Protocol-level domain filtering (rejects at SMTP command level)
using Zetian.Server;
using Zetian.Extensions;
var server = new SmtpServerBuilder()
.WithSenderDomainWhitelist("company.com", "partner.com")
.WithRecipientDomainWhitelist("mydomain.com")
.WithSenderDomainBlacklist("spam.com")
.Build();
// Event-based domain filtering (filters after message received)
server.AddAllowedDomains("example.com"); // Extension method
server.AddSpamFilter(new[] { "spam.com" }); // Extension method
// Custom mailbox filter
using Zetian.Server;
using Zetian.Abstractions;
public class CustomMailboxFilter : IMailboxFilter
{
public async Task<bool> CanAcceptFromAsync(ISmtpSession session, string from, long size, CancellationToken cancellationToken)
{
// Custom validation logic
return !await IsBlacklisted(from);
}
public async Task<bool> CanDeliverToAsync(ISmtpSession session, string to, string from, CancellationToken cancellationToken)
{
// Check if recipient exists
return await UserExists(to);
}
}
var server = new SmtpServerBuilder()
.MailboxFilter(new CustomMailboxFilter())
.Build();// Combine multiple filters with AND logic
using Zetian.Enums;
using Zetian.Server;
using Zetian.Storage;
var compositeFilter = new CompositeMailboxFilter(CompositeMode.All)
.AddFilter(new DomainMailboxFilter().AllowFromDomains("trusted.com"))
.AddFilter(new CustomSpamFilter())
.AddFilter(new RateLimitFilter());
var server = new SmtpServerBuilder()
.MailboxFilter(compositeFilter)
.Build();server.MessageReceived += async (sender, e) =>
{
var message = e.Message;
// Access message details
Console.WriteLine($"From: {message.From?.Address}");
Console.WriteLine($"To: {string.Join(", ", message.Recipients)}");
Console.WriteLine($"Subject: {message.Subject}");
Console.WriteLine($"Size: {message.Size} bytes");
// Get message content
var textBody = message.TextBody;
var htmlBody = message.HtmlBody;
// Save to file
await message.SaveToFileAsync($"{message.Id}.eml");
// Reject if needed
if (IsSpam(message))
{
e.Cancel = true;
e.Response = new SmtpResponse(550, "Rejected as spam");
}
};Zetian provides two different filtering approaches:
Protocol-Level Filtering (via Builder) - Rejects at SMTP command level
WithSenderDomainWhitelist, WithFileMessageStore etc.Event-Based Filtering (via Extensions) - Filters after message received
AddAllowedDomains, SaveMessagesToDirectory etc.Choose based on your needs:
server.AddRateLimiting(RateLimitConfiguration.PerHour(100));server.AddSpamFilter(new[] { "spam.com", "junk.org" });
server.AddSizeFilter(10 * 1024 * 1024); // 10MB maxserver.AddAllowedDomains("example.com", "mycompany.com");server.SaveMessagesToDirectory(@"C:\smtp_messages");using Zetian.Server;
var server = new SmtpServerBuilder()
.WithFileMessageStore(@"C:\mail") // Saves at protocol level
.WithSenderDomainWhitelist("trusted.com") // Rejects at MAIL FROM
.WithRecipientDomainWhitelist("mydomain.com") // Rejects at RCPT TO
.Build();Best Practices:
Report Security Issues: taiizor@vegalya.com
MIT License - see LICENSE
Built with ❤️ for the .NET community