From 92f2da5025bba69782be4ca31d20a32cad01aedf Mon Sep 17 00:00:00 2001 From: James Hillyerd Date: Sat, 31 Mar 2018 13:37:42 -0700 Subject: [PATCH] server: -netdebug flag now controls tracing for #90 Network trace is sent to stdout, no longer part of normal debug logging. --- cmd/inbucket/main.go | 5 +++ pkg/config/config.go | 2 + pkg/server/pop3/handler.go | 77 ++++++++++++++----------------------- pkg/server/pop3/listener.go | 3 ++ pkg/server/smtp/handler.go | 18 +++++++-- pkg/server/smtp/listener.go | 3 ++ 6 files changed, 56 insertions(+), 52 deletions(-) diff --git a/cmd/inbucket/main.go b/cmd/inbucket/main.go index 9423128..4f47434 100644 --- a/cmd/inbucket/main.go +++ b/cmd/inbucket/main.go @@ -59,6 +59,7 @@ func main() { pidfile := flag.String("pidfile", "", "Write our PID into the specified file.") logfile := flag.String("logfile", "stderr", "Write out log into the specified file.") logjson := flag.Bool("logjson", false, "Logs are written in JSON format.") + netdebug := flag.Bool("netdebug", false, "Dump SMTP & POP3 network traffic to stdout.") flag.Usage = func() { fmt.Fprintln(os.Stderr, "Usage: inbucket [options]") flag.PrintDefaults() @@ -89,6 +90,10 @@ func main() { fmt.Fprintf(os.Stderr, "Configuration error: %v\n", err) os.Exit(1) } + if *netdebug { + conf.POP3.Debug = true + conf.SMTP.Debug = true + } // Setup signal handler. sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT) diff --git a/pkg/config/config.go b/pkg/config/config.go index dd35f39..7db13a8 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -45,6 +45,7 @@ type SMTP struct { MaxMessageBytes int `required:"true" default:"10240000" desc:"Maximum message size"` StoreMessages bool `required:"true" default:"true" desc:"Store incoming mail?"` Timeout time.Duration `required:"true" default:"300s" desc:"Idle network timeout"` + Debug bool `ignored:"true"` } // POP3 contains the POP3 server configuration. @@ -52,6 +53,7 @@ type POP3 struct { Addr string `required:"true" default:"0.0.0.0:1100" desc:"POP3 server IP4 host:port"` Domain string `required:"true" default:"inbucket" desc:"HELLO domain"` Timeout time.Duration `required:"true" default:"600s" desc:"Idle network timeout"` + Debug bool `ignored:"true"` } // Web contains the HTTP server configuration. diff --git a/pkg/server/pop3/handler.go b/pkg/server/pop3/handler.go index 8e8eeb8..cbf3628 100644 --- a/pkg/server/pop3/handler.go +++ b/pkg/server/pop3/handler.go @@ -2,7 +2,6 @@ package pop3 import ( "bufio" - "bytes" "fmt" "io" "net" @@ -58,26 +57,35 @@ var commands = map[string]bool{ // Session defines an active POP3 session type Session struct { - server *Server // Reference to the server we belong to - id int // Session ID number - conn net.Conn // Our network connection - remoteHost string // IP address of client - sendError error // Used to bail out of read loop on send error - state State // Current session state - reader *bufio.Reader // Buffered reader for our net conn - user string // Mailbox name - messages []storage.Message // Slice of messages in mailbox - retain []bool // Messages to retain upon UPDATE (true=retain) - msgCount int // Number of undeleted messages - logger zerolog.Logger + server *Server // Reference to the server we belong to. + id int // Session ID number. + conn net.Conn // Our network connection. + remoteHost string // IP address of client. + sendError error // Used to bail out of read loop on send error. + state State // Current session state. + reader *bufio.Reader // Buffered reader for our net conn. + user string // Mailbox name. + messages []storage.Message // Slice of messages in mailbox. + retain []bool // Messages to retain upon UPDATE (true=retain). + msgCount int // Number of undeleted messages. + logger zerolog.Logger // Session specific logger. + debug bool // Print network traffic to stdout. } // NewSession creates a new POP3 session func NewSession(server *Server, id int, conn net.Conn, logger zerolog.Logger) *Session { reader := bufio.NewReader(conn) host, _, _ := net.SplitHostPort(conn.RemoteAddr().String()) - return &Session{server: server, id: id, conn: conn, state: AUTHORIZATION, - reader: reader, remoteHost: host, logger: logger} + return &Session{ + server: server, + id: id, + conn: conn, + state: AUTHORIZATION, + reader: reader, + remoteHost: host, + logger: logger, + debug: server.config.Debug, + } } func (s *Session) String() string { @@ -550,41 +558,12 @@ func (s *Session) send(msg string) { } if _, err := fmt.Fprint(s.conn, msg+"\r\n"); err != nil { s.sendError = err - s.logger.Warn().Msgf("Failed to send: '%v'", msg) + s.logger.Warn().Msgf("Failed to send: %q", msg) return } - s.logger.Debug().Msgf(">> %v >>", msg) -} - -// readByteLine reads a line of input into the provided buffer. Does -// not reset the Buffer - please do so prior to calling. -func (s *Session) readByteLine(buf *bytes.Buffer) error { - if err := s.conn.SetReadDeadline(s.nextDeadline()); err != nil { - return err + if s.debug { + fmt.Printf("%04d > %v\n", s.id, msg) } - for { - line, err := s.reader.ReadBytes('\r') - if err != nil { - return err - } - if _, err = buf.Write(line); err != nil { - return err - } - // Read the next byte looking for '\n' - c, err := s.reader.ReadByte() - if err != nil { - return err - } - if err := buf.WriteByte(c); err != nil { - return err - } - if c == '\n' { - // We've reached the end of the line, return - return nil - } - // Else, keep looking - } - // Should be unreachable } // Reads a line of input @@ -596,7 +575,9 @@ func (s *Session) readLine() (line string, err error) { if err != nil { return "", err } - s.logger.Debug().Msgf("<< %v <<", strings.TrimRight(line, "\r\n")) + if s.debug { + fmt.Printf("%04d %v\n", s.id, strings.TrimRight(line, "\r\n")) + } return line, nil } diff --git a/pkg/server/pop3/listener.go b/pkg/server/pop3/listener.go index bf9d6fd..cf4c14a 100644 --- a/pkg/server/pop3/listener.go +++ b/pkg/server/pop3/listener.go @@ -13,6 +13,8 @@ import ( // Server defines an instance of our POP3 server type Server struct { + // TODO(#91) Refactor config items out of this struct + config config.POP3 host string domain string timeout time.Duration @@ -25,6 +27,7 @@ type Server struct { // New creates a new Server struct func New(cfg config.POP3, shutdownChan chan bool, store storage.Store) *Server { return &Server{ + config: cfg, host: cfg.Addr, domain: cfg.Domain, store: store, diff --git a/pkg/server/smtp/handler.go b/pkg/server/smtp/handler.go index d696776..fea9817 100644 --- a/pkg/server/smtp/handler.go +++ b/pkg/server/smtp/handler.go @@ -80,7 +80,8 @@ type Session struct { reader *bufio.Reader from string recipients []*policy.Recipient - logger zerolog.Logger + logger zerolog.Logger // Session specific logger. + debug bool // Print network traffic to stdout. } // NewSession creates a new Session for the given connection @@ -96,6 +97,7 @@ func NewSession(server *Server, id int, conn net.Conn, logger zerolog.Logger) *S remoteHost: host, recipients: make([]*policy.Recipient, 0), logger: logger, + debug: server.config.Debug, } } @@ -433,7 +435,9 @@ func (s *Session) send(msg string) { s.logger.Warn().Msgf("Failed to send: %q", msg) return } - s.logger.Debug().Msgf(">> %v >>", msg) + if s.debug { + fmt.Printf("%04d > %v\n", s.id, msg) + } } // readByteLine reads a line of input, returns byte slice. @@ -441,7 +445,11 @@ func (s *Session) readByteLine() ([]byte, error) { if err := s.conn.SetReadDeadline(s.nextDeadline()); err != nil { return nil, err } - return s.reader.ReadBytes('\n') + b, err := s.reader.ReadBytes('\n') + if err == nil && s.debug { + fmt.Printf("%04d %s\n", s.id, bytes.TrimRight(b, "\r\n")) + } + return b, err } // Reads a line of input @@ -453,7 +461,9 @@ func (s *Session) readLine() (line string, err error) { if err != nil { return "", err } - s.logger.Debug().Msgf("<< %v <<", strings.TrimRight(line, "\r\n")) + if s.debug { + fmt.Printf("%04d %v\n", s.id, strings.TrimRight(line, "\r\n")) + } return line, nil } diff --git a/pkg/server/smtp/listener.go b/pkg/server/smtp/listener.go index e7fd095..e047520 100644 --- a/pkg/server/smtp/listener.go +++ b/pkg/server/smtp/listener.go @@ -38,6 +38,8 @@ func init() { // Server holds the configuration and state of our SMTP server type Server struct { + // TODO(#91) Refactor config items out of this struct + config config.SMTP // Configuration host string domain string @@ -86,6 +88,7 @@ func NewServer( apolicy *policy.Addressing, ) *Server { return &Server{ + config: cfg, host: cfg.Addr, domain: cfg.Domain, domainNoStore: strings.ToLower(cfg.DomainNoStore),