3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-25 13:29:27 +01:00

make ReloadableListener lock-free

Also stop attaching the *tls.Config to the wrapped connection,
since this forces it to be retained beyond its natural lifetime.
This commit is contained in:
Shivaram Lingamneni 2023-01-05 20:18:14 -05:00
parent bceae9b739
commit 3ceff6a8b1
3 changed files with 29 additions and 27 deletions

View File

@ -295,7 +295,7 @@ func (server *Server) RunClient(conn IRCConn) {
var banMsg string var banMsg string
realIP := utils.AddrToIP(wConn.RemoteAddr()) realIP := utils.AddrToIP(wConn.RemoteAddr())
var proxiedIP net.IP var proxiedIP net.IP
if wConn.Config.Tor { if wConn.Tor {
// cover up details of the tor proxying infrastructure (not a user privacy concern, // cover up details of the tor proxying infrastructure (not a user privacy concern,
// but a hardening measure): // but a hardening measure):
proxiedIP = utils.IPv4LoopbackAddress proxiedIP = utils.IPv4LoopbackAddress
@ -329,7 +329,7 @@ func (server *Server) RunClient(conn IRCConn) {
lastActive: now, lastActive: now,
channels: make(ChannelSet), channels: make(ChannelSet),
ctime: now, ctime: now,
isSTSOnly: wConn.Config.STSOnly, isSTSOnly: wConn.STSOnly,
languages: server.Languages().Default(), languages: server.Languages().Default(),
loginThrottle: connection_limits.GenericThrottle{ loginThrottle: connection_limits.GenericThrottle{
Duration: config.Accounts.LoginThrottling.Duration, Duration: config.Accounts.LoginThrottling.Duration,
@ -358,8 +358,8 @@ func (server *Server) RunClient(conn IRCConn) {
lastActive: now, lastActive: now,
realIP: realIP, realIP: realIP,
proxiedIP: proxiedIP, proxiedIP: proxiedIP,
isTor: wConn.Config.Tor, isTor: wConn.Tor,
hideSTS: wConn.Config.Tor || wConn.Config.HideSTS, hideSTS: wConn.Tor || wConn.HideSTS,
} }
client.sessions = []*Session{session} client.sessions = []*Session{session}
@ -369,7 +369,7 @@ func (server *Server) RunClient(conn IRCConn) {
client.SetMode(modes.TLS, true) client.SetMode(modes.TLS, true)
} }
if wConn.Config.TLSConfig != nil { if wConn.TLS {
// error is not useful to us here anyways so we can ignore it // error is not useful to us here anyways so we can ignore it
session.certfp, session.peerCerts, _ = utils.GetCertFP(wConn.Conn, RegisterTimeout) session.certfp, session.peerCerts, _ = utils.GetCertFP(wConn.Conn, RegisterTimeout)
} }

View File

@ -204,10 +204,10 @@ func confirmProxyData(conn *utils.WrappedConn, remoteAddr, xForwardedFor, xForwa
} }
} }
if conn.Config.TLSConfig != nil || conn.Config.Tor { if conn.TLS || conn.Tor {
// we terminated our own encryption: // we terminated our own encryption:
conn.Secure = true conn.Secure = true
} else if !conn.Config.WebSocket { } else if !conn.WebSocket {
// plaintext normal connection: loopback and secureNets are secure // plaintext normal connection: loopback and secureNets are secure
realIP := utils.AddrToIP(conn.RemoteAddr()) realIP := utils.AddrToIP(conn.RemoteAddr())
conn.Secure = realIP.IsLoopback() || utils.IPInNets(realIP, config.Server.secureNets) conn.Secure = realIP.IsLoopback() || utils.IPInNets(realIP, config.Server.secureNets)

View File

@ -9,7 +9,7 @@ import (
"io" "io"
"net" "net"
"strings" "strings"
"sync" "sync/atomic"
"time" "time"
) )
@ -209,7 +209,11 @@ func parseProxyLineV2(line []byte) (ip net.IP, err error) {
type WrappedConn struct { type WrappedConn struct {
net.Conn net.Conn
ProxiedIP net.IP ProxiedIP net.IP
Config ListenerConfig TLS bool
Tor bool
STSOnly bool
WebSocket bool
HideSTS bool
// Secure indicates whether we believe the connection between us and the client // Secure indicates whether we believe the connection between us and the client
// was secure against interception and modification (including all proxies): // was secure against interception and modification (including all proxies):
Secure bool Secure bool
@ -218,35 +222,30 @@ type WrappedConn struct {
// ReloadableListener is a wrapper for net.Listener that allows reloading // ReloadableListener is a wrapper for net.Listener that allows reloading
// of config data for postprocessing connections (TLS, PROXY protocol, etc.) // of config data for postprocessing connections (TLS, PROXY protocol, etc.)
type ReloadableListener struct { type ReloadableListener struct {
// TODO: make this lock-free
sync.Mutex
realListener net.Listener realListener net.Listener
config ListenerConfig // nil means the listener is closed:
isClosed bool config atomic.Pointer[ListenerConfig]
} }
func NewReloadableListener(realListener net.Listener, config ListenerConfig) *ReloadableListener { func NewReloadableListener(realListener net.Listener, config ListenerConfig) *ReloadableListener {
return &ReloadableListener{ result := &ReloadableListener{
realListener: realListener, realListener: realListener,
config: config,
} }
result.config.Store(&config) // heap escape
return result
} }
func (rl *ReloadableListener) Reload(config ListenerConfig) { func (rl *ReloadableListener) Reload(config ListenerConfig) {
rl.Lock() rl.config.Store(&config)
rl.config = config
rl.Unlock()
} }
func (rl *ReloadableListener) Accept() (conn net.Conn, err error) { func (rl *ReloadableListener) Accept() (conn net.Conn, err error) {
conn, err = rl.realListener.Accept() conn, err = rl.realListener.Accept()
rl.Lock() config := rl.config.Load()
config := rl.config
isClosed := rl.isClosed
rl.Unlock()
if isClosed { if config == nil {
// Close() was called
if err == nil { if err == nil {
conn.Close() conn.Close()
} }
@ -279,14 +278,17 @@ func (rl *ReloadableListener) Accept() (conn net.Conn, err error) {
return &WrappedConn{ return &WrappedConn{
Conn: conn, Conn: conn,
ProxiedIP: proxiedIP, ProxiedIP: proxiedIP,
Config: config, TLS: config.TLSConfig != nil,
Tor: config.Tor,
STSOnly: config.STSOnly,
WebSocket: config.WebSocket,
HideSTS: config.HideSTS,
// Secure will be set later by client code
}, nil }, nil
} }
func (rl *ReloadableListener) Close() error { func (rl *ReloadableListener) Close() error {
rl.Lock() rl.config.Store(nil)
rl.isClosed = true
rl.Unlock()
return rl.realListener.Close() return rl.realListener.Close()
} }