@@ -5,6 +5,9 @@ package portmapper
55
66import (
77 "context"
8+ "encoding/binary"
9+ "net"
10+ "net/netip"
811 "os"
912 "reflect"
1013 "strconv"
@@ -122,3 +125,67 @@ func TestPCPIntegration(t *testing.T) {
122125 t .Errorf ("got nil mapping after successful createOrGetMapping" )
123126 }
124127}
128+
129+ func TestSetMappingLockedReleasesAfterClose (t * testing.T ) {
130+ t .Parallel ()
131+ for _ , tt := range []struct {
132+ name string
133+ version byte
134+ pktLen , lifetimeOff , internalOff , externalOff int
135+ }{
136+ {"PMP" , pmpVersion , 12 , 8 , 4 , 6 },
137+ {"PCP" , pcpVersion , 60 , 4 , 40 , 42 },
138+ } {
139+ t .Run (tt .name , func (t * testing.T ) {
140+ t .Parallel ()
141+ pc , err := net .ListenPacket ("udp4" , "127.0.0.1:0" )
142+ if err != nil {
143+ t .Fatal (err )
144+ }
145+ defer pc .Close ()
146+ c := NewClient (t .Logf , nil , nil , nil )
147+ c .testPxPPort = uint16 (pc .LocalAddr ().(* net.UDPAddr ).Port )
148+ lo := netip .MustParseAddr ("127.0.0.1" )
149+ gw , internal := netip .AddrPortFrom (lo , c .testPxPPort ), netip .AddrPortFrom (lo , 41641 )
150+ newMapping := func (port uint16 ) mapping {
151+ ext := netip .AddrPortFrom (lo , port )
152+ if tt .version == pmpVersion {
153+ return & pmpMapping {c : c , gw : gw , internal : internal , external : ext }
154+ }
155+ return & pcpMapping {c : c , gw : gw , internal : internal , external : ext }
156+ }
157+
158+ before := newMapping (4241 )
159+ c .mu .Lock ()
160+ installed := c .setMappingLocked (before )
161+ retained := c .mapping
162+ c .mu .Unlock ()
163+ if ! installed || retained != before {
164+ t .Fatalf ("setMappingLocked before Close = %v, retained %v" , installed , retained )
165+ }
166+ c .Close ()
167+ c .mu .Lock ()
168+ installed = c .setMappingLocked (newMapping (4242 ))
169+ retained = c .mapping
170+ c .mu .Unlock ()
171+ if installed || retained != nil {
172+ t .Errorf ("setMappingLocked after Close = %v, retained %v; want false, nil" , installed , retained )
173+ }
174+
175+ pc .SetReadDeadline (time .Now ().Add (10 * time .Second ))
176+ buf := make ([]byte , 1500 )
177+ for _ , port := range []uint16 {4241 , 4242 } {
178+ n , _ , err := pc .ReadFrom (buf )
179+ if err != nil {
180+ t .Fatalf ("reading delete for port %d: %v" , port , err )
181+ }
182+ if pkt := buf [:n ]; n != tt .pktLen || pkt [0 ] != tt .version ||
183+ binary .BigEndian .Uint32 (pkt [tt .lifetimeOff :]) != 0 ||
184+ binary .BigEndian .Uint16 (pkt [tt .internalOff :]) != internal .Port () ||
185+ binary .BigEndian .Uint16 (pkt [tt .externalOff :]) != port {
186+ t .Errorf ("got % x; want %s delete for external port %d" , pkt , tt .name , port )
187+ }
188+ }
189+ })
190+ }
191+ }
0 commit comments