Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add windows compatibility for diagnose port-conflict #27457

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5ee195c
initial commit
mwdd146980 Jul 9, 2024
177817f
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Jul 9, 2024
d8f4eb0
add darwin specific file for netstat to pass go linting on mac
mwdd146980 Jul 9, 2024
7cb6807
adding nolint:revive comment to _MIB_TCP6TABLE_OWNER_MODULE struct
mwdd146980 Jul 9, 2024
2b203bf
remove conditional in runner.go that only registers port-conflict for…
mwdd146980 Jul 11, 2024
c321094
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Jul 15, 2024
e4d9e9c
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Jul 22, 2024
d2c5389
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Jul 29, 2024
420f32a
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Aug 5, 2024
d9894d5
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Aug 12, 2024
ddc655d
adding 'agent.exe' as a valid agent name, adding condition for when p…
mwdd146980 Aug 12, 2024
3a9f5f4
add PID to diagnosis message for windows specific conditional
mwdd146980 Aug 12, 2024
e483815
add process-agent.exe and trace-agent.exe as recognized agent proc names
mwdd146980 Aug 12, 2024
ef40577
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Aug 26, 2024
823bf4a
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Aug 30, 2024
8de2ecb
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Sep 4, 2024
ace4274
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Sep 11, 2024
53795f4
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Sep 26, 2024
d50324a
change two diagnosis fails to warnings in ports.go
mwdd146980 Sep 26, 2024
c347676
undo previous commit
mwdd146980 Sep 27, 2024
f1ca435
undo previous commit
mwdd146980 Oct 4, 2024
ce01a14
add conditional to skip apm agent and process agent if on windows
mwdd146980 Oct 4, 2024
cf5fd46
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Oct 4, 2024
643cd54
adding temp test to pkg/config/setup/config_test.go to get AllKeysLow…
mwdd146980 Oct 4, 2024
6c849cb
modifying windows conditional to check Key and not KeyName
mwdd146980 Oct 9, 2024
309b8fa
Merge remote-tracking branch 'origin/main' into mwdd146980/port-confl…
mwdd146980 Oct 9, 2024
1120fe1
add port conflict suite to diagnose_common_test.go
mwdd146980 Oct 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ require (
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
golang.org/x/net v0.26.0
golang.org/x/sync v0.7.0
golang.org/x/sys v0.21.0
golang.org/x/sys v0.22.0
golang.org/x/text v0.16.0
golang.org/x/time v0.5.0
golang.org/x/tools v0.22.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

135 changes: 17 additions & 118 deletions pkg/util/port/portlist/netstat.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,132 +3,31 @@
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2014-present Datadog, Inc.

//go:build darwin

package portlist

import (
"bufio"
"bytes"
"io"

"go4.org/mem"
"net/netip"
)

// parsePort returns the port number at the end of s following the last "." or
// ":", whichever comes last. It returns -1 on a parse error or invalid number
// and 0 if the port number was "*".
//
// This is basically net.SplitHostPort except that it handles a "." (as macOS
// and others return in netstat output), uses mem.RO, and validates that the
// port must be numeric and in the uint16 range.
func parsePort(s mem.RO) int {
// a.b.c.d:1234 or [a:b:c:d]:1234
i1 := mem.LastIndexByte(s, ':')
// a.b.c.d.1234 or [a:b:c:d].1234
i2 := mem.LastIndexByte(s, '.')

i := i1
if i2 > i {
i = i2
}
if i < 0 {
// no match; weird
return -1
}

portstr := s.SliceFrom(i + 1)
if portstr.EqualString("*") {
return 0
}

port, err := mem.ParseUint(portstr, 10, 16)
if err != nil {
// invalid port; weird
return -1
}

return int(port)
// Entry is a single entry in the connection table.
type Entry struct {
Local, Remote netip.AddrPort
Pid int
State string
OSMetadata OSMetadata
}

func isLoopbackAddr(s mem.RO) bool {
return mem.HasPrefix(s, mem.S("127.")) ||
mem.HasPrefix(s, mem.S("[::1]:")) ||
mem.HasPrefix(s, mem.S("::1."))
// Table contains local machine's TCP connection entries.
//
// Currently only TCP (IPv4 and IPv6) are included.
type Table struct {
Entries []Entry
}

// appendParsePortsNetstat appends to base listening ports
// from "netstat" output, read from br. See TestParsePortsNetstat
// for example input lines.
// GetConnTable returns the connection table.
//
// This used to be a lowest common denominator parser for "netstat -na" format.
// All of Linux, Windows, and macOS support -na and give similar-ish output
// formats that we can parse without special detection logic.
// Unfortunately, options to filter by proto or state are non-portable,
// so we'll filter for ourselves.
// Nowadays, though, we only use it for macOS as of 2022-11-04.
func appendParsePortsNetstat(base []Port, br *bufio.Reader, includeLocalhost bool) ([]Port, error) {
ret := base
var fieldBuf [10]mem.RO
for {
line, err := br.ReadBytes('\n')
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
trimline := bytes.TrimSpace(line)
cols := mem.AppendFields(fieldBuf[:0], mem.B(trimline))
if len(cols) < 1 {
continue
}
protos := cols[0]

var proto string
var laddr, raddr mem.RO
if mem.HasPrefixFold(protos, mem.S("tcp")) {
if len(cols) < 4 {
continue
}
proto = "tcp"
laddr = cols[len(cols)-3]
raddr = cols[len(cols)-2]
state := cols[len(cols)-1]
if !mem.HasPrefix(state, mem.S("LISTEN")) {
// not interested in non-listener sockets
continue
}
if !includeLocalhost && isLoopbackAddr(laddr) {
// not interested in loopback-bound listeners
continue
}
} else if mem.HasPrefixFold(protos, mem.S("udp")) {
if len(cols) < 3 {
continue
}
proto = "udp"
laddr = cols[len(cols)-2]
raddr = cols[len(cols)-1]
if !includeLocalhost && isLoopbackAddr(laddr) {
// not interested in loopback-bound listeners
continue
}
} else {
// not interested in other protocols
continue
}

lport := parsePort(laddr)
rport := parsePort(raddr)
if rport > 0 || lport <= 0 {
// not interested in "connected" sockets
continue
}
ret = append(ret, Port{
Proto: proto,
Port: uint16(lport),
})
}
return ret, nil
// It returns ErrNotImplemented if the table is not available for the
// current operating system.
func GetConnTable() (*Table, error) {
return getConnTable()
}
148 changes: 148 additions & 0 deletions pkg/util/port/portlist/netstat_macos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.

//go:build darwin

package portlist

import (
"bufio"
"bytes"
"errors"
"io"

"go4.org/mem"
)

// ErrNotImplemented is the "not implemented" error given by `gopsutil` when an
// OS doesn't support and API. Unfortunately it's in an internal package so
// we can't import it so we'll copy it here.
var ErrNotImplemented = errors.New("not implemented yet")

// OSMetadata includes any additional OS-specific information that may be
// obtained during the retrieval of a given Entry.
type OSMetadata struct{}

// parsePort returns the port number at the end of s following the last "." or
// ":", whichever comes last. It returns -1 on a parse error or invalid number
// and 0 if the port number was "*".
//
// This is basically net.SplitHostPort except that it handles a "." (as macOS
// and others return in netstat output), uses mem.RO, and validates that the
// port must be numeric and in the uint16 range.
func parsePort(s mem.RO) int {
// a.b.c.d:1234 or [a:b:c:d]:1234
i1 := mem.LastIndexByte(s, ':')
// a.b.c.d.1234 or [a:b:c:d].1234
i2 := mem.LastIndexByte(s, '.')

i := i1
if i2 > i {
i = i2
}
if i < 0 {
// no match; weird
return -1
}

portstr := s.SliceFrom(i + 1)
if portstr.EqualString("*") {
return 0
}

port, err := mem.ParseUint(portstr, 10, 16)
if err != nil {
// invalid port; weird
return -1
}

return int(port)
}

func isLoopbackAddr(s mem.RO) bool {
return mem.HasPrefix(s, mem.S("127.")) ||
mem.HasPrefix(s, mem.S("[::1]:")) ||
mem.HasPrefix(s, mem.S("::1."))
}

// appendParsePortsNetstat appends to base listening ports
// from "netstat" output, read from br. See TestParsePortsNetstat
// for example input lines.
//
// This used to be a lowest common denominator parser for "netstat -na" format.
// All of Linux, Windows, and macOS support -na and give similar-ish output
// formats that we can parse without special detection logic.
// Unfortunately, options to filter by proto or state are non-portable,
// so we'll filter for ourselves.
// Nowadays, though, we only use it for macOS as of 2022-11-04.
func appendParsePortsNetstat(base []Port, br *bufio.Reader, includeLocalhost bool) ([]Port, error) {
ret := base
var fieldBuf [10]mem.RO
for {
line, err := br.ReadBytes('\n')
if err != nil {
if err == io.EOF {
break
}
return nil, err
}
trimline := bytes.TrimSpace(line)
cols := mem.AppendFields(fieldBuf[:0], mem.B(trimline))
if len(cols) < 1 {
continue
}
protos := cols[0]

var proto string
var laddr, raddr mem.RO
if mem.HasPrefixFold(protos, mem.S("tcp")) {
if len(cols) < 4 {
continue
}
proto = "tcp"
laddr = cols[len(cols)-3]
raddr = cols[len(cols)-2]
state := cols[len(cols)-1]
if !mem.HasPrefix(state, mem.S("LISTEN")) {
// not interested in non-listener sockets
continue
}
if !includeLocalhost && isLoopbackAddr(laddr) {
// not interested in loopback-bound listeners
continue
}
} else if mem.HasPrefixFold(protos, mem.S("udp")) {
if len(cols) < 3 {
continue
}
proto = "udp"
laddr = cols[len(cols)-2]
raddr = cols[len(cols)-1]
if !includeLocalhost && isLoopbackAddr(laddr) {
// not interested in loopback-bound listeners
continue
}
} else {
// not interested in other protocols
continue
}

lport := parsePort(laddr)
rport := parsePort(raddr)
if rport > 0 || lport <= 0 {
// not interested in "connected" sockets
continue
}
ret = append(ret, Port{
Proto: proto,
Port: uint16(lport),
})
}
return ret, nil
}

func getConnTable() (*Table, error) {
return nil, ErrNotImplemented
}
23 changes: 23 additions & 0 deletions pkg/util/port/portlist/netstat_noimpl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2014-present Datadog, Inc.

//go:build !windows && !darwin

package portlist

import "errors"

// ErrNotImplemented is the "not implemented" error given by `gopsutil` when an
// OS doesn't support and API. Unfortunately it's in an internal package so
// we can't import it so we'll copy it here.
var ErrNotImplemented = errors.New("not implemented yet")

// OSMetadata includes any additional OS-specific information that may be
// obtained during the retrieval of a given Entry.
type OSMetadata struct{}

func getConnTable() (*Table, error) {
return nil, ErrNotImplemented
}
Loading
Loading