mirror of
https://github.com/microsoft/ethr.git
synced 2024-09-20 14:56:15 +08:00
945d59c33b
* Intermediate checkin * Last check-in before deleting ACK message in Ethr. * Support for client port, throttling and tos almost working. * Most functionality working as expected with code all cleaned up. * Linux/OSX Fixes. * Fix handshake mechanism. * Minor cleanup. * More improvements for external mode. * Improve admin-mode, root user permission checking. * Improve detection of IP version for ICMP. * Update README.md
294 lines
7.8 KiB
Go
294 lines
7.8 KiB
Go
// +build windows
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) Microsoft. All rights reserved.
|
|
// Licensed under the MIT license.
|
|
// See LICENSE.txt file in the project root for full license information.
|
|
//-----------------------------------------------------------------------------
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
"syscall"
|
|
"unsafe"
|
|
|
|
tm "github.com/nsf/termbox-go"
|
|
)
|
|
|
|
var (
|
|
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
user32 = syscall.NewLazyDLL("user32.dll")
|
|
iphlpapi = syscall.NewLazyDLL("iphlpapi.dll")
|
|
|
|
proc_get_tcp_statistics_ex = iphlpapi.NewProc("GetTcpStatisticsEx")
|
|
proc_get_if_entry2 = iphlpapi.NewProc("GetIfEntry2")
|
|
proc_get_console_window = kernel32.NewProc("GetConsoleWindow")
|
|
proc_get_system_menu = user32.NewProc("GetSystemMenu")
|
|
proc_delete_menu = user32.NewProc("DeleteMenu")
|
|
)
|
|
|
|
type ethrNetDevInfo struct {
|
|
bytes uint64
|
|
packets uint64
|
|
drop uint64
|
|
errs uint64
|
|
}
|
|
|
|
func getNetDevStats(stats *ethrNetStat) {
|
|
ifs, err := net.Interfaces()
|
|
if err != nil {
|
|
ui.printErr("%v", err)
|
|
return
|
|
}
|
|
|
|
for _, ifi := range ifs {
|
|
if (ifi.Flags&net.FlagUp) == 0 || strings.Contains(ifi.Name, "Pseudo") {
|
|
continue
|
|
}
|
|
row, err := getIfEntry2(uint32(ifi.Index))
|
|
if err != nil {
|
|
ui.printErr("%v", err)
|
|
return
|
|
}
|
|
rxInfo := ethrNetDevInfo{
|
|
bytes: uint64(row.InOctets),
|
|
packets: uint64(row.InUcastPkts),
|
|
drop: uint64(row.InDiscards),
|
|
errs: uint64(row.InErrors),
|
|
}
|
|
txInfo := ethrNetDevInfo{
|
|
bytes: uint64(row.OutOctets),
|
|
packets: uint64(row.OutUcastPkts),
|
|
drop: uint64(row.OutDiscards),
|
|
errs: uint64(row.OutErrors),
|
|
}
|
|
netStats := ethrNetDevStat{
|
|
interfaceName: ifi.Name,
|
|
rxBytes: rxInfo.bytes,
|
|
txBytes: txInfo.bytes,
|
|
rxPkts: rxInfo.packets,
|
|
txPkts: txInfo.packets,
|
|
}
|
|
stats.netDevStats = append(stats.netDevStats, netStats)
|
|
}
|
|
}
|
|
|
|
type mib_tcpstats struct {
|
|
DwRtoAlgorithm uint32
|
|
DwRtoMin uint32
|
|
DwRtoMax uint32
|
|
DwMaxConn uint32
|
|
DwActiveOpens uint32
|
|
DwPassiveOpens uint32
|
|
DwAttemptFails uint32
|
|
DwEstabResets uint32
|
|
DwCurrEstab uint32
|
|
DwInSegs uint32
|
|
DwOutSegs uint32
|
|
DwRetransSegs uint32
|
|
DwInErrs uint32
|
|
DwOutRsts uint32
|
|
DwNumConns uint32
|
|
}
|
|
|
|
const (
|
|
AF_INET = 2
|
|
AF_INET6 = 23
|
|
)
|
|
|
|
func getTCPStats(stats *ethrNetStat) (errcode error) {
|
|
tcpStats := &mib_tcpstats{}
|
|
r0, _, _ := syscall.Syscall(proc_get_tcp_statistics_ex.Addr(), 2,
|
|
uintptr(unsafe.Pointer(tcpStats)), uintptr(AF_INET), 0)
|
|
|
|
if r0 != 0 {
|
|
errcode = syscall.Errno(r0)
|
|
return
|
|
}
|
|
stats.tcpStats.segRetrans = uint64(tcpStats.DwRetransSegs)
|
|
return
|
|
}
|
|
|
|
type guid struct {
|
|
Data1 uint32
|
|
Data2 uint16
|
|
Data3 uint16
|
|
Data4 [8]byte
|
|
}
|
|
|
|
const (
|
|
MAX_STRING_SIZE = 256
|
|
MAX_PHYS_ADDRESS_LENGTH = 32
|
|
pad0for64_4for32 = 0
|
|
)
|
|
|
|
type mibIfRow2 struct {
|
|
InterfaceLuid uint64
|
|
InterfaceIndex uint32
|
|
InterfaceGuid guid
|
|
Alias [MAX_STRING_SIZE + 1]uint16
|
|
Description [MAX_STRING_SIZE + 1]uint16
|
|
PhysicalAddressLength uint32
|
|
PhysicalAddress [MAX_PHYS_ADDRESS_LENGTH]uint8
|
|
PermanentPhysicalAddress [MAX_PHYS_ADDRESS_LENGTH]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
|
|
}
|
|
|
|
func getIfEntry2(ifIndex uint32) (mibIfRow2, error) {
|
|
var res *mibIfRow2
|
|
|
|
res = &mibIfRow2{InterfaceIndex: ifIndex}
|
|
r0, _, _ := syscall.Syscall(proc_get_if_entry2.Addr(), 1,
|
|
uintptr(unsafe.Pointer(res)), 0, 0)
|
|
if r0 != 0 {
|
|
return mibIfRow2{}, syscall.Errno(r0)
|
|
}
|
|
return *res, nil
|
|
}
|
|
|
|
func hideCursor() {
|
|
tm.HideCursor()
|
|
}
|
|
|
|
const (
|
|
MF_BYCOMMAND = 0x00000000
|
|
SC_CLOSE = 0xF060
|
|
SC_MINIMIZE = 0xF020
|
|
SC_MAXIMIZE = 0xF030
|
|
SC_SIZE = 0xF000
|
|
)
|
|
|
|
func blockWindowResize() {
|
|
h, _, err := syscall.Syscall(proc_get_console_window.Addr(), 0, 0, 0, 0)
|
|
if err != 0 {
|
|
return
|
|
}
|
|
|
|
sysMenu, _, err := syscall.Syscall(proc_get_system_menu.Addr(), 2, h, 0, 0)
|
|
if err != 0 {
|
|
return
|
|
}
|
|
|
|
syscall.Syscall(proc_delete_menu.Addr(), 3, sysMenu, SC_MAXIMIZE, MF_BYCOMMAND)
|
|
syscall.Syscall(proc_delete_menu.Addr(), 3, sysMenu, SC_SIZE, MF_BYCOMMAND)
|
|
}
|
|
|
|
func setSockOptInt(fd uintptr, level, opt, val int) (err error) {
|
|
err = syscall.SetsockoptInt(syscall.Handle(fd), level, opt, val)
|
|
if err != nil {
|
|
ui.printErr("Failed to set socket option (%v) to value (%v) during Dial. Error: %s", opt, val, err)
|
|
}
|
|
return
|
|
}
|
|
|
|
const (
|
|
SIO_RCVALL = syscall.IOC_IN | syscall.IOC_VENDOR | 1
|
|
RCVALL_OFF = 0
|
|
RCVALL_ON = 1
|
|
RCVALL_SOCKETLEVELONLY = 2
|
|
RCVALL_IPLEVEL = 3
|
|
)
|
|
|
|
func IcmpNewConn(address string) (net.PacketConn, error) {
|
|
// This is an attempt to work around the problem described here:
|
|
// https://github.com/golang/go/issues/38427
|
|
|
|
// First, get the correct local interface address, as SIO_RCVALL can't be set on a 0.0.0.0 listeners.
|
|
dialedConn, err := net.Dial(Icmp(), address)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
localAddr := dialedConn.LocalAddr()
|
|
dialedConn.Close()
|
|
|
|
// Configure the setup routine in order to extract the socket handle.
|
|
var socketHandle syscall.Handle
|
|
cfg := net.ListenConfig{
|
|
Control: func(network, address string, c syscall.RawConn) error {
|
|
return c.Control(func(s uintptr) {
|
|
socketHandle = syscall.Handle(s)
|
|
})
|
|
},
|
|
}
|
|
|
|
// Bind to interface.
|
|
conn, err := cfg.ListenPacket(context.Background(), Icmp(), localAddr.String())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Set socket option to receive all packets, such as ICMP error messages.
|
|
// This is somewhat dirty, as there is guarantee that socketHandle is still valid.
|
|
// WARNING: The Windows Firewall might just drop the incoming packets you might want to receive.
|
|
unused := uint32(0) // Documentation states that this is unused, but WSAIoctl fails without it.
|
|
flag := uint32(RCVALL_IPLEVEL)
|
|
size := uint32(unsafe.Sizeof(flag))
|
|
err = syscall.WSAIoctl(socketHandle, SIO_RCVALL, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &unused, nil, 0)
|
|
if err != nil {
|
|
// Ignore the error as for ICMP related TraceRoute, this is not required.
|
|
}
|
|
|
|
return conn, nil
|
|
}
|
|
|
|
func VerifyPermissionForTest(testID EthrTestID) {
|
|
if (testID.Type == TraceRoute || testID.Type == MyTraceRoute) &&
|
|
(testID.Protocol == TCP) {
|
|
if !IsAdmin() {
|
|
ui.printMsg("Warning: You are not running as administrator. For %s based %s",
|
|
protoToString(testID.Protocol), testToString(testID.Type))
|
|
ui.printMsg("test, running as administrator is required.\n")
|
|
}
|
|
}
|
|
}
|
|
|
|
func IsAdmin() bool {
|
|
c, err := os.Open("\\\\.\\PHYSICALDRIVE0")
|
|
if err != nil {
|
|
ui.printDbg("Process is not running as admin. Error: %v", err)
|
|
return false
|
|
}
|
|
c.Close()
|
|
return true
|
|
}
|
|
|
|
func SetTClass(fd uintptr, tos int) {
|
|
return
|
|
}
|