I/O-free SMTP client library written in Rust, based on io-socket
This library implements SMTP as I/O-agnostic coroutines — no sockets, no async runtime, no std required.
| Module | What it covers |
|---|---|
login |
LOGIN — legacy de-facto AUTH mechanism (no RFC) |
| 1870 | SIZE — maximum message size declaration |
| 3207 | STARTTLS — upgrade a plain connection to TLS |
| 3461 | DSN — RET, ENVID, NOTIFY, ORCPT ESMTP parameters for MAIL FROM / RCPT TO |
| 3463 | Enhanced status codes — EnhancedStatusCode type |
| 4616 | PLAIN — SASL PLAIN authentication mechanism |
| 4954 | AUTH — SASL exchange protocol |
| 5321 | SMTP — greeting, EHLO, HELO, MAIL FROM, RCPT TO, DATA, NOOP, RSET, QUIT |
| 7628 | OAUTHBEARER — OAuth 2.0 bearer token SASL mechanism |
| 7677 | SCRAM-SHA-256 — SASL SCRAM-SHA-256 mechanism (feature scram) |
use std::net::TcpStream;
use io_smtp::rfc5321::{
ehlo::{SmtpEhlo, SmtpEhloResult},
greeting::{GetSmtpGreeting, GetSmtpGreetingResult},
types::domain::Domain,
};
use io_socket::runtimes::std_stream::handle;
let mut stream = TcpStream::connect("smtp.example.com:25").unwrap();
let domain = Domain::parse(b"localhost").unwrap();
// Read greeting
let mut coroutine = GetSmtpGreeting::new();
let mut arg = None;
loop {
match coroutine.resume(arg.take()) {
GetSmtpGreetingResult::Ok { .. } => break,
GetSmtpGreetingResult::Io { input } => arg = Some(handle(&mut stream, input).unwrap()),
GetSmtpGreetingResult::Err { err } => panic!("{err}"),
}
}
// Send EHLO
let mut coroutine = SmtpEhlo::new(domain.into());
let mut arg = None;
let capabilities = loop {
match coroutine.resume(arg.take()) {
SmtpEhloResult::Ok { capabilities } => break capabilities,
SmtpEhloResult::Io { input } => arg = Some(handle(&mut stream, input).unwrap()),
SmtpEhloResult::Err { err } => panic!("{err}"),
}
};
println!("Server capabilities: {capabilities:?}");use io_smtp::send::{SmtpMessageSend, SmtpMessageSendResult};
use io_smtp::rfc5321::types::{forward_path::ForwardPath, reverse_path::ReversePath};
use io_socket::runtimes::tokio_stream::handle;
use tokio::net::TcpStream;
let mut stream = TcpStream::connect("smtp.example.com:25").await.unwrap();
let from: ReversePath = "<sender@example.com>".parse().unwrap();
let to: ForwardPath = "<recipient@example.com>".parse().unwrap();
let message = b"From: sender@example.com\r\nTo: recipient@example.com\r\nSubject: Test\r\n\r\nHello!".to_vec();
let mut coroutine = SmtpMessageSend::new(from, [to], message);
let mut arg = None;
loop {
match coroutine.resume(arg.take()) {
SmtpMessageSendResult::Ok => break,
SmtpMessageSendResult::Io { input } => arg = Some(handle(&mut stream, input).await.unwrap()),
SmtpMessageSendResult::Err { err } => panic!("{err}"),
}
}See complete examples at ./examples.
Have a look at projects built on top of this library:
- himalaya: CLI to manage emails
This project is licensed under either of:
at your option.
- Chat on Matrix
- News on Mastodon or RSS
- Mail at pimalaya.org@posteo.net
Special thanks to the NLnet foundation and the European Commission that have been financially supporting the project for years:
- 2022: NGI Assure
- 2023: NGI Zero Entrust
- 2024: NGI Zero Core (still ongoing in 2026)
If you appreciate the project, feel free to donate using one of the following providers:
