netstat, portlist: update Windows implementation to disambiguate svchost processes

We change our invocations of GetExtendedTcpTable to request additional
information about the "module" responsible for the port. In addition to pid,
this output also includes sufficient metadata to enable Windows to resolve
process names and disambiguate svchost processes.

We store the OS-specific output in an OSMetadata field in netstat.Entry, which
portlist may then use as necessary to actually resolve the process/module name.

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
This commit is contained in:
Aaron Klotz
2022-11-18 13:09:01 -07:00
parent a06217a8bd
commit 296f53524c
4 changed files with 152 additions and 85 deletions

View File

@@ -5,12 +5,8 @@
package portlist
import (
"path/filepath"
"strings"
"syscall"
"time"
"golang.org/x/sys/windows"
"tailscale.com/net/netstat"
)
@@ -26,7 +22,7 @@ func init() {
type famPort struct {
proto string
port uint16
pid uintptr
pid uint32
}
type windowsImpl struct {
@@ -69,19 +65,25 @@ func (im *windowsImpl) AppendListeningPorts(base []Port) ([]Port, error) {
fp := famPort{
proto: "tcp", // TODO(bradfitz): UDP too; add to netstat
port: e.Local.Port(),
pid: uintptr(e.Pid),
pid: uint32(e.Pid),
}
pm, ok := im.known[fp]
if ok {
pm.keep = true
continue
}
var process string
if e.OSMetadata != nil {
if module, err := e.OSMetadata.GetModule(); err == nil {
process = module
}
}
pm = &portMeta{
keep: true,
port: Port{
Proto: "tcp",
Port: e.Local.Port(),
Process: procNameOfPid(e.Pid),
Process: process,
},
}
im.known[fp] = pm
@@ -94,27 +96,6 @@ func (im *windowsImpl) AppendListeningPorts(base []Port) ([]Port, error) {
}
ret = append(ret, m.port)
}
return sortAndDedup(ret), nil
}
func procNameOfPid(pid int) string {
const da = windows.PROCESS_QUERY_LIMITED_INFORMATION
h, err := syscall.OpenProcess(da, false, uint32(pid))
if err != nil {
return ""
}
defer syscall.CloseHandle(h)
var buf [512]uint16
var size = uint32(len(buf))
if err := windows.QueryFullProcessImageName(windows.Handle(h), 0, &buf[0], &size); err != nil {
return ""
}
name := filepath.Base(windows.UTF16ToString(buf[:]))
if name == "." {
return ""
}
name = strings.TrimSuffix(name, ".exe")
name = strings.TrimSuffix(name, ".EXE")
return name
}