Aman Gupta would like Alex Brainman to review this change.
syscall: add WSASendMsg/WSARecvMsg wrappers on Windows
The function pointers for these syscalls have to be loaded at runtime
with WSAIoctl, similar to how the ConnectEx syscall is loaded. A similar
pattern is used here to load the pair together.
Change-Id: I4fbef1c3b8e0ac0ab3b50b47c26845d43ea0233a
---
M src/syscall/syscall_windows.go
M src/syscall/types_windows.go
2 files changed, 110 insertions(+), 0 deletions(-)
diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go
index 84d5528..ba935d7 100644
--- a/src/syscall/syscall_windows.go
+++ b/src/syscall/syscall_windows.go
@@ -813,6 +813,83 @@
return connectEx(fd, ptr, n, sendBuf, sendDataLen, bytesSent, overlapped)
}
+var sendRecvMsgFunc struct {
+ once sync.Once
+ sendAddr uintptr
+ recvAddr uintptr
+ err error
+}
+
+func LoadSendRecvMsg() error {
+ sendRecvMsgFunc.once.Do(func() {
+ var s Handle
+ s, sendRecvMsgFunc.err = Socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+ if sendRecvMsgFunc.err != nil {
+ return
+ }
+ defer CloseHandle(s)
+ var n uint32
+ sendRecvMsgFunc.err = WSAIoctl(s,
+ SIO_GET_EXTENSION_FUNCTION_POINTER,
+ (*byte)(unsafe.Pointer(&WSAID_WSASENDMSG)),
+ uint32(unsafe.Sizeof(WSAID_WSASENDMSG)),
+ (*byte)(unsafe.Pointer(&sendRecvMsgFunc.sendAddr)),
+ uint32(unsafe.Sizeof(sendRecvMsgFunc.sendAddr)),
+ &n, nil, 0)
+ if sendRecvMsgFunc.err != nil {
+ return
+ }
+ sendRecvMsgFunc.err = WSAIoctl(s,
+ SIO_GET_EXTENSION_FUNCTION_POINTER,
+ (*byte)(unsafe.Pointer(&WSAID_WSARECVMSG)),
+ uint32(unsafe.Sizeof(WSAID_WSARECVMSG)),
+ (*byte)(unsafe.Pointer(&sendRecvMsgFunc.recvAddr)),
+ uint32(unsafe.Sizeof(sendRecvMsgFunc.recvAddr)),
+ &n, nil, 0)
+ })
+ return sendRecvMsgFunc.err
+}
+
+func sendMsg(s Handle, msg *WSAMsg, flags uint32, bytesSent *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) {
+ r1, _, e1 := Syscall9(sendRecvMsgFunc.sendAddr, 6, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags), uintptr(unsafe.Pointer(bytesSent)), uintptr(unsafe.Pointer(overlapped)), completionRoutine, 0, 0, 0)
+ if r1 != 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func recvMsg(s Handle, msg *WSAMsg, bytesReceived *uint32, overlapped *Overlapped, completionRoutine uintptr) (err error) {
+ r1, _, e1 := Syscall9(sendRecvMsgFunc.recvAddr, 5, uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(unsafe.Pointer(bytesReceived)), uintptr(unsafe.Pointer(overlapped)), completionRoutine, 0, 0, 0, 0)
+ if r1 != 0 {
+ if e1 != 0 {
+ err = error(e1)
+ } else {
+ err = EINVAL
+ }
+ }
+ return
+}
+
+func WSASendMsg(fd Handle, msg *WSAMsg, flags uint32, bytesSent *uint32, overlapped *Overlapped, completionRoutine uintptr) error {
+ err := LoadSendRecvMsg()
+ if err != nil {
+ return errorspkg.New("failed to find WSASendMsg/WSARecvMsg: " + err.Error())
+ }
+ return sendMsg(fd, msg, flags, bytesSent, overlapped, completionRoutine)
+}
+
+func WSARecvMsg(fd Handle, msg *WSAMsg, bytesReceived *uint32, overlapped *Overlapped, completionRoutine uintptr) error {
+ err := LoadSendRecvMsg()
+ if err != nil {
+ return errorspkg.New("failed to find WSASendMsg/WSARecvMsg: " + err.Error())
+ }
+ return recvMsg(fd, msg, bytesReceived, overlapped, completionRoutine)
+}
+
// Invented structures to support what package os expects.
type Rusage struct {
CreationTime Filetime
diff --git a/src/syscall/types_windows.go b/src/syscall/types_windows.go
index bc9bd4d..0144d65 100644
--- a/src/syscall/types_windows.go
+++ b/src/syscall/types_windows.go
@@ -579,6 +579,16 @@
WSADESCRIPTION_LEN = 256
WSASYS_STATUS_LEN = 128
+
+ MSG_OOB = 0x1
+ MSG_PEEK = 0x2
+ MSG_DONTROUTE = 0x4
+ MSG_WAITALL = 0x8
+
+ MSG_TRUNC = 0x0100
+ MSG_CTRUNC = 0x0200
+ MSG_BCAST = 0x0400
+ MSG_MCAST = 0x0800
)
type WSABuf struct {
@@ -586,6 +596,15 @@
Buf *byte
}
+type WSAMsg struct {
+ Name uintptr
+ Namelen int32
+ Buffers *WSABuf
+ Bufferslen uint32
+ Control WSABuf
+ Flags uint32
+}
+
// Invented values to support what package os expects.
const (
S_IFMT = 0x1f000
@@ -1013,6 +1032,20 @@
[8]byte{0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e},
}
+var WSAID_WSASENDMSG = GUID{
+ 0xa441e712,
+ 0x754f,
+ 0x43ca,
+ [8]byte{0x84, 0xa7, 0x0d, 0xee, 0x44, 0xcf, 0x60, 0x6d},
+}
+
+var WSAID_WSARECVMSG = GUID{
+ 0xf689d7c8,
+ 0x6f1f,
+ 0x436b,
+ [8]byte{0x8a, 0x53, 0xe5, 0x4f, 0xe3, 0x51, 0xc3, 0x22},
+}
+
const (
FILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1
FILE_SKIP_SET_EVENT_ON_HANDLE = 2
To view, visit change 76392. To unsubscribe, or for help writing mail filters, visit settings.
Aman Gupta uploaded patch set #3 to this change.
syscall: add WSASendMsg/WSARecvMsg wrappers on Windows
The function pointers for these syscalls have to be loaded at runtime
with WSAIoctl, similar to how the ConnectEx syscall is loaded. A similar
pattern is used here to load the pair together.
Change-Id: I4fbef1c3b8e0ac0ab3b50b47c26845d43ea0233a
---
M src/syscall/syscall_windows.go
M src/syscall/types_windows.go
2 files changed, 111 insertions(+), 0 deletions(-)
To view, visit change 76392. To unsubscribe, or for help writing mail filters, visit settings.
Thank you for doing this.
Alex
4 comments:
Patch Set #2, Line 7: syscall: add WSASendMsg/WSARecvMsg wrappers on Windows
You cannot change syscall package (grep "go doc syscall" for "This package is locked down" for details).
So please move all that code into internal/syscall/windows.
File src/syscall/syscall_windows.go:
Patch Set #2, Line 832: sendRecvMsgFunc.err = WSAIoctl(s,
Both WSASendMsg and WSARecvMsg live inside of ws2_32.dll. So you code could be simpler. You could just add this:
const socket_error = uintptr(^uint32(0))
//sys WSASendMsg(fd syscall.Handle, msg *WSAMsg, flags uint32, bytesSent *uint32, overlapped *syscall.Overlapped, completionRoutine uintptr) (err error) [failretval==socket_error] = ws2_32.WSASendMsg
and then run "go generate" command, and it will create the code you need. Same for WSARecvMsg.
These functions do not exist on Windows XP for example, so you would have to keep LoadSendRecvMsg, but it could be just one line - see syscall.LoadGetAddrInfo for details.
File src/syscall/types_windows.go:
Patch Set #2, Line 600: struct
This is actually a pointer to SOCKADDR. I am not an expert in this area, so I wouldn't know. But maybe uintptr is what you want here. Maybe mikio knows.
Patch Set #2, Line 603: Buffers
I suggest
s/Bufferslen/BufferCount/
because it is "The number of buffers pointed to in the lpBuffers member."
To view, visit change 76392. To unsubscribe, or for help writing mail filters, visit settings.
Thanks for the review!
3 comments:
Patch Set #2, Line 7: syscall: add WSASendMsg/WSARecvMsg wrappers on Windows
You cannot change syscall package (grep "go doc syscall" for "This package is locked down" for detai […]
Oof. Does that mean I have to implement them in both go and x/net since both use them?
File src/syscall/syscall_windows.go:
Patch Set #2, Line 832: sendRecvMsgFunc.err = WSAIoctl(s,
Both WSASendMsg and WSARecvMsg live inside of ws2_32.dll. So you code could be simpler. You could just add this:
const socket_error = uintptr(^uint32(0))
//sys WSASendMsg(fd syscall.Handle, msg *WSAMsg, flags uint32, bytesSent *uint32, overlapped *syscall.Overlapped, completionRoutine uintptr) (err error) [failretval==socket_error] = ws2_32.WSASendMsg
and then run "go generate" command, and it will create the code you need. Same for WSARecvMsg.
These functions do not exist on Windows XP for example, so you would have to keep LoadSendRecvMsg, but it could be just one line - see syscall.LoadGetAddrInfo for details.
I copied the pattern used by the existing ConnectEx wrapper, because according to the documentation the WSASendMsg/WSARecvMsg function pointers must be loaded via WSAIoctl. See the Remarks section of https://msdn.microsoft.com/en-us/library/windows/desktop/ms741687%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396:
>The function pointer for the WSARecvMsg function must be obtained at run time by making a call to the WSAIoctl function with the SIO_GET_EXTENSION_FUNCTION_POINTER opcode specified. The input buffer passed to the WSAIoctl function must contain WSAID_WSARECVMSG, a globally unique identifier (GUID) whose value identifies the WSARecvMsg extension function. On success, the output returned by the WSAIoctl function contains a pointer to the WSARecvMsg function. The WSAID_WSARECVMSG GUID is defined in the Mswsock.h header file.
I have no idea why it's designed this way, or if WSAIoctl is required. I assumed it was required because ConnectEx uses it too.
File src/syscall/types_windows.go:
Patch Set #2, Line 603: Buffers
I suggest […]
I also considered using Iov and Iovlen to match the convention of the platforms. I'm fine changing it to BufferCount.
To view, visit change 76392. To unsubscribe, or for help writing mail filters, visit settings.
1 comment:
Patch Set #2, Line 7: syscall: add WSASendMsg/WSARecvMsg wrappers on Windows
Oof. […]
If by "implement them in both" includes copy/pasting, yes.
You can make x/net be the upstream and we can vendor it into go's src/internal/... directory or something probably. We've done similar things in the past, depending on what layer we need this at.
Otherwise, yes, copy/paste.
To view, visit change 76392. To unsubscribe, or for help writing mail filters, visit settings.
2 comments:
Patch Set #2, Line 7: syscall: add WSASendMsg/WSARecvMsg wrappers on Windows
If by "implement them in both" includes copy/pasting, yes.
Great, can do. Thanks for the clarification.
File src/syscall/syscall_windows.go:
Patch Set #2, Line 832: sendRecvMsgFunc.err = WSAIoctl(s,
I have no idea why it's designed this way, or if WSAIoctl is required. I assumed it was required because ConnectEx uses it too.
I found a good explanation here: https://stackoverflow.com/a/37356935/332798
In Vista and later, WSAIoctl is not required. I'll simplify this code accordingly. Thanks for the pointer!
To view, visit change 76392. To unsubscribe, or for help writing mail filters, visit settings.
2 comments:
Patch Set #2, Line 7: syscall: add WSASendMsg/WSARecvMsg wrappers on Windows
If by "implement them in both" includes copy/pasting, yes. […]
Yes, two copies. Normally we make changes in internal/syscall/windows, and then copy them into golang.org/x/sys/windows. But whatever you think is best, or whatever Brad suggests is fine with me too.
File src/syscall/syscall_windows.go:
Patch Set #2, Line 832: sendRecvMsgFunc.err = WSAIoctl(s,
> Both WSASendMsg and WSARecvMsg live inside of ws2_32.dll. So you code could be simpler. […]
It is fine to leave your code as is, if you don't to follow my advice. But I tested my code here, and it seems to work. And if you look at the bottom of https://msdn.microsoft.com/en-us/library/windows/desktop/ms741692(v=vs.85).aspx it says
DLL Ws2_32.dll
so I tried and it is there.
To view, visit change 76392. To unsubscribe, or for help writing mail filters, visit settings.
Aman Gupta abandoned this change.
To view, visit change 76392. To unsubscribe, or for help writing mail filters, visit settings.
1 comment:
Patch Set #2, Line 7: syscall: add WSASendMsg/WSARecvMsg wrappers on Windows
Yes, two copies. Normally we make changes in internal/syscall/windows, and then copy them into golang.org/x/sys/windows. But whatever you think is best, or whatever Brad suggests is fine with me too.
Okay so I was able to copy this into internal/syscall/windows and everything on the go side works.
Now I'm trying to make another copy in x/net. You said you generally copy into x/sys/windows, and I see a lot of infrastructure in there to lazy load system DLLs. However, I need this code in x/net and there's nothing I can re-use there.
Should I put it in x/sys instead, and then make x/net depend on x/sys?
Or should I copy the DLL loading code from x/sys into x/net?
To view, visit change 76392. To unsubscribe, or for help writing mail filters, visit settings.
1 comment:
Patch Set #2, Line 832: sendRecvMsgFunc.err = WSAIoctl(s,
It is fine to leave your code as is, if you don't to follow my advice. But I tested my code here, and it seems to work. And if you look at the bottom of https://msdn.microsoft.com/en-us/library/windows/desktop/ms741692(v=vs.85).aspx it says
DLL Ws2_32.dll
so I tried and it is there.
I tried this and it works for WSASendMsg
But it does not work for WSARecvMsg 😡
To view, visit change 76392. To unsubscribe, or for help writing mail filters, visit settings.
Abandoned
folded into CL76393 since the syscall package is frozen
Sounds good.
Alex
2 comments:
Patch Set #2, Line 7: syscall: add WSASendMsg/WSARecvMsg wrappers on Windows
Should I put it in x/sys instead, and then make x/net depend on
x/sys?
Yes, please. Put the Windows APIs and stucts and consts in golang.org/x/sys/windows. Make them exported. And use them in x/net.
Or should I copy the DLL loading code from x/sys into x/net?
No, please, don't. golang.org/x/sys/windows is just another place were we store syscalls (since syscall package is frozen).
File src/syscall/syscall_windows.go:
Patch Set #2, Line 832: sendRecvMsgFunc.err = WSAIoctl(s,
> It is fine to leave your code as is, if you don't to follow my advice. […]
I just tried WSARecvMsg and it is not in ws2_32.dll. Sorry. You would have to go to your original version.
To view, visit change 76392. To unsubscribe, or for help writing mail filters, visit settings.