2020-09-10 22:21:32 +00:00
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package netstat returns the local machine's network connection table.
package netstat
import (
"errors"
"fmt"
2020-11-15 05:24:09 +00:00
"math/bits"
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 04:14:09 +00:00
"net/netip"
2020-09-10 22:21:32 +00:00
"unsafe"
2022-12-13 03:40:44 +00:00
"github.com/josharian/native"
2020-09-10 22:21:32 +00:00
"golang.org/x/sys/windows"
2022-07-25 03:08:42 +00:00
"tailscale.com/net/netaddr"
2020-09-10 22:21:32 +00:00
)
2022-11-18 20:09:01 +00:00
// OSMetadata includes any additional OS-specific information that may be
// obtained during the retrieval of a given Entry.
type OSMetadata interface {
GetModule ( ) ( string , error )
}
2020-09-10 22:21:32 +00:00
// See https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getextendedtcptable
2022-11-18 20:09:01 +00:00
// TCP_TABLE_OWNER_MODULE_ALL means to include the PID and module. The table type
2020-09-10 22:21:32 +00:00
// we get back from Windows depends on AF_INET vs AF_INET6:
2022-11-18 20:09:01 +00:00
// MIB_TCPTABLE_OWNER_MODULE for v4 or MIB_TCP6TABLE_OWNER_MODULE for v6.
const tcpTableOwnerModuleAll = 8
// TCPIP_OWNER_MODULE_BASIC_INFO means to request "basic information" about the
// owner module.
const tcpipOwnerModuleBasicInfo = 0
2020-09-10 22:21:32 +00:00
var (
2022-11-18 20:09:01 +00:00
iphlpapi = windows . NewLazySystemDLL ( "iphlpapi.dll" )
getTCPTable = iphlpapi . NewProc ( "GetExtendedTcpTable" )
getOwnerModuleFromTcpEntry = iphlpapi . NewProc ( "GetOwnerModuleFromTcpEntry" )
getOwnerModuleFromTcp6Entry = iphlpapi . NewProc ( "GetOwnerModuleFromTcp6Entry" )
2020-09-10 22:21:32 +00:00
// TODO: GetExtendedUdpTable also? if/when needed.
)
2022-11-18 20:09:01 +00:00
// See https://web.archive.org/web/20221219211913/https://learn.microsoft.com/en-us/windows/win32/api/tcpmib/ns-tcpmib-mib_tcprow_owner_module
type _MIB_TCPROW_OWNER_MODULE struct {
state uint32
localAddr uint32
localPort uint32
remoteAddr uint32
remotePort uint32
pid uint32
createTimestamp int64
owningModuleInfo [ 16 ] uint64
}
func ( row * _MIB_TCPROW_OWNER_MODULE ) asEntry ( ) Entry {
return Entry {
Local : ipport4 ( row . localAddr , port ( & row . localPort ) ) ,
Remote : ipport4 ( row . remoteAddr , port ( & row . remotePort ) ) ,
Pid : int ( row . pid ) ,
State : state ( row . state ) ,
OSMetadata : row ,
}
}
type _MIB_TCPTABLE_OWNER_MODULE struct {
numEntries uint32
table _MIB_TCPROW_OWNER_MODULE
}
func ( m * _MIB_TCPTABLE_OWNER_MODULE ) getRows ( ) [ ] _MIB_TCPROW_OWNER_MODULE {
return unsafe . Slice ( & m . table , m . numEntries )
}
// See https://web.archive.org/web/20221219212442/https://learn.microsoft.com/en-us/windows/win32/api/tcpmib/ns-tcpmib-mib_tcp6row_owner_module
type _MIB_TCP6ROW_OWNER_MODULE struct {
localAddr [ 16 ] byte
localScope uint32
localPort uint32
remoteAddr [ 16 ] byte
remoteScope uint32
remotePort uint32
state uint32
pid uint32
createTimestamp int64
owningModuleInfo [ 16 ] uint64
}
func ( row * _MIB_TCP6ROW_OWNER_MODULE ) asEntry ( ) Entry {
return Entry {
Local : ipport6 ( row . localAddr , row . localScope , port ( & row . localPort ) ) ,
Remote : ipport6 ( row . remoteAddr , row . remoteScope , port ( & row . remotePort ) ) ,
Pid : int ( row . pid ) ,
State : state ( row . state ) ,
OSMetadata : row ,
}
}
type _MIB_TCP6TABLE_OWNER_MODULE struct {
numEntries uint32
table _MIB_TCP6ROW_OWNER_MODULE
2020-09-10 22:21:32 +00:00
}
2022-11-18 20:09:01 +00:00
func ( m * _MIB_TCP6TABLE_OWNER_MODULE ) getRows ( ) [ ] _MIB_TCP6ROW_OWNER_MODULE {
return unsafe . Slice ( & m . table , m . numEntries )
}
// See https://web.archive.org/web/20221219213143/https://learn.microsoft.com/en-us/windows/win32/api/iprtrmib/ns-iprtrmib-tcpip_owner_module_basic_info
type _TCPIP_OWNER_MODULE_BASIC_INFO struct {
moduleName * uint16
modulePath * uint16
2020-09-10 22:21:32 +00:00
}
func get ( ) ( * Table , error ) {
t := new ( Table )
if err := t . addEntries ( windows . AF_INET ) ; err != nil {
return nil , fmt . Errorf ( "failed to get IPv4 entries: %w" , err )
}
if err := t . addEntries ( windows . AF_INET6 ) ; err != nil {
return nil , fmt . Errorf ( "failed to get IPv6 entries: %w" , err )
}
return t , nil
}
func ( t * Table ) addEntries ( fam int ) error {
var size uint32
var addr unsafe . Pointer
var buf [ ] byte
for {
err , _ , _ := getTCPTable . Call (
uintptr ( addr ) ,
uintptr ( unsafe . Pointer ( & size ) ) ,
1 , // sorted
uintptr ( fam ) ,
2022-11-18 20:09:01 +00:00
tcpTableOwnerModuleAll ,
2020-09-10 22:21:32 +00:00
0 , // reserved; "must be zero"
)
if err == 0 {
break
}
2022-11-18 20:09:01 +00:00
if err == uintptr ( windows . ERROR_INSUFFICIENT_BUFFER ) {
2020-09-10 22:21:32 +00:00
const maxSize = 10 << 20
if size > maxSize || size < 4 {
return fmt . Errorf ( "unreasonable kernel-reported size %d" , size )
}
buf = make ( [ ] byte , size )
addr = unsafe . Pointer ( & buf [ 0 ] )
continue
}
2022-11-18 20:09:01 +00:00
return windows . Errno ( err )
2020-09-10 22:21:32 +00:00
}
if len ( buf ) < int ( size ) {
return errors . New ( "unexpected size growth from system call" )
}
buf = buf [ : size ]
switch fam {
case windows . AF_INET :
2022-11-18 20:09:01 +00:00
info := ( * _MIB_TCPTABLE_OWNER_MODULE ) ( unsafe . Pointer ( & buf [ 0 ] ) )
rows := info . getRows ( )
for _ , row := range rows {
t . Entries = append ( t . Entries , row . asEntry ( ) )
}
2020-09-10 22:21:32 +00:00
case windows . AF_INET6 :
2022-11-18 20:09:01 +00:00
info := ( * _MIB_TCP6TABLE_OWNER_MODULE ) ( unsafe . Pointer ( & buf [ 0 ] ) )
rows := info . getRows ( )
for _ , row := range rows {
t . Entries = append ( t . Entries , row . asEntry ( ) )
2020-09-10 22:21:32 +00:00
}
}
2022-11-18 20:09:01 +00:00
2020-09-10 22:21:32 +00:00
return nil
}
var states = [ ] string {
"" ,
"CLOSED" ,
"LISTEN" ,
"SYN-SENT" ,
"SYN-RECEIVED" ,
"ESTABLISHED" ,
"FIN-WAIT-1" ,
"FIN-WAIT-2" ,
"CLOSE-WAIT" ,
"CLOSING" ,
"LAST-ACK" ,
"DELETE-TCB" ,
}
func state ( v uint32 ) string {
if v < uint32 ( len ( states ) ) {
return states [ v ]
}
return fmt . Sprintf ( "unknown-state-%d" , v )
}
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 04:14:09 +00:00
func ipport4 ( addr uint32 , port uint16 ) netip . AddrPort {
2022-12-13 03:40:44 +00:00
if ! native . IsBigEndian {
2020-11-15 05:24:09 +00:00
addr = bits . ReverseBytes32 ( addr )
}
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 04:14:09 +00:00
return netip . AddrPortFrom (
2021-05-15 01:07:28 +00:00
netaddr . IPv4 ( byte ( addr >> 24 ) , byte ( addr >> 16 ) , byte ( addr >> 8 ) , byte ( addr ) ) ,
port )
2020-09-10 22:21:32 +00:00
}
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 04:14:09 +00:00
func ipport6 ( addr [ 16 ] byte , scope uint32 , port uint16 ) netip . AddrPort {
2022-08-02 20:38:11 +00:00
ip := netip . AddrFrom16 ( addr ) . Unmap ( )
2020-09-10 22:21:32 +00:00
if scope != 0 {
// TODO: something better here?
ip = ip . WithZone ( fmt . Sprint ( scope ) )
}
all: convert more code to use net/netip directly
perl -i -npe 's,netaddr.IPPrefixFrom,netip.PrefixFrom,' $(git grep -l -F netaddr.)
perl -i -npe 's,netaddr.IPPortFrom,netip.AddrPortFrom,' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPrefix,netip.Prefix,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPPort,netip.AddrPort,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IP\b,netip.Addr,g' $(git grep -l -F netaddr. )
perl -i -npe 's,netaddr.IPv6Raw\b,netip.AddrFrom16,g' $(git grep -l -F netaddr. )
goimports -w .
Then delete some stuff from the net/netaddr shim package which is no
longer neeed.
Updates #5162
Change-Id: Ia7a86893fe21c7e3ee1ec823e8aba288d4566cd8
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2022-07-26 04:14:09 +00:00
return netip . AddrPortFrom ( ip , port )
2020-09-10 22:21:32 +00:00
}
func port ( v * uint32 ) uint16 {
2022-12-13 03:40:44 +00:00
if ! native . IsBigEndian {
2020-11-15 05:24:09 +00:00
return uint16 ( bits . ReverseBytes32 ( * v ) >> 16 )
}
return uint16 ( * v >> 16 )
2020-09-10 22:21:32 +00:00
}
2022-11-18 20:09:01 +00:00
type moduleInfoConstraint interface {
_MIB_TCPROW_OWNER_MODULE | _MIB_TCP6ROW_OWNER_MODULE
}
func moduleInfo [ entryType moduleInfoConstraint ] ( entry * entryType , proc * windows . LazyProc ) ( string , error ) {
var buf [ ] byte
var desiredLen uint32
var addr unsafe . Pointer
for {
e , _ , _ := proc . Call (
uintptr ( unsafe . Pointer ( entry ) ) ,
uintptr ( tcpipOwnerModuleBasicInfo ) ,
uintptr ( addr ) ,
uintptr ( unsafe . Pointer ( & desiredLen ) ) ,
)
err := windows . Errno ( e )
if err == windows . ERROR_SUCCESS {
break
}
if err != windows . ERROR_INSUFFICIENT_BUFFER {
return "" , err
}
buf = make ( [ ] byte , desiredLen )
addr = unsafe . Pointer ( & buf [ 0 ] )
}
basicInfo := ( * _TCPIP_OWNER_MODULE_BASIC_INFO ) ( addr )
return windows . UTF16PtrToString ( basicInfo . moduleName ) , nil
}
func ( m * _MIB_TCPROW_OWNER_MODULE ) GetModule ( ) ( string , error ) {
return moduleInfo ( m , getOwnerModuleFromTcpEntry )
}
func ( m * _MIB_TCP6ROW_OWNER_MODULE ) GetModule ( ) ( string , error ) {
return moduleInfo ( m , getOwnerModuleFromTcp6Entry )
}