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 .") } } } 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() }