2017-02-01 18:26:00 +00:00
|
|
|
// +build windows
|
|
|
|
|
|
|
|
package net
|
|
|
|
|
|
|
|
import (
|
2018-10-19 18:33:23 +00:00
|
|
|
"context"
|
|
|
|
"fmt"
|
2017-02-01 18:26:00 +00:00
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"syscall"
|
2018-10-19 18:33:23 +00:00
|
|
|
"unsafe"
|
2017-02-01 18:26:00 +00:00
|
|
|
|
|
|
|
"github.com/shirou/gopsutil/internal/common"
|
2018-10-19 18:33:23 +00:00
|
|
|
"golang.org/x/sys/windows"
|
2017-02-01 18:26:00 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2018-10-19 18:33:23 +00:00
|
|
|
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
|
2017-02-01 18:26:00 +00:00
|
|
|
procGetExtendedTCPTable = modiphlpapi.NewProc("GetExtendedTcpTable")
|
|
|
|
procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable")
|
2020-07-01 12:47:56 +00:00
|
|
|
procGetIfEntry2 = modiphlpapi.NewProc("GetIfEntry2")
|
2017-02-01 18:26:00 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
TCPTableBasicListener = iota
|
|
|
|
TCPTableBasicConnections
|
|
|
|
TCPTableBasicAll
|
|
|
|
TCPTableOwnerPIDListener
|
|
|
|
TCPTableOwnerPIDConnections
|
|
|
|
TCPTableOwnerPIDAll
|
|
|
|
TCPTableOwnerModuleListener
|
|
|
|
TCPTableOwnerModuleConnections
|
|
|
|
TCPTableOwnerModuleAll
|
|
|
|
)
|
|
|
|
|
2018-10-19 18:33:23 +00:00
|
|
|
type netConnectionKindType struct {
|
|
|
|
family uint32
|
|
|
|
sockType uint32
|
|
|
|
filename string
|
|
|
|
}
|
|
|
|
|
|
|
|
var kindTCP4 = netConnectionKindType{
|
|
|
|
family: syscall.AF_INET,
|
|
|
|
sockType: syscall.SOCK_STREAM,
|
|
|
|
filename: "tcp",
|
|
|
|
}
|
|
|
|
var kindTCP6 = netConnectionKindType{
|
|
|
|
family: syscall.AF_INET6,
|
|
|
|
sockType: syscall.SOCK_STREAM,
|
|
|
|
filename: "tcp6",
|
|
|
|
}
|
|
|
|
var kindUDP4 = netConnectionKindType{
|
|
|
|
family: syscall.AF_INET,
|
|
|
|
sockType: syscall.SOCK_DGRAM,
|
|
|
|
filename: "udp",
|
|
|
|
}
|
|
|
|
var kindUDP6 = netConnectionKindType{
|
|
|
|
family: syscall.AF_INET6,
|
|
|
|
sockType: syscall.SOCK_DGRAM,
|
|
|
|
filename: "udp6",
|
|
|
|
}
|
|
|
|
|
|
|
|
var netConnectionKindMap = map[string][]netConnectionKindType{
|
|
|
|
"all": {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
|
|
|
|
"tcp": {kindTCP4, kindTCP6},
|
|
|
|
"tcp4": {kindTCP4},
|
|
|
|
"tcp6": {kindTCP6},
|
|
|
|
"udp": {kindUDP4, kindUDP6},
|
|
|
|
"udp4": {kindUDP4},
|
|
|
|
"udp6": {kindUDP6},
|
|
|
|
"inet": {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
|
|
|
|
"inet4": {kindTCP4, kindUDP4},
|
|
|
|
"inet6": {kindTCP6, kindUDP6},
|
|
|
|
}
|
|
|
|
|
2020-07-01 12:47:56 +00:00
|
|
|
// https://github.com/microsoft/ethr/blob/aecdaf923970e5a9b4c461b4e2e3963d781ad2cc/plt_windows.go#L114-L170
|
|
|
|
type guid struct {
|
|
|
|
Data1 uint32
|
|
|
|
Data2 uint16
|
|
|
|
Data3 uint16
|
|
|
|
Data4 [8]byte
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
maxStringSize = 256
|
|
|
|
maxPhysAddressLength = 32
|
|
|
|
pad0for64_4for32 = 0
|
|
|
|
)
|
|
|
|
|
|
|
|
type mibIfRow2 struct {
|
|
|
|
InterfaceLuid uint64
|
|
|
|
InterfaceIndex uint32
|
|
|
|
InterfaceGuid guid
|
|
|
|
Alias [maxStringSize + 1]uint16
|
|
|
|
Description [maxStringSize + 1]uint16
|
|
|
|
PhysicalAddressLength uint32
|
|
|
|
PhysicalAddress [maxPhysAddressLength]uint8
|
|
|
|
PermanentPhysicalAddress [maxPhysAddressLength]uint8
|
|
|
|
Mtu uint32
|
|
|
|
Type uint32
|
|
|
|
TunnelType uint32
|
|
|
|
MediaType uint32
|
|
|
|
PhysicalMediumType uint32
|
|
|
|
AccessType uint32
|
|
|
|
DirectionType uint32
|
|
|
|
InterfaceAndOperStatusFlags uint32
|
|
|
|
OperStatus uint32
|
|
|
|
AdminStatus uint32
|
|
|
|
MediaConnectState uint32
|
|
|
|
NetworkGuid guid
|
|
|
|
ConnectionType uint32
|
|
|
|
padding1 [pad0for64_4for32]byte
|
|
|
|
TransmitLinkSpeed uint64
|
|
|
|
ReceiveLinkSpeed uint64
|
|
|
|
InOctets uint64
|
|
|
|
InUcastPkts uint64
|
|
|
|
InNUcastPkts uint64
|
|
|
|
InDiscards uint64
|
|
|
|
InErrors uint64
|
|
|
|
InUnknownProtos uint64
|
|
|
|
InUcastOctets uint64
|
|
|
|
InMulticastOctets uint64
|
|
|
|
InBroadcastOctets uint64
|
|
|
|
OutOctets uint64
|
|
|
|
OutUcastPkts uint64
|
|
|
|
OutNUcastPkts uint64
|
|
|
|
OutDiscards uint64
|
|
|
|
OutErrors uint64
|
|
|
|
OutUcastOctets uint64
|
|
|
|
OutMulticastOctets uint64
|
|
|
|
OutBroadcastOctets uint64
|
|
|
|
OutQLen uint64
|
|
|
|
}
|
|
|
|
|
2017-02-01 18:26:00 +00:00
|
|
|
func IOCounters(pernic bool) ([]IOCountersStat, error) {
|
2018-10-19 18:33:23 +00:00
|
|
|
return IOCountersWithContext(context.Background(), pernic)
|
|
|
|
}
|
|
|
|
|
|
|
|
func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
|
2017-02-01 18:26:00 +00:00
|
|
|
ifs, err := net.Interfaces()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-07-01 12:47:56 +00:00
|
|
|
var counters []IOCountersStat
|
|
|
|
|
|
|
|
err = procGetIfEntry2.Find()
|
|
|
|
if err == nil { // Vista+, uint64 values (issue#693)
|
|
|
|
for _, ifi := range ifs {
|
|
|
|
c := IOCountersStat{
|
|
|
|
Name: ifi.Name,
|
|
|
|
}
|
2017-02-01 18:26:00 +00:00
|
|
|
|
2020-07-01 12:47:56 +00:00
|
|
|
row := mibIfRow2{InterfaceIndex: uint32(ifi.Index)}
|
|
|
|
ret, _, err := procGetIfEntry2.Call(uintptr(unsafe.Pointer(&row)))
|
|
|
|
if ret != 0 {
|
|
|
|
return nil, os.NewSyscallError("GetIfEntry2", err)
|
|
|
|
}
|
|
|
|
c.BytesSent = uint64(row.OutOctets)
|
|
|
|
c.BytesRecv = uint64(row.InOctets)
|
|
|
|
c.PacketsSent = uint64(row.OutUcastPkts)
|
|
|
|
c.PacketsRecv = uint64(row.InUcastPkts)
|
|
|
|
c.Errin = uint64(row.InErrors)
|
|
|
|
c.Errout = uint64(row.OutErrors)
|
|
|
|
c.Dropin = uint64(row.InDiscards)
|
|
|
|
c.Dropout = uint64(row.OutDiscards)
|
|
|
|
|
|
|
|
counters = append(counters, c)
|
2017-02-01 18:26:00 +00:00
|
|
|
}
|
2020-07-01 12:47:56 +00:00
|
|
|
} else { // WinXP fallback, uint32 values
|
|
|
|
for _, ifi := range ifs {
|
|
|
|
c := IOCountersStat{
|
|
|
|
Name: ifi.Name,
|
|
|
|
}
|
2017-02-01 18:26:00 +00:00
|
|
|
|
2020-07-01 12:47:56 +00:00
|
|
|
row := windows.MibIfRow{Index: uint32(ifi.Index)}
|
|
|
|
err = windows.GetIfEntry(&row)
|
|
|
|
if err != nil {
|
|
|
|
return nil, os.NewSyscallError("GetIfEntry", err)
|
|
|
|
}
|
|
|
|
c.BytesSent = uint64(row.OutOctets)
|
|
|
|
c.BytesRecv = uint64(row.InOctets)
|
|
|
|
c.PacketsSent = uint64(row.OutUcastPkts)
|
|
|
|
c.PacketsRecv = uint64(row.InUcastPkts)
|
|
|
|
c.Errin = uint64(row.InErrors)
|
|
|
|
c.Errout = uint64(row.OutErrors)
|
|
|
|
c.Dropin = uint64(row.InDiscards)
|
|
|
|
c.Dropout = uint64(row.OutDiscards)
|
|
|
|
|
|
|
|
counters = append(counters, c)
|
2017-02-01 18:26:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-01 12:47:56 +00:00
|
|
|
if !pernic {
|
|
|
|
return getIOCountersAll(counters)
|
2017-02-01 18:26:00 +00:00
|
|
|
}
|
2020-07-01 12:47:56 +00:00
|
|
|
return counters, nil
|
2017-02-01 18:26:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NetIOCountersByFile is an method which is added just a compatibility for linux.
|
|
|
|
func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
|
2018-10-19 18:33:23 +00:00
|
|
|
return IOCountersByFileWithContext(context.Background(), pernic, filename)
|
|
|
|
}
|
|
|
|
|
|
|
|
func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
|
2017-02-01 18:26:00 +00:00
|
|
|
return IOCounters(pernic)
|
|
|
|
}
|
|
|
|
|
2018-10-19 18:33:23 +00:00
|
|
|
// Return a list of network connections
|
|
|
|
// Available kind:
|
|
|
|
// reference to netConnectionKindMap
|
2017-02-01 18:26:00 +00:00
|
|
|
func Connections(kind string) ([]ConnectionStat, error) {
|
2018-10-19 18:33:23 +00:00
|
|
|
return ConnectionsWithContext(context.Background(), kind)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
|
|
|
|
return ConnectionsPidWithContext(ctx, kind, 0)
|
|
|
|
}
|
2017-02-01 18:26:00 +00:00
|
|
|
|
2018-10-19 18:33:23 +00:00
|
|
|
// ConnectionsPid Return a list of network connections opened by a process
|
|
|
|
func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
|
|
|
|
return ConnectionsPidWithContext(context.Background(), kind, pid)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
|
|
|
|
tmap, ok := netConnectionKindMap[kind]
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("invalid kind, %s", kind)
|
|
|
|
}
|
|
|
|
return getProcInet(tmap, pid)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getProcInet(kinds []netConnectionKindType, pid int32) ([]ConnectionStat, error) {
|
|
|
|
stats := make([]ConnectionStat, 0)
|
|
|
|
|
|
|
|
for _, kind := range kinds {
|
|
|
|
s, err := getNetStatWithKind(kind)
|
|
|
|
if err != nil {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if pid == 0 {
|
|
|
|
stats = append(stats, s...)
|
|
|
|
} else {
|
|
|
|
for _, ns := range s {
|
|
|
|
if ns.Pid != pid {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
stats = append(stats, ns)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return stats, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getNetStatWithKind(kindType netConnectionKindType) ([]ConnectionStat, error) {
|
|
|
|
if kindType.filename == "" {
|
|
|
|
return nil, fmt.Errorf("kind filename must be required")
|
|
|
|
}
|
|
|
|
|
|
|
|
switch kindType.filename {
|
|
|
|
case kindTCP4.filename:
|
|
|
|
return getTCPConnections(kindTCP4.family)
|
|
|
|
case kindTCP6.filename:
|
|
|
|
return getTCPConnections(kindTCP6.family)
|
|
|
|
case kindUDP4.filename:
|
|
|
|
return getUDPConnections(kindUDP4.family)
|
|
|
|
case kindUDP6.filename:
|
|
|
|
return getUDPConnections(kindUDP6.family)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("invalid kind filename, %s", kindType.filename)
|
2017-02-01 18:26:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return a list of network connections opened returning at most `max`
|
|
|
|
// connections for each running process.
|
|
|
|
func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
|
2018-10-19 18:33:23 +00:00
|
|
|
return ConnectionsMaxWithContext(context.Background(), kind, max)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
|
2017-02-01 18:26:00 +00:00
|
|
|
return []ConnectionStat{}, common.ErrNotImplementedError
|
|
|
|
}
|
|
|
|
|
2020-07-01 12:47:56 +00:00
|
|
|
// Return a list of network connections opened, omitting `Uids`.
|
|
|
|
// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
|
|
|
|
// removed from the API in the future.
|
|
|
|
func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
|
|
|
|
return ConnectionsWithoutUidsWithContext(context.Background(), kind)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
|
|
|
|
return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
|
|
|
|
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
|
|
|
|
return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
|
|
|
|
return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
|
|
|
|
return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
|
|
|
return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max)
|
|
|
|
}
|
|
|
|
|
|
|
|
func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
|
|
|
|
return []ConnectionStat{}, common.ErrNotImplementedError
|
|
|
|
}
|
|
|
|
|
2017-02-01 18:26:00 +00:00
|
|
|
func FilterCounters() ([]FilterStat, error) {
|
2018-10-19 18:33:23 +00:00
|
|
|
return FilterCountersWithContext(context.Background())
|
|
|
|
}
|
|
|
|
|
|
|
|
func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
|
2020-10-07 16:57:18 +00:00
|
|
|
return nil, common.ErrNotImplementedError
|
2017-02-01 18:26:00 +00:00
|
|
|
}
|
|
|
|
|
2020-07-01 12:47:56 +00:00
|
|
|
func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
|
|
|
|
return ConntrackStatsWithContext(context.Background(), percpu)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
|
|
|
|
return nil, common.ErrNotImplementedError
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-01 18:26:00 +00:00
|
|
|
// NetProtoCounters returns network statistics for the entire system
|
|
|
|
// If protocols is empty then all protocols are returned, otherwise
|
|
|
|
// just the protocols in the list are returned.
|
|
|
|
// Not Implemented for Windows
|
|
|
|
func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
|
2018-10-19 18:33:23 +00:00
|
|
|
return ProtoCountersWithContext(context.Background(), protocols)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
|
2020-10-07 16:57:18 +00:00
|
|
|
return nil, common.ErrNotImplementedError
|
2017-02-01 18:26:00 +00:00
|
|
|
}
|
2018-10-19 18:33:23 +00:00
|
|
|
|
|
|
|
func getTableUintptr(family uint32, buf []byte) uintptr {
|
|
|
|
var (
|
|
|
|
pmibTCPTable pmibTCPTableOwnerPidAll
|
|
|
|
pmibTCP6Table pmibTCP6TableOwnerPidAll
|
|
|
|
|
|
|
|
p uintptr
|
|
|
|
)
|
|
|
|
switch family {
|
|
|
|
case kindTCP4.family:
|
|
|
|
if len(buf) > 0 {
|
|
|
|
pmibTCPTable = (*mibTCPTableOwnerPid)(unsafe.Pointer(&buf[0]))
|
|
|
|
p = uintptr(unsafe.Pointer(pmibTCPTable))
|
|
|
|
} else {
|
|
|
|
p = uintptr(unsafe.Pointer(pmibTCPTable))
|
|
|
|
}
|
|
|
|
case kindTCP6.family:
|
|
|
|
if len(buf) > 0 {
|
|
|
|
pmibTCP6Table = (*mibTCP6TableOwnerPid)(unsafe.Pointer(&buf[0]))
|
|
|
|
p = uintptr(unsafe.Pointer(pmibTCP6Table))
|
|
|
|
} else {
|
|
|
|
p = uintptr(unsafe.Pointer(pmibTCP6Table))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
func getTableInfo(filename string, table interface{}) (index, step, length int) {
|
|
|
|
switch filename {
|
|
|
|
case kindTCP4.filename:
|
|
|
|
index = int(unsafe.Sizeof(table.(pmibTCPTableOwnerPidAll).DwNumEntries))
|
|
|
|
step = int(unsafe.Sizeof(table.(pmibTCPTableOwnerPidAll).Table))
|
|
|
|
length = int(table.(pmibTCPTableOwnerPidAll).DwNumEntries)
|
|
|
|
case kindTCP6.filename:
|
|
|
|
index = int(unsafe.Sizeof(table.(pmibTCP6TableOwnerPidAll).DwNumEntries))
|
|
|
|
step = int(unsafe.Sizeof(table.(pmibTCP6TableOwnerPidAll).Table))
|
|
|
|
length = int(table.(pmibTCP6TableOwnerPidAll).DwNumEntries)
|
|
|
|
case kindUDP4.filename:
|
|
|
|
index = int(unsafe.Sizeof(table.(pmibUDPTableOwnerPid).DwNumEntries))
|
|
|
|
step = int(unsafe.Sizeof(table.(pmibUDPTableOwnerPid).Table))
|
|
|
|
length = int(table.(pmibUDPTableOwnerPid).DwNumEntries)
|
|
|
|
case kindUDP6.filename:
|
|
|
|
index = int(unsafe.Sizeof(table.(pmibUDP6TableOwnerPid).DwNumEntries))
|
|
|
|
step = int(unsafe.Sizeof(table.(pmibUDP6TableOwnerPid).Table))
|
|
|
|
length = int(table.(pmibUDP6TableOwnerPid).DwNumEntries)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func getTCPConnections(family uint32) ([]ConnectionStat, error) {
|
|
|
|
var (
|
|
|
|
p uintptr
|
|
|
|
buf []byte
|
|
|
|
size uint32
|
|
|
|
|
|
|
|
pmibTCPTable pmibTCPTableOwnerPidAll
|
|
|
|
pmibTCP6Table pmibTCP6TableOwnerPidAll
|
|
|
|
)
|
|
|
|
|
|
|
|
if family == 0 {
|
|
|
|
return nil, fmt.Errorf("faimly must be required")
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
switch family {
|
|
|
|
case kindTCP4.family:
|
|
|
|
if len(buf) > 0 {
|
|
|
|
pmibTCPTable = (*mibTCPTableOwnerPid)(unsafe.Pointer(&buf[0]))
|
|
|
|
p = uintptr(unsafe.Pointer(pmibTCPTable))
|
|
|
|
} else {
|
|
|
|
p = uintptr(unsafe.Pointer(pmibTCPTable))
|
|
|
|
}
|
|
|
|
case kindTCP6.family:
|
|
|
|
if len(buf) > 0 {
|
|
|
|
pmibTCP6Table = (*mibTCP6TableOwnerPid)(unsafe.Pointer(&buf[0]))
|
|
|
|
p = uintptr(unsafe.Pointer(pmibTCP6Table))
|
|
|
|
} else {
|
|
|
|
p = uintptr(unsafe.Pointer(pmibTCP6Table))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err := getExtendedTcpTable(p,
|
|
|
|
&size,
|
|
|
|
true,
|
|
|
|
family,
|
|
|
|
tcpTableOwnerPidAll,
|
|
|
|
0)
|
|
|
|
if err == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if err != windows.ERROR_INSUFFICIENT_BUFFER {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
buf = make([]byte, size)
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
index, step int
|
|
|
|
length int
|
|
|
|
)
|
|
|
|
|
|
|
|
stats := make([]ConnectionStat, 0)
|
|
|
|
switch family {
|
|
|
|
case kindTCP4.family:
|
|
|
|
index, step, length = getTableInfo(kindTCP4.filename, pmibTCPTable)
|
|
|
|
case kindTCP6.family:
|
|
|
|
index, step, length = getTableInfo(kindTCP6.filename, pmibTCP6Table)
|
|
|
|
}
|
|
|
|
|
|
|
|
if length == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < length; i++ {
|
|
|
|
switch family {
|
|
|
|
case kindTCP4.family:
|
|
|
|
mibs := (*mibTCPRowOwnerPid)(unsafe.Pointer(&buf[index]))
|
|
|
|
ns := mibs.convertToConnectionStat()
|
|
|
|
stats = append(stats, ns)
|
|
|
|
case kindTCP6.family:
|
|
|
|
mibs := (*mibTCP6RowOwnerPid)(unsafe.Pointer(&buf[index]))
|
|
|
|
ns := mibs.convertToConnectionStat()
|
|
|
|
stats = append(stats, ns)
|
|
|
|
}
|
|
|
|
|
|
|
|
index += step
|
|
|
|
}
|
|
|
|
return stats, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getUDPConnections(family uint32) ([]ConnectionStat, error) {
|
|
|
|
var (
|
|
|
|
p uintptr
|
|
|
|
buf []byte
|
|
|
|
size uint32
|
|
|
|
|
|
|
|
pmibUDPTable pmibUDPTableOwnerPid
|
|
|
|
pmibUDP6Table pmibUDP6TableOwnerPid
|
|
|
|
)
|
|
|
|
|
|
|
|
if family == 0 {
|
|
|
|
return nil, fmt.Errorf("faimly must be required")
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
|
|
|
switch family {
|
|
|
|
case kindUDP4.family:
|
|
|
|
if len(buf) > 0 {
|
|
|
|
pmibUDPTable = (*mibUDPTableOwnerPid)(unsafe.Pointer(&buf[0]))
|
|
|
|
p = uintptr(unsafe.Pointer(pmibUDPTable))
|
|
|
|
} else {
|
|
|
|
p = uintptr(unsafe.Pointer(pmibUDPTable))
|
|
|
|
}
|
|
|
|
case kindUDP6.family:
|
|
|
|
if len(buf) > 0 {
|
|
|
|
pmibUDP6Table = (*mibUDP6TableOwnerPid)(unsafe.Pointer(&buf[0]))
|
|
|
|
p = uintptr(unsafe.Pointer(pmibUDP6Table))
|
|
|
|
} else {
|
|
|
|
p = uintptr(unsafe.Pointer(pmibUDP6Table))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
err := getExtendedUdpTable(
|
|
|
|
p,
|
|
|
|
&size,
|
|
|
|
true,
|
|
|
|
family,
|
|
|
|
udpTableOwnerPid,
|
|
|
|
0,
|
|
|
|
)
|
|
|
|
if err == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if err != windows.ERROR_INSUFFICIENT_BUFFER {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
buf = make([]byte, size)
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
index, step, length int
|
|
|
|
)
|
|
|
|
|
|
|
|
stats := make([]ConnectionStat, 0)
|
|
|
|
switch family {
|
|
|
|
case kindUDP4.family:
|
|
|
|
index, step, length = getTableInfo(kindUDP4.filename, pmibUDPTable)
|
|
|
|
case kindUDP6.family:
|
|
|
|
index, step, length = getTableInfo(kindUDP6.filename, pmibUDP6Table)
|
|
|
|
}
|
|
|
|
|
|
|
|
if length == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < length; i++ {
|
|
|
|
switch family {
|
|
|
|
case kindUDP4.family:
|
|
|
|
mibs := (*mibUDPRowOwnerPid)(unsafe.Pointer(&buf[index]))
|
|
|
|
ns := mibs.convertToConnectionStat()
|
|
|
|
stats = append(stats, ns)
|
|
|
|
case kindUDP4.family:
|
|
|
|
mibs := (*mibUDP6RowOwnerPid)(unsafe.Pointer(&buf[index]))
|
|
|
|
ns := mibs.convertToConnectionStat()
|
|
|
|
stats = append(stats, ns)
|
|
|
|
}
|
|
|
|
|
|
|
|
index += step
|
|
|
|
}
|
|
|
|
return stats, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// tcpStatuses https://msdn.microsoft.com/en-us/library/windows/desktop/bb485761(v=vs.85).aspx
|
|
|
|
var tcpStatuses = map[mibTCPState]string{
|
|
|
|
1: "CLOSED",
|
|
|
|
2: "LISTEN",
|
|
|
|
3: "SYN_SENT",
|
|
|
|
4: "SYN_RECEIVED",
|
|
|
|
5: "ESTABLISHED",
|
|
|
|
6: "FIN_WAIT_1",
|
|
|
|
7: "FIN_WAIT_2",
|
|
|
|
8: "CLOSE_WAIT",
|
|
|
|
9: "CLOSING",
|
|
|
|
10: "LAST_ACK",
|
|
|
|
11: "TIME_WAIT",
|
|
|
|
12: "DELETE",
|
|
|
|
}
|
|
|
|
|
|
|
|
func getExtendedTcpTable(pTcpTable uintptr, pdwSize *uint32, bOrder bool, ulAf uint32, tableClass tcpTableClass, reserved uint32) (errcode error) {
|
|
|
|
r1, _, _ := syscall.Syscall6(procGetExtendedTCPTable.Addr(), 6, pTcpTable, uintptr(unsafe.Pointer(pdwSize)), getUintptrFromBool(bOrder), uintptr(ulAf), uintptr(tableClass), uintptr(reserved))
|
|
|
|
if r1 != 0 {
|
|
|
|
errcode = syscall.Errno(r1)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func getExtendedUdpTable(pUdpTable uintptr, pdwSize *uint32, bOrder bool, ulAf uint32, tableClass udpTableClass, reserved uint32) (errcode error) {
|
|
|
|
r1, _, _ := syscall.Syscall6(procGetExtendedUDPTable.Addr(), 6, pUdpTable, uintptr(unsafe.Pointer(pdwSize)), getUintptrFromBool(bOrder), uintptr(ulAf), uintptr(tableClass), uintptr(reserved))
|
|
|
|
if r1 != 0 {
|
|
|
|
errcode = syscall.Errno(r1)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func getUintptrFromBool(b bool) uintptr {
|
|
|
|
if b {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
const anySize = 1
|
|
|
|
|
|
|
|
// type MIB_TCP_STATE int32
|
|
|
|
type mibTCPState int32
|
|
|
|
|
|
|
|
type tcpTableClass int32
|
|
|
|
|
|
|
|
const (
|
|
|
|
tcpTableBasicListener tcpTableClass = iota
|
|
|
|
tcpTableBasicConnections
|
|
|
|
tcpTableBasicAll
|
|
|
|
tcpTableOwnerPidListener
|
|
|
|
tcpTableOwnerPidConnections
|
|
|
|
tcpTableOwnerPidAll
|
|
|
|
tcpTableOwnerModuleListener
|
|
|
|
tcpTableOwnerModuleConnections
|
|
|
|
tcpTableOwnerModuleAll
|
|
|
|
)
|
|
|
|
|
|
|
|
type udpTableClass int32
|
|
|
|
|
|
|
|
const (
|
|
|
|
udpTableBasic udpTableClass = iota
|
|
|
|
udpTableOwnerPid
|
|
|
|
udpTableOwnerModule
|
|
|
|
)
|
|
|
|
|
|
|
|
// TCP
|
|
|
|
|
|
|
|
type mibTCPRowOwnerPid struct {
|
|
|
|
DwState uint32
|
|
|
|
DwLocalAddr uint32
|
|
|
|
DwLocalPort uint32
|
|
|
|
DwRemoteAddr uint32
|
|
|
|
DwRemotePort uint32
|
|
|
|
DwOwningPid uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mibTCPRowOwnerPid) convertToConnectionStat() ConnectionStat {
|
|
|
|
ns := ConnectionStat{
|
|
|
|
Family: kindTCP4.family,
|
|
|
|
Type: kindTCP4.sockType,
|
|
|
|
Laddr: Addr{
|
|
|
|
IP: parseIPv4HexString(m.DwLocalAddr),
|
|
|
|
Port: uint32(decodePort(m.DwLocalPort)),
|
|
|
|
},
|
|
|
|
Raddr: Addr{
|
|
|
|
IP: parseIPv4HexString(m.DwRemoteAddr),
|
|
|
|
Port: uint32(decodePort(m.DwRemotePort)),
|
|
|
|
},
|
|
|
|
Pid: int32(m.DwOwningPid),
|
|
|
|
Status: tcpStatuses[mibTCPState(m.DwState)],
|
|
|
|
}
|
|
|
|
|
|
|
|
return ns
|
|
|
|
}
|
|
|
|
|
|
|
|
type mibTCPTableOwnerPid struct {
|
|
|
|
DwNumEntries uint32
|
|
|
|
Table [anySize]mibTCPRowOwnerPid
|
|
|
|
}
|
|
|
|
|
|
|
|
type mibTCP6RowOwnerPid struct {
|
|
|
|
UcLocalAddr [16]byte
|
|
|
|
DwLocalScopeId uint32
|
|
|
|
DwLocalPort uint32
|
|
|
|
UcRemoteAddr [16]byte
|
|
|
|
DwRemoteScopeId uint32
|
|
|
|
DwRemotePort uint32
|
|
|
|
DwState uint32
|
|
|
|
DwOwningPid uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mibTCP6RowOwnerPid) convertToConnectionStat() ConnectionStat {
|
|
|
|
ns := ConnectionStat{
|
|
|
|
Family: kindTCP6.family,
|
|
|
|
Type: kindTCP6.sockType,
|
|
|
|
Laddr: Addr{
|
|
|
|
IP: parseIPv6HexString(m.UcLocalAddr),
|
|
|
|
Port: uint32(decodePort(m.DwLocalPort)),
|
|
|
|
},
|
|
|
|
Raddr: Addr{
|
|
|
|
IP: parseIPv6HexString(m.UcRemoteAddr),
|
|
|
|
Port: uint32(decodePort(m.DwRemotePort)),
|
|
|
|
},
|
|
|
|
Pid: int32(m.DwOwningPid),
|
|
|
|
Status: tcpStatuses[mibTCPState(m.DwState)],
|
|
|
|
}
|
|
|
|
|
|
|
|
return ns
|
|
|
|
}
|
|
|
|
|
|
|
|
type mibTCP6TableOwnerPid struct {
|
|
|
|
DwNumEntries uint32
|
|
|
|
Table [anySize]mibTCP6RowOwnerPid
|
|
|
|
}
|
|
|
|
|
|
|
|
type pmibTCPTableOwnerPidAll *mibTCPTableOwnerPid
|
|
|
|
type pmibTCP6TableOwnerPidAll *mibTCP6TableOwnerPid
|
|
|
|
|
|
|
|
// UDP
|
|
|
|
|
|
|
|
type mibUDPRowOwnerPid struct {
|
|
|
|
DwLocalAddr uint32
|
|
|
|
DwLocalPort uint32
|
|
|
|
DwOwningPid uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mibUDPRowOwnerPid) convertToConnectionStat() ConnectionStat {
|
|
|
|
ns := ConnectionStat{
|
|
|
|
Family: kindUDP4.family,
|
|
|
|
Type: kindUDP4.sockType,
|
|
|
|
Laddr: Addr{
|
|
|
|
IP: parseIPv4HexString(m.DwLocalAddr),
|
|
|
|
Port: uint32(decodePort(m.DwLocalPort)),
|
|
|
|
},
|
|
|
|
Pid: int32(m.DwOwningPid),
|
|
|
|
}
|
|
|
|
|
|
|
|
return ns
|
|
|
|
}
|
|
|
|
|
|
|
|
type mibUDPTableOwnerPid struct {
|
|
|
|
DwNumEntries uint32
|
|
|
|
Table [anySize]mibUDPRowOwnerPid
|
|
|
|
}
|
|
|
|
|
|
|
|
type mibUDP6RowOwnerPid struct {
|
|
|
|
UcLocalAddr [16]byte
|
|
|
|
DwLocalScopeId uint32
|
|
|
|
DwLocalPort uint32
|
|
|
|
DwOwningPid uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mibUDP6RowOwnerPid) convertToConnectionStat() ConnectionStat {
|
|
|
|
ns := ConnectionStat{
|
|
|
|
Family: kindUDP6.family,
|
|
|
|
Type: kindUDP6.sockType,
|
|
|
|
Laddr: Addr{
|
|
|
|
IP: parseIPv6HexString(m.UcLocalAddr),
|
|
|
|
Port: uint32(decodePort(m.DwLocalPort)),
|
|
|
|
},
|
|
|
|
Pid: int32(m.DwOwningPid),
|
|
|
|
}
|
|
|
|
|
|
|
|
return ns
|
|
|
|
}
|
|
|
|
|
|
|
|
type mibUDP6TableOwnerPid struct {
|
|
|
|
DwNumEntries uint32
|
|
|
|
Table [anySize]mibUDP6RowOwnerPid
|
|
|
|
}
|
|
|
|
|
|
|
|
type pmibUDPTableOwnerPid *mibUDPTableOwnerPid
|
|
|
|
type pmibUDP6TableOwnerPid *mibUDP6TableOwnerPid
|
|
|
|
|
|
|
|
func decodePort(port uint32) uint16 {
|
|
|
|
return syscall.Ntohs(uint16(port))
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseIPv4HexString(addr uint32) string {
|
|
|
|
return fmt.Sprintf("%d.%d.%d.%d", addr&255, addr>>8&255, addr>>16&255, addr>>24&255)
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseIPv6HexString(addr [16]byte) string {
|
|
|
|
var ret [16]byte
|
|
|
|
for i := 0; i < 16; i++ {
|
|
|
|
ret[i] = uint8(addr[i])
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert []byte to net.IP
|
|
|
|
ip := net.IP(ret[:])
|
|
|
|
return ip.String()
|
|
|
|
}
|