From 457c8da4d84b1dc44cf64adb726f344129acc41d Mon Sep 17 00:00:00 2001 From: Santiago De la Cruz Date: Thu, 6 Jul 2023 20:28:34 -0400 Subject: [PATCH] use custom conn Fix #79, #24 --- README.md | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- email.go | 44 +++++++++++++++++++-------------- 2 files changed, 98 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 1e0c070..54b58c9 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ Go Simple Mail supports: - Support text/calendar content type body (since v2.11.0) - Support add a List-Unsubscribe header (since v2.11.0) - Support to add a DKIM signarure (since v2.11.0) +- Support to send using custom connection, ideal for proxy (since v2.15.0) ## Documentation @@ -102,7 +103,7 @@ package main import ( "log" - "github.com/xhit/go-simple-mail/v2" + mail "github.com/xhit/go-simple-mail/v2" "github.com/toorop/go-dkim" ) @@ -269,6 +270,77 @@ func main() { } ``` +# Send with custom connection + +It's possible to use a custom connection with custom dieler, like a dialer that uses a proxy server, etc... + +With this, these servers params are ignored: `Host`, `Port`. You have total control of the connection. + +Example using a conn for proxy: + +```go +package main + +import ( + "crypto/tls" + "fmt" + "log" + "net" + + mail "github.com/xhit/go-simple-mail/v2" + "golang.org/x/net/proxy" +) + +func main() { + server := mail.NewSMTPClient() + + host := "smtp.example.com" + port := 587 + proxyAddr := "proxy.server" + + conn, err := getCustomConnWithProxy(proxyAddr, host, port) + if err != nil { + log.Fatal(err) + } + + server.Username = "test@example.com" + server.Password = "examplepass" + server.Encryption = mail.EncryptionSTARTTLS + server.CustomConn = conn + + smtpClient, err := server.Connect() + if err != nil { + log.Fatal(err) + } + + email := mail.NewMSG() + + err = email.SetFrom("From Example ").AddTo("xhit@example.com").Send(smtpClient) + if err != nil { + log.Fatal(err) + } + +} + +func getCustomConnWithProxy(proxyAddr, host string, port int) (net.Conn, error) { + dial, err := proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct) + if err != nil { + return nil, err + } + + dialer, err := dial.Dial("tcp", fmt.Sprintf("%s:%d", host, port)) + if err != nil { + return nil, err + } + + conf := &tls.Config{ServerName: host} + conn := tls.Client(dialer, conf) + + return conn, nil +} + +``` + ## More examples See [example/example_test.go](example/example_test.go). diff --git a/email.go b/email.go index e38dbfe..c590edc 100644 --- a/email.go +++ b/email.go @@ -52,6 +52,9 @@ type SMTPServer struct { Port int KeepAlive bool TLSConfig *tls.Config + + // use custom dialer + CustomConn net.Conn } // SMTPClient represents a SMTP Client for send email @@ -711,27 +714,30 @@ func (email *Email) SendEnvelopeFrom(from string, client *SMTPClient) error { } // dial connects to the smtp server with the request encryption type -func dial(host string, port string, encryption Encryption, config *tls.Config) (*smtpClient, error) { +func dial(customConn net.Conn, host string, port string, encryption Encryption, config *tls.Config) (*smtpClient, error) { var conn net.Conn var err error + var c *smtpClient - address := host + ":" + port - - // do the actual dial - switch encryption { - // TODO: Remove EncryptionSSL check before launch v3 - case EncryptionSSL, EncryptionSSLTLS: - conn, err = tls.Dial("tcp", address, config) - default: - conn, err = net.Dial("tcp", address) - } + if customConn != nil { + conn = customConn + } else { + address := host + ":" + port + // do the actual dial + switch encryption { + // TODO: Remove EncryptionSSL check before launch v3 + case EncryptionSSL, EncryptionSSLTLS: + conn, err = tls.Dial("tcp", address, config) + default: + conn, err = net.Dial("tcp", address) + } - if err != nil { - return nil, errors.New("Mail Error on dialing with encryption type " + encryption.String() + ": " + err.Error()) + if err != nil { + return nil, errors.New("Mail Error on dialing with encryption type " + encryption.String() + ": " + err.Error()) + } } - c, err := newClient(conn, host) - + c, err = newClient(conn, host) if err != nil { return nil, fmt.Errorf("Mail Error on smtp dial: %w", err) } @@ -741,9 +747,9 @@ func dial(host string, port string, encryption Encryption, config *tls.Config) ( // smtpConnect connects to the smtp server and starts TLS and passes auth // if necessary -func smtpConnect(host, port, helo string, encryption Encryption, config *tls.Config) (*smtpClient, error) { +func smtpConnect(customConn net.Conn, host, port, helo string, encryption Encryption, config *tls.Config) (*smtpClient, error) { // connect to the mail server - c, err := dial(host, port, encryption, config) + c, err := dial(customConn, host, port, encryption, config) if err != nil { return nil, err @@ -837,7 +843,7 @@ func (server *SMTPServer) Connect() (*SMTPClient, error) { if server.ConnectTimeout != 0 { smtpConnectChannel = make(chan error, 2) go func() { - c, err = smtpConnect(server.Host, fmt.Sprintf("%d", server.Port), server.Helo, server.Encryption, tlsConfig) + c, err = smtpConnect(server.CustomConn, server.Host, fmt.Sprintf("%d", server.Port), server.Helo, server.Encryption, tlsConfig) // send the result smtpConnectChannel <- err }() @@ -852,7 +858,7 @@ func (server *SMTPServer) Connect() (*SMTPClient, error) { } } else { // no ConnectTimeout, just fire the connect - c, err = smtpConnect(server.Host, fmt.Sprintf("%d", server.Port), server.Helo, server.Encryption, tlsConfig) + c, err = smtpConnect(server.CustomConn, server.Host, fmt.Sprintf("%d", server.Port), server.Helo, server.Encryption, tlsConfig) if err != nil { return nil, err }