@@ -51,6 +51,11 @@ type ClientConfig struct {
5151 // PermissionTimeout sets the refresh interval of permissions. Defaults to 2 minutes.
5252 PermissionRefreshInterval time.Duration
5353
54+ // RequestedAddressFamily is the address family to request in allocations (IPv4 or IPv6).
55+ // If not specified (zero value), the client will attempt to infer from the PacketConn's
56+ // local address, falling back to IPv4 if inference fails. See RFC 6156.
57+ RequestedAddressFamily RequestedAddressFamily
58+
5459 evenPort bool // If EVEN-PORT Attribute should be sent in Allocation
5560 reservationToken []byte // If Server responds with RESERVATION-TOKEN or if Client wishes to send one
5661 bindingRefreshInterval time.Duration
@@ -79,16 +84,76 @@ type Client struct {
7984 mutexTrMap sync.Mutex // Thread-safe
8085 log logging.LeveledLogger // Read-only
8186
82- evenPort bool // If EVEN-PORT Attribute should be sent in Allocation
83- reservationToken []byte // If Server responds with RESERVATION-TOKEN or if Client wishes to send one
87+ // If EVEN-PORT Attribute should be sent in Allocation
88+ evenPort bool
89+
90+ // If Server responds with RESERVATION-TOKEN or if Client wishes to send one
91+ reservationToken []byte
92+
93+ // REQUESTED-ADDRESS-FAMILY attribute for allocations (RFC 6156)
94+ requestedAddressFamily proto.RequestedAddressFamily
95+
8496 permissionRefreshInterval time.Duration
8597 bindingRefreshInterval time.Duration
8698 bindingCheckInterval time.Duration
8799}
88100
101+ // inferAddressFamilyFromConn attempts to determine the address
102+ // family (IPv4 or IPv6) from a PacketConn's local address.
103+ // Returns an error if the address type is not IP-based.
104+ func inferAddressFamilyFromConn (
105+ conn net.PacketConn ,
106+ ) (proto.RequestedAddressFamily , error ) {
107+ addr := conn .LocalAddr ()
108+
109+ switch a := addr .(type ) {
110+ case * net.UDPAddr :
111+ if a .IP .To4 () != nil {
112+ return proto .RequestedFamilyIPv4 , nil
113+ }
114+
115+ return proto .RequestedFamilyIPv6 , nil
116+ case * net.TCPAddr :
117+ if a .IP .To4 () != nil {
118+ return proto .RequestedFamilyIPv4 , nil
119+ }
120+
121+ return proto .RequestedFamilyIPv6 , nil
122+ default :
123+ return 0 , fmt .Errorf ("cannot infer address family from %T" , addr ) //nolint:err113
124+ }
125+ }
126+
127+ // getRequestedAddressFamily determines the address family to use
128+ // for TURN allocations. It follows this priority:
129+ // 1. Use explicitly configured RequestedAddressFamily if set
130+ // 2. Try to infer from the PacketConn's local address
131+ // 3. Fall back to IPv4 default per RFC 6156
132+ func getRequestedAddressFamily (
133+ log logging.LeveledLogger ,
134+ config * ClientConfig ,
135+ ) proto.RequestedAddressFamily {
136+ // If explicitly set, use it
137+ if config .RequestedAddressFamily != 0 {
138+ return config .RequestedAddressFamily
139+ }
140+
141+ // Try to infer from the PacketConn
142+ if inferred , err := inferAddressFamilyFromConn (config .Conn ); err == nil {
143+ log .Debugf ("Inferred address family %v from connection" , inferred )
144+
145+ return inferred
146+ }
147+
148+ log .Debugf ("Could not infer address family, defaulting to IPv4" )
149+
150+ // Default to IPv4 per RFC 6156
151+ return proto .RequestedFamilyIPv4
152+ }
153+
89154// NewClient returns a new Client instance. listeningAddress is the address and port to listen on,
90155// default "0.0.0.0:0".
91- func NewClient (config * ClientConfig ) (* Client , error ) {
156+ func NewClient (config * ClientConfig ) (* Client , error ) { //nolint:gocyclo,cyclop
92157 loggerFactory := config .LoggerFactory
93158 if loggerFactory == nil {
94159 loggerFactory = logging .NewDefaultLoggerFactory ()
@@ -113,11 +178,14 @@ func NewClient(config *ClientConfig) (*Client, error) {
113178 config .Net = n
114179 }
115180
181+ // Determine the requested address family (RFC 6156)
182+ requestedAddressFamily := getRequestedAddressFamily (log , config )
183+
116184 var stunServ , turnServ net.Addr
117185 var err error
118186
119187 if len (config .STUNServerAddr ) > 0 {
120- stunServ , err = config .Net .ResolveUDPAddr ("udp4 " , config .STUNServerAddr )
188+ stunServ , err = config .Net .ResolveUDPAddr ("udp " , config .STUNServerAddr )
121189 if err != nil {
122190 return nil , err
123191 }
@@ -126,7 +194,7 @@ func NewClient(config *ClientConfig) (*Client, error) {
126194 }
127195
128196 if len (config .TURNServerAddr ) > 0 {
129- turnServ , err = config .Net .ResolveUDPAddr ("udp4 " , config .TURNServerAddr )
197+ turnServ , err = config .Net .ResolveUDPAddr ("udp " , config .TURNServerAddr )
130198 if err != nil {
131199 return nil , err
132200 }
@@ -148,6 +216,7 @@ func NewClient(config *ClientConfig) (*Client, error) {
148216 log : log ,
149217 evenPort : config .evenPort ,
150218 reservationToken : config .reservationToken ,
219+ requestedAddressFamily : requestedAddressFamily ,
151220 permissionRefreshInterval : config .PermissionRefreshInterval ,
152221 bindingRefreshInterval : config .bindingRefreshInterval ,
153222 bindingCheckInterval : config .bindingCheckInterval ,
@@ -272,11 +341,14 @@ func (c *Client) sendAllocateRequest(protocol proto.Protocol) ( //nolint:cyclop
272341 proto.RequestedTransport {Protocol : protocol },
273342 stun .Fingerprint ,
274343 }
275- if c .evenPort {
276- allocationSetters = append (allocationSetters , proto.EvenPort {ReservePort : true })
277- }
344+ // RFC 6156: REQUESTED-ADDRESS-FAMILY and RESERVATION-TOKEN are mutually exclusive.
278345 if len (c .reservationToken ) != 0 {
279346 allocationSetters = append (allocationSetters , proto .ReservationToken (c .reservationToken ))
347+ } else {
348+ allocationSetters = append (allocationSetters , c .requestedAddressFamily )
349+ }
350+ if c .evenPort {
351+ allocationSetters = append (allocationSetters , proto.EvenPort {ReservePort : true })
280352 }
281353
282354 msg , err := stun .Build (allocationSetters ... )
@@ -313,11 +385,14 @@ func (c *Client) sendAllocateRequest(protocol proto.Protocol) ( //nolint:cyclop
313385 & c .integrity ,
314386 stun .Fingerprint ,
315387 }
316- if c .evenPort {
317- allocationSetters = append (allocationSetters , proto.EvenPort {ReservePort : true })
318- }
388+ // RFC 6156: REQUESTED-ADDRESS-FAMILY and RESERVATION-TOKEN are mutually exclusive.
319389 if len (c .reservationToken ) != 0 {
320390 allocationSetters = append (allocationSetters , proto .ReservationToken (c .reservationToken ))
391+ } else {
392+ allocationSetters = append (allocationSetters , c .requestedAddressFamily )
393+ }
394+ if c .evenPort {
395+ allocationSetters = append (allocationSetters , proto.EvenPort {ReservePort : true })
321396 }
322397
323398 msg , err = stun .Build (allocationSetters ... )
0 commit comments