Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
- Parser captures quoted local-parts without the escape characters
- mail.Address.String() know when to quote local-part, 
- server's `allowsHost` function is ipv6 address aware (addresses specified in the config will get normalized to their ipv6 simplest form, addresses parsed from RCPT and MAIL commands will have ipv6 normalized)
- if `<postmaster>` is used in the RCPT TO (without a host), then new functionality was added to assume that the host is assumed to be the Hostname setting for the Server
- HELO/EHLO argument validation. flashmob#200
- The “header” processor populates “Received:” headers wrongly. flashmob#198
-  tiny bug in “p_redis.go”. flashmob#196
- “MimeHeaderDecode” (envelope.go) returns an incorrectly-spaced string. flashmob#195
- go-guerrilla cannot properly handle some valid addresses. flashmob#199
  • Loading branch information
flashmob authored Dec 27, 2019
1 parent 51f7dda commit d8ea544
Show file tree
Hide file tree
Showing 17 changed files with 1,521 additions and 120 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ test:
$(GO_VARS) $(GO) test -v ./response
$(GO_VARS) $(GO) test -v ./backends
$(GO_VARS) $(GO) test -v ./mail
$(GO_VARS) $(GO) test -v ./mail/encoding
$(GO_VARS) $(GO) test -v ./mail/rfc5321

testrace:
$(GO_VARS) $(GO) test -v . -race
Expand Down
12 changes: 10 additions & 2 deletions backends/p_guerrilla_db_redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,11 +435,19 @@ func GuerrillaDbRedis() Decorator {
e.Subject,
ts)
e.QueuedId = hash

// Add extra headers
protocol := "SMTP"
if e.ESMTP {
protocol = "E" + protocol
}
if e.TLS {
protocol = protocol + "S"
}
var addHead string
addHead += "Delivered-To: " + to + "\r\n"
addHead += "Received: from " + e.Helo + " (" + e.Helo + " [" + e.RemoteIP + "])\r\n"
addHead += " by " + e.RcptTo[0].Host + " with SMTP id " + hash + "@" + e.RcptTo[0].Host + ";\r\n"
addHead += "Received: from " + e.RemoteIP + " ([" + e.RemoteIP + "])\r\n"
addHead += " by " + e.RcptTo[0].Host + " with " + protocol + " id " + hash + "@" + e.RcptTo[0].Host + ";\r\n"
addHead += " " + time.Now().Format(time.RFC1123Z) + "\r\n"

// data will be compressed when printed, with addHead added to beginning
Expand Down
11 changes: 9 additions & 2 deletions backends/p_header.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,18 @@ func Header() Decorator {
if len(e.Hashes) > 0 {
hash = e.Hashes[0]
}
protocol := "SMTP"
if e.ESMTP {
protocol = "E" + protocol
}
if e.TLS {
protocol = protocol + "S"
}
var addHead string
addHead += "Delivered-To: " + to + "\n"
addHead += "Received: from " + e.Helo + " (" + e.Helo + " [" + e.RemoteIP + "])\n"
addHead += "Received: from " + e.RemoteIP + " ([" + e.RemoteIP + "])\n"
if len(e.RcptTo) > 0 {
addHead += " by " + e.RcptTo[0].Host + " with SMTP id " + hash + "@" + e.RcptTo[0].Host + ";\n"
addHead += " by " + e.RcptTo[0].Host + " with " + protocol + " id " + hash + "@" + e.RcptTo[0].Host + ";\n"
}
addHead += " " + time.Now().Format(time.RFC1123Z) + "\n"
// save the result
Expand Down
2 changes: 1 addition & 1 deletion backends/p_redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func Redis() Decorator {
if doErr != nil {
Log().WithError(doErr).Warn("Error while SETEX to redis")
result := NewResult(response.Canned.FailBackendTransaction)
return result, redisErr
return result, doErr
}
e.Values["redis"] = "redis" // the next processor will know to look in redis for the message data
} else {
Expand Down
2 changes: 2 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ func (c *client) parsePath(in []byte, p pathParser) (mail.Address, error) {
ADL: c.parser.ADL,
PathParams: c.parser.PathParams,
NullPath: c.parser.NullPath,
Quoted: c.parser.LocalPartQuotes,
IP: c.parser.IP,
}
}
return address, err
Expand Down
20 changes: 10 additions & 10 deletions cmd/guerrillad/serve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ func TestServerAddEvent(t *testing.T) {
if conn, buffin, err := test.Connect(newServer, 20); err != nil {
t.Error("Could not connect to new server", newServer.ListenInterface, err)
} else {
if result, err := test.Command(conn, buffin, "HELO"); err == nil {
if result, err := test.Command(conn, buffin, "HELO example.com"); err == nil {
expect := "250 mail.test.com Hello"
if strings.Index(result, expect) != 0 {
t.Error("Expected", expect, "but got", result)
Expand Down Expand Up @@ -766,7 +766,7 @@ func TestServerStartEvent(t *testing.T) {
if conn, buffin, err := test.Connect(newConf.Servers[1], 20); err != nil {
t.Error("Could not connect to new server", newConf.Servers[1].ListenInterface)
} else {
if result, err := test.Command(conn, buffin, "HELO"); err == nil {
if result, err := test.Command(conn, buffin, "HELO example.com"); err == nil {
expect := "250 enable.test.com Hello"
if strings.Index(result, expect) != 0 {
t.Error("Expected", expect, "but got", result)
Expand Down Expand Up @@ -845,7 +845,7 @@ func TestServerStopEvent(t *testing.T) {
if conn, buffin, err := test.Connect(newConf.Servers[1], 20); err != nil {
t.Error("Could not connect to new server", newConf.Servers[1].ListenInterface)
} else {
if result, err := test.Command(conn, buffin, "HELO"); err == nil {
if result, err := test.Command(conn, buffin, "HELO example.com"); err == nil {
expect := "250 enable.test.com Hello"
if strings.Index(result, expect) != 0 {
t.Error("Expected", expect, "but got", result)
Expand Down Expand Up @@ -958,7 +958,7 @@ func TestAllowedHostsEvent(t *testing.T) {
if conn, buffin, err := test.Connect(conf.Servers[1], 20); err != nil {
t.Error("Could not connect to new server", conf.Servers[1].ListenInterface, err)
} else {
if result, err := test.Command(conn, buffin, "HELO"); err == nil {
if result, err := test.Command(conn, buffin, "HELO example.com"); err == nil {
expect := "250 secure.test.com Hello"
if strings.Index(result, expect) != 0 {
t.Error("Expected", expect, "but got", result)
Expand Down Expand Up @@ -997,7 +997,7 @@ func TestAllowedHostsEvent(t *testing.T) {
if conn, buffin, err := test.Connect(conf.Servers[1], 20); err != nil {
t.Error("Could not connect to new server", conf.Servers[1].ListenInterface, err)
} else {
if result, err := test.Command(conn, buffin, "HELO"); err == nil {
if result, err := test.Command(conn, buffin, "HELO example.com"); err == nil {
expect := "250 secure.test.com Hello"
if strings.Index(result, expect) != 0 {
t.Error("Expected", expect, "but got", result)
Expand Down Expand Up @@ -1068,7 +1068,7 @@ func TestTLSConfigEvent(t *testing.T) {
if conn, buffin, err := test.Connect(conf.Servers[0], 20); err != nil {
t.Error("Could not connect to server", conf.Servers[0].ListenInterface, err)
} else {
if result, err := test.Command(conn, buffin, "HELO"); err == nil {
if result, err := test.Command(conn, buffin, "HELO example.com"); err == nil {
expect := "250 mail.test.com Hello"
if strings.Index(result, expect) != 0 {
t.Error("Expected", expect, "but got", result)
Expand Down Expand Up @@ -1254,7 +1254,7 @@ func TestBadTLSReload(t *testing.T) {
if conn, buffin, err := test.Connect(conf.Servers[0], 20); err != nil {
t.Error("Could not connect to server", conf.Servers[0].ListenInterface, err)
} else {
if result, err := test.Command(conn, buffin, "HELO"); err == nil {
if result, err := test.Command(conn, buffin, "HELO example.com"); err == nil {
expect := "250 mail.test.com Hello"
if strings.Index(result, expect) != 0 {
t.Error("Expected", expect, "but got", result)
Expand Down Expand Up @@ -1294,7 +1294,7 @@ func TestBadTLSReload(t *testing.T) {
if conn, buffin, err := test.Connect(conf.Servers[0], 20); err != nil {
t.Error("Could not connect to server", conf.Servers[0].ListenInterface, err)
} else {
if result, err := test.Command(conn, buffin, "HELO"); err == nil {
if result, err := test.Command(conn, buffin, "HELO example.com"); err == nil {
expect := "250 mail.test.com Hello"
if strings.Index(result, expect) != 0 {
t.Error("Expected", expect, "but got", result)
Expand Down Expand Up @@ -1373,7 +1373,7 @@ func TestSetTimeoutEvent(t *testing.T) {
} else {
waitTimeout.Add(1)
go func() {
if result, err := test.Command(conn, buffin, "HELO"); err == nil {
if result, err := test.Command(conn, buffin, "HELO example.com"); err == nil {
expect := "250 mail.test.com Hello"
if strings.Index(result, expect) != 0 {
t.Error("Expected", expect, "but got", result)
Expand Down Expand Up @@ -1442,7 +1442,7 @@ func TestDebugLevelChange(t *testing.T) {
if conn, buffin, err := test.Connect(conf.Servers[0], 20); err != nil {
t.Error("Could not connect to server", conf.Servers[0].ListenInterface, err)
} else {
if result, err := test.Command(conn, buffin, "HELO"); err == nil {
if result, err := test.Command(conn, buffin, "HELO example.com"); err == nil {
expect := "250 mail.test.com Hello"
if strings.Index(result, expect) != 0 {
t.Error("Expected", expect, "but got", result)
Expand Down
2 changes: 2 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type ServerConfig struct {
LogFile string `json:"log_file,omitempty"`
// Hostname will be used in the server's reply to HELO/EHLO. If TLS enabled
// make sure that the Hostname matches the cert. Defaults to os.Hostname()
// Hostname will also be used to fill the 'Host' property when the "RCPT TO" address is
// addressed to just <postmaster>
Hostname string `json:"host_name"`
// Listen interface specified in <ip>:<port> - defaults to 127.0.0.1:2525
ListenInterface string `json:"listen_interface"`
Expand Down
65 changes: 65 additions & 0 deletions mail/encoding/encoding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,70 @@ func TestEncodingMimeHeaderDecode(t *testing.T) {
str = mail.MimeHeaderDecode("=?ISO-8859-1?Q?Andr=E9?= Pirard <[email protected]>")
if strings.Index(str, "André Pirard") != 0 {
t.Error("expecting André Pirard, got:", str)

}

str = mail.MimeHeaderDecode("=?ISO-8859-1?Q?Andr=E9?=\tPirard <[email protected]>")
if strings.Index(str, "André\tPirard") != 0 {
t.Error("expecting André Pirard, got:", str)

}

}

// TestEncodingMimeHeaderDecodeEnding tests when the encoded word is at the end
func TestEncodingMimeHeaderDecodeEnding(t *testing.T) {

// plaintext at the beginning
str := mail.MimeHeaderDecode("What about this one? =?ISO-8859-1?Q?Andr=E9?=")
if str != "What about this one? André" {
t.Error("expecting: What about this one? André, but got:", str)

}

// not plaintext at beginning
str = mail.MimeHeaderDecode("=?ISO-8859-1?Q?Andr=E9?= What about this one? =?ISO-8859-1?Q?Andr=E9?=")
if str != "André What about this one? André" {
t.Error("expecting: André What about this one? André, but got:", str)

}
// plaintext at beginning corruped
str = mail.MimeHeaderDecode("=?ISO-8859-1?B?Andr=E9?= What about this one? =?ISO-8859-1?Q?Andr=E9?=")
if strings.Index(str, "=?ISO-8859-1?B?Andr=E9?= What about this one? André") != 0 {
t.Error("expecting:=?ISO-8859-1?B?Andr=E9?= What about this one? André, but got:", str)

}
}

// TestEncodingMimeHeaderDecodeBad tests the case of a malformed encoding
func TestEncodingMimeHeaderDecodeBad(t *testing.T) {
// bad base64 encoding, it should return the string unencoded
str := mail.MimeHeaderDecode("=?ISO-8859-1?B?Andr=E9?=\tPirard <[email protected]>")
if strings.Index(str, "=?ISO-8859-1?B?Andr=E9?=\tPirard <[email protected]>") != 0 {
t.Error("expecting =?ISO-8859-1?B?Andr=E9?=\tPirard <[email protected]>, got:", str)

}

}

func TestEncodingMimeHeaderDecodeNoSpace(t *testing.T) {
// there is no space
str := mail.MimeHeaderDecode("A =?ISO-8859-1?Q?Andr=E9?=WORLD IN YOUR POCKET")
if str != "A AndréWORLD IN YOUR POCKET" {
// in this case, if it's QP and ?= is found at the end then we can assume no space?
t.Error("Did not get [A AndréWORLD IN YOUR POCKET]")
}
}

func TestEncodingMimeHeaderDecodeMulti(t *testing.T) {

str := mail.MimeHeaderDecode("=?iso-2022-jp?B?GyRCIVpLXEZ8Om89fCFbPEIkT0lUOk5NUSROJU0lPyROSn0bKEI=?= =?iso-2022-jp?B?GyRCJCxCPyQkJEckORsoQg==?=")
if strings.Index(str, "【本日削除】実は不採用のネタの方が多いです") != 0 {
t.Error("expecting 【本日削除】実は不採用のネタの方が多いです, got:", str)
}

str = mail.MimeHeaderDecode("=?iso-2022-jp?B?GyRCIVpLXEZ8Om89fCFbPEIkT0lUOk5NUSROJU0lPyROSn0bKEI=?= \t =?iso-2022-jp?B?GyRCJCxCPyQkJEckORsoQg==?=")
if strings.Index(str, "【本日削除】実は不採用のネタの方が多いです") != 0 {
t.Error("expecting 【本日削除】実は不採用のネタの方が多いです, got:", str)
}
}
Loading

0 comments on commit d8ea544

Please sign in to comment.