summaryrefslogtreecommitdiffstats
path: root/katzenpost/dispatcher/envelope.go
blob: b9ff7b8d140ef4393048a41a54dca207295df0a0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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
}