func smtpSendMailPart2(c *smtp.Client, smtpHost string, a smtp.Auth, from string, to []string, msg []byte) (err error) { defer c.Close() if err = c.Hello("localhost"); err != nil { return err } if ok, _ := c.Extension("STARTTLS"); ok { config := &tls.Config{ServerName: smtpHost} if err = c.StartTLS(config); err != nil { return err } } if a != nil { if ok, _ := c.Extension("AUTH"); ok { if err = c.Auth(a); err != nil { return err } } } if err = c.Mail(from); err != nil { return err } for _, addr := range to { if err = c.Rcpt(addr); err != nil { return err } } w, err := c.Data() if err != nil { return err } _, err = w.Write(msg) if err != nil { return err } err = w.Close() if err != nil { return err } return c.Quit() }
// Attempt to connect to the specified server. The connection attempt is // performed in a separate goroutine, allowing it to be aborted if the host // queue is shut down. func (h *Host) tryMailServer(server, hostname string) (*smtp.Client, error) { var ( c *smtp.Client err error done = make(chan bool) ) go func() { c, err = smtp.Dial(fmt.Sprintf("%s:25", server)) close(done) }() select { case <-done: case <-h.stop: return nil, nil } if err != nil { return nil, err } if err := c.Hello(hostname); err != nil { return nil, err } if ok, _ := c.Extension("STARTTLS"); ok { config := &tls.Config{ServerName: server} if h.config.DisableSSLVerification { config.InsecureSkipVerify = true } if err := c.StartTLS(config); err != nil { return nil, err } } return c, nil }
func NewSmtpClient(addr string, a smtp.Auth, concurrency int) (client *SmtpClient, err error) { pool := make(chan *smtp.Client, concurrency) for i := 0; i < concurrency; i++ { var c *smtp.Client c, err = smtp.Dial(addr) if err != nil { return } if err = c.Hello("localhost"); err != nil { return } if ok, _ := c.Extension("STARTTLS"); ok { if err = c.StartTLS(nil); err != nil { return } } if ok, _ := c.Extension("AUTH"); ok { if err = c.Auth(a); err != nil { return } } pool <- c } client = &SmtpClient{pool, &sync.Mutex{}, false} return }
// TestSMTP tests if can connect with the server and send some commands. func TestSMTP(addr string, a smtp.Auth, hello string, timeout time.Duration, insecureSkipVerify bool) error { serverName := addr port := "" s := strings.SplitN(addr, ":", 2) if len(s) >= 2 { serverName = s[0] port = s[1] } if serverName == "" || port == "" { return e.New("addrs is invalid") } hosts, err := dns.LookupHostCache(serverName) if err != nil { return e.Forward(err) } if len(hosts) == 0 { return e.New("can't resolve the addr") } conn, err := net.DialTimeout("tcp", hosts[0]+":"+port, timeout) if err != nil { return e.Forward(err) } defer conn.Close() command := &Command{ Timeout: timeout, Conn: conn, } var c *smtp.Client r := command.Exec(smtp.NewClient, conn, serverName) r(&c, &err) if err != nil { return e.Forward(err) } defer c.Close() if hello != "" { r = command.Exec(c.Hello, hello) r(&err) if err != nil { return e.Forward(err) } } if ok, _ := c.Extension("STARTTLS"); ok { r = command.Exec(c.StartTLS, &tls.Config{ ServerName: serverName, InsecureSkipVerify: insecureSkipVerify, }) r(&err) if err != nil { return e.Forward(err) } } if a != nil { found, _ := c.Extension("AUTH") if found { r = command.Exec(c.Auth, a) r(&err) if err != nil { return e.Forward(err) } } } r = command.Exec(c.Reset) r(&err) if err != nil { return e.New(err) } r = command.Exec(c.Quit) r(&err) if err != nil { return e.New(err) } return nil }
// SendMail send a message to specific destination (to) using smtp server in addrs // and a auth. func SendMail(addr string, a smtp.Auth, from string, to []string, hello string, msg []byte, timeout time.Duration, insecureSkipVerify bool) error { serverName := addr port := "" serverName, port, err := net.SplitHostPort(addr) if err != nil { return e.Push(err, "invalid adderess") } if serverName == "" || port == "" { return e.New("addrs is invalid") } conn, err := net.DialTimeout("tcp", addr, timeout) if err != nil { return e.New(err) } defer conn.Close() command := &Command{ Timeout: timeout, Conn: conn, } var c *smtp.Client r := command.Exec(smtp.NewClient, conn, serverName) r(&c, &err) if err != nil { return e.Forward(err) } defer c.Close() if hello != "" { r = command.Exec(c.Hello, hello) r(&err) if err != nil { return e.Forward(err) } } if ok, _ := c.Extension("STARTTLS"); ok { r = command.Exec(c.StartTLS, &tls.Config{ ServerName: serverName, InsecureSkipVerify: insecureSkipVerify, }) r(&err) if err != nil { return e.Forward(err) } } if a != nil { found, _ := c.Extension("AUTH") if found { r = command.Exec(c.Auth, a) r(&err) if err != nil { return e.Forward(err) } } } r = command.Exec(c.Mail, from) r(&err) if err != nil { return e.Forward(err) } for _, addr := range to { r = command.Exec(c.Rcpt, addr) r(&err) if err != nil { return e.New(err) } } var w io.WriteCloser r = command.ExecTimeout(0, c.Data) r(&w, &err) if err != nil { return e.New(err) } _, err = w.Write(msg) if err != nil { return e.New(err) } err = w.Close() if err != nil { return e.New(err) } r = command.Exec(c.Quit) r(&err) if err != nil { return e.New(err) } return nil }
// SendMailMessage handles outgoing message to SMTP server. // // - The connections to the service can be either plain (port 25) // or SSL/TLS (port 465) // // - If the server supports STARTTLS and the channel is not already // encrypted (via SSL), the application will use the "STLS" command // to initiate a channel encryption. // // - Connections can be tunneled through any SOCKS5 proxy (like Tor) func SendMailMessage(host, proxy, fromAddr, toAddr string, body []byte) error { var ( c0 net.Conn c1 *tls.Conn cli *smtp.Client ) defer func() { if cli != nil { cli.Close() } if c1 != nil { c1.Close() } if c0 != nil { c0.Close() } }() uSrv, err := url.Parse(host) if err != nil { return err } if proxy == "" { c0, err = net.Dial("tcp", uSrv.Host) } else { host, port, err := SplitHost(uSrv.Host) if err != nil { return err } c0, err = Socks5Connect("tcp", host, port, proxy) } if err != nil { return err } if c0 == nil { return errors.New("Can't estabish connection to " + uSrv.Host) } sslConfig := &tls.Config{InsecureSkipVerify: true} if uSrv.Scheme == "smtps" { c1 = tls.Client(c0, sslConfig) if err = c1.Handshake(); err != nil { return err } cli, err = smtp.NewClient(c1, uSrv.Host) } else { cli, err = smtp.NewClient(c0, uSrv.Host) if err == nil { if ok, _ := cli.Extension("STLS"); ok { err = cli.StartTLS(sslConfig) } } } if err != nil { return err } pw, _ := uSrv.User.Password() auth := smtp.PlainAuth("", uSrv.User.Username(), pw, uSrv.Host) if err = cli.Auth(auth); err != nil { return err } if err = cli.Mail(fromAddr); err != nil { return err } if err = cli.Rcpt(toAddr); err != nil { return err } wrt, err := cli.Data() if err != nil { return err } wrt.Write(body) wrt.Close() if err = cli.Quit(); err != nil { return err } return nil }
func SendMail(timeout time.Duration, addr string, from string, to string, msg []byte) error { response := make(chan error, 1) var conn *smtp.Client var err error go func() { conn, err = smtp.Dial(addr) if err != nil { response <- err return } response <- nil }() select { case res := <-response: if res == nil { go func() { defer conn.Close() if err = conn.Hello("localhost"); err != nil { response <- err return } if ok, _ := conn.Extension("STARTTLS"); ok { config := &tls.Config{ServerName: addr} if err = conn.StartTLS(config); err != nil { response <- err return } } if err = conn.Mail(from); err != nil { response <- err return } if err = conn.Rcpt(to); err != nil { response <- err return } w, err := conn.Data() if err != nil { response <- err return } _, err = w.Write(msg) if err != nil { response <- err return } err = w.Close() if err != nil { response <- err return } response <- conn.Quit() }() return <-response } else { return res } case <-time.After(time.Second * timeout): //don't do the smtp transaction, abandon the socket, it'll timeout after ~3 mins in syn_sent return fmt.Errorf("Sending timeout") } }
// Send an email using the given host, SMTP auth (optional) and HELO hostname, returns any error thrown by smtp.SendMail // This function merges the To, Cc, and Bcc fields and calls the smtp.SendMail function using the Email.Bytes() output as the message func (e *Email) SendWithHELO(hostname string, port int32, a smtp.Auth, heloHostname string, esCerts *configprofile.CodeSigningCerts) error { // format server address addr := fmt.Sprintf("%s:%d", hostname, port) // Merge the To, Cc, and Bcc fields to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc)) to = append(append(append(to, e.To...), e.Cc...), e.Bcc...) for i := 0; i < len(to); i++ { addr, err := mail.ParseAddress(to[i]) if err != nil { return err } to[i] = addr.Address } // Check to make sure there is at least one recipient and one "From" address if e.From == "" || len(to) == 0 { return errors.New("Must specify at least one From address and one To address") } from, err := mail.ParseAddress(e.From) if err != nil { return err } // Sign the email with S/MIME cmd := exec.Command("openssl", "smime", "-sign", "-signer", esCerts.Cert, "-inkey", esCerts.Key) emailBytes, err := e.Bytes() if err != nil { return err } stdin, err := cmd.StdinPipe() if err != nil { return err } stdout, err := cmd.StdoutPipe() if err != nil { return err } err = cmd.Start() if err != nil { return err } stdin.Write(emailBytes) stdin.Close() signedData, err := ioutil.ReadAll(stdout) if err != nil { return err } err = cmd.Wait() if err != nil { return err } var signedEmail bytes.Buffer headerToBytes(&signedEmail, e.msgHeaders()) signedEmail.Write(signedData) raw := signedEmail.Bytes() // Manually send email using net/smtp var c *smtp.Client if port == 465 { // TLS config tlsconfig := &tls.Config{ InsecureSkipVerify: true, ServerName: hostname, } // Here is the key, you need to call tls.Dial instead of smtp.Dial // for smtp servers running on 465 that require an ssl connection // from the very beginning (no starttls) conn, err := tls.Dial("tcp", addr, tlsconfig) if err != nil { return err } c, err = smtp.NewClient(conn, hostname) if err != nil { return err } } else { c, err = smtp.Dial(addr) if err != nil { return err } } defer c.Close() if err = c.Hello(heloHostname); err != nil { return err } if ok, _ := c.Extension("STARTTLS"); ok { config := &tls.Config{ServerName: hostname} if err = c.StartTLS(config); err != nil { return err } } if a != nil { if ok, _ := c.Extension("AUTH"); ok { if err = c.Auth(a); err != nil { return err } } } if err = c.Mail(from.Address); err != nil { return err } for _, addr := range to { if err = c.Rcpt(addr); err != nil { return err } } w, err := c.Data() if err != nil { return err } _, err = w.Write(raw) if err != nil { return err } err = w.Close() if err != nil { return err } return c.Quit() }