Go-Based SMTP Relay Server
A custom-built SMTP (Simple Mail Transfer Protocol) server developed from scratch in Go to explore low-level TCP networking, state machine logic, and secure SMTP relaying.
Project Overview
The server acts as a Mail Transfer Agent (MTA). It listens for local TCP connections, parses raw SMTP commands, maintains a stateful session, and relays the received data to a real-world inbox using Gmail's infrastructure.
Engineering & System Design
1. Stateful Protocol Management (Finite State Machine)
SMTP is fundamentally stateful, requiring the server to remember the current context to validate incoming commands.
- Pattern Used: Finite State Machine (FSM)
- Initial State: The server starts in an
INITstate waiting for a handshake. - Transitions: It only allows transitions to
MAILafter a successfulHELO. - Security: This validates the sequence of all incoming TCP packets to prevent protocol violations.
2. Concurrency: Per-Connection Goroutines
The server leverages Go's lightweight thread model to handle multiple sessions simultaneously.
- Non-blocking I/O: When
Accept()receives a TCP connection, it hands off the socket to a new goroutine. - Isolation: This ensures that a slow relay process to Gmail does not block other local clients from connecting.
3. Buffer-Oriented Stream Processing
Instead of waiting for an entire email to arrive, the server processes a continuous byte stream.
- Stream Scanning: Uses
bufio.NewScannerto split input at\r\n(CRLF). - Memory Management: During the
DATAphase, it utilizesstrings.Builderto accumulate message data efficiently, avoiding repeated string reallocation.
Interactive System Flow
Technical Specifications
Network Configuration
| Setting | Value |
|---|---|
| Port | 2525 (Bypasses ISP restrictions on Port 25) |
| Host | 127.0.0.1 (Localhost) |
| Protocol | TCP |
| Security | STARTTLS via Go's net/smtp for Gmail relay |
Supported SMTP Commands (RFC 5321)
| Command | Purpose | Technical Trigger |
|---|---|---|
| HELO / EHLO | Client Identification | Moves FSM to StateMailFrom; resets session data. |
| MAIL FROM: | Specify Sender | Identifies the "Envelope Sender". Transitions to StateRcptTo. |
| RCPT TO: | Specify Recipient | Appends email address to the recipient slice. |
| DATA | Initialize Payload | Switches to StateData; sends 354 code. |
| . (Single Dot) | End-of-Message | Triggers the relayToGmail function to begin relay. |
| QUIT | Terminate Session | Sends 221 and closes the TCP socket. |
Project Architecture
graph TD
A[SMTP Client] -->|TCP Connection| B[SMTP Server]
B -->|Goroutine per Conneciton| C{FSM Validator}
C -->|Command OK| D[Buffer Stream Processing]
C -->|Invalid Sequence| E[Error Response]
D -->|End of DATA| F[Relay to Gmail SMTP]
F -->|Auth & STARTTLS| G[Gmail Infrastructure]
G -->|Delivery| H[Recipient Inbox]
Built With
- Go (Golang)
- net package (TCP Sockets)
- net/smtp (Relay client)
- bufio & strings (Stream processing)