Skip to content

Commit

Permalink
use custom conn
Browse files Browse the repository at this point in the history
Fix #79, #24
  • Loading branch information
xhit committed Jul 7, 2023
1 parent 6b425f7 commit 457c8da
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 20 deletions.
74 changes: 73 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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"
)

Expand Down Expand Up @@ -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 <nube@example.com>").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).
44 changes: 25 additions & 19 deletions email.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand All @@ -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
Expand Down Expand Up @@ -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
}()
Expand All @@ -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
}
Expand Down

0 comments on commit 457c8da

Please sign in to comment.