package dispatcher import ( "bytes" "errors" "fmt" "net/mail" "strings" ) const MaxEnvelopeBytes = 64 * 1024 // Envelope is the only application payload accepted by the dispatcher. Message // must already be a complete YAMN transport envelope, not user supplied mail. type Envelope struct { EntryAddress string `json:"entry_address"` Message []byte `json:"message"` } func (e Envelope) Validate(allowedDomains map[string]struct{}) error { if len(e.Message) == 0 || len(e.Message) > MaxEnvelopeBytes { return fmt.Errorf("invalid YAMN envelope size") } if bytes.Contains(e.Message, []byte("\r")) { return errors.New("YAMN envelope must use LF line endings") } address, err := mail.ParseAddress(e.EntryAddress) if err != nil || address.Address != e.EntryAddress { return errors.New("invalid entry remailer address") } parts := strings.Split(address.Address, "@") if len(parts) != 2 || parts[0] == "" || parts[1] == "" { return errors.New("invalid entry remailer address") } if _, ok := allowedDomains[strings.ToLower(parts[1])]; !ok { return errors.New("entry remailer domain is not allowed") } if !bytes.HasPrefix(e.Message, []byte("To: "+e.EntryAddress+"\n")) { return errors.New("YAMN envelope recipient does not match entry remailer") } if !bytes.Contains(e.Message, []byte("-----BEGIN REMAILER MESSAGE-----\n")) { return errors.New("invalid YAMN envelope") } return nil }