mirror of
https://github.com/SagerNet/sing.git
synced 2025-03-31 10:27:39 +03:00
Add winiphlpapi
This commit is contained in:
parent
2b41455f5a
commit
ea0ac932ae
6 changed files with 780 additions and 4 deletions
|
@ -1,16 +1,14 @@
|
|||
//go:build windows
|
||||
|
||||
package windnsapi
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDNSAPI(t *testing.T) {
|
||||
if runtime.GOOS != "windows" {
|
||||
t.SkipNow()
|
||||
}
|
||||
t.Parallel()
|
||||
require.NoError(t, FlushResolverCache())
|
||||
}
|
||||
|
|
217
common/winiphlpapi/helper.go
Normal file
217
common/winiphlpapi/helper.go
Normal file
|
@ -0,0 +1,217 @@
|
|||
//go:build windows
|
||||
|
||||
package winiphlpapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
)
|
||||
|
||||
func LoadEStats() error {
|
||||
err := modiphlpapi.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = procGetTcpTable.Find()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = procGetTcp6Table.Find()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = procGetPerTcp6ConnectionEStats.Find()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = procGetPerTcp6ConnectionEStats.Find()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = procSetPerTcpConnectionEStats.Find()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = procSetPerTcp6ConnectionEStats.Find()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadExtendedTable() error {
|
||||
err := modiphlpapi.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = procGetExtendedTcpTable.Find()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = procGetExtendedUdpTable.Find()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FindPid(network string, source netip.AddrPort) (uint32, error) {
|
||||
switch N.NetworkName(network) {
|
||||
case N.NetworkTCP:
|
||||
if source.Addr().Is4() {
|
||||
tcpTable, err := GetExtendedTcpTable()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, row := range tcpTable {
|
||||
if source == netip.AddrPortFrom(DwordToAddr(row.DwLocalAddr), DwordToPort(row.DwLocalPort)) {
|
||||
return row.DwOwningPid, nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tcpTable, err := GetExtendedTcp6Table()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, row := range tcpTable {
|
||||
if source == netip.AddrPortFrom(netip.AddrFrom16(row.UcLocalAddr), DwordToPort(row.DwLocalPort)) {
|
||||
return row.DwOwningPid, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
case N.NetworkUDP:
|
||||
if source.Addr().Is4() {
|
||||
udpTable, err := GetExtendedUdpTable()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, row := range udpTable {
|
||||
if source == netip.AddrPortFrom(DwordToAddr(row.DwLocalAddr), DwordToPort(row.DwLocalPort)) {
|
||||
return row.DwOwningPid, nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
udpTable, err := GetExtendedUdp6Table()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
for _, row := range udpTable {
|
||||
if source == netip.AddrPortFrom(netip.AddrFrom16(row.UcLocalAddr), DwordToPort(row.DwLocalPort)) {
|
||||
return row.DwOwningPid, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0, E.New("process not found for ", source)
|
||||
}
|
||||
|
||||
func WriteAndWaitAck(ctx context.Context, conn net.Conn, payload []byte) error {
|
||||
source := M.AddrPortFromNet(conn.LocalAddr())
|
||||
destination := M.AddrPortFromNet(conn.RemoteAddr())
|
||||
if source.Addr().Is4() {
|
||||
tcpTable, err := GetTcpTable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var tcpRow *MibTcpRow
|
||||
for _, row := range tcpTable {
|
||||
if source == netip.AddrPortFrom(DwordToAddr(row.DwLocalAddr), DwordToPort(row.DwLocalPort)) ||
|
||||
destination == netip.AddrPortFrom(DwordToAddr(row.DwRemoteAddr), DwordToPort(row.DwRemotePort)) {
|
||||
tcpRow = &row
|
||||
break
|
||||
}
|
||||
}
|
||||
if tcpRow == nil {
|
||||
return E.New("row not found for: ", source)
|
||||
}
|
||||
err = SetPerTcpConnectionEStatsSendBuffer(tcpRow, &TcpEstatsSendBuffRwV0{
|
||||
EnableCollection: true,
|
||||
})
|
||||
if err != nil {
|
||||
return os.NewSyscallError("SetPerTcpConnectionEStatsSendBufferV0", err)
|
||||
}
|
||||
defer SetPerTcpConnectionEStatsSendBuffer(tcpRow, &TcpEstatsSendBuffRwV0{
|
||||
EnableCollection: false,
|
||||
})
|
||||
_, err = conn.Write(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
eStstsSendBuffer, err := GetPerTcpConnectionEStatsSendBuffer(tcpRow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if eStstsSendBuffer.CurRetxQueue == 0 {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
} else {
|
||||
tcpTable, err := GetTcp6Table()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var tcpRow *MibTcp6Row
|
||||
for _, row := range tcpTable {
|
||||
if source == netip.AddrPortFrom(netip.AddrFrom16(row.LocalAddr), DwordToPort(row.LocalPort)) ||
|
||||
destination == netip.AddrPortFrom(netip.AddrFrom16(row.RemoteAddr), DwordToPort(row.RemotePort)) {
|
||||
tcpRow = &row
|
||||
break
|
||||
}
|
||||
}
|
||||
if tcpRow == nil {
|
||||
return E.New("row not found for: ", source)
|
||||
}
|
||||
err = SetPerTcp6ConnectionEStatsSendBuffer(tcpRow, &TcpEstatsSendBuffRwV0{
|
||||
EnableCollection: true,
|
||||
})
|
||||
if err != nil {
|
||||
return os.NewSyscallError("SetPerTcpConnectionEStatsSendBufferV0", err)
|
||||
}
|
||||
defer SetPerTcp6ConnectionEStatsSendBuffer(tcpRow, &TcpEstatsSendBuffRwV0{
|
||||
EnableCollection: false,
|
||||
})
|
||||
_, err = conn.Write(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
eStstsSendBuffer, err := GetPerTcp6ConnectionEStatsSendBuffer(tcpRow)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if eStstsSendBuffer.CurRetxQueue == 0 {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func DwordToAddr(addr uint32) netip.Addr {
|
||||
return netip.AddrFrom4(*(*[4]byte)(unsafe.Pointer(&addr)))
|
||||
}
|
||||
|
||||
func DwordToPort(dword uint32) uint16 {
|
||||
return binary.BigEndian.Uint16((*[4]byte)(unsafe.Pointer(&dword))[:])
|
||||
}
|
313
common/winiphlpapi/iphlpapi.go
Normal file
313
common/winiphlpapi/iphlpapi.go
Normal file
|
@ -0,0 +1,313 @@
|
|||
//go:build windows
|
||||
|
||||
package winiphlpapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const (
|
||||
TcpTableBasicListener uint32 = iota
|
||||
TcpTableBasicConnections
|
||||
TcpTableBasicAll
|
||||
TcpTableOwnerPidListener
|
||||
TcpTableOwnerPidConnections
|
||||
TcpTableOwnerPidAll
|
||||
TcpTableOwnerModuleListener
|
||||
TcpTableOwnerModuleConnections
|
||||
TcpTableOwnerModuleAll
|
||||
)
|
||||
|
||||
const (
|
||||
UdpTableBasic uint32 = iota
|
||||
UdpTableOwnerPid
|
||||
UdpTableOwnerModule
|
||||
)
|
||||
|
||||
const (
|
||||
TcpConnectionEstatsSynOpts uint32 = iota
|
||||
TcpConnectionEstatsData
|
||||
TcpConnectionEstatsSndCong
|
||||
TcpConnectionEstatsPath
|
||||
TcpConnectionEstatsSendBuff
|
||||
TcpConnectionEstatsRec
|
||||
TcpConnectionEstatsObsRec
|
||||
TcpConnectionEstatsBandwidth
|
||||
TcpConnectionEstatsFineRtt
|
||||
TcpConnectionEstatsMaximum
|
||||
)
|
||||
|
||||
type MibTcpTable struct {
|
||||
DwNumEntries uint32
|
||||
Table [1]MibTcpRow
|
||||
}
|
||||
|
||||
type MibTcpRow struct {
|
||||
DwState uint32
|
||||
DwLocalAddr uint32
|
||||
DwLocalPort uint32
|
||||
DwRemoteAddr uint32
|
||||
DwRemotePort uint32
|
||||
}
|
||||
|
||||
type MibTcp6Table struct {
|
||||
DwNumEntries uint32
|
||||
Table [1]MibTcp6Row
|
||||
}
|
||||
|
||||
type MibTcp6Row struct {
|
||||
State uint32
|
||||
LocalAddr [16]byte
|
||||
LocalScopeId uint32
|
||||
LocalPort uint32
|
||||
RemoteAddr [16]byte
|
||||
RemoteScopeId uint32
|
||||
RemotePort uint32
|
||||
}
|
||||
|
||||
type MibTcpTableOwnerPid struct {
|
||||
DwNumEntries uint32
|
||||
Table [1]MibTcpRowOwnerPid
|
||||
}
|
||||
|
||||
type MibTcpRowOwnerPid struct {
|
||||
DwState uint32
|
||||
DwLocalAddr uint32
|
||||
DwLocalPort uint32
|
||||
DwRemoteAddr uint32
|
||||
DwRemotePort uint32
|
||||
DwOwningPid uint32
|
||||
}
|
||||
|
||||
type MibTcp6TableOwnerPid struct {
|
||||
DwNumEntries uint32
|
||||
Table [1]MibTcp6RowOwnerPid
|
||||
}
|
||||
|
||||
type MibTcp6RowOwnerPid struct {
|
||||
UcLocalAddr [16]byte
|
||||
DwLocalScopeId uint32
|
||||
DwLocalPort uint32
|
||||
UcRemoteAddr [16]byte
|
||||
DwRemoteScopeId uint32
|
||||
DwRemotePort uint32
|
||||
DwState uint32
|
||||
DwOwningPid uint32
|
||||
}
|
||||
|
||||
type MibUdpTableOwnerPid struct {
|
||||
DwNumEntries uint32
|
||||
Table [1]MibUdpRowOwnerPid
|
||||
}
|
||||
|
||||
type MibUdpRowOwnerPid struct {
|
||||
DwLocalAddr uint32
|
||||
DwLocalPort uint32
|
||||
DwOwningPid uint32
|
||||
}
|
||||
|
||||
type MibUdp6TableOwnerPid struct {
|
||||
DwNumEntries uint32
|
||||
Table [1]MibUdp6RowOwnerPid
|
||||
}
|
||||
|
||||
type MibUdp6RowOwnerPid struct {
|
||||
UcLocalAddr [16]byte
|
||||
DwLocalScopeId uint32
|
||||
DwLocalPort uint32
|
||||
DwOwningPid uint32
|
||||
}
|
||||
|
||||
type TcpEstatsSendBufferRodV0 struct {
|
||||
CurRetxQueue uint64
|
||||
MaxRetxQueue uint64
|
||||
CurAppWQueue uint64
|
||||
MaxAppWQueue uint64
|
||||
}
|
||||
|
||||
type TcpEstatsSendBuffRwV0 struct {
|
||||
EnableCollection bool
|
||||
}
|
||||
|
||||
const (
|
||||
offsetOfMibTcpTable = unsafe.Offsetof(MibTcpTable{}.Table)
|
||||
offsetOfMibTcp6Table = unsafe.Offsetof(MibTcp6Table{}.Table)
|
||||
offsetOfMibTcpTableOwnerPid = unsafe.Offsetof(MibTcpTableOwnerPid{}.Table)
|
||||
offsetOfMibTcp6TableOwnerPid = unsafe.Offsetof(MibTcpTableOwnerPid{}.Table)
|
||||
offsetOfMibUdpTableOwnerPid = unsafe.Offsetof(MibUdpTableOwnerPid{}.Table)
|
||||
offsetOfMibUdp6TableOwnerPid = unsafe.Offsetof(MibUdp6TableOwnerPid{}.Table)
|
||||
sizeOfTcpEstatsSendBuffRwV0 = unsafe.Sizeof(TcpEstatsSendBuffRwV0{})
|
||||
sizeOfTcpEstatsSendBufferRodV0 = unsafe.Sizeof(TcpEstatsSendBufferRodV0{})
|
||||
)
|
||||
|
||||
func GetTcpTable() ([]MibTcpRow, error) {
|
||||
var size uint32
|
||||
err := getTcpTable(nil, &size, false)
|
||||
if !errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
table := make([]byte, size)
|
||||
err = getTcpTable(&table[0], &size, false)
|
||||
if err != nil {
|
||||
if errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
dwNumEntries := int(*(*uint32)(unsafe.Pointer(&table[0])))
|
||||
return unsafe.Slice((*MibTcpRow)(unsafe.Pointer(&table[offsetOfMibTcpTable])), dwNumEntries), nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetTcp6Table() ([]MibTcp6Row, error) {
|
||||
var size uint32
|
||||
err := getTcp6Table(nil, &size, false)
|
||||
if !errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
table := make([]byte, size)
|
||||
err = getTcp6Table(&table[0], &size, false)
|
||||
if err != nil {
|
||||
if errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
dwNumEntries := int(*(*uint32)(unsafe.Pointer(&table[0])))
|
||||
return unsafe.Slice((*MibTcp6Row)(unsafe.Pointer(&table[offsetOfMibTcp6Table])), dwNumEntries), nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetExtendedTcpTable() ([]MibTcpRowOwnerPid, error) {
|
||||
var size uint32
|
||||
err := getExtendedTcpTable(nil, &size, false, windows.AF_INET, TcpTableOwnerPidConnections, 0)
|
||||
if !errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
|
||||
return nil, os.NewSyscallError("GetExtendedTcpTable", err)
|
||||
}
|
||||
for {
|
||||
table := make([]byte, size)
|
||||
err = getExtendedTcpTable(&table[0], &size, false, windows.AF_INET, TcpTableOwnerPidConnections, 0)
|
||||
if err != nil {
|
||||
if errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
|
||||
continue
|
||||
}
|
||||
return nil, os.NewSyscallError("GetExtendedTcpTable", err)
|
||||
}
|
||||
dwNumEntries := int(*(*uint32)(unsafe.Pointer(&table[0])))
|
||||
return unsafe.Slice((*MibTcpRowOwnerPid)(unsafe.Pointer(&table[offsetOfMibTcpTableOwnerPid])), dwNumEntries), nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetExtendedTcp6Table() ([]MibTcp6RowOwnerPid, error) {
|
||||
var size uint32
|
||||
err := getExtendedTcpTable(nil, &size, false, windows.AF_INET6, TcpTableOwnerPidConnections, 0)
|
||||
if !errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
|
||||
return nil, os.NewSyscallError("GetExtendedTcpTable", err)
|
||||
}
|
||||
for {
|
||||
table := make([]byte, size)
|
||||
err = getExtendedTcpTable(&table[0], &size, false, windows.AF_INET6, TcpTableOwnerPidConnections, 0)
|
||||
if err != nil {
|
||||
if errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
|
||||
continue
|
||||
}
|
||||
return nil, os.NewSyscallError("GetExtendedTcpTable", err)
|
||||
}
|
||||
dwNumEntries := int(*(*uint32)(unsafe.Pointer(&table[0])))
|
||||
return unsafe.Slice((*MibTcp6RowOwnerPid)(unsafe.Pointer(&table[offsetOfMibTcp6TableOwnerPid])), dwNumEntries), nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetExtendedUdpTable() ([]MibUdpRowOwnerPid, error) {
|
||||
var size uint32
|
||||
err := getExtendedUdpTable(nil, &size, false, windows.AF_INET, UdpTableOwnerPid, 0)
|
||||
if !errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
|
||||
return nil, os.NewSyscallError("GetExtendedUdpTable", err)
|
||||
}
|
||||
for {
|
||||
table := make([]byte, size)
|
||||
err = getExtendedUdpTable(&table[0], &size, false, windows.AF_INET, UdpTableOwnerPid, 0)
|
||||
if err != nil {
|
||||
if errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
|
||||
continue
|
||||
}
|
||||
return nil, os.NewSyscallError("GetExtendedUdpTable", err)
|
||||
}
|
||||
dwNumEntries := int(*(*uint32)(unsafe.Pointer(&table[0])))
|
||||
return unsafe.Slice((*MibUdpRowOwnerPid)(unsafe.Pointer(&table[offsetOfMibUdpTableOwnerPid])), dwNumEntries), nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetExtendedUdp6Table() ([]MibUdp6RowOwnerPid, error) {
|
||||
var size uint32
|
||||
err := getExtendedUdpTable(nil, &size, false, windows.AF_INET6, UdpTableOwnerPid, 0)
|
||||
if !errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
|
||||
return nil, os.NewSyscallError("GetExtendedUdpTable", err)
|
||||
}
|
||||
for {
|
||||
table := make([]byte, size)
|
||||
err = getExtendedUdpTable(&table[0], &size, false, windows.AF_INET6, UdpTableOwnerPid, 0)
|
||||
if err != nil {
|
||||
if errors.Is(err, windows.ERROR_INSUFFICIENT_BUFFER) {
|
||||
continue
|
||||
}
|
||||
return nil, os.NewSyscallError("GetExtendedUdpTable", err)
|
||||
}
|
||||
dwNumEntries := int(*(*uint32)(unsafe.Pointer(&table[0])))
|
||||
return unsafe.Slice((*MibUdp6RowOwnerPid)(unsafe.Pointer(&table[offsetOfMibUdp6TableOwnerPid])), dwNumEntries), nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetPerTcpConnectionEStatsSendBuffer(row *MibTcpRow) (*TcpEstatsSendBufferRodV0, error) {
|
||||
var rod TcpEstatsSendBufferRodV0
|
||||
err := getPerTcpConnectionEStats(row,
|
||||
TcpConnectionEstatsSendBuff,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&rod)),
|
||||
0,
|
||||
uint64(sizeOfTcpEstatsSendBufferRodV0),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rod, nil
|
||||
}
|
||||
|
||||
func GetPerTcp6ConnectionEStatsSendBuffer(row *MibTcp6Row) (*TcpEstatsSendBufferRodV0, error) {
|
||||
var rod TcpEstatsSendBufferRodV0
|
||||
err := getPerTcp6ConnectionEStats(row,
|
||||
TcpConnectionEstatsSendBuff,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
uintptr(unsafe.Pointer(&rod)),
|
||||
0,
|
||||
uint64(sizeOfTcpEstatsSendBufferRodV0),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rod, nil
|
||||
}
|
||||
|
||||
func SetPerTcpConnectionEStatsSendBuffer(row *MibTcpRow, rw *TcpEstatsSendBuffRwV0) error {
|
||||
return setPerTcpConnectionEStats(row, TcpConnectionEstatsSendBuff, uintptr(unsafe.Pointer(&rw)), 0, uint64(sizeOfTcpEstatsSendBuffRwV0), 0)
|
||||
}
|
||||
|
||||
func SetPerTcp6ConnectionEStatsSendBuffer(row *MibTcp6Row, rw *TcpEstatsSendBuffRwV0) error {
|
||||
return setPerTcp6ConnectionEStats(row, TcpConnectionEstatsSendBuff, uintptr(unsafe.Pointer(&rw)), 0, uint64(sizeOfTcpEstatsSendBuffRwV0), 0)
|
||||
}
|
90
common/winiphlpapi/iphlpapi_test.go
Normal file
90
common/winiphlpapi/iphlpapi_test.go
Normal file
|
@ -0,0 +1,90 @@
|
|||
//go:build windows
|
||||
|
||||
package winiphlpapi_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/winiphlpapi"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFindPidTcp4(t *testing.T) {
|
||||
t.Parallel()
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
defer listener.Close()
|
||||
go listener.Accept()
|
||||
conn, err := net.Dial("tcp", listener.Addr().String())
|
||||
require.NoError(t, err)
|
||||
defer conn.Close()
|
||||
pid, err := winiphlpapi.FindPid(N.NetworkTCP, M.AddrPortFromNet(conn.LocalAddr()))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint32(syscall.Getpid()), pid)
|
||||
}
|
||||
|
||||
func TestFindPidTcp6(t *testing.T) {
|
||||
t.Parallel()
|
||||
listener, err := net.Listen("tcp", "[::1]:0")
|
||||
require.NoError(t, err)
|
||||
defer listener.Close()
|
||||
go listener.Accept()
|
||||
conn, err := net.Dial("tcp", listener.Addr().String())
|
||||
require.NoError(t, err)
|
||||
defer conn.Close()
|
||||
pid, err := winiphlpapi.FindPid(N.NetworkTCP, M.AddrPortFromNet(conn.LocalAddr()))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint32(syscall.Getpid()), pid)
|
||||
}
|
||||
|
||||
func TestFindPidUdp4(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
defer conn.Close()
|
||||
pid, err := winiphlpapi.FindPid(N.NetworkUDP, M.AddrPortFromNet(conn.LocalAddr()))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint32(syscall.Getpid()), pid)
|
||||
}
|
||||
|
||||
func TestFindPidUdp6(t *testing.T) {
|
||||
t.Parallel()
|
||||
conn, err := net.ListenPacket("udp", "[::1]:0")
|
||||
require.NoError(t, err)
|
||||
defer conn.Close()
|
||||
pid, err := winiphlpapi.FindPid(N.NetworkUDP, M.AddrPortFromNet(conn.LocalAddr()))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint32(syscall.Getpid()), pid)
|
||||
}
|
||||
|
||||
func TestWaitAck4(t *testing.T) {
|
||||
t.Parallel()
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
defer listener.Close()
|
||||
go listener.Accept()
|
||||
conn, err := net.Dial("tcp", listener.Addr().String())
|
||||
require.NoError(t, err)
|
||||
defer conn.Close()
|
||||
err = winiphlpapi.WriteAndWaitAck(context.Background(), conn, []byte("hello"))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestWaitAck6(t *testing.T) {
|
||||
t.Parallel()
|
||||
listener, err := net.Listen("tcp", "[::1]:0")
|
||||
require.NoError(t, err)
|
||||
defer listener.Close()
|
||||
go listener.Accept()
|
||||
conn, err := net.Dial("tcp", listener.Addr().String())
|
||||
require.NoError(t, err)
|
||||
defer conn.Close()
|
||||
err = winiphlpapi.WriteAndWaitAck(context.Background(), conn, []byte("hello"))
|
||||
require.NoError(t, err)
|
||||
}
|
27
common/winiphlpapi/syscall_windows.go
Normal file
27
common/winiphlpapi/syscall_windows.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package winiphlpapi
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall_windows.go
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-gettcptable
|
||||
//sys getTcpTable(tcpTable *byte, sizePointer *uint32, order bool) (errcode error) = iphlpapi.GetTcpTable
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-gettcp6table
|
||||
//sys getTcp6Table(tcpTable *byte, sizePointer *uint32, order bool) (errcode error) = iphlpapi.GetTcp6Table
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getpertcpconnectionestats
|
||||
//sys getPerTcpConnectionEStats(row *MibTcpRow, estatsType uint32, rw uintptr, rwVersion uint64, rwSize uint64, ros uintptr, rosVersion uint64, rosSize uint64, rod uintptr, rodVersion uint64, rodSize uint64) (errcode error) = iphlpapi.GetPerTcpConnectionEStats
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getpertcp6connectionestats
|
||||
//sys getPerTcp6ConnectionEStats(row *MibTcp6Row, estatsType uint32, rw uintptr, rwVersion uint64, rwSize uint64, ros uintptr, rosVersion uint64, rosSize uint64, rod uintptr, rodVersion uint64, rodSize uint64) (errcode error) = iphlpapi.GetPerTcp6ConnectionEStats
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-setpertcpconnectionestats
|
||||
//sys setPerTcpConnectionEStats(row *MibTcpRow, estatsType uint32, rw uintptr, rwVersion uint64, rwSize uint64, offset uint64) (errcode error) = iphlpapi.SetPerTcpConnectionEStats
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-setpertcp6connectionestats
|
||||
//sys setPerTcp6ConnectionEStats(row *MibTcp6Row, estatsType uint32, rw uintptr, rwVersion uint64, rwSize uint64, offset uint64) (errcode error) = iphlpapi.SetPerTcp6ConnectionEStats
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getextendedtcptable
|
||||
//sys getExtendedTcpTable(pTcpTable *byte, pdwSize *uint32, bOrder bool, ulAf uint64, tableClass uint32, reserved uint64) = (errcode error) = iphlpapi.GetExtendedTcpTable
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getextendedudptable
|
||||
//sys getExtendedUdpTable(pUdpTable *byte, pdwSize *uint32, bOrder bool, ulAf uint64, tableClass uint32, reserved uint64) = (errcode error) = iphlpapi.GetExtendedUdpTable
|
131
common/winiphlpapi/zsyscall_windows.go
Normal file
131
common/winiphlpapi/zsyscall_windows.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package winiphlpapi
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return errERROR_EINVAL
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
||||
|
||||
procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable")
|
||||
procGetExtendedUdpTable = modiphlpapi.NewProc("GetExtendedUdpTable")
|
||||
procGetPerTcp6ConnectionEStats = modiphlpapi.NewProc("GetPerTcp6ConnectionEStats")
|
||||
procGetPerTcpConnectionEStats = modiphlpapi.NewProc("GetPerTcpConnectionEStats")
|
||||
procGetTcp6Table = modiphlpapi.NewProc("GetTcp6Table")
|
||||
procGetTcpTable = modiphlpapi.NewProc("GetTcpTable")
|
||||
procSetPerTcp6ConnectionEStats = modiphlpapi.NewProc("SetPerTcp6ConnectionEStats")
|
||||
procSetPerTcpConnectionEStats = modiphlpapi.NewProc("SetPerTcpConnectionEStats")
|
||||
)
|
||||
|
||||
func getExtendedTcpTable(pTcpTable *byte, pdwSize *uint32, bOrder bool, ulAf uint64, tableClass uint32, reserved uint64) (errcode error) {
|
||||
var _p0 uint32
|
||||
if bOrder {
|
||||
_p0 = 1
|
||||
}
|
||||
r0, _, _ := syscall.Syscall6(procGetExtendedTcpTable.Addr(), 6, uintptr(unsafe.Pointer(pTcpTable)), uintptr(unsafe.Pointer(pdwSize)), uintptr(_p0), uintptr(ulAf), uintptr(tableClass), uintptr(reserved))
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getExtendedUdpTable(pUdpTable *byte, pdwSize *uint32, bOrder bool, ulAf uint64, tableClass uint32, reserved uint64) (errcode error) {
|
||||
var _p0 uint32
|
||||
if bOrder {
|
||||
_p0 = 1
|
||||
}
|
||||
r0, _, _ := syscall.Syscall6(procGetExtendedUdpTable.Addr(), 6, uintptr(unsafe.Pointer(pUdpTable)), uintptr(unsafe.Pointer(pdwSize)), uintptr(_p0), uintptr(ulAf), uintptr(tableClass), uintptr(reserved))
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getPerTcp6ConnectionEStats(row *MibTcp6Row, estatsType uint32, rw uintptr, rwVersion uint64, rwSize uint64, ros uintptr, rosVersion uint64, rosSize uint64, rod uintptr, rodVersion uint64, rodSize uint64) (errcode error) {
|
||||
r0, _, _ := syscall.Syscall12(procGetPerTcp6ConnectionEStats.Addr(), 11, uintptr(unsafe.Pointer(row)), uintptr(estatsType), uintptr(rw), uintptr(rwVersion), uintptr(rwSize), uintptr(ros), uintptr(rosVersion), uintptr(rosSize), uintptr(rod), uintptr(rodVersion), uintptr(rodSize), 0)
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getPerTcpConnectionEStats(row *MibTcpRow, estatsType uint32, rw uintptr, rwVersion uint64, rwSize uint64, ros uintptr, rosVersion uint64, rosSize uint64, rod uintptr, rodVersion uint64, rodSize uint64) (errcode error) {
|
||||
r0, _, _ := syscall.Syscall12(procGetPerTcpConnectionEStats.Addr(), 11, uintptr(unsafe.Pointer(row)), uintptr(estatsType), uintptr(rw), uintptr(rwVersion), uintptr(rwSize), uintptr(ros), uintptr(rosVersion), uintptr(rosSize), uintptr(rod), uintptr(rodVersion), uintptr(rodSize), 0)
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getTcp6Table(tcpTable *byte, sizePointer *uint32, order bool) (errcode error) {
|
||||
var _p0 uint32
|
||||
if order {
|
||||
_p0 = 1
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procGetTcp6Table.Addr(), 3, uintptr(unsafe.Pointer(tcpTable)), uintptr(unsafe.Pointer(sizePointer)), uintptr(_p0))
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getTcpTable(tcpTable *byte, sizePointer *uint32, order bool) (errcode error) {
|
||||
var _p0 uint32
|
||||
if order {
|
||||
_p0 = 1
|
||||
}
|
||||
r0, _, _ := syscall.Syscall(procGetTcpTable.Addr(), 3, uintptr(unsafe.Pointer(tcpTable)), uintptr(unsafe.Pointer(sizePointer)), uintptr(_p0))
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setPerTcp6ConnectionEStats(row *MibTcp6Row, estatsType uint32, rw uintptr, rwVersion uint64, rwSize uint64, offset uint64) (errcode error) {
|
||||
r0, _, _ := syscall.Syscall6(procSetPerTcp6ConnectionEStats.Addr(), 6, uintptr(unsafe.Pointer(row)), uintptr(estatsType), uintptr(rw), uintptr(rwVersion), uintptr(rwSize), uintptr(offset))
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setPerTcpConnectionEStats(row *MibTcpRow, estatsType uint32, rw uintptr, rwVersion uint64, rwSize uint64, offset uint64) (errcode error) {
|
||||
r0, _, _ := syscall.Syscall6(procSetPerTcpConnectionEStats.Addr(), 6, uintptr(unsafe.Pointer(row)), uintptr(estatsType), uintptr(rw), uintptr(rwVersion), uintptr(rwSize), uintptr(offset))
|
||||
if r0 != 0 {
|
||||
errcode = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue