Feature: Experimental Unix socket support for HTTPD & SMTPD (#373)

This commit is contained in:
Ralph Slooten
2024-10-24 23:12:34 +13:00
parent e2c3256f0c
commit 31ec6681a7
16 changed files with 1324 additions and 177 deletions

View File

@@ -18,15 +18,15 @@ import (
"fmt"
"io"
"net/mail"
"net/smtp"
"os"
"os/user"
"path"
"regexp"
"strings"
"github.com/axllent/mailpit/config"
"github.com/axllent/mailpit/internal/logger"
"github.com/reiver/go-telnet"
"github.com/mneis/go-telnet"
flag "github.com/spf13/pflag"
)
@@ -113,14 +113,23 @@ func Run() {
os.Exit(1)
}
socketAddr, isSocket := socketAddress(SMTPAddr)
// handles `sendmail -bs`
// telnet directly to SMTP
if UseB && UseS {
var caller telnet.Caller = telnet.StandardCaller
// telnet directly to SMTP
if err := telnet.DialToAndCall(SMTPAddr, caller); err != nil {
fmt.Println(err)
os.Exit(1)
if isSocket {
if err := telnet.DialToAndCallUnix(socketAddr, caller); err != nil {
fmt.Println(err)
os.Exit(1)
}
} else {
if err := telnet.DialToAndCall(SMTPAddr, caller); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
return
@@ -167,8 +176,7 @@ func Run() {
os.Exit(11)
}
err = smtp.SendMail(SMTPAddr, nil, from.Address, addresses, body)
if err != nil {
if err := Send(SMTPAddr, from.Address, addresses, body); err != nil {
fmt.Fprintln(os.Stderr, "error sending mail")
logger.Log().Fatal(err)
}
@@ -192,3 +200,17 @@ Flags:
-v Ignored
`, config.Version, strings.Join(args, " "), FromAddr)
}
// SocketAddress returns a path and a FileMode if the address is in
// the format of unix:<path>
func socketAddress(address string) (string, bool) {
re := regexp.MustCompile(`^unix:(.*)$`)
if !re.MatchString(address) {
return "", false
}
m := re.FindAllStringSubmatch(address, 1)
return path.Clean(m[0][1]), true
}

71
sendmail/cmd/smtp.go Normal file
View File

@@ -0,0 +1,71 @@
// Package cmd is a wrapper library to send mail
package cmd
import (
"fmt"
"net"
"net/mail"
"net/smtp"
"os"
"github.com/axllent/mailpit/internal/logger"
)
// Send is a wrapper for smtp.SendMail() which also supports sending via unix sockets.
// Unix sockets must be set as unix:/path/to/socket
// It does not support authentication.
func Send(addr string, from string, to []string, msg []byte) error {
socketPath, isSocket := socketAddress(addr)
fromAddress, err := mail.ParseAddress(from)
if err != nil {
return fmt.Errorf("invalid from address: %s", from)
}
if len(to) == 0 {
return fmt.Errorf("no To addresses specified")
}
if !isSocket {
return smtp.SendMail(addr, nil, fromAddress.Address, to, msg)
}
conn, err := net.Dial("unix", socketPath)
if err != nil {
return fmt.Errorf("error connecting to %s", addr)
}
client, err := smtp.NewClient(conn, "")
if err != nil {
return err
}
// Set the sender
if err := client.Mail(fromAddress.Address); err != nil {
fmt.Fprintln(os.Stderr, "error sending mail")
logger.Log().Fatal(err)
}
// Set the recipient
for _, a := range to {
if err := client.Rcpt(a); err != nil {
return err
}
}
wc, err := client.Data()
if err != nil {
return err
}
_, err = wc.Write(msg)
if err != nil {
return err
}
err = wc.Close()
if err != nil {
return err
}
return nil
}