github.com/freddyisaac/sicortex-golang@v0.0.0-20231019035217-e03519e66f60/src/net/smtp/smtp_test.go (about) 1 // Copyright 2010 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package smtp 6 7 import ( 8 "bufio" 9 "bytes" 10 "crypto/tls" 11 "crypto/x509" 12 "io" 13 "net" 14 "net/textproto" 15 "strings" 16 "testing" 17 "time" 18 ) 19 20 type authTest struct { 21 auth Auth 22 challenges []string 23 name string 24 responses []string 25 } 26 27 var authTests = []authTest{ 28 {PlainAuth("", "user", "pass", "testserver"), []string{}, "PLAIN", []string{"\x00user\x00pass"}}, 29 {PlainAuth("foo", "bar", "baz", "testserver"), []string{}, "PLAIN", []string{"foo\x00bar\x00baz"}}, 30 {CRAMMD5Auth("user", "pass"), []string{"<123456.1322876914@testserver>"}, "CRAM-MD5", []string{"", "user 287eb355114cf5c471c26a875f1ca4ae"}}, 31 } 32 33 func TestAuth(t *testing.T) { 34 testLoop: 35 for i, test := range authTests { 36 name, resp, err := test.auth.Start(&ServerInfo{"testserver", true, nil}) 37 if name != test.name { 38 t.Errorf("#%d got name %s, expected %s", i, name, test.name) 39 } 40 if !bytes.Equal(resp, []byte(test.responses[0])) { 41 t.Errorf("#%d got response %s, expected %s", i, resp, test.responses[0]) 42 } 43 if err != nil { 44 t.Errorf("#%d error: %s", i, err) 45 } 46 for j := range test.challenges { 47 challenge := []byte(test.challenges[j]) 48 expected := []byte(test.responses[j+1]) 49 resp, err := test.auth.Next(challenge, true) 50 if err != nil { 51 t.Errorf("#%d error: %s", i, err) 52 continue testLoop 53 } 54 if !bytes.Equal(resp, expected) { 55 t.Errorf("#%d got %s, expected %s", i, resp, expected) 56 continue testLoop 57 } 58 } 59 } 60 } 61 62 func TestAuthPlain(t *testing.T) { 63 64 tests := []struct { 65 authName string 66 server *ServerInfo 67 err string 68 }{ 69 { 70 authName: "servername", 71 server: &ServerInfo{Name: "servername", TLS: true}, 72 }, 73 { 74 // OK to use PlainAuth on localhost without TLS 75 authName: "localhost", 76 server: &ServerInfo{Name: "localhost", TLS: false}, 77 }, 78 { 79 // NOT OK on non-localhost, even if server says PLAIN is OK. 80 // (We don't know that the server is the real server.) 81 authName: "servername", 82 server: &ServerInfo{Name: "servername", Auth: []string{"PLAIN"}}, 83 err: "unencrypted connection", 84 }, 85 { 86 authName: "servername", 87 server: &ServerInfo{Name: "servername", Auth: []string{"CRAM-MD5"}}, 88 err: "unencrypted connection", 89 }, 90 { 91 authName: "servername", 92 server: &ServerInfo{Name: "attacker", TLS: true}, 93 err: "wrong host name", 94 }, 95 } 96 for i, tt := range tests { 97 auth := PlainAuth("foo", "bar", "baz", tt.authName) 98 _, _, err := auth.Start(tt.server) 99 got := "" 100 if err != nil { 101 got = err.Error() 102 } 103 if got != tt.err { 104 t.Errorf("%d. got error = %q; want %q", i, got, tt.err) 105 } 106 } 107 } 108 109 // Issue 17794: don't send a trailing space on AUTH command when there's no password. 110 func TestClientAuthTrimSpace(t *testing.T) { 111 server := "220 hello world\r\n" + 112 "200 some more" 113 var wrote bytes.Buffer 114 var fake faker 115 fake.ReadWriter = struct { 116 io.Reader 117 io.Writer 118 }{ 119 strings.NewReader(server), 120 &wrote, 121 } 122 c, err := NewClient(fake, "fake.host") 123 if err != nil { 124 t.Fatalf("NewClient: %v", err) 125 } 126 c.tls = true 127 c.didHello = true 128 c.Auth(toServerEmptyAuth{}) 129 c.Close() 130 if got, want := wrote.String(), "AUTH FOOAUTH\r\n*\r\nQUIT\r\n"; got != want { 131 t.Errorf("wrote %q; want %q", got, want) 132 } 133 } 134 135 // toServerEmptyAuth is an implementation of Auth that only implements 136 // the Start method, and returns "FOOAUTH", nil, nil. Notably, it returns 137 // zero bytes for "toServer" so we can test that we don't send spaces at 138 // the end of the line. See TestClientAuthTrimSpace. 139 type toServerEmptyAuth struct{} 140 141 func (toServerEmptyAuth) Start(server *ServerInfo) (proto string, toServer []byte, err error) { 142 return "FOOAUTH", nil, nil 143 } 144 145 func (toServerEmptyAuth) Next(fromServer []byte, more bool) (toServer []byte, err error) { 146 panic("unexpected call") 147 } 148 149 type faker struct { 150 io.ReadWriter 151 } 152 153 func (f faker) Close() error { return nil } 154 func (f faker) LocalAddr() net.Addr { return nil } 155 func (f faker) RemoteAddr() net.Addr { return nil } 156 func (f faker) SetDeadline(time.Time) error { return nil } 157 func (f faker) SetReadDeadline(time.Time) error { return nil } 158 func (f faker) SetWriteDeadline(time.Time) error { return nil } 159 160 func TestBasic(t *testing.T) { 161 server := strings.Join(strings.Split(basicServer, "\n"), "\r\n") 162 client := strings.Join(strings.Split(basicClient, "\n"), "\r\n") 163 164 var cmdbuf bytes.Buffer 165 bcmdbuf := bufio.NewWriter(&cmdbuf) 166 var fake faker 167 fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) 168 c := &Client{Text: textproto.NewConn(fake), localName: "localhost"} 169 170 if err := c.helo(); err != nil { 171 t.Fatalf("HELO failed: %s", err) 172 } 173 if err := c.ehlo(); err == nil { 174 t.Fatalf("Expected first EHLO to fail") 175 } 176 if err := c.ehlo(); err != nil { 177 t.Fatalf("Second EHLO failed: %s", err) 178 } 179 180 c.didHello = true 181 if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" { 182 t.Fatalf("Expected AUTH supported") 183 } 184 if ok, _ := c.Extension("DSN"); ok { 185 t.Fatalf("Shouldn't support DSN") 186 } 187 188 if err := c.Mail("user@gmail.com"); err == nil { 189 t.Fatalf("MAIL should require authentication") 190 } 191 192 if err := c.Verify("user1@gmail.com"); err == nil { 193 t.Fatalf("First VRFY: expected no verification") 194 } 195 if err := c.Verify("user2@gmail.com"); err != nil { 196 t.Fatalf("Second VRFY: expected verification, got %s", err) 197 } 198 199 // fake TLS so authentication won't complain 200 c.tls = true 201 c.serverName = "smtp.google.com" 202 if err := c.Auth(PlainAuth("", "user", "pass", "smtp.google.com")); err != nil { 203 t.Fatalf("AUTH failed: %s", err) 204 } 205 206 if err := c.Mail("user@gmail.com"); err != nil { 207 t.Fatalf("MAIL failed: %s", err) 208 } 209 if err := c.Rcpt("golang-nuts@googlegroups.com"); err != nil { 210 t.Fatalf("RCPT failed: %s", err) 211 } 212 msg := `From: user@gmail.com 213 To: golang-nuts@googlegroups.com 214 Subject: Hooray for Go 215 216 Line 1 217 .Leading dot line . 218 Goodbye.` 219 w, err := c.Data() 220 if err != nil { 221 t.Fatalf("DATA failed: %s", err) 222 } 223 if _, err := w.Write([]byte(msg)); err != nil { 224 t.Fatalf("Data write failed: %s", err) 225 } 226 if err := w.Close(); err != nil { 227 t.Fatalf("Bad data response: %s", err) 228 } 229 230 if err := c.Quit(); err != nil { 231 t.Fatalf("QUIT failed: %s", err) 232 } 233 234 bcmdbuf.Flush() 235 actualcmds := cmdbuf.String() 236 if client != actualcmds { 237 t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client) 238 } 239 } 240 241 var basicServer = `250 mx.google.com at your service 242 502 Unrecognized command. 243 250-mx.google.com at your service 244 250-SIZE 35651584 245 250-AUTH LOGIN PLAIN 246 250 8BITMIME 247 530 Authentication required 248 252 Send some mail, I'll try my best 249 250 User is valid 250 235 Accepted 251 250 Sender OK 252 250 Receiver OK 253 354 Go ahead 254 250 Data OK 255 221 OK 256 ` 257 258 var basicClient = `HELO localhost 259 EHLO localhost 260 EHLO localhost 261 MAIL FROM:<user@gmail.com> BODY=8BITMIME 262 VRFY user1@gmail.com 263 VRFY user2@gmail.com 264 AUTH PLAIN AHVzZXIAcGFzcw== 265 MAIL FROM:<user@gmail.com> BODY=8BITMIME 266 RCPT TO:<golang-nuts@googlegroups.com> 267 DATA 268 From: user@gmail.com 269 To: golang-nuts@googlegroups.com 270 Subject: Hooray for Go 271 272 Line 1 273 ..Leading dot line . 274 Goodbye. 275 . 276 QUIT 277 ` 278 279 func TestNewClient(t *testing.T) { 280 server := strings.Join(strings.Split(newClientServer, "\n"), "\r\n") 281 client := strings.Join(strings.Split(newClientClient, "\n"), "\r\n") 282 283 var cmdbuf bytes.Buffer 284 bcmdbuf := bufio.NewWriter(&cmdbuf) 285 out := func() string { 286 bcmdbuf.Flush() 287 return cmdbuf.String() 288 } 289 var fake faker 290 fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) 291 c, err := NewClient(fake, "fake.host") 292 if err != nil { 293 t.Fatalf("NewClient: %v\n(after %v)", err, out()) 294 } 295 defer c.Close() 296 if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" { 297 t.Fatalf("Expected AUTH supported") 298 } 299 if ok, _ := c.Extension("DSN"); ok { 300 t.Fatalf("Shouldn't support DSN") 301 } 302 if err := c.Quit(); err != nil { 303 t.Fatalf("QUIT failed: %s", err) 304 } 305 306 actualcmds := out() 307 if client != actualcmds { 308 t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client) 309 } 310 } 311 312 var newClientServer = `220 hello world 313 250-mx.google.com at your service 314 250-SIZE 35651584 315 250-AUTH LOGIN PLAIN 316 250 8BITMIME 317 221 OK 318 ` 319 320 var newClientClient = `EHLO localhost 321 QUIT 322 ` 323 324 func TestNewClient2(t *testing.T) { 325 server := strings.Join(strings.Split(newClient2Server, "\n"), "\r\n") 326 client := strings.Join(strings.Split(newClient2Client, "\n"), "\r\n") 327 328 var cmdbuf bytes.Buffer 329 bcmdbuf := bufio.NewWriter(&cmdbuf) 330 var fake faker 331 fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) 332 c, err := NewClient(fake, "fake.host") 333 if err != nil { 334 t.Fatalf("NewClient: %v", err) 335 } 336 defer c.Close() 337 if ok, _ := c.Extension("DSN"); ok { 338 t.Fatalf("Shouldn't support DSN") 339 } 340 if err := c.Quit(); err != nil { 341 t.Fatalf("QUIT failed: %s", err) 342 } 343 344 bcmdbuf.Flush() 345 actualcmds := cmdbuf.String() 346 if client != actualcmds { 347 t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, client) 348 } 349 } 350 351 var newClient2Server = `220 hello world 352 502 EH? 353 250-mx.google.com at your service 354 250-SIZE 35651584 355 250-AUTH LOGIN PLAIN 356 250 8BITMIME 357 221 OK 358 ` 359 360 var newClient2Client = `EHLO localhost 361 HELO localhost 362 QUIT 363 ` 364 365 func TestNewClientWithTLS(t *testing.T) { 366 cert, err := tls.X509KeyPair(localhostCert, localhostKey) 367 if err != nil { 368 t.Fatalf("loadcert: %v", err) 369 } 370 371 config := tls.Config{Certificates: []tls.Certificate{cert}} 372 373 ln, err := tls.Listen("tcp", "127.0.0.1:0", &config) 374 if err != nil { 375 ln, err = tls.Listen("tcp", "[::1]:0", &config) 376 if err != nil { 377 t.Fatalf("server: listen: %s", err) 378 } 379 } 380 381 go func() { 382 conn, err := ln.Accept() 383 if err != nil { 384 t.Fatalf("server: accept: %s", err) 385 return 386 } 387 defer conn.Close() 388 389 _, err = conn.Write([]byte("220 SIGNS\r\n")) 390 if err != nil { 391 t.Fatalf("server: write: %s", err) 392 return 393 } 394 }() 395 396 config.InsecureSkipVerify = true 397 conn, err := tls.Dial("tcp", ln.Addr().String(), &config) 398 if err != nil { 399 t.Fatalf("client: dial: %s", err) 400 } 401 defer conn.Close() 402 403 client, err := NewClient(conn, ln.Addr().String()) 404 if err != nil { 405 t.Fatalf("smtp: newclient: %s", err) 406 } 407 if !client.tls { 408 t.Errorf("client.tls Got: %t Expected: %t", client.tls, true) 409 } 410 } 411 412 func TestHello(t *testing.T) { 413 414 if len(helloServer) != len(helloClient) { 415 t.Fatalf("Hello server and client size mismatch") 416 } 417 418 for i := 0; i < len(helloServer); i++ { 419 server := strings.Join(strings.Split(baseHelloServer+helloServer[i], "\n"), "\r\n") 420 client := strings.Join(strings.Split(baseHelloClient+helloClient[i], "\n"), "\r\n") 421 var cmdbuf bytes.Buffer 422 bcmdbuf := bufio.NewWriter(&cmdbuf) 423 var fake faker 424 fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) 425 c, err := NewClient(fake, "fake.host") 426 if err != nil { 427 t.Fatalf("NewClient: %v", err) 428 } 429 defer c.Close() 430 c.localName = "customhost" 431 err = nil 432 433 switch i { 434 case 0: 435 err = c.Hello("customhost") 436 case 1: 437 err = c.StartTLS(nil) 438 if err.Error() == "502 Not implemented" { 439 err = nil 440 } 441 case 2: 442 err = c.Verify("test@example.com") 443 case 3: 444 c.tls = true 445 c.serverName = "smtp.google.com" 446 err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com")) 447 case 4: 448 err = c.Mail("test@example.com") 449 case 5: 450 ok, _ := c.Extension("feature") 451 if ok { 452 t.Errorf("Expected FEATURE not to be supported") 453 } 454 case 6: 455 err = c.Reset() 456 case 7: 457 err = c.Quit() 458 case 8: 459 err = c.Verify("test@example.com") 460 if err != nil { 461 err = c.Hello("customhost") 462 if err != nil { 463 t.Errorf("Want error, got none") 464 } 465 } 466 default: 467 t.Fatalf("Unhandled command") 468 } 469 470 if err != nil { 471 t.Errorf("Command %d failed: %v", i, err) 472 } 473 474 bcmdbuf.Flush() 475 actualcmds := cmdbuf.String() 476 if client != actualcmds { 477 t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client) 478 } 479 } 480 } 481 482 var baseHelloServer = `220 hello world 483 502 EH? 484 250-mx.google.com at your service 485 250 FEATURE 486 ` 487 488 var helloServer = []string{ 489 "", 490 "502 Not implemented\n", 491 "250 User is valid\n", 492 "235 Accepted\n", 493 "250 Sender ok\n", 494 "", 495 "250 Reset ok\n", 496 "221 Goodbye\n", 497 "250 Sender ok\n", 498 } 499 500 var baseHelloClient = `EHLO customhost 501 HELO customhost 502 ` 503 504 var helloClient = []string{ 505 "", 506 "STARTTLS\n", 507 "VRFY test@example.com\n", 508 "AUTH PLAIN AHVzZXIAcGFzcw==\n", 509 "MAIL FROM:<test@example.com>\n", 510 "", 511 "RSET\n", 512 "QUIT\n", 513 "VRFY test@example.com\n", 514 } 515 516 func TestSendMail(t *testing.T) { 517 server := strings.Join(strings.Split(sendMailServer, "\n"), "\r\n") 518 client := strings.Join(strings.Split(sendMailClient, "\n"), "\r\n") 519 var cmdbuf bytes.Buffer 520 bcmdbuf := bufio.NewWriter(&cmdbuf) 521 l, err := net.Listen("tcp", "127.0.0.1:0") 522 if err != nil { 523 t.Fatalf("Unable to to create listener: %v", err) 524 } 525 defer l.Close() 526 527 // prevent data race on bcmdbuf 528 var done = make(chan struct{}) 529 go func(data []string) { 530 531 defer close(done) 532 533 conn, err := l.Accept() 534 if err != nil { 535 t.Errorf("Accept error: %v", err) 536 return 537 } 538 defer conn.Close() 539 540 tc := textproto.NewConn(conn) 541 for i := 0; i < len(data) && data[i] != ""; i++ { 542 tc.PrintfLine(data[i]) 543 for len(data[i]) >= 4 && data[i][3] == '-' { 544 i++ 545 tc.PrintfLine(data[i]) 546 } 547 if data[i] == "221 Goodbye" { 548 return 549 } 550 read := false 551 for !read || data[i] == "354 Go ahead" { 552 msg, err := tc.ReadLine() 553 bcmdbuf.Write([]byte(msg + "\r\n")) 554 read = true 555 if err != nil { 556 t.Errorf("Read error: %v", err) 557 return 558 } 559 if data[i] == "354 Go ahead" && msg == "." { 560 break 561 } 562 } 563 } 564 }(strings.Split(server, "\r\n")) 565 566 err = SendMail(l.Addr().String(), nil, "test@example.com", []string{"other@example.com"}, []byte(strings.Replace(`From: test@example.com 567 To: other@example.com 568 Subject: SendMail test 569 570 SendMail is working for me. 571 `, "\n", "\r\n", -1))) 572 573 if err != nil { 574 t.Errorf("%v", err) 575 } 576 577 <-done 578 bcmdbuf.Flush() 579 actualcmds := cmdbuf.String() 580 if client != actualcmds { 581 t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client) 582 } 583 } 584 585 var sendMailServer = `220 hello world 586 502 EH? 587 250 mx.google.com at your service 588 250 Sender ok 589 250 Receiver ok 590 354 Go ahead 591 250 Data ok 592 221 Goodbye 593 ` 594 595 var sendMailClient = `EHLO localhost 596 HELO localhost 597 MAIL FROM:<test@example.com> 598 RCPT TO:<other@example.com> 599 DATA 600 From: test@example.com 601 To: other@example.com 602 Subject: SendMail test 603 604 SendMail is working for me. 605 . 606 QUIT 607 ` 608 609 func TestAuthFailed(t *testing.T) { 610 server := strings.Join(strings.Split(authFailedServer, "\n"), "\r\n") 611 client := strings.Join(strings.Split(authFailedClient, "\n"), "\r\n") 612 var cmdbuf bytes.Buffer 613 bcmdbuf := bufio.NewWriter(&cmdbuf) 614 var fake faker 615 fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) 616 c, err := NewClient(fake, "fake.host") 617 if err != nil { 618 t.Fatalf("NewClient: %v", err) 619 } 620 defer c.Close() 621 622 c.tls = true 623 c.serverName = "smtp.google.com" 624 err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com")) 625 626 if err == nil { 627 t.Error("Auth: expected error; got none") 628 } else if err.Error() != "535 Invalid credentials\nplease see www.example.com" { 629 t.Errorf("Auth: got error: %v, want: %s", err, "535 Invalid credentials\nplease see www.example.com") 630 } 631 632 bcmdbuf.Flush() 633 actualcmds := cmdbuf.String() 634 if client != actualcmds { 635 t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client) 636 } 637 } 638 639 var authFailedServer = `220 hello world 640 250-mx.google.com at your service 641 250 AUTH LOGIN PLAIN 642 535-Invalid credentials 643 535 please see www.example.com 644 221 Goodbye 645 ` 646 647 var authFailedClient = `EHLO localhost 648 AUTH PLAIN AHVzZXIAcGFzcw== 649 * 650 QUIT 651 ` 652 653 func TestTLSClient(t *testing.T) { 654 ln := newLocalListener(t) 655 defer ln.Close() 656 errc := make(chan error) 657 go func() { 658 errc <- sendMail(ln.Addr().String()) 659 }() 660 conn, err := ln.Accept() 661 if err != nil { 662 t.Fatalf("failed to accept connection: %v", err) 663 } 664 defer conn.Close() 665 if err := serverHandle(conn, t); err != nil { 666 t.Fatalf("failed to handle connection: %v", err) 667 } 668 if err := <-errc; err != nil { 669 t.Fatalf("client error: %v", err) 670 } 671 } 672 673 func TestTLSConnState(t *testing.T) { 674 ln := newLocalListener(t) 675 defer ln.Close() 676 clientDone := make(chan bool) 677 serverDone := make(chan bool) 678 go func() { 679 defer close(serverDone) 680 c, err := ln.Accept() 681 if err != nil { 682 t.Errorf("Server accept: %v", err) 683 return 684 } 685 defer c.Close() 686 if err := serverHandle(c, t); err != nil { 687 t.Errorf("server error: %v", err) 688 } 689 }() 690 go func() { 691 defer close(clientDone) 692 c, err := Dial(ln.Addr().String()) 693 if err != nil { 694 t.Errorf("Client dial: %v", err) 695 return 696 } 697 defer c.Quit() 698 cfg := &tls.Config{ServerName: "example.com"} 699 testHookStartTLS(cfg) // set the RootCAs 700 if err := c.StartTLS(cfg); err != nil { 701 t.Errorf("StartTLS: %v", err) 702 return 703 } 704 cs, ok := c.TLSConnectionState() 705 if !ok { 706 t.Errorf("TLSConnectionState returned ok == false; want true") 707 return 708 } 709 if cs.Version == 0 || !cs.HandshakeComplete { 710 t.Errorf("ConnectionState = %#v; expect non-zero Version and HandshakeComplete", cs) 711 } 712 }() 713 <-clientDone 714 <-serverDone 715 } 716 717 func newLocalListener(t *testing.T) net.Listener { 718 ln, err := net.Listen("tcp", "127.0.0.1:0") 719 if err != nil { 720 ln, err = net.Listen("tcp6", "[::1]:0") 721 } 722 if err != nil { 723 t.Fatal(err) 724 } 725 return ln 726 } 727 728 type smtpSender struct { 729 w io.Writer 730 } 731 732 func (s smtpSender) send(f string) { 733 s.w.Write([]byte(f + "\r\n")) 734 } 735 736 // smtp server, finely tailored to deal with our own client only! 737 func serverHandle(c net.Conn, t *testing.T) error { 738 send := smtpSender{c}.send 739 send("220 127.0.0.1 ESMTP service ready") 740 s := bufio.NewScanner(c) 741 for s.Scan() { 742 switch s.Text() { 743 case "EHLO localhost": 744 send("250-127.0.0.1 ESMTP offers a warm hug of welcome") 745 send("250-STARTTLS") 746 send("250 Ok") 747 case "STARTTLS": 748 send("220 Go ahead") 749 keypair, err := tls.X509KeyPair(localhostCert, localhostKey) 750 if err != nil { 751 return err 752 } 753 config := &tls.Config{Certificates: []tls.Certificate{keypair}} 754 c = tls.Server(c, config) 755 defer c.Close() 756 return serverHandleTLS(c, t) 757 default: 758 t.Fatalf("unrecognized command: %q", s.Text()) 759 } 760 } 761 return s.Err() 762 } 763 764 func serverHandleTLS(c net.Conn, t *testing.T) error { 765 send := smtpSender{c}.send 766 s := bufio.NewScanner(c) 767 for s.Scan() { 768 switch s.Text() { 769 case "EHLO localhost": 770 send("250 Ok") 771 case "MAIL FROM:<joe1@example.com>": 772 send("250 Ok") 773 case "RCPT TO:<joe2@example.com>": 774 send("250 Ok") 775 case "DATA": 776 send("354 send the mail data, end with .") 777 send("250 Ok") 778 case "Subject: test": 779 case "": 780 case "howdy!": 781 case ".": 782 case "QUIT": 783 send("221 127.0.0.1 Service closing transmission channel") 784 return nil 785 default: 786 t.Fatalf("unrecognized command during TLS: %q", s.Text()) 787 } 788 } 789 return s.Err() 790 } 791 792 func init() { 793 testRootCAs := x509.NewCertPool() 794 testRootCAs.AppendCertsFromPEM(localhostCert) 795 testHookStartTLS = func(config *tls.Config) { 796 config.RootCAs = testRootCAs 797 } 798 } 799 800 func sendMail(hostPort string) error { 801 host, _, err := net.SplitHostPort(hostPort) 802 if err != nil { 803 return err 804 } 805 auth := PlainAuth("", "", "", host) 806 from := "joe1@example.com" 807 to := []string{"joe2@example.com"} 808 return SendMail(hostPort, auth, from, to, []byte("Subject: test\n\nhowdy!")) 809 } 810 811 // (copied from net/http/httptest) 812 // localhostCert is a PEM-encoded TLS cert with SAN IPs 813 // "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end 814 // of ASN.1 time). 815 // generated from src/crypto/tls: 816 // 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 817 var localhostCert = []byte(`-----BEGIN CERTIFICATE----- 818 MIIBjjCCATigAwIBAgIQMon9v0s3pDFXvAMnPgelpzANBgkqhkiG9w0BAQsFADAS 819 MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw 820 MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB 821 AM0u/mNXKkhAzNsFkwKZPSpC4lZZaePQ55IyaJv3ovMM2smvthnlqaUfVKVmz7FF 822 wLP9csX6vGtvkZg1uWAtvfkCAwEAAaNoMGYwDgYDVR0PAQH/BAQDAgKkMBMGA1Ud 823 JQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhh 824 bXBsZS5jb22HBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQAD 825 QQBOZsFVC7IwX+qibmSbt2IPHkUgXhfbq0a9MYhD6tHcj4gbDcTXh4kZCbgHCz22 826 gfSj2/G2wxzopoISVDucuncj 827 -----END CERTIFICATE-----`) 828 829 // localhostKey is the private key for localhostCert. 830 var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY----- 831 MIIBOwIBAAJBAM0u/mNXKkhAzNsFkwKZPSpC4lZZaePQ55IyaJv3ovMM2smvthnl 832 qaUfVKVmz7FFwLP9csX6vGtvkZg1uWAtvfkCAwEAAQJART2qkxODLUbQ2siSx7m2 833 rmBLyR/7X+nLe8aPDrMOxj3heDNl4YlaAYLexbcY8d7VDfCRBKYoAOP0UCP1Vhuf 834 UQIhAO6PEI55K3SpNIdc2k5f0xz+9rodJCYzu51EwWX7r8ufAiEA3C9EkLiU2NuK 835 3L3DHCN5IlUSN1Nr/lw8NIt50Yorj2cCIQCDw1VbvCV6bDLtSSXzAA51B4ZzScE7 836 sHtB5EYF9Dwm9QIhAJuCquuH4mDzVjUntXjXOQPdj7sRqVGCNWdrJwOukat7AiAy 837 LXLEwb77DIPoI5ZuaXQC+MnyyJj1ExC9RFcGz+bexA== 838 -----END RSA PRIVATE KEY-----`)