win32api/network_api_test.go
starainrt 0f82ba044b
修正 Win32 封装语义并补齐关键结构体/进程测试覆盖
- 修正 WTS 会话相关类型、枚举与活动会话选择逻辑
- 对齐 FILE_ID_DESCRIPTOR 布局与 FILE_ID_TYPE 语义,修复 OpenFileById 调用前提
- 修正 user32/shell32/kernel32 部分 API 的返回值、参数个数与错误处理
- 完善剪贴板更新格式读取的缓冲区重试逻辑
- 补充常用进程、线程、调试、桌面与会话 helper
- 增加结构体布局、会话查询、剪贴板、CreateProcess 等回归测试
- 将默认 CreateProcess 相关测试切到 helper 进程,并保留显式开启的 cmd.exe 集成覆盖
2026-06-06 17:46:02 +08:00

837 lines
22 KiB
Go

//go:build windows
package win32api
import (
"fmt"
"io"
"net"
"os"
"runtime"
"strings"
"testing"
"time"
"unsafe"
)
func TestGetHostName(t *testing.T) {
host, err := GetHostName()
if err != nil {
t.Fatalf("GetHostName failed: %v", err)
}
host = strings.TrimSpace(host)
if host == "" {
t.Fatal("GetHostName returned empty string")
}
}
func TestInetPtonNtopIPv4(t *testing.T) {
raw, err := InetPton(AF_INET, "127.0.0.1")
if err != nil {
t.Fatalf("InetPton ipv4 failed: %v", err)
}
if len(raw) != 4 {
t.Fatalf("InetPton ipv4 len mismatch: got=%d", len(raw))
}
ip, err := InetNtop(AF_INET, raw)
if err != nil {
t.Fatalf("InetNtop ipv4 failed: %v", err)
}
if !net.ParseIP(ip).Equal(net.ParseIP("127.0.0.1")) {
t.Fatalf("InetNtop ipv4 mismatch: got=%q", ip)
}
}
func TestInetPtonNtopIPv6(t *testing.T) {
raw, err := InetPton(AF_INET6, "::1")
if err != nil {
t.Fatalf("InetPton ipv6 failed: %v", err)
}
if len(raw) != 16 {
t.Fatalf("InetPton ipv6 len mismatch: got=%d", len(raw))
}
ip, err := InetNtop(AF_INET6, raw)
if err != nil {
t.Fatalf("InetNtop ipv6 failed: %v", err)
}
if !net.ParseIP(ip).Equal(net.ParseIP("::1")) {
t.Fatalf("InetNtop ipv6 mismatch: got=%q", ip)
}
}
func TestInetPtonInvalidInput(t *testing.T) {
if _, err := InetPton(AF_INET, "999.999.999.999"); err == nil {
t.Fatal("InetPton should fail on invalid ipv4 address")
}
}
func TestGetAddrInfoLocalhost(t *testing.T) {
hints := &ADDRINFOW{
Family: AF_UNSPEC,
Socktype: SOCK_STREAM,
Protocol: IPPROTO_TCP,
}
result, err := GetAddrInfo("localhost", "80", hints)
if err != nil {
t.Fatalf("GetAddrInfo failed: %v", err)
}
defer func() {
if freeErr := FreeAddrInfo(result); freeErr != nil {
t.Fatalf("FreeAddrInfo failed: %v", freeErr)
}
}()
total := 0
validIP := 0
for p := result; p != nil; p = p.Next {
total++
if p.Addr == nil {
continue
}
switch p.Family {
case AF_INET, AF_INET6:
ip, ipErr := SockaddrIPString(p.Addr)
if ipErr != nil {
t.Fatalf("SockaddrIPString failed: %v", ipErr)
}
if net.ParseIP(ip) == nil {
t.Fatalf("invalid ip parsed from addrinfo: %q", ip)
}
validIP++
}
}
if total == 0 {
t.Fatal("GetAddrInfo returned empty result list")
}
if validIP == 0 {
t.Fatal("GetAddrInfo returned no AF_INET/AF_INET6 entries")
}
}
func TestGetNameInfoNumericIPv4(t *testing.T) {
sa := SOCKADDR_IN{
Family: ADDRESS_FAMILY(AF_INET),
Port: htons(80),
Addr: [4]byte{127, 0, 0, 1},
}
host, service, err := GetNameInfo((*SOCKADDR)(unsafe.Pointer(&sa)), int32(unsafe.Sizeof(sa)), NI_NUMERICHOST|NI_NUMERICSERV)
if err != nil {
t.Fatalf("GetNameInfo failed: %v", err)
}
if host != "127.0.0.1" {
t.Fatalf("GetNameInfo host mismatch: got=%q", host)
}
if service != "80" {
t.Fatalf("GetNameInfo service mismatch: got=%q", service)
}
}
func TestHostNetworkByteOrderHelpers(t *testing.T) {
if Htons(0x1234) != 0x3412 {
t.Fatalf("Htons mismatch: got=%#x", Htons(0x1234))
}
if Ntohs(0x3412) != 0x1234 {
t.Fatalf("Ntohs mismatch: got=%#x", Ntohs(0x3412))
}
tcp4 := MIB_TCPROW_OWNER_PID{LocalPort: uint32(Htons(8080)), RemotePort: uint32(Htons(443))}
if tcp4.LocalPortHost() != 8080 || tcp4.RemotePortHost() != 443 {
t.Fatalf("tcp4 port helper mismatch: local=%d remote=%d", tcp4.LocalPortHost(), tcp4.RemotePortHost())
}
tcp6 := MIB_TCP6ROW_OWNER_PID{LocalPort: uint32(Htons(8081)), RemotePort: uint32(Htons(8443))}
if tcp6.LocalPortHost() != 8081 || tcp6.RemotePortHost() != 8443 {
t.Fatalf("tcp6 port helper mismatch: local=%d remote=%d", tcp6.LocalPortHost(), tcp6.RemotePortHost())
}
udp4 := MIB_UDPROW_OWNER_PID{LocalPort: uint32(Htons(5353))}
if udp4.LocalPortHost() != 5353 {
t.Fatalf("udp4 port helper mismatch: local=%d", udp4.LocalPortHost())
}
udp6 := MIB_UDP6ROW_OWNER_PID{LocalPort: uint32(Htons(5354))}
if udp6.LocalPortHost() != 5354 {
t.Fatalf("udp6 port helper mismatch: local=%d", udp6.LocalPortHost())
}
}
func TestIphlpapiTableLayouts(t *testing.T) {
wantIfTableOffset := uintptr(8)
wantIfRowSize := uintptr(1352)
if runtime.GOARCH == "386" {
wantIfTableOffset = 4
wantIfRowSize = 1348
}
if got := unsafe.Offsetof(MIB_IF_TABLE2{}.Table); got != wantIfTableOffset {
t.Fatalf("MIB_IF_TABLE2.Table offset mismatch: got=%d want=%d", got, wantIfTableOffset)
}
if got := unsafe.Sizeof(MIB_IF_ROW2{}); got != wantIfRowSize {
t.Fatalf("MIB_IF_ROW2 size mismatch: got=%d want=%d", got, wantIfRowSize)
}
if got := unsafe.Offsetof(MIB_TCPTABLE_OWNER_PID{}.Table); got != 4 {
t.Fatalf("MIB_TCPTABLE_OWNER_PID.Table offset mismatch: got=%d want=4", got)
}
if got := unsafe.Sizeof(MIB_TCPROW_OWNER_PID{}); got != 24 {
t.Fatalf("MIB_TCPROW_OWNER_PID size mismatch: got=%d want=24", got)
}
if got := unsafe.Offsetof(MIB_TCP6TABLE_OWNER_PID{}.Table); got != 4 {
t.Fatalf("MIB_TCP6TABLE_OWNER_PID.Table offset mismatch: got=%d want=4", got)
}
if got := unsafe.Sizeof(MIB_TCP6ROW_OWNER_PID{}); got != 56 {
t.Fatalf("MIB_TCP6ROW_OWNER_PID size mismatch: got=%d want=56", got)
}
if got := unsafe.Offsetof(MIB_UDPTABLE_OWNER_PID{}.Table); got != 4 {
t.Fatalf("MIB_UDPTABLE_OWNER_PID.Table offset mismatch: got=%d want=4", got)
}
if got := unsafe.Sizeof(MIB_UDPROW_OWNER_PID{}); got != 12 {
t.Fatalf("MIB_UDPROW_OWNER_PID size mismatch: got=%d want=12", got)
}
if got := unsafe.Offsetof(MIB_UDP6TABLE_OWNER_PID{}.Table); got != 4 {
t.Fatalf("MIB_UDP6TABLE_OWNER_PID.Table offset mismatch: got=%d want=4", got)
}
if got := unsafe.Sizeof(MIB_UDP6ROW_OWNER_PID{}); got != 28 {
t.Fatalf("MIB_UDP6ROW_OWNER_PID size mismatch: got=%d want=28", got)
}
}
func requireWSA(t *testing.T) {
t.Helper()
var data WSADATA
if err := WSAStartup(makeWord(2, 2), &data); err != nil {
t.Fatalf("WSAStartup failed: %v", err)
}
t.Cleanup(func() {
_ = WSACleanup()
})
}
func TestSocketConnectSendRecv(t *testing.T) {
requireWSA(t)
ln, err := net.Listen("tcp4", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen failed: %v", err)
}
defer func() {
_ = ln.Close()
}()
done := make(chan error, 1)
go func() {
conn, acceptErr := ln.Accept()
if acceptErr != nil {
done <- acceptErr
return
}
defer func() {
_ = conn.Close()
}()
buf := make([]byte, 4)
if _, readErr := io.ReadFull(conn, buf); readErr != nil {
done <- readErr
return
}
if string(buf) != "ping" {
done <- io.ErrUnexpectedEOF
return
}
_, writeErr := conn.Write([]byte("pong"))
done <- writeErr
}()
s, err := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
if err != nil {
t.Fatalf("Socket failed: %v", err)
}
defer func() {
_ = Closesocket(s)
}()
tcpAddr, ok := ln.Addr().(*net.TCPAddr)
if !ok {
t.Fatalf("listen addr type mismatch: %T", ln.Addr())
}
sa := SOCKADDR_IN{
Family: ADDRESS_FAMILY(AF_INET),
Port: htons(uint16(tcpAddr.Port)),
Addr: [4]byte{127, 0, 0, 1},
}
if err := Connect(s, (*SOCKADDR)(unsafe.Pointer(&sa)), int32(unsafe.Sizeof(sa))); err != nil {
t.Fatalf("Connect failed: %v", err)
}
payload := []byte("ping")
written, err := Send(s, payload, 0)
if err != nil {
t.Fatalf("Send failed: %v", err)
}
if written != len(payload) {
t.Fatalf("Send wrote mismatch: got=%d want=%d", written, len(payload))
}
reply := make([]byte, 4)
read, err := Recv(s, reply, 0)
if err != nil {
t.Fatalf("Recv failed: %v", err)
}
if string(reply[:read]) != "pong" {
t.Fatalf("Recv data mismatch: got=%q", string(reply[:read]))
}
if err := Shutdown(s, SD_BOTH); err != nil {
t.Fatalf("Shutdown failed: %v", err)
}
select {
case serverErr := <-done:
if serverErr != nil {
t.Fatalf("server goroutine failed: %v", serverErr)
}
case <-time.After(3 * time.Second):
t.Fatal("server goroutine timeout")
}
}
func TestBindListenEphemeral(t *testing.T) {
requireWSA(t)
s, err := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
if err != nil {
t.Fatalf("Socket failed: %v", err)
}
defer func() {
_ = Closesocket(s)
}()
sa := SOCKADDR_IN{
Family: ADDRESS_FAMILY(AF_INET),
Port: 0,
Addr: [4]byte{127, 0, 0, 1},
}
if err := Bind(s, (*SOCKADDR)(unsafe.Pointer(&sa)), int32(unsafe.Sizeof(sa))); err != nil {
t.Fatalf("Bind failed: %v", err)
}
if err := Listen(s, 1); err != nil {
t.Fatalf("Listen failed: %v", err)
}
}
func TestGetSockNameAndPeerName(t *testing.T) {
requireWSA(t)
ln, err := net.Listen("tcp4", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen failed: %v", err)
}
defer func() {
_ = ln.Close()
}()
connCh := make(chan net.Conn, 1)
errCh := make(chan error, 1)
go func() {
conn, acceptErr := ln.Accept()
if acceptErr != nil {
errCh <- acceptErr
return
}
connCh <- conn
}()
client, err := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
if err != nil {
t.Fatalf("Socket failed: %v", err)
}
defer func() {
_ = Closesocket(client)
}()
tcpAddr := ln.Addr().(*net.TCPAddr)
peer := SOCKADDR_IN{
Family: ADDRESS_FAMILY(AF_INET),
Port: htons(uint16(tcpAddr.Port)),
Addr: [4]byte{127, 0, 0, 1},
}
if err := Connect(client, (*SOCKADDR)(unsafe.Pointer(&peer)), int32(unsafe.Sizeof(peer))); err != nil {
t.Fatalf("Connect failed: %v", err)
}
var serverConn net.Conn
select {
case serverConn = <-connCh:
defer func() {
_ = serverConn.Close()
}()
case acceptErr := <-errCh:
t.Fatalf("accept failed: %v", acceptErr)
case <-time.After(3 * time.Second):
t.Fatal("accept timeout")
}
var local SOCKADDR_IN
localLen := int32(unsafe.Sizeof(local))
if err := GetSockName(client, (*SOCKADDR)(unsafe.Pointer(&local)), &localLen); err != nil {
t.Fatalf("GetSockName failed: %v", err)
}
if local.Family != ADDRESS_FAMILY(AF_INET) {
t.Fatalf("GetSockName family mismatch: got=%d", local.Family)
}
if htons(local.Port) == 0 {
t.Fatal("GetSockName returned zero local port")
}
var remote SOCKADDR_IN
remoteLen := int32(unsafe.Sizeof(remote))
if err := GetPeerName(client, (*SOCKADDR)(unsafe.Pointer(&remote)), &remoteLen); err != nil {
t.Fatalf("GetPeerName failed: %v", err)
}
if remote.Family != ADDRESS_FAMILY(AF_INET) {
t.Fatalf("GetPeerName family mismatch: got=%d", remote.Family)
}
if remote.Addr != [4]byte{127, 0, 0, 1} {
t.Fatalf("GetPeerName ip mismatch: got=%v", remote.Addr)
}
if int(htons(remote.Port)) != tcpAddr.Port {
t.Fatalf("GetPeerName port mismatch: got=%d want=%d", htons(remote.Port), tcpAddr.Port)
}
}
func TestSetGetSockOptInt(t *testing.T) {
requireWSA(t)
s, err := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
if err != nil {
t.Fatalf("Socket failed: %v", err)
}
defer func() {
_ = Closesocket(s)
}()
if err := SetSockOptInt(s, SOL_SOCKET, SO_REUSEADDR, 1); err != nil {
t.Fatalf("SetSockOptInt failed: %v", err)
}
v, err := GetSockOptInt(s, SOL_SOCKET, SO_REUSEADDR)
if err != nil {
t.Fatalf("GetSockOptInt failed: %v", err)
}
if v == 0 {
t.Fatalf("GetSockOptInt(SO_REUSEADDR) expected non-zero, got %d", v)
}
}
func TestSendToRecvFromUDP(t *testing.T) {
requireWSA(t)
receiver, err := Socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
if err != nil {
t.Fatalf("receiver Socket failed: %v", err)
}
defer func() {
_ = Closesocket(receiver)
}()
bindAddr := SOCKADDR_IN{
Family: ADDRESS_FAMILY(AF_INET),
Port: 0,
Addr: [4]byte{127, 0, 0, 1},
}
if err := Bind(receiver, (*SOCKADDR)(unsafe.Pointer(&bindAddr)), int32(unsafe.Sizeof(bindAddr))); err != nil {
t.Fatalf("receiver Bind failed: %v", err)
}
var recvBound SOCKADDR_IN
recvBoundLen := int32(unsafe.Sizeof(recvBound))
if err := GetSockName(receiver, (*SOCKADDR)(unsafe.Pointer(&recvBound)), &recvBoundLen); err != nil {
t.Fatalf("receiver GetSockName failed: %v", err)
}
recvPort := htons(recvBound.Port)
if recvPort == 0 {
t.Fatal("receiver bound port is zero")
}
sender, err := Socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
if err != nil {
t.Fatalf("sender Socket failed: %v", err)
}
defer func() {
_ = Closesocket(sender)
}()
target := SOCKADDR_IN{
Family: ADDRESS_FAMILY(AF_INET),
Port: recvBound.Port,
Addr: [4]byte{127, 0, 0, 1},
}
payload := []byte("udp-ping")
sent, err := SendTo(sender, payload, 0, (*SOCKADDR)(unsafe.Pointer(&target)), int32(unsafe.Sizeof(target)))
if err != nil {
t.Fatalf("SendTo failed: %v", err)
}
if sent != len(payload) {
t.Fatalf("SendTo length mismatch: got=%d want=%d", sent, len(payload))
}
buf := make([]byte, 64)
var from SOCKADDR_IN
fromLen := int32(unsafe.Sizeof(from))
n, err := RecvFrom(receiver, buf, 0, (*SOCKADDR)(unsafe.Pointer(&from)), &fromLen)
if err != nil {
t.Fatalf("RecvFrom failed: %v", err)
}
if string(buf[:n]) != string(payload) {
t.Fatalf("RecvFrom payload mismatch: got=%q want=%q", string(buf[:n]), string(payload))
}
if from.Family != ADDRESS_FAMILY(AF_INET) {
t.Fatalf("RecvFrom family mismatch: got=%d", from.Family)
}
if from.Addr != [4]byte{127, 0, 0, 1} {
t.Fatalf("RecvFrom source ip mismatch: got=%v", from.Addr)
}
if htons(from.Port) == 0 {
t.Fatal("RecvFrom source port is zero")
}
}
func TestAcceptSendRecvServerFlow(t *testing.T) {
requireWSA(t)
listener, err := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
if err != nil {
t.Fatalf("listener Socket failed: %v", err)
}
defer func() {
_ = Closesocket(listener)
}()
if err := SetSockOptInt(listener, SOL_SOCKET, SO_REUSEADDR, 1); err != nil {
t.Fatalf("SetSockOptInt(SO_REUSEADDR) failed: %v", err)
}
bindAddr := SOCKADDR_IN{
Family: ADDRESS_FAMILY(AF_INET),
Port: 0,
Addr: [4]byte{127, 0, 0, 1},
}
if err := Bind(listener, (*SOCKADDR)(unsafe.Pointer(&bindAddr)), int32(unsafe.Sizeof(bindAddr))); err != nil {
t.Fatalf("Bind failed: %v", err)
}
if err := Listen(listener, 1); err != nil {
t.Fatalf("Listen failed: %v", err)
}
var bound SOCKADDR_IN
boundLen := int32(unsafe.Sizeof(bound))
if err := GetSockName(listener, (*SOCKADDR)(unsafe.Pointer(&bound)), &boundLen); err != nil {
t.Fatalf("GetSockName(listener) failed: %v", err)
}
port := htons(bound.Port)
if port == 0 {
t.Fatal("listener port is zero")
}
serverDone := make(chan error, 1)
go func() {
var peer SOCKADDR_IN
peerLen := int32(unsafe.Sizeof(peer))
clientSock, acceptErr := Accept(listener, (*SOCKADDR)(unsafe.Pointer(&peer)), &peerLen)
if acceptErr != nil {
serverDone <- acceptErr
return
}
defer func() {
_ = Closesocket(clientSock)
}()
buf := make([]byte, 16)
n, recvErr := Recv(clientSock, buf, 0)
if recvErr != nil {
serverDone <- recvErr
return
}
if string(buf[:n]) != "accept-ping" {
serverDone <- fmt.Errorf("server recv mismatch: %q", string(buf[:n]))
return
}
if _, sendErr := Send(clientSock, []byte("accept-pong"), 0); sendErr != nil {
serverDone <- sendErr
return
}
serverDone <- nil
}()
conn, err := net.DialTimeout("tcp4", fmt.Sprintf("127.0.0.1:%d", port), 3*time.Second)
if err != nil {
t.Fatalf("DialTimeout failed: %v", err)
}
defer func() {
_ = conn.Close()
}()
if _, err := conn.Write([]byte("accept-ping")); err != nil {
t.Fatalf("client Write failed: %v", err)
}
reply := make([]byte, 16)
n, err := io.ReadFull(conn, reply[:11])
if err != nil {
t.Fatalf("client ReadFull failed: %v", err)
}
if string(reply[:n]) != "accept-pong" {
t.Fatalf("client recv mismatch: %q", string(reply[:n]))
}
select {
case err := <-serverDone:
if err != nil {
t.Fatalf("server flow failed: %v", err)
}
case <-time.After(3 * time.Second):
t.Fatal("server flow timeout")
}
}
func TestGetSockOptSOErrorAfterFailedConnect(t *testing.T) {
requireWSA(t)
s, err := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
if err != nil {
t.Fatalf("Socket failed: %v", err)
}
defer func() {
_ = Closesocket(s)
}()
target := SOCKADDR_IN{
Family: ADDRESS_FAMILY(AF_INET),
Port: htons(1),
Addr: [4]byte{127, 0, 0, 1},
}
connectErr := Connect(s, (*SOCKADDR)(unsafe.Pointer(&target)), int32(unsafe.Sizeof(target)))
if connectErr == nil {
t.Skip("port 1 is open in current environment, skip SO_ERROR failure-path test")
}
soErr, err := GetSockOptInt(s, SOL_SOCKET, SO_ERROR)
if err != nil {
t.Fatalf("GetSockOptInt(SO_ERROR) failed: %v", err)
}
if soErr == 0 {
// On some stacks the synchronous connect error can be reported directly and
// SO_ERROR may already be cleared; keep this as diagnostic instead of flaky fail.
t.Logf("SO_ERROR=0 after connect err=%v", connectErr)
}
}
func TestGetAdaptersAddresses(t *testing.T) {
adapters, err := GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX|GAA_FLAG_INCLUDE_GATEWAYS)
if err != nil {
t.Fatalf("GetAdaptersAddresses failed: %v", err)
}
if len(adapters) == 0 {
t.Fatal("GetAdaptersAddresses returned empty list")
}
foundNamed := false
for _, a := range adapters {
if strings.TrimSpace(a.FriendlyName) != "" || strings.TrimSpace(a.AdapterName) != "" {
foundNamed = true
break
}
}
if !foundNamed {
t.Fatal("GetAdaptersAddresses returned adapters without names")
}
for _, a := range adapters {
if a.PhysicalAddressLength > 0 {
if strings.TrimSpace(a.MACAddress) == "" {
t.Fatalf("adapter has physical address length %d but empty MACAddress", a.PhysicalAddressLength)
}
if _, err := net.ParseMAC(a.MACAddress); err != nil {
t.Fatalf("invalid MACAddress format %q: %v", a.MACAddress, err)
}
}
for _, ip := range a.UnicastIPs {
if net.ParseIP(ip) == nil {
t.Fatalf("invalid unicast ip in adapter info: %q", ip)
}
}
for _, ip := range a.DNSServers {
if net.ParseIP(ip) == nil {
t.Fatalf("invalid dns server ip in adapter info: %q", ip)
}
}
for _, ip := range a.Gateways {
if net.ParseIP(ip) == nil {
t.Fatalf("invalid gateway ip in adapter info: %q", ip)
}
}
}
}
func TestGetIfTable2AndEntry2(t *testing.T) {
rows, err := GetIfTable2()
if err != nil {
t.Fatalf("GetIfTable2 failed: %v", err)
}
if len(rows) == 0 {
t.Fatal("GetIfTable2 returned empty list")
}
row := rows[0]
if row.InterfaceIndex == 0 && row.InterfaceLuid == 0 {
t.Fatal("GetIfTable2 returned row without interface identity")
}
if err := GetIfEntry2(&row); err != nil {
t.Fatalf("GetIfEntry2 failed: %v", err)
}
if row.InterfaceIndex == 0 && row.InterfaceLuid == 0 {
t.Fatal("GetIfEntry2 cleared interface identity")
}
stats := []uint64{
row.InOctets,
row.OutOctets,
row.InUcastOctets,
row.OutUcastOctets,
row.InMulticastOctets,
row.OutMulticastOctets,
row.InBroadcastOctets,
row.OutBroadcastOctets,
}
if len(stats) != 8 {
t.Fatalf("unexpected stats field count: got=%d", len(stats))
}
}
func TestGetExtendedTcp4TableIncludesCurrentListener(t *testing.T) {
listener, err := net.Listen("tcp4", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen tcp4 failed: %v", err)
}
defer func() {
_ = listener.Close()
}()
port := listener.Addr().(*net.TCPAddr).Port
row := waitForTCP4OwnerPIDRow(t, uint32(os.Getpid()), port)
if row.State != MIB_TCP_STATE_LISTEN {
t.Fatalf("unexpected tcp state: got=%d want=%d", row.State, MIB_TCP_STATE_LISTEN)
}
}
func TestGetExtendedUdp4TableIncludesCurrentSocket(t *testing.T) {
packetConn, err := net.ListenPacket("udp4", "127.0.0.1:0")
if err != nil {
t.Fatalf("listen udp4 failed: %v", err)
}
defer func() {
_ = packetConn.Close()
}()
port := packetConn.LocalAddr().(*net.UDPAddr).Port
_ = waitForUDP4OwnerPIDRow(t, uint32(os.Getpid()), port)
}
func TestGetExtendedTcp6TableIncludesCurrentListener(t *testing.T) {
listener, err := net.Listen("tcp6", "[::1]:0")
if err != nil {
t.Skipf("tcp6 unavailable: %v", err)
}
defer func() {
_ = listener.Close()
}()
port := listener.Addr().(*net.TCPAddr).Port
row := waitForTCP6OwnerPIDRow(t, uint32(os.Getpid()), port)
if row.State != MIB_TCP_STATE_LISTEN {
t.Fatalf("unexpected tcp6 state: got=%d want=%d", row.State, MIB_TCP_STATE_LISTEN)
}
}
func TestGetExtendedUdp6TableIncludesCurrentSocket(t *testing.T) {
packetConn, err := net.ListenPacket("udp6", "[::1]:0")
if err != nil {
t.Skipf("udp6 unavailable: %v", err)
}
defer func() {
_ = packetConn.Close()
}()
port := packetConn.LocalAddr().(*net.UDPAddr).Port
_ = waitForUDP6OwnerPIDRow(t, uint32(os.Getpid()), port)
}
func waitForTCP4OwnerPIDRow(t *testing.T, pid uint32, port int) MIB_TCPROW_OWNER_PID {
t.Helper()
deadline := time.Now().Add(2 * time.Second)
for time.Now().Before(deadline) {
rows, err := GetExtendedTcp4Table(false, TCP_TABLE_OWNER_PID_ALL)
if err != nil {
t.Fatalf("GetExtendedTcp4Table failed: %v", err)
}
for _, row := range rows {
if row.OwningPid == pid && int(row.LocalPortHost()) == port {
return row
}
}
time.Sleep(50 * time.Millisecond)
}
t.Fatalf("did not find tcp4 row for pid=%d port=%d", pid, port)
return MIB_TCPROW_OWNER_PID{}
}
func waitForTCP6OwnerPIDRow(t *testing.T, pid uint32, port int) MIB_TCP6ROW_OWNER_PID {
t.Helper()
deadline := time.Now().Add(2 * time.Second)
for time.Now().Before(deadline) {
rows, err := GetExtendedTcp6Table(false, TCP_TABLE_OWNER_PID_ALL)
if err != nil {
t.Fatalf("GetExtendedTcp6Table failed: %v", err)
}
for _, row := range rows {
if row.OwningPid == pid && int(row.LocalPortHost()) == port {
return row
}
}
time.Sleep(50 * time.Millisecond)
}
t.Fatalf("did not find tcp6 row for pid=%d port=%d", pid, port)
return MIB_TCP6ROW_OWNER_PID{}
}
func waitForUDP4OwnerPIDRow(t *testing.T, pid uint32, port int) MIB_UDPROW_OWNER_PID {
t.Helper()
deadline := time.Now().Add(2 * time.Second)
for time.Now().Before(deadline) {
rows, err := GetExtendedUdp4Table(false, UDP_TABLE_OWNER_PID)
if err != nil {
t.Fatalf("GetExtendedUdp4Table failed: %v", err)
}
for _, row := range rows {
if row.OwningPid == pid && int(row.LocalPortHost()) == port {
return row
}
}
time.Sleep(50 * time.Millisecond)
}
t.Fatalf("did not find udp4 row for pid=%d port=%d", pid, port)
return MIB_UDPROW_OWNER_PID{}
}
func waitForUDP6OwnerPIDRow(t *testing.T, pid uint32, port int) MIB_UDP6ROW_OWNER_PID {
t.Helper()
deadline := time.Now().Add(2 * time.Second)
for time.Now().Before(deadline) {
rows, err := GetExtendedUdp6Table(false, UDP_TABLE_OWNER_PID)
if err != nil {
t.Fatalf("GetExtendedUdp6Table failed: %v", err)
}
for _, row := range rows {
if row.OwningPid == pid && int(row.LocalPortHost()) == port {
return row
}
}
time.Sleep(50 * time.Millisecond)
}
t.Fatalf("did not find udp6 row for pid=%d port=%d", pid, port)
return MIB_UDP6ROW_OWNER_PID{}
}