New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
x/sys/windows: Add WSALookupService #54232
Comments
What is the API being suggested for x/sys/windows? If it is a direct port of structs and functions from Windows APIs, we typically fast-path it through the proposal process. Looking at the linked PRs, I see some extra methods
and a new function WcharToString as well as a Wchar type. Are those being suggested for x/sys/windows too? Those would require more discussion. If we can add just the struct and the direct DLL methods, that would simplify the process. |
This proposal has been added to the active column of the proposals project |
@rsc I'm not forcing those, if there is a solution which works I'm good with it. Also if you agree on the proposal, I'm happy to create a change request on Gerrit. On the other had, I more care about the |
I try to figure out the reason of the What can be a solution for this? |
Also I fount out, that the length of WSAQUERYSET is 120. I need to pass it that size into the WSALookupServiceNext, which overwrites that (since that parameter of the syscall is an |
I could have fix it, so I have a properly working example of my proposal.
From my side the proposal is completed so if you decides on that I can have a PR on Gerrit. Note: These changes are located on the fix-runtime-issue branch. |
@PumpkinSeed can you please post the API changes here as a self-contained comment? Thanks. /cc @alexbrainman |
@rsc Yes, those are located here (these are actually used in production in our windows app). // https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsalookupservicebeginw dwControlFlags
const (
LUP_DEEP = 0x0001
LUP_CONTAINERS = 0x0002
LUP_NOCONTAINERS = 0x0004
LUP_NEAREST = 0x0008
LUP_RETURN_NAME = 0x0010
LUP_RETURN_TYPE = 0x0020
LUP_RETURN_VERSION = 0x0040
LUP_RETURN_COMMENT = 0x0080
LUP_RETURN_ADDR = 0x0100
LUP_RETURN_BLOB = 0x0200
LUP_RETURN_ALIASES = 0x0400
LUP_RETURN_QUERY_STRING = 0x0800
LUP_RETURN_ALL = 0x0FF0
LUP_RES_SERVICE = 0x8000
LUP_FLUSHCACHE = 0x1000
LUP_FLUSHPREVIOUS = 0x2000
LUP_NON_AUTHORITATIVE = 0x4000
LUP_SECURE = 0x8000
LUP_RETURN_PREFERRED_NAMES = 0x10000
LUP_DNS_ONLY = 0x20000
LUP_ADDRCONFIG = 0x100000
LUP_DUAL_ADDR = 0x200000
LUP_FILESERVER = 0x400000
LUP_DISABLE_IDN_ENCODING = 0x00800000
LUP_API_ANSI = 0x01000000
LUP_RESOLUTION_HANDLE = 0x80000000
)
const socket_error = uintptr(^uint32(0))
const errnoERROR_IO_PENDING = 997
var (
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
errERROR_EINVAL error = syscall.EINVAL
)
var (
modws2_32 = windows.NewLazySystemDLL("ws2_32.dll")
procWSALookupServiceBegin = modws2_32.NewProc("WSALookupServiceBeginW")
procWSALookupServiceNext = modws2_32.NewProc("WSALookupServiceNextW")
procWSALookupServiceEnd = modws2_32.NewProc("WSALookupServiceEnd")
)
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/ns-winsock2-wsaquerysetw
type WSAQUERYSET struct {
Size uint32
ServiceInstanceName *uint16
ServiceClassId *windows.GUID
Version *WSAVersion
Comment *uint16
NameSpace uint32
NSProviderId *windows.GUID
Context *uint16
NumberOfProtocols uint32
AfpProtocols *AFProtocols
QueryString *uint16
NumberOfCsAddrs uint32
SaBuffer *AddrInfo
OutputFlags uint32
Blob *BLOB
}
// ----
// This section are helper methods, because these fields are raw pointers.
func (w WSAQUERYSET) ServiceInstanceNameToString() string {
return RawPointerToString(w.ServiceInstanceName)
}
func (w WSAQUERYSET) CommentToString() string {
return RawPointerToString(w.Comment)
}
func (w WSAQUERYSET) ContextToString() string {
return RawPointerToString(w.Context)
}
func (w WSAQUERYSET) QueryStringToString() string {
return RawPointerToString(w.QueryString)
}
// ----
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/ns-winsock2-wsaversion
type WSAVersion struct {
Version uint32 // DWORD
EnumerationOfComparision int32 // WSAEcomparator enum
}
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/ns-winsock2-afprotocols
type AFProtocols struct {
AddressFamily int32
Protocol int32
}
// https://docs.microsoft.com/en-us/windows/win32/winsock/sockaddr-2
type Sockaddr struct {
Family uint16
Data [14]byte
}
// https://docs.microsoft.com/en-us/windows/win32/api/Ws2def/ns-ws2def-socket_address
type SocketAddress struct {
Sockaddr *Sockaddr
SockaddrLength int
}
// https://docs.microsoft.com/en-us/windows/win32/api/ws2def/ns-ws2def-csaddr_info
type AddrInfo struct {
LocalAddr SocketAddress
RemoteAddr SocketAddress
SocketType int32
Protocol int32
}
// https://docs.microsoft.com/en-us/windows/win32/api/winsock2/ns-winsock2-blob
type BLOB struct {
Size uint32
BlobData *byte // TODO how to represent a block of data in Go?
}
// this can be unexported
func RawPointerToString(w *uint16) string {
if w != nil {
us := make([]uint16, 0, 256)
for p := uintptr(unsafe.Pointer(w)); ; p += 2 {
u := *(*uint16)(unsafe.Pointer(p))
if u == 0 {
return string(utf16.Decode(us))
}
us = append(us, u)
}
}
return ""
}
func WSALookupServiceBegin(querySet *WSAQUERYSET, flags uint32, handle *windows.Handle) error {
var qs = unsafe.Pointer(querySet)
r, _, errNo := syscall.SyscallN(procWSALookupServiceBegin.Addr(), uintptr(qs), uintptr(flags), uintptr(unsafe.Pointer(handle)))
if r == socket_error {
return errnoErr(errNo)
}
return nil
}
func WSALookupServiceNext(handle windows.Handle, flags uint32, size *int32) (WSAQUERYSET, error) {
var data = initializeWSAQUERYSET()
r, _, errNo := syscall.SyscallN(procWSALookupServiceNext.Addr(), uintptr(handle), uintptr(flags), uintptr(unsafe.Pointer(size)), uintptr(unsafe.Pointer(data)))
if r == socket_error {
return WSAQUERYSET{}, errnoErr(errNo)
}
var ret WSAQUERYSET
if data != nil {
ret = *data
}
return ret, nil
}
func WSALookupServiceEnd(handle windows.Handle) error {
r, _, errNo := syscall.SyscallN(procWSALookupServiceEnd.Addr(), uintptr(handle))
if r == socket_error {
return errnoErr(errNo)
}
return nil
}
// This one is better to have in here, because makes it easier to use the WSALookupServiceNext without runtime panic (see above)
func initializeWSAQUERYSET() *WSAQUERYSET {
var serviceInstanceName = new(uint16)
var serviceClassId = new(windows.GUID)
var version = new(WSAVersion)
var nsProviderId = new(windows.GUID)
var comment = new(uint16)
var context = new(uint16)
var afpProtocols = new(AFProtocols)
var queryString = new(uint16)
var saBuffer = &AddrInfo{
LocalAddr: SocketAddress{
Sockaddr: &Sockaddr{},
},
RemoteAddr: SocketAddress{
Sockaddr: &Sockaddr{},
},
}
var blob = &BLOB{
BlobData: new(byte),
}
return &WSAQUERYSET{
ServiceInstanceName: serviceInstanceName,
ServiceClassId: serviceClassId,
Version: version,
NSProviderId: nsProviderId,
Comment: comment,
Context: context,
AfpProtocols: afpProtocols,
QueryString: queryString,
SaBuffer: saBuffer,
Blob: blob,
}
}
// I'm not sure this needs in here, probably already exists
func errnoErr(e syscall.Errno) error {
switch e {
case 0:
return errERROR_EINVAL
case errnoERROR_IO_PENDING:
return errERROR_IO_PENDING
}
return e
} |
If you just want to add these (and correspondent consts) to golang.org/x/sys/windows package, go ahead. I usually review such additions. I expected you aware that Go already has code to access DnsQuery Windows API. Alex |
Change https://go.dev/cl/422997 mentions this issue: |
@alexbrainman I added the changes. One thing which is detailed above (also commented in the CL), that it requires the initializeWSAQUERYSET because it returns a pointer and this "force" a heap allocation. Since the WSALookupServiceNext modifies that struct while it's unsafe.Pointer it throws a runtime panic otherwise. The initializeWSAQUERYSET initialize the struct fields and force that heap allocation so the runtime will be aware of the changes of memory while it's unsafe.Pointer. |
CL 422997 has just data structures. If that's the proposal, that's fine. RawPointerToString and the helper methods seem out of place here. The rest of the package doesn't provide that by convention. Perhaps it should, but if so that should be a separate proposal and we should apply the change consistently across the whole package, not just in this one API. If we can drop RawPointerToString and the helpers, then I think this is a clear accept. |
The package is not consistent about string helpers. For example DrvInfoData has helpers to convert |
@rsc I didn't add them, because I didn't feel an extreme necessity of that. I can create a new proposal right after it's getting merged, but for now I would be very happy to have it in. @aclements I think once we have this in, I can create another proposal which can have a talk about this issue. I think many of these wrappers in Windows ecosystem requires this |
Let's discuss details on Alex |
The current CL 422997 is just adding data structures and functions from the DLL itself, which is completely fine. Will mark this accepted assuming the CL stays that way. |
Currently test running into a runtime panic. This is because the corresponding buffer isn't initialized properly, I investigated the source of the issue, see above. Since the CL does not contain that changes it will throw the runtime panic. Alex commented that he can look into to problem. Also I think it would be nice to have a wrapper function which makes the WSALookupServiceNext's usage easier, because currently it is a pain to use just like having the DLL functions. What do you think about? Can I open an other proposal for that? Also I saw that the generation of the DLL functions using deprecated functions. This is something what I can work on? Is there an issue for that or can I create one? |
No change in consensus, so accepted. 🎉 |
Do you have any update on this one? It's pending on the CL side. |
Change https://go.dev/cl/461296 mentions this issue: |
Brief overview
Adding the following structs and functions:
Implementation proposal
I started to wrap up a possible implementation. I wrote everything in C first and after I implemented the same functionality and structs in Go.
Exact functionality and structs
Testing environment
Proofs of memory layout
There are proofs, that the WSAQUERYSET and other functions memory layout and data alignment are the same as the syscall can accept it.
C implementation
Current issue what I run into
The implementation works fine, prints the device what I expect, but at the and of the Scan function in the discover.go I just get something like a panic, which says
runtime: unknown pc 0x0
. I couldn't solve this issue yet. The detailed view shows that it's printing the devices and it's MAC addresses and the debug lines as well, and right after dies. It seems like the closing}
wants to free some memory which doesn't exists or I don't know.The text was updated successfully, but these errors were encountered: