mirror of
https://gitlab.com/ric_harvey/MailHog.git
synced 2025-02-17 08:15:55 +00:00
More commands, better connection handling/logging
This commit is contained in:
parent
a7c4cca524
commit
fc2097fc33
3 changed files with 83 additions and 61 deletions
13
mailhog/config.go
Normal file
13
mailhog/config.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package mailhog
|
||||||
|
|
||||||
|
func DefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
BindAddr: "0.0.0.0:1025",
|
||||||
|
Hostname: "mailhog.example",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
BindAddr string
|
||||||
|
Hostname string
|
||||||
|
}
|
|
@ -6,13 +6,23 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
"github.com/ian-kent/MailHog/mailhog"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
conn *net.TCPConn
|
conn *net.TCPConn
|
||||||
line string
|
line string
|
||||||
|
conf *mailhog.Config
|
||||||
|
state int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ESTABLISH = iota
|
||||||
|
MAIL
|
||||||
|
RCPT
|
||||||
|
DATA
|
||||||
|
)
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
From string
|
From string
|
||||||
To string
|
To string
|
||||||
|
@ -20,19 +30,20 @@ type Message struct {
|
||||||
Helo string
|
Helo string
|
||||||
}
|
}
|
||||||
|
|
||||||
func StartSession(conn *net.TCPConn) {
|
func StartSession(conn *net.TCPConn, conf *mailhog.Config) {
|
||||||
conv := &Session{conn, ""}
|
conv := &Session{conn, "", conf, ESTABLISH}
|
||||||
log.Printf("Starting session with %s", conn.RemoteAddr())
|
conv.log("Starting session")
|
||||||
conv.Begin()
|
conv.Write("220", "Go-MailHog")
|
||||||
|
conv.Read()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Session) log(message string, args ...interface{}) {
|
func (c *Session) log(message string, args ...interface{}) {
|
||||||
message = strings.Join([]string{"[%s]", message}, " ")
|
message = strings.Join([]string{"[%s, %d]", message}, " ")
|
||||||
args = append([]interface{}{c.conn.RemoteAddr()}, args...)
|
args = append([]interface{}{c.conn.RemoteAddr(), c.state}, args...)
|
||||||
log.Printf(message, args...)
|
log.Printf(message, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Session) Read() {
|
func (c *Session) Read() {
|
||||||
buf := make([]byte, 1024)
|
buf := make([]byte, 1024)
|
||||||
n, err := c.conn.Read(buf)
|
n, err := c.conn.Read(buf)
|
||||||
|
|
||||||
|
@ -45,26 +56,29 @@ func (c Session) Read() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
text := string(buf)
|
text := string(buf[0:n])
|
||||||
c.log("Received %d bytes: %s\n", n, text)
|
c.log("Received %d bytes: '%s'\n", n, text)
|
||||||
|
|
||||||
c.line += text
|
c.line += text
|
||||||
|
|
||||||
c.Parse()
|
c.Parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Session) Parse() {
|
func (c *Session) Parse() {
|
||||||
for strings.Contains(c.line, "\n") {
|
for strings.Contains(c.line, "\n") {
|
||||||
parts := strings.SplitN(c.line, "\n", 2)
|
parts := strings.SplitN(c.line, "\n", 2)
|
||||||
|
if len(parts) == 2 {
|
||||||
c.line = parts[1]
|
c.line = parts[1]
|
||||||
c.log("Parsing string: %s", parts[0])
|
} else {
|
||||||
|
c.line = ""
|
||||||
|
}
|
||||||
c.Process(strings.Trim(parts[0], "\r\n"))
|
c.Process(strings.Trim(parts[0], "\r\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Read()
|
c.Read()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Session) Write(code string, text ...string) {
|
func (c *Session) Write(code string, text ...string) {
|
||||||
if len(text) == 1 {
|
if len(text) == 1 {
|
||||||
c.conn.Write([]byte(code + " " + text[0] + "\n"))
|
c.conn.Write([]byte(code + " " + text[0] + "\n"))
|
||||||
return
|
return
|
||||||
|
@ -75,28 +89,43 @@ func (c Session) Write(code string, text ...string) {
|
||||||
c.conn.Write([]byte(code + " " + text[len(text)] + "\n"))
|
c.conn.Write([]byte(code + " " + text[len(text)] + "\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Session) Process(line string) {
|
func (c *Session) Process(line string) {
|
||||||
c.log("Processing line: %s", line)
|
c.log("Processing line: %s", line)
|
||||||
|
|
||||||
words := strings.Split(line, " ")
|
words := strings.Split(line, " ")
|
||||||
|
command := words[0]
|
||||||
|
c.log("In state %d, got command '%s'", c.state, command)
|
||||||
|
|
||||||
switch words[0] {
|
switch {
|
||||||
|
case command == "RSET":
|
||||||
|
c.log("Got RSET command, switching to ESTABLISH state")
|
||||||
|
c.state = ESTABLISH
|
||||||
|
c.Write("250", "OK")
|
||||||
|
case command == "NOOP":
|
||||||
|
c.log("Got NOOP command")
|
||||||
|
c.Write("250", "OK")
|
||||||
|
case command == "QUIT":
|
||||||
|
c.log("Got QUIT command")
|
||||||
|
c.Write("221", "OK")
|
||||||
|
case c.state == ESTABLISH:
|
||||||
|
switch command {
|
||||||
case "HELO":
|
case "HELO":
|
||||||
c.log("Got HELO command")
|
c.log("Got HELO command, switching to MAIL state")
|
||||||
c.Write("250", "HELO " + "my.hostname")
|
c.state = MAIL
|
||||||
|
c.Write("250", "HELO " + c.conf.Hostname)
|
||||||
case "EHLO":
|
case "EHLO":
|
||||||
c.log("Got EHLO command")
|
c.log("Got EHLO command, switching to MAIL state")
|
||||||
c.Write("250", "HELO " + "my.hostname")
|
c.state = MAIL
|
||||||
|
c.Write("250", "EHLO " + c.conf.Hostname)
|
||||||
default:
|
default:
|
||||||
c.log("Got unknown command: '%s'", words[0])
|
c.log("Got unknown command for ESTABLISH state: '%s'", command)
|
||||||
|
}
|
||||||
|
case c.state == MAIL:
|
||||||
|
switch command {
|
||||||
|
case "MAIL":
|
||||||
|
c.log("Got MAIL command")
|
||||||
|
default:
|
||||||
|
c.log("Got unknown command for MAIL state: '%s'", command)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Session) Begin() {
|
|
||||||
_, err := c.conn.Write([]byte("220 Go-MailHog\n"))
|
|
||||||
if err != nil {
|
|
||||||
c.log("Failed writing to socket: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.Read()
|
|
||||||
}
|
}
|
||||||
|
|
36
main.go
36
main.go
|
@ -4,13 +4,11 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"github.com/ian-kent/MailHog/mailhog"
|
||||||
"github.com/ian-kent/MailHog/mailhog/smtp"
|
"github.com/ian-kent/MailHog/mailhog/smtp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var conf = map[string]string {
|
var conf *mailhog.Config
|
||||||
"BIND_ADDRESS": "0.0.0.0:1025",
|
|
||||||
"HOSTNAME": "mailhog.example",
|
|
||||||
}
|
|
||||||
|
|
||||||
func config() {
|
func config() {
|
||||||
var listen, hostname string
|
var listen, hostname string
|
||||||
|
@ -20,14 +18,16 @@ func config() {
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
conf["BIND_ADDRESS"] = listen
|
conf = &mailhog.Config{
|
||||||
conf["HOSTNAME"] = hostname
|
BindAddr: listen,
|
||||||
|
Hostname: hostname,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
config()
|
config()
|
||||||
|
|
||||||
ln := listen(conf["BIND_ADDRESS"])
|
ln := listen(conf.BindAddr)
|
||||||
defer ln.Close()
|
defer ln.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -38,7 +38,7 @@ func main() {
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
go smtp.StartSession(conn.(*net.TCPConn))
|
go smtp.StartSession(conn.(*net.TCPConn), conf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,23 +50,3 @@ func listen(bind string) (*net.TCPListener) {
|
||||||
}
|
}
|
||||||
return ln.(*net.TCPListener)
|
return ln.(*net.TCPListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
func accept(conn net.Conn) {
|
|
||||||
buf := make([]byte, 1024)
|
|
||||||
n, err := conn.Read(buf)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error reading from socket: %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Received %s bytes: %s\n", n, string(buf))
|
|
||||||
|
|
||||||
_, err = conn.Write(buf)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error writing to socket: %s\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("Reply sent\n")
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue