Browse Source

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.
tags/v2.12.0-rc1
Shivaram Lingamneni 1 year ago
parent
commit
3ceff6a8b1
3 changed files with 29 additions and 27 deletions
  1. 5
    5
      irc/client.go
  2. 2
    2
      irc/listeners.go
  3. 22
    20
      irc/utils/proxy.go

+ 5
- 5
irc/client.go View File

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

+ 2
- 2
irc/listeners.go View File

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

+ 22
- 20
irc/utils/proxy.go View File

@@ -9,7 +9,7 @@ import (
9 9
 	"io"
10 10
 	"net"
11 11
 	"strings"
12
-	"sync"
12
+	"sync/atomic"
13 13
 	"time"
14 14
 )
15 15
 
@@ -209,7 +209,11 @@ func parseProxyLineV2(line []byte) (ip net.IP, err error) {
209 209
 type WrappedConn struct {
210 210
 	net.Conn
211 211
 	ProxiedIP net.IP
212
-	Config    ListenerConfig
212
+	TLS       bool
213
+	Tor       bool
214
+	STSOnly   bool
215
+	WebSocket bool
216
+	HideSTS   bool
213 217
 	// Secure indicates whether we believe the connection between us and the client
214 218
 	// was secure against interception and modification (including all proxies):
215 219
 	Secure bool
@@ -218,35 +222,30 @@ type WrappedConn struct {
218 222
 // ReloadableListener is a wrapper for net.Listener that allows reloading
219 223
 // of config data for postprocessing connections (TLS, PROXY protocol, etc.)
220 224
 type ReloadableListener struct {
221
-	// TODO: make this lock-free
222
-	sync.Mutex
223 225
 	realListener net.Listener
224
-	config       ListenerConfig
225
-	isClosed     bool
226
+	// nil means the listener is closed:
227
+	config atomic.Pointer[ListenerConfig]
226 228
 }
227 229
 
228 230
 func NewReloadableListener(realListener net.Listener, config ListenerConfig) *ReloadableListener {
229
-	return &ReloadableListener{
231
+	result := &ReloadableListener{
230 232
 		realListener: realListener,
231
-		config:       config,
232 233
 	}
234
+	result.config.Store(&config) // heap escape
235
+	return result
233 236
 }
234 237
 
235 238
 func (rl *ReloadableListener) Reload(config ListenerConfig) {
236
-	rl.Lock()
237
-	rl.config = config
238
-	rl.Unlock()
239
+	rl.config.Store(&config)
239 240
 }
240 241
 
241 242
 func (rl *ReloadableListener) Accept() (conn net.Conn, err error) {
242 243
 	conn, err = rl.realListener.Accept()
243 244
 
244
-	rl.Lock()
245
-	config := rl.config
246
-	isClosed := rl.isClosed
247
-	rl.Unlock()
245
+	config := rl.config.Load()
248 246
 
249
-	if isClosed {
247
+	if config == nil {
248
+		// Close() was called
250 249
 		if err == nil {
251 250
 			conn.Close()
252 251
 		}
@@ -279,14 +278,17 @@ func (rl *ReloadableListener) Accept() (conn net.Conn, err error) {
279 278
 	return &WrappedConn{
280 279
 		Conn:      conn,
281 280
 		ProxiedIP: proxiedIP,
282
-		Config:    config,
281
+		TLS:       config.TLSConfig != nil,
282
+		Tor:       config.Tor,
283
+		STSOnly:   config.STSOnly,
284
+		WebSocket: config.WebSocket,
285
+		HideSTS:   config.HideSTS,
286
+		// Secure will be set later by client code
283 287
 	}, nil
284 288
 }
285 289
 
286 290
 func (rl *ReloadableListener) Close() error {
287
-	rl.Lock()
288
-	rl.isClosed = true
289
-	rl.Unlock()
291
+	rl.config.Store(nil)
290 292
 
291 293
 	return rl.realListener.Close()
292 294
 }

Loading…
Cancel
Save