staros/network_windows.go

315 lines
7.9 KiB
Go
Raw Permalink Normal View History

//go:build windows
2020-07-17 09:56:23 +08:00
// +build windows
package staros
import (
"errors"
"net"
"strconv"
"strings"
"syscall"
2020-07-17 09:56:23 +08:00
"time"
"b612.me/win32api"
2020-07-17 09:56:23 +08:00
)
const windowsErrorNotSupported syscall.Errno = 50
2020-07-17 09:56:23 +08:00
func NetUsage() ([]NetAdapter, error) {
rows, err := win32api.GetIfTable2()
if err != nil {
return nil, err
}
res := make([]NetAdapter, 0, len(rows))
for _, row := range rows {
name := windowsInterfaceName(row)
if name == "" {
continue
}
res = append(res, NetAdapter{
Name: name,
RecvBytes: row.InOctets,
SendBytes: row.OutOctets,
})
}
if len(res) == 0 {
return nil, errors.New("No Adaptor")
}
2020-07-17 09:56:23 +08:00
return res, nil
}
func NetUsageByname(name string) (NetAdapter, error) {
ada, err := NetUsage()
if err != nil {
return NetAdapter{}, err
}
for _, v := range ada {
if v.Name == name {
return v, nil
}
}
return NetAdapter{}, errors.New("Not Found")
2020-07-17 09:56:23 +08:00
}
func NetSpeeds(duration time.Duration) ([]NetSpeed, error) {
if duration <= 0 {
return nil, errors.New("duration must be positive")
}
list1, err := NetUsage()
if err != nil {
return nil, err
}
time.Sleep(duration)
list2, err := NetUsage()
if err != nil {
return nil, err
}
byName := make(map[string]NetAdapter, len(list2))
for _, item := range list2 {
byName[item.Name] = item
}
res := make([]NetSpeed, 0, len(list1))
for _, v := range list1 {
next, ok := byName[v.Name]
if !ok {
continue
}
var recvDelta, sendDelta uint64
if next.RecvBytes >= v.RecvBytes {
recvDelta = next.RecvBytes - v.RecvBytes
}
if next.SendBytes >= v.SendBytes {
sendDelta = next.SendBytes - v.SendBytes
}
res = append(res, NetSpeed{
Name: v.Name,
RecvSpeeds: float64(recvDelta) / duration.Seconds(),
SendSpeeds: float64(sendDelta) / duration.Seconds(),
RecvBytes: next.RecvBytes,
SendBytes: next.SendBytes,
})
}
if len(res) == 0 {
return nil, errors.New("NetWork Adaptor Num Not ok")
}
2020-07-17 09:56:23 +08:00
return res, nil
}
func NetSpeedsByName(duration time.Duration, name string) (NetSpeed, error) {
ada, err := NetSpeeds(duration)
if err != nil {
return NetSpeed{}, err
}
for _, v := range ada {
if v.Name == name {
return v, nil
}
}
return NetSpeed{}, errors.New("Not Found")
2020-07-17 09:56:23 +08:00
}
2021-06-04 10:44:53 +08:00
// NetConnections return all TCP/UDP/UNIX DOMAIN SOCKET Connections
// if your uid != 0 ,and analysePid==true ,you should have CAP_SYS_PRTACE and CAP_DAC_OVERRIDE/CAP_DAC_READ_SEARCH Caps
func NetConnections(analysePid bool, types string) ([]NetConn, error) {
wantTCP, wantUDP, err := windowsNetConnectionTypes(types)
if err != nil {
return nil, err
}
result := make([]NetConn, 0)
processCache := make(map[int64]*Process)
if wantTCP {
result, err = appendWindowsTCPConnections(result, analysePid, processCache)
if err != nil {
return result, err
}
}
if wantUDP {
result, err = appendWindowsUDPConnections(result, analysePid, processCache)
if err != nil {
return result, err
}
}
return result, nil
}
func GetInodeMap() (map[string]int64, error) {
return nil, ERR_UNSUPPORTED
}
func windowsInterfaceName(row win32api.MIB_IF_ROW2) string {
name := strings.TrimSpace(syscall.UTF16ToString(row.Alias[:]))
if name != "" {
return name
}
name = strings.TrimSpace(syscall.UTF16ToString(row.Description[:]))
if name != "" {
return name
}
if row.InterfaceIndex != 0 {
return "if" + strconv.FormatUint(uint64(row.InterfaceIndex), 10)
}
if row.InterfaceLuid != 0 {
return "luid" + strconv.FormatUint(row.InterfaceLuid, 10)
}
return ""
}
func windowsNetConnectionTypes(types string) (wantTCP, wantUDP bool, err error) {
normalized := strings.ToLower(strings.TrimSpace(types))
if strings.Contains(normalized, "unix") {
return false, false, ERR_UNSUPPORTED
}
if normalized == "" || strings.Contains(normalized, "all") {
return true, true, nil
}
if strings.Contains(normalized, "tcp") {
wantTCP = true
}
if strings.Contains(normalized, "udp") {
wantUDP = true
}
if !wantTCP && !wantUDP {
return false, false, errors.New("unsupported net connection type")
}
return wantTCP, wantUDP, nil
}
func appendWindowsTCPConnections(result []NetConn, analysePid bool, processCache map[int64]*Process) ([]NetConn, error) {
rows4, err := win32api.GetExtendedTcp4Table(false, win32api.TCP_TABLE_OWNER_PID_ALL)
if err != nil {
return result, err
}
for _, row := range rows4 {
conn := NetConn{
LocalAddr: windowsIPv4FromDWORD(row.LocalAddr),
LocalPort: int(row.LocalPortHost()),
RemoteAddr: windowsIPv4FromDWORD(row.RemoteAddr),
RemotePort: int(row.RemotePortHost()),
Status: windowsTCPState(row.State),
Typed: "tcp",
}
attachWindowsProcess(&conn, row.OwningPid, analysePid, processCache)
result = append(result, conn)
}
rows6, err := win32api.GetExtendedTcp6Table(false, win32api.TCP_TABLE_OWNER_PID_ALL)
if err != nil {
if isOptionalWindowsNetTableError(err) {
return result, nil
}
return result, err
}
for _, row := range rows6 {
conn := NetConn{
LocalAddr: net.IP(row.LocalAddr[:]).String(),
LocalPort: int(row.LocalPortHost()),
RemoteAddr: net.IP(row.RemoteAddr[:]).String(),
RemotePort: int(row.RemotePortHost()),
Status: windowsTCPState(row.State),
Typed: "tcp6",
}
attachWindowsProcess(&conn, row.OwningPid, analysePid, processCache)
result = append(result, conn)
}
2021-06-04 10:44:53 +08:00
return result, nil
}
func appendWindowsUDPConnections(result []NetConn, analysePid bool, processCache map[int64]*Process) ([]NetConn, error) {
rows4, err := win32api.GetExtendedUdp4Table(false, win32api.UDP_TABLE_OWNER_PID)
if err != nil {
return result, err
}
for _, row := range rows4 {
conn := NetConn{
LocalAddr: windowsIPv4FromDWORD(row.LocalAddr),
LocalPort: int(row.LocalPortHost()),
Typed: "udp",
}
attachWindowsProcess(&conn, row.OwningPid, analysePid, processCache)
result = append(result, conn)
}
rows6, err := win32api.GetExtendedUdp6Table(false, win32api.UDP_TABLE_OWNER_PID)
if err != nil {
if isOptionalWindowsNetTableError(err) {
return result, nil
}
return result, err
}
for _, row := range rows6 {
conn := NetConn{
LocalAddr: net.IP(row.LocalAddr[:]).String(),
LocalPort: int(row.LocalPortHost()),
Typed: "udp6",
}
attachWindowsProcess(&conn, row.OwningPid, analysePid, processCache)
result = append(result, conn)
}
return result, nil
}
func attachWindowsProcess(conn *NetConn, pid uint32, analysePid bool, processCache map[int64]*Process) {
if conn == nil || !analysePid {
return
}
conn.Pid = int64(pid)
if conn.Pid <= 0 {
return
}
if proc, ok := processCache[conn.Pid]; ok {
conn.Process = proc
return
}
proc, err := FindProcessByPid(conn.Pid)
if err != nil {
processCache[conn.Pid] = nil
return
}
processCache[conn.Pid] = &proc
conn.Process = &proc
}
func windowsIPv4FromDWORD(addr uint32) string {
return net.IPv4(byte(addr), byte(addr>>8), byte(addr>>16), byte(addr>>24)).String()
}
func windowsTCPState(state win32api.MIB_TCP_STATE) string {
switch state {
case win32api.MIB_TCP_STATE_CLOSED:
return TCP_STATE[TCP_CLOSE]
case win32api.MIB_TCP_STATE_LISTEN:
return TCP_STATE[TCP_LISTEN]
case win32api.MIB_TCP_STATE_SYN_SENT:
return TCP_STATE[TCP_SYN_SENT]
case win32api.MIB_TCP_STATE_SYN_RCVD:
return TCP_STATE[TCP_SYN_RECV]
case win32api.MIB_TCP_STATE_ESTAB:
return TCP_STATE[TCP_ESTABLISHED]
case win32api.MIB_TCP_STATE_FIN_WAIT1:
return TCP_STATE[TCP_FIN_WAIT1]
case win32api.MIB_TCP_STATE_FIN_WAIT2:
return TCP_STATE[TCP_FIN_WAIT2]
case win32api.MIB_TCP_STATE_CLOSE_WAIT:
return TCP_STATE[TCP_CLOSE_WAIT]
case win32api.MIB_TCP_STATE_CLOSING:
return TCP_STATE[TCP_CLOSING]
case win32api.MIB_TCP_STATE_LAST_ACK:
return TCP_STATE[TCP_LAST_ACK]
case win32api.MIB_TCP_STATE_TIME_WAIT:
return TCP_STATE[TCP_TIME_WAIT]
case win32api.MIB_TCP_STATE_DELETE_TCB:
return "TCP_DELETE_TCB"
default:
return TCP_STATE[TCP_UNKNOWN]
}
}
func isOptionalWindowsNetTableError(err error) bool {
if errno, ok := err.(syscall.Errno); ok {
return errno == windowsErrorNotSupported
}
return false
}