137 lines
2.8 KiB
Go
137 lines
2.8 KiB
Go
package network
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"strings"
|
|
|
|
"git.uwushka.cc/nzx056/nzx056/nzx_tmpmail/loggingshit"
|
|
)
|
|
|
|
type Session struct {
|
|
Conn net.Conn
|
|
Reader *bufio.Reader
|
|
Writer *bufio.Writer
|
|
MailFrom string
|
|
RcptTo []string
|
|
RemoteAddr string
|
|
hasMailFrom bool
|
|
hasRcptTo bool
|
|
}
|
|
|
|
func init() {
|
|
loggingshit.Log("lmtp init", 1)
|
|
ln, err := net.Listen("tcp", "127.0.0.1:2525")
|
|
if err != nil {
|
|
loggingshit.Log("lmtp init failed with error %v", 3, err)
|
|
}
|
|
|
|
for {
|
|
conn, err := ln.Accept()
|
|
if err != nil {
|
|
loggingshit.Log("failed to accept connection with error %v, skipping", 2, err)
|
|
continue
|
|
}
|
|
|
|
go handle_conn(conn)
|
|
}
|
|
}
|
|
|
|
func handle_conn(conn net.Conn) {
|
|
defer conn.Close()
|
|
|
|
s := &Session{
|
|
Conn: conn,
|
|
Writer: bufio.NewWriter(conn),
|
|
Reader: bufio.NewReader(conn),
|
|
RemoteAddr: conn.RemoteAddr().String(),
|
|
}
|
|
|
|
s.reply("220 nzx056.love LMTP ready")
|
|
|
|
for {
|
|
l, err := s.readline()
|
|
if err != nil {
|
|
if err != io.EOF {
|
|
loggingshit.Log("readline failed for %s with error: %v", 2, s.RemoteAddr, err)
|
|
}
|
|
loggingshit.Log("caught EOF from %s, closing conn", 2, s.RemoteAddr)
|
|
return
|
|
}
|
|
|
|
upper := strings.ToUpper(l)
|
|
|
|
switch {
|
|
case strings.HasPrefix(upper, "LHLO "):
|
|
s.reply("250-nzx056.love")
|
|
s.reply("250-8BITMIME")
|
|
s.reply("250 PIPELINING")
|
|
case strings.HasPrefix(upper, "MAIL FROM:"):
|
|
s.MailFrom = extractSender(l[len("MAIL FROM:"):])
|
|
s.hasMailFrom = true
|
|
s.RcptTo = nil
|
|
s.reply("250 2.1.0 Ok")
|
|
case strings.HasPrefix(upper, "RCPT TO:"):
|
|
rcpt := strings.ToLower(extractSender(l[len("RCPT TO:"):]))
|
|
s.hasRcptTo = true
|
|
|
|
if !strings.HasSuffix(rcpt, "@nzx056.love") {
|
|
s.reply("550 5.1.1 User unknown")
|
|
loggingshit.Log("got an email for unknown domain %s, skipping", 1, rcpt)
|
|
continue
|
|
}
|
|
|
|
s.RcptTo = append(s.RcptTo, rcpt)
|
|
s.reply("250 2.1.5 Ok")
|
|
case upper == "DATA":
|
|
if !s.hasMailFrom {
|
|
s.reply("503 5.5.1 Need MAIL FROM first")
|
|
continue
|
|
}
|
|
|
|
if !s.hasRcptTo {
|
|
s.reply("503 5.5.1 Need RCPT TO first")
|
|
continue
|
|
}
|
|
|
|
s.reply("354 End data with <CR><LF>.<CR><LF>")
|
|
}
|
|
}
|
|
}
|
|
|
|
func extractSender(s string) string {
|
|
s = strings.TrimSpace(s)
|
|
if strings.HasPrefix(s, "<") {
|
|
end := strings.Index(s, ">")
|
|
if end >= 0 {
|
|
return s[1:end]
|
|
}
|
|
}
|
|
|
|
fields := strings.Fields(s)
|
|
if len(fields) == 0 {
|
|
return ""
|
|
}
|
|
|
|
return strings.Trim(fields[0], "<>")
|
|
}
|
|
|
|
func (s *Session) readline() (string, error) {
|
|
l, err := s.Reader.ReadString('\n')
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
l = strings.TrimRight(l, "\r\n")
|
|
loggingshit.Log("readline %s from %s", 1, l, s.RemoteAddr)
|
|
return l, nil
|
|
}
|
|
|
|
func (s *Session) reply(st string) {
|
|
loggingshit.Log("write to %s: %s", 1, s.RemoteAddr, st)
|
|
fmt.Fprintf(s.Writer, "%s\r\n", st)
|
|
s.Writer.Flush()
|
|
}
|