1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2026-04-08 14:55:28 -04:00
Files
Xiaokang Wang (Shelikhoo) 1f4dc3b375 Stun Nat Type Testing (#3646)
* Add NAT Type Testing utility

* Add dualstack udp support for wireguard outbound

* tidy go.mod

* Do not include stun command on android by default

* fix lints

* add additional test when a secondary server was provided
2026-03-10 17:30:21 +00:00

146 lines
4.5 KiB
Go

package gvisorstack
import (
"context"
"fmt"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
"gvisor.dev/gvisor/pkg/tcpip/network/ipv6"
"github.com/v2fly/v2ray-core/v5/common/dualStack/fusedPacketConn"
"github.com/v2fly/v2ray-core/v5/common/net"
)
// DialTCP will create a connection to the given destination, using the stack.
// Machine Generated
func (w *WrappedStack) DialTCP(ctx context.Context, remoteAddress net.Destination) (net.Conn, error) {
if w == nil || w.stack == nil {
return nil, fmt.Errorf("gvisor stack not initialized")
}
if remoteAddress.Network != net.Network_TCP {
return nil, fmt.Errorf("destination is not tcp: %v", remoteAddress.Network)
}
// Resolve address to IP if necessary.
var ipBytes []byte
switch remoteAddress.Address.Family() {
case net.AddressFamilyIPv4:
ipBytes = remoteAddress.Address.IP().To4()
case net.AddressFamilyIPv6:
ipBytes = remoteAddress.Address.IP().To16()
case net.AddressFamilyDomain:
// Do not resolve domain names here. Return explicit error.
return nil, fmt.Errorf("domain address not supported for gVisor dial: %s", remoteAddress.Address.String())
default:
return nil, fmt.Errorf("unsupported address family: %v", remoteAddress.Address.Family())
}
if ipBytes == nil {
return nil, fmt.Errorf("failed to obtain IP bytes for %v", remoteAddress.Address)
}
// Choose network protocol number based on IP length.
netProto := ipv4.ProtocolNumber
if len(ipBytes) == 16 {
netProto = ipv6.ProtocolNumber
}
remote := tcpip.FullAddress{
Addr: tcpip.AddrFromSlice(ipBytes),
Port: uint16(remoteAddress.Port),
}
// Use gonet dialer to create a TCP connection on the in-memory stack.
conn, err := gonet.DialContextTCP(ctx, w.stack, remote, netProto)
if err != nil {
return nil, err
}
return conn, nil
}
// ListenUDP will create a connection to the given destination, using the stack.
// Machine Generated
func (w *WrappedStack) ListenUDP(ctx context.Context, localAddress net.Destination) (net.PacketConn, error) {
// allow ctx to be accepted by the function signature
_ = ctx
if w == nil || w.stack == nil {
return nil, fmt.Errorf("gvisor stack not initialized")
}
if localAddress.Network != net.Network_UDP {
return nil, fmt.Errorf("destination is not udp: %v", localAddress.Network)
}
// Determine local address bytes.
var ipBytes []byte
specified := false
if localAddress.Address == nil {
// If address is nil, treat as unspecified (zero) address.
specified = false
} else {
switch localAddress.Address.Family() {
case net.AddressFamilyIPv4:
specified = true
ipBytes = localAddress.Address.IP().To4()
case net.AddressFamilyIPv6:
specified = true
ipBytes = localAddress.Address.IP().To16()
case net.AddressFamilyDomain:
// Listening on a domain name is not supported.
return nil, fmt.Errorf("listening on domain address not supported: %s", localAddress.Address.String())
default:
// If unspecified (zero) address, allow kernel (stack) to choose.
specified = false
}
}
var laddr *tcpip.FullAddress
if specified {
if ipBytes == nil {
return nil, fmt.Errorf("failed to obtain IP bytes for %v", localAddress.Address)
}
netProto := ipv4.ProtocolNumber
if len(ipBytes) == 16 {
netProto = ipv6.ProtocolNumber
}
l := tcpip.FullAddress{Addr: tcpip.AddrFromSlice(ipBytes), Port: uint16(localAddress.Port)}
laddr = &l
// Create UDP endpoint bound to local address.
udpConn, err := gonet.DialUDP(w.stack, laddr, nil, netProto)
if err != nil {
return nil, err
}
return udpConn, nil
}
if w.config.DualStackUdp {
udpConn4, err := gonet.DialUDP(w.stack, nil, nil, ipv4.ProtocolNumber)
if err != nil {
return nil, fmt.Errorf("failed to create IPv4 UDP conn for dual stack: %w", err)
}
udpConn6, err := gonet.DialUDP(w.stack, nil, nil, ipv6.ProtocolNumber)
if err != nil {
udpConn4.Close()
return nil, fmt.Errorf("failed to create IPv6 UDP conn for dual stack: %w", err)
}
preferIPv6 := w.config.GetPreferIpv6ForUdp()
return fusedPacketConn.NewFusedPacketConn(udpConn4, udpConn6, int(w.config.Mtu), preferIPv6), nil
}
// If not specified, let the stack choose the local address (pass nil laddr).
// Default network selection honors PreferIpv6ForUdp if configured.
defaultNet := ipv4.ProtocolNumber
if w.config != nil && w.config.GetPreferIpv6ForUdp() {
defaultNet = ipv6.ProtocolNumber
}
udpConn, err := gonet.DialUDP(w.stack, nil, nil, defaultNet)
if err != nil {
return nil, err
}
return udpConn, nil
}