1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package target2021import (22 "context"23 "errors"24 "net"25 "strings"26 "time"2728 "github.com/foxcpp/maddy/framework/address"29 "github.com/foxcpp/maddy/framework/dns"30 "github.com/foxcpp/maddy/framework/module"31)3233func SanitizeForHeader(raw string) string {34 return strings.Replace(raw, "\n", "", -1)35}3637func GenerateReceived(ctx context.Context, msgMeta *module.MsgMetadata, ourHostname, mailFrom string) (string, error) {38 if msgMeta.Conn == nil {39 return "", errors.New("can't generate Received for a locally generated message")40 }4142 builder := strings.Builder{}4344 // Empirically guessed value that should be enough to fit45 // the entire value in most cases.46 builder.Grow(256 + len(msgMeta.Conn.Hostname))4748 if !msgMeta.DontTraceSender && (strings.Contains(msgMeta.Conn.Proto, "SMTP") ||49 strings.Contains(msgMeta.Conn.Proto, "LMTP")) {50 // INTERNATIONALIZATION: See RFC 6531 Section 3.7.3.51 hostname, err := dns.SelectIDNA(msgMeta.SMTPOpts.UTF8, msgMeta.Conn.Hostname)52 if err == nil {53 builder.WriteString("from ")54 builder.WriteString(hostname)55 }5657 if tcpAddr, ok := msgMeta.Conn.RemoteAddr.(*net.TCPAddr); ok {58 builder.WriteString(" (")59 if msgMeta.Conn.RDNSName != nil {60 rdnsName, err := msgMeta.Conn.RDNSName.GetContext(ctx)61 if err == nil && rdnsName != nil && rdnsName.(string) != "" {62 // INTERNATIONALIZATION: See RFC 6531 Section 3.7.3.63 encoded, err := dns.SelectIDNA(msgMeta.SMTPOpts.UTF8, rdnsName.(string))64 if err == nil {65 builder.WriteString(encoded)66 builder.WriteRune(' ')67 }68 }69 }70 builder.WriteRune('[')71 builder.WriteString(tcpAddr.IP.String())72 builder.WriteString("])")73 }74 }7576 if ourHostname != "" {77 ourHostname, err := dns.SelectIDNA(msgMeta.SMTPOpts.UTF8, ourHostname)78 if err == nil {79 builder.WriteString(" by ")80 builder.WriteString(SanitizeForHeader(ourHostname))81 }82 }8384 if mailFrom != "" {85 // INTERNATIONALIZATION: See RFC 6531 Section 3.7.3.86 mailFrom, err := address.SelectIDNA(msgMeta.SMTPOpts.UTF8, mailFrom)87 if err == nil {88 builder.WriteString(" (envelope-sender <")89 builder.WriteString(SanitizeForHeader(mailFrom))90 builder.WriteString(">)")91 }92 }9394 if msgMeta.Conn.Proto != "" {95 builder.WriteString(" with ")96 if msgMeta.SMTPOpts.UTF8 {97 builder.WriteString("UTF8")98 }99 builder.WriteString(msgMeta.Conn.Proto)100 }101 builder.WriteString(" id ")102 builder.WriteString(msgMeta.ID)103 builder.WriteString("; ")104 builder.WriteString(time.Now().Format(time.RFC1123Z))105106 return strings.TrimSpace(builder.String()), nil107}