-
-
Save chrisgillis/10888032 to your computer and use it in GitHub Desktop.
package main | |
import ( | |
"fmt" | |
"log" | |
"net" | |
"net/mail" | |
"net/smtp" | |
"crypto/tls" | |
) | |
// SSL/TLS Email Example | |
func main() { | |
from := mail.Address{"", "[email protected]"} | |
to := mail.Address{"", "[email protected]"} | |
subj := "This is the email subject" | |
body := "This is an example body.\n With two lines." | |
// Setup headers | |
headers := make(map[string]string) | |
headers["From"] = from.String() | |
headers["To"] = to.String() | |
headers["Subject"] = subj | |
// Setup message | |
message := "" | |
for k,v := range headers { | |
message += fmt.Sprintf("%s: %s\r\n", k, v) | |
} | |
message += "\r\n" + body | |
// Connect to the SMTP Server | |
servername := "smtp.example.tld:465" | |
host, _, _ := net.SplitHostPort(servername) | |
auth := smtp.PlainAuth("","[email protected]", "password", host) | |
// TLS config | |
tlsconfig := &tls.Config { | |
InsecureSkipVerify: true, | |
ServerName: host, | |
} | |
// 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", servername, tlsconfig) | |
if err != nil { | |
log.Panic(err) | |
} | |
c, err := smtp.NewClient(conn, host) | |
if err != nil { | |
log.Panic(err) | |
} | |
// Auth | |
if err = c.Auth(auth); err != nil { | |
log.Panic(err) | |
} | |
// To && From | |
if err = c.Mail(from.Address); err != nil { | |
log.Panic(err) | |
} | |
if err = c.Rcpt(to.Address); err != nil { | |
log.Panic(err) | |
} | |
// Data | |
w, err := c.Data() | |
if err != nil { | |
log.Panic(err) | |
} | |
_, err = w.Write([]byte(message)) | |
if err != nil { | |
log.Panic(err) | |
} | |
err = w.Close() | |
if err != nil { | |
log.Panic(err) | |
} | |
c.Quit() | |
} |
thanks a lot @chrisgillis & @cddmp
Works perfectly, thanks for sharing
You can lose the annoying Go Vet warnings about composite literal using unkeyed fields by:-
from := mail.Address{Name:"", Address:"[email protected]"}
to := mail.Address{Name:"", Address:"[email protected]"}
This worked for me. Thanks a lot
Awesome, thanks for this!
This indeed works.
Appreaciated.
Generational answer 🫡
Passed on from debugger to debugger
Work well.
Works great!
Thanks a lot
This worked great for fastmail.com STMP. My tls config didn't require the insecure verify as well:
// TLS config
tlsconfig := &tls.Config{
ServerName: host,
}
In case someone finds it useful, to send a HTML body, add the following headers:
body := "<html><body><h1>Hello World!</h1></body></html>"
headers["MIME-Version"] = "1.0"
headers["Content-Type"] = "text/html; charset=UTF-8"
// Setup message
message := ""
for k,v := range headers {
message += fmt.Sprintf("%s: %s\r\n", k, v)
}
message += "\r\n" + body
Should we make the protection for SMTP-header-injection more explicit? like:
https://www.geeksforgeeks.org/what-is-smtp-header-injection/
https://github.com/golang/go/blob/87ec2c959c73e62bfae230ef7efca11ec2a90804/src/net/smtp/smtp.go#L429
// Setup message
message := ""
for k, v := range headers {
line := fmt.Sprintf("%s: %s", k, v)
// security assurance: RFC 5321
if err := validateLine(line); err != nil {
return err
}
message += line + "\r\n"
}
message += "\r\n" + body
// validateLine checks to see if a line has CR or LF as per RFC 5321.
func validateLine(line string) error {
if strings.ContainsAny(line, "\n\r") {
return errors.New("[mailer] smtp: A line must not contain CR or LF")
}
return nil
}
To the people getting "first record does not look like a TLS handshake" on port 587: use port 465 instead, if your provider supports it.
Your suggestion worked for me! Thanks bro! Also thanks to @chrisgillis
Please note, this example is prone to man-in-the-middle attacks.
InsecureSkipVerify
must be set tofalse,
otherwise the server certificate is not being verified. Thus, a mitm attacker can just present any (e.g. self-signed) certificate and the client will silently accept and connect. You don't want that!See also https://golang.org/pkg/crypto/tls/#Config
You should also add the date header, in order to be conform with RFC 5322:
headers["Date"] = time.Now().Format("Mon, 02 Jan 2006 15:04:05 -0700")
Thanks for the example!