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 module2021import (22 "crypto/rand"23 "crypto/tls"24 "encoding/hex"25 "io"26 "net"2728 "github.com/emersion/go-smtp"29 "github.com/foxcpp/maddy/framework/future"30)3132// ConnState structure holds the state information of the protocol used to33// accept this message.34type ConnState struct {35 // IANA name (ESMTP, ESMTPS, etc) of the protocol message was received36 // over. If the message was generated locally, this field is empty.37 Proto string3839 // Information about the SMTP connection, including HELO hostname and40 // source IP. Valid only if Proto refers the SMTP protocol or its variant41 // (e.g. LMTP).42 Hostname string43 LocalAddr net.Addr44 RemoteAddr net.Addr45 TLS tls.ConnectionState4647 // The RDNSName field contains the result of Reverse DNS lookup on the48 // client IP.49 //50 // The underlying type is the string or untyped nil value. It is the51 // message source responsibility to populate this field.52 //53 // Valid values of this field consumers need to be aware of:54 // RDNSName = nil55 // The reverse DNS lookup is not applicable for that message source.56 // Typically the case for messages generated locally.57 // RDNSName != nil, but Get returns nil58 // The reverse DNS lookup was attempted, but resulted in an error.59 // Consumers should assume that the PTR record doesn't exist.60 RDNSName *future.Future6162 // If the client successfully authenticated using a username/password pair.63 // This field contains the username.64 AuthUser string6566 // If the client successfully authenticated using a username/password pair.67 // This field should be cleaned if the ConnState object is serialized68 AuthPassword string6970 ModData ModSpecificData71}7273// MsgMetadata structure contains all information about the origin of74// the message and all associated flags indicating how it should be handled75// by components.76//77// All fields should be considered read-only except when otherwise is noted.78// Module instances should avoid keeping reference to the instance passed to it79// and copy the structure using DeepCopy method instead.80//81// Compatibility with older values should be considered when changing this82// structure since it is serialized to the disk by the queue module using83// JSON. Modules should correctly handle missing or invalid values.84type MsgMetadata struct {85 // Unique identifier for this message. Randomly generated by the86 // message source module.87 ID string8889 // Original message sender address as it was received by the message source.90 //91 // Note that this field is meant for use for tracing purposes.92 // All routing and other decisions should be made based on the sender address93 // passed separately (for example, mailFrom argument for CheckSender function)94 // Note that addresses may contain unescaped Unicode characters.95 OriginalFrom string9697 // If set - no SrcHostname and SrcAddr will be added to Received98 // header. These fields are still written to the server log.99 DontTraceSender bool100101 // Quarantine is a message flag that is should be set if message is102 // considered "suspicious" and should be put into "Junk" folder103 // in the storage.104 //105 // This field should not be modified by the checks that verify106 // the message. It is set only by the message pipeline.107 Quarantine bool108109 // OriginalRcpts contains the mapping from the final recipient to the110 // recipient that was presented by the client.111 //112 // MsgPipeline will update that field when recipient modifiers113 // are executed.114 //115 // It should be used when reporting information back to client (via DSN,116 // for example) to prevent disclosing information about aliases117 // which is usually unwanted.118 OriginalRcpts map[string]string119120 // SMTPOpts contains the SMTP MAIL FROM command arguments, if the message121 // was accepted over SMTP or SMTP-like protocol (such as LMTP).122 //123 // Note that the Size field should not be used as source of information about124 // the body size. Especially since it counts the header too whereas125 // Buffer.Len does not.126 SMTPOpts smtp.MailOptions127128 // Conn contains the information about the underlying protocol connection129 // that was used to accept this message. The referenced instance may be shared130 // between multiple messages.131 //132 // It can be nil for locally generated messages.133 Conn *ConnState134135 // This is set by endpoint/smtp to indicate that body contains "TLS-Required: No"136 // header. It is only meaningful if server has seen the body at least once137 // (e.g. the message was passed via queue).138 TLSRequireOverride bool139}140141// DeepCopy creates a copy of the MsgMetadata structure, also142// copying contents of the maps and slices.143//144// There are a few exceptions, however:145// - SrcAddr is not copied and copy field references original value.146func (msgMeta *MsgMetadata) DeepCopy() *MsgMetadata {147 cpy := *msgMeta148 // There is no good way to copy net.Addr, but it should not be149 // modified by anything anyway so we are safe.150 return &cpy151}152153// GenerateMsgID generates a string usable as MsgID field in module.MsgMeta.154func GenerateMsgID() (string, error) {155 rawID := make([]byte, 4)156 _, err := io.ReadFull(rand.Reader, rawID)157 return hex.EncodeToString(rawID), err158}