How to connect and send emails from exchange server in Go using starttls?

huangapple go评论87阅读模式
英文:

How to connect and send emails from exchange server in Go using starttls?

问题

我正在将一些现有的Python代码移植到Go语言。其中一部分负责通过Exchange服务器(SMTP + STARTTLS)发送电子邮件。现有的(简化的)代码如下:

package main

import (
	"log"
	"net/smtp"
)

func main() {
	auth := smtp.PlainAuth("", "user", "password", "exchangeserver.com")
	err := smtp.SendMail("exchangeserver.com:587", auth, "user@exchangeserver.com", []string{"otheruser1@exchangeserver.com", "otheruser2@exchangeserver.com"}, []byte("..message.."))
	if err != nil {
		log.Fatal(err)
	}
}

希望对你有所帮助,谢谢。

英文:

I'm working on porting some existing Python code to Go. One bit is in charge of sending emails through Exchange server (SMTP + STARTTLS). Existing (simplified) code looks like this:

import smtplib
client = smtplib.SMTP("exchangeserver.com")
client.starttls()
client.login('user', 'password')
client.sendmail('user@exchangeserver.com', 
    ['otheruser1@exchangeserver.com', 'otheruser2@exchangeserver.com'], 
    '..message..')

I'd like to do the same thing with Go, help is appreciated - thanks.

答案1

得分: 5

以下是使用tls连接设置的示例代码。我没有测试环境来进行连接测试,所以在我的环境中连接失败,但我认为这是使其正常工作所需的内容:

package main

import (
  "crypto/tls"
  "fmt"
  "log"
  "net/smtp"
)

func main() {
  var (
    host     = "smtp.google.com"
    port     = 587
    from     = "foo@bar.com"
    password = "baz"
    to       = []string{"bin@bar.com"}
    msg      = []byte("This is my message")
    auth     = smtp.PlainAuth("", from, password, "smtp.gmail.com")
  )
  serverAddr := fmt.Sprintf("%s:%d", host, port)

  conn, err := tls.Dial("tcp", serverAddr, nil)
  if err != nil {
    log.Printf("Error Dialing %s\n", err)
    return
  }

  client, err := smtp.NewClient(conn, host)
  if err != nil {
    log.Printf("Error SMTP connection: %s\n", err)
    return
  }

  if ok, _ := client.Extension("AUTH"); ok {
    if err := client.Auth(auth); err != nil {
      log.Printf("Error during AUTH %s\n", err)
      return
    }
  }

  if err := client.Mail(from); err != nil {
    log.Printf("Error: %s\n", err)
    return
  }

  for _, addr := range to {
    if err := client.Rcpt(addr); err != nil {
      log.Printf("Error: %s\n", err)
      return
    }
  }

  w, err := client.Data()
  if err != nil {
    log.Printf("Error: %s\n", err)
    return
  }

  _, err = w.Write(msg)
  if err != nil {
    log.Printf("Error: %s\n", err)
    return

  }

  err = w.Close()
  if err != nil {
    log.Printf("Error: %s\n", err)
    return

  }

  client.Quit()
}

我认为类似这样的代码可能会起作用。您需要根据服务器、用户名/密码等进行适当的更改。如果需要更多帮助,请告诉我。

package main

import (
  "fmt"
  "log"
  "net/smtp"
)

func main() {
  to := "foo@foo.com"
  from := "bin@baz.com"
  password := "myPassword"
  subject := "subject line of email"
  msg := "a one-line email message"

  emailTemplate := `To: %s
Subject: %s

%s
`
  body := fmt.Sprintf(emailTemplate, to, subject, msg)
  auth := smtp.PlainAuth("", from, password, "smtp.gmail.com")
  err := smtp.SendMail(
    "smtp.gmail.com:587",
    auth,
    from,
    []string{to},
    []byte(body),
  )
  if err != nil {
    log.Fatal(err)
  }
}

希望对你有帮助!

英文:

Edited:

Here is an example using the tls connection setup. I don't have one to test against, so it fails on my end connecting, but I suspect this is what you need to make it work

package main
import (
"crypto/tls"
"fmt"
"log"
"net/smtp"
)
func main() {
var (
host     = "smtp.google.com"
port     = 587
from     = "foo@bar.com"
password = "baz"
to       = []string{"bin@bar.com"}
msg      = []byte("This is my message")
auth     = smtp.PlainAuth("", from, password, "smtp.gmail.com")
)
serverAddr := fmt.Sprintf("%s:%d", host, port)
conn, err := tls.Dial("tcp", serverAddr, nil)
if err != nil {
log.Printf("Error Dialing %s\n", err)
return
}
client, err := smtp.NewClient(conn, host)
if err != nil {
log.Printf("Error SMTP connection: %s\n", err)
return
}
if ok, _ := client.Extension("AUTH"); ok {
if err := client.Auth(auth); err != nil {
log.Printf("Error during AUTH %s\n", err)
return
}
}
if err := client.Mail(from); err != nil {
log.Printf("Error: %s\n", err)
return
}
for _, addr := range to {
if err := client.Rcpt(addr); err != nil {
log.Printf("Error: %s\n", err)
return
}
}
w, err := client.Data()
if err != nil {
log.Printf("Error: %s\n", err)
return
}
_, err = w.Write(msg)
if err != nil {
log.Printf("Error: %s\n", err)
return
}
err = w.Close()
if err != nil {
log.Printf("Error: %s\n", err)
return
}
client.Quit()
}

I think something like this might work. You'll have to make the appropriate changes for server, user/password, etc. Let me know if you need more help.

package main
import (
"fmt"
"log"
"net/smtp"
)
func main() {
to := "foo@foo.com"
from := "bin@baz.com"
password := "myPassword"
subject := "subject line of email"
msg := "a one-line email message"
emailTemplate := `To: %s
Subject: %s
%s
`
body := fmt.Sprintf(emailTemplate, to, subject, msg)
auth := smtp.PlainAuth("", from, password, "smtp.gmail.com")
err := smtp.SendMail(
"smtp.gmail.com:587",
auth,
from,
[]string{to},
[]byte(body),
)
if err != nil {
log.Fatal(err)
}
}

答案2

得分: 5

以下是翻译好的内容:

要扩展Cory LaNou的答案,你可以在普通的net.Conn上使用StartTLS。你需要定义一个tls.Config,然后使用它来升级连接以使用TLS。

下面的示例基于Cory的答案,并从crypto/tls测试文件中借用了一些代码来创建RSA证书和私钥。在实际生产中,你显然会用真实的证书和密钥替换这些。

可能需要根据你的环境对变量进行一些自定义,以及对tls.Config进行一些更改。

package main

import (
	"crypto/rsa"
	"crypto/tls"
	"encoding/hex"
	"fmt"
	"log"
	"math/big"
	"net"
	"net/smtp"
)

func main() {

	var (
		host     = "smtp.myexchange.com"
		port     = 587
		from     = "sender@example.org"
		password = "password"
		to       = []string{"recipient@example.org"}
		msg      = []byte("This is my message")
		auth     = smtp.PlainAuth("", from, password, "smtp.myexchange.com")
	)

	conf := new(tls.Config)
	conf.Certificates = make([]tls.Certificate, 1)
	conf.Certificates[0].Certificate = [][]byte{testRSACertificate}
	conf.Certificates[0].PrivateKey = testRSAPrivateKey
	conf.CipherSuites = []uint16{tls.TLS_RSA_WITH_RC4_128_SHA}
	conf.InsecureSkipVerify = true
	conf.MinVersion = tls.VersionSSL30
	conf.MaxVersion = tls.VersionTLS10
	serverAddr := fmt.Sprintf("%s:%d", host, port)

	conn, err := net.Dial("tcp", serverAddr)
	if err != nil {
		log.Printf("Error Dialing %s\n", err)
		return
	}

	client, err := smtp.NewClient(conn, host)
	if err != nil {
		log.Printf("Error SMTP connection: %s\n", err)
		return
	}

	if err = client.StartTLS(conf); err != nil {
		log.Printf("Error performing StartTLS: %s\n", err)
		return
	}

	if ok, _ := client.Extension("AUTH"); ok {
		if err := client.Auth(auth); err != nil {
			log.Printf("Error during AUTH %s\n", err)
			return
		}
	}

	if err := client.Mail(from); err != nil {
		log.Printf("Error: %s\n", err)
		return
	}

	for _, addr := range to {
		if err := client.Rcpt(addr); err != nil {
			log.Printf("Error: %s\n", err)
			return
		}
	}

	w, err := client.Data()
	if err != nil {
		log.Printf("Error: %s\n", err)
		return
	}

	_, err = w.Write(msg)
	if err != nil {
		log.Printf("Error: %s\n", err)
		return

	}

	err = w.Close()
	if err != nil {
		log.Printf("Error: %s\n", err)
		return

	}

	client.Quit()
}

// 以下代码来自http://golang.org/src/pkg/crypto/tls/handshake_server_test.go

func bigFromString(s string) *big.Int {
	ret := new(big.Int)
	ret.SetString(s, 10)
	return ret
}

func fromHex(s string) []byte {
	b, _ := hex.DecodeString(s)
	return b
}

var testRSACertificate = fromHex("308202b030820219a00302010202090085b0bba48a7fb8ca300d06092a864886f70d01010505003045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3130303432343039303933385a170d3131303432343039303933385a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819f300d06092a864886f70d010101050003818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a381a73081a4301d0603551d0e04160414b1ade2855acfcb28db69ce2369ded3268e18883930750603551d23046e306c8014b1ade2855acfcb28db69ce2369ded3268e188839a149a4473045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746482090085b0bba48a7fb8ca300c0603551d13040530030101ff300d06092a864886f70d010105050003818100086c4524c76bb159ab0c52ccf2b014d7879d7a6475b55a9566e4c52b8eae12661feb4f38b36e60d392fdf74108b52513b1187a24fb301dbaed98b917ece7d73159db95d31d78ea50565cd5825a2d5a5f33c4b6d8c97590968c0f5298b5cd981f89205ff2a01ca31b9694dda9fd57e970e8266d71999b266e3850296c90a7bdd9")

var testRSAPrivateKey = &rsa.PrivateKey{
	PublicKey: rsa.PublicKey{
		N: bigFromString("131650079503776001033793877885499001334664249354723305978524647182322416328664556247316495448366990052837680518067798333412266673813370895702118944398081598789828837447552603077848001020611640547221687072142537202428102790818451901395596882588063427854225330436740647715202971973145151161964464812406232198521"),
		E: 65537,
	},
	D: bigFromString("29354450337804273969007277378287027274721892607543397931919078829901848876371746653677097639302788129485893852488285045793268732234230875671682624082413996177431586734171663258657462237320300610850244186316880055243099640544518318093544057213190320837094958164973959123058337475052510833916491060913053867729"),
	Primes: []*big.Int{
		bigFromString("11969277782311800166562047708379380720136961987713178380670422671426759650127150688426177829077494755200794297055316163155755835813760102405344560929062149"),
		bigFromString("10998999429884441391899182616418192492905073053684657075974935218461686523870125521822756579792315215543092255516093840728890783887287417039645833477273829"),
	},
}

[Playground](http://play.golang.org/p/OcUq9IAet1)
英文:

To expand on Cory LaNou's answer, you can use StartTLS on a normal net.Conn. You need to define a tls.Config and then use that to upgrade the connection to use TLS.

The example below is based on Cory's answer and also steals some code from the crypto/tls test files for creating the RSA cert and private key. In production you'd obviously replace these with real certs and keys.

It may require some customization of the variables, as well as some changes to the tls.Config to suit your environment.

package main
import (
"crypto/rsa"
"crypto/tls"
"encoding/hex"
"fmt"
"log"
"math/big"
"net"
"net/smtp"
)
func main() {
var (
host     = "smtp.myexchange.com"
port     = 587
from     = "sender@example.org"
password = "password"
to       = []string{"recipient@example.org"}
msg      = []byte("This is my message")
auth     = smtp.PlainAuth("", from, password, "smtp.myexchange.com")
)
conf := new(tls.Config)
conf.Certificates = make([]tls.Certificate, 1)
conf.Certificates[0].Certificate = [][]byte{testRSACertificate}
conf.Certificates[0].PrivateKey = testRSAPrivateKey
conf.CipherSuites = []uint16{tls.TLS_RSA_WITH_RC4_128_SHA}
conf.InsecureSkipVerify = true
conf.MinVersion = tls.VersionSSL30
conf.MaxVersion = tls.VersionTLS10
serverAddr := fmt.Sprintf("%s:%d", host, port)
conn, err := net.Dial("tcp", serverAddr)
if err != nil {
log.Printf("Error Dialing %s\n", err)
return
}
client, err := smtp.NewClient(conn, host)
if err != nil {
log.Printf("Error SMTP connection: %s\n", err)
return
}
if err = client.StartTLS(conf); err != nil {
log.Printf("Error performing StartTLS: %s\n", err)
return
}
if ok, _ := client.Extension("AUTH"); ok {
if err := client.Auth(auth); err != nil {
log.Printf("Error during AUTH %s\n", err)
return
}
}
if err := client.Mail(from); err != nil {
log.Printf("Error: %s\n", err)
return
}
for _, addr := range to {
if err := client.Rcpt(addr); err != nil {
log.Printf("Error: %s\n", err)
return
}
}
w, err := client.Data()
if err != nil {
log.Printf("Error: %s\n", err)
return
}
_, err = w.Write(msg)
if err != nil {
log.Printf("Error: %s\n", err)
return
}
err = w.Close()
if err != nil {
log.Printf("Error: %s\n", err)
return
}
client.Quit()
}
// Code below from http://golang.org/src/pkg/crypto/tls/handshake_server_test.go
func bigFromString(s string) *big.Int {
ret := new(big.Int)
ret.SetString(s, 10)
return ret
}
func fromHex(s string) []byte {
b, _ := hex.DecodeString(s)
return b
}
var testRSACertificate = fromHex("308202b030820219a00302010202090085b0bba48a7fb8ca300d06092a864886f70d01010505003045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3130303432343039303933385a170d3131303432343039303933385a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819f300d06092a864886f70d010101050003818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a381a73081a4301d0603551d0e04160414b1ade2855acfcb28db69ce2369ded3268e18883930750603551d23046e306c8014b1ade2855acfcb28db69ce2369ded3268e188839a149a4473045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746482090085b0bba48a7fb8ca300c0603551d13040530030101ff300d06092a864886f70d010105050003818100086c4524c76bb159ab0c52ccf2b014d7879d7a6475b55a9566e4c52b8eae12661feb4f38b36e60d392fdf74108b52513b1187a24fb301dbaed98b917ece7d73159db95d31d78ea50565cd5825a2d5a5f33c4b6d8c97590968c0f5298b5cd981f89205ff2a01ca31b9694dda9fd57e970e8266d71999b266e3850296c90a7bdd9")
var testRSAPrivateKey = &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: bigFromString("131650079503776001033793877885499001334664249354723305978524647182322416328664556247316495448366990052837680518067798333412266673813370895702118944398081598789828837447552603077848001020611640547221687072142537202428102790818451901395596882588063427854225330436740647715202971973145151161964464812406232198521"),
E: 65537,
},
D: bigFromString("29354450337804273969007277378287027274721892607543397931919078829901848876371746653677097639302788129485893852488285045793268732234230875671682624082413996177431586734171663258657462237320300610850244186316880055243099640544518318093544057213190320837094958164973959123058337475052510833916491060913053867729"),
Primes: []*big.Int{
bigFromString("11969277782311800166562047708379380720136961987713178380670422671426759650127150688426177829077494755200794297055316163155755835813760102405344560929062149"),
bigFromString("10998999429884441391899182616418192492905073053684657075974935218461686523870125521822756579792315215543092255516093840728890783887287417039645833477273829"),
},
}

Playground

huangapple
  • 本文由 发表于 2014年3月2日 01:32:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/22117778.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定