func RunTCP(t TCPService) { l, err := net.ListenTCP("tcp", t.Addr()) if err != nil { log.Error("failed to bind to local address %s", t.Addr()) t.Shutdown() return } defer l.Close() log.Info("listening for connections on %s", t.Addr()) for { conn, err := l.AcceptTCP() if err != nil { log.Error("failed to accept connection: %v", err) continue } if err := t.SetClientOptions(conn); err != nil { conn.Close() continue } log.Trace(func() string { return fmt.Sprintf("%s: client connected to %s", conn.RemoteAddr(), t.Addr()) }) go t.Handle(conn) } }
// Set TCP socket options on a new SMTP connection. func (s *SMTPService) SetClientOptions(conn *net.TCPConn) error { if err := conn.SetKeepAlive(false); err != nil { log.Error("%s: SetKeepAlive: %v", conn.RemoteAddr(), err) return err } if err := conn.SetLinger(-1); err != nil { log.Error("%s: SetLinger: %v", conn.RemoteAddr(), err) return err } return nil }
// Respond to client, reporting session termination if there was an error // writing to the socket. func (s *SMTPSession) codeWithVerdict(code int) Verdict { if err := s.respondCode(code); err != nil { log.Error("%s: failed to send response: %v", s.remote, err) return Terminate } return Continue }
// Respond to client, reporting session termination if there was an error // writing to the socket. func (s *SMTPSession) respondWithVerdict(code int, message string) Verdict { if err := s.respond(code, message); err != nil { log.Error("%s: failed to send response: %v", s.remote, err) return Terminate } return Continue }
func (c *config) readConfig(path string) (err error) { file, err := os.Open(path) if err != nil { return } rd := bufio.NewReader(file) idx := 0 for { line, err := rd.ReadString('\n') if err != nil { if err == io.EOF { break } log.Error("I/O error reading from file: %s: %v", path, err) return err } idx++ line = strings.Trim(line, "\r\n") // skip comments and blank lines if len(strings.Trim(line, " ")) == 0 || line[0] == '#' { continue } if err = c.parseLine(line, idx); err != nil { return err } } return nil }
func main() { flag.Parse() cfg, err := LoadConfig(*configPath) if err != nil { log.Error("failed to load config: %s", err) return } log.Info("loaded config: %s", cfg) runtime.GOMAXPROCS(cfg.Cores()) exitChan := trapSignals() go RunTCP(NewSMTPService(cfg, exitChan)) <-exitChan }
// Process a DATA command. func (s *SMTPSession) handleData(data []byte) Verdict { if s.message.To.Len() < 1 { return s.respondWithVerdict(554, "no valid recipients given") } if err := s.respondCode(354); err != nil { return Terminate } s.state = dataReceived body, err := s.readBody() if err != nil { log.Error("failed to read body of message: %v", err) return Terminate } s.message.Body = body s.state = bodyReceived return s.codeWithVerdict(250) }