f8d9fa9e80
This upgrades all of libgo other than the runtime package to the Go 1.4 release. In Go 1.4 much of the runtime was rewritten into Go. Merging that code will take more time and will not change the API, so I'm putting it off for now. There are a few runtime changes anyhow, to accomodate other packages that rely on minor modifications to the runtime support. The compiler changes slightly to add a one-bit flag to each type descriptor kind that is stored directly in an interface, which for gccgo is currently only pointer types. Another one-bit flag (gcprog) is reserved because it is used by the gc compiler, but gccgo does not currently use it. There is another error check in the compiler since I ran across it during testing. gotools/: * Makefile.am (go_cmd_go_files): Sort entries. Add generate.go. * Makefile.in: Rebuild. From-SVN: r219627
695 lines
17 KiB
Go
695 lines
17 KiB
Go
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package smtp
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"io"
|
|
"net"
|
|
"net/textproto"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type authTest struct {
|
|
auth Auth
|
|
challenges []string
|
|
name string
|
|
responses []string
|
|
}
|
|
|
|
var authTests = []authTest{
|
|
{PlainAuth("", "user", "pass", "testserver"), []string{}, "PLAIN", []string{"\x00user\x00pass"}},
|
|
{PlainAuth("foo", "bar", "baz", "testserver"), []string{}, "PLAIN", []string{"foo\x00bar\x00baz"}},
|
|
{CRAMMD5Auth("user", "pass"), []string{"<123456.1322876914@testserver>"}, "CRAM-MD5", []string{"", "user 287eb355114cf5c471c26a875f1ca4ae"}},
|
|
}
|
|
|
|
func TestAuth(t *testing.T) {
|
|
testLoop:
|
|
for i, test := range authTests {
|
|
name, resp, err := test.auth.Start(&ServerInfo{"testserver", true, nil})
|
|
if name != test.name {
|
|
t.Errorf("#%d got name %s, expected %s", i, name, test.name)
|
|
}
|
|
if !bytes.Equal(resp, []byte(test.responses[0])) {
|
|
t.Errorf("#%d got response %s, expected %s", i, resp, test.responses[0])
|
|
}
|
|
if err != nil {
|
|
t.Errorf("#%d error: %s", i, err)
|
|
}
|
|
for j := range test.challenges {
|
|
challenge := []byte(test.challenges[j])
|
|
expected := []byte(test.responses[j+1])
|
|
resp, err := test.auth.Next(challenge, true)
|
|
if err != nil {
|
|
t.Errorf("#%d error: %s", i, err)
|
|
continue testLoop
|
|
}
|
|
if !bytes.Equal(resp, expected) {
|
|
t.Errorf("#%d got %s, expected %s", i, resp, expected)
|
|
continue testLoop
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAuthPlain(t *testing.T) {
|
|
auth := PlainAuth("foo", "bar", "baz", "servername")
|
|
|
|
tests := []struct {
|
|
server *ServerInfo
|
|
err string
|
|
}{
|
|
{
|
|
server: &ServerInfo{Name: "servername", TLS: true},
|
|
},
|
|
{
|
|
// Okay; explicitly advertised by server.
|
|
server: &ServerInfo{Name: "servername", Auth: []string{"PLAIN"}},
|
|
},
|
|
{
|
|
server: &ServerInfo{Name: "servername", Auth: []string{"CRAM-MD5"}},
|
|
err: "unencrypted connection",
|
|
},
|
|
{
|
|
server: &ServerInfo{Name: "attacker", TLS: true},
|
|
err: "wrong host name",
|
|
},
|
|
}
|
|
for i, tt := range tests {
|
|
_, _, err := auth.Start(tt.server)
|
|
got := ""
|
|
if err != nil {
|
|
got = err.Error()
|
|
}
|
|
if got != tt.err {
|
|
t.Errorf("%d. got error = %q; want %q", i, got, tt.err)
|
|
}
|
|
}
|
|
}
|
|
|
|
type faker struct {
|
|
io.ReadWriter
|
|
}
|
|
|
|
func (f faker) Close() error { return nil }
|
|
func (f faker) LocalAddr() net.Addr { return nil }
|
|
func (f faker) RemoteAddr() net.Addr { return nil }
|
|
func (f faker) SetDeadline(time.Time) error { return nil }
|
|
func (f faker) SetReadDeadline(time.Time) error { return nil }
|
|
func (f faker) SetWriteDeadline(time.Time) error { return nil }
|
|
|
|
func TestBasic(t *testing.T) {
|
|
server := strings.Join(strings.Split(basicServer, "\n"), "\r\n")
|
|
client := strings.Join(strings.Split(basicClient, "\n"), "\r\n")
|
|
|
|
var cmdbuf bytes.Buffer
|
|
bcmdbuf := bufio.NewWriter(&cmdbuf)
|
|
var fake faker
|
|
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
|
|
c := &Client{Text: textproto.NewConn(fake), localName: "localhost"}
|
|
|
|
if err := c.helo(); err != nil {
|
|
t.Fatalf("HELO failed: %s", err)
|
|
}
|
|
if err := c.ehlo(); err == nil {
|
|
t.Fatalf("Expected first EHLO to fail")
|
|
}
|
|
if err := c.ehlo(); err != nil {
|
|
t.Fatalf("Second EHLO failed: %s", err)
|
|
}
|
|
|
|
c.didHello = true
|
|
if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" {
|
|
t.Fatalf("Expected AUTH supported")
|
|
}
|
|
if ok, _ := c.Extension("DSN"); ok {
|
|
t.Fatalf("Shouldn't support DSN")
|
|
}
|
|
|
|
if err := c.Mail("user@gmail.com"); err == nil {
|
|
t.Fatalf("MAIL should require authentication")
|
|
}
|
|
|
|
if err := c.Verify("user1@gmail.com"); err == nil {
|
|
t.Fatalf("First VRFY: expected no verification")
|
|
}
|
|
if err := c.Verify("user2@gmail.com"); err != nil {
|
|
t.Fatalf("Second VRFY: expected verification, got %s", err)
|
|
}
|
|
|
|
// fake TLS so authentication won't complain
|
|
c.tls = true
|
|
c.serverName = "smtp.google.com"
|
|
if err := c.Auth(PlainAuth("", "user", "pass", "smtp.google.com")); err != nil {
|
|
t.Fatalf("AUTH failed: %s", err)
|
|
}
|
|
|
|
if err := c.Mail("user@gmail.com"); err != nil {
|
|
t.Fatalf("MAIL failed: %s", err)
|
|
}
|
|
if err := c.Rcpt("golang-nuts@googlegroups.com"); err != nil {
|
|
t.Fatalf("RCPT failed: %s", err)
|
|
}
|
|
msg := `From: user@gmail.com
|
|
To: golang-nuts@googlegroups.com
|
|
Subject: Hooray for Go
|
|
|
|
Line 1
|
|
.Leading dot line .
|
|
Goodbye.`
|
|
w, err := c.Data()
|
|
if err != nil {
|
|
t.Fatalf("DATA failed: %s", err)
|
|
}
|
|
if _, err := w.Write([]byte(msg)); err != nil {
|
|
t.Fatalf("Data write failed: %s", err)
|
|
}
|
|
if err := w.Close(); err != nil {
|
|
t.Fatalf("Bad data response: %s", err)
|
|
}
|
|
|
|
if err := c.Quit(); err != nil {
|
|
t.Fatalf("QUIT failed: %s", err)
|
|
}
|
|
|
|
bcmdbuf.Flush()
|
|
actualcmds := cmdbuf.String()
|
|
if client != actualcmds {
|
|
t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
|
|
}
|
|
}
|
|
|
|
var basicServer = `250 mx.google.com at your service
|
|
502 Unrecognized command.
|
|
250-mx.google.com at your service
|
|
250-SIZE 35651584
|
|
250-AUTH LOGIN PLAIN
|
|
250 8BITMIME
|
|
530 Authentication required
|
|
252 Send some mail, I'll try my best
|
|
250 User is valid
|
|
235 Accepted
|
|
250 Sender OK
|
|
250 Receiver OK
|
|
354 Go ahead
|
|
250 Data OK
|
|
221 OK
|
|
`
|
|
|
|
var basicClient = `HELO localhost
|
|
EHLO localhost
|
|
EHLO localhost
|
|
MAIL FROM:<user@gmail.com> BODY=8BITMIME
|
|
VRFY user1@gmail.com
|
|
VRFY user2@gmail.com
|
|
AUTH PLAIN AHVzZXIAcGFzcw==
|
|
MAIL FROM:<user@gmail.com> BODY=8BITMIME
|
|
RCPT TO:<golang-nuts@googlegroups.com>
|
|
DATA
|
|
From: user@gmail.com
|
|
To: golang-nuts@googlegroups.com
|
|
Subject: Hooray for Go
|
|
|
|
Line 1
|
|
..Leading dot line .
|
|
Goodbye.
|
|
.
|
|
QUIT
|
|
`
|
|
|
|
func TestNewClient(t *testing.T) {
|
|
server := strings.Join(strings.Split(newClientServer, "\n"), "\r\n")
|
|
client := strings.Join(strings.Split(newClientClient, "\n"), "\r\n")
|
|
|
|
var cmdbuf bytes.Buffer
|
|
bcmdbuf := bufio.NewWriter(&cmdbuf)
|
|
out := func() string {
|
|
bcmdbuf.Flush()
|
|
return cmdbuf.String()
|
|
}
|
|
var fake faker
|
|
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
|
|
c, err := NewClient(fake, "fake.host")
|
|
if err != nil {
|
|
t.Fatalf("NewClient: %v\n(after %v)", err, out())
|
|
}
|
|
defer c.Close()
|
|
if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" {
|
|
t.Fatalf("Expected AUTH supported")
|
|
}
|
|
if ok, _ := c.Extension("DSN"); ok {
|
|
t.Fatalf("Shouldn't support DSN")
|
|
}
|
|
if err := c.Quit(); err != nil {
|
|
t.Fatalf("QUIT failed: %s", err)
|
|
}
|
|
|
|
actualcmds := out()
|
|
if client != actualcmds {
|
|
t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
|
|
}
|
|
}
|
|
|
|
var newClientServer = `220 hello world
|
|
250-mx.google.com at your service
|
|
250-SIZE 35651584
|
|
250-AUTH LOGIN PLAIN
|
|
250 8BITMIME
|
|
221 OK
|
|
`
|
|
|
|
var newClientClient = `EHLO localhost
|
|
QUIT
|
|
`
|
|
|
|
func TestNewClient2(t *testing.T) {
|
|
server := strings.Join(strings.Split(newClient2Server, "\n"), "\r\n")
|
|
client := strings.Join(strings.Split(newClient2Client, "\n"), "\r\n")
|
|
|
|
var cmdbuf bytes.Buffer
|
|
bcmdbuf := bufio.NewWriter(&cmdbuf)
|
|
var fake faker
|
|
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
|
|
c, err := NewClient(fake, "fake.host")
|
|
if err != nil {
|
|
t.Fatalf("NewClient: %v", err)
|
|
}
|
|
defer c.Close()
|
|
if ok, _ := c.Extension("DSN"); ok {
|
|
t.Fatalf("Shouldn't support DSN")
|
|
}
|
|
if err := c.Quit(); err != nil {
|
|
t.Fatalf("QUIT failed: %s", err)
|
|
}
|
|
|
|
bcmdbuf.Flush()
|
|
actualcmds := cmdbuf.String()
|
|
if client != actualcmds {
|
|
t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client)
|
|
}
|
|
}
|
|
|
|
var newClient2Server = `220 hello world
|
|
502 EH?
|
|
250-mx.google.com at your service
|
|
250-SIZE 35651584
|
|
250-AUTH LOGIN PLAIN
|
|
250 8BITMIME
|
|
221 OK
|
|
`
|
|
|
|
var newClient2Client = `EHLO localhost
|
|
HELO localhost
|
|
QUIT
|
|
`
|
|
|
|
func TestHello(t *testing.T) {
|
|
|
|
if len(helloServer) != len(helloClient) {
|
|
t.Fatalf("Hello server and client size mismatch")
|
|
}
|
|
|
|
for i := 0; i < len(helloServer); i++ {
|
|
server := strings.Join(strings.Split(baseHelloServer+helloServer[i], "\n"), "\r\n")
|
|
client := strings.Join(strings.Split(baseHelloClient+helloClient[i], "\n"), "\r\n")
|
|
var cmdbuf bytes.Buffer
|
|
bcmdbuf := bufio.NewWriter(&cmdbuf)
|
|
var fake faker
|
|
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
|
|
c, err := NewClient(fake, "fake.host")
|
|
if err != nil {
|
|
t.Fatalf("NewClient: %v", err)
|
|
}
|
|
defer c.Close()
|
|
c.localName = "customhost"
|
|
err = nil
|
|
|
|
switch i {
|
|
case 0:
|
|
err = c.Hello("customhost")
|
|
case 1:
|
|
err = c.StartTLS(nil)
|
|
if err.Error() == "502 Not implemented" {
|
|
err = nil
|
|
}
|
|
case 2:
|
|
err = c.Verify("test@example.com")
|
|
case 3:
|
|
c.tls = true
|
|
c.serverName = "smtp.google.com"
|
|
err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com"))
|
|
case 4:
|
|
err = c.Mail("test@example.com")
|
|
case 5:
|
|
ok, _ := c.Extension("feature")
|
|
if ok {
|
|
t.Errorf("Expected FEATURE not to be supported")
|
|
}
|
|
case 6:
|
|
err = c.Reset()
|
|
case 7:
|
|
err = c.Quit()
|
|
case 8:
|
|
err = c.Verify("test@example.com")
|
|
if err != nil {
|
|
err = c.Hello("customhost")
|
|
if err != nil {
|
|
t.Errorf("Want error, got none")
|
|
}
|
|
}
|
|
default:
|
|
t.Fatalf("Unhandled command")
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("Command %d failed: %v", i, err)
|
|
}
|
|
|
|
bcmdbuf.Flush()
|
|
actualcmds := cmdbuf.String()
|
|
if client != actualcmds {
|
|
t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client)
|
|
}
|
|
}
|
|
}
|
|
|
|
var baseHelloServer = `220 hello world
|
|
502 EH?
|
|
250-mx.google.com at your service
|
|
250 FEATURE
|
|
`
|
|
|
|
var helloServer = []string{
|
|
"",
|
|
"502 Not implemented\n",
|
|
"250 User is valid\n",
|
|
"235 Accepted\n",
|
|
"250 Sender ok\n",
|
|
"",
|
|
"250 Reset ok\n",
|
|
"221 Goodbye\n",
|
|
"250 Sender ok\n",
|
|
}
|
|
|
|
var baseHelloClient = `EHLO customhost
|
|
HELO customhost
|
|
`
|
|
|
|
var helloClient = []string{
|
|
"",
|
|
"STARTTLS\n",
|
|
"VRFY test@example.com\n",
|
|
"AUTH PLAIN AHVzZXIAcGFzcw==\n",
|
|
"MAIL FROM:<test@example.com>\n",
|
|
"",
|
|
"RSET\n",
|
|
"QUIT\n",
|
|
"VRFY test@example.com\n",
|
|
}
|
|
|
|
func TestSendMail(t *testing.T) {
|
|
server := strings.Join(strings.Split(sendMailServer, "\n"), "\r\n")
|
|
client := strings.Join(strings.Split(sendMailClient, "\n"), "\r\n")
|
|
var cmdbuf bytes.Buffer
|
|
bcmdbuf := bufio.NewWriter(&cmdbuf)
|
|
l, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatalf("Unable to to create listener: %v", err)
|
|
}
|
|
defer l.Close()
|
|
|
|
// prevent data race on bcmdbuf
|
|
var done = make(chan struct{})
|
|
go func(data []string) {
|
|
|
|
defer close(done)
|
|
|
|
conn, err := l.Accept()
|
|
if err != nil {
|
|
t.Errorf("Accept error: %v", err)
|
|
return
|
|
}
|
|
defer conn.Close()
|
|
|
|
tc := textproto.NewConn(conn)
|
|
for i := 0; i < len(data) && data[i] != ""; i++ {
|
|
tc.PrintfLine(data[i])
|
|
for len(data[i]) >= 4 && data[i][3] == '-' {
|
|
i++
|
|
tc.PrintfLine(data[i])
|
|
}
|
|
if data[i] == "221 Goodbye" {
|
|
return
|
|
}
|
|
read := false
|
|
for !read || data[i] == "354 Go ahead" {
|
|
msg, err := tc.ReadLine()
|
|
bcmdbuf.Write([]byte(msg + "\r\n"))
|
|
read = true
|
|
if err != nil {
|
|
t.Errorf("Read error: %v", err)
|
|
return
|
|
}
|
|
if data[i] == "354 Go ahead" && msg == "." {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}(strings.Split(server, "\r\n"))
|
|
|
|
err = SendMail(l.Addr().String(), nil, "test@example.com", []string{"other@example.com"}, []byte(strings.Replace(`From: test@example.com
|
|
To: other@example.com
|
|
Subject: SendMail test
|
|
|
|
SendMail is working for me.
|
|
`, "\n", "\r\n", -1)))
|
|
|
|
if err != nil {
|
|
t.Errorf("%v", err)
|
|
}
|
|
|
|
<-done
|
|
bcmdbuf.Flush()
|
|
actualcmds := cmdbuf.String()
|
|
if client != actualcmds {
|
|
t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client)
|
|
}
|
|
}
|
|
|
|
var sendMailServer = `220 hello world
|
|
502 EH?
|
|
250 mx.google.com at your service
|
|
250 Sender ok
|
|
250 Receiver ok
|
|
354 Go ahead
|
|
250 Data ok
|
|
221 Goodbye
|
|
`
|
|
|
|
var sendMailClient = `EHLO localhost
|
|
HELO localhost
|
|
MAIL FROM:<test@example.com>
|
|
RCPT TO:<other@example.com>
|
|
DATA
|
|
From: test@example.com
|
|
To: other@example.com
|
|
Subject: SendMail test
|
|
|
|
SendMail is working for me.
|
|
.
|
|
QUIT
|
|
`
|
|
|
|
func TestAuthFailed(t *testing.T) {
|
|
server := strings.Join(strings.Split(authFailedServer, "\n"), "\r\n")
|
|
client := strings.Join(strings.Split(authFailedClient, "\n"), "\r\n")
|
|
var cmdbuf bytes.Buffer
|
|
bcmdbuf := bufio.NewWriter(&cmdbuf)
|
|
var fake faker
|
|
fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf)
|
|
c, err := NewClient(fake, "fake.host")
|
|
if err != nil {
|
|
t.Fatalf("NewClient: %v", err)
|
|
}
|
|
defer c.Close()
|
|
|
|
c.tls = true
|
|
c.serverName = "smtp.google.com"
|
|
err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com"))
|
|
|
|
if err == nil {
|
|
t.Error("Auth: expected error; got none")
|
|
} else if err.Error() != "535 Invalid credentials\nplease see www.example.com" {
|
|
t.Errorf("Auth: got error: %v, want: %s", err, "535 Invalid credentials\nplease see www.example.com")
|
|
}
|
|
|
|
bcmdbuf.Flush()
|
|
actualcmds := cmdbuf.String()
|
|
if client != actualcmds {
|
|
t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client)
|
|
}
|
|
}
|
|
|
|
var authFailedServer = `220 hello world
|
|
250-mx.google.com at your service
|
|
250 AUTH LOGIN PLAIN
|
|
535-Invalid credentials
|
|
535 please see www.example.com
|
|
221 Goodbye
|
|
`
|
|
|
|
var authFailedClient = `EHLO localhost
|
|
AUTH PLAIN AHVzZXIAcGFzcw==
|
|
*
|
|
QUIT
|
|
`
|
|
|
|
func TestTLSClient(t *testing.T) {
|
|
ln := newLocalListener(t)
|
|
defer ln.Close()
|
|
errc := make(chan error)
|
|
go func() {
|
|
errc <- sendMail(ln.Addr().String())
|
|
}()
|
|
conn, err := ln.Accept()
|
|
if err != nil {
|
|
t.Fatalf("failed to accept connection: %v", err)
|
|
}
|
|
defer conn.Close()
|
|
if err := serverHandle(conn, t); err != nil {
|
|
t.Fatalf("failed to handle connection: %v", err)
|
|
}
|
|
if err := <-errc; err != nil {
|
|
t.Fatalf("client error: %v", err)
|
|
}
|
|
}
|
|
|
|
func newLocalListener(t *testing.T) net.Listener {
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
ln, err = net.Listen("tcp6", "[::1]:0")
|
|
}
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return ln
|
|
}
|
|
|
|
type smtpSender struct {
|
|
w io.Writer
|
|
}
|
|
|
|
func (s smtpSender) send(f string) {
|
|
s.w.Write([]byte(f + "\r\n"))
|
|
}
|
|
|
|
// smtp server, finely tailored to deal with our own client only!
|
|
func serverHandle(c net.Conn, t *testing.T) error {
|
|
send := smtpSender{c}.send
|
|
send("220 127.0.0.1 ESMTP service ready")
|
|
s := bufio.NewScanner(c)
|
|
for s.Scan() {
|
|
switch s.Text() {
|
|
case "EHLO localhost":
|
|
send("250-127.0.0.1 ESMTP offers a warm hug of welcome")
|
|
send("250-STARTTLS")
|
|
send("250 Ok")
|
|
case "STARTTLS":
|
|
send("220 Go ahead")
|
|
keypair, err := tls.X509KeyPair(localhostCert, localhostKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
config := &tls.Config{Certificates: []tls.Certificate{keypair}}
|
|
c = tls.Server(c, config)
|
|
defer c.Close()
|
|
return serverHandleTLS(c, t)
|
|
default:
|
|
t.Fatalf("unrecognized command: %q", s.Text())
|
|
}
|
|
}
|
|
return s.Err()
|
|
}
|
|
|
|
func serverHandleTLS(c net.Conn, t *testing.T) error {
|
|
send := smtpSender{c}.send
|
|
s := bufio.NewScanner(c)
|
|
for s.Scan() {
|
|
switch s.Text() {
|
|
case "EHLO localhost":
|
|
send("250 Ok")
|
|
case "MAIL FROM:<joe1@example.com>":
|
|
send("250 Ok")
|
|
case "RCPT TO:<joe2@example.com>":
|
|
send("250 Ok")
|
|
case "DATA":
|
|
send("354 send the mail data, end with .")
|
|
send("250 Ok")
|
|
case "Subject: test":
|
|
case "":
|
|
case "howdy!":
|
|
case ".":
|
|
case "QUIT":
|
|
send("221 127.0.0.1 Service closing transmission channel")
|
|
return nil
|
|
default:
|
|
t.Fatalf("unrecognized command during TLS: %q", s.Text())
|
|
}
|
|
}
|
|
return s.Err()
|
|
}
|
|
|
|
func init() {
|
|
testRootCAs := x509.NewCertPool()
|
|
testRootCAs.AppendCertsFromPEM(localhostCert)
|
|
testHookStartTLS = func(config *tls.Config) {
|
|
config.RootCAs = testRootCAs
|
|
}
|
|
}
|
|
|
|
func sendMail(hostPort string) error {
|
|
host, _, err := net.SplitHostPort(hostPort)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
auth := PlainAuth("", "", "", host)
|
|
from := "joe1@example.com"
|
|
to := []string{"joe2@example.com"}
|
|
return SendMail(hostPort, auth, from, to, []byte("Subject: test\n\nhowdy!"))
|
|
}
|
|
|
|
// (copied from net/http/httptest)
|
|
// localhostCert is a PEM-encoded TLS cert with SAN IPs
|
|
// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
|
|
// of ASN.1 time).
|
|
// generated from src/crypto/tls:
|
|
// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
|
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
|
|
MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
|
|
bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
|
|
bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa
|
|
IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA
|
|
AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
|
|
EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
|
|
AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk
|
|
Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA==
|
|
-----END CERTIFICATE-----`)
|
|
|
|
// localhostKey is the private key for localhostCert.
|
|
var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
|
|
MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0
|
|
0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV
|
|
NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d
|
|
AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW
|
|
MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD
|
|
EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA
|
|
1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE=
|
|
-----END RSA PRIVATE KEY-----`)
|