Wow it forwards mail :D
This commit is contained in:
parent
49568598ed
commit
dafe3096ca
5 changed files with 136 additions and 53 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 ¯\_(ツ)_/¯)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue