From d9fb6a37885bfdf2163d66c72c989bd0c88d8210 Mon Sep 17 00:00:00 2001 From: cyBerta <cyberta@riseup.net> Date: Mon, 24 Feb 2025 12:57:17 +0100 Subject: [PATCH] fix nilpointer dereference in server hopping mode, in case ports obvpn wants to listen to are already bound. Allow 10% failure rate for failing port bindings for now, clients should hop to next IP:port tuple if dialing fails. --- server/udpserver.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/server/udpserver.go b/server/udpserver.go index 70de25b..af7d368 100644 --- a/server/udpserver.go +++ b/server/udpserver.go @@ -55,14 +55,28 @@ func (s *UDPServer) Start() error { if s.cfg.HoppingEnabled { listeners = make([]net.Listener, s.cfg.PortCount) portHopRange := int(s.cfg.MaxHopPort - s.cfg.MinHopPort) + listenerCount := 0 for i := 0; i < int(s.cfg.PortCount); i++ { portOffset := r.Intn(portHopRange) addr := net.JoinHostPort(s.cfg.Obfs4ListenIP, fmt.Sprint(portOffset+int(s.cfg.MinHopPort))) - listeners[i], err = listenConfig.Listen(s.ctx, addr) + listener, err := listenConfig.Listen(s.ctx, addr) if err != nil { s.logger.Printf("Error binding to %s: %v", addr, err) + continue } + listeners[listenerCount] = listener + listenerCount++ + } + + if listenerCount < int(float64(s.cfg.PortCount)*0.9) { + // if too many of the required ports are already occupied, clients will suffer from + // bad throughput due to many dialing errors + return fmt.Errorf("too many port binding errors: %d/%d", len(listeners), int(s.cfg.PortCount)) + } else if listenerCount < int(s.cfg.PortCount) { + // adjust the listener slice size so that we avoid nil entries + listeners = listeners[:listenerCount] + s.debug.Printf("WARNING: listening on less ports then clients expect: %d / %d", len(listeners), int(s.cfg.PortCount)) } } else { listenAddr := net.JoinHostPort(s.cfg.Obfs4ListenIP, strconv.Itoa(s.cfg.Obfs4ListenPort)) -- GitLab