Example #1
0
// signalProcessor is a goroutine that handles OS signals
func signalProcessor(c <-chan os.Signal) {
	for {
		sig := <-c
		switch sig {
		case syscall.SIGHUP:
			// Rotate logs if configured
			if logf != nil {
				log.LogInfo("Recieved SIGHUP, cycling logfile")
				closeLogFile()
				openLogFile()
			} else {
				log.LogInfo("Ignoring SIGHUP, logfile not configured")
			}
		case syscall.SIGTERM:
			// Initiate shutdown
			log.LogInfo("Received SIGTERM, shutting down")
			go timedExit()
			web.Stop()
			if smtpServer != nil {
				smtpServer.Stop()
			} else {
				log.LogError("smtpServer was nil during shutdown")
			}
		}
	}
}
Example #2
0
// Initialize websocket from incus
func setupWebSocket(cfg config.WebConfig, ds *data.DataStore) {
	mymap := make(map[string]string)

	mymap["client_broadcasts"] = strconv.FormatBool(cfg.ClientBroadcasts)
	mymap["connection_timeout"] = strconv.Itoa(cfg.ConnTimeout)
	mymap["redis_enabled"] = strconv.FormatBool(cfg.RedisEnabled)
	mymap["debug"] = "true"

	conf := incus.InitConfig(mymap)
	store := incus.InitStore(&conf)
	Websocket = incus.CreateServer(&conf, store)

	log.LogInfo("Incus Websocket Init")

	go func() {
		for {
			select {
			case msg := <-ds.NotifyMailChan:
				go Websocket.AppListener(msg)
			}
		}
	}()

	go Websocket.RedisListener()
	go Websocket.SendHeartbeats()
}
Example #3
0
// Start() the web server
func Start() {
	addr := fmt.Sprintf("%v:%v", webConfig.Ip4address, webConfig.Ip4port)
	server := &http.Server{
		Addr:         addr,
		Handler:      nil,
		ReadTimeout:  60 * time.Second,
		WriteTimeout: 60 * time.Second,
	}

	// We don't use ListenAndServe because it lacks a way to close the listener
	log.LogInfo("HTTP listening on TCP4 %v", addr)
	var err error
	listener, err = net.Listen("tcp", addr)
	if err != nil {
		log.LogError("HTTP failed to start TCP4 listener: %v", err)
		// TODO More graceful early-shutdown procedure
		panic(err)
	}

	err = server.Serve(listener)
	if shutdown {
		log.LogTrace("HTTP server shutting down on request")
	} else if err != nil {
		log.LogError("HTTP server failed: %v", err)
	}
}
Example #4
0
func (ds *DataStore) StorageConnect() {

	if ds.Config.Storage == "mongodb" {
		log.LogInfo("Trying MongoDB storage")
		s := CreateMongoDB(ds.Config)
		if s == nil {
			log.LogInfo("MongoDB storage unavailable")
		} else {
			log.LogInfo("Using MongoDB storage")
			ds.Storage = s
		}

		// start some savemail workers
		for i := 0; i < 3; i++ {
			go ds.SaveMail()
		}
	}
}
Example #5
0
func setupRoutes(cfg config.WebConfig) {
	log.LogInfo("Theme templates mapped to '%v'", cfg.TemplateDir)
	log.LogInfo("Theme static content mapped to '%v'", cfg.PublicDir)

	r := mux.NewRouter()

	// Static content
	r.PathPrefix("/public/").Handler(http.StripPrefix("/public/", http.FileServer(http.Dir(cfg.PublicDir))))

	// Register a couple of routes
	r.Path("/").Handler(handler(Home)).Name("Home").Methods("GET")
	r.Path("/status").Handler(handler(Status)).Name("Status").Methods("GET")

	// Mail
	r.Path("/mails").Handler(handler(MailList)).Name("Mails").Methods("GET")
	r.Path("/mails/{page:[0-9]+}").Handler(handler(MailList)).Name("MailList").Methods("GET")
	r.Path("/mail/{id:[0-9a-z]+}").Handler(handler(MailView)).Name("MailView").Methods("GET")
	r.Path("/mail/attachment/{id:[0-9a-z]+}/{[*.*]}").Handler(handler(MailAttachment)).Name("MailAttachment").Methods("GET")
	r.Path("/mail/delete/{id:[0-9a-z]+}").Handler(handler(MailDelete)).Name("MailDelete").Methods("GET")

	// Login
	r.Path("/login").Handler(handler(Login)).Methods("POST")
	r.Path("/login").Handler(handler(LoginForm)).Name("Login").Methods("GET")
	r.Path("/logout").Handler(handler(Logout)).Name("Logout").Methods("GET")
	r.Path("/register").Handler(handler(Register)).Methods("POST")
	r.Path("/register").Handler(handler(RegisterForm)).Name("Register").Methods("GET")

	// Add to greylist
	r.Path("/greylist/host/{id:[0-9a-z]+}").Handler(handler(MailView)).Name("GreyHostAdd").Methods("GET")
	r.Path("/greylist/mailfrom/{id:[0-9a-z]+}").Handler(handler(GreyMailFromAdd)).Name("GreyMailFromAdd").Methods("GET")
	r.Path("/greylist/tomail/{id:[0-9a-z]+}").Handler(handler(GreyMailFromAdd)).Name("GreyMailToAdd").Methods("GET")

	// Nginx Xclient auth
	r.Path("/auth-smtp").Handler(handler(NginxHTTPAuth)).Name("Nginx")
	r.Path("/ping").Handler(handler(Ping)).Name("Ping").Methods("GET")

	// Web-Socket & Fallback longpoll
	r.HandleFunc("/ws/", Websocket.SocketListener)
	r.HandleFunc("/lp", Websocket.LongPollListener)

	Router = r
	// Send all incoming requests to router.
	http.Handle("/", Router)
}
Example #6
0
func (s *Server) handleClient(c *Client) {
	log.LogInfo("SMTP Connection from %v, starting session <%v>", c.conn.RemoteAddr(), c.id)

	defer func() {
		s.closeClient(c)
		s.waitgroup.Done()
	}()

	c.greet()

	// check if client on trusted hosts
	if s.trustedHosts[net.ParseIP(c.remoteHost).String()] {
		c.logInfo("Remote Client is Trusted: <%s>", c.remoteHost)
		c.trusted = true
	}

	// This is our command reading loop
	for i := 0; i < 100; i++ {
		if c.state == 2 {
			// Special case, does not use SMTP command format
			c.processData()
			continue
		}

		line, err := c.readLine()
		if err == nil {
			if cmd, arg, ok := c.parseCmd(line); ok {
				c.handle(cmd, arg, line)
			}
		} else {
			// readLine() returned an error
			if err == io.EOF {
				c.logWarn("Got EOF while in state %v", c.state)
				break
			}
			// not an EOF
			c.logWarn("Connection error: %v", err)
			if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
				c.Write("221", "Idle timeout, bye bye")
				break
			}

			c.Write("221", "Connection error, sorry")
			break
		}

		if c.kill_time > 1 || c.errors > 3 {
			return
		}
	}

	c.logInfo("Closing connection")
}
Example #7
0
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()
}
Example #8
0
func (c *Client) logInfo(msg string, args ...interface{}) {
	log.LogInfo("SMTP[%v]<%v> %v", c.remoteHost, c.id, fmt.Sprintf(msg, args...))
}
Example #9
0
// 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,
			})
		}
	}
}