Browse Source

- Reload config on SIGHUP
- Write current process id (pid) to a file, /var/run/go-guerrilla.pid by default

flashmob 9 years ago
parent
commit
61cbb5bf77
5 changed files with 105 additions and 79 deletions
  1. 3 0
      .gitignore
  2. 16 0
      LICENSE
  3. 21 10
      README.md
  4. 1 2
      goguerrilla.conf.sample
  5. 64 67
      goguerrilla.go

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+.idea
+goguerrilla.conf
+go-guerrilla

+ 16 - 0
LICENSE

@@ -0,0 +1,16 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 GuerrillaMail.com.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 21 - 10
README.md

@@ -125,23 +125,27 @@ Copy goguerrilla.conf.sample to goguerrilla.conf
 	    "GM_MAX_CLIENTS":"500", // max clients that can be handled
 	    "GM_MAX_CLIENTS":"500", // max clients that can be handled
 		"NGINX_AUTH_ENABLED":"N",// Y or N
 		"NGINX_AUTH_ENABLED":"N",// Y or N
 		"NGINX_AUTH":"127.0.0.1:8025", // If using Nginx proxy, choose an ip and port to serve Auth requsts for Nginx
 		"NGINX_AUTH":"127.0.0.1:8025", // If using Nginx proxy, choose an ip and port to serve Auth requsts for Nginx
-	    "SGID":"508",// group id of the user from /etc/passwd
-		"GUID":"504" // uid from /etc/passwd
+	    "PID_FILE":		  "/var/run/go-guerrilla.pid",
 	}
 	}
 
 
+Releases
+=========================================================
+
+- Reload config on SIGHUP
+- Write current process id (pid) to a file, /var/run/go-guerrilla.pid by default
+
+
 Using Nginx as a proxy
 Using Nginx as a proxy
 =========================================================
 =========================================================
 Nginx can be used to proxy SMTP traffic for GoGuerrilla SMTPd
 Nginx can be used to proxy SMTP traffic for GoGuerrilla SMTPd
 
 
-Why proxy SMTP?
+Why proxy SMTP with Nginx?
 
 
- *	Terminate TLS connections: Golang is not there yet when it comes to TLS.
-At present, only a partial implementation of TLS is provided (as of Nov 2012). 
-OpenSSL on the other hand, used in Nginx, has a complete implementation of
-SSL v2/v3 and TLS protocols.
- *	Could be used for load balancing and authentication in the future.
+ *	Terminate TLS connections: Early Golang was not there yet when it came to TLS.
+ OpenSSL on the other hand, used in Nginx, has a complete implementation of TLS with familiar configuration.
+ *	Nginx could be used for load balancing and authentication
 
 
- 1.	Compile nginx with --with-mail --with-mail_ssl_module
+ 1.	Compile nginx with --with-mail --with-mail_ssl_module (most current nginx packages have this compiled already)
 
 
  2.	Configuration:
  2.	Configuration:
 
 
@@ -195,4 +199,11 @@ Starting from the command line (example)
 This will place goguerrilla in the background and continue running
 This will place goguerrilla in the background and continue running
 
 
 You may also put another process to watch your goguerrilla process and re-start it
 You may also put another process to watch your goguerrilla process and re-start it
-if something goes wrong.
+if something goes wrong.
+
+Benchmarking:
+==========================================================
+
+http://www.jrh.org/smtp/index.html
+Test 500 clients:
+$ time smtp-source -c -l 5000 -t [email protected] -s 500 -m 5000 5.9.7.183

+ 1 - 2
goguerrilla.conf.sample

@@ -18,6 +18,5 @@
     "GM_MAX_CLIENTS":"500",
     "GM_MAX_CLIENTS":"500",
 	"NGINX_AUTH_ENABLED":"N",
 	"NGINX_AUTH_ENABLED":"N",
 	"NGINX_AUTH":"127.0.0.1:8025",
 	"NGINX_AUTH":"127.0.0.1:8025",
-    "SGID":"508",
-	"GUID":"504"
+    "PID_FILE":		  "/var/run/go-guerrilla.pid",
 }
 }

+ 64 - 67
goguerrilla.go

@@ -1,35 +1,7 @@
 /** 
 /** 
 Go-Guerrilla SMTPd
 Go-Guerrilla SMTPd
-A minimalist SMTP server written in Go, made for receiving large volumes of mail.
-Works either as a stand-alone or in conjunction with Nginx SMTP proxy.
-TO DO: add http server for nginx
 
 
-Copyright (c) 2012 Flashmob, GuerrillaMail.com
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
-documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
-rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
-Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
-WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-What is Go Guerrilla SMTPd?
-It's a small SMTP server written in Go, optimized for receiving email.
-Written for GuerrillaMail.com which processes tens of thousands of emails
-every hour.
-
-Benchmarking:
-http://www.jrh.org/smtp/index.html
-Test 500 clients:
-$ time smtp-source -c -l 5000 -t [email protected] -s 500 -m 5000 5.9.7.183
-
-Version: 1.1
+Version: 1.2
 Author: Flashmob, GuerrillaMail.com
 Author: Flashmob, GuerrillaMail.com
 Contact: [email protected]
 Contact: [email protected]
 License: MIT
 License: MIT
@@ -38,17 +10,6 @@ Site: http://www.guerrillamail.com/
 
 
 See README for more details
 See README for more details
 
 
-To build
-Install the following
-$ go get github.com/ziutek/mymysql/thrsafe
-$ go get github.com/ziutek/mymysql/autorc
-$ go get github.com/ziutek/mymysql/godrv
-$ go get github.com/sloonz/go-iconv
-
-TODO: after failing tls, 
-
-patch:
-rebuild all: go build -a -v new.go
 
 
 */
 */
 
 
@@ -78,10 +39,12 @@ import (
 	"net"
 	"net"
 	"net/http"
 	"net/http"
 	"os"
 	"os"
+	"os/signal"
 	"regexp"
 	"regexp"
 	"runtime"
 	"runtime"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
+        "syscall"
 	"time"
 	"time"
 )
 )
 
 
@@ -112,8 +75,9 @@ var max_size int // max email DATA size
 var timeout time.Duration
 var timeout time.Duration
 var allowedHosts = make(map[string]bool, 15)
 var allowedHosts = make(map[string]bool, 15)
 var sem chan int // currently active clients
 var sem chan int // currently active clients
-
+var signalChannel = make(chan os.Signal, 1)
 var SaveMailChan chan *Client // workers for saving mail
 var SaveMailChan chan *Client // workers for saving mail
+var flagVerbouse, flagIface, flagConfigFile string;
 // defaults. Overwrite any of these in the configure() function which loads them from a json file
 // defaults. Overwrite any of these in the configure() function which loads them from a json file
 var gConfig = map[string]string{
 var gConfig = map[string]string{
 	"GSMTP_MAX_SIZE":         "131072",
 	"GSMTP_MAX_SIZE":         "131072",
@@ -134,8 +98,7 @@ var gConfig = map[string]string{
 	"GM_MAX_CLIENTS":         "500",
 	"GM_MAX_CLIENTS":         "500",
 	"NGINX_AUTH_ENABLED":     "N",              // Y or N
 	"NGINX_AUTH_ENABLED":     "N",              // Y or N
 	"NGINX_AUTH":             "127.0.0.1:8025", // If using Nginx proxy, ip and port to serve Auth requsts
 	"NGINX_AUTH":             "127.0.0.1:8025", // If using Nginx proxy, ip and port to serve Auth requsts
-	"SGID":                   "1008",           // group id
-	"SUID":                   "1008",           // user id, from /etc/passwd
+	"PID_FILE":		  "/var/run/go-guerrilla.pid",
 }
 }
 
 
 type redisClient struct {
 type redisClient struct {
@@ -157,30 +120,36 @@ func logln(level int, s string) {
 	}
 	}
 }
 }
 
 
-func configure() {
-	var configFile, verbose, iface string
+func readConfig() {
 	log.SetOutput(os.Stdout)
 	log.SetOutput(os.Stdout)
 	// parse command line arguments
 	// parse command line arguments
-	flag.StringVar(&configFile, "config", "goguerrilla.conf", "Path to the configuration file")
-	flag.StringVar(&verbose, "v", "n", "Verbose, [y | n] ")
-	flag.StringVar(&iface, "if", "", "Interface and port to listen on, eg. 127.0.0.1:2525 ")
-	flag.Parse()
+	if !flag.Parsed() {
+		flag.StringVar(&flagConfigFile, "config", "goguerrilla.conf", "Path to the configuration file")
+		flag.StringVar(&flagVerbouse, "v", "n", "Verbose, [y | n] ")
+		flag.StringVar(&flagIface, "if", "127.0.0.1:2525", "Interface and port to listen on, eg. 127.0.0.1:2525 ")
+		flag.Parse()
+	}
+
 	// load in the config.
 	// load in the config.
-	b, err := ioutil.ReadFile(configFile)
+	b, err := ioutil.ReadFile(flagConfigFile)
 	if err != nil {
 	if err != nil {
-		log.Fatalln("Could not read config file")
+		log.Fatalln("Could not read config file", err)
 	}
 	}
 	var myConfig map[string]string
 	var myConfig map[string]string
 	err = json.Unmarshal(b, &myConfig)
 	err = json.Unmarshal(b, &myConfig)
 	if err != nil {
 	if err != nil {
-		log.Fatalln("Could not parse config file")
+		log.Fatalln("Could not parse config file:", err)
 	}
 	}
 	for k, v := range myConfig {
 	for k, v := range myConfig {
 		gConfig[k] = v
 		gConfig[k] = v
 	}
 	}
-	gConfig["GSMTP_VERBOSE"] = strings.ToUpper(verbose)
-	if len(iface) > 0 {
-		gConfig["GSTMP_LISTEN_INTERFACE"] = iface
+	// copy command line flag over so it takes precedence
+	if len(flagVerbouse) > 0 {
+		gConfig["GSMTP_VERBOSE"] = strings.ToUpper(flagVerbouse)
+	}
+
+	if len(flagIface) > 0 {
+		gConfig["GSTMP_LISTEN_INTERFACE"] = flagIface
 	}
 	}
 	// map the allow hosts for easy lookup
 	// map the allow hosts for easy lookup
 	if arr := strings.Split(gConfig["GM_ALLOWED_HOSTS"], ","); len(arr) > 0 {
 	if arr := strings.Split(gConfig["GM_ALLOWED_HOSTS"], ","); len(arr) > 0 {
@@ -190,14 +159,7 @@ func configure() {
 	}
 	}
 	var n int
 	var n int
 	var n_err error
 	var n_err error
-	// sem is an active clients channel used for counting clients
-	if n, n_err = strconv.Atoi(gConfig["GM_MAX_CLIENTS"]); n_err != nil {
-		n = 50
-	}
-	// currently active client list
-	sem = make(chan int, n)
-	// database writing workers
-	SaveMailChan = make(chan *Client, 5)
+
 	// timeout for reads
 	// timeout for reads
 	if n, n_err = strconv.Atoi(gConfig["GSMTP_TIMEOUT"]); n_err != nil {
 	if n, n_err = strconv.Atoi(gConfig["GSMTP_TIMEOUT"]); n_err != nil {
 		timeout = time.Duration(10)
 		timeout = time.Duration(10)
@@ -208,6 +170,27 @@ func configure() {
 	if max_size, n_err = strconv.Atoi(gConfig["GSMTP_MAX_SIZE"]); n_err != nil {
 	if max_size, n_err = strconv.Atoi(gConfig["GSMTP_MAX_SIZE"]); n_err != nil {
 		max_size = 131072
 		max_size = 131072
 	}
 	}
+	return
+}
+
+func sigHandler() {
+	for range signalChannel {
+		readConfig()
+		fmt.Printf("Reloading Configuration!\n")
+	}
+}
+
+func initialise() {
+	var n int
+	var n_err error
+	// sem is an active clients channel used for counting clients
+	if n, n_err = strconv.Atoi(gConfig["GM_MAX_CLIENTS"]); n_err != nil {
+		n = 50
+	}
+	// currently active client list
+	sem = make(chan int, n)
+	// database writing workers
+	SaveMailChan = make(chan *Client, 5)
 	// custom log file
 	// custom log file
 	if len(gConfig["GSMTP_LOG_FILE"]) > 0 {
 	if len(gConfig["GSMTP_LOG_FILE"]) > 0 {
 		logfile, err := os.OpenFile(gConfig["GSMTP_LOG_FILE"], os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC, 0600)
 		logfile, err := os.OpenFile(gConfig["GSMTP_LOG_FILE"], os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC, 0600)
@@ -216,12 +199,25 @@ func configure() {
 		}
 		}
 		log.SetOutput(logfile)
 		log.SetOutput(logfile)
 	}
 	}
+	if f, err := os.Create(gConfig["PID_FILE"]); err == nil {
+		defer f.Close()
+		if _, err := f.WriteString(strconv.Itoa(os.Getpid())); err == nil {
+			f.Sync()
+		}
+	}
+	// handle SIGHUP for reloading the configuration while running
+
+	signal.Notify(signalChannel, syscall.SIGHUP)
+	go sigHandler()
 
 
 	return
 	return
 }
 }
 
 
+
+
 func main() {
 func main() {
-	configure()
+	readConfig()
+	initialise()
 	cert, err := tls.LoadX509KeyPair(gConfig["GSMTP_PUB_KEY"], gConfig["GSMTP_PRV_KEY"])
 	cert, err := tls.LoadX509KeyPair(gConfig["GSMTP_PUB_KEY"], gConfig["GSMTP_PRV_KEY"])
 	if err != nil {
 	if err != nil {
 		logln(2, fmt.Sprintf("There was a problem with loading the certificate: %s", err))
 		logln(2, fmt.Sprintf("There was a problem with loading the certificate: %s", err))
@@ -478,8 +474,8 @@ func saveMail() {
 	db := autorc.New("tcp", "", gConfig["MYSQL_HOST"], gConfig["MYSQL_USER"], gConfig["MYSQL_PASS"], gConfig["MYSQL_DB"])
 	db := autorc.New("tcp", "", gConfig["MYSQL_HOST"], gConfig["MYSQL_USER"], gConfig["MYSQL_PASS"], gConfig["MYSQL_DB"])
 	db.Register("set names utf8")
 	db.Register("set names utf8")
 	sql := "INSERT INTO " + gConfig["GM_MAIL_TABLE"] + " "
 	sql := "INSERT INTO " + gConfig["GM_MAIL_TABLE"] + " "
-	sql += "(`date`, `to`, `from`, `subject`, `body`, `charset`, `mail`, `spam_score`, `hash`, `content_type`, `recipient`, `has_attach`, `ip_addr`)"
-	sql += " values (NOW(), ?, ?, ?, ? , 'UTF-8' , ?, 0, ?, '', ?, 0, ?)"
+	sql += "(`date`, `to`, `from`, `subject`, `body`, `charset`, `mail`, `spam_score`, `hash`, `content_type`, `recipient`, `has_attach`, `ip_addr`, `return_path`)"
+	sql += " values (NOW(), ?, ?, ?, ? , 'UTF-8' , ?, 0, ?, '', ?, 0, ?, ?)"
 	ins, sql_err := db.Prepare(sql)
 	ins, sql_err := db.Prepare(sql)
 	if sql_err != nil {
 	if sql_err != nil {
 		logln(2, fmt.Sprintf("Sql statement incorrect: %s", sql_err))
 		logln(2, fmt.Sprintf("Sql statement incorrect: %s", sql_err))
@@ -533,7 +529,8 @@ func saveMail() {
 			client.data,
 			client.data,
 			client.hash,
 			client.hash,
 			to,
 			to,
-			client.address)
+			client.address,
+			client.mail_from)
 		// save, discard result
 		// save, discard result
 		_, _, err = ins.Exec()
 		_, _, err = ins.Exec()
 		if err != nil {
 		if err != nil {