// Accept starts a new SMTP session using io.ReadWriteCloser func Accept(remoteAddress string, conn io.ReadWriteCloser, storage storage.Storage, messageChan chan *data.Message, hostname string, monkey monkey.ChaosMonkey) { defer conn.Close() proto := smtp.NewProtocol() proto.Hostname = hostname var link *linkio.Link reader := io.Reader(conn) writer := io.Writer(conn) if monkey != nil { linkSpeed := monkey.LinkSpeed() if linkSpeed != nil { link = linkio.NewLink(*linkSpeed * linkio.BytePerSecond) reader = link.NewLinkReader(io.Reader(conn)) writer = link.NewLinkWriter(io.Writer(conn)) } } session := &Session{conn, proto, storage, messageChan, remoteAddress, false, "", link, reader, writer, monkey} proto.LogHandler = session.logf proto.MessageReceivedHandler = session.acceptMessage proto.ValidateSenderHandler = session.validateSender proto.ValidateRecipientHandler = session.validateRecipient proto.ValidateAuthenticationHandler = session.validateAuthentication proto.GetAuthenticationMechanismsHandler = func() []string { return []string{"PLAIN"} } session.logf("Starting session") session.Write(proto.Start()) for session.Read() == true { if monkey != nil && monkey.Disconnect != nil && monkey.Disconnect() { session.conn.Close() break } } session.logf("Session ended") }
// connectionHandler handles all smtp sessions currently running. // Must be run as a goroutine. func (serv *smtpServer) connectionHandler() { serv.wait.Add(1) defer serv.wait.Done() connections := make(map[net.Conn]struct{}) // The list of simultaneous connections. doneSessionChan := make(chan net.Conn) quit := false // Whether the handler has received the message to quit. for { select { case <-serv.quit: quit = true if len(connections) == 0 { return } for conn := range connections { conn.Close() } // A new connection is being initiated. case conn := <-serv.newConnChan: // Don't allow more than the maximum number of simultaneous sessions. if len(connections) >= serv.maxConn { conn.Close() continue } connections[conn] = struct{}{} // Set up the smtp state machine. smtp := smtp.NewProtocol() // What to do with an email that is received. smtp.MessageReceivedHandler = func(message *data.Message) (string, error) { fmt.Println("Message received:", message.Content.Body) return string(message.ID), nil } // start running the protocol. go func() { smtpRun(smtp, conn) doneSessionChan <- conn }() // A session has finished. case conn := <-doneSessionChan: conn.Close() delete(connections, conn) if len(connections) == 0 && quit { return } } } }
// Serve serves SMTP requests on the given listener. func (serv *SMTPServer) Serve(l net.Listener) error { defer l.Close() for { conn, err := l.Accept() if err != nil { return smtpLog.Errorf("Error accepting connection: %s\n", err) } // Set up the SMTP state machine. smtp := smtp.NewProtocol() // TODO add TLS support // smtp.RequireTLS = serv.cfg.RequireTLS smtp.LogHandler = smtpLogHandler smtp.ValidateSenderHandler = serv.validateSender smtp.ValidateRecipientHandler = validateEmail smtp.ValidateAuthenticationHandler = serv.validateAuth smtp.GetAuthenticationMechanismsHandler = func() []string { return []string{"PLAIN"} } smtp.MessageReceivedHandler = serv.messageReceived // Start running the protocol. go smtpRun(smtp, conn) } }