From 9a3c953d87b7422968842daf219fcea3e1e0772d Mon Sep 17 00:00:00 2001 From: Christian Groschupp Date: Wed, 25 Nov 2020 21:10:09 +0100 Subject: [PATCH] add configurable smtp greeter --- config.go | 23 +++++++++++++++++++---- server.go | 23 +++++++++++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/config.go b/config.go index 5bf13a7..e330781 100644 --- a/config.go +++ b/config.go @@ -8,6 +8,7 @@ import ( "os" "reflect" "strings" + "text/template" "time" "github.com/phires/go-guerrilla/backends" @@ -60,6 +61,9 @@ type ServerConfig struct { // XClientOn when using a proxy such as Nginx, XCLIENT command is used to pass the // original client's IP address & client's HELO XClientOn bool `json:"xclient_on,omitempty"` + // GreeterTemplate will be used to greet the client. + // Defaults to defaultGreeterString + GreeterTemplate *template.Template `json:"greeter_template"` } type ServerTLSConfig struct { @@ -151,10 +155,13 @@ var TLSClientAuthTypes = map[string]tls.ClientAuthType{ "RequireAndVerifyClientCert": tls.RequireAndVerifyClientCert, } -const defaultMaxClients = 100 -const defaultTimeout = 30 -const defaultInterface = "127.0.0.1:2525" -const defaultMaxSize = int64(10 << 20) // 10 Mebibytes +const ( + defaultMaxClients = 100 + defaultTimeout = 30 + defaultInterface = "127.0.0.1:2525" + defaultMaxSize = int64(10 << 20) // 10 Mebibytes + defaultGreeterString = "220 {{ .Hostname }} SMTP Guerrilla({{ .Version }}) #{{ .ClientID }} ({{ .ActiveClientsCount }}) {{ .Time }}" +) // Unmarshalls json data into AppConfig struct and any other initialization of the struct // also does validation, returns error if validation failed or something went wrong @@ -279,6 +286,10 @@ func (c *AppConfig) setDefaults() error { if err != nil { return err } + defaultGreeterTemplate, err := template.New("greeter").Parse(defaultGreeterString) + if err != nil { + return fmt.Errorf("unable to parse template: %v", err) + } if len(c.Servers) == 0 { sc := ServerConfig{} sc.LogFile = c.LogFile @@ -288,6 +299,7 @@ func (c *AppConfig) setDefaults() error { sc.MaxClients = defaultMaxClients sc.Timeout = defaultTimeout sc.MaxSize = defaultMaxSize + sc.GreeterTemplate = defaultGreeterTemplate c.Servers = append(c.Servers, sc) } else { // make sure each server has defaults correctly configured @@ -304,6 +316,9 @@ func (c *AppConfig) setDefaults() error { if c.Servers[i].MaxSize == 0 { c.Servers[i].MaxSize = defaultMaxSize // 10 Mebibytes } + if c.Servers[i].GreeterTemplate == nil { + c.Servers[i].GreeterTemplate = defaultGreeterTemplate + } if c.Servers[i].ListenInterface == "" { return fmt.Errorf("listen interface not specified for server at index %d", i) } diff --git a/server.go b/server.go index bc91b63..d6de83d 100644 --- a/server.go +++ b/server.go @@ -67,6 +67,14 @@ type allowedHosts struct { sync.Mutex // guard access to the map } +type greeterData struct { + Hostname string + Version string + ClientID uint64 + ActiveClientsCount int + Time string +} + type command []byte var ( @@ -368,9 +376,16 @@ func (s *server) handleClient(client *client) { s.log().Infof("Handle client [%s], id: %d", client.RemoteIP, client.ID) // Initial greeting - greeting := fmt.Sprintf("220 %s SMTP Guerrilla(%s) #%d (%d) %s", - sc.Hostname, Version, client.ID, - s.clientPool.GetActiveClientsCount(), time.Now().Format(time.RFC3339)) + greeterData := greeterData{ + Hostname: sc.Hostname, + Version: Version, + ClientID: client.ID, + ActiveClientsCount: s.clientPool.GetActiveClientsCount(), + Time: time.Now().Format(time.RFC3339), + } + + var greeting bytes.Buffer + sc.GreeterTemplate.Execute(&greeting, greeterData) helo := fmt.Sprintf("250 %s Hello", sc.Hostname) // ehlo is a multi-line reply and need additional \r\n at the end @@ -405,7 +420,7 @@ func (s *server) handleClient(client *client) { for client.isAlive() { switch client.state { case ClientGreeting: - client.sendResponse(greeting) + client.sendResponse(greeting.String()) client.state = ClientCmd case ClientCmd: client.bufin.setLimit(CommandLineMaxLength)