用go语言直接向mx记录的25端口发邮件 Oct 30, 2024 • Xiang Xisheng 已测试可以成功向qq邮箱、icloud和gmail发邮件,除了gmail会进垃圾邮箱,其他都正常收信 下面是linux的搭建步骤 wget -O /root/go1.23.2.linux-amd64.tar.gz https://golang.google.cn/dl/go1.23.2.linux-amd64.tar.gz tar xf /root/go1.23.2.linux-amd64.tar.gz -C /root mkdir /root/sendmail cd /root/sendmail nano main.go /root/go/bin/go mod init sendmail /root/go/bin/go build ./sendmail -from go-sendmail-test@2398dj.com -to 309385018@qq.com -subject GO语言测试邮件 -body 这是你要发的邮件内容 AI提示词 用go语言直接向mx记录的25端口发邮件,要采用STARTTLS连接方式,要包含Message-ID,要做成实用工具可指定from\to\subject\body package main import ( "crypto/tls" "flag" "fmt" "log" "math/rand" "net" "net/smtp" "strings" "time" ) func main() { // 定义命令行参数 fromPtr := flag.String("from", "", "发件人邮箱") toPtr := flag.String("to", "", "收件人邮箱") subjectPtr := flag.String("subject", "", "邮件主题") bodyPtr := flag.String("body", "", "邮件内容") flag.Parse() // 检查必填参数 if *fromPtr == "" || *toPtr == "" || *subjectPtr == "" || *bodyPtr == "" { fmt.Println("Usage: ./sendmail -from <sender_email> -to <recipient_email> -subject <subject> -body <bodytext>") return } // 发送邮件 err := SendMail(*fromPtr, *toPtr, *subjectPtr, *bodyPtr) if err != nil { log.Fatal(err) } fmt.Println("Email sent successfully") } func generateMessageID(from string) string { timestamp := time.Now().UnixNano() randomPart := rand.Int() domain := strings.Split(from, "@")[1] return fmt.Sprintf("<%d.%d@%s>", timestamp, randomPart, domain) } func SendMail(from, to, subject, body string) error { // 1. 解析收件人域名的MX记录 domain := strings.Split(to, "@")[1] mxRecords, err := net.LookupMX(domain) if err != nil { return fmt.Errorf("failed to lookup MX records: %v", err) } if len(mxRecords) == 0 { return fmt.Errorf("no MX records found for domain %s", domain) } // 2. 连接到第一个MX记录的25端口 mxHost := mxRecords[0].Host conn, err := net.Dial("tcp", mxHost+":25") if err != nil { return fmt.Errorf("failed to connect to MX host %s: %v", mxHost, err) } defer conn.Close() client, err := smtp.NewClient(conn, mxHost) if err != nil { return fmt.Errorf("failed to create SMTP client: %v", err) } defer client.Quit() // 3. 发送 HELO 命令,使用发件人的域名 senderDomain := strings.Split(from, "@")[1] if err := client.Hello(senderDomain); err != nil { return fmt.Errorf("failed to say HELO: %v", err) } // 4. 发送 STARTTLS 命令 if ok, _ := client.Extension("STARTTLS"); !ok { return fmt.Errorf("STARTTLS not supported by %s", mxHost) } config := &tls.Config{ServerName: mxHost, InsecureSkipVerify: true} if err := client.StartTLS(config); err != nil { return fmt.Errorf("failed to start TLS: %v", err) } // 5. 设置发件人和收件人 if err := client.Mail(from); err != nil { return fmt.Errorf("failed to set sender: %v", err) } if err := client.Rcpt(to); err != nil { return fmt.Errorf("failed to set recipient: %v", err) } // 6. 编写邮件内容,添加Message-ID头 wc, err := client.Data() if err != nil { return fmt.Errorf("failed to get write closer: %v", err) } msgID := generateMessageID(from) msg := fmt.Sprintf("Message-ID: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s", msgID, from, to, subject, body) _, err = wc.Write([]byte(msg)) if err != nil { return fmt.Errorf("failed to write message: %v", err) } if err := wc.Close(); err != nil { return fmt.Errorf("failed to close write closer: %v", err) } return nil }