diff --git a/proxy/dokodemo/fakeudp_other.go b/proxy/dokodemo/fakeudp_other.go index 490f0912..f240dd85 100644 --- a/proxy/dokodemo/fakeudp_other.go +++ b/proxy/dokodemo/fakeudp_other.go @@ -1,5 +1,5 @@ -//go:build !linux -// +build !linux +//go:build !linux && !windows +// +build !linux,!windows package dokodemo diff --git a/proxy/dokodemo/fakeudp_windows.go b/proxy/dokodemo/fakeudp_windows.go new file mode 100644 index 00000000..09706a64 --- /dev/null +++ b/proxy/dokodemo/fakeudp_windows.go @@ -0,0 +1,27 @@ +//go:build windows +// +build windows + +package dokodemo + +import ( + "net" + "syscall" +) + +func FakeUDP(addr *net.UDPAddr, mark int) (net.PacketConn, error) { + udpConn, err := net.ListenUDP("udp", addr) + if err != nil { + return udpConn, err + } + rawConn, err := udpConn.SyscallConn() + if err != nil { + return nil, err + } + err = rawConn.Control(func(fd uintptr) { + syscall.SetsockoptInt(syscall.Handle(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) + }) + if err != nil { + return nil, err + } + return udpConn, nil +} diff --git a/transport/internet/sockopt_windows.go b/transport/internet/sockopt_windows.go index 1389ca06..463b7cd3 100644 --- a/transport/internet/sockopt_windows.go +++ b/transport/internet/sockopt_windows.go @@ -11,6 +11,7 @@ import ( "unsafe" "github.com/xtls/xray-core/common/errors" + "golang.org/x/sys/windows" ) const ( @@ -142,6 +143,15 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) } } + if config.ReceiveOriginalDestAddress && isUDPSocket(network) { + if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IP, windows.IP_PKTINFO, 1); err != nil { + return errors.New("failed to set IP_PKTINFO").Base(err) + } + if err := syscall.SetsockoptInt(syscall.Handle(fd), syscall.IPPROTO_IPV6, windows.IPV6_PKTINFO, 1); err != nil { + return errors.New("failed to set IPV6_PKTINFO").Base(err) + } + } + if len(config.CustomSockopt) > 0 { for _, custom := range config.CustomSockopt { if custom.System != "" && custom.System != runtime.GOOS { diff --git a/transport/internet/udp/hub_other.go b/transport/internet/udp/hub_other.go index 3a784183..e5a61de1 100644 --- a/transport/internet/udp/hub_other.go +++ b/transport/internet/udp/hub_other.go @@ -1,5 +1,5 @@ -//go:build !linux && !freebsd && !darwin -// +build !linux,!freebsd,!darwin +//go:build !linux && !freebsd && !darwin && !windows +// +build !linux,!freebsd,!darwin,!windows package udp diff --git a/transport/internet/udp/hub_windows.go b/transport/internet/udp/hub_windows.go new file mode 100644 index 00000000..2cf7b6a8 --- /dev/null +++ b/transport/internet/udp/hub_windows.go @@ -0,0 +1,54 @@ +//go:build windows +// +build windows + +package udp + +import ( + "encoding/binary" + "unsafe" + + "github.com/xtls/xray-core/common/buf" + "github.com/xtls/xray-core/common/net" + "golang.org/x/sys/windows" +) + +func RetrieveOriginalDest(oob []byte) net.Destination { + dest := net.Destination{} + port := binary.LittleEndian.Uint16(oob[:2]) + buf := buf.FromBytes(oob[2:]) + defer buf.Release() + + for !buf.IsEmpty() { + cm := &windows.WSACMSGHDR{} + len := make([]byte, unsafe.Sizeof(cm.Len)) + nRead, err := buf.Read(len) + if err != nil { + return dest + } + cm.Len = uintptr(binary.LittleEndian.Uint16(len)) + binary.Read(buf, binary.LittleEndian, &cm.Level) + binary.Read(buf, binary.LittleEndian, &cm.Type) + nRead += 8 // len cm.Level + cm.Type + + if cm.Type == windows.IP_PKTINFO { + if cm.Level == windows.IPPROTO_IP { // IPv4 + pktinf := &windows.IN_PKTINFO{} + binary.Read(buf, binary.LittleEndian, pktinf) + return net.UDPDestination(net.IPAddress(pktinf.Addr[:]), net.Port(port)) + } else { // IPv6 + pktinfv6 := &windows.IN6_PKTINFO{} + binary.Read(buf, binary.LittleEndian, pktinfv6) + return net.UDPDestination(net.IPAddress(pktinfv6.Addr[:]), net.Port(port)) + } + } + buf.Advance(int32(cm.Len) - int32(nRead)) + } + return dest +} + +func ReadUDPMsg(conn *net.UDPConn, payload []byte, oob []byte) (int, int, int, *net.UDPAddr, error) { + udpAddr, _ := net.ResolveUDPAddr(conn.LocalAddr().Network(), conn.LocalAddr().String()) + binary.LittleEndian.PutUint16(oob[:2], uint16(udpAddr.Port)) + n, oobn, flags, addr, err := conn.ReadMsgUDP(payload, oob[2:]) + return n, oobn + 2, flags, addr, err +}