1// +build windows
2
3package net
4
5import (
6	"context"
7	"errors"
8	"fmt"
9	"net"
10	"os"
11	"syscall"
12	"unsafe"
13
14	"github.com/shirou/gopsutil/internal/common"
15	"golang.org/x/sys/windows"
16)
17
18var (
19	modiphlpapi             = windows.NewLazySystemDLL("iphlpapi.dll")
20	procGetExtendedTCPTable = modiphlpapi.NewProc("GetExtendedTcpTable")
21	procGetExtendedUDPTable = modiphlpapi.NewProc("GetExtendedUdpTable")
22	procGetIfEntry2         = modiphlpapi.NewProc("GetIfEntry2")
23)
24
25const (
26	TCPTableBasicListener = iota
27	TCPTableBasicConnections
28	TCPTableBasicAll
29	TCPTableOwnerPIDListener
30	TCPTableOwnerPIDConnections
31	TCPTableOwnerPIDAll
32	TCPTableOwnerModuleListener
33	TCPTableOwnerModuleConnections
34	TCPTableOwnerModuleAll
35)
36
37type netConnectionKindType struct {
38	family   uint32
39	sockType uint32
40	filename string
41}
42
43var kindTCP4 = netConnectionKindType{
44	family:   syscall.AF_INET,
45	sockType: syscall.SOCK_STREAM,
46	filename: "tcp",
47}
48var kindTCP6 = netConnectionKindType{
49	family:   syscall.AF_INET6,
50	sockType: syscall.SOCK_STREAM,
51	filename: "tcp6",
52}
53var kindUDP4 = netConnectionKindType{
54	family:   syscall.AF_INET,
55	sockType: syscall.SOCK_DGRAM,
56	filename: "udp",
57}
58var kindUDP6 = netConnectionKindType{
59	family:   syscall.AF_INET6,
60	sockType: syscall.SOCK_DGRAM,
61	filename: "udp6",
62}
63
64var netConnectionKindMap = map[string][]netConnectionKindType{
65	"all":   {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
66	"tcp":   {kindTCP4, kindTCP6},
67	"tcp4":  {kindTCP4},
68	"tcp6":  {kindTCP6},
69	"udp":   {kindUDP4, kindUDP6},
70	"udp4":  {kindUDP4},
71	"udp6":  {kindUDP6},
72	"inet":  {kindTCP4, kindTCP6, kindUDP4, kindUDP6},
73	"inet4": {kindTCP4, kindUDP4},
74	"inet6": {kindTCP6, kindUDP6},
75}
76
77// https://github.com/microsoft/ethr/blob/aecdaf923970e5a9b4c461b4e2e3963d781ad2cc/plt_windows.go#L114-L170
78type guid struct {
79	Data1 uint32
80	Data2 uint16
81	Data3 uint16
82	Data4 [8]byte
83}
84
85const (
86	maxStringSize        = 256
87	maxPhysAddressLength = 32
88	pad0for64_4for32     = 0
89)
90
91type mibIfRow2 struct {
92	InterfaceLuid               uint64
93	InterfaceIndex              uint32
94	InterfaceGuid               guid
95	Alias                       [maxStringSize + 1]uint16
96	Description                 [maxStringSize + 1]uint16
97	PhysicalAddressLength       uint32
98	PhysicalAddress             [maxPhysAddressLength]uint8
99	PermanentPhysicalAddress    [maxPhysAddressLength]uint8
100	Mtu                         uint32
101	Type                        uint32
102	TunnelType                  uint32
103	MediaType                   uint32
104	PhysicalMediumType          uint32
105	AccessType                  uint32
106	DirectionType               uint32
107	InterfaceAndOperStatusFlags uint32
108	OperStatus                  uint32
109	AdminStatus                 uint32
110	MediaConnectState           uint32
111	NetworkGuid                 guid
112	ConnectionType              uint32
113	padding1                    [pad0for64_4for32]byte
114	TransmitLinkSpeed           uint64
115	ReceiveLinkSpeed            uint64
116	InOctets                    uint64
117	InUcastPkts                 uint64
118	InNUcastPkts                uint64
119	InDiscards                  uint64
120	InErrors                    uint64
121	InUnknownProtos             uint64
122	InUcastOctets               uint64
123	InMulticastOctets           uint64
124	InBroadcastOctets           uint64
125	OutOctets                   uint64
126	OutUcastPkts                uint64
127	OutNUcastPkts               uint64
128	OutDiscards                 uint64
129	OutErrors                   uint64
130	OutUcastOctets              uint64
131	OutMulticastOctets          uint64
132	OutBroadcastOctets          uint64
133	OutQLen                     uint64
134}
135
136func IOCounters(pernic bool) ([]IOCountersStat, error) {
137	return IOCountersWithContext(context.Background(), pernic)
138}
139
140func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, error) {
141	ifs, err := net.Interfaces()
142	if err != nil {
143		return nil, err
144	}
145	var counters []IOCountersStat
146
147	err = procGetIfEntry2.Find()
148	if err == nil { // Vista+, uint64 values (issue#693)
149		for _, ifi := range ifs {
150			c := IOCountersStat{
151				Name: ifi.Name,
152			}
153
154			row := mibIfRow2{InterfaceIndex: uint32(ifi.Index)}
155			ret, _, err := procGetIfEntry2.Call(uintptr(unsafe.Pointer(&row)))
156			if ret != 0 {
157				return nil, os.NewSyscallError("GetIfEntry2", err)
158			}
159			c.BytesSent = uint64(row.OutOctets)
160			c.BytesRecv = uint64(row.InOctets)
161			c.PacketsSent = uint64(row.OutUcastPkts)
162			c.PacketsRecv = uint64(row.InUcastPkts)
163			c.Errin = uint64(row.InErrors)
164			c.Errout = uint64(row.OutErrors)
165			c.Dropin = uint64(row.InDiscards)
166			c.Dropout = uint64(row.OutDiscards)
167
168			counters = append(counters, c)
169		}
170	} else { // WinXP fallback, uint32 values
171		for _, ifi := range ifs {
172			c := IOCountersStat{
173				Name: ifi.Name,
174			}
175
176			row := windows.MibIfRow{Index: uint32(ifi.Index)}
177			err = windows.GetIfEntry(&row)
178			if err != nil {
179				return nil, os.NewSyscallError("GetIfEntry", err)
180			}
181			c.BytesSent = uint64(row.OutOctets)
182			c.BytesRecv = uint64(row.InOctets)
183			c.PacketsSent = uint64(row.OutUcastPkts)
184			c.PacketsRecv = uint64(row.InUcastPkts)
185			c.Errin = uint64(row.InErrors)
186			c.Errout = uint64(row.OutErrors)
187			c.Dropin = uint64(row.InDiscards)
188			c.Dropout = uint64(row.OutDiscards)
189
190			counters = append(counters, c)
191		}
192	}
193
194	if !pernic {
195		return getIOCountersAll(counters)
196	}
197	return counters, nil
198}
199
200// NetIOCountersByFile is an method which is added just a compatibility for linux.
201func IOCountersByFile(pernic bool, filename string) ([]IOCountersStat, error) {
202	return IOCountersByFileWithContext(context.Background(), pernic, filename)
203}
204
205func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename string) ([]IOCountersStat, error) {
206	return IOCounters(pernic)
207}
208
209// Return a list of network connections
210// Available kind:
211//   reference to netConnectionKindMap
212func Connections(kind string) ([]ConnectionStat, error) {
213	return ConnectionsWithContext(context.Background(), kind)
214}
215
216func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
217	return ConnectionsPidWithContext(ctx, kind, 0)
218}
219
220// ConnectionsPid Return a list of network connections opened by a process
221func ConnectionsPid(kind string, pid int32) ([]ConnectionStat, error) {
222	return ConnectionsPidWithContext(context.Background(), kind, pid)
223}
224
225func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
226	tmap, ok := netConnectionKindMap[kind]
227	if !ok {
228		return nil, fmt.Errorf("invalid kind, %s", kind)
229	}
230	return getProcInet(tmap, pid)
231}
232
233func getProcInet(kinds []netConnectionKindType, pid int32) ([]ConnectionStat, error) {
234	stats := make([]ConnectionStat, 0)
235
236	for _, kind := range kinds {
237		s, err := getNetStatWithKind(kind)
238		if err != nil {
239			continue
240		}
241
242		if pid == 0 {
243			stats = append(stats, s...)
244		} else {
245			for _, ns := range s {
246				if ns.Pid != pid {
247					continue
248				}
249				stats = append(stats, ns)
250			}
251		}
252	}
253
254	return stats, nil
255}
256
257func getNetStatWithKind(kindType netConnectionKindType) ([]ConnectionStat, error) {
258	if kindType.filename == "" {
259		return nil, fmt.Errorf("kind filename must be required")
260	}
261
262	switch kindType.filename {
263	case kindTCP4.filename:
264		return getTCPConnections(kindTCP4.family)
265	case kindTCP6.filename:
266		return getTCPConnections(kindTCP6.family)
267	case kindUDP4.filename:
268		return getUDPConnections(kindUDP4.family)
269	case kindUDP6.filename:
270		return getUDPConnections(kindUDP6.family)
271	}
272
273	return nil, fmt.Errorf("invalid kind filename, %s", kindType.filename)
274}
275
276// Return a list of network connections opened returning at most `max`
277// connections for each running process.
278func ConnectionsMax(kind string, max int) ([]ConnectionStat, error) {
279	return ConnectionsMaxWithContext(context.Background(), kind, max)
280}
281
282func ConnectionsMaxWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
283	return []ConnectionStat{}, common.ErrNotImplementedError
284}
285
286// Return a list of network connections opened, omitting `Uids`.
287// WithoutUids functions are reliant on implementation details. They may be altered to be an alias for Connections or be
288// removed from the API in the future.
289func ConnectionsWithoutUids(kind string) ([]ConnectionStat, error) {
290	return ConnectionsWithoutUidsWithContext(context.Background(), kind)
291}
292
293func ConnectionsWithoutUidsWithContext(ctx context.Context, kind string) ([]ConnectionStat, error) {
294	return ConnectionsMaxWithoutUidsWithContext(ctx, kind, 0)
295}
296
297func ConnectionsMaxWithoutUidsWithContext(ctx context.Context, kind string, max int) ([]ConnectionStat, error) {
298	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, 0, max)
299}
300
301func ConnectionsPidWithoutUids(kind string, pid int32) ([]ConnectionStat, error) {
302	return ConnectionsPidWithoutUidsWithContext(context.Background(), kind, pid)
303}
304
305func ConnectionsPidWithoutUidsWithContext(ctx context.Context, kind string, pid int32) ([]ConnectionStat, error) {
306	return ConnectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, 0)
307}
308
309func ConnectionsPidMaxWithoutUids(kind string, pid int32, max int) ([]ConnectionStat, error) {
310	return ConnectionsPidMaxWithoutUidsWithContext(context.Background(), kind, pid, max)
311}
312
313func ConnectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
314	return connectionsPidMaxWithoutUidsWithContext(ctx, kind, pid, max)
315}
316
317func connectionsPidMaxWithoutUidsWithContext(ctx context.Context, kind string, pid int32, max int) ([]ConnectionStat, error) {
318	return []ConnectionStat{}, common.ErrNotImplementedError
319}
320
321func FilterCounters() ([]FilterStat, error) {
322	return FilterCountersWithContext(context.Background())
323}
324
325func FilterCountersWithContext(ctx context.Context) ([]FilterStat, error) {
326	return nil, errors.New("NetFilterCounters not implemented for windows")
327}
328
329func ConntrackStats(percpu bool) ([]ConntrackStat, error) {
330	return ConntrackStatsWithContext(context.Background(), percpu)
331}
332
333func ConntrackStatsWithContext(ctx context.Context, percpu bool) ([]ConntrackStat, error) {
334	return nil, common.ErrNotImplementedError
335}
336
337
338// NetProtoCounters returns network statistics for the entire system
339// If protocols is empty then all protocols are returned, otherwise
340// just the protocols in the list are returned.
341// Not Implemented for Windows
342func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) {
343	return ProtoCountersWithContext(context.Background(), protocols)
344}
345
346func ProtoCountersWithContext(ctx context.Context, protocols []string) ([]ProtoCountersStat, error) {
347	return nil, errors.New("NetProtoCounters not implemented for windows")
348}
349
350func getTableUintptr(family uint32, buf []byte) uintptr {
351	var (
352		pmibTCPTable  pmibTCPTableOwnerPidAll
353		pmibTCP6Table pmibTCP6TableOwnerPidAll
354
355		p uintptr
356	)
357	switch family {
358	case kindTCP4.family:
359		if len(buf) > 0 {
360			pmibTCPTable = (*mibTCPTableOwnerPid)(unsafe.Pointer(&buf[0]))
361			p = uintptr(unsafe.Pointer(pmibTCPTable))
362		} else {
363			p = uintptr(unsafe.Pointer(pmibTCPTable))
364		}
365	case kindTCP6.family:
366		if len(buf) > 0 {
367			pmibTCP6Table = (*mibTCP6TableOwnerPid)(unsafe.Pointer(&buf[0]))
368			p = uintptr(unsafe.Pointer(pmibTCP6Table))
369		} else {
370			p = uintptr(unsafe.Pointer(pmibTCP6Table))
371		}
372	}
373	return p
374}
375
376func getTableInfo(filename string, table interface{}) (index, step, length int) {
377	switch filename {
378	case kindTCP4.filename:
379		index = int(unsafe.Sizeof(table.(pmibTCPTableOwnerPidAll).DwNumEntries))
380		step = int(unsafe.Sizeof(table.(pmibTCPTableOwnerPidAll).Table))
381		length = int(table.(pmibTCPTableOwnerPidAll).DwNumEntries)
382	case kindTCP6.filename:
383		index = int(unsafe.Sizeof(table.(pmibTCP6TableOwnerPidAll).DwNumEntries))
384		step = int(unsafe.Sizeof(table.(pmibTCP6TableOwnerPidAll).Table))
385		length = int(table.(pmibTCP6TableOwnerPidAll).DwNumEntries)
386	case kindUDP4.filename:
387		index = int(unsafe.Sizeof(table.(pmibUDPTableOwnerPid).DwNumEntries))
388		step = int(unsafe.Sizeof(table.(pmibUDPTableOwnerPid).Table))
389		length = int(table.(pmibUDPTableOwnerPid).DwNumEntries)
390	case kindUDP6.filename:
391		index = int(unsafe.Sizeof(table.(pmibUDP6TableOwnerPid).DwNumEntries))
392		step = int(unsafe.Sizeof(table.(pmibUDP6TableOwnerPid).Table))
393		length = int(table.(pmibUDP6TableOwnerPid).DwNumEntries)
394	}
395
396	return
397}
398
399func getTCPConnections(family uint32) ([]ConnectionStat, error) {
400	var (
401		p    uintptr
402		buf  []byte
403		size uint32
404
405		pmibTCPTable  pmibTCPTableOwnerPidAll
406		pmibTCP6Table pmibTCP6TableOwnerPidAll
407	)
408
409	if family == 0 {
410		return nil, fmt.Errorf("faimly must be required")
411	}
412
413	for {
414		switch family {
415		case kindTCP4.family:
416			if len(buf) > 0 {
417				pmibTCPTable = (*mibTCPTableOwnerPid)(unsafe.Pointer(&buf[0]))
418				p = uintptr(unsafe.Pointer(pmibTCPTable))
419			} else {
420				p = uintptr(unsafe.Pointer(pmibTCPTable))
421			}
422		case kindTCP6.family:
423			if len(buf) > 0 {
424				pmibTCP6Table = (*mibTCP6TableOwnerPid)(unsafe.Pointer(&buf[0]))
425				p = uintptr(unsafe.Pointer(pmibTCP6Table))
426			} else {
427				p = uintptr(unsafe.Pointer(pmibTCP6Table))
428			}
429		}
430
431		err := getExtendedTcpTable(p,
432			&size,
433			true,
434			family,
435			tcpTableOwnerPidAll,
436			0)
437		if err == nil {
438			break
439		}
440		if err != windows.ERROR_INSUFFICIENT_BUFFER {
441			return nil, err
442		}
443		buf = make([]byte, size)
444	}
445
446	var (
447		index, step int
448		length      int
449	)
450
451	stats := make([]ConnectionStat, 0)
452	switch family {
453	case kindTCP4.family:
454		index, step, length = getTableInfo(kindTCP4.filename, pmibTCPTable)
455	case kindTCP6.family:
456		index, step, length = getTableInfo(kindTCP6.filename, pmibTCP6Table)
457	}
458
459	if length == 0 {
460		return nil, nil
461	}
462
463	for i := 0; i < length; i++ {
464		switch family {
465		case kindTCP4.family:
466			mibs := (*mibTCPRowOwnerPid)(unsafe.Pointer(&buf[index]))
467			ns := mibs.convertToConnectionStat()
468			stats = append(stats, ns)
469		case kindTCP6.family:
470			mibs := (*mibTCP6RowOwnerPid)(unsafe.Pointer(&buf[index]))
471			ns := mibs.convertToConnectionStat()
472			stats = append(stats, ns)
473		}
474
475		index += step
476	}
477	return stats, nil
478}
479
480func getUDPConnections(family uint32) ([]ConnectionStat, error) {
481	var (
482		p    uintptr
483		buf  []byte
484		size uint32
485
486		pmibUDPTable  pmibUDPTableOwnerPid
487		pmibUDP6Table pmibUDP6TableOwnerPid
488	)
489
490	if family == 0 {
491		return nil, fmt.Errorf("faimly must be required")
492	}
493
494	for {
495		switch family {
496		case kindUDP4.family:
497			if len(buf) > 0 {
498				pmibUDPTable = (*mibUDPTableOwnerPid)(unsafe.Pointer(&buf[0]))
499				p = uintptr(unsafe.Pointer(pmibUDPTable))
500			} else {
501				p = uintptr(unsafe.Pointer(pmibUDPTable))
502			}
503		case kindUDP6.family:
504			if len(buf) > 0 {
505				pmibUDP6Table = (*mibUDP6TableOwnerPid)(unsafe.Pointer(&buf[0]))
506				p = uintptr(unsafe.Pointer(pmibUDP6Table))
507			} else {
508				p = uintptr(unsafe.Pointer(pmibUDP6Table))
509			}
510		}
511
512		err := getExtendedUdpTable(
513			p,
514			&size,
515			true,
516			family,
517			udpTableOwnerPid,
518			0,
519		)
520		if err == nil {
521			break
522		}
523		if err != windows.ERROR_INSUFFICIENT_BUFFER {
524			return nil, err
525		}
526		buf = make([]byte, size)
527	}
528
529	var (
530		index, step, length int
531	)
532
533	stats := make([]ConnectionStat, 0)
534	switch family {
535	case kindUDP4.family:
536		index, step, length = getTableInfo(kindUDP4.filename, pmibUDPTable)
537	case kindUDP6.family:
538		index, step, length = getTableInfo(kindUDP6.filename, pmibUDP6Table)
539	}
540
541	if length == 0 {
542		return nil, nil
543	}
544
545	for i := 0; i < length; i++ {
546		switch family {
547		case kindUDP4.family:
548			mibs := (*mibUDPRowOwnerPid)(unsafe.Pointer(&buf[index]))
549			ns := mibs.convertToConnectionStat()
550			stats = append(stats, ns)
551		case kindUDP4.family:
552			mibs := (*mibUDP6RowOwnerPid)(unsafe.Pointer(&buf[index]))
553			ns := mibs.convertToConnectionStat()
554			stats = append(stats, ns)
555		}
556
557		index += step
558	}
559	return stats, nil
560}
561
562// tcpStatuses https://msdn.microsoft.com/en-us/library/windows/desktop/bb485761(v=vs.85).aspx
563var tcpStatuses = map[mibTCPState]string{
564	1:  "CLOSED",
565	2:  "LISTEN",
566	3:  "SYN_SENT",
567	4:  "SYN_RECEIVED",
568	5:  "ESTABLISHED",
569	6:  "FIN_WAIT_1",
570	7:  "FIN_WAIT_2",
571	8:  "CLOSE_WAIT",
572	9:  "CLOSING",
573	10: "LAST_ACK",
574	11: "TIME_WAIT",
575	12: "DELETE",
576}
577
578func getExtendedTcpTable(pTcpTable uintptr, pdwSize *uint32, bOrder bool, ulAf uint32, tableClass tcpTableClass, reserved uint32) (errcode error) {
579	r1, _, _ := syscall.Syscall6(procGetExtendedTCPTable.Addr(), 6, pTcpTable, uintptr(unsafe.Pointer(pdwSize)), getUintptrFromBool(bOrder), uintptr(ulAf), uintptr(tableClass), uintptr(reserved))
580	if r1 != 0 {
581		errcode = syscall.Errno(r1)
582	}
583	return
584}
585
586func getExtendedUdpTable(pUdpTable uintptr, pdwSize *uint32, bOrder bool, ulAf uint32, tableClass udpTableClass, reserved uint32) (errcode error) {
587	r1, _, _ := syscall.Syscall6(procGetExtendedUDPTable.Addr(), 6, pUdpTable, uintptr(unsafe.Pointer(pdwSize)), getUintptrFromBool(bOrder), uintptr(ulAf), uintptr(tableClass), uintptr(reserved))
588	if r1 != 0 {
589		errcode = syscall.Errno(r1)
590	}
591	return
592}
593
594func getUintptrFromBool(b bool) uintptr {
595	if b {
596		return 1
597	}
598	return 0
599}
600
601const anySize = 1
602
603// type MIB_TCP_STATE int32
604type mibTCPState int32
605
606type tcpTableClass int32
607
608const (
609	tcpTableBasicListener tcpTableClass = iota
610	tcpTableBasicConnections
611	tcpTableBasicAll
612	tcpTableOwnerPidListener
613	tcpTableOwnerPidConnections
614	tcpTableOwnerPidAll
615	tcpTableOwnerModuleListener
616	tcpTableOwnerModuleConnections
617	tcpTableOwnerModuleAll
618)
619
620type udpTableClass int32
621
622const (
623	udpTableBasic udpTableClass = iota
624	udpTableOwnerPid
625	udpTableOwnerModule
626)
627
628// TCP
629
630type mibTCPRowOwnerPid struct {
631	DwState      uint32
632	DwLocalAddr  uint32
633	DwLocalPort  uint32
634	DwRemoteAddr uint32
635	DwRemotePort uint32
636	DwOwningPid  uint32
637}
638
639func (m *mibTCPRowOwnerPid) convertToConnectionStat() ConnectionStat {
640	ns := ConnectionStat{
641		Family: kindTCP4.family,
642		Type:   kindTCP4.sockType,
643		Laddr: Addr{
644			IP:   parseIPv4HexString(m.DwLocalAddr),
645			Port: uint32(decodePort(m.DwLocalPort)),
646		},
647		Raddr: Addr{
648			IP:   parseIPv4HexString(m.DwRemoteAddr),
649			Port: uint32(decodePort(m.DwRemotePort)),
650		},
651		Pid:    int32(m.DwOwningPid),
652		Status: tcpStatuses[mibTCPState(m.DwState)],
653	}
654
655	return ns
656}
657
658type mibTCPTableOwnerPid struct {
659	DwNumEntries uint32
660	Table        [anySize]mibTCPRowOwnerPid
661}
662
663type mibTCP6RowOwnerPid struct {
664	UcLocalAddr     [16]byte
665	DwLocalScopeId  uint32
666	DwLocalPort     uint32
667	UcRemoteAddr    [16]byte
668	DwRemoteScopeId uint32
669	DwRemotePort    uint32
670	DwState         uint32
671	DwOwningPid     uint32
672}
673
674func (m *mibTCP6RowOwnerPid) convertToConnectionStat() ConnectionStat {
675	ns := ConnectionStat{
676		Family: kindTCP6.family,
677		Type:   kindTCP6.sockType,
678		Laddr: Addr{
679			IP:   parseIPv6HexString(m.UcLocalAddr),
680			Port: uint32(decodePort(m.DwLocalPort)),
681		},
682		Raddr: Addr{
683			IP:   parseIPv6HexString(m.UcRemoteAddr),
684			Port: uint32(decodePort(m.DwRemotePort)),
685		},
686		Pid:    int32(m.DwOwningPid),
687		Status: tcpStatuses[mibTCPState(m.DwState)],
688	}
689
690	return ns
691}
692
693type mibTCP6TableOwnerPid struct {
694	DwNumEntries uint32
695	Table        [anySize]mibTCP6RowOwnerPid
696}
697
698type pmibTCPTableOwnerPidAll *mibTCPTableOwnerPid
699type pmibTCP6TableOwnerPidAll *mibTCP6TableOwnerPid
700
701// UDP
702
703type mibUDPRowOwnerPid struct {
704	DwLocalAddr uint32
705	DwLocalPort uint32
706	DwOwningPid uint32
707}
708
709func (m *mibUDPRowOwnerPid) convertToConnectionStat() ConnectionStat {
710	ns := ConnectionStat{
711		Family: kindUDP4.family,
712		Type:   kindUDP4.sockType,
713		Laddr: Addr{
714			IP:   parseIPv4HexString(m.DwLocalAddr),
715			Port: uint32(decodePort(m.DwLocalPort)),
716		},
717		Pid: int32(m.DwOwningPid),
718	}
719
720	return ns
721}
722
723type mibUDPTableOwnerPid struct {
724	DwNumEntries uint32
725	Table        [anySize]mibUDPRowOwnerPid
726}
727
728type mibUDP6RowOwnerPid struct {
729	UcLocalAddr    [16]byte
730	DwLocalScopeId uint32
731	DwLocalPort    uint32
732	DwOwningPid    uint32
733}
734
735func (m *mibUDP6RowOwnerPid) convertToConnectionStat() ConnectionStat {
736	ns := ConnectionStat{
737		Family: kindUDP6.family,
738		Type:   kindUDP6.sockType,
739		Laddr: Addr{
740			IP:   parseIPv6HexString(m.UcLocalAddr),
741			Port: uint32(decodePort(m.DwLocalPort)),
742		},
743		Pid: int32(m.DwOwningPid),
744	}
745
746	return ns
747}
748
749type mibUDP6TableOwnerPid struct {
750	DwNumEntries uint32
751	Table        [anySize]mibUDP6RowOwnerPid
752}
753
754type pmibUDPTableOwnerPid *mibUDPTableOwnerPid
755type pmibUDP6TableOwnerPid *mibUDP6TableOwnerPid
756
757func decodePort(port uint32) uint16 {
758	return syscall.Ntohs(uint16(port))
759}
760
761func parseIPv4HexString(addr uint32) string {
762	return fmt.Sprintf("%d.%d.%d.%d", addr&255, addr>>8&255, addr>>16&255, addr>>24&255)
763}
764
765func parseIPv6HexString(addr [16]byte) string {
766	var ret [16]byte
767	for i := 0; i < 16; i++ {
768		ret[i] = uint8(addr[i])
769	}
770
771	// convert []byte to net.IP
772	ip := net.IP(ret[:])
773	return ip.String()
774}
775