mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-15 14:56:42 +00:00
Other: Bump go-smtp version to fix race condition
There was a race condition internal to the go-smtp library. In order to fix it, a version bump was necessary. However, this significantly changed the library interface.
This commit is contained in:
@ -64,8 +64,7 @@ type Bridge struct {
|
||||
imapListener net.Listener
|
||||
|
||||
// smtpServer is the bridge's SMTP server.
|
||||
smtpServer *smtp.Server
|
||||
smtpBackend *smtpBackend
|
||||
smtpServer *smtp.Server
|
||||
|
||||
// updater is the bridge's updater.
|
||||
updater Updater
|
||||
@ -131,8 +130,6 @@ func New( //nolint:funlen
|
||||
return nil, nil, fmt.Errorf("failed to get Gluon directory: %w", err)
|
||||
}
|
||||
|
||||
smtpBackend := newSMTPBackend()
|
||||
|
||||
imapServer, err := newIMAPServer(gluonDir, curVersion, tlsConfig, logIMAPClient, logIMAPServer)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create IMAP server: %w", err)
|
||||
@ -159,7 +156,6 @@ func New( //nolint:funlen
|
||||
// Service stuff
|
||||
tlsConfig,
|
||||
imapServer,
|
||||
smtpBackend,
|
||||
focusService,
|
||||
|
||||
// Logging stuff
|
||||
@ -191,11 +187,10 @@ func newBridge(
|
||||
|
||||
tlsConfig *tls.Config,
|
||||
imapServer *gluon.Server,
|
||||
smtpBackend *smtpBackend,
|
||||
focusService *focus.Service,
|
||||
logIMAPClient, logIMAPServer, logSMTP bool,
|
||||
) *Bridge {
|
||||
return &Bridge{
|
||||
bridge := &Bridge{
|
||||
vault: vault,
|
||||
|
||||
users: safe.NewMap[string, *user.User](nil),
|
||||
@ -205,10 +200,8 @@ func newBridge(
|
||||
proxyCtl: proxyCtl,
|
||||
identifier: identifier,
|
||||
|
||||
tlsConfig: tlsConfig,
|
||||
imapServer: imapServer,
|
||||
smtpServer: newSMTPServer(smtpBackend, tlsConfig, logSMTP),
|
||||
smtpBackend: smtpBackend,
|
||||
tlsConfig: tlsConfig,
|
||||
imapServer: imapServer,
|
||||
|
||||
updater: updater,
|
||||
curVersion: curVersion,
|
||||
@ -226,6 +219,10 @@ func newBridge(
|
||||
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
|
||||
bridge.smtpServer = newSMTPServer(&smtpBackend{bridge}, tlsConfig, logSMTP)
|
||||
|
||||
return bridge
|
||||
}
|
||||
|
||||
func (bridge *Bridge) init(tlsReporter TLSReporter) error {
|
||||
|
||||
@ -26,7 +26,6 @@ import (
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/logging"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
||||
"github.com/emersion/go-sasl"
|
||||
"github.com/emersion/go-smtp"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -67,7 +66,7 @@ func (bridge *Bridge) restartSMTP() error {
|
||||
return err
|
||||
}
|
||||
|
||||
bridge.smtpServer = newSMTPServer(bridge.smtpBackend, bridge.tlsConfig, bridge.logSMTP)
|
||||
bridge.smtpServer = newSMTPServer(&smtpBackend{bridge}, bridge.tlsConfig, bridge.logSMTP)
|
||||
|
||||
return bridge.serveSMTP()
|
||||
}
|
||||
@ -99,18 +98,5 @@ func newSMTPServer(smtpBackend *smtpBackend, tlsConfig *tls.Config, shouldLog bo
|
||||
smtpServer.Debug = logging.NewSMTPDebugLogger()
|
||||
}
|
||||
|
||||
smtpServer.EnableAuth(sasl.Login, func(conn *smtp.Conn) sasl.Server {
|
||||
return sasl.NewLoginServer(func(address, password string) error {
|
||||
user, err := conn.Server().Backend.Login(nil, address, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
conn.SetSession(user)
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
return smtpServer
|
||||
}
|
||||
|
||||
@ -18,69 +18,82 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/user"
|
||||
"github.com/emersion/go-smtp"
|
||||
)
|
||||
|
||||
type smtpBackend struct {
|
||||
users map[string]*user.User
|
||||
usersLock sync.RWMutex
|
||||
bridge *Bridge
|
||||
}
|
||||
|
||||
func newSMTPBackend() *smtpBackend {
|
||||
return &smtpBackend{
|
||||
users: make(map[string]*user.User),
|
||||
}
|
||||
type smtpSession struct {
|
||||
bridge *Bridge
|
||||
|
||||
userID string
|
||||
authID string
|
||||
|
||||
from string
|
||||
to []string
|
||||
}
|
||||
|
||||
func (backend *smtpBackend) Login(_ *smtp.ConnectionState, email, password string) (smtp.Session, error) {
|
||||
backend.usersLock.RLock()
|
||||
defer backend.usersLock.RUnlock()
|
||||
func (be *smtpBackend) NewSession(_ *smtp.Conn) (smtp.Session, error) {
|
||||
return &smtpSession{
|
||||
bridge: be.bridge,
|
||||
}, nil
|
||||
}
|
||||
|
||||
for _, user := range backend.users {
|
||||
session, err := user.NewSMTPSession(email, []byte(password))
|
||||
if err != nil {
|
||||
continue
|
||||
func (s *smtpSession) AuthPlain(username, password string) error {
|
||||
return s.bridge.users.ValuesErr(func(users []*user.User) error {
|
||||
for _, user := range users {
|
||||
addrID, err := user.CheckAuth(username, []byte(password))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
s.userID = user.ID()
|
||||
s.authID = addrID
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
return nil, ErrNoSuchUser
|
||||
return fmt.Errorf("invalid username or password")
|
||||
})
|
||||
}
|
||||
|
||||
func (backend *smtpBackend) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, error) {
|
||||
return nil, ErrNotImplemented
|
||||
func (s *smtpSession) Reset() {
|
||||
s.from = ""
|
||||
s.to = nil
|
||||
}
|
||||
|
||||
// addUser adds the given user to the backend.
|
||||
// It returns an error if a user with the same ID already exists.
|
||||
func (backend *smtpBackend) addUser(newUser *user.User) error {
|
||||
backend.usersLock.Lock()
|
||||
defer backend.usersLock.Unlock()
|
||||
func (s *smtpSession) Logout() error {
|
||||
s.Reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, ok := backend.users[newUser.ID()]; ok {
|
||||
return ErrUserAlreadyExists
|
||||
func (s *smtpSession) Mail(from string, opts *smtp.MailOptions) error {
|
||||
s.from = from
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *smtpSession) Rcpt(to string) error {
|
||||
if len(to) > 0 {
|
||||
s.to = append(s.to, to)
|
||||
}
|
||||
|
||||
backend.users[newUser.ID()] = newUser
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeUser removes the given user from the backend.
|
||||
// It returns an error if the user doesn't exist.
|
||||
func (backend *smtpBackend) removeUser(user *user.User) error {
|
||||
backend.usersLock.Lock()
|
||||
defer backend.usersLock.Unlock()
|
||||
|
||||
if _, ok := backend.users[user.ID()]; !ok {
|
||||
return ErrNoSuchUser
|
||||
func (s *smtpSession) Data(r io.Reader) error {
|
||||
if ok, err := s.bridge.users.GetErr(s.userID, func(user *user.User) error {
|
||||
return user.SendMail(s.authID, s.from, s.to, r)
|
||||
}); !ok {
|
||||
return fmt.Errorf("no such user %q", s.userID)
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("failed to send mail: %w", err)
|
||||
}
|
||||
|
||||
delete(backend.users, user.ID())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -401,11 +401,6 @@ func (bridge *Bridge) addUserWithVault(
|
||||
return fmt.Errorf("failed to add IMAP user: %w", err)
|
||||
}
|
||||
|
||||
// Connect the user's address(es) to the SMTP server.
|
||||
if err := bridge.smtpBackend.addUser(user); err != nil {
|
||||
return fmt.Errorf("failed to add user to SMTP backend: %w", err)
|
||||
}
|
||||
|
||||
// Handle events coming from the user before forwarding them to the bridge.
|
||||
// For example, if the user's addresses change, we need to update them in gluon.
|
||||
go func() {
|
||||
@ -497,10 +492,6 @@ func (bridge *Bridge) addIMAPUser(ctx context.Context, user *user.User) error {
|
||||
// logoutUser logs the given user out from bridge.
|
||||
func (bridge *Bridge) logoutUser(ctx context.Context, userID string) error {
|
||||
if ok, err := bridge.users.GetDeleteErr(userID, func(user *user.User) error {
|
||||
if err := bridge.smtpBackend.removeUser(user); err != nil {
|
||||
logrus.WithError(err).Error("Failed to remove user from SMTP backend")
|
||||
}
|
||||
|
||||
for _, gluonID := range user.GetGluonIDs() {
|
||||
if err := bridge.imapServer.RemoveUser(ctx, gluonID, false); err != nil {
|
||||
logrus.WithError(err).Error("Failed to remove IMAP user")
|
||||
@ -528,10 +519,6 @@ func (bridge *Bridge) logoutUser(ctx context.Context, userID string) error {
|
||||
// deleteUser deletes the given user from bridge.
|
||||
func (bridge *Bridge) deleteUser(ctx context.Context, userID string) {
|
||||
if ok := bridge.users.GetDelete(userID, func(user *user.User) {
|
||||
if err := bridge.smtpBackend.removeUser(user); err != nil {
|
||||
logrus.WithError(err).Error("Failed to remove user from SMTP backend")
|
||||
}
|
||||
|
||||
for _, gluonID := range user.GetGluonIDs() {
|
||||
if err := bridge.imapServer.RemoveUser(ctx, gluonID, true); err != nil {
|
||||
logrus.WithError(err).Error("Failed to remove IMAP user")
|
||||
|
||||
Reference in New Issue
Block a user