用go语言直接向mx记录的25端口发邮件

已测试可以成功向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
}
posted on 2024-10-31 10:58  项希盛  阅读(9)  评论(0编辑  收藏  举报