修正 Win32 封装语义并补齐关键结构体/进程测试覆盖
- 修正 WTS 会话相关类型、枚举与活动会话选择逻辑 - 对齐 FILE_ID_DESCRIPTOR 布局与 FILE_ID_TYPE 语义,修复 OpenFileById 调用前提 - 修正 user32/shell32/kernel32 部分 API 的返回值、参数个数与错误处理 - 完善剪贴板更新格式读取的缓冲区重试逻辑 - 补充常用进程、线程、调试、桌面与会话 helper - 增加结构体布局、会话查询、剪贴板、CreateProcess 等回归测试 - 将默认 CreateProcess 相关测试切到 helper 进程,并保留显式开启的 cmd.exe 集成覆盖
This commit is contained in:
parent
03b79c4247
commit
0f82ba044b
270
advapi32.go
270
advapi32.go
@ -1,7 +1,6 @@
|
|||||||
package win32api
|
package win32api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@ -12,16 +11,11 @@ func DuplicateTokenEx(hExistingToken HANDLE, dwDesiredAccess DWORD,
|
|||||||
lpTokenAttributes uintptr, ImpersonationLevel int,
|
lpTokenAttributes uintptr, ImpersonationLevel int,
|
||||||
TokenType TOKEN_TYPE, phNewToken *TOKEN) error {
|
TokenType TOKEN_TYPE, phNewToken *TOKEN) error {
|
||||||
|
|
||||||
advapi32, err := syscall.LoadLibrary("advapi32.dll")
|
Dup, err := getProcAddr("advapi32.dll", "DuplicateTokenEx")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Can't Load Advapi32 API")
|
return err
|
||||||
}
|
}
|
||||||
defer syscall.FreeLibrary(advapi32)
|
r, _, errno := syscall.Syscall6(Dup, 6, uintptr(hExistingToken), uintptr(dwDesiredAccess), lpTokenAttributes, uintptr(ImpersonationLevel),
|
||||||
Dup, err := syscall.GetProcAddress(syscall.Handle(advapi32), "DuplicateTokenEx")
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("Can't Load WTSQueryUserToken API")
|
|
||||||
}
|
|
||||||
r, _, errno := syscall.Syscall6(uintptr(Dup), 6, uintptr(hExistingToken), uintptr(dwDesiredAccess), lpTokenAttributes, uintptr(ImpersonationLevel),
|
|
||||||
uintptr(TokenType), uintptr(unsafe.Pointer(phNewToken)))
|
uintptr(TokenType), uintptr(unsafe.Pointer(phNewToken)))
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
return error(errno)
|
return error(errno)
|
||||||
@ -31,20 +25,19 @@ func DuplicateTokenEx(hExistingToken HANDLE, dwDesiredAccess DWORD,
|
|||||||
|
|
||||||
func CreateProcessAsUser(hToken TOKEN, lpApplicationName, lpCommandLine string,
|
func CreateProcessAsUser(hToken TOKEN, lpApplicationName, lpCommandLine string,
|
||||||
lpProcessAttributes, lpThreadAttributes, bInheritHandles uintptr,
|
lpProcessAttributes, lpThreadAttributes, bInheritHandles uintptr,
|
||||||
dwCreationFlags uint16, lpEnvironment HANDLE, lpCurrentDirectory string,
|
dwCreationFlags DWORD, lpEnvironment HANDLE, lpCurrentDirectory string,
|
||||||
lpStartupInfo *StartupInfo, lpProcessInformation *ProcessInformation) error {
|
lpStartupInfo *StartupInfo, lpProcessInformation *ProcessInformation) error {
|
||||||
var (
|
var (
|
||||||
commandLine uintptr = 0
|
applicationName uintptr
|
||||||
workingDir uintptr = 0
|
commandLine uintptr
|
||||||
|
workingDir uintptr
|
||||||
)
|
)
|
||||||
advapi32, err := syscall.LoadLibrary("advapi32.dll")
|
CPAU, err := getProcAddr("advapi32.dll", "CreateProcessAsUserW")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Can't Load Advapi32 API")
|
return err
|
||||||
}
|
}
|
||||||
defer syscall.FreeLibrary(advapi32)
|
if len(lpApplicationName) > 0 {
|
||||||
CPAU, err := syscall.GetProcAddress(syscall.Handle(advapi32), "CreateProcessAsUserW")
|
applicationName = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpApplicationName)))
|
||||||
if err != nil {
|
|
||||||
return errors.New("Can't Load CreateProcessAsUserW API")
|
|
||||||
}
|
}
|
||||||
if len(lpCommandLine) > 0 {
|
if len(lpCommandLine) > 0 {
|
||||||
commandLine = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpCommandLine)))
|
commandLine = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpCommandLine)))
|
||||||
@ -52,7 +45,7 @@ func CreateProcessAsUser(hToken TOKEN, lpApplicationName, lpCommandLine string,
|
|||||||
if len(lpCurrentDirectory) > 0 {
|
if len(lpCurrentDirectory) > 0 {
|
||||||
workingDir = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpCurrentDirectory)))
|
workingDir = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpCurrentDirectory)))
|
||||||
}
|
}
|
||||||
r, _, errno := syscall.Syscall12(uintptr(CPAU), 11, uintptr(hToken), uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpApplicationName))),
|
r, _, errno := syscall.Syscall12(CPAU, 11, uintptr(hToken), applicationName,
|
||||||
commandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, uintptr(dwCreationFlags), uintptr(lpEnvironment),
|
commandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, uintptr(dwCreationFlags), uintptr(lpEnvironment),
|
||||||
workingDir, uintptr(unsafe.Pointer(lpStartupInfo)), uintptr(unsafe.Pointer(lpProcessInformation)), 0)
|
workingDir, uintptr(unsafe.Pointer(lpStartupInfo)), uintptr(unsafe.Pointer(lpProcessInformation)), 0)
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
@ -62,18 +55,241 @@ func CreateProcessAsUser(hToken TOKEN, lpApplicationName, lpCommandLine string,
|
|||||||
}
|
}
|
||||||
func GetTokenInformation(TokenHandle HANDLE, TokenInformationClass, TokenInformation,
|
func GetTokenInformation(TokenHandle HANDLE, TokenInformationClass, TokenInformation,
|
||||||
TokenInformationLength uintptr, ReturnLength *uintptr) error {
|
TokenInformationLength uintptr, ReturnLength *uintptr) error {
|
||||||
advapi32, err := syscall.LoadLibrary("advapi32.dll")
|
GTI, err := getProcAddr("advapi32.dll", "GetTokenInformation")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Can't Load Advapi32 API")
|
return err
|
||||||
}
|
}
|
||||||
defer syscall.FreeLibrary(advapi32)
|
if r, _, errno := syscall.Syscall6(GTI, 5, uintptr(TokenHandle), TokenInformationClass,
|
||||||
GTI, err := syscall.GetProcAddress(syscall.Handle(advapi32), "GetTokenInformation")
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("Can't Load GetTokenInformation API")
|
|
||||||
}
|
|
||||||
if r, _, errno := syscall.Syscall6(uintptr(GTI), 5, uintptr(TokenHandle), TokenInformationClass,
|
|
||||||
TokenInformation, TokenInformationLength, uintptr(unsafe.Pointer(ReturnLength)), 0); r == 0 {
|
TokenInformation, TokenInformationLength, uintptr(unsafe.Pointer(ReturnLength)), 0); r == 0 {
|
||||||
return error(errno)
|
return error(errno)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetUserName() (string, error) {
|
||||||
|
gun, err := getProcAddr("advapi32.dll", "GetUserNameW")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
size := uint32(64)
|
||||||
|
for {
|
||||||
|
buf := make([]uint16, size)
|
||||||
|
n := uint32(len(buf))
|
||||||
|
r, _, errno := syscall.Syscall(gun, 2, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0)
|
||||||
|
if r != 0 {
|
||||||
|
return syscall.UTF16ToString(buf), nil
|
||||||
|
}
|
||||||
|
if errno == syscall.ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
if n > size {
|
||||||
|
size = n
|
||||||
|
} else {
|
||||||
|
size *= 2
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if errno != 0 {
|
||||||
|
return "", error(errno)
|
||||||
|
}
|
||||||
|
return "", syscall.EINVAL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenProcessToken(ProcessHandle HANDLE, DesiredAccess DWORD, TokenHandle *TOKEN) error {
|
||||||
|
if TokenHandle == nil {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
proc, err := getProcAddr("advapi32.dll", "OpenProcessToken")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, errno := syscall.Syscall(proc, 3, uintptr(ProcessHandle), uintptr(DesiredAccess), uintptr(unsafe.Pointer(TokenHandle)))
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckTokenMembership(tokenHandle HANDLE, sidToCheck unsafe.Pointer) (bool, error) {
|
||||||
|
if sidToCheck == nil {
|
||||||
|
return false, syscall.EINVAL
|
||||||
|
}
|
||||||
|
proc, err := getProcAddr("advapi32.dll", "CheckTokenMembership")
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
var isMember int32
|
||||||
|
r, _, errno := syscall.Syscall(proc, 3,
|
||||||
|
uintptr(tokenHandle),
|
||||||
|
uintptr(sidToCheck),
|
||||||
|
uintptr(unsafe.Pointer(&isMember)),
|
||||||
|
)
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return false, error(errno)
|
||||||
|
}
|
||||||
|
return false, syscall.EINVAL
|
||||||
|
}
|
||||||
|
return isMember != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LookupPrivilegeValue(lpSystemName, lpName string, lpLuid *LUID) error {
|
||||||
|
if lpLuid == nil {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
proc, err := getProcAddr("advapi32.dll", "LookupPrivilegeValueW")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var systemNamePtr uintptr
|
||||||
|
if len(lpSystemName) > 0 {
|
||||||
|
systemNamePtr = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpSystemName)))
|
||||||
|
}
|
||||||
|
r, _, errno := syscall.Syscall(proc, 3,
|
||||||
|
systemNamePtr,
|
||||||
|
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpName))),
|
||||||
|
uintptr(unsafe.Pointer(lpLuid)),
|
||||||
|
)
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AdjustTokenPrivileges(TokenHandle TOKEN, DisableAllPrivileges bool, NewState *TOKEN_PRIVILEGES, BufferLength DWORD,
|
||||||
|
PreviousState *TOKEN_PRIVILEGES, ReturnLength *DWORD) error {
|
||||||
|
proc, err := getProcAddr("advapi32.dll", "AdjustTokenPrivileges")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
disableAll := uintptr(0)
|
||||||
|
if DisableAllPrivileges {
|
||||||
|
disableAll = 1
|
||||||
|
}
|
||||||
|
r, _, errno := syscall.Syscall6(proc, 6,
|
||||||
|
uintptr(TokenHandle),
|
||||||
|
disableAll,
|
||||||
|
uintptr(unsafe.Pointer(NewState)),
|
||||||
|
uintptr(BufferLength),
|
||||||
|
uintptr(unsafe.Pointer(PreviousState)),
|
||||||
|
uintptr(unsafe.Pointer(ReturnLength)),
|
||||||
|
)
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
if errno == windows.ERROR_NOT_ALL_ASSIGNED {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RevertToSelf() error {
|
||||||
|
proc, err := getProcAddr("advapi32.dll", "RevertToSelf")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, errno := syscall.Syscall(proc, 0, 0, 0, 0)
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateProcessWithToken(hToken TOKEN, dwLogonFlags DWORD, lpApplicationName, lpCommandLine string,
|
||||||
|
dwCreationFlags DWORD, lpEnvironment HANDLE, lpCurrentDirectory string,
|
||||||
|
lpStartupInfo *StartupInfo, lpProcessInformation *ProcessInformation) error {
|
||||||
|
var (
|
||||||
|
applicationName uintptr
|
||||||
|
commandLine uintptr
|
||||||
|
currentDir uintptr
|
||||||
|
)
|
||||||
|
if len(lpApplicationName) > 0 {
|
||||||
|
applicationName = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpApplicationName)))
|
||||||
|
}
|
||||||
|
if len(lpCommandLine) > 0 {
|
||||||
|
commandLine = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpCommandLine)))
|
||||||
|
}
|
||||||
|
if len(lpCurrentDirectory) > 0 {
|
||||||
|
currentDir = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(lpCurrentDirectory)))
|
||||||
|
}
|
||||||
|
|
||||||
|
proc, err := getProcAddr("advapi32.dll", "CreateProcessWithTokenW")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, errno := syscall.Syscall12(proc, 9,
|
||||||
|
uintptr(hToken),
|
||||||
|
uintptr(dwLogonFlags),
|
||||||
|
applicationName,
|
||||||
|
commandLine,
|
||||||
|
uintptr(dwCreationFlags),
|
||||||
|
uintptr(lpEnvironment),
|
||||||
|
currentDir,
|
||||||
|
uintptr(unsafe.Pointer(lpStartupInfo)),
|
||||||
|
uintptr(unsafe.Pointer(lpProcessInformation)),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsTokenElevated(token TOKEN) (bool, error) {
|
||||||
|
var elevation TOKEN_ELEVATION
|
||||||
|
var retLen uintptr
|
||||||
|
if err := GetTokenInformation(
|
||||||
|
HANDLE(token),
|
||||||
|
TokenElevation,
|
||||||
|
uintptr(unsafe.Pointer(&elevation)),
|
||||||
|
uintptr(unsafe.Sizeof(elevation)),
|
||||||
|
&retLen,
|
||||||
|
); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return elevation.TokenIsElevated != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsCurrentProcessElevated() (bool, error) {
|
||||||
|
processHandle, err := syscall.GetCurrentProcess()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
var token TOKEN
|
||||||
|
if err := OpenProcessToken(HANDLE(processHandle), TOKEN_QUERY, &token); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = CloseHandle(HANDLE(token))
|
||||||
|
}()
|
||||||
|
return IsTokenElevated(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsCurrentUserInAdminGroup() (bool, error) {
|
||||||
|
adminSID, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Passing tokenHandle=0 lets Windows use the calling thread/process effective token.
|
||||||
|
return CheckTokenMembership(0, unsafe.Pointer(adminSID))
|
||||||
|
}
|
||||||
|
|||||||
@ -3,3 +3,53 @@ package win32api
|
|||||||
type TOKEN_LINKED_TOKEN struct {
|
type TOKEN_LINKED_TOKEN struct {
|
||||||
LinkedToken TOKEN
|
LinkedToken TOKEN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TOKEN_ASSIGN_PRIMARY DWORD = 0x0001
|
||||||
|
TOKEN_DUPLICATE DWORD = 0x0002
|
||||||
|
TOKEN_IMPERSONATE DWORD = 0x0004
|
||||||
|
TOKEN_QUERY DWORD = 0x0008
|
||||||
|
TOKEN_QUERY_SOURCE DWORD = 0x0010
|
||||||
|
TOKEN_ADJUST_PRIVILEGES DWORD = 0x0020
|
||||||
|
TOKEN_ADJUST_GROUPS DWORD = 0x0040
|
||||||
|
TOKEN_ADJUST_DEFAULT DWORD = 0x0080
|
||||||
|
TOKEN_ADJUST_SESSIONID DWORD = 0x0100
|
||||||
|
TOKEN_ALL_ACCESS DWORD = 0xF01FF
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SE_PRIVILEGE_ENABLED DWORD = 0x00000002
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LOGON_WITH_PROFILE DWORD = 0x00000001
|
||||||
|
LOGON_NETCREDENTIALS_ONLY DWORD = 0x00000002
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SE_DEBUG_NAME = "SeDebugPrivilege"
|
||||||
|
SE_CHANGE_NOTIFY_NAME = "SeChangeNotifyPrivilege"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TokenElevation uintptr = 20
|
||||||
|
)
|
||||||
|
|
||||||
|
type LUID struct {
|
||||||
|
LowPart DWORD
|
||||||
|
HighPart int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type LUID_AND_ATTRIBUTES struct {
|
||||||
|
Luid LUID
|
||||||
|
Attributes DWORD
|
||||||
|
}
|
||||||
|
|
||||||
|
type TOKEN_PRIVILEGES struct {
|
||||||
|
PrivilegeCount DWORD
|
||||||
|
Privileges [1]LUID_AND_ATTRIBUTES
|
||||||
|
}
|
||||||
|
|
||||||
|
type TOKEN_ELEVATION struct {
|
||||||
|
TokenIsElevated DWORD
|
||||||
|
}
|
||||||
|
|||||||
1478
common_api_test.go
Normal file
1478
common_api_test.go
Normal file
File diff suppressed because it is too large
Load Diff
11
doc.go
Normal file
11
doc.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Package win32api provides thin Win32 API wrappers for Go on Windows.
|
||||||
|
//
|
||||||
|
// The package keeps the exported shape close to the native APIs:
|
||||||
|
// strings are accepted as Go strings where practical, handles and structs are
|
||||||
|
// kept explicit, and higher-level helpers are added only for common workflows
|
||||||
|
// such as session, adapter, process, thread, and module enumeration.
|
||||||
|
//
|
||||||
|
// Current coverage focuses on the parts used by the surrounding projects:
|
||||||
|
// process and token control, file and memory operations, sessions and desktop
|
||||||
|
// access, window helpers, socket/network helpers, and basic debug workflows.
|
||||||
|
package win32api
|
||||||
350
iphlpapi.go
Normal file
350
iphlpapi.go
Normal file
@ -0,0 +1,350 @@
|
|||||||
|
package win32api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetAdaptersAddresses(family, flags uint32) ([]AdapterAddressInfo, error) {
|
||||||
|
size := uint32(15 * 1024)
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
buf := make([]byte, size)
|
||||||
|
head := (*windows.IpAdapterAddresses)(unsafe.Pointer(&buf[0]))
|
||||||
|
err := windows.GetAdaptersAddresses(family, flags, 0, head, &size)
|
||||||
|
if err == nil {
|
||||||
|
return collectAdapterAddressInfo(head), nil
|
||||||
|
}
|
||||||
|
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_BUFFER_OVERFLOW {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("GetAdaptersAddresses exceeded retry limit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetIfTable2() ([]MIB_IF_ROW2, error) {
|
||||||
|
proc, err := getProcAddr("iphlpapi.dll", "GetIfTable2")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var table *MIB_IF_TABLE2
|
||||||
|
r, _, _ := syscall.Syscall(proc, 1, uintptr(unsafe.Pointer(&table)), 0, 0)
|
||||||
|
if r != 0 {
|
||||||
|
return nil, syscall.Errno(r)
|
||||||
|
}
|
||||||
|
if table == nil {
|
||||||
|
return nil, fmt.Errorf("GetIfTable2 returned nil table")
|
||||||
|
}
|
||||||
|
defer FreeMibTable(unsafe.Pointer(table))
|
||||||
|
|
||||||
|
count := int(table.NumEntries)
|
||||||
|
rows := make([]MIB_IF_ROW2, 0, count)
|
||||||
|
rowSize := unsafe.Sizeof(table.Table[0])
|
||||||
|
base := uintptr(unsafe.Pointer(&table.Table[0]))
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
row := (*MIB_IF_ROW2)(unsafe.Pointer(base + uintptr(i)*rowSize))
|
||||||
|
rows = append(rows, *row)
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetIfEntry2(row *MIB_IF_ROW2) error {
|
||||||
|
if row == nil {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
proc, err := getProcAddr("iphlpapi.dll", "GetIfEntry2")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 1, uintptr(unsafe.Pointer(row)), 0, 0)
|
||||||
|
if r != 0 {
|
||||||
|
return syscall.Errno(r)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FreeMibTable(memory unsafe.Pointer) {
|
||||||
|
if memory == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
proc, err := getProcAddr("iphlpapi.dll", "FreeMibTable")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
syscall.Syscall(proc, 1, uintptr(memory), 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetExtendedTcp4Table(order bool, tableClass TCP_TABLE_CLASS) ([]MIB_TCPROW_OWNER_PID, error) {
|
||||||
|
if err := validateTCPOwnerPIDTableClass(tableClass); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf, err := getExtendedTCPTable(order, AF_INET, tableClass)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
table := (*MIB_TCPTABLE_OWNER_PID)(unsafe.Pointer(&buf[0]))
|
||||||
|
count := int(table.NumEntries)
|
||||||
|
rows := make([]MIB_TCPROW_OWNER_PID, 0, count)
|
||||||
|
rowSize := unsafe.Sizeof(table.Table[0])
|
||||||
|
base := uintptr(unsafe.Pointer(&table.Table[0]))
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
row := (*MIB_TCPROW_OWNER_PID)(unsafe.Pointer(base + uintptr(i)*rowSize))
|
||||||
|
rows = append(rows, *row)
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetExtendedTcp6Table(order bool, tableClass TCP_TABLE_CLASS) ([]MIB_TCP6ROW_OWNER_PID, error) {
|
||||||
|
if err := validateTCPOwnerPIDTableClass(tableClass); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf, err := getExtendedTCPTable(order, AF_INET6, tableClass)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
table := (*MIB_TCP6TABLE_OWNER_PID)(unsafe.Pointer(&buf[0]))
|
||||||
|
count := int(table.NumEntries)
|
||||||
|
rows := make([]MIB_TCP6ROW_OWNER_PID, 0, count)
|
||||||
|
rowSize := unsafe.Sizeof(table.Table[0])
|
||||||
|
base := uintptr(unsafe.Pointer(&table.Table[0]))
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
row := (*MIB_TCP6ROW_OWNER_PID)(unsafe.Pointer(base + uintptr(i)*rowSize))
|
||||||
|
rows = append(rows, *row)
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetExtendedUdp4Table(order bool, tableClass UDP_TABLE_CLASS) ([]MIB_UDPROW_OWNER_PID, error) {
|
||||||
|
if err := validateUDPOwnerPIDTableClass(tableClass); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf, err := getExtendedUDPTable(order, AF_INET, tableClass)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
table := (*MIB_UDPTABLE_OWNER_PID)(unsafe.Pointer(&buf[0]))
|
||||||
|
count := int(table.NumEntries)
|
||||||
|
rows := make([]MIB_UDPROW_OWNER_PID, 0, count)
|
||||||
|
rowSize := unsafe.Sizeof(table.Table[0])
|
||||||
|
base := uintptr(unsafe.Pointer(&table.Table[0]))
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
row := (*MIB_UDPROW_OWNER_PID)(unsafe.Pointer(base + uintptr(i)*rowSize))
|
||||||
|
rows = append(rows, *row)
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetExtendedUdp6Table(order bool, tableClass UDP_TABLE_CLASS) ([]MIB_UDP6ROW_OWNER_PID, error) {
|
||||||
|
if err := validateUDPOwnerPIDTableClass(tableClass); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf, err := getExtendedUDPTable(order, AF_INET6, tableClass)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
table := (*MIB_UDP6TABLE_OWNER_PID)(unsafe.Pointer(&buf[0]))
|
||||||
|
count := int(table.NumEntries)
|
||||||
|
rows := make([]MIB_UDP6ROW_OWNER_PID, 0, count)
|
||||||
|
rowSize := unsafe.Sizeof(table.Table[0])
|
||||||
|
base := uintptr(unsafe.Pointer(&table.Table[0]))
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
row := (*MIB_UDP6ROW_OWNER_PID)(unsafe.Pointer(base + uintptr(i)*rowSize))
|
||||||
|
rows = append(rows, *row)
|
||||||
|
}
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (row MIB_TCPROW_OWNER_PID) LocalPortHost() uint16 {
|
||||||
|
return Ntohs(uint16(row.LocalPort))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (row MIB_TCPROW_OWNER_PID) RemotePortHost() uint16 {
|
||||||
|
return Ntohs(uint16(row.RemotePort))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (row MIB_TCP6ROW_OWNER_PID) LocalPortHost() uint16 {
|
||||||
|
return Ntohs(uint16(row.LocalPort))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (row MIB_TCP6ROW_OWNER_PID) RemotePortHost() uint16 {
|
||||||
|
return Ntohs(uint16(row.RemotePort))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (row MIB_UDPROW_OWNER_PID) LocalPortHost() uint16 {
|
||||||
|
return Ntohs(uint16(row.LocalPort))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (row MIB_UDP6ROW_OWNER_PID) LocalPortHost() uint16 {
|
||||||
|
return Ntohs(uint16(row.LocalPort))
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateTCPOwnerPIDTableClass(tableClass TCP_TABLE_CLASS) error {
|
||||||
|
switch tableClass {
|
||||||
|
case TCP_TABLE_OWNER_PID_LISTENER, TCP_TABLE_OWNER_PID_CONNECTIONS, TCP_TABLE_OWNER_PID_ALL:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported TCP owner-pid table class: %d", tableClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateUDPOwnerPIDTableClass(tableClass UDP_TABLE_CLASS) error {
|
||||||
|
if tableClass == UDP_TABLE_OWNER_PID {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unsupported UDP owner-pid table class: %d", tableClass)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExtendedTCPTable(order bool, family int, tableClass TCP_TABLE_CLASS) ([]byte, error) {
|
||||||
|
proc, err := getProcAddr("iphlpapi.dll", "GetExtendedTcpTable")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return getExtendedIPTable(proc, order, family, uint32(tableClass))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExtendedUDPTable(order bool, family int, tableClass UDP_TABLE_CLASS) ([]byte, error) {
|
||||||
|
proc, err := getProcAddr("iphlpapi.dll", "GetExtendedUdpTable")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return getExtendedIPTable(proc, order, family, uint32(tableClass))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExtendedIPTable(proc uintptr, order bool, family int, tableClass uint32) ([]byte, error) {
|
||||||
|
size := uint32(0)
|
||||||
|
r, _, _ := syscall.Syscall6(proc, 6,
|
||||||
|
0,
|
||||||
|
uintptr(unsafe.Pointer(&size)),
|
||||||
|
boolToUintptr(order),
|
||||||
|
uintptr(family),
|
||||||
|
uintptr(tableClass),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if r != 0 && syscall.Errno(r) != syscall.ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return nil, syscall.Errno(r)
|
||||||
|
}
|
||||||
|
if size == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
buf := make([]byte, size)
|
||||||
|
r, _, _ = syscall.Syscall6(proc, 6,
|
||||||
|
uintptr(unsafe.Pointer(&buf[0])),
|
||||||
|
uintptr(unsafe.Pointer(&size)),
|
||||||
|
boolToUintptr(order),
|
||||||
|
uintptr(family),
|
||||||
|
uintptr(tableClass),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if r == 0 {
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
if syscall.Errno(r) != syscall.ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
return nil, syscall.Errno(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("GetExtended IP table exceeded retry limit")
|
||||||
|
}
|
||||||
|
|
||||||
|
func boolToUintptr(v bool) uintptr {
|
||||||
|
if v {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectAdapterAddressInfo(head *windows.IpAdapterAddresses) []AdapterAddressInfo {
|
||||||
|
out := make([]AdapterAddressInfo, 0, 8)
|
||||||
|
for a := head; a != nil; a = a.Next {
|
||||||
|
item := AdapterAddressInfo{
|
||||||
|
IfIndex: a.IfIndex,
|
||||||
|
AdapterName: windows.BytePtrToString(a.AdapterName),
|
||||||
|
FriendlyName: windows.UTF16PtrToString(a.FriendlyName),
|
||||||
|
Description: windows.UTF16PtrToString(a.Description),
|
||||||
|
DNSSuffix: windows.UTF16PtrToString(a.DnsSuffix),
|
||||||
|
OperStatus: a.OperStatus,
|
||||||
|
Mtu: a.Mtu,
|
||||||
|
MACAddress: formatMACAddress(a.PhysicalAddress[:], a.PhysicalAddressLength),
|
||||||
|
PhysicalAddressLength: a.PhysicalAddressLength,
|
||||||
|
TransmitLinkSpeed: a.TransmitLinkSpeed,
|
||||||
|
ReceiveLinkSpeed: a.ReceiveLinkSpeed,
|
||||||
|
UnicastIPs: collectUnicastIPs(a.FirstUnicastAddress),
|
||||||
|
DNSServers: collectDNSServerIPs(a.FirstDnsServerAddress),
|
||||||
|
Gateways: collectGatewayIPs(a.FirstGatewayAddress),
|
||||||
|
}
|
||||||
|
out = append(out, item)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectUnicastIPs(addr *windows.IpAdapterUnicastAddress) []string {
|
||||||
|
out := make([]string, 0, 4)
|
||||||
|
for ua := addr; ua != nil; ua = ua.Next {
|
||||||
|
ip := ua.Address.IP()
|
||||||
|
if ip == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, ip.String())
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectDNSServerIPs(addr *windows.IpAdapterDnsServerAdapter) []string {
|
||||||
|
out := make([]string, 0, 2)
|
||||||
|
for cur := addr; cur != nil; cur = cur.Next {
|
||||||
|
ip := cur.Address.IP()
|
||||||
|
if ip == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, ip.String())
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectGatewayIPs(addr *windows.IpAdapterGatewayAddress) []string {
|
||||||
|
out := make([]string, 0, 2)
|
||||||
|
for cur := addr; cur != nil; cur = cur.Next {
|
||||||
|
ip := cur.Address.IP()
|
||||||
|
if ip == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, ip.String())
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatMACAddress(raw []byte, n uint32) string {
|
||||||
|
if n == 0 || len(raw) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if int(n) > len(raw) {
|
||||||
|
n = uint32(len(raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
var b strings.Builder
|
||||||
|
for i := 0; i < int(n); i++ {
|
||||||
|
if i > 0 {
|
||||||
|
b.WriteByte(':')
|
||||||
|
}
|
||||||
|
_, _ = fmt.Fprintf(&b, "%02X", raw[i])
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
154
iphlpapi_typedef.go
Normal file
154
iphlpapi_typedef.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package win32api
|
||||||
|
|
||||||
|
const (
|
||||||
|
IF_MAX_STRING_SIZE = 256
|
||||||
|
IF_MAX_PHYS_ADDRESS_LENGTH = 32
|
||||||
|
)
|
||||||
|
|
||||||
|
type GUID struct {
|
||||||
|
Data1 uint32
|
||||||
|
Data2 uint16
|
||||||
|
Data3 uint16
|
||||||
|
Data4 [8]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type TCP_TABLE_CLASS uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
TCP_TABLE_BASIC_LISTENER TCP_TABLE_CLASS = iota
|
||||||
|
TCP_TABLE_BASIC_CONNECTIONS
|
||||||
|
TCP_TABLE_BASIC_ALL
|
||||||
|
TCP_TABLE_OWNER_PID_LISTENER
|
||||||
|
TCP_TABLE_OWNER_PID_CONNECTIONS
|
||||||
|
TCP_TABLE_OWNER_PID_ALL
|
||||||
|
TCP_TABLE_OWNER_MODULE_LISTENER
|
||||||
|
TCP_TABLE_OWNER_MODULE_CONNECTIONS
|
||||||
|
TCP_TABLE_OWNER_MODULE_ALL
|
||||||
|
)
|
||||||
|
|
||||||
|
type UDP_TABLE_CLASS uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
UDP_TABLE_BASIC UDP_TABLE_CLASS = iota
|
||||||
|
UDP_TABLE_OWNER_PID
|
||||||
|
UDP_TABLE_OWNER_MODULE
|
||||||
|
)
|
||||||
|
|
||||||
|
type MIB_TCP_STATE uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
MIB_TCP_STATE_CLOSED MIB_TCP_STATE = iota + 1
|
||||||
|
MIB_TCP_STATE_LISTEN
|
||||||
|
MIB_TCP_STATE_SYN_SENT
|
||||||
|
MIB_TCP_STATE_SYN_RCVD
|
||||||
|
MIB_TCP_STATE_ESTAB
|
||||||
|
MIB_TCP_STATE_FIN_WAIT1
|
||||||
|
MIB_TCP_STATE_FIN_WAIT2
|
||||||
|
MIB_TCP_STATE_CLOSE_WAIT
|
||||||
|
MIB_TCP_STATE_CLOSING
|
||||||
|
MIB_TCP_STATE_LAST_ACK
|
||||||
|
MIB_TCP_STATE_TIME_WAIT
|
||||||
|
MIB_TCP_STATE_DELETE_TCB
|
||||||
|
)
|
||||||
|
|
||||||
|
type MIB_IF_ROW2 struct {
|
||||||
|
InterfaceLuid uint64
|
||||||
|
InterfaceIndex uint32
|
||||||
|
InterfaceGuid GUID
|
||||||
|
Alias [IF_MAX_STRING_SIZE + 1]uint16
|
||||||
|
Description [IF_MAX_STRING_SIZE + 1]uint16
|
||||||
|
PhysicalAddressLength uint32
|
||||||
|
PhysicalAddress [IF_MAX_PHYS_ADDRESS_LENGTH]byte
|
||||||
|
PermanentPhysicalAddress [IF_MAX_PHYS_ADDRESS_LENGTH]byte
|
||||||
|
Mtu uint32
|
||||||
|
Type uint32
|
||||||
|
TunnelType uint32
|
||||||
|
MediaType uint32
|
||||||
|
PhysicalMediumType uint32
|
||||||
|
AccessType uint32
|
||||||
|
DirectionType uint32
|
||||||
|
InterfaceAndOperStatusFlags byte
|
||||||
|
OperStatus uint32
|
||||||
|
AdminStatus uint32
|
||||||
|
MediaConnectState uint32
|
||||||
|
NetworkGuid GUID
|
||||||
|
ConnectionType uint32
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
type MIB_IF_TABLE2 struct {
|
||||||
|
NumEntries uint32
|
||||||
|
Table [1]MIB_IF_ROW2
|
||||||
|
}
|
||||||
|
|
||||||
|
type MIB_TCPROW_OWNER_PID struct {
|
||||||
|
State MIB_TCP_STATE
|
||||||
|
LocalAddr uint32
|
||||||
|
LocalPort uint32
|
||||||
|
RemoteAddr uint32
|
||||||
|
RemotePort uint32
|
||||||
|
OwningPid uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type MIB_TCPTABLE_OWNER_PID struct {
|
||||||
|
NumEntries uint32
|
||||||
|
Table [1]MIB_TCPROW_OWNER_PID
|
||||||
|
}
|
||||||
|
|
||||||
|
type MIB_TCP6ROW_OWNER_PID struct {
|
||||||
|
LocalAddr [16]byte
|
||||||
|
LocalScopeId uint32
|
||||||
|
LocalPort uint32
|
||||||
|
RemoteAddr [16]byte
|
||||||
|
RemoteScopeId uint32
|
||||||
|
RemotePort uint32
|
||||||
|
State MIB_TCP_STATE
|
||||||
|
OwningPid uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type MIB_TCP6TABLE_OWNER_PID struct {
|
||||||
|
NumEntries uint32
|
||||||
|
Table [1]MIB_TCP6ROW_OWNER_PID
|
||||||
|
}
|
||||||
|
|
||||||
|
type MIB_UDPROW_OWNER_PID struct {
|
||||||
|
LocalAddr uint32
|
||||||
|
LocalPort uint32
|
||||||
|
OwningPid uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type MIB_UDPTABLE_OWNER_PID struct {
|
||||||
|
NumEntries uint32
|
||||||
|
Table [1]MIB_UDPROW_OWNER_PID
|
||||||
|
}
|
||||||
|
|
||||||
|
type MIB_UDP6ROW_OWNER_PID struct {
|
||||||
|
LocalAddr [16]byte
|
||||||
|
LocalScopeId uint32
|
||||||
|
LocalPort uint32
|
||||||
|
OwningPid uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type MIB_UDP6TABLE_OWNER_PID struct {
|
||||||
|
NumEntries uint32
|
||||||
|
Table [1]MIB_UDP6ROW_OWNER_PID
|
||||||
|
}
|
||||||
1240
kernel32.go
1240
kernel32.go
File diff suppressed because it is too large
Load Diff
128
kernel32_helper.go
Normal file
128
kernel32_helper.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package win32api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (entry PROCESSENTRY32) ExeFile() string {
|
||||||
|
n := bytes.IndexByte(entry.SzExeFile[:], 0)
|
||||||
|
if n < 0 {
|
||||||
|
n = len(entry.SzExeFile)
|
||||||
|
}
|
||||||
|
return string(entry.SzExeFile[:n])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry MODULEENTRY32W) ModuleName() string {
|
||||||
|
return syscall.UTF16ToString(entry.SzModule[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry MODULEENTRY32W) ExePath() string {
|
||||||
|
return syscall.UTF16ToString(entry.SzExePath[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info DebugEventInfo) String() string {
|
||||||
|
return info.CodeName
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnumerateProcesses() ([]PROCESSENTRY32, error) {
|
||||||
|
snapshot, err := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = CloseHandle(snapshot)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var entry PROCESSENTRY32
|
||||||
|
entry.DwSize = Ulong(unsafe.Sizeof(entry))
|
||||||
|
if err := Process32First(snapshot, &entry); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
processes := make([]PROCESSENTRY32, 0, 64)
|
||||||
|
for {
|
||||||
|
processes = append(processes, entry)
|
||||||
|
entry.DwSize = Ulong(unsafe.Sizeof(entry))
|
||||||
|
err = Process32Next(snapshot, &entry)
|
||||||
|
if err != nil {
|
||||||
|
if errno, ok := err.(syscall.Errno); ok && errno == ERROR_NO_MORE_FILES {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err == syscall.EINVAL {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return processes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnumerateThreads(ownerProcessID DWORD) ([]THREADENTRY32, error) {
|
||||||
|
snapshot, err := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = CloseHandle(snapshot)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var entry THREADENTRY32
|
||||||
|
entry.DwSize = DWORD(unsafe.Sizeof(entry))
|
||||||
|
if err := Thread32First(snapshot, &entry); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
threads := make([]THREADENTRY32, 0, 64)
|
||||||
|
for {
|
||||||
|
if ownerProcessID == 0 || entry.Th32OwnerProcessID == ownerProcessID {
|
||||||
|
threads = append(threads, entry)
|
||||||
|
}
|
||||||
|
entry.DwSize = DWORD(unsafe.Sizeof(entry))
|
||||||
|
err = Thread32Next(snapshot, &entry)
|
||||||
|
if err != nil {
|
||||||
|
if errno, ok := err.(syscall.Errno); ok && errno == ERROR_NO_MORE_FILES {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err == syscall.EINVAL {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return threads, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnumerateModules(processID DWORD) ([]MODULEENTRY32W, error) {
|
||||||
|
snapshot, err := CreateToolhelp32Snapshot(TH32CS_SNAPMODULE|TH32CS_SNAPMODULE32, processID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = CloseHandle(snapshot)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var entry MODULEENTRY32W
|
||||||
|
entry.DwSize = DWORD(unsafe.Sizeof(entry))
|
||||||
|
if err := Module32First(snapshot, &entry); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
modules := make([]MODULEENTRY32W, 0, 32)
|
||||||
|
for {
|
||||||
|
modules = append(modules, entry)
|
||||||
|
entry.DwSize = DWORD(unsafe.Sizeof(entry))
|
||||||
|
err = Module32Next(snapshot, &entry)
|
||||||
|
if err != nil {
|
||||||
|
if errno, ok := err.(syscall.Errno); ok && errno == ERROR_NO_MORE_FILES {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err == syscall.EINVAL {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modules, nil
|
||||||
|
}
|
||||||
@ -23,6 +23,96 @@ type PROCESSENTRY32 struct {
|
|||||||
SzExeFile [260]byte
|
SzExeFile [260]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type THREADENTRY32 struct {
|
||||||
|
DwSize DWORD
|
||||||
|
CntUsage DWORD
|
||||||
|
Th32ThreadID DWORD
|
||||||
|
Th32OwnerProcessID DWORD
|
||||||
|
TpBasePri int32
|
||||||
|
TpDeltaPri int32
|
||||||
|
DwFlags DWORD
|
||||||
|
}
|
||||||
|
|
||||||
|
type MODULEENTRY32W struct {
|
||||||
|
DwSize DWORD
|
||||||
|
Th32ModuleID DWORD
|
||||||
|
Th32ProcessID DWORD
|
||||||
|
GlblcntUsage DWORD
|
||||||
|
ProccntUsage DWORD
|
||||||
|
ModBaseAddr uintptr
|
||||||
|
ModBaseSize DWORD
|
||||||
|
HModule HMODULE
|
||||||
|
SzModule [MAX_MODULE_NAME32 + 1]uint16
|
||||||
|
SzExePath [syscall.MAX_PATH]uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type M128A struct {
|
||||||
|
Low uint64
|
||||||
|
High int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// AMD64_CONTEXT mirrors the Windows x64 CONTEXT layout closely enough for
|
||||||
|
// GetThreadContext/SetThreadContext on amd64 processes.
|
||||||
|
type AMD64_CONTEXT struct {
|
||||||
|
P1Home uint64
|
||||||
|
P2Home uint64
|
||||||
|
P3Home uint64
|
||||||
|
P4Home uint64
|
||||||
|
P5Home uint64
|
||||||
|
P6Home uint64
|
||||||
|
ContextFlags DWORD
|
||||||
|
MxCsr DWORD
|
||||||
|
SegCs WORD
|
||||||
|
SegDs WORD
|
||||||
|
SegEs WORD
|
||||||
|
SegFs WORD
|
||||||
|
SegGs WORD
|
||||||
|
SegSs WORD
|
||||||
|
EFlags DWORD
|
||||||
|
Dr0 uint64
|
||||||
|
Dr1 uint64
|
||||||
|
Dr2 uint64
|
||||||
|
Dr3 uint64
|
||||||
|
Dr6 uint64
|
||||||
|
Dr7 uint64
|
||||||
|
Rax uint64
|
||||||
|
Rcx uint64
|
||||||
|
Rdx uint64
|
||||||
|
Rbx uint64
|
||||||
|
Rsp uint64
|
||||||
|
Rbp uint64
|
||||||
|
Rsi uint64
|
||||||
|
Rdi uint64
|
||||||
|
R8 uint64
|
||||||
|
R9 uint64
|
||||||
|
R10 uint64
|
||||||
|
R11 uint64
|
||||||
|
R12 uint64
|
||||||
|
R13 uint64
|
||||||
|
R14 uint64
|
||||||
|
R15 uint64
|
||||||
|
Rip uint64
|
||||||
|
ExtendedRegisters [512]byte
|
||||||
|
VectorRegister [26]M128A
|
||||||
|
VectorControl uint64
|
||||||
|
DebugControl uint64
|
||||||
|
LastBranchToRip uint64
|
||||||
|
LastBranchFromRip uint64
|
||||||
|
LastExceptionToRip uint64
|
||||||
|
LastExceptionFromRip uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type DEBUG_EVENT_HEADER struct {
|
||||||
|
DwDebugEventCode DWORD
|
||||||
|
DwProcessId DWORD
|
||||||
|
DwThreadId DWORD
|
||||||
|
}
|
||||||
|
|
||||||
|
type DebugEventInfo struct {
|
||||||
|
Header DEBUG_EVENT_HEADER
|
||||||
|
CodeName string
|
||||||
|
}
|
||||||
|
|
||||||
type MEMORYSTATUSEX struct {
|
type MEMORYSTATUSEX struct {
|
||||||
DwLength DWORD
|
DwLength DWORD
|
||||||
DwMemoryLoad DWORD
|
DwMemoryLoad DWORD
|
||||||
@ -35,6 +125,16 @@ type MEMORYSTATUSEX struct {
|
|||||||
UllAvailExtendedVirtual DWORDLONG
|
UllAvailExtendedVirtual DWORDLONG
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MEMORY_BASIC_INFORMATION struct {
|
||||||
|
BaseAddress uintptr
|
||||||
|
AllocationBase uintptr
|
||||||
|
AllocationProtect DWORD
|
||||||
|
RegionSize uintptr
|
||||||
|
State DWORD
|
||||||
|
Protect DWORD
|
||||||
|
Type DWORD
|
||||||
|
}
|
||||||
|
|
||||||
type USN_JOURNAL_DATA struct {
|
type USN_JOURNAL_DATA struct {
|
||||||
UsnJournalID DWORDLONG
|
UsnJournalID DWORDLONG
|
||||||
FirstUsn USN
|
FirstUsn USN
|
||||||
@ -78,42 +178,184 @@ type MFT_ENUM_DATA struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
FSCTL_ENUM_USN_DATA = 0x900B3
|
TH32CS_SNAPPROCESS DWORD = 0x00000002
|
||||||
FSCTL_QUERY_USN_JOURNAL = 0x900F4
|
TH32CS_SNAPTHREAD DWORD = 0x00000004
|
||||||
FSCTL_READ_USN_JOURNAL = 0x900BB
|
TH32CS_SNAPMODULE DWORD = 0x00000008
|
||||||
O_RDONLY = syscall.O_RDONLY
|
TH32CS_SNAPMODULE32 DWORD = 0x00000010
|
||||||
O_RDWR = syscall.O_RDWR
|
FSCTL_ENUM_USN_DATA = 0x900B3
|
||||||
O_CREAT = syscall.O_CREAT
|
FSCTL_QUERY_USN_JOURNAL = 0x900F4
|
||||||
O_WRONLY = syscall.O_WRONLY
|
FSCTL_READ_USN_JOURNAL = 0x900BB
|
||||||
GENERIC_READ = syscall.GENERIC_READ
|
O_RDONLY = syscall.O_RDONLY
|
||||||
GENERIC_WRITE = syscall.GENERIC_WRITE
|
O_RDWR = syscall.O_RDWR
|
||||||
FILE_APPEND_DATA = syscall.FILE_APPEND_DATA
|
O_CREAT = syscall.O_CREAT
|
||||||
FILE_SHARE_READ = syscall.FILE_SHARE_READ
|
O_WRONLY = syscall.O_WRONLY
|
||||||
FILE_SHARE_WRITE = syscall.FILE_SHARE_WRITE
|
GENERIC_READ = syscall.GENERIC_READ
|
||||||
ERROR_FILE_NOT_FOUND = syscall.ERROR_FILE_NOT_FOUND
|
GENERIC_WRITE = syscall.GENERIC_WRITE
|
||||||
O_APPEND = syscall.O_APPEND
|
FILE_APPEND_DATA = syscall.FILE_APPEND_DATA
|
||||||
O_CLOEXEC = syscall.O_CLOEXEC
|
FILE_SHARE_READ = syscall.FILE_SHARE_READ
|
||||||
O_EXCL = syscall.O_EXCL
|
FILE_SHARE_WRITE = syscall.FILE_SHARE_WRITE
|
||||||
O_TRUNC = syscall.O_TRUNC
|
ERROR_NO_MORE_FILES = syscall.ERROR_NO_MORE_FILES
|
||||||
CREATE_ALWAYS = syscall.CREATE_ALWAYS
|
ERROR_FILE_NOT_FOUND = syscall.ERROR_FILE_NOT_FOUND
|
||||||
CREATE_NEW = syscall.CREATE_NEW
|
O_APPEND = syscall.O_APPEND
|
||||||
OPEN_ALWAYS = syscall.OPEN_ALWAYS
|
O_CLOEXEC = syscall.O_CLOEXEC
|
||||||
TRUNCATE_EXISTING = syscall.TRUNCATE_EXISTING
|
O_EXCL = syscall.O_EXCL
|
||||||
OPEN_EXISTING = syscall.OPEN_EXISTING
|
O_TRUNC = syscall.O_TRUNC
|
||||||
FILE_ATTRIBUTE_NORMAL = syscall.FILE_ATTRIBUTE_NORMAL
|
CREATE_ALWAYS = syscall.CREATE_ALWAYS
|
||||||
FILE_FLAG_BACKUP_SEMANTICS = syscall.FILE_FLAG_BACKUP_SEMANTICS
|
CREATE_NEW = syscall.CREATE_NEW
|
||||||
FILE_ATTRIBUTE_DIRECTORY = syscall.FILE_ATTRIBUTE_DIRECTORY
|
OPEN_ALWAYS = syscall.OPEN_ALWAYS
|
||||||
MAX_LONG_PATH = syscall.MAX_LONG_PATH
|
TRUNCATE_EXISTING = syscall.TRUNCATE_EXISTING
|
||||||
|
OPEN_EXISTING = syscall.OPEN_EXISTING
|
||||||
|
FILE_ATTRIBUTE_NORMAL = syscall.FILE_ATTRIBUTE_NORMAL
|
||||||
|
FILE_FLAG_BACKUP_SEMANTICS = syscall.FILE_FLAG_BACKUP_SEMANTICS
|
||||||
|
FILE_ATTRIBUTE_DIRECTORY = syscall.FILE_ATTRIBUTE_DIRECTORY
|
||||||
|
MAX_LONG_PATH = syscall.MAX_LONG_PATH
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MAX_MODULE_NAME32 = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PROCESS_CREATE_THREAD DWORD = 0x0002
|
||||||
|
PROCESS_TERMINATE DWORD = 0x0001
|
||||||
|
PROCESS_VM_OPERATION DWORD = 0x0008
|
||||||
|
PROCESS_VM_READ DWORD = 0x0010
|
||||||
|
PROCESS_VM_WRITE DWORD = 0x0020
|
||||||
|
PROCESS_QUERY_INFORMATION DWORD = 0x0400
|
||||||
|
PROCESS_QUERY_LIMITED_INFORMATION DWORD = 0x1000
|
||||||
|
PROCESS_SUSPEND_RESUME DWORD = 0x0800
|
||||||
|
SYNCHRONIZE DWORD = 0x00100000
|
||||||
|
PROCESS_NAME_NATIVE DWORD = 0x00000001
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
THREAD_TERMINATE DWORD = 0x0001
|
||||||
|
THREAD_SUSPEND_RESUME DWORD = 0x0002
|
||||||
|
THREAD_GET_CONTEXT DWORD = 0x0008
|
||||||
|
THREAD_SET_CONTEXT DWORD = 0x0010
|
||||||
|
THREAD_QUERY_INFORMATION DWORD = 0x0040
|
||||||
|
THREAD_SET_INFORMATION DWORD = 0x0020
|
||||||
|
THREAD_QUERY_LIMITED_INFO DWORD = 0x0800
|
||||||
|
THREAD_SET_LIMITED_INFO DWORD = 0x0400
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CONTEXT_AMD64 DWORD = 0x00100000
|
||||||
|
CONTEXT_CONTROL DWORD = CONTEXT_AMD64 | 0x00000001
|
||||||
|
CONTEXT_INTEGER DWORD = CONTEXT_AMD64 | 0x00000002
|
||||||
|
CONTEXT_SEGMENTS DWORD = CONTEXT_AMD64 | 0x00000004
|
||||||
|
CONTEXT_FLOATING_POINT DWORD = CONTEXT_AMD64 | 0x00000008
|
||||||
|
CONTEXT_DEBUG_REGISTERS DWORD = CONTEXT_AMD64 | 0x00000010
|
||||||
|
CONTEXT_FULL DWORD = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT
|
||||||
|
CONTEXT_ALL DWORD = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PAGE_NOACCESS DWORD = 0x01
|
||||||
|
PAGE_READONLY DWORD = 0x02
|
||||||
|
PAGE_READWRITE DWORD = 0x04
|
||||||
|
PAGE_WRITECOPY DWORD = 0x08
|
||||||
|
PAGE_EXECUTE DWORD = 0x10
|
||||||
|
PAGE_EXECUTE_READ DWORD = 0x20
|
||||||
|
PAGE_EXECUTE_READWRITE DWORD = 0x40
|
||||||
|
PAGE_EXECUTE_WRITECOPY DWORD = 0x80
|
||||||
|
PAGE_GUARD DWORD = 0x100
|
||||||
|
PAGE_NOCACHE DWORD = 0x200
|
||||||
|
PAGE_WRITECOMBINE DWORD = 0x400
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MEM_COMMIT DWORD = 0x00001000
|
||||||
|
MEM_RESERVE DWORD = 0x00002000
|
||||||
|
MEM_DECOMMIT DWORD = 0x00004000
|
||||||
|
MEM_RELEASE DWORD = 0x00008000
|
||||||
|
MEM_FREE DWORD = 0x00010000
|
||||||
|
MEM_PRIVATE DWORD = 0x00020000
|
||||||
|
MEM_MAPPED DWORD = 0x00040000
|
||||||
|
MEM_TOP_DOWN DWORD = 0x00100000
|
||||||
|
MEM_WRITE_WATCH DWORD = 0x00200000
|
||||||
|
MEM_PHYSICAL DWORD = 0x00400000
|
||||||
|
MEM_RESET DWORD = 0x00080000
|
||||||
|
MEM_RESET_UNDO DWORD = 0x01000000
|
||||||
|
MEM_LARGE_PAGES DWORD = 0x20000000
|
||||||
|
MEM_IMAGE DWORD = 0x01000000
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WAIT_OBJECT_0 DWORD = 0x00000000
|
||||||
|
WAIT_ABANDONED = 0x00000080
|
||||||
|
WAIT_TIMEOUT = 0x00000102
|
||||||
|
WAIT_FAILED = 0xFFFFFFFF
|
||||||
|
INFINITE = 0xFFFFFFFF
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
STILL_ACTIVE DWORD = 259
|
||||||
|
INVALID_FILE_ATTRIBUTES DWORD = 0xFFFFFFFF
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MAXIMUM_WAIT_OBJECTS DWORD = 64
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MOVEFILE_REPLACE_EXISTING DWORD = 0x00000001
|
||||||
|
MOVEFILE_COPY_ALLOWED DWORD = 0x00000002
|
||||||
|
MOVEFILE_DELAY_UNTIL_REBOOT DWORD = 0x00000004
|
||||||
|
MOVEFILE_WRITE_THROUGH DWORD = 0x00000008
|
||||||
|
MOVEFILE_CREATE_HARDLINK DWORD = 0x00000010
|
||||||
|
MOVEFILE_FAIL_IF_NOT_TRACKABLE DWORD = 0x00000020
|
||||||
)
|
)
|
||||||
|
|
||||||
type FILE_ID_DESCRIPTOR struct {
|
type FILE_ID_DESCRIPTOR struct {
|
||||||
DwSize DWORD
|
DwSize DWORD
|
||||||
Type DWORD
|
Type FILE_ID_TYPE
|
||||||
FileId DWORDLONG
|
FileId DWORDLONG
|
||||||
ObjectId DWORDLONG
|
_ [8]byte
|
||||||
ExtendedFileId DWORDLONG
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FILE_ID_TYPE DWORD
|
||||||
|
|
||||||
|
const (
|
||||||
|
FileIdType FILE_ID_TYPE = iota
|
||||||
|
ObjectIdType
|
||||||
|
ExtendedFileIdType
|
||||||
|
MaximumFileIdType
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER DWORD = 0x00000100
|
||||||
|
FORMAT_MESSAGE_IGNORE_INSERTS DWORD = 0x00000200
|
||||||
|
FORMAT_MESSAGE_FROM_SYSTEM DWORD = 0x00001000
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FILE_SHARE_DELETE = syscall.FILE_SHARE_DELETE
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
CREATE_SUSPENDED DWORD = 0x00000004
|
||||||
|
DEBUG_PROCESS DWORD = 0x00000001
|
||||||
|
DEBUG_ONLY_THIS_PROCESS DWORD = 0x00000002
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
EXCEPTION_DEBUG_EVENT DWORD = 1
|
||||||
|
CREATE_THREAD_DEBUG_EVENT DWORD = 2
|
||||||
|
CREATE_PROCESS_DEBUG_EVENT DWORD = 3
|
||||||
|
EXIT_THREAD_DEBUG_EVENT DWORD = 4
|
||||||
|
EXIT_PROCESS_DEBUG_EVENT DWORD = 5
|
||||||
|
LOAD_DLL_DEBUG_EVENT DWORD = 6
|
||||||
|
UNLOAD_DLL_DEBUG_EVENT DWORD = 7
|
||||||
|
OUTPUT_DEBUG_STRING_EVENT DWORD = 8
|
||||||
|
RIP_EVENT DWORD = 9
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DBG_CONTINUE DWORD = 0x00010002
|
||||||
|
DBG_EXCEPTION_NOT_HANDLED DWORD = 0x80010001
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GMEM_MOVEABLE = 0x0002
|
GMEM_MOVEABLE = 0x0002
|
||||||
GMEM_ZEROINIT = 0x0040
|
GMEM_ZEROINIT = 0x0040
|
||||||
|
|||||||
836
network_api_test.go
Normal file
836
network_api_test.go
Normal file
@ -0,0 +1,836 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package win32api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetHostName(t *testing.T) {
|
||||||
|
host, err := GetHostName()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetHostName failed: %v", err)
|
||||||
|
}
|
||||||
|
host = strings.TrimSpace(host)
|
||||||
|
if host == "" {
|
||||||
|
t.Fatal("GetHostName returned empty string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInetPtonNtopIPv4(t *testing.T) {
|
||||||
|
raw, err := InetPton(AF_INET, "127.0.0.1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("InetPton ipv4 failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(raw) != 4 {
|
||||||
|
t.Fatalf("InetPton ipv4 len mismatch: got=%d", len(raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := InetNtop(AF_INET, raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("InetNtop ipv4 failed: %v", err)
|
||||||
|
}
|
||||||
|
if !net.ParseIP(ip).Equal(net.ParseIP("127.0.0.1")) {
|
||||||
|
t.Fatalf("InetNtop ipv4 mismatch: got=%q", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInetPtonNtopIPv6(t *testing.T) {
|
||||||
|
raw, err := InetPton(AF_INET6, "::1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("InetPton ipv6 failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(raw) != 16 {
|
||||||
|
t.Fatalf("InetPton ipv6 len mismatch: got=%d", len(raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := InetNtop(AF_INET6, raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("InetNtop ipv6 failed: %v", err)
|
||||||
|
}
|
||||||
|
if !net.ParseIP(ip).Equal(net.ParseIP("::1")) {
|
||||||
|
t.Fatalf("InetNtop ipv6 mismatch: got=%q", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInetPtonInvalidInput(t *testing.T) {
|
||||||
|
if _, err := InetPton(AF_INET, "999.999.999.999"); err == nil {
|
||||||
|
t.Fatal("InetPton should fail on invalid ipv4 address")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAddrInfoLocalhost(t *testing.T) {
|
||||||
|
hints := &ADDRINFOW{
|
||||||
|
Family: AF_UNSPEC,
|
||||||
|
Socktype: SOCK_STREAM,
|
||||||
|
Protocol: IPPROTO_TCP,
|
||||||
|
}
|
||||||
|
result, err := GetAddrInfo("localhost", "80", hints)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetAddrInfo failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if freeErr := FreeAddrInfo(result); freeErr != nil {
|
||||||
|
t.Fatalf("FreeAddrInfo failed: %v", freeErr)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
total := 0
|
||||||
|
validIP := 0
|
||||||
|
for p := result; p != nil; p = p.Next {
|
||||||
|
total++
|
||||||
|
if p.Addr == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch p.Family {
|
||||||
|
case AF_INET, AF_INET6:
|
||||||
|
ip, ipErr := SockaddrIPString(p.Addr)
|
||||||
|
if ipErr != nil {
|
||||||
|
t.Fatalf("SockaddrIPString failed: %v", ipErr)
|
||||||
|
}
|
||||||
|
if net.ParseIP(ip) == nil {
|
||||||
|
t.Fatalf("invalid ip parsed from addrinfo: %q", ip)
|
||||||
|
}
|
||||||
|
validIP++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if total == 0 {
|
||||||
|
t.Fatal("GetAddrInfo returned empty result list")
|
||||||
|
}
|
||||||
|
if validIP == 0 {
|
||||||
|
t.Fatal("GetAddrInfo returned no AF_INET/AF_INET6 entries")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetNameInfoNumericIPv4(t *testing.T) {
|
||||||
|
sa := SOCKADDR_IN{
|
||||||
|
Family: ADDRESS_FAMILY(AF_INET),
|
||||||
|
Port: htons(80),
|
||||||
|
Addr: [4]byte{127, 0, 0, 1},
|
||||||
|
}
|
||||||
|
host, service, err := GetNameInfo((*SOCKADDR)(unsafe.Pointer(&sa)), int32(unsafe.Sizeof(sa)), NI_NUMERICHOST|NI_NUMERICSERV)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetNameInfo failed: %v", err)
|
||||||
|
}
|
||||||
|
if host != "127.0.0.1" {
|
||||||
|
t.Fatalf("GetNameInfo host mismatch: got=%q", host)
|
||||||
|
}
|
||||||
|
if service != "80" {
|
||||||
|
t.Fatalf("GetNameInfo service mismatch: got=%q", service)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHostNetworkByteOrderHelpers(t *testing.T) {
|
||||||
|
if Htons(0x1234) != 0x3412 {
|
||||||
|
t.Fatalf("Htons mismatch: got=%#x", Htons(0x1234))
|
||||||
|
}
|
||||||
|
if Ntohs(0x3412) != 0x1234 {
|
||||||
|
t.Fatalf("Ntohs mismatch: got=%#x", Ntohs(0x3412))
|
||||||
|
}
|
||||||
|
|
||||||
|
tcp4 := MIB_TCPROW_OWNER_PID{LocalPort: uint32(Htons(8080)), RemotePort: uint32(Htons(443))}
|
||||||
|
if tcp4.LocalPortHost() != 8080 || tcp4.RemotePortHost() != 443 {
|
||||||
|
t.Fatalf("tcp4 port helper mismatch: local=%d remote=%d", tcp4.LocalPortHost(), tcp4.RemotePortHost())
|
||||||
|
}
|
||||||
|
tcp6 := MIB_TCP6ROW_OWNER_PID{LocalPort: uint32(Htons(8081)), RemotePort: uint32(Htons(8443))}
|
||||||
|
if tcp6.LocalPortHost() != 8081 || tcp6.RemotePortHost() != 8443 {
|
||||||
|
t.Fatalf("tcp6 port helper mismatch: local=%d remote=%d", tcp6.LocalPortHost(), tcp6.RemotePortHost())
|
||||||
|
}
|
||||||
|
udp4 := MIB_UDPROW_OWNER_PID{LocalPort: uint32(Htons(5353))}
|
||||||
|
if udp4.LocalPortHost() != 5353 {
|
||||||
|
t.Fatalf("udp4 port helper mismatch: local=%d", udp4.LocalPortHost())
|
||||||
|
}
|
||||||
|
udp6 := MIB_UDP6ROW_OWNER_PID{LocalPort: uint32(Htons(5354))}
|
||||||
|
if udp6.LocalPortHost() != 5354 {
|
||||||
|
t.Fatalf("udp6 port helper mismatch: local=%d", udp6.LocalPortHost())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIphlpapiTableLayouts(t *testing.T) {
|
||||||
|
wantIfTableOffset := uintptr(8)
|
||||||
|
wantIfRowSize := uintptr(1352)
|
||||||
|
if runtime.GOARCH == "386" {
|
||||||
|
wantIfTableOffset = 4
|
||||||
|
wantIfRowSize = 1348
|
||||||
|
}
|
||||||
|
if got := unsafe.Offsetof(MIB_IF_TABLE2{}.Table); got != wantIfTableOffset {
|
||||||
|
t.Fatalf("MIB_IF_TABLE2.Table offset mismatch: got=%d want=%d", got, wantIfTableOffset)
|
||||||
|
}
|
||||||
|
if got := unsafe.Sizeof(MIB_IF_ROW2{}); got != wantIfRowSize {
|
||||||
|
t.Fatalf("MIB_IF_ROW2 size mismatch: got=%d want=%d", got, wantIfRowSize)
|
||||||
|
}
|
||||||
|
if got := unsafe.Offsetof(MIB_TCPTABLE_OWNER_PID{}.Table); got != 4 {
|
||||||
|
t.Fatalf("MIB_TCPTABLE_OWNER_PID.Table offset mismatch: got=%d want=4", got)
|
||||||
|
}
|
||||||
|
if got := unsafe.Sizeof(MIB_TCPROW_OWNER_PID{}); got != 24 {
|
||||||
|
t.Fatalf("MIB_TCPROW_OWNER_PID size mismatch: got=%d want=24", got)
|
||||||
|
}
|
||||||
|
if got := unsafe.Offsetof(MIB_TCP6TABLE_OWNER_PID{}.Table); got != 4 {
|
||||||
|
t.Fatalf("MIB_TCP6TABLE_OWNER_PID.Table offset mismatch: got=%d want=4", got)
|
||||||
|
}
|
||||||
|
if got := unsafe.Sizeof(MIB_TCP6ROW_OWNER_PID{}); got != 56 {
|
||||||
|
t.Fatalf("MIB_TCP6ROW_OWNER_PID size mismatch: got=%d want=56", got)
|
||||||
|
}
|
||||||
|
if got := unsafe.Offsetof(MIB_UDPTABLE_OWNER_PID{}.Table); got != 4 {
|
||||||
|
t.Fatalf("MIB_UDPTABLE_OWNER_PID.Table offset mismatch: got=%d want=4", got)
|
||||||
|
}
|
||||||
|
if got := unsafe.Sizeof(MIB_UDPROW_OWNER_PID{}); got != 12 {
|
||||||
|
t.Fatalf("MIB_UDPROW_OWNER_PID size mismatch: got=%d want=12", got)
|
||||||
|
}
|
||||||
|
if got := unsafe.Offsetof(MIB_UDP6TABLE_OWNER_PID{}.Table); got != 4 {
|
||||||
|
t.Fatalf("MIB_UDP6TABLE_OWNER_PID.Table offset mismatch: got=%d want=4", got)
|
||||||
|
}
|
||||||
|
if got := unsafe.Sizeof(MIB_UDP6ROW_OWNER_PID{}); got != 28 {
|
||||||
|
t.Fatalf("MIB_UDP6ROW_OWNER_PID size mismatch: got=%d want=28", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func requireWSA(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
var data WSADATA
|
||||||
|
if err := WSAStartup(makeWord(2, 2), &data); err != nil {
|
||||||
|
t.Fatalf("WSAStartup failed: %v", err)
|
||||||
|
}
|
||||||
|
t.Cleanup(func() {
|
||||||
|
_ = WSACleanup()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSocketConnectSendRecv(t *testing.T) {
|
||||||
|
requireWSA(t)
|
||||||
|
|
||||||
|
ln, err := net.Listen("tcp4", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("listen failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = ln.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
done := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
conn, acceptErr := ln.Accept()
|
||||||
|
if acceptErr != nil {
|
||||||
|
done <- acceptErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = conn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
if _, readErr := io.ReadFull(conn, buf); readErr != nil {
|
||||||
|
done <- readErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if string(buf) != "ping" {
|
||||||
|
done <- io.ErrUnexpectedEOF
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, writeErr := conn.Write([]byte("pong"))
|
||||||
|
done <- writeErr
|
||||||
|
}()
|
||||||
|
|
||||||
|
s, err := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Socket failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = Closesocket(s)
|
||||||
|
}()
|
||||||
|
|
||||||
|
tcpAddr, ok := ln.Addr().(*net.TCPAddr)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("listen addr type mismatch: %T", ln.Addr())
|
||||||
|
}
|
||||||
|
sa := SOCKADDR_IN{
|
||||||
|
Family: ADDRESS_FAMILY(AF_INET),
|
||||||
|
Port: htons(uint16(tcpAddr.Port)),
|
||||||
|
Addr: [4]byte{127, 0, 0, 1},
|
||||||
|
}
|
||||||
|
if err := Connect(s, (*SOCKADDR)(unsafe.Pointer(&sa)), int32(unsafe.Sizeof(sa))); err != nil {
|
||||||
|
t.Fatalf("Connect failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := []byte("ping")
|
||||||
|
written, err := Send(s, payload, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Send failed: %v", err)
|
||||||
|
}
|
||||||
|
if written != len(payload) {
|
||||||
|
t.Fatalf("Send wrote mismatch: got=%d want=%d", written, len(payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
reply := make([]byte, 4)
|
||||||
|
read, err := Recv(s, reply, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Recv failed: %v", err)
|
||||||
|
}
|
||||||
|
if string(reply[:read]) != "pong" {
|
||||||
|
t.Fatalf("Recv data mismatch: got=%q", string(reply[:read]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Shutdown(s, SD_BOTH); err != nil {
|
||||||
|
t.Fatalf("Shutdown failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case serverErr := <-done:
|
||||||
|
if serverErr != nil {
|
||||||
|
t.Fatalf("server goroutine failed: %v", serverErr)
|
||||||
|
}
|
||||||
|
case <-time.After(3 * time.Second):
|
||||||
|
t.Fatal("server goroutine timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindListenEphemeral(t *testing.T) {
|
||||||
|
requireWSA(t)
|
||||||
|
|
||||||
|
s, err := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Socket failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = Closesocket(s)
|
||||||
|
}()
|
||||||
|
|
||||||
|
sa := SOCKADDR_IN{
|
||||||
|
Family: ADDRESS_FAMILY(AF_INET),
|
||||||
|
Port: 0,
|
||||||
|
Addr: [4]byte{127, 0, 0, 1},
|
||||||
|
}
|
||||||
|
if err := Bind(s, (*SOCKADDR)(unsafe.Pointer(&sa)), int32(unsafe.Sizeof(sa))); err != nil {
|
||||||
|
t.Fatalf("Bind failed: %v", err)
|
||||||
|
}
|
||||||
|
if err := Listen(s, 1); err != nil {
|
||||||
|
t.Fatalf("Listen failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSockNameAndPeerName(t *testing.T) {
|
||||||
|
requireWSA(t)
|
||||||
|
|
||||||
|
ln, err := net.Listen("tcp4", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("listen failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = ln.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
connCh := make(chan net.Conn, 1)
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
conn, acceptErr := ln.Accept()
|
||||||
|
if acceptErr != nil {
|
||||||
|
errCh <- acceptErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
connCh <- conn
|
||||||
|
}()
|
||||||
|
|
||||||
|
client, err := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Socket failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = Closesocket(client)
|
||||||
|
}()
|
||||||
|
|
||||||
|
tcpAddr := ln.Addr().(*net.TCPAddr)
|
||||||
|
peer := SOCKADDR_IN{
|
||||||
|
Family: ADDRESS_FAMILY(AF_INET),
|
||||||
|
Port: htons(uint16(tcpAddr.Port)),
|
||||||
|
Addr: [4]byte{127, 0, 0, 1},
|
||||||
|
}
|
||||||
|
if err := Connect(client, (*SOCKADDR)(unsafe.Pointer(&peer)), int32(unsafe.Sizeof(peer))); err != nil {
|
||||||
|
t.Fatalf("Connect failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverConn net.Conn
|
||||||
|
select {
|
||||||
|
case serverConn = <-connCh:
|
||||||
|
defer func() {
|
||||||
|
_ = serverConn.Close()
|
||||||
|
}()
|
||||||
|
case acceptErr := <-errCh:
|
||||||
|
t.Fatalf("accept failed: %v", acceptErr)
|
||||||
|
case <-time.After(3 * time.Second):
|
||||||
|
t.Fatal("accept timeout")
|
||||||
|
}
|
||||||
|
|
||||||
|
var local SOCKADDR_IN
|
||||||
|
localLen := int32(unsafe.Sizeof(local))
|
||||||
|
if err := GetSockName(client, (*SOCKADDR)(unsafe.Pointer(&local)), &localLen); err != nil {
|
||||||
|
t.Fatalf("GetSockName failed: %v", err)
|
||||||
|
}
|
||||||
|
if local.Family != ADDRESS_FAMILY(AF_INET) {
|
||||||
|
t.Fatalf("GetSockName family mismatch: got=%d", local.Family)
|
||||||
|
}
|
||||||
|
if htons(local.Port) == 0 {
|
||||||
|
t.Fatal("GetSockName returned zero local port")
|
||||||
|
}
|
||||||
|
|
||||||
|
var remote SOCKADDR_IN
|
||||||
|
remoteLen := int32(unsafe.Sizeof(remote))
|
||||||
|
if err := GetPeerName(client, (*SOCKADDR)(unsafe.Pointer(&remote)), &remoteLen); err != nil {
|
||||||
|
t.Fatalf("GetPeerName failed: %v", err)
|
||||||
|
}
|
||||||
|
if remote.Family != ADDRESS_FAMILY(AF_INET) {
|
||||||
|
t.Fatalf("GetPeerName family mismatch: got=%d", remote.Family)
|
||||||
|
}
|
||||||
|
if remote.Addr != [4]byte{127, 0, 0, 1} {
|
||||||
|
t.Fatalf("GetPeerName ip mismatch: got=%v", remote.Addr)
|
||||||
|
}
|
||||||
|
if int(htons(remote.Port)) != tcpAddr.Port {
|
||||||
|
t.Fatalf("GetPeerName port mismatch: got=%d want=%d", htons(remote.Port), tcpAddr.Port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetGetSockOptInt(t *testing.T) {
|
||||||
|
requireWSA(t)
|
||||||
|
|
||||||
|
s, err := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Socket failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = Closesocket(s)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := SetSockOptInt(s, SOL_SOCKET, SO_REUSEADDR, 1); err != nil {
|
||||||
|
t.Fatalf("SetSockOptInt failed: %v", err)
|
||||||
|
}
|
||||||
|
v, err := GetSockOptInt(s, SOL_SOCKET, SO_REUSEADDR)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetSockOptInt failed: %v", err)
|
||||||
|
}
|
||||||
|
if v == 0 {
|
||||||
|
t.Fatalf("GetSockOptInt(SO_REUSEADDR) expected non-zero, got %d", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSendToRecvFromUDP(t *testing.T) {
|
||||||
|
requireWSA(t)
|
||||||
|
|
||||||
|
receiver, err := Socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("receiver Socket failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = Closesocket(receiver)
|
||||||
|
}()
|
||||||
|
|
||||||
|
bindAddr := SOCKADDR_IN{
|
||||||
|
Family: ADDRESS_FAMILY(AF_INET),
|
||||||
|
Port: 0,
|
||||||
|
Addr: [4]byte{127, 0, 0, 1},
|
||||||
|
}
|
||||||
|
if err := Bind(receiver, (*SOCKADDR)(unsafe.Pointer(&bindAddr)), int32(unsafe.Sizeof(bindAddr))); err != nil {
|
||||||
|
t.Fatalf("receiver Bind failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var recvBound SOCKADDR_IN
|
||||||
|
recvBoundLen := int32(unsafe.Sizeof(recvBound))
|
||||||
|
if err := GetSockName(receiver, (*SOCKADDR)(unsafe.Pointer(&recvBound)), &recvBoundLen); err != nil {
|
||||||
|
t.Fatalf("receiver GetSockName failed: %v", err)
|
||||||
|
}
|
||||||
|
recvPort := htons(recvBound.Port)
|
||||||
|
if recvPort == 0 {
|
||||||
|
t.Fatal("receiver bound port is zero")
|
||||||
|
}
|
||||||
|
|
||||||
|
sender, err := Socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("sender Socket failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = Closesocket(sender)
|
||||||
|
}()
|
||||||
|
|
||||||
|
target := SOCKADDR_IN{
|
||||||
|
Family: ADDRESS_FAMILY(AF_INET),
|
||||||
|
Port: recvBound.Port,
|
||||||
|
Addr: [4]byte{127, 0, 0, 1},
|
||||||
|
}
|
||||||
|
payload := []byte("udp-ping")
|
||||||
|
sent, err := SendTo(sender, payload, 0, (*SOCKADDR)(unsafe.Pointer(&target)), int32(unsafe.Sizeof(target)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SendTo failed: %v", err)
|
||||||
|
}
|
||||||
|
if sent != len(payload) {
|
||||||
|
t.Fatalf("SendTo length mismatch: got=%d want=%d", sent, len(payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 64)
|
||||||
|
var from SOCKADDR_IN
|
||||||
|
fromLen := int32(unsafe.Sizeof(from))
|
||||||
|
n, err := RecvFrom(receiver, buf, 0, (*SOCKADDR)(unsafe.Pointer(&from)), &fromLen)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RecvFrom failed: %v", err)
|
||||||
|
}
|
||||||
|
if string(buf[:n]) != string(payload) {
|
||||||
|
t.Fatalf("RecvFrom payload mismatch: got=%q want=%q", string(buf[:n]), string(payload))
|
||||||
|
}
|
||||||
|
if from.Family != ADDRESS_FAMILY(AF_INET) {
|
||||||
|
t.Fatalf("RecvFrom family mismatch: got=%d", from.Family)
|
||||||
|
}
|
||||||
|
if from.Addr != [4]byte{127, 0, 0, 1} {
|
||||||
|
t.Fatalf("RecvFrom source ip mismatch: got=%v", from.Addr)
|
||||||
|
}
|
||||||
|
if htons(from.Port) == 0 {
|
||||||
|
t.Fatal("RecvFrom source port is zero")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAcceptSendRecvServerFlow(t *testing.T) {
|
||||||
|
requireWSA(t)
|
||||||
|
|
||||||
|
listener, err := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("listener Socket failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = Closesocket(listener)
|
||||||
|
}()
|
||||||
|
if err := SetSockOptInt(listener, SOL_SOCKET, SO_REUSEADDR, 1); err != nil {
|
||||||
|
t.Fatalf("SetSockOptInt(SO_REUSEADDR) failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bindAddr := SOCKADDR_IN{
|
||||||
|
Family: ADDRESS_FAMILY(AF_INET),
|
||||||
|
Port: 0,
|
||||||
|
Addr: [4]byte{127, 0, 0, 1},
|
||||||
|
}
|
||||||
|
if err := Bind(listener, (*SOCKADDR)(unsafe.Pointer(&bindAddr)), int32(unsafe.Sizeof(bindAddr))); err != nil {
|
||||||
|
t.Fatalf("Bind failed: %v", err)
|
||||||
|
}
|
||||||
|
if err := Listen(listener, 1); err != nil {
|
||||||
|
t.Fatalf("Listen failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bound SOCKADDR_IN
|
||||||
|
boundLen := int32(unsafe.Sizeof(bound))
|
||||||
|
if err := GetSockName(listener, (*SOCKADDR)(unsafe.Pointer(&bound)), &boundLen); err != nil {
|
||||||
|
t.Fatalf("GetSockName(listener) failed: %v", err)
|
||||||
|
}
|
||||||
|
port := htons(bound.Port)
|
||||||
|
if port == 0 {
|
||||||
|
t.Fatal("listener port is zero")
|
||||||
|
}
|
||||||
|
|
||||||
|
serverDone := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
var peer SOCKADDR_IN
|
||||||
|
peerLen := int32(unsafe.Sizeof(peer))
|
||||||
|
clientSock, acceptErr := Accept(listener, (*SOCKADDR)(unsafe.Pointer(&peer)), &peerLen)
|
||||||
|
if acceptErr != nil {
|
||||||
|
serverDone <- acceptErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = Closesocket(clientSock)
|
||||||
|
}()
|
||||||
|
|
||||||
|
buf := make([]byte, 16)
|
||||||
|
n, recvErr := Recv(clientSock, buf, 0)
|
||||||
|
if recvErr != nil {
|
||||||
|
serverDone <- recvErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if string(buf[:n]) != "accept-ping" {
|
||||||
|
serverDone <- fmt.Errorf("server recv mismatch: %q", string(buf[:n]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, sendErr := Send(clientSock, []byte("accept-pong"), 0); sendErr != nil {
|
||||||
|
serverDone <- sendErr
|
||||||
|
return
|
||||||
|
}
|
||||||
|
serverDone <- nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
conn, err := net.DialTimeout("tcp4", fmt.Sprintf("127.0.0.1:%d", port), 3*time.Second)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("DialTimeout failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = conn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err := conn.Write([]byte("accept-ping")); err != nil {
|
||||||
|
t.Fatalf("client Write failed: %v", err)
|
||||||
|
}
|
||||||
|
reply := make([]byte, 16)
|
||||||
|
n, err := io.ReadFull(conn, reply[:11])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("client ReadFull failed: %v", err)
|
||||||
|
}
|
||||||
|
if string(reply[:n]) != "accept-pong" {
|
||||||
|
t.Fatalf("client recv mismatch: %q", string(reply[:n]))
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-serverDone:
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("server flow failed: %v", err)
|
||||||
|
}
|
||||||
|
case <-time.After(3 * time.Second):
|
||||||
|
t.Fatal("server flow timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSockOptSOErrorAfterFailedConnect(t *testing.T) {
|
||||||
|
requireWSA(t)
|
||||||
|
|
||||||
|
s, err := Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Socket failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = Closesocket(s)
|
||||||
|
}()
|
||||||
|
|
||||||
|
target := SOCKADDR_IN{
|
||||||
|
Family: ADDRESS_FAMILY(AF_INET),
|
||||||
|
Port: htons(1),
|
||||||
|
Addr: [4]byte{127, 0, 0, 1},
|
||||||
|
}
|
||||||
|
connectErr := Connect(s, (*SOCKADDR)(unsafe.Pointer(&target)), int32(unsafe.Sizeof(target)))
|
||||||
|
if connectErr == nil {
|
||||||
|
t.Skip("port 1 is open in current environment, skip SO_ERROR failure-path test")
|
||||||
|
}
|
||||||
|
|
||||||
|
soErr, err := GetSockOptInt(s, SOL_SOCKET, SO_ERROR)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetSockOptInt(SO_ERROR) failed: %v", err)
|
||||||
|
}
|
||||||
|
if soErr == 0 {
|
||||||
|
// On some stacks the synchronous connect error can be reported directly and
|
||||||
|
// SO_ERROR may already be cleared; keep this as diagnostic instead of flaky fail.
|
||||||
|
t.Logf("SO_ERROR=0 after connect err=%v", connectErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAdaptersAddresses(t *testing.T) {
|
||||||
|
adapters, err := GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX|GAA_FLAG_INCLUDE_GATEWAYS)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetAdaptersAddresses failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(adapters) == 0 {
|
||||||
|
t.Fatal("GetAdaptersAddresses returned empty list")
|
||||||
|
}
|
||||||
|
|
||||||
|
foundNamed := false
|
||||||
|
for _, a := range adapters {
|
||||||
|
if strings.TrimSpace(a.FriendlyName) != "" || strings.TrimSpace(a.AdapterName) != "" {
|
||||||
|
foundNamed = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundNamed {
|
||||||
|
t.Fatal("GetAdaptersAddresses returned adapters without names")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, a := range adapters {
|
||||||
|
if a.PhysicalAddressLength > 0 {
|
||||||
|
if strings.TrimSpace(a.MACAddress) == "" {
|
||||||
|
t.Fatalf("adapter has physical address length %d but empty MACAddress", a.PhysicalAddressLength)
|
||||||
|
}
|
||||||
|
if _, err := net.ParseMAC(a.MACAddress); err != nil {
|
||||||
|
t.Fatalf("invalid MACAddress format %q: %v", a.MACAddress, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ip := range a.UnicastIPs {
|
||||||
|
if net.ParseIP(ip) == nil {
|
||||||
|
t.Fatalf("invalid unicast ip in adapter info: %q", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ip := range a.DNSServers {
|
||||||
|
if net.ParseIP(ip) == nil {
|
||||||
|
t.Fatalf("invalid dns server ip in adapter info: %q", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ip := range a.Gateways {
|
||||||
|
if net.ParseIP(ip) == nil {
|
||||||
|
t.Fatalf("invalid gateway ip in adapter info: %q", ip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetIfTable2AndEntry2(t *testing.T) {
|
||||||
|
rows, err := GetIfTable2()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetIfTable2 failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(rows) == 0 {
|
||||||
|
t.Fatal("GetIfTable2 returned empty list")
|
||||||
|
}
|
||||||
|
|
||||||
|
row := rows[0]
|
||||||
|
if row.InterfaceIndex == 0 && row.InterfaceLuid == 0 {
|
||||||
|
t.Fatal("GetIfTable2 returned row without interface identity")
|
||||||
|
}
|
||||||
|
if err := GetIfEntry2(&row); err != nil {
|
||||||
|
t.Fatalf("GetIfEntry2 failed: %v", err)
|
||||||
|
}
|
||||||
|
if row.InterfaceIndex == 0 && row.InterfaceLuid == 0 {
|
||||||
|
t.Fatal("GetIfEntry2 cleared interface identity")
|
||||||
|
}
|
||||||
|
stats := []uint64{
|
||||||
|
row.InOctets,
|
||||||
|
row.OutOctets,
|
||||||
|
row.InUcastOctets,
|
||||||
|
row.OutUcastOctets,
|
||||||
|
row.InMulticastOctets,
|
||||||
|
row.OutMulticastOctets,
|
||||||
|
row.InBroadcastOctets,
|
||||||
|
row.OutBroadcastOctets,
|
||||||
|
}
|
||||||
|
if len(stats) != 8 {
|
||||||
|
t.Fatalf("unexpected stats field count: got=%d", len(stats))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetExtendedTcp4TableIncludesCurrentListener(t *testing.T) {
|
||||||
|
listener, err := net.Listen("tcp4", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("listen tcp4 failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = listener.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
port := listener.Addr().(*net.TCPAddr).Port
|
||||||
|
row := waitForTCP4OwnerPIDRow(t, uint32(os.Getpid()), port)
|
||||||
|
if row.State != MIB_TCP_STATE_LISTEN {
|
||||||
|
t.Fatalf("unexpected tcp state: got=%d want=%d", row.State, MIB_TCP_STATE_LISTEN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetExtendedUdp4TableIncludesCurrentSocket(t *testing.T) {
|
||||||
|
packetConn, err := net.ListenPacket("udp4", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("listen udp4 failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = packetConn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
port := packetConn.LocalAddr().(*net.UDPAddr).Port
|
||||||
|
_ = waitForUDP4OwnerPIDRow(t, uint32(os.Getpid()), port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetExtendedTcp6TableIncludesCurrentListener(t *testing.T) {
|
||||||
|
listener, err := net.Listen("tcp6", "[::1]:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Skipf("tcp6 unavailable: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = listener.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
port := listener.Addr().(*net.TCPAddr).Port
|
||||||
|
row := waitForTCP6OwnerPIDRow(t, uint32(os.Getpid()), port)
|
||||||
|
if row.State != MIB_TCP_STATE_LISTEN {
|
||||||
|
t.Fatalf("unexpected tcp6 state: got=%d want=%d", row.State, MIB_TCP_STATE_LISTEN)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetExtendedUdp6TableIncludesCurrentSocket(t *testing.T) {
|
||||||
|
packetConn, err := net.ListenPacket("udp6", "[::1]:0")
|
||||||
|
if err != nil {
|
||||||
|
t.Skipf("udp6 unavailable: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = packetConn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
port := packetConn.LocalAddr().(*net.UDPAddr).Port
|
||||||
|
_ = waitForUDP6OwnerPIDRow(t, uint32(os.Getpid()), port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForTCP4OwnerPIDRow(t *testing.T, pid uint32, port int) MIB_TCPROW_OWNER_PID {
|
||||||
|
t.Helper()
|
||||||
|
deadline := time.Now().Add(2 * time.Second)
|
||||||
|
for time.Now().Before(deadline) {
|
||||||
|
rows, err := GetExtendedTcp4Table(false, TCP_TABLE_OWNER_PID_ALL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetExtendedTcp4Table failed: %v", err)
|
||||||
|
}
|
||||||
|
for _, row := range rows {
|
||||||
|
if row.OwningPid == pid && int(row.LocalPortHost()) == port {
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
t.Fatalf("did not find tcp4 row for pid=%d port=%d", pid, port)
|
||||||
|
return MIB_TCPROW_OWNER_PID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForTCP6OwnerPIDRow(t *testing.T, pid uint32, port int) MIB_TCP6ROW_OWNER_PID {
|
||||||
|
t.Helper()
|
||||||
|
deadline := time.Now().Add(2 * time.Second)
|
||||||
|
for time.Now().Before(deadline) {
|
||||||
|
rows, err := GetExtendedTcp6Table(false, TCP_TABLE_OWNER_PID_ALL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetExtendedTcp6Table failed: %v", err)
|
||||||
|
}
|
||||||
|
for _, row := range rows {
|
||||||
|
if row.OwningPid == pid && int(row.LocalPortHost()) == port {
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
t.Fatalf("did not find tcp6 row for pid=%d port=%d", pid, port)
|
||||||
|
return MIB_TCP6ROW_OWNER_PID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForUDP4OwnerPIDRow(t *testing.T, pid uint32, port int) MIB_UDPROW_OWNER_PID {
|
||||||
|
t.Helper()
|
||||||
|
deadline := time.Now().Add(2 * time.Second)
|
||||||
|
for time.Now().Before(deadline) {
|
||||||
|
rows, err := GetExtendedUdp4Table(false, UDP_TABLE_OWNER_PID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetExtendedUdp4Table failed: %v", err)
|
||||||
|
}
|
||||||
|
for _, row := range rows {
|
||||||
|
if row.OwningPid == pid && int(row.LocalPortHost()) == port {
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
t.Fatalf("did not find udp4 row for pid=%d port=%d", pid, port)
|
||||||
|
return MIB_UDPROW_OWNER_PID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForUDP6OwnerPIDRow(t *testing.T, pid uint32, port int) MIB_UDP6ROW_OWNER_PID {
|
||||||
|
t.Helper()
|
||||||
|
deadline := time.Now().Add(2 * time.Second)
|
||||||
|
for time.Now().Before(deadline) {
|
||||||
|
rows, err := GetExtendedUdp6Table(false, UDP_TABLE_OWNER_PID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetExtendedUdp6Table failed: %v", err)
|
||||||
|
}
|
||||||
|
for _, row := range rows {
|
||||||
|
if row.OwningPid == pid && int(row.LocalPortHost()) == port {
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
t.Fatalf("did not find udp6 row for pid=%d port=%d", pid, port)
|
||||||
|
return MIB_UDP6ROW_OWNER_PID{}
|
||||||
|
}
|
||||||
41
proc_cache.go
Normal file
41
proc_cache.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package win32api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
procCacheMu sync.Mutex
|
||||||
|
dllCache = map[string]syscall.Handle{}
|
||||||
|
procCache = map[string]uintptr{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func getProcAddr(dllName, procName string) (uintptr, error) {
|
||||||
|
cacheKey := dllName + "!" + procName
|
||||||
|
|
||||||
|
procCacheMu.Lock()
|
||||||
|
defer procCacheMu.Unlock()
|
||||||
|
|
||||||
|
if proc, ok := procCache[cacheKey]; ok {
|
||||||
|
return proc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dll, ok := dllCache[dllName]
|
||||||
|
if !ok {
|
||||||
|
var err error
|
||||||
|
dll, err = syscall.LoadLibrary(dllName)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("load %s: %w", dllName, err)
|
||||||
|
}
|
||||||
|
dllCache[dllName] = dll
|
||||||
|
}
|
||||||
|
|
||||||
|
proc, err := syscall.GetProcAddress(syscall.Handle(dll), procName)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("resolve %s!%s: %w", dllName, procName, err)
|
||||||
|
}
|
||||||
|
procCache[cacheKey] = proc
|
||||||
|
return proc, nil
|
||||||
|
}
|
||||||
34
shell32.go
34
shell32.go
@ -8,15 +8,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func ShellExecute(hwnd HWND, lpOperation, lpFile, lpParameters, lpDirectory string, nShowCmd int) error {
|
func ShellExecute(hwnd HWND, lpOperation, lpFile, lpParameters, lpDirectory string, nShowCmd int) error {
|
||||||
shell32, err := syscall.LoadLibrary("shell32.dll")
|
ShellExecute, err := getProcAddr("shell32.dll", "ShellExecuteW")
|
||||||
var op, param, directory uintptr
|
var op, param, directory uintptr
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Can't Load Shell32 API")
|
return err
|
||||||
}
|
|
||||||
defer syscall.FreeLibrary(shell32)
|
|
||||||
ShellExecute, err := syscall.GetProcAddress(syscall.Handle(shell32), "ShellExecuteW")
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("Can't Load ShellExecute API")
|
|
||||||
}
|
}
|
||||||
if len(lpOperation) != 0 {
|
if len(lpOperation) != 0 {
|
||||||
op = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpOperation)))
|
op = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpOperation)))
|
||||||
@ -27,7 +22,7 @@ func ShellExecute(hwnd HWND, lpOperation, lpFile, lpParameters, lpDirectory stri
|
|||||||
if len(lpDirectory) != 0 {
|
if len(lpDirectory) != 0 {
|
||||||
directory = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpDirectory)))
|
directory = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpDirectory)))
|
||||||
}
|
}
|
||||||
r, _, _ := syscall.Syscall6(uintptr(ShellExecute), 6,
|
r, _, _ := syscall.Syscall6(ShellExecute, 6,
|
||||||
uintptr(hwnd),
|
uintptr(hwnd),
|
||||||
op,
|
op,
|
||||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpFile))),
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpFile))),
|
||||||
@ -70,30 +65,25 @@ func ShellExecuteEX2(hwnd HWND, lpVerb, lpFile, lpParameters, lpDirectory string
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
func ShellExecuteEx(muzika *SHELLEXECUTEINFOW) error {
|
func ShellExecuteEx(muzika *SHELLEXECUTEINFOW) error {
|
||||||
shell32, err := syscall.LoadLibrary("shell32.dll")
|
if muzika == nil {
|
||||||
|
return syscall.EINVAL
|
||||||
if err != nil {
|
|
||||||
return errors.New("Can't Load Shell32 API")
|
|
||||||
}
|
}
|
||||||
defer syscall.FreeLibrary(shell32)
|
ShellExecuteEx, err := getProcAddr("shell32.dll", "ShellExecuteExW")
|
||||||
ShellExecuteEx, err := syscall.GetProcAddress(syscall.Handle(shell32), "ShellExecuteExW")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Can't Load ShellExecuteEx API")
|
return err
|
||||||
}
|
}
|
||||||
r, _, errno := syscall.Syscall6(ShellExecuteEx, 1, uintptr(unsafe.Pointer(muzika)), 0, 0, 0, 0, 0)
|
r, _, errno := syscall.Syscall6(ShellExecuteEx, 1, uintptr(unsafe.Pointer(muzika)), 0, 0, 0, 0, 0)
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
return error(errno)
|
if errno != 0 {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DragQueryFile(hDrop HDROP, iFile DWORD, lpszFile *uint16, cch DWORD) (DWORD, error) {
|
func DragQueryFile(hDrop HDROP, iFile DWORD, lpszFile *uint16, cch DWORD) (DWORD, error) {
|
||||||
shell32, err := syscall.LoadLibrary("shell32.dll")
|
dqf, err := getProcAddr("shell32.dll", "DragQueryFileW")
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
defer syscall.FreeLibrary(shell32)
|
|
||||||
dqf, err := syscall.GetProcAddress(syscall.Handle(shell32), "DragQueryFileW")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ type SHELLEXECUTEINFOW struct {
|
|||||||
LpFile uintptr
|
LpFile uintptr
|
||||||
LpParameters uintptr
|
LpParameters uintptr
|
||||||
LpDirectory uintptr
|
LpDirectory uintptr
|
||||||
NShow int
|
NShow int32
|
||||||
HInstApp HINSTANCE
|
HInstApp HINSTANCE
|
||||||
LpIDList LPVOID
|
LpIDList LPVOID
|
||||||
LpClass uintptr
|
LpClass uintptr
|
||||||
|
|||||||
265
user32.go
265
user32.go
@ -65,7 +65,7 @@ func OpenClipboard(hWnd HWND) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if r, _, errno := syscall.Syscall(oc, 0, 0, 0, 0); r == 0 {
|
if r, _, errno := syscall.Syscall(oc, 1, 0, 0, 0); r == 0 {
|
||||||
return error(errno)
|
return error(errno)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -116,6 +116,9 @@ func EnumClipboardFormats(uFormat DWORD) (DWORD, error) {
|
|||||||
}
|
}
|
||||||
r, _, errno := syscall.Syscall(ecf, 1, uintptr(uFormat), 0, 0)
|
r, _, errno := syscall.Syscall(ecf, 1, uintptr(uFormat), 0, 0)
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
|
if errno == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
return 0, error(errno)
|
return 0, error(errno)
|
||||||
}
|
}
|
||||||
return DWORD(r), nil
|
return DWORD(r), nil
|
||||||
@ -123,12 +126,17 @@ func EnumClipboardFormats(uFormat DWORD) (DWORD, error) {
|
|||||||
|
|
||||||
func EnumAllClipboardFormats() ([]DWORD, error) {
|
func EnumAllClipboardFormats() ([]DWORD, error) {
|
||||||
var formats []DWORD
|
var formats []DWORD
|
||||||
for i := 0; ; i++ {
|
var current DWORD
|
||||||
format, err := EnumClipboardFormats(DWORD(i))
|
for {
|
||||||
|
format, err := EnumClipboardFormats(current)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if format == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
formats = append(formats, format)
|
formats = append(formats, format)
|
||||||
|
current = format
|
||||||
}
|
}
|
||||||
return formats, nil
|
return formats, nil
|
||||||
}
|
}
|
||||||
@ -193,6 +201,9 @@ func CountClipboardFormats() (int, error) {
|
|||||||
}
|
}
|
||||||
r, _, errno := syscall.Syscall(ccf, 0, 0, 0, 0)
|
r, _, errno := syscall.Syscall(ccf, 0, 0, 0, 0)
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
|
if errno == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
return 0, error(errno)
|
return 0, error(errno)
|
||||||
}
|
}
|
||||||
return int(r), nil
|
return int(r), nil
|
||||||
@ -210,6 +221,9 @@ func GetClipboardOwner() (HWND, error) {
|
|||||||
}
|
}
|
||||||
r, _, errno := syscall.Syscall(gco, 0, 0, 0, 0)
|
r, _, errno := syscall.Syscall(gco, 0, 0, 0, 0)
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
|
if errno == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
return 0, error(errno)
|
return 0, error(errno)
|
||||||
}
|
}
|
||||||
return HWND(r), nil
|
return HWND(r), nil
|
||||||
@ -232,18 +246,41 @@ func GetUpdatedClipboardFormats(lpuiFormats unsafe.Pointer, cFormats int, pcForm
|
|||||||
return int(r), nil
|
return int(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type updatedClipboardFormatsFunc func(lpuiFormats unsafe.Pointer, cFormats int, pcFormats unsafe.Pointer) (int, error)
|
||||||
|
|
||||||
|
func getUpdatedClipboardFormatsAll(fetch updatedClipboardFormatsFunc) ([]DWORD, error) {
|
||||||
|
if fetch == nil {
|
||||||
|
fetch = GetUpdatedClipboardFormats
|
||||||
|
}
|
||||||
|
for size := 32; ; {
|
||||||
|
formats := make([]uint32, size)
|
||||||
|
var count uint32
|
||||||
|
_, err := fetch(unsafe.Pointer(&formats[0]), len(formats), unsafe.Pointer(&count))
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, syscall.ERROR_INSUFFICIENT_BUFFER) {
|
||||||
|
nextSize := size * 2
|
||||||
|
if count > uint32(size) {
|
||||||
|
nextSize = int(count)
|
||||||
|
}
|
||||||
|
size = nextSize
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if count > uint32(len(formats)) {
|
||||||
|
size = int(count)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
res := make([]DWORD, 0, int(count))
|
||||||
|
for i := 0; i < int(count); i++ {
|
||||||
|
res = append(res, DWORD(formats[i]))
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func GetUpdatedClipboardFormatsAll() ([]DWORD, error) {
|
func GetUpdatedClipboardFormatsAll() ([]DWORD, error) {
|
||||||
var res []DWORD
|
return getUpdatedClipboardFormatsAll(GetUpdatedClipboardFormats)
|
||||||
formats := make([]uint32, 32)
|
|
||||||
var count uint32
|
|
||||||
_, err := GetUpdatedClipboardFormats(unsafe.Pointer(&formats[0]), len(formats), unsafe.Pointer(&count))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for i := 0; i < int(count); i++ {
|
|
||||||
res = append(res, DWORD(formats[i]))
|
|
||||||
}
|
|
||||||
return res, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsClipboardFormatAvailable(uFormat DWORD) (bool, error) {
|
func IsClipboardFormatAvailable(uFormat DWORD) (bool, error) {
|
||||||
@ -270,8 +307,14 @@ func AddClipboardFormatListener(hWnd HWND) (bool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
r, _, _ := syscall.Syscall(acfl, 1, uintptr(hWnd), 0, 0)
|
r, _, errno := syscall.Syscall(acfl, 1, uintptr(hWnd), 0, 0)
|
||||||
return r != 0, nil
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return false, error(errno)
|
||||||
|
}
|
||||||
|
return false, syscall.EINVAL
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveClipboardFormatListener(hWnd HWND) (bool, error) {
|
func RemoveClipboardFormatListener(hWnd HWND) (bool, error) {
|
||||||
@ -284,8 +327,14 @@ func RemoveClipboardFormatListener(hWnd HWND) (bool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
r, _, _ := syscall.Syscall(rcfl, 1, uintptr(hWnd), 0, 0)
|
r, _, errno := syscall.Syscall(rcfl, 1, uintptr(hWnd), 0, 0)
|
||||||
return r != 0, nil
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return false, error(errno)
|
||||||
|
}
|
||||||
|
return false, syscall.EINVAL
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetClipboardData(uFormat DWORD, hMem HGLOBAL) (HGLOBAL, error) {
|
func SetClipboardData(uFormat DWORD, hMem HGLOBAL) (HGLOBAL, error) {
|
||||||
@ -317,6 +366,9 @@ func SetClipboardViewer(hWndNewViewer HWND) (HWND, error) {
|
|||||||
}
|
}
|
||||||
r, _, errno := syscall.Syscall(scv, 1, uintptr(hWndNewViewer), 0, 0)
|
r, _, errno := syscall.Syscall(scv, 1, uintptr(hWndNewViewer), 0, 0)
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
|
if errno == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
return 0, error(errno)
|
return 0, error(errno)
|
||||||
}
|
}
|
||||||
return HWND(r), nil
|
return HWND(r), nil
|
||||||
@ -349,7 +401,7 @@ func CreateWindowEx(dwExStyle DWORD, lpClassName, lpWindowName string, dwStyle D
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, errors.New("Can't Load CreateWindowEx API")
|
return 0, errors.New("Can't Load CreateWindowEx API")
|
||||||
}
|
}
|
||||||
r, _, errno := syscall.Syscall12(cwe, 11, uintptr(dwExStyle), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpClassName))), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpWindowName))), uintptr(dwStyle), uintptr(x), uintptr(y), uintptr(nWidth), uintptr(nHeight), uintptr(hWndParent), uintptr(hMenu), uintptr(hInstance), uintptr(lpParam))
|
r, _, errno := syscall.Syscall12(cwe, 12, uintptr(dwExStyle), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpClassName))), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpWindowName))), uintptr(dwStyle), uintptr(x), uintptr(y), uintptr(nWidth), uintptr(nHeight), uintptr(hWndParent), uintptr(hMenu), uintptr(hInstance), uintptr(lpParam))
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
return 0, error(errno)
|
return 0, error(errno)
|
||||||
}
|
}
|
||||||
@ -366,8 +418,14 @@ func DestroyWindow(hWnd HWND) (bool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, errors.New("Can't Load DestroyWindow API")
|
return false, errors.New("Can't Load DestroyWindow API")
|
||||||
}
|
}
|
||||||
r, _, _ := syscall.Syscall(dw, 1, uintptr(hWnd), 0, 0)
|
r, _, errno := syscall.Syscall(dw, 1, uintptr(hWnd), 0, 0)
|
||||||
return r != 0, nil
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return false, error(errno)
|
||||||
|
}
|
||||||
|
return false, syscall.EINVAL
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMessage(lpMsg *MSG, hWnd HWND, wMsgFilterMin, wMsgFilterMax DWORD) (DWORD, error) {
|
func GetMessage(lpMsg *MSG, hWnd HWND, wMsgFilterMin, wMsgFilterMax DWORD) (DWORD, error) {
|
||||||
@ -381,7 +439,7 @@ func GetMessage(lpMsg *MSG, hWnd HWND, wMsgFilterMin, wMsgFilterMax DWORD) (DWOR
|
|||||||
return 0, errors.New("Can't Load GetMessage API")
|
return 0, errors.New("Can't Load GetMessage API")
|
||||||
}
|
}
|
||||||
r, _, errno := syscall.Syscall6(gm, 4, uintptr(unsafe.Pointer(lpMsg)), uintptr(hWnd), uintptr(wMsgFilterMin), uintptr(wMsgFilterMax), 0, 0)
|
r, _, errno := syscall.Syscall6(gm, 4, uintptr(unsafe.Pointer(lpMsg)), uintptr(hWnd), uintptr(wMsgFilterMin), uintptr(wMsgFilterMax), 0, 0)
|
||||||
if r == 0 {
|
if int32(r) == -1 {
|
||||||
return 0, error(errno)
|
return 0, error(errno)
|
||||||
}
|
}
|
||||||
return DWORD(r), nil
|
return DWORD(r), nil
|
||||||
@ -411,8 +469,8 @@ func DispatchMessage(lpMsg *MSG) (LRESULT, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
r, _, err := syscall.Syscall(proc, 1, uintptr(unsafe.Pointer(lpMsg)), 0, 0)
|
r, _, _ := syscall.Syscall(proc, 1, uintptr(unsafe.Pointer(lpMsg)), 0, 0)
|
||||||
return LRESULT(r), err
|
return LRESULT(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefWindowProc(hWnd HWND, uMsg UINT, wParam WPARAM, lParam LPARAM) LRESULT {
|
func DefWindowProc(hWnd HWND, uMsg UINT, wParam WPARAM, lParam LPARAM) LRESULT {
|
||||||
@ -439,8 +497,14 @@ func PostMessage(hWnd HWND, msg UINT, wParam WPARAM, lParam LPARAM) (bool, error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
r, _, _ := syscall.Syscall6(proc, 4, uintptr(hWnd), uintptr(msg), uintptr(wParam), uintptr(lParam), 0, 0)
|
r, _, errno := syscall.Syscall6(proc, 4, uintptr(hWnd), uintptr(msg), uintptr(wParam), uintptr(lParam), 0, 0)
|
||||||
return r != 0, nil
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return false, error(errno)
|
||||||
|
}
|
||||||
|
return false, syscall.EINVAL
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func PostQuitMessage(nExitCode int) {
|
func PostQuitMessage(nExitCode int) {
|
||||||
@ -472,3 +536,152 @@ func RegisterClassEx(lpWndClass *WNDCLASSEX) (uint16, error) {
|
|||||||
}
|
}
|
||||||
return uint16(r), nil
|
return uint16(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func OpenInputDesktop(dwFlags DWORD, fInherit bool, dwDesiredAccess DWORD) (HDESK, error) {
|
||||||
|
proc, err := getProcAddr("user32.dll", "OpenInputDesktop")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
inherit := uintptr(0)
|
||||||
|
if fInherit {
|
||||||
|
inherit = 1
|
||||||
|
}
|
||||||
|
r, _, errno := syscall.Syscall(proc, 3, uintptr(dwFlags), inherit, uintptr(dwDesiredAccess))
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return 0, error(errno)
|
||||||
|
}
|
||||||
|
return 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
return HDESK(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CloseDesktop(hDesktop HDESK) error {
|
||||||
|
proc, err := getProcAddr("user32.dll", "CloseDesktop")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, errno := syscall.Syscall(proc, 1, uintptr(hDesktop), 0, 0)
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SwitchDesktop(hDesktop HDESK) error {
|
||||||
|
proc, err := getProcAddr("user32.dll", "SwitchDesktop")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, errno := syscall.Syscall(proc, 1, uintptr(hDesktop), 0, 0)
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetThreadDesktop(dwThreadId DWORD) (HDESK, error) {
|
||||||
|
proc, err := getProcAddr("user32.dll", "GetThreadDesktop")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
r, _, errno := syscall.Syscall(proc, 1, uintptr(dwThreadId), 0, 0)
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return 0, error(errno)
|
||||||
|
}
|
||||||
|
return 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
return HDESK(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetThreadDesktop(hDesktop HDESK) error {
|
||||||
|
proc, err := getProcAddr("user32.dll", "SetThreadDesktop")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, errno := syscall.Syscall(proc, 1, uintptr(hDesktop), 0, 0)
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDesktopWindow() HWND {
|
||||||
|
proc, err := getProcAddr("user32.dll", "GetDesktopWindow")
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 0, 0, 0, 0)
|
||||||
|
return HWND(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetShellWindow() HWND {
|
||||||
|
proc, err := getProcAddr("user32.dll", "GetShellWindow")
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 0, 0, 0, 0)
|
||||||
|
return HWND(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetForegroundWindow() HWND {
|
||||||
|
proc, err := getProcAddr("user32.dll", "GetForegroundWindow")
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 0, 0, 0, 0)
|
||||||
|
return HWND(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWindowThreadProcessId(hWnd HWND) (DWORD, DWORD, error) {
|
||||||
|
proc, err := getProcAddr("user32.dll", "GetWindowThreadProcessId")
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, err
|
||||||
|
}
|
||||||
|
var processID DWORD
|
||||||
|
r, _, errno := syscall.Syscall(proc, 2, uintptr(hWnd), uintptr(unsafe.Pointer(&processID)), 0)
|
||||||
|
threadID := DWORD(r)
|
||||||
|
if threadID == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return 0, processID, error(errno)
|
||||||
|
}
|
||||||
|
return 0, processID, syscall.EINVAL
|
||||||
|
}
|
||||||
|
return threadID, processID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWindowText(hWnd HWND) (string, error) {
|
||||||
|
lenProc, err := getProcAddr("user32.dll", "GetWindowTextLengthW")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
textProc, err := getProcAddr("user32.dll", "GetWindowTextW")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
n, _, _ := syscall.Syscall(lenProc, 1, uintptr(hWnd), 0, 0)
|
||||||
|
size := uint32(n) + 1
|
||||||
|
if size < 2 {
|
||||||
|
size = 2
|
||||||
|
}
|
||||||
|
buf := make([]uint16, size)
|
||||||
|
r, _, errno := syscall.Syscall(textProc, 3, uintptr(hWnd), uintptr(unsafe.Pointer(&buf[0])), uintptr(size))
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return "", error(errno)
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
return syscall.UTF16ToString(buf[:r]), nil
|
||||||
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package win32api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -76,3 +77,42 @@ func TestGetUpdatedClipboardFormatsAll(t *testing.T) {
|
|||||||
}
|
}
|
||||||
fmt.Println(d)
|
fmt.Println(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetUpdatedClipboardFormatsAllRetriesOnInsufficientBuffer(t *testing.T) {
|
||||||
|
calls := 0
|
||||||
|
got, err := getUpdatedClipboardFormatsAll(func(lpuiFormats unsafe.Pointer, cFormats int, pcFormats unsafe.Pointer) (int, error) {
|
||||||
|
calls++
|
||||||
|
count := (*uint32)(pcFormats)
|
||||||
|
switch calls {
|
||||||
|
case 1:
|
||||||
|
if cFormats != 32 {
|
||||||
|
t.Fatalf("first call cFormats = %d, want 32", cFormats)
|
||||||
|
}
|
||||||
|
*count = 40
|
||||||
|
return 0, syscall.ERROR_INSUFFICIENT_BUFFER
|
||||||
|
case 2:
|
||||||
|
if cFormats != 40 {
|
||||||
|
t.Fatalf("second call cFormats = %d, want 40", cFormats)
|
||||||
|
}
|
||||||
|
*count = 3
|
||||||
|
formats := (*[1 << 12]uint32)(lpuiFormats)[:cFormats:cFormats]
|
||||||
|
formats[0] = uint32(CF_TEXT)
|
||||||
|
formats[1] = uint32(CF_UNICODETEXT)
|
||||||
|
formats[2] = 0xC000
|
||||||
|
return 1, nil
|
||||||
|
default:
|
||||||
|
t.Fatalf("unexpected call count %d", calls)
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("getUpdatedClipboardFormatsAll failed: %v", err)
|
||||||
|
}
|
||||||
|
want := []DWORD{CF_TEXT, CF_UNICODETEXT, 0xC000}
|
||||||
|
if !reflect.DeepEqual(got, want) {
|
||||||
|
t.Fatalf("formats = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if calls != 2 {
|
||||||
|
t.Fatalf("calls = %d, want 2", calls)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -247,3 +247,15 @@ const (
|
|||||||
WM_QUIT = 0x0012
|
WM_QUIT = 0x0012
|
||||||
WM_DESTROY = 0x0002
|
WM_DESTROY = 0x0002
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
DESKTOP_READOBJECTS DWORD = 0x0001
|
||||||
|
DESKTOP_CREATEWINDOW DWORD = 0x0002
|
||||||
|
DESKTOP_CREATEMENU DWORD = 0x0004
|
||||||
|
DESKTOP_HOOKCONTROL DWORD = 0x0008
|
||||||
|
DESKTOP_JOURNALRECORD DWORD = 0x0010
|
||||||
|
DESKTOP_JOURNALPLAYBACK DWORD = 0x0020
|
||||||
|
DESKTOP_ENUMERATE DWORD = 0x0040
|
||||||
|
DESKTOP_WRITEOBJECTS DWORD = 0x0080
|
||||||
|
DESKTOP_SWITCHDESKTOP DWORD = 0x0100
|
||||||
|
)
|
||||||
|
|||||||
129
user32_window_test.go
Normal file
129
user32_window_test.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package win32api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDesktopWindowAndThreadProcess(t *testing.T) {
|
||||||
|
desktop := GetDesktopWindow()
|
||||||
|
if desktop == 0 {
|
||||||
|
t.Fatal("GetDesktopWindow returned 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
threadID, processID, err := GetWindowThreadProcessId(desktop)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetWindowThreadProcessId(desktop) failed: %v", err)
|
||||||
|
}
|
||||||
|
if threadID == 0 {
|
||||||
|
t.Fatal("GetWindowThreadProcessId(desktop) threadID is 0")
|
||||||
|
}
|
||||||
|
if processID == 0 {
|
||||||
|
t.Fatal("GetWindowThreadProcessId(desktop) processID is 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := GetWindowText(desktop); err != nil {
|
||||||
|
t.Fatalf("GetWindowText(desktop) failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestShellAndForegroundWindow(t *testing.T) {
|
||||||
|
shell := GetShellWindow()
|
||||||
|
if shell != 0 {
|
||||||
|
if _, _, err := GetWindowThreadProcessId(shell); err != nil {
|
||||||
|
t.Fatalf("GetWindowThreadProcessId(shell) failed: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := GetWindowText(shell); err != nil {
|
||||||
|
t.Fatalf("GetWindowText(shell) failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fg := GetForegroundWindow()
|
||||||
|
if fg != 0 {
|
||||||
|
if _, _, err := GetWindowThreadProcessId(fg); err != nil {
|
||||||
|
t.Fatalf("GetWindowThreadProcessId(foreground) failed: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := GetWindowText(fg); err != nil {
|
||||||
|
t.Fatalf("GetWindowText(foreground) failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOpenInputDesktopAndSwitch(t *testing.T) {
|
||||||
|
desk, err := OpenInputDesktop(0, false, DESKTOP_READOBJECTS|DESKTOP_SWITCHDESKTOP)
|
||||||
|
if err != nil {
|
||||||
|
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_ACCESS_DENIED {
|
||||||
|
t.Skipf("OpenInputDesktop access denied in current context: %v", err)
|
||||||
|
}
|
||||||
|
t.Fatalf("OpenInputDesktop failed: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = CloseDesktop(desk)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := SwitchDesktop(desk); err != nil {
|
||||||
|
if errno, ok := err.(syscall.Errno); ok && errno == syscall.ERROR_ACCESS_DENIED {
|
||||||
|
t.Skipf("SwitchDesktop access denied in current context: %v", err)
|
||||||
|
}
|
||||||
|
t.Fatalf("SwitchDesktop failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetThreadDesktopAndSetCurrentDesktop(t *testing.T) {
|
||||||
|
threadID := GetCurrentThreadId()
|
||||||
|
if threadID == 0 {
|
||||||
|
t.Fatal("GetCurrentThreadId returned 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
desk, err := GetThreadDesktop(threadID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetThreadDesktop failed: %v", err)
|
||||||
|
}
|
||||||
|
if desk == 0 {
|
||||||
|
t.Fatal("GetThreadDesktop returned 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := SetThreadDesktop(desk); err != nil {
|
||||||
|
if errno, ok := err.(syscall.Errno); ok {
|
||||||
|
if errno == syscall.Errno(170) || errno == syscall.ERROR_ACCESS_DENIED {
|
||||||
|
t.Skipf("SetThreadDesktop is restricted in current context: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Fatalf("SetThreadDesktop failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMessageReturnsNilOnPostedQuit(t *testing.T) {
|
||||||
|
PostQuitMessage(23)
|
||||||
|
var msg MSG
|
||||||
|
ret, err := GetMessage(&msg, 0, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GetMessage returned error for WM_QUIT: %v", err)
|
||||||
|
}
|
||||||
|
if ret != 0 {
|
||||||
|
t.Fatalf("GetMessage return = %d, want 0", ret)
|
||||||
|
}
|
||||||
|
if msg.Message != WM_QUIT {
|
||||||
|
t.Fatalf("message = %#x, want WM_QUIT", msg.Message)
|
||||||
|
}
|
||||||
|
if msg.WParam != 23 {
|
||||||
|
t.Fatalf("WM_QUIT exit code = %d, want 23", msg.WParam)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUser32BoolWrappersReturnErrors(t *testing.T) {
|
||||||
|
if ok, err := AddClipboardFormatListener(0); err == nil || ok {
|
||||||
|
t.Fatalf("AddClipboardFormatListener(0) = (%v, %v), want failure with error", ok, err)
|
||||||
|
}
|
||||||
|
if ok, err := RemoveClipboardFormatListener(0); err == nil || ok {
|
||||||
|
t.Fatalf("RemoveClipboardFormatListener(0) = (%v, %v), want failure with error", ok, err)
|
||||||
|
}
|
||||||
|
if ok, err := DestroyWindow(0); err == nil || ok {
|
||||||
|
t.Fatalf("DestroyWindow(0) = (%v, %v), want failure with error", ok, err)
|
||||||
|
}
|
||||||
|
if ok, err := PostMessage(HWND(1), WM_USER, 0, 0); err == nil || ok {
|
||||||
|
t.Fatalf("PostMessage(invalid) = (%v, %v), want failure with error", ok, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
33
userenv.go
33
userenv.go
@ -1,7 +1,6 @@
|
|||||||
package win32api
|
package win32api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
@ -15,18 +14,34 @@ BOOL CreateEnvironmentBlock(
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
func CreateEnvironmentBlock(lpEnvironment *HANDLE, hToken TOKEN, bInherit uintptr) error {
|
func CreateEnvironmentBlock(lpEnvironment *HANDLE, hToken TOKEN, bInherit uintptr) error {
|
||||||
userenv, err := syscall.LoadLibrary("userenv.dll")
|
if lpEnvironment == nil {
|
||||||
if err != nil {
|
return syscall.EINVAL
|
||||||
return errors.New("Can't Load Userenv API")
|
|
||||||
}
|
}
|
||||||
defer syscall.FreeLibrary(userenv)
|
Dup, err := getProcAddr("userenv.dll", "CreateEnvironmentBlock")
|
||||||
Dup, err := syscall.GetProcAddress(syscall.Handle(userenv), "CreateEnvironmentBlock")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Can't Load WTSQueryUserToken API")
|
return err
|
||||||
}
|
}
|
||||||
r, _, errno := syscall.Syscall6(uintptr(Dup), 3, uintptr(unsafe.Pointer(lpEnvironment)), uintptr(hToken), bInherit, 0, 0, 0)
|
r, _, errno := syscall.Syscall6(Dup, 3, uintptr(unsafe.Pointer(lpEnvironment)), uintptr(hToken), bInherit, 0, 0, 0)
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
return error(errno)
|
if errno != 0 {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DestroyEnvironmentBlock(lpEnvironment HANDLE) error {
|
||||||
|
proc, err := getProcAddr("userenv.dll", "DestroyEnvironmentBlock")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, errno := syscall.Syscall(proc, 1, uintptr(lpEnvironment), 0, 0)
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
51
win32api.go
51
win32api.go
@ -22,6 +22,7 @@ type (
|
|||||||
HBRUSH HANDLE
|
HBRUSH HANDLE
|
||||||
HCURSOR HANDLE
|
HCURSOR HANDLE
|
||||||
HDC HANDLE
|
HDC HANDLE
|
||||||
|
HDESK HANDLE
|
||||||
HDROP HANDLE
|
HDROP HANDLE
|
||||||
HDWP HANDLE
|
HDWP HANDLE
|
||||||
HENHMETAFILE HANDLE
|
HENHMETAFILE HANDLE
|
||||||
@ -54,13 +55,14 @@ type (
|
|||||||
ULONG uint32
|
ULONG uint32
|
||||||
ULONG_PTR uintptr
|
ULONG_PTR uintptr
|
||||||
WPARAM uintptr
|
WPARAM uintptr
|
||||||
WTS_CONNECTSTATE_CLASS int
|
WTS_CONNECTSTATE_CLASS int32
|
||||||
|
WTS_INFO_CLASS int32
|
||||||
TRACEHANDLE uintptr
|
TRACEHANDLE uintptr
|
||||||
TOKEN HANDLE
|
TOKEN HANDLE
|
||||||
LPWSTR *uint16
|
LPWSTR *uint16
|
||||||
TOKEN_TYPE int
|
TOKEN_TYPE int32
|
||||||
SW int
|
SW int32
|
||||||
SECURITY_IMPERSONATION_LEVEL int
|
SECURITY_IMPERSONATION_LEVEL int32
|
||||||
WCHAR uint16
|
WCHAR uint16
|
||||||
WORD uint16
|
WORD uint16
|
||||||
USN int64
|
USN int64
|
||||||
@ -70,13 +72,14 @@ type (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type WTS_SESSION_INFO struct {
|
type WTS_SESSION_INFO struct {
|
||||||
SessionID HANDLE
|
SessionID DWORD
|
||||||
WinStationName *uint16
|
WinStationName *uint16
|
||||||
State WTS_CONNECTSTATE_CLASS
|
State WTS_CONNECTSTATE_CLASS
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
WTS_CURRENT_SERVER_HANDLE uintptr = 0
|
WTS_CURRENT_SERVER_HANDLE uintptr = 0
|
||||||
|
WTS_CURRENT_SESSION DWORD = 0xFFFFFFFF
|
||||||
)
|
)
|
||||||
const (
|
const (
|
||||||
WTSActive WTS_CONNECTSTATE_CLASS = iota
|
WTSActive WTS_CONNECTSTATE_CLASS = iota
|
||||||
@ -90,6 +93,38 @@ const (
|
|||||||
WTSDown
|
WTSDown
|
||||||
WTSInit
|
WTSInit
|
||||||
)
|
)
|
||||||
|
const (
|
||||||
|
WTSInitialProgram WTS_INFO_CLASS = iota
|
||||||
|
WTSApplicationName
|
||||||
|
WTSWorkingDirectory
|
||||||
|
WTSOEMId
|
||||||
|
WTSSessionId
|
||||||
|
WTSUserName
|
||||||
|
WTSWinStationName
|
||||||
|
WTSDomainName
|
||||||
|
WTSConnectState
|
||||||
|
WTSClientBuildNumber
|
||||||
|
WTSClientName
|
||||||
|
WTSClientDirectory
|
||||||
|
WTSClientProductId
|
||||||
|
WTSClientHardwareId
|
||||||
|
WTSClientAddress
|
||||||
|
WTSClientDisplay
|
||||||
|
WTSClientProtocolType
|
||||||
|
WTSIdleTime
|
||||||
|
WTSLogonTime
|
||||||
|
WTSIncomingBytes
|
||||||
|
WTSOutgoingBytes
|
||||||
|
WTSIncomingFrames
|
||||||
|
WTSOutgoingFrames
|
||||||
|
WTSClientInfo
|
||||||
|
WTSSessionInfo
|
||||||
|
WTSSessionInfoEx
|
||||||
|
WTSConfigInfo
|
||||||
|
WTSValidationInfo
|
||||||
|
WTSSessionAddressV4
|
||||||
|
WTSIsRemoteSession
|
||||||
|
)
|
||||||
const (
|
const (
|
||||||
SecurityAnonymous SECURITY_IMPERSONATION_LEVEL = iota
|
SecurityAnonymous SECURITY_IMPERSONATION_LEVEL = iota
|
||||||
SecurityIdentification
|
SecurityIdentification
|
||||||
@ -117,9 +152,9 @@ const (
|
|||||||
SW_MAX = 1
|
SW_MAX = 1
|
||||||
)
|
)
|
||||||
const (
|
const (
|
||||||
CREATE_UNICODE_ENVIRONMENT uint16 = 0x00000400
|
CREATE_UNICODE_ENVIRONMENT DWORD = 0x00000400
|
||||||
CREATE_NO_WINDOW = 0x08000000
|
CREATE_NO_WINDOW DWORD = 0x08000000
|
||||||
CREATE_NEW_CONSOLE = 0x00000010
|
CREATE_NEW_CONSOLE DWORD = 0x00000010
|
||||||
)
|
)
|
||||||
|
|
||||||
type StartupInfo struct {
|
type StartupInfo struct {
|
||||||
|
|||||||
591
ws2_32.go
Normal file
591
ws2_32.go
Normal file
@ -0,0 +1,591 @@
|
|||||||
|
package win32api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
func makeWord(low, high byte) WORD {
|
||||||
|
return WORD(uint16(low) | uint16(high)<<8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WSAStartup(versionRequested WORD, data *WSADATA) error {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "WSAStartup")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 2, uintptr(versionRequested), uintptr(unsafe.Pointer(data)), 0)
|
||||||
|
if r != 0 {
|
||||||
|
return syscall.Errno(r)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WSACleanup() error {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "WSACleanup")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 0, 0, 0, 0)
|
||||||
|
if int32(r) == -1 {
|
||||||
|
last := WSAGetLastError()
|
||||||
|
if last != 0 {
|
||||||
|
return syscall.Errno(last)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WSAGetLastError() int {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "WSAGetLastError")
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 0, 0, 0, 0)
|
||||||
|
return int(int32(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
func wsaLastErrorOr(defaultErr error) error {
|
||||||
|
last := WSAGetLastError()
|
||||||
|
if last != 0 {
|
||||||
|
return syscall.Errno(last)
|
||||||
|
}
|
||||||
|
if defaultErr != nil {
|
||||||
|
return defaultErr
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHostName() (string, error) {
|
||||||
|
var data WSADATA
|
||||||
|
if err := WSAStartup(makeWord(2, 2), &data); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = WSACleanup()
|
||||||
|
}()
|
||||||
|
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "gethostname")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 256)
|
||||||
|
r, _, _ := syscall.Syscall(proc, 2, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0)
|
||||||
|
if int32(r) == -1 {
|
||||||
|
last := WSAGetLastError()
|
||||||
|
if last != 0 {
|
||||||
|
return "", syscall.Errno(last)
|
||||||
|
}
|
||||||
|
return "", syscall.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := bytes.IndexByte(buf, 0)
|
||||||
|
if idx < 0 {
|
||||||
|
idx = len(buf)
|
||||||
|
}
|
||||||
|
host := string(buf[:idx])
|
||||||
|
if host == "" {
|
||||||
|
return "", fmt.Errorf("gethostname returned empty host")
|
||||||
|
}
|
||||||
|
return host, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func InetPton(family int, ip string) ([]byte, error) {
|
||||||
|
var data WSADATA
|
||||||
|
if err := WSAStartup(makeWord(2, 2), &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = WSACleanup()
|
||||||
|
}()
|
||||||
|
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "InetPtonW")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var out []byte
|
||||||
|
switch family {
|
||||||
|
case AF_INET:
|
||||||
|
out = make([]byte, 4)
|
||||||
|
case AF_INET6:
|
||||||
|
out = make([]byte, 16)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported address family: %d", family)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, _, _ := syscall.Syscall(proc, 3,
|
||||||
|
uintptr(family),
|
||||||
|
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(ip))),
|
||||||
|
uintptr(unsafe.Pointer(&out[0])),
|
||||||
|
)
|
||||||
|
if int32(r) == 1 {
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
if int32(r) == 0 {
|
||||||
|
return nil, fmt.Errorf("invalid ip address: %s", ip)
|
||||||
|
}
|
||||||
|
last := WSAGetLastError()
|
||||||
|
if last != 0 {
|
||||||
|
return nil, syscall.Errno(last)
|
||||||
|
}
|
||||||
|
return nil, syscall.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
func InetNtop(family int, addr []byte) (string, error) {
|
||||||
|
var data WSADATA
|
||||||
|
if err := WSAStartup(makeWord(2, 2), &data); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = WSACleanup()
|
||||||
|
}()
|
||||||
|
|
||||||
|
switch family {
|
||||||
|
case AF_INET:
|
||||||
|
if len(addr) != 4 {
|
||||||
|
return "", fmt.Errorf("inet4 requires 4 bytes, got %d", len(addr))
|
||||||
|
}
|
||||||
|
case AF_INET6:
|
||||||
|
if len(addr) != 16 {
|
||||||
|
return "", fmt.Errorf("inet6 requires 16 bytes, got %d", len(addr))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unsupported address family: %d", family)
|
||||||
|
}
|
||||||
|
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "InetNtopW")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]uint16, 65)
|
||||||
|
r, _, _ := syscall.Syscall6(proc, 4,
|
||||||
|
uintptr(family),
|
||||||
|
uintptr(unsafe.Pointer(&addr[0])),
|
||||||
|
uintptr(unsafe.Pointer(&buf[0])),
|
||||||
|
uintptr(len(buf)),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if r == 0 {
|
||||||
|
last := WSAGetLastError()
|
||||||
|
if last != 0 {
|
||||||
|
return "", syscall.Errno(last)
|
||||||
|
}
|
||||||
|
return "", syscall.EINVAL
|
||||||
|
}
|
||||||
|
return syscall.UTF16ToString(buf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Socket(family, socketType, protocol int32) (SOCKET, error) {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "socket")
|
||||||
|
if err != nil {
|
||||||
|
return INVALID_SOCKET, err
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 3, uintptr(family), uintptr(socketType), uintptr(protocol))
|
||||||
|
s := SOCKET(r)
|
||||||
|
if s == INVALID_SOCKET {
|
||||||
|
return INVALID_SOCKET, wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Closesocket(s SOCKET) error {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "closesocket")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 1, uintptr(s), 0, 0)
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Connect(s SOCKET, name *SOCKADDR, namelen int32) error {
|
||||||
|
if name == nil {
|
||||||
|
return fmt.Errorf("sockaddr is nil")
|
||||||
|
}
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "connect")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 3, uintptr(s), uintptr(unsafe.Pointer(name)), uintptr(namelen))
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Bind(s SOCKET, name *SOCKADDR, namelen int32) error {
|
||||||
|
if name == nil {
|
||||||
|
return fmt.Errorf("sockaddr is nil")
|
||||||
|
}
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "bind")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 3, uintptr(s), uintptr(unsafe.Pointer(name)), uintptr(namelen))
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Listen(s SOCKET, backlog int32) error {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "listen")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 2, uintptr(s), uintptr(backlog), 0)
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Accept(s SOCKET, addr *SOCKADDR, addrlen *int32) (SOCKET, error) {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "accept")
|
||||||
|
if err != nil {
|
||||||
|
return INVALID_SOCKET, err
|
||||||
|
}
|
||||||
|
var addrPtr uintptr
|
||||||
|
if addr != nil {
|
||||||
|
addrPtr = uintptr(unsafe.Pointer(addr))
|
||||||
|
}
|
||||||
|
var addrLenPtr uintptr
|
||||||
|
if addrlen != nil {
|
||||||
|
addrLenPtr = uintptr(unsafe.Pointer(addrlen))
|
||||||
|
}
|
||||||
|
|
||||||
|
r, _, _ := syscall.Syscall(proc, 3, uintptr(s), addrPtr, addrLenPtr)
|
||||||
|
client := SOCKET(r)
|
||||||
|
if client == INVALID_SOCKET {
|
||||||
|
return INVALID_SOCKET, wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Send(s SOCKET, buf []byte, flags int32) (int, error) {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "send")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var bufPtr uintptr
|
||||||
|
if len(buf) > 0 {
|
||||||
|
bufPtr = uintptr(unsafe.Pointer(&buf[0]))
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall6(proc, 4, uintptr(s), bufPtr, uintptr(len(buf)), uintptr(flags), 0, 0)
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return 0, wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return int(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Recv(s SOCKET, buf []byte, flags int32) (int, error) {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "recv")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var bufPtr uintptr
|
||||||
|
if len(buf) > 0 {
|
||||||
|
bufPtr = uintptr(unsafe.Pointer(&buf[0]))
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall6(proc, 4, uintptr(s), bufPtr, uintptr(len(buf)), uintptr(flags), 0, 0)
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return 0, wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return int(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Shutdown(s SOCKET, how int32) error {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "shutdown")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 2, uintptr(s), uintptr(how), 0)
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSockName(s SOCKET, name *SOCKADDR, nameLen *int32) error {
|
||||||
|
if name == nil || nameLen == nil {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "getsockname")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 3, uintptr(s), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)))
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPeerName(s SOCKET, name *SOCKADDR, nameLen *int32) error {
|
||||||
|
if name == nil || nameLen == nil {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "getpeername")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall(proc, 3, uintptr(s), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)))
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetSockOpt(s SOCKET, level, optName int32, optValue []byte) error {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "setsockopt")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var valuePtr uintptr
|
||||||
|
if len(optValue) > 0 {
|
||||||
|
valuePtr = uintptr(unsafe.Pointer(&optValue[0]))
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall6(proc, 5,
|
||||||
|
uintptr(s),
|
||||||
|
uintptr(level),
|
||||||
|
uintptr(optName),
|
||||||
|
valuePtr,
|
||||||
|
uintptr(len(optValue)),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSockOpt(s SOCKET, level, optName int32, optValue []byte) (int32, error) {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "getsockopt")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var valuePtr uintptr
|
||||||
|
if len(optValue) > 0 {
|
||||||
|
valuePtr = uintptr(unsafe.Pointer(&optValue[0]))
|
||||||
|
}
|
||||||
|
optLen := int32(len(optValue))
|
||||||
|
r, _, _ := syscall.Syscall6(proc, 5,
|
||||||
|
uintptr(s),
|
||||||
|
uintptr(level),
|
||||||
|
uintptr(optName),
|
||||||
|
valuePtr,
|
||||||
|
uintptr(unsafe.Pointer(&optLen)),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return 0, wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return optLen, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetSockOptInt(s SOCKET, level, optName int32, value int32) error {
|
||||||
|
buf := (*[4]byte)(unsafe.Pointer(&value))[:]
|
||||||
|
return SetSockOpt(s, level, optName, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSockOptInt(s SOCKET, level, optName int32) (int32, error) {
|
||||||
|
var value int32
|
||||||
|
buf := (*[4]byte)(unsafe.Pointer(&value))[:]
|
||||||
|
optLen, err := GetSockOpt(s, level, optName, buf)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if optLen < 4 {
|
||||||
|
return 0, fmt.Errorf("getsockopt returned short length: %d", optLen)
|
||||||
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendTo(s SOCKET, buf []byte, flags int32, to *SOCKADDR, toLen int32) (int, error) {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "sendto")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var bufPtr uintptr
|
||||||
|
if len(buf) > 0 {
|
||||||
|
bufPtr = uintptr(unsafe.Pointer(&buf[0]))
|
||||||
|
}
|
||||||
|
var toPtr uintptr
|
||||||
|
if to != nil {
|
||||||
|
toPtr = uintptr(unsafe.Pointer(to))
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall6(proc, 6,
|
||||||
|
uintptr(s),
|
||||||
|
bufPtr,
|
||||||
|
uintptr(len(buf)),
|
||||||
|
uintptr(flags),
|
||||||
|
toPtr,
|
||||||
|
uintptr(toLen),
|
||||||
|
)
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return 0, wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return int(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RecvFrom(s SOCKET, buf []byte, flags int32, from *SOCKADDR, fromLen *int32) (int, error) {
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "recvfrom")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var bufPtr uintptr
|
||||||
|
if len(buf) > 0 {
|
||||||
|
bufPtr = uintptr(unsafe.Pointer(&buf[0]))
|
||||||
|
}
|
||||||
|
var fromPtr uintptr
|
||||||
|
if from != nil {
|
||||||
|
fromPtr = uintptr(unsafe.Pointer(from))
|
||||||
|
}
|
||||||
|
var fromLenPtr uintptr
|
||||||
|
if fromLen != nil {
|
||||||
|
fromLenPtr = uintptr(unsafe.Pointer(fromLen))
|
||||||
|
}
|
||||||
|
r, _, _ := syscall.Syscall6(proc, 6,
|
||||||
|
uintptr(s),
|
||||||
|
bufPtr,
|
||||||
|
uintptr(len(buf)),
|
||||||
|
uintptr(flags),
|
||||||
|
fromPtr,
|
||||||
|
fromLenPtr,
|
||||||
|
)
|
||||||
|
if int32(r) == SOCKET_ERROR {
|
||||||
|
return 0, wsaLastErrorOr(syscall.EINVAL)
|
||||||
|
}
|
||||||
|
return int(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAddrInfo(node, service string, hints *ADDRINFOW) (*ADDRINFOW, error) {
|
||||||
|
var data WSADATA
|
||||||
|
if err := WSAStartup(makeWord(2, 2), &data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = WSACleanup()
|
||||||
|
}()
|
||||||
|
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "GetAddrInfoW")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodePtr *uint16
|
||||||
|
if node != "" {
|
||||||
|
nodePtr = syscall.StringToUTF16Ptr(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
var servicePtr *uint16
|
||||||
|
if service != "" {
|
||||||
|
servicePtr = syscall.StringToUTF16Ptr(service)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result *ADDRINFOW
|
||||||
|
r, _, _ := syscall.Syscall6(proc, 4,
|
||||||
|
uintptr(unsafe.Pointer(nodePtr)),
|
||||||
|
uintptr(unsafe.Pointer(servicePtr)),
|
||||||
|
uintptr(unsafe.Pointer(hints)),
|
||||||
|
uintptr(unsafe.Pointer(&result)),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if int32(r) != 0 {
|
||||||
|
return nil, fmt.Errorf("GetAddrInfoW failed: %d", int32(r))
|
||||||
|
}
|
||||||
|
if result == nil {
|
||||||
|
return nil, fmt.Errorf("GetAddrInfoW returned nil result")
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FreeAddrInfo(result *ADDRINFOW) error {
|
||||||
|
if result == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "FreeAddrInfoW")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
syscall.Syscall(proc, 1, uintptr(unsafe.Pointer(result)), 0, 0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNameInfo(sa *SOCKADDR, saLen int32, flags int32) (string, string, error) {
|
||||||
|
if sa == nil {
|
||||||
|
return "", "", fmt.Errorf("sockaddr is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
var data WSADATA
|
||||||
|
if err := WSAStartup(makeWord(2, 2), &data); err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = WSACleanup()
|
||||||
|
}()
|
||||||
|
|
||||||
|
proc, err := getProcAddr("ws2_32.dll", "GetNameInfoW")
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
host := make([]uint16, NI_MAXHOST)
|
||||||
|
service := make([]uint16, NI_MAXSERV)
|
||||||
|
r, _, _ := syscall.Syscall9(proc, 7,
|
||||||
|
uintptr(unsafe.Pointer(sa)),
|
||||||
|
uintptr(saLen),
|
||||||
|
uintptr(unsafe.Pointer(&host[0])),
|
||||||
|
uintptr(len(host)),
|
||||||
|
uintptr(unsafe.Pointer(&service[0])),
|
||||||
|
uintptr(len(service)),
|
||||||
|
uintptr(flags),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if int32(r) != 0 {
|
||||||
|
return "", "", fmt.Errorf("GetNameInfoW failed: %d", int32(r))
|
||||||
|
}
|
||||||
|
return syscall.UTF16ToString(host), syscall.UTF16ToString(service), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SockaddrIPString(sa *SOCKADDR) (string, error) {
|
||||||
|
if sa == nil {
|
||||||
|
return "", fmt.Errorf("sockaddr is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch int(sa.Family) {
|
||||||
|
case AF_INET:
|
||||||
|
addr := (*SOCKADDR_IN)(unsafe.Pointer(sa))
|
||||||
|
return InetNtop(AF_INET, addr.Addr[:])
|
||||||
|
case AF_INET6:
|
||||||
|
addr := (*SOCKADDR_IN6)(unsafe.Pointer(sa))
|
||||||
|
return InetNtop(AF_INET6, addr.Addr[:])
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unsupported sockaddr family: %d", sa.Family)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Htons(v uint16) uint16 {
|
||||||
|
return (v << 8) | (v >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Ntohs(v uint16) uint16 {
|
||||||
|
return Htons(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func htons(v uint16) uint16 {
|
||||||
|
return Htons(v)
|
||||||
|
}
|
||||||
137
ws2_32typedef.go
Normal file
137
ws2_32typedef.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
package win32api
|
||||||
|
|
||||||
|
const (
|
||||||
|
WSADESCRIPTION_LEN = 256
|
||||||
|
WSASYS_STATUS_LEN = 128
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AF_UNSPEC = 0
|
||||||
|
AF_INET = 2
|
||||||
|
AF_INET6 = 23
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SOCK_STREAM = 1
|
||||||
|
SOCK_DGRAM = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SOL_SOCKET = 0xffff
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SO_REUSEADDR = 0x0004
|
||||||
|
SO_ERROR = 0x1007
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
IPPROTO_IP = 0
|
||||||
|
IPPROTO_TCP = 6
|
||||||
|
IPPROTO_UDP = 17
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
AI_PASSIVE = 0x00000001
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
GAA_FLAG_SKIP_UNICAST = 0x00000001
|
||||||
|
GAA_FLAG_SKIP_ANYCAST = 0x00000002
|
||||||
|
GAA_FLAG_SKIP_MULTICAST = 0x00000004
|
||||||
|
GAA_FLAG_SKIP_DNS_SERVER = 0x00000008
|
||||||
|
GAA_FLAG_INCLUDE_PREFIX = 0x00000010
|
||||||
|
GAA_FLAG_SKIP_FRIENDLY_NAME = 0x00000020
|
||||||
|
GAA_FLAG_INCLUDE_WINS_INFO = 0x00000040
|
||||||
|
GAA_FLAG_INCLUDE_GATEWAYS = 0x00000080
|
||||||
|
GAA_FLAG_INCLUDE_ALL_INTERFACES = 0x00000100
|
||||||
|
GAA_FLAG_INCLUDE_ALL_COMPARTMENTS = 0x00000200
|
||||||
|
GAA_FLAG_INCLUDE_TUNNEL_BINDINGORDER = 0x00000400
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NI_MAXHOST = 1025
|
||||||
|
NI_MAXSERV = 32
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NI_NUMERICHOST = 0x00000002
|
||||||
|
NI_NUMERICSERV = 0x00000008
|
||||||
|
)
|
||||||
|
|
||||||
|
type SOCKET uintptr
|
||||||
|
|
||||||
|
const (
|
||||||
|
INVALID_SOCKET SOCKET = ^SOCKET(0)
|
||||||
|
SOCKET_ERROR int32 = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SD_RECEIVE = 0
|
||||||
|
SD_SEND = 1
|
||||||
|
SD_BOTH = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SOMAXCONN int32 = 0x7fffffff
|
||||||
|
)
|
||||||
|
|
||||||
|
type WSADATA struct {
|
||||||
|
WVersion WORD
|
||||||
|
WHighVersion WORD
|
||||||
|
SzDescription [WSADESCRIPTION_LEN + 1]byte
|
||||||
|
SzSystemStatus [WSASYS_STATUS_LEN + 1]byte
|
||||||
|
IMaxSockets WORD
|
||||||
|
IMaxUdpDg WORD
|
||||||
|
LPVendorInfo *byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type ADDRESS_FAMILY uint16
|
||||||
|
|
||||||
|
type SOCKADDR struct {
|
||||||
|
Family ADDRESS_FAMILY
|
||||||
|
Data [14]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type SOCKADDR_IN struct {
|
||||||
|
Family ADDRESS_FAMILY
|
||||||
|
Port uint16
|
||||||
|
Addr [4]byte
|
||||||
|
Zero [8]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type SOCKADDR_IN6 struct {
|
||||||
|
Family ADDRESS_FAMILY
|
||||||
|
Port uint16
|
||||||
|
Flowinfo uint32
|
||||||
|
Addr [16]byte
|
||||||
|
ScopeID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type ADDRINFOW struct {
|
||||||
|
Flags int32
|
||||||
|
Family int32
|
||||||
|
Socktype int32
|
||||||
|
Protocol int32
|
||||||
|
Addrlen uintptr
|
||||||
|
Canonname *uint16
|
||||||
|
Addr *SOCKADDR
|
||||||
|
Next *ADDRINFOW
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdapterAddressInfo struct {
|
||||||
|
IfIndex uint32
|
||||||
|
AdapterName string
|
||||||
|
FriendlyName string
|
||||||
|
Description string
|
||||||
|
DNSSuffix string
|
||||||
|
OperStatus uint32
|
||||||
|
Mtu uint32
|
||||||
|
MACAddress string
|
||||||
|
PhysicalAddressLength uint32
|
||||||
|
TransmitLinkSpeed uint64
|
||||||
|
ReceiveLinkSpeed uint64
|
||||||
|
UnicastIPs []string
|
||||||
|
DNSServers []string
|
||||||
|
Gateways []string
|
||||||
|
}
|
||||||
90
wtsapi32.go
90
wtsapi32.go
@ -1,42 +1,74 @@
|
|||||||
package win32api
|
package win32api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func WTSQueryUserToken(SessionId HANDLE, phToken *HANDLE) error {
|
func WTSQueryUserToken(SessionId DWORD, phToken *HANDLE) error {
|
||||||
wtsapi32, err := syscall.LoadLibrary("wtsapi32.dll")
|
WTGet, err := getProcAddr("wtsapi32.dll", "WTSQueryUserToken")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Can't Load Wtsapi32 API")
|
return err
|
||||||
}
|
}
|
||||||
defer syscall.FreeLibrary(wtsapi32)
|
r, _, errno := syscall.Syscall(WTGet, 2, uintptr(SessionId), uintptr(unsafe.Pointer(phToken)), 0)
|
||||||
WTGet, err := syscall.GetProcAddress(syscall.Handle(wtsapi32), "WTSQueryUserToken")
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("Can't Load WTSQueryUserToken API")
|
|
||||||
}
|
|
||||||
r, _, errno := syscall.Syscall(uintptr(WTGet), 2, uintptr(SessionId), uintptr(unsafe.Pointer(phToken)), 0)
|
|
||||||
if r == 0 {
|
if r == 0 {
|
||||||
return error(errno)
|
if errno != 0 {
|
||||||
} else {
|
return error(errno)
|
||||||
return nil
|
}
|
||||||
}
|
return syscall.EINVAL
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
func WTSEnumerateSessions(hServer HANDLE, Reserved, Version DWORD, ppSessionInfo *HANDLE, pCount *int) error {
|
}
|
||||||
wtsapi32, err := syscall.LoadLibrary("wtsapi32.dll")
|
|
||||||
if err != nil {
|
func WTSEnumerateSessions(hServer HANDLE, Reserved, Version DWORD, ppSessionInfo *HANDLE, pCount *DWORD) error {
|
||||||
return errors.New("Can't Load Wtsapi32 API")
|
WT, err := getProcAddr("wtsapi32.dll", "WTSEnumerateSessionsW")
|
||||||
}
|
if err != nil {
|
||||||
defer syscall.FreeLibrary(wtsapi32)
|
return err
|
||||||
WT, err := syscall.GetProcAddress(syscall.Handle(wtsapi32), "WTSEnumerateSessionsW")
|
}
|
||||||
if err != nil {
|
r, _, errno := syscall.Syscall6(WT, 5, uintptr(hServer), uintptr(Reserved), uintptr(Version), uintptr(unsafe.Pointer(ppSessionInfo)), uintptr(unsafe.Pointer(pCount)), 0)
|
||||||
return errors.New("Can't Load WTSQueryUserToken API")
|
if r == 0 {
|
||||||
}
|
if errno != 0 {
|
||||||
r, _, errno := syscall.Syscall6(uintptr(WT), 5, uintptr(hServer), uintptr(Reserved), uintptr(Version), uintptr(unsafe.Pointer(ppSessionInfo)), uintptr(unsafe.Pointer(pCount)), 0)
|
return error(errno)
|
||||||
if r == 0 {
|
}
|
||||||
return error(errno)
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WTSFreeMemory(pMemory HANDLE) error {
|
||||||
|
wfm, err := getProcAddr("wtsapi32.dll", "WTSFreeMemory")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
syscall.Syscall(wfm, 1, uintptr(pMemory), 0, 0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WTSQuerySessionInformation(hServer HANDLE, sessionID DWORD, wtsInfoClass WTS_INFO_CLASS, ppBuffer *HANDLE, pBytesReturned *DWORD) error {
|
||||||
|
if ppBuffer == nil || pBytesReturned == nil {
|
||||||
|
return syscall.EINVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
proc, err := getProcAddr("wtsapi32.dll", "WTSQuerySessionInformationW")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
r, _, errno := syscall.Syscall6(
|
||||||
|
proc,
|
||||||
|
5,
|
||||||
|
uintptr(hServer),
|
||||||
|
uintptr(sessionID),
|
||||||
|
uintptr(wtsInfoClass),
|
||||||
|
uintptr(unsafe.Pointer(ppBuffer)),
|
||||||
|
uintptr(unsafe.Pointer(pBytesReturned)),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if r == 0 {
|
||||||
|
if errno != 0 {
|
||||||
|
return error(errno)
|
||||||
|
}
|
||||||
|
return syscall.EINVAL
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
148
wtsapi32_helper.go
Normal file
148
wtsapi32_helper.go
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package win32api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WTSSession struct {
|
||||||
|
SessionID DWORD
|
||||||
|
WinStationName string
|
||||||
|
State WTS_CONNECTSTATE_CLASS
|
||||||
|
}
|
||||||
|
|
||||||
|
type WTSSessionDetails struct {
|
||||||
|
SessionID DWORD
|
||||||
|
UserName string
|
||||||
|
DomainName string
|
||||||
|
WinStationName string
|
||||||
|
ConnectState WTS_CONNECTSTATE_CLASS
|
||||||
|
IsRemote bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnumerateSessions() ([]WTSSession, error) {
|
||||||
|
var sessionInfo HANDLE
|
||||||
|
var sessionCount DWORD
|
||||||
|
if err := WTSEnumerateSessions(0, 0, 1, &sessionInfo, &sessionCount); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if sessionInfo == 0 || sessionCount <= 0 {
|
||||||
|
return []WTSSession{}, nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = WTSFreeMemory(sessionInfo)
|
||||||
|
}()
|
||||||
|
|
||||||
|
structSize := unsafe.Sizeof(WTS_SESSION_INFO{})
|
||||||
|
current := uintptr(sessionInfo)
|
||||||
|
sessions := make([]WTSSession, 0, int(sessionCount))
|
||||||
|
for i := DWORD(0); i < sessionCount; i++ {
|
||||||
|
info := (*WTS_SESSION_INFO)(unsafe.Pointer(current))
|
||||||
|
name := ""
|
||||||
|
if info.WinStationName != nil {
|
||||||
|
name = windows.UTF16PtrToString(info.WinStationName)
|
||||||
|
}
|
||||||
|
sessions = append(sessions, WTSSession{
|
||||||
|
SessionID: info.SessionID,
|
||||||
|
WinStationName: name,
|
||||||
|
State: info.State,
|
||||||
|
})
|
||||||
|
current += structSize
|
||||||
|
}
|
||||||
|
return sessions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ActiveSessionID() (DWORD, error) {
|
||||||
|
sessions, err := EnumerateSessions()
|
||||||
|
if err == nil {
|
||||||
|
for _, session := range sessions {
|
||||||
|
if session.State == WTSActive {
|
||||||
|
return session.SessionID, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionID, sessionErr := WTSGetActiveConsoleSessionId()
|
||||||
|
if sessionID != WTS_CURRENT_SESSION {
|
||||||
|
return sessionID, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return sessionID, fmt.Errorf("enumerate sessions: %w; active console session fallback: %v", err, sessionErr)
|
||||||
|
}
|
||||||
|
if sessionErr != nil {
|
||||||
|
return sessionID, fmt.Errorf("get active console session id: %w", sessionErr)
|
||||||
|
}
|
||||||
|
return sessionID, fmt.Errorf("WTSGetActiveConsoleSessionId returned invalid session id")
|
||||||
|
}
|
||||||
|
|
||||||
|
func WTSQuerySessionString(hServer HANDLE, sessionID DWORD, infoClass WTS_INFO_CLASS) (string, error) {
|
||||||
|
var buffer HANDLE
|
||||||
|
var bytesReturned DWORD
|
||||||
|
if err := WTSQuerySessionInformation(hServer, sessionID, infoClass, &buffer, &bytesReturned); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if buffer == 0 || bytesReturned == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = WTSFreeMemory(buffer)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return windows.UTF16PtrToString((*uint16)(unsafe.Pointer(buffer))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func WTSQuerySessionDWORD(hServer HANDLE, sessionID DWORD, infoClass WTS_INFO_CLASS) (DWORD, error) {
|
||||||
|
var buffer HANDLE
|
||||||
|
var bytesReturned DWORD
|
||||||
|
if err := WTSQuerySessionInformation(hServer, sessionID, infoClass, &buffer, &bytesReturned); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if buffer == 0 || bytesReturned < DWORD(unsafe.Sizeof(DWORD(0))) {
|
||||||
|
return 0, syscall.EINVAL
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = WTSFreeMemory(buffer)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return *(*DWORD)(unsafe.Pointer(buffer)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSessionDetails(hServer HANDLE, sessionID DWORD) (WTSSessionDetails, error) {
|
||||||
|
userName, err := WTSQuerySessionString(hServer, sessionID, WTSUserName)
|
||||||
|
if err != nil {
|
||||||
|
return WTSSessionDetails{}, err
|
||||||
|
}
|
||||||
|
domainName, err := WTSQuerySessionString(hServer, sessionID, WTSDomainName)
|
||||||
|
if err != nil {
|
||||||
|
return WTSSessionDetails{}, err
|
||||||
|
}
|
||||||
|
winStationName, err := WTSQuerySessionString(hServer, sessionID, WTSWinStationName)
|
||||||
|
if err != nil {
|
||||||
|
return WTSSessionDetails{}, err
|
||||||
|
}
|
||||||
|
stateRaw, err := WTSQuerySessionDWORD(hServer, sessionID, WTSConnectState)
|
||||||
|
if err != nil {
|
||||||
|
return WTSSessionDetails{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
isRemote := false
|
||||||
|
if remoteRaw, remoteErr := WTSQuerySessionDWORD(hServer, sessionID, WTSIsRemoteSession); remoteErr == nil {
|
||||||
|
isRemote = remoteRaw != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return WTSSessionDetails{
|
||||||
|
SessionID: sessionID,
|
||||||
|
UserName: userName,
|
||||||
|
DomainName: domainName,
|
||||||
|
WinStationName: winStationName,
|
||||||
|
ConnectState: WTS_CONNECTSTATE_CLASS(stateRaw),
|
||||||
|
IsRemote: isRemote,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CurrentProcessSessionID() (DWORD, error) {
|
||||||
|
return ProcessIdToSessionId(GetCurrentProcessId())
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user