[go] 通过代理使用SMTP的最佳实践

[go] 通过代理使用SMTP的最佳实践 如果你有关于SMTP代理的经验,请帮忙,我在网上找不到任何相关信息,求助

16 回复

你好。你在哪里遇到EOF错误?


是的,我已经全部填写完毕=)

感谢您的回答,我已经放弃了上面的代码,在网上找到的唯一一个——到目前为止也还是不行……

我发现了这个,但每次都遇到EOF错误

https://play.golang.org/p/ZCx8_8Uhzs2

谁知道如何使用SMTP+SOCKS5,例如通过代理使用Gmail发送邮件?

我尝试了这段在谷歌上找到的代码:

func main() {
    fmt.Println("hello world")
}

搜索其他示例,网上有很多。

我需要通过代理(socks5)+smtp方法,但什么也没找到。

在连接SMTP服务器时出现了一个错误,响应显示可能是EOF错误,这很可能是由于缺少相关代码导致的,而我对此 unfortunately 缺乏了解。

我想知道的是在函数内部哪个位置发生了错误。这个错误随后被返回并打印出来,但为了帮助你,我们必须知道程序在何处出错。

乍看之下,你的示例似乎不支持TLS,这可能是无法与Gmail正常工作的主要原因。建议你搜索其他示例,网上有很多相关资源。

https://www.google.com/search?q=golang+gmail+sender

愚蠢的问题。你是否已经将与代理相关的所有字段值都填写完整了?

ProxyUserName: "",
ProxyPassword: "",
ProxyAddress:  "",

Address:  ":",
UserName: "",
Password: "",

你在哪里遇到EOF错误?

err := s.Send("测试 from socks5", "from@xx.com", []string{"to@xx.com"}, "sockssmtp测试")
if err != nil {
    log.Fatalln(err)
}

ext:

err := s.Send("测试 from socks5", "from@xx.com", []string{"to@xx.com"}, "sockssmtp测试")
if err != nil {
log.Fatalln(err)
}

但这处于顶层。错误必定发生在函数的某个位置并被返回。这个错误来自函数中的哪个位置?

我也尝试这样做

Steffen Ullrich

Go语言中SMTP客户端的自定义拨号器?

标签: go, proxy, smtp, socks

我也失败了,可能方法不对

在Go语言中通过代理使用SMTP发送邮件,可以通过配置net/smtp包结合代理连接实现。以下是具体实现方法:

核心解决方案:

  1. 使用golang.org/x/net/proxy包建立代理连接
  2. 通过代理连接创建SMTP客户端
  3. 使用TLS加密确保安全性

示例代码:

package main

import (
    "crypto/tls"
    "fmt"
    "net"
    "net/smtp"
    
    "golang.org/x/net/proxy"
)

type LoginAuth struct {
    username, password string
}

func (a *LoginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
    return "LOGIN", []byte(a.username), nil
}

func (a *LoginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
    if more {
        switch string(fromServer) {
        case "Username:":
            return []byte(a.username), nil
        case "Password:":
            return []byte(a.password), nil
        }
    }
    return nil, nil
}

func SendMailViaProxy(proxyAddr, smtpServer, from, password string, to []string, msg []byte) error {
    // 1. 通过SOCKS5代理建立连接
    dialer, err := proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct)
    if err != nil {
        return fmt.Errorf("创建代理拨号器失败: %v", err)
    }

    // 2. 通过代理连接到SMTP服务器
    conn, err := dialer.Dial("tcp", smtpServer)
    if err != nil {
        return fmt.Errorf("通过代理连接SMTP服务器失败: %v", err)
    }
    defer conn.Close()

    // 3. 创建SMTP客户端
    client, err := smtp.NewClient(conn, smtpServer)
    if err != nil {
        return fmt.Errorf("创建SMTP客户端失败: %v", err)
    }
    defer client.Close()

    // 4. 启用TLS加密
    tlsConfig := &tls.Config{
        ServerName: smtpServer,
    }
    if err = client.StartTLS(tlsConfig); err != nil {
        return fmt.Errorf("启用TLS失败: %v", err)
    }

    // 5. 身份验证
    auth := &LoginAuth{username: from, password: password}
    if err = client.Auth(auth); err != nil {
        return fmt.Errorf("身份验证失败: %v", err)
    }

    // 6. 设置发件人和收件人
    if err = client.Mail(from); err != nil {
        return fmt.Errorf("设置发件人失败: %v", err)
    }
    for _, recipient := range to {
        if err = client.Rcpt(recipient); err != nil {
            return fmt.Errorf("设置收件人失败: %v", err)
        }
    }

    // 7. 发送邮件内容
    w, err := client.Data()
    if err != nil {
        return fmt.Errorf("准备数据写入失败: %v", err)
    }
    _, err = w.Write(msg)
    if err != nil {
        return fmt.Errorf("写入邮件内容失败: %v", err)
    }
    err = w.Close()
    if err != nil {
        return fmt.Errorf("关闭数据写入失败: %v", err)
    }

    return nil
}

func main() {
    proxyAddr := "127.0.0.1:1080"  // 代理服务器地址
    smtpServer := "smtp.example.com:587"
    from := "user@example.com"
    password := "your-password"
    to := []string{"recipient@example.com"}
    
    msg := []byte("From: user@example.com\r\n" +
        "To: recipient@example.com\r\n" +
        "Subject: 测试邮件\r\n" +
        "\r\n" +
        "这是一封通过代理发送的测试邮件。\r\n")

    err := SendMailViaProxy(proxyAddr, smtpServer, from, password, to, msg)
    if err != nil {
        fmt.Printf("发送邮件失败: %v\n", err)
    } else {
        fmt.Println("邮件发送成功")
    }
}

关键点说明:

  • 使用proxy.SOCKS5创建代理拨号器,支持SOCKS5代理协议
  • 通过dialer.Dial建立到SMTP服务器的代理连接
  • 必须启用TLS加密以确保通信安全
  • 实现了LoginAuth结构体来处理SMTP登录认证

依赖安装:

go get golang.org/x/net/proxy

此实现支持SOCKS5代理,如果需要HTTP代理,可以使用golang.org/x/net/http/httpproxy包进行类似配置。

package main

import (
	"encoding/base64"
	"fmt"
	"log"
	"math/rand"
	"net"
	"net/smtp"
	"os"
	"strings"
	"time"

	"github.com/pkg/errors"
	"golang.org/x/net/proxy"
)

type SocksSMTP struct {
	ProxyUserName string
	ProxyPassword string
	ProxyAddress  string

	Address  string
	UserName string
	Password string
}

func BaseEncode(s string) string {
	return fmt.Sprintf("=?UTF-8?B?%s?=", base64.StdEncoding.EncodeToString([]byte(s)))
}

func (s *SocksSMTP) CreateClient() (*smtp.Client, error) {
	var auth *proxy.Auth
	if s.ProxyUserName != "" {
		auth = &proxy.Auth{
			User:     s.ProxyUserName,
			Password: s.ProxyPassword,
		}
	}
	dialer, err := proxy.SOCKS5("tcp", s.ProxyAddress, auth, proxy.Direct)
	if err != nil {
		return nil, errors.Wrap(err, "Create SOCKS5")
	}
	conn, err := dialer.Dial("tcp", s.Address)
	if err != nil {
		return nil, errors.Wrap(err, "Dial")
	}

	host, _, _ := net.SplitHostPort(s.Address)
	c, err := smtp.NewClient(conn, host)

	return c, err
}

type _PlainAuth struct {
	identity, username, password string
	host                         string
}

func PlainAuth(identity, username, password string,
	host string) smtp.Auth {
	return &_PlainAuth{identity, username, password,
		host}
}

func (a *_PlainAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
	if server.Name != a.host {
		return "", nil, errors.New("wrong host name")
	}
	resp := []byte(a.identity + "\x00" + a.username + "\x00" + a.password)
	return "PLAIN", resp, nil
}

func (a *_PlainAuth) Next(from_server []byte, more bool) ([]byte, error) {
	if more {
		// We've already sent everything.
		return nil, errors.New("unexpected server challenge")
	}
	return nil, nil
}

// 发送文本
func (s *SocksSMTP) Send(subjct string, from_name string, to []string, body string) error {
	c, err := s.CreateClient()
	if err != nil {
		return errors.Wrap(err, "CreateClient")
	}
	defer c.Close()

	if err = c.Hello("localhost"); err != nil {
		return errors.Wrap(err, "hello")
	}

	host, _, err := net.SplitHostPort(s.Address)
	if err != nil {
		return err
	}

	if ok, _ := c.Extension("STARTTLS"); ok {
		fmt.Println("NEED TLS")
		// TODO
	}

	auth := PlainAuth("", s.UserName, s.Password, host)
	if err = c.Auth(auth); err != nil {
		return errors.Wrap(err, "Auth")
	}

	// 设置发件人
	if err = c.Mail(s.UserName); err != nil {
		return errors.Wrap(err, "Set Sender")
	}

	// 设置接收人
	for _, addr := range to {
		if err = c.Rcpt(addr); err != nil {
			return errors.Wrap(err, "Set Receiver")
		}
	}
	w, err := c.Data()
	if err != nil {
		return errors.Wrap(err, "Client.Data")
	}

	hostname, err := os.Hostname()
	if err != nil {
		return errors.Wrap(err, "Get hostname")
	}
	headers := map[string]string{}
	headers["Subject"] = BaseEncode(subjct)
	headers["To"] = strings.Join(to, ", ")
	headers["From"] = from_name
	headers["MIME-Version"] = "1.0"
	headers["Content-Type"] = "text/plain; charset=UTF-8"
	headers["Message-ID"] = fmt.Sprintf("<%f.%d@%s>", rand.Float64(),
		time.Now().UnixNano(), hostname)

	msg := ""
	for k, v := range headers {
		msg += fmt.Sprintf("%s: %s\r\n", k, v)
	}
	msg += fmt.Sprintf("\r\n" + body)

	if _, err = w.Write([]byte(msg)); err != nil {
		return errors.Wrap(err, "Writer msg")
	}
	if err = w.Close(); err != nil {
		return err
	}

	return c.Quit()
}

func main() {
	s := &SocksSMTP{
		ProxyUserName: "",
		ProxyPassword: "",
		ProxyAddress:  "",

		Address:  "<host>:<port>",
		UserName: "",
		Password: "",
	}

	err := s.Send("测试 from socks5", "from@xx.com", []string{"to@xx.com"}, "sockssmtp测试")
	if err != nil {
		log.Fatalln(err)
	}
}
回到顶部