package smtpd import ( "net" "bufio" "strings" "fmt" "time" ) type session struct { server *Server peer Peer envelope *Envelope conn net.Conn reader *bufio.Reader writer *bufio.Writer scanner *bufio.Scanner tls bool } func (session *session) serve() { defer session.close() if !session.server.EnableProxyProtocol { session.welcome() } for { for session.scanner.Scan() { line := session.scanner.Text() session.logf("received: %s", strings.TrimSpace(line)) session.handle(line) } err := session.scanner.Err() if err == bufio.ErrTooLong { session.reply(500, "Line too long") // Advance reader to the next newline session.reader.ReadString('\n') session.scanner = bufio.NewScanner(session.reader) // Reset and have the client start over. session.reset() continue } break } } func (session *session) reject() { session.reply(421, "Too busy. Try again later.") session.close() } func (session *session) reset() { session.envelope = nil } func (session *session) welcome() { if session.server.ConnectionChecker != nil { err := session.server.ConnectionChecker(session.peer) if err != nil { session.error(err) session.close() return } } session.reply(220, session.server.WelcomeMessage) } func (session *session) reply(code int, message string) { session.logf("sending: %d %s", code, message) fmt.Fprintf(session.writer, "%d %s\r\n", code, message) session.flush() } func (session *session) flush() { session.conn.SetWriteDeadline(time.Now().Add(session.server.WriteTimeout)) session.writer.Flush() session.conn.SetReadDeadline(time.Now().Add(session.server.ReadTimeout)) } func (session *session) error(err error) { if smtpdError, ok := err.(Error); ok { session.reply(smtpdError.Code, smtpdError.Message) } else { session.reply(502, fmt.Sprintf("%s", err)) } } func (session *session) logf(format string, v ...interface{}) { if session.server.ProtocolLogger == nil { return } session.server.ProtocolLogger.Info(fmt.Sprintf( "%s [peer:%s]", fmt.Sprintf(format, v...), session.peer.Addr, )) } func (session *session) logError(err error, desc string) { session.server.ProtocolLogger.Error(desc, "error", err) } func (session *session) extensions() []string { extensions := []string{ fmt.Sprintf("SIZE %d", session.server.MaxMessageSize), "8BITMIME", "PIPELINING", } if session.server.EnableXCLIENT { extensions = append(extensions, "XCLIENT") } if session.server.TLSConfig != nil && !session.tls { extensions = append(extensions, "STARTTLS") } if session.server.Authenticator != nil && session.tls { extensions = append(extensions, "AUTH PLAIN LOGIN") } return extensions } func (session *session) deliver() error { if session.server.Handler != nil { return session.server.Handler(session.peer, *session.envelope) } return nil } func (session *session) close() { session.writer.Flush() time.Sleep(200 * time.Millisecond) session.conn.Close() }