diff --git a/lib/upnp/upnp.go b/lib/upnp/upnp.go index 85854102c..3bee77fc1 100644 --- a/lib/upnp/upnp.go +++ b/lib/upnp/upnp.go @@ -160,12 +160,6 @@ USER-AGENT: syncthing/1.0 } defer socket.Close() // Make sure our socket gets closed - err = socket.SetDeadline(time.Now().Add(timeout)) - if err != nil { - l.Debugln("UPnP discovery: setting socket deadline:", err) - return - } - l.Debugln("Sending search request for device type", deviceType, "on", intf.Name) _, err = socket.WriteTo(search, ssdp) @@ -178,16 +172,33 @@ USER-AGENT: syncthing/1.0 l.Debugln("Listening for UPnP response for device type", deviceType, "on", intf.Name) - // Listen for responses until a timeout is reached + ctx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + // Listen for responses until a timeout is reached or the context is + // cancelled + resp := make([]byte, 65536) +loop: for { - resp := make([]byte, 65536) - n, _, err := socket.ReadFrom(resp) - if err != nil { - if e, ok := err.(net.Error); !ok || !e.Timeout() { - l.Infoln("UPnP read:", err) //legitimate error, not a timeout. - } + if err := socket.SetDeadline(time.Now().Add(250 * time.Millisecond)); err != nil { + l.Infoln("UPnP socket:", err) break } + + n, _, err := socket.ReadFrom(resp) + if err != nil { + select { + case <-ctx.Done(): + break loop + default: + } + if e, ok := err.(net.Error); ok && e.Timeout() { + continue // continue reading + } + l.Infoln("UPnP read:", err) //legitimate error, not a timeout. + break + } + igds, err := parseResponse(ctx, deviceType, resp[:n]) if err != nil { switch err.(type) {