From c737259113f99f675ebf3b4f3e408f6e4a838e95 Mon Sep 17 00:00:00 2001 From: Ahmet Ozturk Date: Fri, 17 Jul 2026 10:45:58 +0200 Subject: [PATCH 1/2] check_connections: on windows, use win-netstat library the library uses iphlpapi.dll functions GetTcpTable2, GetTcp6Table2, GetExtendedTcpTable. it also has GetExtendedUdpTable , but they are not used. the library transforms the connection states to english in common.go . the strings are always in english and not localized. this means check_connection can test for english strings in output without worry. --- go.mod | 1 + go.sum | 2 + pkg/snclient/check_connections_windows.go | 48 ++++++++++------------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index dbed1f5d..da6a4492 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/miekg/dns v1.1.72 github.com/otiai10/copy v1.14.1 github.com/prometheus/client_golang v1.23.2 + github.com/pytimer/win-netstat v0.0.0-20180710031115-efa1aff6aafc github.com/reeflective/readline v1.3.0 github.com/sasha-s/go-deadlock v0.3.9 github.com/sassoftware/go-rpmutils v0.4.0 diff --git a/go.sum b/go.sum index b3300a0e..2c1df0ac 100644 --- a/go.sum +++ b/go.sum @@ -83,6 +83,8 @@ github.com/prometheus/common v0.68.1 h1:omjRRl4QP4komogpXuhfeOiisQg7xdy8VM1UY+pS github.com/prometheus/common v0.68.1/go.mod h1:ZzL3f6u94qUxh9p+tJTrF+FvBS1XXbbRAZCQkytAL0Y= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= +github.com/pytimer/win-netstat v0.0.0-20180710031115-efa1aff6aafc h1:NJabarGmZw//DrC1ixmeUOZgmrGnbXL8/IN4CHOeckM= +github.com/pytimer/win-netstat v0.0.0-20180710031115-efa1aff6aafc/go.mod h1:XrlGxma7SaoqMKNMYaUpU3l24A/5Lytk6243pea+K/4= github.com/reeflective/readline v1.3.0 h1:uh9c2SEmyoy7A/auequfXZjvK0NP5HVEAJFcL9Uf7qE= github.com/reeflective/readline v1.3.0/go.mod h1:bOpqx2/VqGlIoobyWR1Vgt/p5FiMfIHj4OicPuw6RfU= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= diff --git a/pkg/snclient/check_connections_windows.go b/pkg/snclient/check_connections_windows.go index 250635f4..9bdcd7d1 100644 --- a/pkg/snclient/check_connections_windows.go +++ b/pkg/snclient/check_connections_windows.go @@ -3,12 +3,13 @@ package snclient import ( "context" "fmt" - "strings" + + winnetstat "github.com/pytimer/win-netstat" ) -// get open tcp connections from netstat.exe -func (l *CheckConnections) addIPV4(ctx context.Context, check *CheckData) error { - counter, err := l.getNetstat(ctx, "TCP") +// get open tcp connections from the windows iphlpapi via win-netstat library +func (l *CheckConnections) addIPV4(_ context.Context, check *CheckData) error { + counter, err := l.getNetstat("tcp4") if err != nil { return err } @@ -17,8 +18,8 @@ func (l *CheckConnections) addIPV4(ctx context.Context, check *CheckData) error return nil } -func (l *CheckConnections) addIPV6(ctx context.Context, check *CheckData) error { - counter, err := l.getNetstat(ctx, "TCPv6") +func (l *CheckConnections) addIPV6(_ context.Context, check *CheckData) error { + counter, err := l.getNetstat("tcp6") if err != nil { return err } @@ -27,32 +28,25 @@ func (l *CheckConnections) addIPV6(ctx context.Context, check *CheckData) error return nil } -func (l *CheckConnections) getNetstat(ctx context.Context, name string) ([]uint64, error) { - output, stderr, rc, err := l.snc.execCommand(ctx, "netstat.exe /a /n /p "+name, l.snc.getBuiltinCmdTimeout()) +func (l *CheckConnections) getNetstat(kind string) ([]uint64, error) { + connections, err := winnetstat.Connections(kind) if err != nil { - return nil, fmt.Errorf("netstat.exe failed: %s\n%s", err.Error(), stderr) - } - if rc != 0 { - return nil, fmt.Errorf("netstat.exe failed: %s\n%s", output, stderr) + return nil, fmt.Errorf("fetching %s connections failed with error: %s", kind, err.Error()) } counter := make([]uint64, tcpStateMAX-1) - for line := range strings.SplitSeq(output, "\n") { - cols := strings.Fields(line) - if len(cols) < 4 { - continue - } - if cols[0] != "TCP" { - continue - } - - // available states: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/netstat#remarks - switch cols[3] { + for idx := range connections { + // available states: https://learn.microsoft.com/en-us/windows/win32/api/tcpmib/ne-tcpmib-mib_tcp_state + // State object is saved as english string, converted in win-netstat\common.go + switch connections[idx].State { case "CLOSE_WAIT": counter[tcpCloseWait]++ - case "CLOSED": + // Deleted counts as closed as well + case "CLOSED", "DELETE": counter[tcpClose]++ + case "CLOSING": + counter[tcpClosing]++ case "ESTABLISHED": counter[tcpEstablished]++ case "FIN_WAIT_1": @@ -61,16 +55,16 @@ func (l *CheckConnections) getNetstat(ctx context.Context, name string) ([]uint6 counter[tcpFinWait2]++ case "LAST_ACK": counter[tcpLastAck]++ - case "LISTEN", "LISTENING": + case "LISTEN": counter[tcpListen]++ case "SYN_RECEIVED": counter[tcpSynRecv]++ case "SYN_SENT": counter[tcpSynSent]++ - case "TIMED_WAIT", "TIME_WAIT": + case "TIME_WAIT": counter[tcpTimeWait]++ default: - log.Errorf("unhandled tcp state: %s", cols[3]) + log.Tracef("unknown tcp state: %s", connections[idx].State) } counter[tcpTotal]++ } From c0d85f4b50661207b67ce7541c02408d4a37645d Mon Sep 17 00:00:00 2001 From: Ahmet Ozturk Date: Fri, 17 Jul 2026 14:59:04 +0200 Subject: [PATCH 2/2] check_connection: use gopsutil instead of win-netstat gopsutil already has the same functionality and uses same windows api calls. --- go.mod | 1 - go.sum | 2 -- pkg/snclient/check_connections_windows.go | 22 +++++++++++----------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index da6a4492..dbed1f5d 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ require ( github.com/miekg/dns v1.1.72 github.com/otiai10/copy v1.14.1 github.com/prometheus/client_golang v1.23.2 - github.com/pytimer/win-netstat v0.0.0-20180710031115-efa1aff6aafc github.com/reeflective/readline v1.3.0 github.com/sasha-s/go-deadlock v0.3.9 github.com/sassoftware/go-rpmutils v0.4.0 diff --git a/go.sum b/go.sum index 2c1df0ac..b3300a0e 100644 --- a/go.sum +++ b/go.sum @@ -83,8 +83,6 @@ github.com/prometheus/common v0.68.1 h1:omjRRl4QP4komogpXuhfeOiisQg7xdy8VM1UY+pS github.com/prometheus/common v0.68.1/go.mod h1:ZzL3f6u94qUxh9p+tJTrF+FvBS1XXbbRAZCQkytAL0Y= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= -github.com/pytimer/win-netstat v0.0.0-20180710031115-efa1aff6aafc h1:NJabarGmZw//DrC1ixmeUOZgmrGnbXL8/IN4CHOeckM= -github.com/pytimer/win-netstat v0.0.0-20180710031115-efa1aff6aafc/go.mod h1:XrlGxma7SaoqMKNMYaUpU3l24A/5Lytk6243pea+K/4= github.com/reeflective/readline v1.3.0 h1:uh9c2SEmyoy7A/auequfXZjvK0NP5HVEAJFcL9Uf7qE= github.com/reeflective/readline v1.3.0/go.mod h1:bOpqx2/VqGlIoobyWR1Vgt/p5FiMfIHj4OicPuw6RfU= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= diff --git a/pkg/snclient/check_connections_windows.go b/pkg/snclient/check_connections_windows.go index 9bdcd7d1..84a85c71 100644 --- a/pkg/snclient/check_connections_windows.go +++ b/pkg/snclient/check_connections_windows.go @@ -4,12 +4,12 @@ import ( "context" "fmt" - winnetstat "github.com/pytimer/win-netstat" + "github.com/shirou/gopsutil/v4/net" ) -// get open tcp connections from the windows iphlpapi via win-netstat library -func (l *CheckConnections) addIPV4(_ context.Context, check *CheckData) error { - counter, err := l.getNetstat("tcp4") +// get open tcp connections from the windows iphlpapi via gopsutil library +func (l *CheckConnections) addIPV4(ctx context.Context, check *CheckData) error { + counter, err := l.getNetstat(ctx, "tcp4") if err != nil { return err } @@ -18,8 +18,8 @@ func (l *CheckConnections) addIPV4(_ context.Context, check *CheckData) error { return nil } -func (l *CheckConnections) addIPV6(_ context.Context, check *CheckData) error { - counter, err := l.getNetstat("tcp6") +func (l *CheckConnections) addIPV6(ctx context.Context, check *CheckData) error { + counter, err := l.getNetstat(ctx, "tcp6") if err != nil { return err } @@ -28,8 +28,8 @@ func (l *CheckConnections) addIPV6(_ context.Context, check *CheckData) error { return nil } -func (l *CheckConnections) getNetstat(kind string) ([]uint64, error) { - connections, err := winnetstat.Connections(kind) +func (l *CheckConnections) getNetstat(ctx context.Context, kind string) ([]uint64, error) { + connections, err := net.ConnectionsWithContext(ctx, kind) if err != nil { return nil, fmt.Errorf("fetching %s connections failed with error: %s", kind, err.Error()) } @@ -38,8 +38,8 @@ func (l *CheckConnections) getNetstat(kind string) ([]uint64, error) { for idx := range connections { // available states: https://learn.microsoft.com/en-us/windows/win32/api/tcpmib/ne-tcpmib-mib_tcp_state - // State object is saved as english string, converted in win-netstat\common.go - switch connections[idx].State { + // Status is a fixed english string, converted from the numeric tcp state in gopsutil net_windows.go + switch connections[idx].Status { case "CLOSE_WAIT": counter[tcpCloseWait]++ // Deleted counts as closed as well @@ -64,7 +64,7 @@ func (l *CheckConnections) getNetstat(kind string) ([]uint64, error) { case "TIME_WAIT": counter[tcpTimeWait]++ default: - log.Tracef("unknown tcp state: %s", connections[idx].State) + log.Tracef("unknown tcp state: %s", connections[idx].Status) } counter[tcpTotal]++ }