From 8fcbd0a796ef939d11a4d752b68ce99f1fefae7d Mon Sep 17 00:00:00 2001 From: suMin Date: Tue, 23 Jun 2026 04:14:04 +0900 Subject: [PATCH] webrtc: skip unresolvable webrtcAdditionalHosts entries instead of aborting (#5845) Since #4866, hostnames in webrtcAdditionalHosts are resolved server-side via net.LookupIP, and a resolution failure aborts the entire WHEP/WHIP session. A single entry that can't be resolved on the server (e.g. air-gapped networks without DNS, or a name that only resolves on the client) therefore takes down all WebRTC playback, even when the other entries are valid. Log a warning and skip the unresolvable entry instead of returning an error, so the remaining valid candidates are still offered. Signed-off-by: suMin --- internal/protocols/webrtc/peer_connection.go | 7 ++- .../protocols/webrtc/peer_connection_test.go | 47 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/internal/protocols/webrtc/peer_connection.go b/internal/protocols/webrtc/peer_connection.go index 98ea2bda..e98fda04 100644 --- a/internal/protocols/webrtc/peer_connection.go +++ b/internal/protocols/webrtc/peer_connection.go @@ -504,7 +504,12 @@ func (co *PeerConnection) addAdditionalCandidates(firstMedia *sdp.MediaDescripti } else { tmp, err := net.LookupIP(host) if err != nil { - return err + // The host can't be resolved server-side - e.g. in air-gapped + // networks without DNS, or with split-horizon / overlay DNS + // names that only resolve on the client. Skip it instead of + // failing the entire session, so the other entries still work. + co.Log.Log(logger.Warn, "cannot resolve additional host %q, skipping it: %v", host, err) + continue } ips = make([]string, len(tmp)) diff --git a/internal/protocols/webrtc/peer_connection_test.go b/internal/protocols/webrtc/peer_connection_test.go index 713b229b..47f29bc6 100644 --- a/internal/protocols/webrtc/peer_connection_test.go +++ b/internal/protocols/webrtc/peer_connection_test.go @@ -886,3 +886,50 @@ func TestPeerConnectionPublishDataChannel(t *testing.T) { <-dataReceived } + +func TestPeerConnectionAdditionalHostsUnresolvable(t *testing.T) { + // A host in AdditionalHosts that can't be resolved server-side - for + // instance air-gapped networks without DNS, or split-horizon / overlay + // DNS names that only resolve on the client - must be skipped instead of + // aborting the session; the other (valid) entries must still produce + // candidates. + clientPC := &PeerConnection{ + LocalRandomUDP: true, + IPsFromInterfaces: true, + IPsFromInterfacesList: []string{"lo"}, + Log: test.NilLogger, + } + err := clientPC.Start() + require.NoError(t, err) + defer clientPC.Close() + + ln, err := net.ListenPacket("udp4", ":0") + require.NoError(t, err) + defer ln.Close() + udpMux := webrtc.NewICEUDPMux(webrtcNilLogger, ln) + + serverPC := &PeerConnection{ + ICEUDPMux: udpMux, + AdditionalHosts: []string{"127.0.0.1", "unresolvable.invalid"}, + Publish: true, + OutboundTracks: []*OutboundTrack{{ + Caps: webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeAV1, + ClockRate: 90000, + }, + }}, + Log: test.NilLogger, + } + err = serverPC.Start() + require.NoError(t, err) + defer serverPC.Close() + + offer, err := clientPC.CreatePartialOffer(false) + require.NoError(t, err) + + answer, err := serverPC.CreateFullAnswer(offer, false) + require.NoError(t, err) + + require.Contains(t, answer.SDP, "127.0.0.1") + require.NotContains(t, answer.SDP, "unresolvable.invalid") +}