Wow it forwards mail :D

This commit is contained in:
Gnarwhal 2024-10-02 00:28:14 +00:00
parent 49568598ed
commit dafe3096ca
Signed by: Gnarwhal
GPG key ID: 0989A73D8C421174
5 changed files with 136 additions and 53 deletions

View file

@ -25,7 +25,11 @@ import (
) )
func main() { func main() {
err := smtp.Run(":4650", false) err := smtp.Run(
"localhost",
"4650",
false,
)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View file

@ -25,10 +25,10 @@ import (
type Command struct { type Command struct {
name string name string
Exec func(SMTPSession, string)(bool, error) Exec func(*SMTPSession, string)(bool, error)
} }
func (self Command) Name() string { func (self Command) GetName() string {
return self.name return self.name
} }
@ -36,9 +36,12 @@ func (self Command) Check(message string) bool {
return strings.HasPrefix(message, self.name) return strings.HasPrefix(message, self.name)
} }
func Greet(smtp_session SMTPSession) error { func Greet(smtp_session *SMTPSession) error {
err := smtp_session.Write( err := smtp_session.Write(
"220 localhost ESMTPSession diodemail -- Service ready" + "\n", fmt.Sprintf(
"220 %v ESMTP diodemail -- Service ready" + "\n",
smtp_session.GetHost(),
),
) )
if err != nil { if err != nil {
return err return err
@ -47,93 +50,111 @@ func Greet(smtp_session SMTPSession) error {
return nil return nil
} }
func Helo(smtp_session SMTPSession, message string) (bool, error) { func Helo(smtp_session *SMTPSession, message string) (bool, error) {
err := smtp_session.Write( err := smtp_session.Write(
fmt.Sprintf( fmt.Sprintf(
"250 %v is shy" + "\r\n", "250 %v is shy" + "\r\n",
smtp_session.GetAddress(), smtp_session.GetHost(),
), ),
) )
if err != nil { if err != nil {
return false, err return true, err
} }
return false, nil return false, nil
} }
func Ehlo(smtp_session SMTPSession, message string) (bool, error) { func Ehlo(smtp_session *SMTPSession, message string) (bool, error) {
err := smtp_session.Write( err := smtp_session.Write(
fmt.Sprintf( fmt.Sprintf(
"250 %v is shy" + "\r\n", "250 %v is shy" + "\r\n",
smtp_session.GetAddress(), smtp_session.GetHost(),
), ),
) )
if err != nil { if err != nil {
return false, err return true, err
} }
return false, nil return false, nil
} }
func MailFrom(smtp_session SMTPSession, message string) (bool, error) { func MailFrom(smtp_session *SMTPSession, message string) (bool, error) {
match := ReversePath.FindStringSubmatch(message) match := ReversePath.FindStringSubmatch(message)
if match == nil { if match == nil {
smtp_session.Write( err := smtp_session.Write(
"501 Could not parse reverse-path", "501 Could not parse reverse-path\n",
) )
if err != nil {
return true, err
}
} else if len(match) > 1 { } else if len(match) > 1 {
smtp_session.MailFrom(match[1]) smtp_session.MailFrom(match[1])
err := smtp_session.Write( err := smtp_session.Write(
"250 OK\n", "250 OK\n",
) )
if err != nil { if err != nil {
return false, err return true, err
} }
} }
return false, nil return false, nil
} }
func RcptTo(smtp_session SMTPSession, message string) (bool, error) { func RcptTo(smtp_session *SMTPSession, message string) (bool, error) {
match := ForwardPath.FindStringSubmatch(message) match := ForwardPath.FindStringSubmatch(message)
if match == nil { if match == nil {
smtp_session.Write( err := smtp_session.Write(
"501 Could not parse forward-path", "501 Could not parse forward-path\n",
) )
if err != nil {
return true, err
}
} else { } else {
smtp_session.AddRecipient(match[1]) smtp_session.AddRecipient(match[1])
err := smtp_session.Write( err := smtp_session.Write(
"250 OK\n", "250 OK\n",
) )
if err != nil { if err != nil {
return false, err return true, err
} }
} }
return false, nil return false, nil
} }
func Data(smtp_session SMTPSession, message string) (bool, error) { func Data(smtp_session *SMTPSession, message string) (bool, error) {
err := smtp_session.Write( err := smtp_session.Write(
"354 Start Input\n", "354 Start Input\n",
) )
if err != nil { if err != nil {
return false, err return true, err
} }
data, err := smtp_session.Read("\r\n.\r\n") data, err := smtp_session.Read("\r\n.\r\n")
smtp_session.SetMailBuffer(data) if err != nil {
smtp_session.Write(
"550 Action not taken\n",
)
return false, err
}
err = smtp_session.SendMail(data)
if err != nil {
smtp_session.Write(
"550 Action not taken\n",
)
return false, err
}
smtp_session.Reset()
err = smtp_session.Write( err = smtp_session.Write(
"250 OK\n", "250 OK\n",
) )
smtp_session.Reset()
if err != nil { if err != nil {
return false, err return true, err
} }
return false, nil return false, nil
} }
func Quit(smtp_session SMTPSession, message string) (bool, error) { func Quit(smtp_session *SMTPSession, message string) (bool, error) {
err := smtp_session.Write( err := smtp_session.Write(
"221 Goodbye :)\n", "221 Goodbye :)\n",
) )
@ -144,24 +165,24 @@ func Quit(smtp_session SMTPSession, message string) (bool, error) {
return true, nil return true, nil
} }
func Noop(smtp_session SMTPSession, message string) (bool, error) { func Noop(smtp_session *SMTPSession, message string) (bool, error) {
err := smtp_session.Write( err := smtp_session.Write(
"250 OK\n", "250 OK\n",
) )
if err != nil { if err != nil {
return false, err return true, err
} }
return false, nil return false, nil
} }
func Rset(smtp_session SMTPSession, message string) (bool, error) { func Rset(smtp_session *SMTPSession, message string) (bool, error) {
smtp_session.Reset()
err := smtp_session.Write( err := smtp_session.Write(
"250 Reset\n", "250 Reset\n",
) )
smtp_session.Reset()
if err != nil { if err != nil {
return false, err return true, err
} }
return false, nil return false, nil

View file

@ -25,6 +25,7 @@ import (
var ReversePath = regexp.MustCompile(fmt.Sprintf("(?:%v)|<>", path)) var ReversePath = regexp.MustCompile(fmt.Sprintf("(?:%v)|<>", path))
var ForwardPath = regexp.MustCompile(path) var ForwardPath = regexp.MustCompile(path)
var Domain = regexp.MustCompile("\\w+@(\\w+(?:\\.\\w+)*)")
// https://datatracker.ietf.org/doc/html/rfc5321#page-41 // https://datatracker.ietf.org/doc/html/rfc5321#page-41
// Is this...legal, m'lord? (no, but ¯\_(ツ)_/¯) // Is this...legal, m'lord? (no, but ¯\_(ツ)_/¯)

View file

@ -19,6 +19,7 @@
package smtp package smtp
import ( import (
"fmt"
"net" "net"
"os" "os"
@ -30,14 +31,14 @@ type PlainListener struct {
listener net.Listener listener net.Listener
} }
func handle(connection net.Conn) { func handle(connection net.Conn, host string) {
log.Info().Msgf( log.Info().Msgf(
"New connection %v. Starting session.", "New connection %v. Starting session.",
connection.RemoteAddr(), connection.RemoteAddr(),
) )
defer connection.Close() defer connection.Close()
session := MakeSMTPSession(connection) session := MakeSMTPSession(connection, host)
err := session.Run() err := session.Run()
if err != nil { if err != nil {
log.Error().Msgf( log.Error().Msgf(
@ -53,7 +54,7 @@ func handle(connection net.Conn) {
} }
} }
func Run(host string, implicit_tls bool) error { func Run(host string, port string, implicit_tls bool) error {
log.Logger = zerolog. log.Logger = zerolog.
New(zerolog.ConsoleWriter{Out: os.Stderr}). New(zerolog.ConsoleWriter{Out: os.Stderr}).
With(). With().
@ -61,18 +62,19 @@ func Run(host string, implicit_tls bool) error {
Logger(). Logger().
Level(zerolog.TraceLevel) Level(zerolog.TraceLevel)
listener, err := net.Listen("tcp", host) listener, err := net.Listen("tcp", fmt.Sprintf("%v:%v", host, port))
if err != nil { if err != nil {
return err return err
} }
log.Info().Msgf("Server started on port %v for host %v", port, host)
for { for {
connection, err := listener.Accept() connection, err := listener.Accept()
if err != nil { if err != nil {
return err return err
} }
go handle(connection) go handle(connection, host)
} }
} }

View file

@ -20,30 +20,32 @@ package smtp
import ( import (
"net" "net"
"net/smtp"
"fmt" "fmt"
"strings" "strings"
"crypto/tls"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
type SMTPSession struct { type SMTPSession struct {
connection net.Conn connection net.Conn
host string
ReversePathBuffer *string ReversePathBuffer *string
ForwardPathBuffer []string ForwardPathBuffer []string
MailBuffer *string
} }
func MakeSMTPSession(connection net.Conn) SMTPSession { func MakeSMTPSession(connection net.Conn, host string) SMTPSession {
return SMTPSession{ return SMTPSession{
connection, connection,
host,
nil, nil,
[]string{}, []string{},
nil,
} }
} }
func (self SMTPSession) Run() error { func (self *SMTPSession) Run() error {
err := Greet(self) err := Greet(self)
if err != nil { if err != nil {
return err return err
@ -69,9 +71,13 @@ func (self SMTPSession) Run() error {
for _, command := range COMMANDS { for _, command := range COMMANDS {
if command.Check(message) { if command.Check(message) {
command_found = true command_found = true
quit, err = command.Exec(self, message[len(command.Name()):]) quit, err = command.Exec(self, message[len(command.GetName()):])
if err != nil { if err != nil {
return err if quit {
return err
} else {
log.Error().Msgf("%v: %v", self.connection.RemoteAddr(), err)
}
} }
break break
} }
@ -83,14 +89,14 @@ func (self SMTPSession) Run() error {
return nil return nil
} }
func (self SMTPSession) TraceSession(direction string, message string) { func (self *SMTPSession) TraceSession(direction string, message string) {
lines := strings.Split(message, "\n") lines := strings.Split(message, "\n")
for index, line := range lines { for index, line := range lines {
if index != len(lines) - 1 || line != "" { if index != len(lines) - 1 || line != "" {
if index == 0 { if index == 0 {
log.Trace().Msgf("%v %v %v", self.connection.RemoteAddr(), direction, line) log.Trace().Msgf("%v %v %v", self.connection.RemoteAddr(), direction, line)
} else { } else {
log.Trace().Msgf("%v %v", self.connection.RemoteAddr(), line) log.Trace().Msgf("%v %v", self.connection.RemoteAddr(), line)
} }
} }
} }
@ -98,7 +104,7 @@ func (self SMTPSession) TraceSession(direction string, message string) {
} }
var buffer = [1024]byte{} var buffer = [1024]byte{}
func (self SMTPSession) Read(delimiter string) (string, error) { func (self *SMTPSession) Read(delimiter string) (string, error) {
var message string var message string
for !strings.Contains(message, delimiter) { for !strings.Contains(message, delimiter) {
num_read, err := self.connection.Read(buffer[:]) num_read, err := self.connection.Read(buffer[:])
@ -112,31 +118,80 @@ func (self SMTPSession) Read(delimiter string) (string, error) {
return message[:len(message) - len(delimiter)], nil return message[:len(message) - len(delimiter)], nil
} }
func (self SMTPSession) Write(message string) error { func (self *SMTPSession) Write(message string) error {
self.TraceSession("<- ", message) self.TraceSession("<- ", message)
_, err := self.connection.Write([]byte(message)) _, err := self.connection.Write([]byte(message))
return err return err
} }
func (self SMTPSession) GetAddress() string { func (self *SMTPSession) GetHost() string {
return fmt.Sprint(self.connection.LocalAddr()) return self.host
} }
func (self SMTPSession) MailFrom(from string) { func (self *SMTPSession) MailFrom(from string) {
self.Reset() self.Reset()
self.ReversePathBuffer = &from self.ReversePathBuffer = &from
} }
func (self SMTPSession) AddRecipient(recipient string) { func (self *SMTPSession) AddRecipient(recipient string) {
self.ForwardPathBuffer = append(self.ForwardPathBuffer, recipient) self.ForwardPathBuffer = append(self.ForwardPathBuffer, recipient)
} }
func (self SMTPSession) SetMailBuffer(data string) { func (self *SMTPSession) SendMail(data string) error {
self.MailBuffer = &data mx_hostname := Domain.FindStringSubmatch(self.ForwardPathBuffer[0])[1]
mx, err := net.LookupMX(mx_hostname)
if err != nil {
return err
}
var smtp_hostname string
if len(mx) == 0 {
smtp_hostname = self.host
} else {
smtp_hostname = mx[0].Host
}
tls_connection, err := tls.Dial("tcp", fmt.Sprintf("%v:4560", smtp_hostname), nil)
if err != nil {
return err
}
smtp_client, err := smtp.NewClient(tls_connection, "")
if err != nil {
return err
}
defer smtp_client.Close()
err = smtp_client.Hello(self.host)
if err != nil {
return err
}
err = smtp_client.Mail(*self.ReversePathBuffer)
if err != nil {
return err
}
err = smtp_client.Rcpt(self.ForwardPathBuffer[0])
if err != nil {
return err
}
writer, err := smtp_client.Data()
if err != nil {
return err
}
_, err = writer.Write([]byte(data))
if err != nil {
return err
}
err = writer.Close()
if err != nil {
return err
}
err = smtp_client.Quit()
if err != nil {
return err
}
return nil
} }
func (self SMTPSession) Reset() { func (self *SMTPSession) Reset() {
self.ReversePathBuffer = nil self.ReversePathBuffer = nil
self.ForwardPathBuffer = []string{} self.ForwardPathBuffer = []string{}
self.MailBuffer = nil
} }