git » chasquid » commit 0718749

Update auto-generated code

author Alberto Bertogli
2019-10-22 23:08:15 UTC
committer Alberto Bertogli
2019-10-24 20:37:09 UTC
parent f399fe3e843b01296a2f950bf32ba3498d5f5af7

Update auto-generated code

This patch updates the auto-generated code to match the latest tooling
versions.

In particular, the protobufs are regenerated, and the new version no
longer supports unkeyed literals, so some minor changes are needed.

Other than that, the cipher list is extended with the latest ciphers.

internal/config/config.pb.go +157 -37
internal/domaininfo/domaininfo.pb.go +75 -35
internal/protoio/testpb/testpb.pb.go +45 -23
internal/queue/dsn_test.go +8 -8
internal/queue/queue.pb.go +177 -69
internal/queue/queue_test.go +14 -4
internal/tlsconst/ciphers.go +7 -0
internal/userdb/userdb.pb.go +197 -130
internal/userdb/userdb_test.go +3 -1
test/t-03-queue_persistency/addtoqueue.go +5 -1

diff --git a/internal/config/config.pb.go b/internal/config/config.pb.go
index e1c9082..a48a21f 100644
--- a/internal/config/config.pb.go
+++ b/internal/config/config.pb.go
@@ -1,21 +1,13 @@
-// Code generated by protoc-gen-go.
+// Code generated by protoc-gen-go. DO NOT EDIT.
 // source: config.proto
-// DO NOT EDIT!
 
-/*
-Package config is a generated protocol buffer package.
-
-It is generated from these files:
-	config.proto
-
-It has these top-level messages:
-	Config
-*/
 package config
 
-import proto "github.com/golang/protobuf/proto"
-import fmt "fmt"
-import math "math"
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
@@ -26,38 +18,38 @@ var _ = math.Inf
 // is compatible with the proto package it is being compiled against.
 // A compilation error at this line likely means your copy of the
 // proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
 type Config struct {
 	// Default hostname to use when saying hello.
 	// This is used to say hello to clients, for aesthetic purposes.
 	// Default: the system's hostname.
-	Hostname string `protobuf:"bytes,1,opt,name=hostname" json:"hostname,omitempty"`
+	Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"`
 	// Maximum email size, in megabytes.
 	// Default: 50.
-	MaxDataSizeMb int64 `protobuf:"varint,2,opt,name=max_data_size_mb,json=maxDataSizeMb" json:"max_data_size_mb,omitempty"`
+	MaxDataSizeMb int64 `protobuf:"varint,2,opt,name=max_data_size_mb,json=maxDataSizeMb,proto3" json:"max_data_size_mb,omitempty"`
 	// Addresses to listen on for SMTP (usually port 25).
 	// Default: "systemd", which means systemd passes sockets to us.
 	// systemd sockets must be named with "FileDescriptorName=smtp".
-	SmtpAddress []string `protobuf:"bytes,3,rep,name=smtp_address,json=smtpAddress" json:"smtp_address,omitempty"`
+	SmtpAddress []string `protobuf:"bytes,3,rep,name=smtp_address,json=smtpAddress,proto3" json:"smtp_address,omitempty"`
 	// Addresses to listen on for submission (usually port 587).
 	// Default: "systemd", which means systemd passes sockets to us.
 	// systemd sockets must be named with "FileDescriptorName=submission".
-	SubmissionAddress []string `protobuf:"bytes,4,rep,name=submission_address,json=submissionAddress" json:"submission_address,omitempty"`
+	SubmissionAddress []string `protobuf:"bytes,4,rep,name=submission_address,json=submissionAddress,proto3" json:"submission_address,omitempty"`
 	// Addresses to listen on for submission-over-TLS (usually port 465).
 	// Default: "systemd", which means systemd passes sockets to us.
 	// systemd sockets must be named with "FileDescriptorName=submission_tls".
-	SubmissionOverTlsAddress []string `protobuf:"bytes,5,rep,name=submission_over_tls_address,json=submissionOverTlsAddress" json:"submission_over_tls_address,omitempty"`
+	SubmissionOverTlsAddress []string `protobuf:"bytes,5,rep,name=submission_over_tls_address,json=submissionOverTlsAddress,proto3" json:"submission_over_tls_address,omitempty"`
 	// Address for the monitoring http server.
 	// Do NOT expose this to the public internet.
 	// Default: no monitoring http server.
-	MonitoringAddress string `protobuf:"bytes,6,opt,name=monitoring_address,json=monitoringAddress" json:"monitoring_address,omitempty"`
+	MonitoringAddress string `protobuf:"bytes,6,opt,name=monitoring_address,json=monitoringAddress,proto3" json:"monitoring_address,omitempty"`
 	// Mail delivery agent (MDA, also known as LDA) to use.
 	// This should point to the binary to use to deliver email to local users.
 	// The content of the email will be passed via stdin.
 	// If it exits unsuccessfully, we assume the mail was not delivered.
 	// Default: "maildrop".
-	MailDeliveryAgentBin string `protobuf:"bytes,7,opt,name=mail_delivery_agent_bin,json=mailDeliveryAgentBin" json:"mail_delivery_agent_bin,omitempty"`
+	MailDeliveryAgentBin string `protobuf:"bytes,7,opt,name=mail_delivery_agent_bin,json=mailDeliveryAgentBin,proto3" json:"mail_delivery_agent_bin,omitempty"`
 	// Command line arguments for the mail delivery agent. One per argument.
 	// Some replacements will be done.
 	// On an email sent from marsnik@mars to venera@venus:
@@ -70,54 +62,182 @@ type Config struct {
 	//
 	// Default: "-f", "%from%", "-d", "%to_user%"  (adequate for procmail
 	// and maildrop).
-	MailDeliveryAgentArgs []string `protobuf:"bytes,8,rep,name=mail_delivery_agent_args,json=mailDeliveryAgentArgs" json:"mail_delivery_agent_args,omitempty"`
+	MailDeliveryAgentArgs []string `protobuf:"bytes,8,rep,name=mail_delivery_agent_args,json=mailDeliveryAgentArgs,proto3" json:"mail_delivery_agent_args,omitempty"`
 	// Directory where we store our persistent data.
 	// Default: "/var/lib/chasquid"
-	DataDir string `protobuf:"bytes,9,opt,name=data_dir,json=dataDir" json:"data_dir,omitempty"`
+	DataDir string `protobuf:"bytes,9,opt,name=data_dir,json=dataDir,proto3" json:"data_dir,omitempty"`
 	// Suffix separator, to perform suffix removal of local users.
 	// For example, if you set this to "-+", email to local user
 	// "user-blah" and "user+blah" will be delivered to "user".
 	// Including "+" is strongly encouraged, as it is assumed for email
 	// forwarding.
 	// Default: "+".
-	SuffixSeparators string `protobuf:"bytes,10,opt,name=suffix_separators,json=suffixSeparators" json:"suffix_separators,omitempty"`
+	SuffixSeparators string `protobuf:"bytes,10,opt,name=suffix_separators,json=suffixSeparators,proto3" json:"suffix_separators,omitempty"`
 	// Characters to drop from the user part on local emails.
 	// For example, if you set this to "._", email to local user
 	// "u.se_r" will be delivered to "user".
 	// Default: ".".
-	DropCharacters string `protobuf:"bytes,11,opt,name=drop_characters,json=dropCharacters" json:"drop_characters,omitempty"`
+	DropCharacters string `protobuf:"bytes,11,opt,name=drop_characters,json=dropCharacters,proto3" json:"drop_characters,omitempty"`
 	// Path where to write the mail log to.
 	// If "<syslog>", log using the syslog (at MAIL|INFO priority).
 	// Default: <syslog>
-	MailLogPath string `protobuf:"bytes,12,opt,name=mail_log_path,json=mailLogPath" json:"mail_log_path,omitempty"`
+	MailLogPath string `protobuf:"bytes,12,opt,name=mail_log_path,json=mailLogPath,proto3" json:"mail_log_path,omitempty"`
 	// Enable dovecot authentication.
 	// Domains that don't have an user database will be authenticated via
 	// dovecot.
-	DovecotAuth bool `protobuf:"varint,13,opt,name=dovecot_auth,json=dovecotAuth" json:"dovecot_auth,omitempty"`
+	DovecotAuth bool `protobuf:"varint,13,opt,name=dovecot_auth,json=dovecotAuth,proto3" json:"dovecot_auth,omitempty"`
 	// Dovecot userdb path. If dovecot_auth is set and this
 	// is not, we will try to autodetect it.
 	// Example: /var/run/dovecot/auth-userdb
-	DovecotUserdbPath string `protobuf:"bytes,14,opt,name=dovecot_userdb_path,json=dovecotUserdbPath" json:"dovecot_userdb_path,omitempty"`
+	DovecotUserdbPath string `protobuf:"bytes,14,opt,name=dovecot_userdb_path,json=dovecotUserdbPath,proto3" json:"dovecot_userdb_path,omitempty"`
 	// Dovecot client path. If dovecot_auth is set and this
 	// is not, we will try to autodetect it.
 	// Example: /var/run/dovecot/auth-client
-	DovecotClientPath string `protobuf:"bytes,15,opt,name=dovecot_client_path,json=dovecotClientPath" json:"dovecot_client_path,omitempty"`
+	DovecotClientPath    string   `protobuf:"bytes,15,opt,name=dovecot_client_path,json=dovecotClientPath,proto3" json:"dovecot_client_path,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Config) Reset()         { *m = Config{} }
+func (m *Config) String() string { return proto.CompactTextString(m) }
+func (*Config) ProtoMessage()    {}
+func (*Config) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{0}
+}
+
+func (m *Config) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Config.Unmarshal(m, b)
+}
+func (m *Config) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Config.Marshal(b, m, deterministic)
+}
+func (m *Config) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Config.Merge(m, src)
+}
+func (m *Config) XXX_Size() int {
+	return xxx_messageInfo_Config.Size(m)
+}
+func (m *Config) XXX_DiscardUnknown() {
+	xxx_messageInfo_Config.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Config proto.InternalMessageInfo
+
+func (m *Config) GetHostname() string {
+	if m != nil {
+		return m.Hostname
+	}
+	return ""
+}
+
+func (m *Config) GetMaxDataSizeMb() int64 {
+	if m != nil {
+		return m.MaxDataSizeMb
+	}
+	return 0
+}
+
+func (m *Config) GetSmtpAddress() []string {
+	if m != nil {
+		return m.SmtpAddress
+	}
+	return nil
+}
+
+func (m *Config) GetSubmissionAddress() []string {
+	if m != nil {
+		return m.SubmissionAddress
+	}
+	return nil
+}
+
+func (m *Config) GetSubmissionOverTlsAddress() []string {
+	if m != nil {
+		return m.SubmissionOverTlsAddress
+	}
+	return nil
+}
+
+func (m *Config) GetMonitoringAddress() string {
+	if m != nil {
+		return m.MonitoringAddress
+	}
+	return ""
+}
+
+func (m *Config) GetMailDeliveryAgentBin() string {
+	if m != nil {
+		return m.MailDeliveryAgentBin
+	}
+	return ""
+}
+
+func (m *Config) GetMailDeliveryAgentArgs() []string {
+	if m != nil {
+		return m.MailDeliveryAgentArgs
+	}
+	return nil
+}
+
+func (m *Config) GetDataDir() string {
+	if m != nil {
+		return m.DataDir
+	}
+	return ""
+}
+
+func (m *Config) GetSuffixSeparators() string {
+	if m != nil {
+		return m.SuffixSeparators
+	}
+	return ""
+}
+
+func (m *Config) GetDropCharacters() string {
+	if m != nil {
+		return m.DropCharacters
+	}
+	return ""
+}
+
+func (m *Config) GetMailLogPath() string {
+	if m != nil {
+		return m.MailLogPath
+	}
+	return ""
 }
 
-func (m *Config) Reset()                    { *m = Config{} }
-func (m *Config) String() string            { return proto.CompactTextString(m) }
-func (*Config) ProtoMessage()               {}
-func (*Config) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *Config) GetDovecotAuth() bool {
+	if m != nil {
+		return m.DovecotAuth
+	}
+	return false
+}
+
+func (m *Config) GetDovecotUserdbPath() string {
+	if m != nil {
+		return m.DovecotUserdbPath
+	}
+	return ""
+}
+
+func (m *Config) GetDovecotClientPath() string {
+	if m != nil {
+		return m.DovecotClientPath
+	}
+	return ""
+}
 
 func init() {
 	proto.RegisterType((*Config)(nil), "Config")
 }
 
-func init() { proto.RegisterFile("config.proto", fileDescriptor0) }
+func init() { proto.RegisterFile("config.proto", fileDescriptor_3eaf2c85e69e9ea4) }
 
-var fileDescriptor0 = []byte{
+var fileDescriptor_3eaf2c85e69e9ea4 = []byte{
 	// 409 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x92, 0x41, 0x8f, 0x12, 0x31,
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x92, 0x41, 0x8f, 0x12, 0x31,
 	0x14, 0xc7, 0x83, 0xb8, 0x2c, 0x14, 0xd8, 0x5d, 0xaa, 0xc6, 0xaa, 0x17, 0xdc, 0xcb, 0x92, 0x18,
 	0xf7, 0x62, 0x8c, 0x27, 0x0f, 0x08, 0x47, 0x8d, 0x86, 0xd5, 0x73, 0xf3, 0x66, 0xa6, 0xcc, 0x34,
 	0x99, 0x69, 0x27, 0xef, 0x75, 0x08, 0xf2, 0x3d, 0xfc, 0xbe, 0xa6, 0x0f, 0x18, 0x30, 0xee, 0xb1,
diff --git a/internal/domaininfo/domaininfo.pb.go b/internal/domaininfo/domaininfo.pb.go
index 6c5116a..8c3cb50 100644
--- a/internal/domaininfo/domaininfo.pb.go
+++ b/internal/domaininfo/domaininfo.pb.go
@@ -1,21 +1,13 @@
-// Code generated by protoc-gen-go.
+// Code generated by protoc-gen-go. DO NOT EDIT.
 // source: domaininfo.proto
-// DO NOT EDIT!
 
-/*
-Package domaininfo is a generated protocol buffer package.
-
-It is generated from these files:
-	domaininfo.proto
-
-It has these top-level messages:
-	Domain
-*/
 package domaininfo
 
-import proto "github.com/golang/protobuf/proto"
-import fmt "fmt"
-import math "math"
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
@@ -26,7 +18,7 @@ var _ = math.Inf
 // is compatible with the proto package it is being compiled against.
 // A compilation error at this line likely means your copy of the
 // proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
 type SecLevel int32
 
@@ -47,6 +39,7 @@ var SecLevel_name = map[int32]string{
 	2: "TLS_INSECURE",
 	3: "TLS_SECURE",
 }
+
 var SecLevel_value = map[string]int32{
 	"PLAIN":        0,
 	"TLS_CLIENT":   1,
@@ -57,40 +50,87 @@ var SecLevel_value = map[string]int32{
 func (x SecLevel) String() string {
 	return proto.EnumName(SecLevel_name, int32(x))
 }
-func (SecLevel) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+func (SecLevel) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_622326b6f7a15daa, []int{0}
+}
 
 type Domain struct {
-	Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
 	// Security level for mail coming from this domain (they send to us).
-	IncomingSecLevel SecLevel `protobuf:"varint,2,opt,name=incoming_sec_level,json=incomingSecLevel,enum=domaininfo.SecLevel" json:"incoming_sec_level,omitempty"`
+	IncomingSecLevel SecLevel `protobuf:"varint,2,opt,name=incoming_sec_level,json=incomingSecLevel,proto3,enum=domaininfo.SecLevel" json:"incoming_sec_level,omitempty"`
 	// Security level for mail going to this domain (we send to them).
-	OutgoingSecLevel SecLevel `protobuf:"varint,3,opt,name=outgoing_sec_level,json=outgoingSecLevel,enum=domaininfo.SecLevel" json:"outgoing_sec_level,omitempty"`
+	OutgoingSecLevel     SecLevel `protobuf:"varint,3,opt,name=outgoing_sec_level,json=outgoingSecLevel,proto3,enum=domaininfo.SecLevel" json:"outgoing_sec_level,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Domain) Reset()         { *m = Domain{} }
+func (m *Domain) String() string { return proto.CompactTextString(m) }
+func (*Domain) ProtoMessage()    {}
+func (*Domain) Descriptor() ([]byte, []int) {
+	return fileDescriptor_622326b6f7a15daa, []int{0}
+}
+
+func (m *Domain) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Domain.Unmarshal(m, b)
+}
+func (m *Domain) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Domain.Marshal(b, m, deterministic)
+}
+func (m *Domain) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Domain.Merge(m, src)
+}
+func (m *Domain) XXX_Size() int {
+	return xxx_messageInfo_Domain.Size(m)
+}
+func (m *Domain) XXX_DiscardUnknown() {
+	xxx_messageInfo_Domain.DiscardUnknown(m)
 }
 
-func (m *Domain) Reset()                    { *m = Domain{} }
-func (m *Domain) String() string            { return proto.CompactTextString(m) }
-func (*Domain) ProtoMessage()               {}
-func (*Domain) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+var xxx_messageInfo_Domain proto.InternalMessageInfo
+
+func (m *Domain) GetName() string {
+	if m != nil {
+		return m.Name
+	}
+	return ""
+}
+
+func (m *Domain) GetIncomingSecLevel() SecLevel {
+	if m != nil {
+		return m.IncomingSecLevel
+	}
+	return SecLevel_PLAIN
+}
+
+func (m *Domain) GetOutgoingSecLevel() SecLevel {
+	if m != nil {
+		return m.OutgoingSecLevel
+	}
+	return SecLevel_PLAIN
+}
 
 func init() {
-	proto.RegisterType((*Domain)(nil), "domaininfo.Domain")
 	proto.RegisterEnum("domaininfo.SecLevel", SecLevel_name, SecLevel_value)
+	proto.RegisterType((*Domain)(nil), "domaininfo.Domain")
 }
 
-func init() { proto.RegisterFile("domaininfo.proto", fileDescriptor0) }
+func init() { proto.RegisterFile("domaininfo.proto", fileDescriptor_622326b6f7a15daa) }
 
-var fileDescriptor0 = []byte{
-	// 189 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x48, 0xc9, 0xcf, 0x4d,
+var fileDescriptor_622326b6f7a15daa = []byte{
+	// 190 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x48, 0xc9, 0xcf, 0x4d,
 	0xcc, 0xcc, 0xcb, 0xcc, 0x4b, 0xcb, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88,
 	0x28, 0x2d, 0x61, 0xe4, 0x62, 0x73, 0x01, 0x73, 0x85, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53,
 	0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x21, 0x27, 0x2e, 0xa1, 0xcc, 0xbc, 0xe4,
 	0xfc, 0xdc, 0xcc, 0xbc, 0xf4, 0xf8, 0xe2, 0xd4, 0xe4, 0xf8, 0x9c, 0xd4, 0xb2, 0xd4, 0x1c, 0x09,
-	0x26, 0xa0, 0x0a, 0x3e, 0x23, 0x11, 0x3d, 0x24, 0x93, 0x83, 0x53, 0x93, 0x7d, 0x40, 0x72, 0x41,
-	0x02, 0x30, 0xf5, 0x30, 0x11, 0x90, 0x19, 0xf9, 0xa5, 0x25, 0xe9, 0xf9, 0xa8, 0x66, 0x30, 0xe3,
-	0x33, 0x03, 0xa6, 0x1e, 0x26, 0xa2, 0xe5, 0xce, 0xc5, 0x01, 0x37, 0x8f, 0x93, 0x8b, 0x35, 0xc0,
-	0xc7, 0xd1, 0xd3, 0x4f, 0x80, 0x41, 0x88, 0x8f, 0x8b, 0x2b, 0xc4, 0x27, 0x38, 0xde, 0xd9, 0xc7,
-	0xd3, 0xd5, 0x2f, 0x44, 0x80, 0x51, 0x48, 0x80, 0x8b, 0x07, 0xc4, 0xf7, 0xf4, 0x0b, 0x76, 0x75,
-	0x0e, 0x0d, 0x72, 0x15, 0x60, 0x82, 0xa9, 0x80, 0xf2, 0x99, 0x93, 0xd8, 0xc0, 0x41, 0x60, 0x0c,
-	0x08, 0x00, 0x00, 0xff, 0xff, 0x2c, 0x78, 0x65, 0x5b, 0x16, 0x01, 0x00, 0x00,
+	0x26, 0x05, 0x46, 0x0d, 0x3e, 0x23, 0x11, 0x3d, 0x24, 0x93, 0x83, 0x53, 0x93, 0x7d, 0x40, 0x72,
+	0x41, 0x02, 0x30, 0xf5, 0x30, 0x11, 0x90, 0x19, 0xf9, 0xa5, 0x25, 0xe9, 0xf9, 0xa8, 0x66, 0x30,
+	0xe3, 0x33, 0x03, 0xa6, 0x1e, 0x26, 0xa2, 0xe5, 0xce, 0xc5, 0x01, 0x37, 0x8f, 0x93, 0x8b, 0x35,
+	0xc0, 0xc7, 0xd1, 0xd3, 0x4f, 0x80, 0x41, 0x88, 0x8f, 0x8b, 0x2b, 0xc4, 0x27, 0x38, 0xde, 0xd9,
+	0xc7, 0xd3, 0xd5, 0x2f, 0x44, 0x80, 0x51, 0x48, 0x80, 0x8b, 0x07, 0xc4, 0xf7, 0xf4, 0x0b, 0x76,
+	0x75, 0x0e, 0x0d, 0x72, 0x15, 0x60, 0x82, 0xa9, 0x80, 0xf2, 0x99, 0x93, 0xd8, 0xc0, 0x41, 0x60,
+	0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x2c, 0x78, 0x65, 0x5b, 0x16, 0x01, 0x00, 0x00,
 }
diff --git a/internal/protoio/testpb/testpb.pb.go b/internal/protoio/testpb/testpb.pb.go
index 01e9c4f..e29be55 100644
--- a/internal/protoio/testpb/testpb.pb.go
+++ b/internal/protoio/testpb/testpb.pb.go
@@ -1,21 +1,13 @@
-// Code generated by protoc-gen-go.
+// Code generated by protoc-gen-go. DO NOT EDIT.
 // source: testpb.proto
-// DO NOT EDIT!
 
-/*
-Package testpb is a generated protocol buffer package.
-
-It is generated from these files:
-	testpb.proto
-
-It has these top-level messages:
-	M
-*/
 package testpb
 
-import proto "github.com/golang/protobuf/proto"
-import fmt "fmt"
-import math "math"
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
@@ -26,26 +18,56 @@ var _ = math.Inf
 // is compatible with the proto package it is being compiled against.
 // A compilation error at this line likely means your copy of the
 // proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
 type M struct {
-	Content string `protobuf:"bytes,1,opt,name=content" json:"content,omitempty"`
+	Content              string   `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *M) Reset()         { *m = M{} }
+func (m *M) String() string { return proto.CompactTextString(m) }
+func (*M) ProtoMessage()    {}
+func (*M) Descriptor() ([]byte, []int) {
+	return fileDescriptor_1b98c0ed33edeb52, []int{0}
+}
+
+func (m *M) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_M.Unmarshal(m, b)
 }
+func (m *M) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_M.Marshal(b, m, deterministic)
+}
+func (m *M) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_M.Merge(m, src)
+}
+func (m *M) XXX_Size() int {
+	return xxx_messageInfo_M.Size(m)
+}
+func (m *M) XXX_DiscardUnknown() {
+	xxx_messageInfo_M.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_M proto.InternalMessageInfo
 
-func (m *M) Reset()                    { *m = M{} }
-func (m *M) String() string            { return proto.CompactTextString(m) }
-func (*M) ProtoMessage()               {}
-func (*M) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+func (m *M) GetContent() string {
+	if m != nil {
+		return m.Content
+	}
+	return ""
+}
 
 func init() {
 	proto.RegisterType((*M)(nil), "testpb.M")
 }
 
-func init() { proto.RegisterFile("testpb.proto", fileDescriptor0) }
+func init() { proto.RegisterFile("testpb.proto", fileDescriptor_1b98c0ed33edeb52) }
 
-var fileDescriptor0 = []byte{
+var fileDescriptor_1b98c0ed33edeb52 = []byte{
 	// 72 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0x49, 0x2d, 0x2e,
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0x49, 0x2d, 0x2e,
 	0x29, 0x48, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x83, 0xf0, 0x94, 0x64, 0xb9, 0x18,
 	0x7d, 0x85, 0x24, 0xb8, 0xd8, 0x93, 0xf3, 0xf3, 0x4a, 0x52, 0xf3, 0x4a, 0x24, 0x18, 0x15, 0x18,
 	0x35, 0x38, 0x83, 0x60, 0xdc, 0x24, 0x36, 0xb0, 0x6a, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff,
diff --git a/internal/queue/dsn_test.go b/internal/queue/dsn_test.go
index 70edabc..9b2988e 100644
--- a/internal/queue/dsn_test.go
+++ b/internal/queue/dsn_test.go
@@ -25,14 +25,14 @@ func TestDSN(t *testing.T) {
 			From: "from@from.org",
 			To:   []string{"ñaca@africa.org", "negra@sosa.org"},
 			Rcpt: []*Recipient{
-				{"poe@rcpt", Recipient_EMAIL, Recipient_FAILED,
-					"oh! horror!", "ñaca@africa.org"},
-				{"muchos@rcpt", Recipient_EMAIL, Recipient_FAILED,
-					multilineErr, "pepe@africa.org"},
-				{"newman@rcpt", Recipient_EMAIL, Recipient_PENDING,
-					"oh! the humanity!", "ñaca@africa.org"},
-				{"ant@rcpt", Recipient_EMAIL, Recipient_SENT,
-					"", "negra@sosa.org"},
+				mkR("poe@rcpt", Recipient_EMAIL, Recipient_FAILED,
+					"oh! horror!", "ñaca@africa.org"),
+				mkR("muchos@rcpt", Recipient_EMAIL, Recipient_FAILED,
+					multilineErr, "pepe@africa.org"),
+				mkR("newman@rcpt", Recipient_EMAIL, Recipient_PENDING,
+					"oh! the humanity!", "ñaca@africa.org"),
+				mkR("ant@rcpt", Recipient_EMAIL, Recipient_SENT,
+					"", "negra@sosa.org"),
 			},
 			Data: []byte(data),
 		},
diff --git a/internal/queue/queue.pb.go b/internal/queue/queue.pb.go
index 5ac595f..94bfc29 100644
--- a/internal/queue/queue.pb.go
+++ b/internal/queue/queue.pb.go
@@ -1,23 +1,14 @@
-// Code generated by protoc-gen-go.
+// Code generated by protoc-gen-go. DO NOT EDIT.
 // source: queue.proto
-// DO NOT EDIT!
 
-/*
-Package queue is a generated protocol buffer package.
-
-It is generated from these files:
-	queue.proto
-
-It has these top-level messages:
-	Message
-	Recipient
-*/
 package queue
 
-import proto "github.com/golang/protobuf/proto"
-import fmt "fmt"
-import math "math"
-import google_protobuf "github.com/golang/protobuf/ptypes/timestamp"
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	timestamp "github.com/golang/protobuf/ptypes/timestamp"
+	math "math"
+)
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
@@ -28,7 +19,7 @@ var _ = math.Inf
 // is compatible with the proto package it is being compiled against.
 // A compilation error at this line likely means your copy of the
 // proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
 type Recipient_Type int32
 
@@ -41,6 +32,7 @@ var Recipient_Type_name = map[int32]string{
 	0: "EMAIL",
 	1: "PIPE",
 }
+
 var Recipient_Type_value = map[string]int32{
 	"EMAIL": 0,
 	"PIPE":  1,
@@ -49,7 +41,10 @@ var Recipient_Type_value = map[string]int32{
 func (x Recipient_Type) String() string {
 	return proto.EnumName(Recipient_Type_name, int32(x))
 }
-func (Recipient_Type) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} }
+
+func (Recipient_Type) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_96e4d7d76a734cd8, []int{1, 0}
+}
 
 type Recipient_Status int32
 
@@ -64,6 +59,7 @@ var Recipient_Status_name = map[int32]string{
 	1: "SENT",
 	2: "FAILED",
 }
+
 var Recipient_Status_value = map[string]int32{
 	"PENDING": 0,
 	"SENT":    1,
@@ -73,25 +69,72 @@ var Recipient_Status_value = map[string]int32{
 func (x Recipient_Status) String() string {
 	return proto.EnumName(Recipient_Status_name, int32(x))
 }
-func (Recipient_Status) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 1} }
+
+func (Recipient_Status) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_96e4d7d76a734cd8, []int{1, 1}
+}
 
 type Message struct {
 	// Message ID. Uniquely identifies this message, it is used for
 	// auditing and troubleshooting.
-	ID string `protobuf:"bytes,1,opt,name=ID,json=iD" json:"ID,omitempty"`
+	ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
 	// The envelope for this message.
-	From string       `protobuf:"bytes,2,opt,name=from" json:"from,omitempty"`
-	To   []string     `protobuf:"bytes,3,rep,name=To,json=to" json:"To,omitempty"`
-	Rcpt []*Recipient `protobuf:"bytes,4,rep,name=rcpt" json:"rcpt,omitempty"`
+	From string       `protobuf:"bytes,2,opt,name=from,proto3" json:"from,omitempty"`
+	To   []string     `protobuf:"bytes,3,rep,name=To,proto3" json:"To,omitempty"`
+	Rcpt []*Recipient `protobuf:"bytes,4,rep,name=rcpt,proto3" json:"rcpt,omitempty"`
 	Data []byte       `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"`
 	// Creation timestamp.
-	CreatedAtTs *google_protobuf.Timestamp `protobuf:"bytes,6,opt,name=created_at_ts,json=createdAtTs" json:"created_at_ts,omitempty"`
+	CreatedAtTs          *timestamp.Timestamp `protobuf:"bytes,6,opt,name=created_at_ts,json=createdAtTs,proto3" json:"created_at_ts,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}             `json:"-"`
+	XXX_unrecognized     []byte               `json:"-"`
+	XXX_sizecache        int32                `json:"-"`
+}
+
+func (m *Message) Reset()         { *m = Message{} }
+func (m *Message) String() string { return proto.CompactTextString(m) }
+func (*Message) ProtoMessage()    {}
+func (*Message) Descriptor() ([]byte, []int) {
+	return fileDescriptor_96e4d7d76a734cd8, []int{0}
+}
+
+func (m *Message) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Message.Unmarshal(m, b)
+}
+func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Message.Marshal(b, m, deterministic)
+}
+func (m *Message) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Message.Merge(m, src)
+}
+func (m *Message) XXX_Size() int {
+	return xxx_messageInfo_Message.Size(m)
+}
+func (m *Message) XXX_DiscardUnknown() {
+	xxx_messageInfo_Message.DiscardUnknown(m)
 }
 
-func (m *Message) Reset()                    { *m = Message{} }
-func (m *Message) String() string            { return proto.CompactTextString(m) }
-func (*Message) ProtoMessage()               {}
-func (*Message) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+var xxx_messageInfo_Message proto.InternalMessageInfo
+
+func (m *Message) GetID() string {
+	if m != nil {
+		return m.ID
+	}
+	return ""
+}
+
+func (m *Message) GetFrom() string {
+	if m != nil {
+		return m.From
+	}
+	return ""
+}
+
+func (m *Message) GetTo() []string {
+	if m != nil {
+		return m.To
+	}
+	return nil
+}
 
 func (m *Message) GetRcpt() []*Recipient {
 	if m != nil {
@@ -100,7 +143,14 @@ func (m *Message) GetRcpt() []*Recipient {
 	return nil
 }
 
-func (m *Message) GetCreatedAtTs() *google_protobuf.Timestamp {
+func (m *Message) GetData() []byte {
+	if m != nil {
+		return m.Data
+	}
+	return nil
+}
+
+func (m *Message) GetCreatedAtTs() *timestamp.Timestamp {
 	if m != nil {
 		return m.CreatedAtTs
 	}
@@ -110,55 +160,113 @@ func (m *Message) GetCreatedAtTs() *google_protobuf.Timestamp {
 type Recipient struct {
 	// Address to send the message to.
 	// This is the final one, after expanding aliases.
-	Address            string           `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"`
-	Type               Recipient_Type   `protobuf:"varint,2,opt,name=type,enum=queue.Recipient_Type" json:"type,omitempty"`
-	Status             Recipient_Status `protobuf:"varint,3,opt,name=status,enum=queue.Recipient_Status" json:"status,omitempty"`
-	LastFailureMessage string           `protobuf:"bytes,4,opt,name=last_failure_message,json=lastFailureMessage" json:"last_failure_message,omitempty"`
+	Address            string           `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
+	Type               Recipient_Type   `protobuf:"varint,2,opt,name=type,proto3,enum=queue.Recipient_Type" json:"type,omitempty"`
+	Status             Recipient_Status `protobuf:"varint,3,opt,name=status,proto3,enum=queue.Recipient_Status" json:"status,omitempty"`
+	LastFailureMessage string           `protobuf:"bytes,4,opt,name=last_failure_message,json=lastFailureMessage,proto3" json:"last_failure_message,omitempty"`
 	// Address that this recipient was originally intended to.
 	// This is before expanding aliases and only used in very particular
 	// cases.
-	OriginalAddress string `protobuf:"bytes,5,opt,name=original_address,json=originalAddress" json:"original_address,omitempty"`
+	OriginalAddress      string   `protobuf:"bytes,5,opt,name=original_address,json=originalAddress,proto3" json:"original_address,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Recipient) Reset()         { *m = Recipient{} }
+func (m *Recipient) String() string { return proto.CompactTextString(m) }
+func (*Recipient) ProtoMessage()    {}
+func (*Recipient) Descriptor() ([]byte, []int) {
+	return fileDescriptor_96e4d7d76a734cd8, []int{1}
+}
+
+func (m *Recipient) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Recipient.Unmarshal(m, b)
+}
+func (m *Recipient) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Recipient.Marshal(b, m, deterministic)
+}
+func (m *Recipient) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Recipient.Merge(m, src)
+}
+func (m *Recipient) XXX_Size() int {
+	return xxx_messageInfo_Recipient.Size(m)
+}
+func (m *Recipient) XXX_DiscardUnknown() {
+	xxx_messageInfo_Recipient.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Recipient proto.InternalMessageInfo
+
+func (m *Recipient) GetAddress() string {
+	if m != nil {
+		return m.Address
+	}
+	return ""
+}
+
+func (m *Recipient) GetType() Recipient_Type {
+	if m != nil {
+		return m.Type
+	}
+	return Recipient_EMAIL
 }
 
-func (m *Recipient) Reset()                    { *m = Recipient{} }
-func (m *Recipient) String() string            { return proto.CompactTextString(m) }
-func (*Recipient) ProtoMessage()               {}
-func (*Recipient) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+func (m *Recipient) GetStatus() Recipient_Status {
+	if m != nil {
+		return m.Status
+	}
+	return Recipient_PENDING
+}
+
+func (m *Recipient) GetLastFailureMessage() string {
+	if m != nil {
+		return m.LastFailureMessage
+	}
+	return ""
+}
+
+func (m *Recipient) GetOriginalAddress() string {
+	if m != nil {
+		return m.OriginalAddress
+	}
+	return ""
+}
 
 func init() {
-	proto.RegisterType((*Message)(nil), "queue.Message")
-	proto.RegisterType((*Recipient)(nil), "queue.Recipient")
 	proto.RegisterEnum("queue.Recipient_Type", Recipient_Type_name, Recipient_Type_value)
 	proto.RegisterEnum("queue.Recipient_Status", Recipient_Status_name, Recipient_Status_value)
+	proto.RegisterType((*Message)(nil), "queue.Message")
+	proto.RegisterType((*Recipient)(nil), "queue.Recipient")
 }
 
-func init() { proto.RegisterFile("queue.proto", fileDescriptor0) }
-
-var fileDescriptor0 = []byte{
-	// 386 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x50, 0xd1, 0x0e, 0x93, 0x30,
-	0x14, 0x15, 0x06, 0xcc, 0x5d, 0x74, 0x92, 0x46, 0x23, 0x99, 0x2f, 0x0b, 0xf1, 0xc1, 0xc5, 0x04,
-	0xcc, 0x7c, 0x34, 0x31, 0x59, 0x02, 0x33, 0x24, 0x6e, 0x59, 0x18, 0xef, 0xa4, 0x83, 0x0e, 0x9b,
-	0xc0, 0x8a, 0xb4, 0x3c, 0xf8, 0x47, 0xfe, 0x81, 0xbf, 0x67, 0x5b, 0x40, 0x13, 0x7d, 0xbb, 0x3d,
-	0xe7, 0xf4, 0xde, 0x73, 0x0e, 0xb8, 0xdf, 0x07, 0x32, 0x90, 0xb0, 0xeb, 0x99, 0x60, 0xc8, 0xd6,
-	0x8f, 0xcd, 0xa7, 0x9a, 0x8a, 0x6f, 0xc3, 0x2d, 0x2c, 0x59, 0x1b, 0xd5, 0xac, 0xc1, 0x8f, 0x3a,
-	0xd2, 0xfc, 0x6d, 0xb8, 0x47, 0x9d, 0xf8, 0xd1, 0x11, 0x1e, 0x09, 0xda, 0x12, 0x2e, 0x70, 0xdb,
-	0xfd, 0x9d, 0xc6, 0x1d, 0xc1, 0x2f, 0x03, 0x96, 0x27, 0xc2, 0x39, 0xae, 0x09, 0x5a, 0x83, 0x99,
-	0xc6, 0xbe, 0xb1, 0x35, 0xde, 0xad, 0x32, 0x93, 0xc6, 0x08, 0x81, 0x75, 0xef, 0x59, 0xeb, 0x9b,
-	0x1a, 0xd1, 0xb3, 0xd2, 0xe4, 0xcc, 0x5f, 0x6c, 0x17, 0x4a, 0x23, 0x3d, 0xbc, 0x05, 0xab, 0x2f,
-	0x3b, 0xe1, 0x5b, 0x12, 0x71, 0xf7, 0x5e, 0x38, 0xfa, 0xcb, 0x48, 0x49, 0x3b, 0x4a, 0x1e, 0x22,
-	0xd3, 0xac, 0xda, 0x54, 0x61, 0x81, 0x7d, 0x5b, 0x6e, 0x7a, 0x96, 0xe9, 0x19, 0x7d, 0x86, 0xe7,
-	0x65, 0x4f, 0xb0, 0x20, 0x55, 0x81, 0x45, 0x21, 0xb8, 0xef, 0x48, 0xd2, 0xdd, 0x6f, 0xc2, 0x9a,
-	0xb1, 0xba, 0x99, 0x32, 0xca, 0x0c, 0x61, 0x3e, 0x5b, 0xce, 0xdc, 0xe9, 0xc3, 0x41, 0xe4, 0x3c,
-	0xf8, 0x69, 0xc2, 0xea, 0xcf, 0x1d, 0xe4, 0xc3, 0x12, 0x57, 0x55, 0x2f, 0x93, 0x4c, 0x01, 0xe6,
-	0x27, 0xda, 0x81, 0xa5, 0x4a, 0xd0, 0x29, 0xd6, 0xfb, 0x57, 0xff, 0x3a, 0x0c, 0x73, 0x49, 0x66,
-	0x5a, 0x82, 0x22, 0x70, 0xe4, 0x21, 0x31, 0x70, 0x19, 0x50, 0x89, 0x5f, 0xff, 0x27, 0xbe, 0x6a,
-	0x3a, 0x9b, 0x64, 0xe8, 0x03, 0xbc, 0x6c, 0x30, 0x17, 0xc5, 0x1d, 0xd3, 0x66, 0xe8, 0x49, 0xd1,
-	0x8e, 0x4d, 0xca, 0x36, 0x94, 0x05, 0xa4, 0xb8, 0xe3, 0x48, 0xcd, 0x1d, 0xef, 0xc0, 0x63, 0x3d,
-	0xad, 0xe9, 0x03, 0x37, 0xc5, 0x6c, 0xd8, 0xd6, 0xea, 0x17, 0x33, 0x7e, 0x18, 0xe1, 0xe0, 0x0d,
-	0x58, 0xca, 0x1b, 0x5a, 0x81, 0x9d, 0x9c, 0x0e, 0xe9, 0x57, 0xef, 0x09, 0x7a, 0x0a, 0xd6, 0x25,
-	0xbd, 0x24, 0x9e, 0x11, 0xbc, 0x07, 0x67, 0xf4, 0x82, 0x5c, 0x58, 0x5e, 0x92, 0x73, 0x9c, 0x9e,
-	0xbf, 0x8c, 0x82, 0x6b, 0x72, 0xce, 0x3d, 0x03, 0x01, 0x38, 0x47, 0xf9, 0x29, 0x89, 0x3d, 0xf3,
-	0xe6, 0xe8, 0x2e, 0x3f, 0xfe, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xda, 0xc1, 0x0d, 0xbb, 0x3e, 0x02,
-	0x00, 0x00,
+func init() { proto.RegisterFile("queue.proto", fileDescriptor_96e4d7d76a734cd8) }
+
+var fileDescriptor_96e4d7d76a734cd8 = []byte{
+	// 391 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0xcf, 0x8a, 0x9c, 0x40,
+	0x10, 0xc6, 0xe3, 0xdf, 0xc9, 0x94, 0xc9, 0x46, 0x9a, 0x84, 0x34, 0x9b, 0x8b, 0x48, 0x0e, 0x2e,
+	0x01, 0x0d, 0x93, 0x63, 0x20, 0x20, 0xe8, 0x06, 0x61, 0x67, 0x18, 0x1c, 0xef, 0xd2, 0x6a, 0x6b,
+	0x04, 0x9d, 0x36, 0x76, 0x7b, 0x98, 0x37, 0xca, 0x1b, 0xe4, 0xf5, 0x82, 0xad, 0x26, 0x90, 0xbd,
+	0x55, 0xd5, 0xf7, 0xeb, 0xea, 0xaf, 0x3e, 0xb0, 0x7e, 0x4e, 0x74, 0xa2, 0xfe, 0x30, 0x32, 0xc1,
+	0x90, 0x21, 0x9b, 0xfb, 0xaf, 0x4d, 0x2b, 0x7e, 0x4c, 0x85, 0x5f, 0xb2, 0x3e, 0x68, 0x58, 0x47,
+	0xae, 0x4d, 0x20, 0xf5, 0x62, 0xaa, 0x83, 0x41, 0xdc, 0x06, 0xca, 0x03, 0xd1, 0xf6, 0x94, 0x0b,
+	0xd2, 0x0f, 0xff, 0xaa, 0x65, 0x87, 0xfb, 0x5b, 0x81, 0xdd, 0x91, 0x72, 0x4e, 0x1a, 0x8a, 0xee,
+	0x40, 0x4d, 0x22, 0xac, 0x38, 0x8a, 0xb7, 0x4f, 0xd5, 0x24, 0x42, 0x08, 0xf4, 0x7a, 0x64, 0x3d,
+	0x56, 0xe5, 0x44, 0xd6, 0x33, 0x93, 0x31, 0xac, 0x39, 0xda, 0xcc, 0x64, 0x0c, 0x7d, 0x04, 0x7d,
+	0x2c, 0x07, 0x81, 0x75, 0x47, 0xf3, 0xac, 0x83, 0xed, 0x2f, 0xfe, 0x52, 0x5a, 0xb6, 0x43, 0x4b,
+	0xaf, 0x22, 0x95, 0xea, 0xbc, 0xa9, 0x22, 0x82, 0x60, 0xc3, 0x51, 0xbc, 0x57, 0xa9, 0xac, 0xd1,
+	0x37, 0x78, 0x5d, 0x8e, 0x94, 0x08, 0x5a, 0xe5, 0x44, 0xe4, 0x82, 0x63, 0xd3, 0x51, 0x3c, 0xeb,
+	0x70, 0xef, 0x37, 0x8c, 0x35, 0xdd, 0x7a, 0x63, 0x31, 0xd5, 0x7e, 0xb6, 0x59, 0x4e, 0xad, 0xf5,
+	0x41, 0x28, 0x32, 0xee, 0xfe, 0x52, 0x61, 0xff, 0xf7, 0x1f, 0x84, 0x61, 0x47, 0xaa, 0x6a, 0xa4,
+	0x9c, 0xaf, 0x07, 0x6c, 0x2d, 0x7a, 0x00, 0x7d, 0x0e, 0x41, 0x5e, 0x71, 0x77, 0x78, 0xf7, 0xbf,
+	0x43, 0x3f, 0xbb, 0x0d, 0x34, 0x95, 0x08, 0x0a, 0xc0, 0xe4, 0x82, 0x88, 0x89, 0x63, 0x4d, 0xc2,
+	0xef, 0x9f, 0xc1, 0x17, 0x29, 0xa7, 0x2b, 0x86, 0x3e, 0xc3, 0xdb, 0x8e, 0x70, 0x91, 0xd7, 0xa4,
+	0xed, 0xa6, 0x91, 0xe6, 0xfd, 0x92, 0x24, 0xd6, 0xa5, 0x05, 0x34, 0x6b, 0x8f, 0x8b, 0xb4, 0x65,
+	0xfc, 0x00, 0x36, 0x1b, 0xdb, 0xa6, 0xbd, 0x92, 0x2e, 0xdf, 0x0c, 0x1b, 0x92, 0x7e, 0xb3, 0xcd,
+	0xc3, 0x65, 0xec, 0x7e, 0x00, 0x7d, 0xf6, 0x86, 0xf6, 0x60, 0xc4, 0xc7, 0x30, 0x79, 0xb2, 0x5f,
+	0xa0, 0x97, 0xa0, 0x9f, 0x93, 0x73, 0x6c, 0x2b, 0xee, 0x27, 0x30, 0x17, 0x2f, 0xc8, 0x82, 0xdd,
+	0x39, 0x3e, 0x45, 0xc9, 0xe9, 0xfb, 0x02, 0x5c, 0xe2, 0x53, 0x66, 0x2b, 0x08, 0xc0, 0x7c, 0x0c,
+	0x93, 0xa7, 0x38, 0xb2, 0xd5, 0xc2, 0x94, 0x59, 0x7e, 0xf9, 0x13, 0x00, 0x00, 0xff, 0xff, 0x9b,
+	0xcb, 0xcf, 0x07, 0x3e, 0x02, 0x00, 0x00,
 }
diff --git a/internal/queue/queue_test.go b/internal/queue/queue_test.go
index 7b18bad..7bf35a0 100644
--- a/internal/queue/queue_test.go
+++ b/internal/queue/queue_test.go
@@ -112,7 +112,7 @@ func TestDSNOnTimeout(t *testing.T) {
 			ID:   <-newID,
 			From: fmt.Sprintf("from@loco"),
 			Rcpt: []*Recipient{
-				{"to@to", Recipient_EMAIL, Recipient_PENDING, "err", "to@to"}},
+				mkR("to@to", Recipient_EMAIL, Recipient_PENDING, "err", "to@to")},
 			Data: []byte("data"),
 		},
 		CreatedAt: time.Now().Add(-24 * time.Hour),
@@ -211,7 +211,7 @@ func TestFullQueue(t *testing.T) {
 				ID:   <-newID,
 				From: fmt.Sprintf("from-%d", i),
 				Rcpt: []*Recipient{
-					{"to", Recipient_EMAIL, Recipient_PENDING, "", ""}},
+					mkR("to", Recipient_EMAIL, Recipient_PENDING, "", "")},
 				Data: []byte("data"),
 			},
 			CreatedAt: time.Now(),
@@ -249,7 +249,7 @@ func TestPipes(t *testing.T) {
 			ID:   <-newID,
 			From: "from",
 			Rcpt: []*Recipient{
-				{"true", Recipient_PIPE, Recipient_PENDING, "", ""}},
+				mkR("true", Recipient_PIPE, Recipient_PENDING, "", "")},
 			Data: []byte("data"),
 		},
 		CreatedAt: time.Now(),
@@ -292,7 +292,7 @@ func TestSerialization(t *testing.T) {
 			ID:   <-newID,
 			From: fmt.Sprintf("from@loco"),
 			Rcpt: []*Recipient{
-				{"to@to", Recipient_EMAIL, Recipient_PENDING, "err", "to@to"}},
+				mkR("to@to", Recipient_EMAIL, Recipient_PENDING, "err", "to@to")},
 			Data: []byte("data"),
 		},
 		CreatedAt: time.Now().Add(-1 * time.Hour),
@@ -321,3 +321,13 @@ func TestSerialization(t *testing.T) {
 		t.Errorf("wrong email: %v", req)
 	}
 }
+
+func mkR(a string, t Recipient_Type, s Recipient_Status, m, o string) *Recipient {
+	return &Recipient{
+		Address:            a,
+		Type:               t,
+		Status:             s,
+		LastFailureMessage: m,
+		OriginalAddress:    o,
+	}
+}
diff --git a/internal/tlsconst/ciphers.go b/internal/tlsconst/ciphers.go
index 331e699..32a910d 100644
--- a/internal/tlsconst/ciphers.go
+++ b/internal/tlsconst/ciphers.go
@@ -147,6 +147,8 @@ var cipherSuiteName = map[uint16]string{
 	0x00c3: "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256",
 	0x00c4: "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",
 	0x00c5: "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256",
+	0x00c6: "TLS_SM4_GCM_SM3",
+	0x00c7: "TLS_SM4_CCM_SM3",
 	0x00ff: "TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
 	0x1301: "TLS_AES_128_GCM_SHA256",
 	0x1302: "TLS_AES_256_GCM_SHA384",
@@ -333,6 +335,11 @@ var cipherSuiteName = map[uint16]string{
 	0xc0b1: "TLS_ECCPWD_WITH_AES_256_GCM_SHA384",
 	0xc0b2: "TLS_ECCPWD_WITH_AES_128_CCM_SHA256",
 	0xc0b3: "TLS_ECCPWD_WITH_AES_256_CCM_SHA384",
+	0xc0b4: "TLS_SHA256_SHA256",
+	0xc0b5: "TLS_SHA384_SHA384",
+	0xc100: "TLS_GOSTR341112_256_WITH_KUZNYECHIK_CTR_OMAC",
+	0xc101: "TLS_GOSTR341112_256_WITH_MAGMA_CTR_OMAC",
+	0xc102: "TLS_GOSTR341112_256_WITH_28147_CNT_IMIT",
 	0xcca8: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
 	0xcca9: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
 	0xccaa: "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
diff --git a/internal/userdb/userdb.pb.go b/internal/userdb/userdb.pb.go
index c94d8f1..859d489 100644
--- a/internal/userdb/userdb.pb.go
+++ b/internal/userdb/userdb.pb.go
@@ -1,24 +1,13 @@
-// Code generated by protoc-gen-go.
+// Code generated by protoc-gen-go. DO NOT EDIT.
 // source: userdb.proto
-// DO NOT EDIT!
 
-/*
-Package userdb is a generated protocol buffer package.
-
-It is generated from these files:
-	userdb.proto
-
-It has these top-level messages:
-	ProtoDB
-	Password
-	Scrypt
-	Plain
-*/
 package userdb
 
-import proto "github.com/golang/protobuf/proto"
-import fmt "fmt"
-import math "math"
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
@@ -29,16 +18,39 @@ var _ = math.Inf
 // is compatible with the proto package it is being compiled against.
 // A compilation error at this line likely means your copy of the
 // proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
 type ProtoDB struct {
-	Users map[string]*Password `protobuf:"bytes,1,rep,name=users" json:"users,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+	Users                map[string]*Password `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+	XXX_NoUnkeyedLiteral struct{}             `json:"-"`
+	XXX_unrecognized     []byte               `json:"-"`
+	XXX_sizecache        int32                `json:"-"`
+}
+
+func (m *ProtoDB) Reset()         { *m = ProtoDB{} }
+func (m *ProtoDB) String() string { return proto.CompactTextString(m) }
+func (*ProtoDB) ProtoMessage()    {}
+func (*ProtoDB) Descriptor() ([]byte, []int) {
+	return fileDescriptor_1492f046cfa19579, []int{0}
+}
+
+func (m *ProtoDB) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ProtoDB.Unmarshal(m, b)
+}
+func (m *ProtoDB) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ProtoDB.Marshal(b, m, deterministic)
+}
+func (m *ProtoDB) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ProtoDB.Merge(m, src)
+}
+func (m *ProtoDB) XXX_Size() int {
+	return xxx_messageInfo_ProtoDB.Size(m)
+}
+func (m *ProtoDB) XXX_DiscardUnknown() {
+	xxx_messageInfo_ProtoDB.DiscardUnknown(m)
 }
 
-func (m *ProtoDB) Reset()                    { *m = ProtoDB{} }
-func (m *ProtoDB) String() string            { return proto.CompactTextString(m) }
-func (*ProtoDB) ProtoMessage()               {}
-func (*ProtoDB) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+var xxx_messageInfo_ProtoDB proto.InternalMessageInfo
 
 func (m *ProtoDB) GetUsers() map[string]*Password {
 	if m != nil {
@@ -51,27 +63,52 @@ type Password struct {
 	// Types that are valid to be assigned to Scheme:
 	//	*Password_Scrypt
 	//	*Password_Plain
-	Scheme isPassword_Scheme `protobuf_oneof:"scheme"`
+	Scheme               isPassword_Scheme `protobuf_oneof:"scheme"`
+	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
+	XXX_unrecognized     []byte            `json:"-"`
+	XXX_sizecache        int32             `json:"-"`
 }
 
-func (m *Password) Reset()                    { *m = Password{} }
-func (m *Password) String() string            { return proto.CompactTextString(m) }
-func (*Password) ProtoMessage()               {}
-func (*Password) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+func (m *Password) Reset()         { *m = Password{} }
+func (m *Password) String() string { return proto.CompactTextString(m) }
+func (*Password) ProtoMessage()    {}
+func (*Password) Descriptor() ([]byte, []int) {
+	return fileDescriptor_1492f046cfa19579, []int{1}
+}
+
+func (m *Password) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Password.Unmarshal(m, b)
+}
+func (m *Password) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Password.Marshal(b, m, deterministic)
+}
+func (m *Password) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Password.Merge(m, src)
+}
+func (m *Password) XXX_Size() int {
+	return xxx_messageInfo_Password.Size(m)
+}
+func (m *Password) XXX_DiscardUnknown() {
+	xxx_messageInfo_Password.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Password proto.InternalMessageInfo
 
 type isPassword_Scheme interface {
 	isPassword_Scheme()
 }
 
 type Password_Scrypt struct {
-	Scrypt *Scrypt `protobuf:"bytes,2,opt,name=scrypt,oneof"`
+	Scrypt *Scrypt `protobuf:"bytes,2,opt,name=scrypt,proto3,oneof"`
 }
+
 type Password_Plain struct {
-	Plain *Plain `protobuf:"bytes,3,opt,name=plain,oneof"`
+	Plain *Plain `protobuf:"bytes,3,opt,name=plain,proto3,oneof"`
 }
 
 func (*Password_Scrypt) isPassword_Scheme() {}
-func (*Password_Plain) isPassword_Scheme()  {}
+
+func (*Password_Plain) isPassword_Scheme() {}
 
 func (m *Password) GetScheme() isPassword_Scheme {
 	if m != nil {
@@ -94,131 +131,161 @@ func (m *Password) GetPlain() *Plain {
 	return nil
 }
 
-// XXX_OneofFuncs is for the internal use of the proto package.
-func (*Password) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
-	return _Password_OneofMarshaler, _Password_OneofUnmarshaler, _Password_OneofSizer, []interface{}{
+// XXX_OneofWrappers is for the internal use of the proto package.
+func (*Password) XXX_OneofWrappers() []interface{} {
+	return []interface{}{
 		(*Password_Scrypt)(nil),
 		(*Password_Plain)(nil),
 	}
 }
 
-func _Password_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
-	m := msg.(*Password)
-	// scheme
-	switch x := m.Scheme.(type) {
-	case *Password_Scrypt:
-		b.EncodeVarint(2<<3 | proto.WireBytes)
-		if err := b.EncodeMessage(x.Scrypt); err != nil {
-			return err
-		}
-	case *Password_Plain:
-		b.EncodeVarint(3<<3 | proto.WireBytes)
-		if err := b.EncodeMessage(x.Plain); err != nil {
-			return err
-		}
-	case nil:
-	default:
-		return fmt.Errorf("Password.Scheme has unexpected type %T", x)
+type Scrypt struct {
+	LogN                 uint64   `protobuf:"varint,1,opt,name=logN,proto3" json:"logN,omitempty"`
+	R                    int32    `protobuf:"varint,2,opt,name=r,proto3" json:"r,omitempty"`
+	P                    int32    `protobuf:"varint,3,opt,name=p,proto3" json:"p,omitempty"`
+	KeyLen               int32    `protobuf:"varint,4,opt,name=keyLen,proto3" json:"keyLen,omitempty"`
+	Salt                 []byte   `protobuf:"bytes,5,opt,name=salt,proto3" json:"salt,omitempty"`
+	Encrypted            []byte   `protobuf:"bytes,6,opt,name=encrypted,proto3" json:"encrypted,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Scrypt) Reset()         { *m = Scrypt{} }
+func (m *Scrypt) String() string { return proto.CompactTextString(m) }
+func (*Scrypt) ProtoMessage()    {}
+func (*Scrypt) Descriptor() ([]byte, []int) {
+	return fileDescriptor_1492f046cfa19579, []int{2}
+}
+
+func (m *Scrypt) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Scrypt.Unmarshal(m, b)
+}
+func (m *Scrypt) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Scrypt.Marshal(b, m, deterministic)
+}
+func (m *Scrypt) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Scrypt.Merge(m, src)
+}
+func (m *Scrypt) XXX_Size() int {
+	return xxx_messageInfo_Scrypt.Size(m)
+}
+func (m *Scrypt) XXX_DiscardUnknown() {
+	xxx_messageInfo_Scrypt.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Scrypt proto.InternalMessageInfo
+
+func (m *Scrypt) GetLogN() uint64 {
+	if m != nil {
+		return m.LogN
+	}
+	return 0
+}
+
+func (m *Scrypt) GetR() int32 {
+	if m != nil {
+		return m.R
 	}
-	return nil
+	return 0
 }
 
-func _Password_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
-	m := msg.(*Password)
-	switch tag {
-	case 2: // scheme.scrypt
-		if wire != proto.WireBytes {
-			return true, proto.ErrInternalBadWireType
-		}
-		msg := new(Scrypt)
-		err := b.DecodeMessage(msg)
-		m.Scheme = &Password_Scrypt{msg}
-		return true, err
-	case 3: // scheme.plain
-		if wire != proto.WireBytes {
-			return true, proto.ErrInternalBadWireType
-		}
-		msg := new(Plain)
-		err := b.DecodeMessage(msg)
-		m.Scheme = &Password_Plain{msg}
-		return true, err
-	default:
-		return false, nil
+func (m *Scrypt) GetP() int32 {
+	if m != nil {
+		return m.P
 	}
+	return 0
 }
 
-func _Password_OneofSizer(msg proto.Message) (n int) {
-	m := msg.(*Password)
-	// scheme
-	switch x := m.Scheme.(type) {
-	case *Password_Scrypt:
-		s := proto.Size(x.Scrypt)
-		n += proto.SizeVarint(2<<3 | proto.WireBytes)
-		n += proto.SizeVarint(uint64(s))
-		n += s
-	case *Password_Plain:
-		s := proto.Size(x.Plain)
-		n += proto.SizeVarint(3<<3 | proto.WireBytes)
-		n += proto.SizeVarint(uint64(s))
-		n += s
-	case nil:
-	default:
-		panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
+func (m *Scrypt) GetKeyLen() int32 {
+	if m != nil {
+		return m.KeyLen
 	}
-	return n
+	return 0
 }
 
-type Scrypt struct {
-	LogN      uint64 `protobuf:"varint,1,opt,name=logN" json:"logN,omitempty"`
-	R         int32  `protobuf:"varint,2,opt,name=r" json:"r,omitempty"`
-	P         int32  `protobuf:"varint,3,opt,name=p" json:"p,omitempty"`
-	KeyLen    int32  `protobuf:"varint,4,opt,name=keyLen" json:"keyLen,omitempty"`
-	Salt      []byte `protobuf:"bytes,5,opt,name=salt,proto3" json:"salt,omitempty"`
-	Encrypted []byte `protobuf:"bytes,6,opt,name=encrypted,proto3" json:"encrypted,omitempty"`
+func (m *Scrypt) GetSalt() []byte {
+	if m != nil {
+		return m.Salt
+	}
+	return nil
 }
 
-func (m *Scrypt) Reset()                    { *m = Scrypt{} }
-func (m *Scrypt) String() string            { return proto.CompactTextString(m) }
-func (*Scrypt) ProtoMessage()               {}
-func (*Scrypt) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+func (m *Scrypt) GetEncrypted() []byte {
+	if m != nil {
+		return m.Encrypted
+	}
+	return nil
+}
 
 type Plain struct {
-	Password []byte `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
+	Password             []byte   `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Plain) Reset()         { *m = Plain{} }
+func (m *Plain) String() string { return proto.CompactTextString(m) }
+func (*Plain) ProtoMessage()    {}
+func (*Plain) Descriptor() ([]byte, []int) {
+	return fileDescriptor_1492f046cfa19579, []int{3}
 }
 
-func (m *Plain) Reset()                    { *m = Plain{} }
-func (m *Plain) String() string            { return proto.CompactTextString(m) }
-func (*Plain) ProtoMessage()               {}
-func (*Plain) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+func (m *Plain) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Plain.Unmarshal(m, b)
+}
+func (m *Plain) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Plain.Marshal(b, m, deterministic)
+}
+func (m *Plain) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Plain.Merge(m, src)
+}
+func (m *Plain) XXX_Size() int {
+	return xxx_messageInfo_Plain.Size(m)
+}
+func (m *Plain) XXX_DiscardUnknown() {
+	xxx_messageInfo_Plain.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Plain proto.InternalMessageInfo
+
+func (m *Plain) GetPassword() []byte {
+	if m != nil {
+		return m.Password
+	}
+	return nil
+}
 
 func init() {
 	proto.RegisterType((*ProtoDB)(nil), "userdb.ProtoDB")
+	proto.RegisterMapType((map[string]*Password)(nil), "userdb.ProtoDB.UsersEntry")
 	proto.RegisterType((*Password)(nil), "userdb.Password")
 	proto.RegisterType((*Scrypt)(nil), "userdb.Scrypt")
 	proto.RegisterType((*Plain)(nil), "userdb.Plain")
 }
 
-func init() { proto.RegisterFile("userdb.proto", fileDescriptor0) }
-
-var fileDescriptor0 = []byte{
-	// 289 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x44, 0x91, 0x41, 0x4b, 0x03, 0x31,
-	0x10, 0x85, 0x4d, 0xdb, 0xc4, 0x76, 0xba, 0x4a, 0x99, 0x83, 0x84, 0xe2, 0x41, 0x56, 0x94, 0x9e,
-	0x16, 0xa9, 0x17, 0xf1, 0x58, 0x14, 0x44, 0x44, 0x24, 0xe2, 0x0f, 0xd8, 0xba, 0x41, 0xc5, 0x75,
-	0x37, 0x24, 0x5b, 0x65, 0xaf, 0x5e, 0xfc, 0xdb, 0x26, 0xb3, 0xe9, 0xf6, 0x36, 0xef, 0x7b, 0x33,
-	0x6f, 0x26, 0x04, 0x92, 0x8d, 0xd3, 0xb6, 0x58, 0x67, 0xc6, 0xd6, 0x4d, 0x8d, 0xa2, 0x53, 0xe9,
-	0x1f, 0x83, 0xfd, 0xa7, 0x40, 0x6e, 0x56, 0x78, 0x01, 0x3c, 0x50, 0x27, 0xd9, 0xc9, 0x70, 0x31,
-	0x5d, 0xce, 0xb3, 0x38, 0x11, 0xfd, 0xec, 0x25, 0x98, 0xb7, 0x55, 0x63, 0x5b, 0xd5, 0x35, 0xce,
-	0xef, 0x01, 0x76, 0x10, 0x67, 0x30, 0xfc, 0xd4, 0xad, 0x9f, 0x66, 0x8b, 0x89, 0x0a, 0x25, 0x9e,
-	0x03, 0xff, 0xce, 0xcb, 0x8d, 0x96, 0x03, 0xcf, 0xa6, 0xcb, 0x59, 0x9f, 0x98, 0x3b, 0xf7, 0x53,
-	0xdb, 0x42, 0x75, 0xf6, 0xf5, 0xe0, 0x8a, 0xa5, 0x1a, 0xc6, 0x5b, 0x8c, 0x0b, 0x10, 0xee, 0xd5,
-	0xb6, 0xa6, 0x89, 0x83, 0x87, 0xdb, 0xc1, 0x67, 0xa2, 0x77, 0x7b, 0x2a, 0xfa, 0x78, 0x06, 0xdc,
-	0x94, 0xf9, 0x47, 0x25, 0x87, 0xd4, 0x78, 0xd0, 0x6f, 0x08, 0xd0, 0xf7, 0x75, 0xee, 0x6a, 0x1c,
-	0x02, 0xdf, 0xf5, 0x97, 0x4e, 0x7f, 0x19, 0x88, 0x2e, 0x05, 0x11, 0x46, 0x65, 0xfd, 0xf6, 0x48,
-	0x07, 0x8f, 0x14, 0xd5, 0x98, 0x00, 0xb3, 0xb4, 0x94, 0x2b, 0x66, 0x83, 0x32, 0x94, 0xec, 0x95,
-	0xc1, 0x23, 0x10, 0xfe, 0x51, 0x0f, 0xba, 0x92, 0x23, 0x42, 0x51, 0x85, 0x1c, 0x97, 0x97, 0x8d,
-	0xe4, 0x9e, 0x26, 0x8a, 0x6a, 0x3c, 0x86, 0x89, 0xae, 0x68, 0x8d, 0x2e, 0xa4, 0x20, 0x63, 0x07,
-	0xd2, 0x53, 0xe0, 0x74, 0x20, 0xce, 0x61, 0x6c, 0xe2, 0xa3, 0xe9, 0x8c, 0x44, 0xf5, 0x7a, 0x2d,
-	0xe8, 0xa7, 0x2e, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe4, 0x93, 0xae, 0x19, 0xb9, 0x01, 0x00,
-	0x00,
+func init() { proto.RegisterFile("userdb.proto", fileDescriptor_1492f046cfa19579) }
+
+var fileDescriptor_1492f046cfa19579 = []byte{
+	// 292 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x91, 0x41, 0x4b, 0xc3, 0x40,
+	0x10, 0x85, 0xbb, 0x6d, 0x77, 0x6d, 0xa7, 0x51, 0xca, 0x1c, 0x64, 0x29, 0x1e, 0x42, 0x44, 0xc9,
+	0x29, 0x48, 0xbd, 0x88, 0xc7, 0xa2, 0x50, 0x44, 0x44, 0x56, 0xfc, 0x01, 0xa9, 0x59, 0x54, 0x1a,
+	0x93, 0x65, 0x37, 0x55, 0x72, 0xf5, 0xe2, 0xdf, 0x96, 0x9d, 0x6c, 0xd3, 0xdb, 0xbc, 0xef, 0xcd,
+	0xbc, 0x99, 0x65, 0x21, 0xda, 0x39, 0x6d, 0x8b, 0x4d, 0x66, 0x6c, 0xdd, 0xd4, 0x28, 0x3a, 0x95,
+	0xfc, 0x31, 0x38, 0x7a, 0xf6, 0xe4, 0x6e, 0x85, 0x57, 0xc0, 0x3d, 0x75, 0x92, 0xc5, 0xa3, 0x74,
+	0xb6, 0x5c, 0x64, 0x61, 0x22, 0xf8, 0xd9, 0xab, 0x37, 0xef, 0xab, 0xc6, 0xb6, 0xaa, 0x6b, 0x5c,
+	0x3c, 0x00, 0x1c, 0x20, 0xce, 0x61, 0xb4, 0xd5, 0xad, 0x64, 0x31, 0x4b, 0xa7, 0xca, 0x97, 0x78,
+	0x09, 0xfc, 0x3b, 0x2f, 0x77, 0x5a, 0x0e, 0x63, 0x96, 0xce, 0x96, 0xf3, 0x3e, 0x31, 0x77, 0xee,
+	0xa7, 0xb6, 0x85, 0xea, 0xec, 0xdb, 0xe1, 0x0d, 0x4b, 0x34, 0x4c, 0xf6, 0x18, 0x53, 0x10, 0xee,
+	0xcd, 0xb6, 0xa6, 0x09, 0x83, 0x27, 0xfb, 0xc1, 0x17, 0xa2, 0xeb, 0x81, 0x0a, 0x3e, 0x5e, 0x00,
+	0x37, 0x65, 0xfe, 0x59, 0xc9, 0x11, 0x35, 0x1e, 0xf7, 0x1b, 0x3c, 0x5c, 0x0f, 0x54, 0xe7, 0xae,
+	0x26, 0x3e, 0xf0, 0x43, 0x7f, 0xe9, 0xe4, 0x97, 0x81, 0xe8, 0x52, 0x10, 0x61, 0x5c, 0xd6, 0xef,
+	0x4f, 0x74, 0xf0, 0x58, 0x51, 0x8d, 0x11, 0x30, 0x4b, 0x4b, 0xb9, 0x62, 0xd6, 0x2b, 0x43, 0xc9,
+	0x5c, 0x31, 0x83, 0xa7, 0x20, 0xb6, 0xba, 0x7d, 0xd4, 0x95, 0x1c, 0x13, 0x0a, 0xca, 0xe7, 0xb8,
+	0xbc, 0x6c, 0x24, 0x8f, 0x59, 0x1a, 0x29, 0xaa, 0xf1, 0x0c, 0xa6, 0xba, 0xa2, 0x35, 0xba, 0x90,
+	0x82, 0x8c, 0x03, 0x48, 0xce, 0x81, 0xd3, 0x81, 0xb8, 0x80, 0x89, 0x09, 0x8f, 0xa6, 0x33, 0x22,
+	0xd5, 0xeb, 0x8d, 0xa0, 0x9f, 0xba, 0xfe, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xe4, 0x93, 0xae, 0x19,
+	0xb9, 0x01, 0x00, 0x00,
 }
diff --git a/internal/userdb/userdb_test.go b/internal/userdb/userdb_test.go
index f4b176a..b9eec38 100644
--- a/internal/userdb/userdb_test.go
+++ b/internal/userdb/userdb_test.go
@@ -208,7 +208,9 @@ func TestInvalidUsername(t *testing.T) {
 
 func plainPassword(p string) *Password {
 	return &Password{
-		Scheme: &Password_Plain{&Plain{[]byte(p)}},
+		Scheme: &Password_Plain{
+			Plain: &Plain{Password: []byte(p)},
+		},
 	}
 }
 
diff --git a/test/t-03-queue_persistency/addtoqueue.go b/test/t-03-queue_persistency/addtoqueue.go
index 446ceb0..55bb8e0 100644
--- a/test/t-03-queue_persistency/addtoqueue.go
+++ b/test/t-03-queue_persistency/addtoqueue.go
@@ -40,7 +40,11 @@ func main() {
 			From: *from,
 			To:   []string{*rcpt},
 			Rcpt: []*queue.Recipient{
-				{*rcpt, queue.Recipient_EMAIL, queue.Recipient_PENDING, "", ""},
+				{
+					Address: *rcpt,
+					Type:    queue.Recipient_EMAIL,
+					Status:  queue.Recipient_PENDING,
+				},
 			},
 			Data: data,
 		},