Make receiver hostname configurable via --receiver-hostname flag

Remove the package-level global hostname from parser.go.

Adds a log warning when the last Received hop doesn't match the
expected receiver hostname.

Bug: https://github.com/happyDomain/happydeliver/issues/11
This commit is contained in:
nemunaire 2026-03-25 10:58:34 +07:00
commit 76ee50a100
18 changed files with 53 additions and 40 deletions

View file

@ -34,6 +34,7 @@ func declareFlags(o *Config) {
flag.StringVar(&o.Email.Domain, "domain", o.Email.Domain, "Domain used to receive emails")
flag.StringVar(&o.Email.TestAddressPrefix, "address-prefix", o.Email.TestAddressPrefix, "Expected email adress prefix (deny address that doesn't start with this prefix)")
flag.StringVar(&o.Email.LMTPAddr, "lmtp-addr", o.Email.LMTPAddr, "LMTP server listen address")
flag.StringVar(&o.Email.ReceiverHostname, "receiver-hostname", o.Email.ReceiverHostname, "Hostname used to filter Authentication-Results headers (defaults to os.Hostname())")
flag.DurationVar(&o.Analysis.DNSTimeout, "dns-timeout", o.Analysis.DNSTimeout, "Timeout when performing DNS query")
flag.DurationVar(&o.Analysis.HTTPTimeout, "http-timeout", o.Analysis.HTTPTimeout, "Timeout when performing HTTP query")
flag.Var(&StringArray{&o.Analysis.RBLs}, "rbl", "Append a RBL (use this option multiple time to append multiple RBLs)")

View file

@ -34,6 +34,11 @@ import (
openapi_types "github.com/oapi-codegen/runtime/types"
)
func getHostname() string {
h, _ := os.Hostname()
return h
}
// Config represents the application configuration
type Config struct {
DevProxy string
@ -58,6 +63,7 @@ type EmailConfig struct {
Domain string
TestAddressPrefix string
LMTPAddr string
ReceiverHostname string
}
// AnalysisConfig contains timeout and behavior settings for email analysis
@ -84,6 +90,7 @@ func DefaultConfig() *Config {
Domain: "happydeliver.local",
TestAddressPrefix: "test-",
LMTPAddr: "127.0.0.1:2525",
ReceiverHostname: getHostname(),
},
Analysis: AnalysisConfig{
DNSTimeout: 5 * time.Second,

View file

@ -98,6 +98,17 @@ func (r *EmailReceiver) ProcessEmailBytes(rawEmail []byte, recipientEmail string
log.Printf("Analysis complete. Grade: %s. Score: %d/100", result.Report.Grade, result.Report.Score)
// Warn if the last Received hop doesn't match the expected receiver hostname
if r.config.Email.ReceiverHostname != "" &&
result.Report.HeaderAnalysis != nil &&
result.Report.HeaderAnalysis.ReceivedChain != nil &&
len(*result.Report.HeaderAnalysis.ReceivedChain) > 0 {
lastHop := (*result.Report.HeaderAnalysis.ReceivedChain)[0]
if lastHop.By != nil && *lastHop.By != r.config.Email.ReceiverHostname {
log.Printf("WARNING: Last Received hop 'by' field (%s) does not match expected receiver hostname (%s): check your RECEIVER_HOSTNAME config as authentication results will be false", *lastHop.By, r.config.Email.ReceiverHostname)
}
}
// Marshal report to JSON
reportJSON, err := json.Marshal(result.Report)
if err != nil {