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() {
|
||||
err := smtp.Run(":4650", false)
|
||||
err := smtp.Run(
|
||||
"localhost",
|
||||
"4650",
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -25,10 +25,10 @@ import (
|
|||
|
||||
type Command struct {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -36,9 +36,12 @@ func (self Command) Check(message string) bool {
|
|||
return strings.HasPrefix(message, self.name)
|
||||
}
|
||||
|
||||
func Greet(smtp_session SMTPSession) error {
|
||||
func Greet(smtp_session *SMTPSession) error {
|
||||
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 {
|
||||
return err
|
||||
|
@ -47,93 +50,111 @@ func Greet(smtp_session SMTPSession) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func Helo(smtp_session SMTPSession, message string) (bool, error) {
|
||||
func Helo(smtp_session *SMTPSession, message string) (bool, error) {
|
||||
err := smtp_session.Write(
|
||||
fmt.Sprintf(
|
||||
"250 %v is shy" + "\r\n",
|
||||
smtp_session.GetAddress(),
|
||||
smtp_session.GetHost(),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
|
||||
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(
|
||||
fmt.Sprintf(
|
||||
"250 %v is shy" + "\r\n",
|
||||
smtp_session.GetAddress(),
|
||||
smtp_session.GetHost(),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
|
||||
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)
|
||||
if match == nil {
|
||||
smtp_session.Write(
|
||||
"501 Could not parse reverse-path",
|
||||
err := smtp_session.Write(
|
||||
"501 Could not parse reverse-path\n",
|
||||
)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
} else if len(match) > 1 {
|
||||
smtp_session.MailFrom(match[1])
|
||||
err := smtp_session.Write(
|
||||
"250 OK\n",
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
if match == nil {
|
||||
smtp_session.Write(
|
||||
"501 Could not parse forward-path",
|
||||
err := smtp_session.Write(
|
||||
"501 Could not parse forward-path\n",
|
||||
)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
} else {
|
||||
smtp_session.AddRecipient(match[1])
|
||||
err := smtp_session.Write(
|
||||
"250 OK\n",
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
"354 Start Input\n",
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
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(
|
||||
"250 OK\n",
|
||||
)
|
||||
smtp_session.Reset()
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
|
||||
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(
|
||||
"221 Goodbye :)\n",
|
||||
)
|
||||
|
@ -144,24 +165,24 @@ func Quit(smtp_session SMTPSession, message string) (bool, error) {
|
|||
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(
|
||||
"250 OK\n",
|
||||
)
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
|
||||
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(
|
||||
"250 Reset\n",
|
||||
)
|
||||
smtp_session.Reset()
|
||||
if err != nil {
|
||||
return false, err
|
||||
return true, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
var ReversePath = regexp.MustCompile(fmt.Sprintf("(?:%v)|<>", path))
|
||||
var ForwardPath = regexp.MustCompile(path)
|
||||
var Domain = regexp.MustCompile("\\w+@(\\w+(?:\\.\\w+)*)")
|
||||
|
||||
// https://datatracker.ietf.org/doc/html/rfc5321#page-41
|
||||
// Is this...legal, m'lord? (no, but ¯\_(ツ)_/¯)
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package smtp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
|
@ -30,14 +31,14 @@ type PlainListener struct {
|
|||
listener net.Listener
|
||||
}
|
||||
|
||||
func handle(connection net.Conn) {
|
||||
func handle(connection net.Conn, host string) {
|
||||
log.Info().Msgf(
|
||||
"New connection %v. Starting session.",
|
||||
connection.RemoteAddr(),
|
||||
)
|
||||
defer connection.Close()
|
||||
|
||||
session := MakeSMTPSession(connection)
|
||||
session := MakeSMTPSession(connection, host)
|
||||
err := session.Run()
|
||||
if err != nil {
|
||||
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.
|
||||
New(zerolog.ConsoleWriter{Out: os.Stderr}).
|
||||
With().
|
||||
|
@ -61,18 +62,19 @@ func Run(host string, implicit_tls bool) error {
|
|||
Logger().
|
||||
Level(zerolog.TraceLevel)
|
||||
|
||||
listener, err := net.Listen("tcp", host)
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf("%v:%v", host, port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Msgf("Server started on port %v for host %v", port, host)
|
||||
for {
|
||||
connection, err := listener.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go handle(connection)
|
||||
go handle(connection, host)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,30 +20,32 @@ package smtp
|
|||
|
||||
import (
|
||||
"net"
|
||||
"net/smtp"
|
||||
"fmt"
|
||||
"strings"
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type SMTPSession struct {
|
||||
connection net.Conn
|
||||
host string
|
||||
|
||||
ReversePathBuffer *string
|
||||
ForwardPathBuffer []string
|
||||
MailBuffer *string
|
||||
}
|
||||
|
||||
func MakeSMTPSession(connection net.Conn) SMTPSession {
|
||||
func MakeSMTPSession(connection net.Conn, host string) SMTPSession {
|
||||
return SMTPSession{
|
||||
connection,
|
||||
host,
|
||||
nil,
|
||||
[]string{},
|
||||
nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (self SMTPSession) Run() error {
|
||||
func (self *SMTPSession) Run() error {
|
||||
err := Greet(self)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -69,9 +71,13 @@ func (self SMTPSession) Run() error {
|
|||
for _, command := range COMMANDS {
|
||||
if command.Check(message) {
|
||||
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 quit {
|
||||
return err
|
||||
} else {
|
||||
log.Error().Msgf("%v: %v", self.connection.RemoteAddr(), err)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
@ -83,7 +89,7 @@ func (self SMTPSession) Run() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (self SMTPSession) TraceSession(direction string, message string) {
|
||||
func (self *SMTPSession) TraceSession(direction string, message string) {
|
||||
lines := strings.Split(message, "\n")
|
||||
for index, line := range lines {
|
||||
if index != len(lines) - 1 || line != "" {
|
||||
|
@ -98,7 +104,7 @@ func (self SMTPSession) TraceSession(direction string, message string) {
|
|||
}
|
||||
|
||||
var buffer = [1024]byte{}
|
||||
func (self SMTPSession) Read(delimiter string) (string, error) {
|
||||
func (self *SMTPSession) Read(delimiter string) (string, error) {
|
||||
var message string
|
||||
for !strings.Contains(message, delimiter) {
|
||||
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
|
||||
}
|
||||
|
||||
func (self SMTPSession) Write(message string) error {
|
||||
func (self *SMTPSession) Write(message string) error {
|
||||
self.TraceSession("<- ", message)
|
||||
_, err := self.connection.Write([]byte(message))
|
||||
return err
|
||||
}
|
||||
|
||||
func (self SMTPSession) GetAddress() string {
|
||||
return fmt.Sprint(self.connection.LocalAddr())
|
||||
func (self *SMTPSession) GetHost() string {
|
||||
return self.host
|
||||
}
|
||||
|
||||
func (self SMTPSession) MailFrom(from string) {
|
||||
func (self *SMTPSession) MailFrom(from string) {
|
||||
self.Reset()
|
||||
self.ReversePathBuffer = &from
|
||||
}
|
||||
|
||||
func (self SMTPSession) AddRecipient(recipient string) {
|
||||
func (self *SMTPSession) AddRecipient(recipient string) {
|
||||
self.ForwardPathBuffer = append(self.ForwardPathBuffer, recipient)
|
||||
}
|
||||
|
||||
func (self SMTPSession) SetMailBuffer(data string) {
|
||||
self.MailBuffer = &data
|
||||
func (self *SMTPSession) SendMail(data string) error {
|
||||
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.ForwardPathBuffer = []string{}
|
||||
self.MailBuffer = nil
|
||||
}
|
Loading…
Reference in a new issue