// If running Nginx as a proxy, give Nginx the IP address and port for the SMTP server // Primary use of Nginx is to terminate TLS so that Go doesn't need to deal with it. // This could perform auth and load balancing too // See http://wiki.nginx.org/MailCoreModule func NginxHTTPAuth(w http.ResponseWriter, r *http.Request, ctx *Context) error { //log.LogTrace("Nginx Auth Client: %s", parseRemoteAddr(r)) log.LogTrace("Nginx Auth Client IP <%s> (%s)", r.Header.Get("Client-IP"), r.Header.Get("Client-Host")) cfg := config.GetSmtpConfig() w.Header().Add("Auth-Status", "OK") w.Header().Add("Auth-Server", cfg.Ip4address.String()) w.Header().Add("Auth-Port", strconv.Itoa(cfg.Ip4port)) fmt.Fprint(w, "") return nil }
func main() { flag.Parse() runtime.GOMAXPROCS(runtime.NumCPU()) if *help { flag.Usage() return } // Load & Parse config /* if flag.NArg() != 1 { flag.Usage() os.Exit(1) }*/ //err := config.LoadConfig(flag.Arg(0)) err := config.LoadConfig(*configfile) if err != nil { fmt.Fprintf(os.Stderr, "Failed to parse config: %v\n", err) os.Exit(1) } // Setup signal handler sigChan := make(chan os.Signal) signal.Notify(sigChan, syscall.SIGHUP, syscall.SIGTERM) go signalProcessor(sigChan) // Configure logging, close std* fds level, _ := config.Config.String("logging", "level") log.SetLogLevel(level) if *logfile != "stderr" { // stderr is the go logging default if *logfile == "stdout" { // set to stdout golog.SetOutput(os.Stdout) } else { err := openLogFile() if err != nil { fmt.Fprintf(os.Stderr, "%v", err) os.Exit(1) } defer closeLogFile() // close std* streams os.Stdout.Close() os.Stderr.Close() // Warning: this will hide panic() output os.Stdin.Close() os.Stdout = logf os.Stderr = logf } } log.LogInfo("Smtpd %v (%v) starting...", VERSION, BUILD_DATE) // Write pidfile if requested // TODO: Probably supposed to remove pidfile during shutdown if *pidfile != "none" { pidf, err := os.Create(*pidfile) if err != nil { log.LogError("Failed to create %v: %v", *pidfile, err) os.Exit(1) } defer pidf.Close() fmt.Fprintf(pidf, "%v\n", os.Getpid()) } // Grab our datastore ds := data.NewDataStore() // Start HTTP server //web.Initialize(config.GetWebConfig(), ds) //go web.Start() // Startup SMTP server, block until it exits smtpServer = smtpd.NewSmtpServer(config.GetSmtpConfig(), ds) smtpServer.Start() // Wait for active connections to finish smtpServer.Drain() }
// Main listener loop func (s *Server) Start() { cfg := config.GetSmtpConfig() log.LogTrace("Loading the certificate: %s", cfg.PubKey) cert, err := tls.LoadX509KeyPair(cfg.PubKey, cfg.PrvKey) if err != nil { log.LogError("There was a problem with loading the certificate: %s", err) } else { s.TLSConfig = &tls.Config{ Certificates: []tls.Certificate{cert}, ClientAuth: tls.VerifyClientCertIfGiven, ServerName: cfg.Domain, } //s.TLSConfig .Rand = rand.Reader } defer s.Stop() addr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%v:%v", cfg.Ip4address, cfg.Ip4port)) if err != nil { log.LogError("Failed to build tcp4 address: %v", err) // TODO More graceful early-shutdown procedure //panic(err) s.Stop() return } // Start listening for SMTP connections log.LogInfo("SMTP listening on TCP4 %v", addr) s.listener, err = net.ListenTCP("tcp4", addr) if err != nil { log.LogError("SMTP failed to start tcp4 listener: %v", err) // TODO More graceful early-shutdown procedure //panic(err) s.Stop() return } //Connect database s.Store.StorageConnect() var tempDelay time.Duration var clientId int64 // Handle incoming connections for clientId = 1; ; clientId++ { if conn, err := s.listener.Accept(); err != nil { if nerr, ok := err.(net.Error); ok && nerr.Temporary() { // Temporary error, sleep for a bit and try again if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } log.LogError("SMTP accept error: %v; retrying in %v", err, tempDelay) time.Sleep(tempDelay) continue } else { if s.shutdown { log.LogTrace("SMTP listener shutting down on request") return } // TODO Implement a max error counter before shutdown? // or maybe attempt to restart smtpd panic(err) } } else { tempDelay = 0 s.waitgroup.Add(1) log.LogInfo("There are now %s serving goroutines", strconv.Itoa(runtime.NumGoroutine())) host, _, _ := net.SplitHostPort(conn.RemoteAddr().String()) s.sem <- 1 // Wait for active queue to drain. go s.handleClient(&Client{ state: 1, server: s, conn: conn, remoteHost: host, time: time.Now().Unix(), bufin: bufio.NewReader(conn), bufout: bufio.NewWriter(conn), id: clientId, }) } } }