Skip to content

Commit e49bc46

Browse files
tmm1alexbrainman
authored andcommitted
net: implement ReadMsg/WriteMsg on windows
This means {Read,Write}Msg{UDP,IP} now work on windows. Fixes #9252 Change-Id: Ifb105f9ad18d61289b22d7358a95faabe73d2d02 Reviewed-on: https://go-review.googlesource.com/76393 TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
1 parent 5d0cab0 commit e49bc46

File tree

9 files changed

+231
-26
lines changed

9 files changed

+231
-26
lines changed

src/go/build/deps_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ var pkgDeps = map[string][]string{
154154
"syscall",
155155
},
156156

157-
"internal/poll": {"L0", "internal/race", "syscall", "time", "unicode/utf16", "unicode/utf8"},
157+
"internal/poll": {"L0", "internal/race", "syscall", "time", "unicode/utf16", "unicode/utf8", "internal/syscall/windows"},
158158
"os": {"L1", "os", "syscall", "time", "internal/poll", "internal/syscall/windows"},
159159
"path/filepath": {"L2", "os", "syscall", "internal/syscall/windows"},
160160
"io/ioutil": {"L2", "os", "path/filepath", "time"},

src/internal/poll/fd_windows.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package poll
77
import (
88
"errors"
99
"internal/race"
10+
"internal/syscall/windows"
1011
"io"
1112
"runtime"
1213
"sync"
@@ -92,6 +93,7 @@ type operation struct {
9293
fd *FD
9394
errc chan error
9495
buf syscall.WSABuf
96+
msg windows.WSAMsg
9597
sa syscall.Sockaddr
9698
rsa *syscall.RawSockaddrAny
9799
rsan int32
@@ -132,6 +134,22 @@ func (o *operation) ClearBufs() {
132134
o.bufs = o.bufs[:0]
133135
}
134136

137+
func (o *operation) InitMsg(p []byte, oob []byte) {
138+
o.InitBuf(p)
139+
o.msg.Buffers = &o.buf
140+
o.msg.BufferCount = 1
141+
142+
o.msg.Name = nil
143+
o.msg.Namelen = 0
144+
145+
o.msg.Flags = 0
146+
o.msg.Control.Len = uint32(len(oob))
147+
o.msg.Control.Buf = nil
148+
if len(oob) != 0 {
149+
o.msg.Control.Buf = &oob[0]
150+
}
151+
}
152+
135153
// ioSrv executes net IO requests.
136154
type ioSrv struct {
137155
req chan ioSrvReq
@@ -898,3 +916,77 @@ func (fd *FD) RawRead(f func(uintptr) bool) error {
898916
func (fd *FD) RawWrite(f func(uintptr) bool) error {
899917
return errors.New("not implemented")
900918
}
919+
920+
func sockaddrToRaw(sa syscall.Sockaddr) (unsafe.Pointer, int32, error) {
921+
switch sa := sa.(type) {
922+
case *syscall.SockaddrInet4:
923+
var raw syscall.RawSockaddrInet4
924+
raw.Family = syscall.AF_INET
925+
p := (*[2]byte)(unsafe.Pointer(&raw.Port))
926+
p[0] = byte(sa.Port >> 8)
927+
p[1] = byte(sa.Port)
928+
for i := 0; i < len(sa.Addr); i++ {
929+
raw.Addr[i] = sa.Addr[i]
930+
}
931+
return unsafe.Pointer(&raw), int32(unsafe.Sizeof(raw)), nil
932+
case *syscall.SockaddrInet6:
933+
var raw syscall.RawSockaddrInet6
934+
raw.Family = syscall.AF_INET6
935+
p := (*[2]byte)(unsafe.Pointer(&raw.Port))
936+
p[0] = byte(sa.Port >> 8)
937+
p[1] = byte(sa.Port)
938+
raw.Scope_id = sa.ZoneId
939+
for i := 0; i < len(sa.Addr); i++ {
940+
raw.Addr[i] = sa.Addr[i]
941+
}
942+
return unsafe.Pointer(&raw), int32(unsafe.Sizeof(raw)), nil
943+
default:
944+
return nil, 0, syscall.EWINDOWS
945+
}
946+
}
947+
948+
// ReadMsg wraps the WSARecvMsg network call.
949+
func (fd *FD) ReadMsg(p []byte, oob []byte) (int, int, int, syscall.Sockaddr, error) {
950+
if err := fd.readLock(); err != nil {
951+
return 0, 0, 0, nil, err
952+
}
953+
defer fd.readUnlock()
954+
955+
o := &fd.rop
956+
o.InitMsg(p, oob)
957+
o.rsa = new(syscall.RawSockaddrAny)
958+
o.msg.Name = o.rsa
959+
o.msg.Namelen = int32(unsafe.Sizeof(*o.rsa))
960+
n, err := rsrv.ExecIO(o, func(o *operation) error {
961+
return windows.WSARecvMsg(o.fd.Sysfd, &o.msg, &o.qty, &o.o, nil)
962+
})
963+
err = fd.eofError(n, err)
964+
var sa syscall.Sockaddr
965+
if err == nil {
966+
sa, err = o.rsa.Sockaddr()
967+
}
968+
return n, int(o.msg.Control.Len), int(o.msg.Flags), sa, err
969+
}
970+
971+
// WriteMsg wraps the WSASendMsg network call.
972+
func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, error) {
973+
if err := fd.writeLock(); err != nil {
974+
return 0, 0, err
975+
}
976+
defer fd.writeUnlock()
977+
978+
o := &fd.wop
979+
o.InitMsg(p, oob)
980+
if sa != nil {
981+
rsa, len, err := sockaddrToRaw(sa)
982+
if err != nil {
983+
return 0, 0, err
984+
}
985+
o.msg.Name = (*syscall.RawSockaddrAny)(rsa)
986+
o.msg.Namelen = len
987+
}
988+
n, err := wsrv.ExecIO(o, func(o *operation) error {
989+
return windows.WSASendMsg(o.fd.Sysfd, &o.msg, 0, &o.qty, &o.o, nil)
990+
})
991+
return n, int(o.msg.Control.Len), err
992+
}

src/internal/syscall/windows/syscall_windows.go

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
package windows
66

7-
import "syscall"
7+
import (
8+
"sync"
9+
"syscall"
10+
"unsafe"
11+
)
812

913
const (
1014
ERROR_SHARING_VIOLATION syscall.Errno = 32
@@ -115,10 +119,109 @@ const (
115119
const (
116120
WSA_FLAG_OVERLAPPED = 0x01
117121
WSA_FLAG_NO_HANDLE_INHERIT = 0x80
122+
123+
WSAEMSGSIZE syscall.Errno = 10040
124+
125+
MSG_TRUNC = 0x0100
126+
MSG_CTRUNC = 0x0200
127+
128+
socket_error = uintptr(^uint32(0))
118129
)
119130

131+
var WSAID_WSASENDMSG = syscall.GUID{
132+
Data1: 0xa441e712,
133+
Data2: 0x754f,
134+
Data3: 0x43ca,
135+
Data4: [8]byte{0x84, 0xa7, 0x0d, 0xee, 0x44, 0xcf, 0x60, 0x6d},
136+
}
137+
138+
var WSAID_WSARECVMSG = syscall.GUID{
139+
Data1: 0xf689d7c8,
140+
Data2: 0x6f1f,
141+
Data3: 0x436b,
142+
Data4: [8]byte{0x8a, 0x53, 0xe5, 0x4f, 0xe3, 0x51, 0xc3, 0x22},
143+
}
144+
145+
var sendRecvMsgFunc struct {
146+
once sync.Once
147+
sendAddr uintptr
148+
recvAddr uintptr
149+
err error
150+
}
151+
152+
type WSAMsg struct {
153+
Name *syscall.RawSockaddrAny
154+
Namelen int32
155+
Buffers *syscall.WSABuf
156+
BufferCount uint32
157+
Control syscall.WSABuf
158+
Flags uint32
159+
}
160+
120161
//sys WSASocket(af int32, typ int32, protocol int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = ws2_32.WSASocketW
121162

163+
func loadWSASendRecvMsg() error {
164+
sendRecvMsgFunc.once.Do(func() {
165+
var s syscall.Handle
166+
s, sendRecvMsgFunc.err = syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, syscall.IPPROTO_UDP)
167+
if sendRecvMsgFunc.err != nil {
168+
return
169+
}
170+
defer syscall.CloseHandle(s)
171+
var n uint32
172+
sendRecvMsgFunc.err = syscall.WSAIoctl(s,
173+
syscall.SIO_GET_EXTENSION_FUNCTION_POINTER,
174+
(*byte)(unsafe.Pointer(&WSAID_WSARECVMSG)),
175+
uint32(unsafe.Sizeof(WSAID_WSARECVMSG)),
176+
(*byte)(unsafe.Pointer(&sendRecvMsgFunc.recvAddr)),
177+
uint32(unsafe.Sizeof(sendRecvMsgFunc.recvAddr)),
178+
&n, nil, 0)
179+
if sendRecvMsgFunc.err != nil {
180+
return
181+
}
182+
sendRecvMsgFunc.err = syscall.WSAIoctl(s,
183+
syscall.SIO_GET_EXTENSION_FUNCTION_POINTER,
184+
(*byte)(unsafe.Pointer(&WSAID_WSASENDMSG)),
185+
uint32(unsafe.Sizeof(WSAID_WSASENDMSG)),
186+
(*byte)(unsafe.Pointer(&sendRecvMsgFunc.sendAddr)),
187+
uint32(unsafe.Sizeof(sendRecvMsgFunc.sendAddr)),
188+
&n, nil, 0)
189+
})
190+
return sendRecvMsgFunc.err
191+
}
192+
193+
func WSASendMsg(fd syscall.Handle, msg *WSAMsg, flags uint32, bytesSent *uint32, overlapped *syscall.Overlapped, croutine *byte) error {
194+
err := loadWSASendRecvMsg()
195+
if err != nil {
196+
return err
197+
}
198+
r1, _, e1 := syscall.Syscall6(sendRecvMsgFunc.sendAddr, 6, uintptr(fd), uintptr(unsafe.Pointer(msg)), uintptr(flags), uintptr(unsafe.Pointer(bytesSent)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)))
199+
if r1 == socket_error {
200+
if e1 != 0 {
201+
err = errnoErr(e1)
202+
} else {
203+
err = syscall.EINVAL
204+
}
205+
}
206+
return err
207+
}
208+
209+
func WSARecvMsg(fd syscall.Handle, msg *WSAMsg, bytesReceived *uint32, overlapped *syscall.Overlapped, croutine *byte) error {
210+
err := loadWSASendRecvMsg()
211+
if err != nil {
212+
return err
213+
}
214+
r1, _, e1 := syscall.Syscall6(sendRecvMsgFunc.recvAddr, 5, uintptr(fd), uintptr(unsafe.Pointer(msg)), uintptr(unsafe.Pointer(bytesReceived)), uintptr(unsafe.Pointer(overlapped)), uintptr(unsafe.Pointer(croutine)), 0)
215+
if r1 == socket_error {
216+
if e1 != 0 {
217+
err = errnoErr(e1)
218+
} else {
219+
err = syscall.EINVAL
220+
}
221+
}
222+
return err
223+
}
224+
122225
const (
123226
ComputerNameNetBIOS = 0
124227
ComputerNameDnsHostname = 1

src/net/fd_windows.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -223,17 +223,21 @@ func (fd *netFD) accept() (*netFD, error) {
223223
return netfd, nil
224224
}
225225

226+
func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
227+
n, oobn, flags, sa, err = fd.pfd.ReadMsg(p, oob)
228+
runtime.KeepAlive(fd)
229+
return n, oobn, flags, sa, wrapSyscallError("wsarecvmsg", err)
230+
}
231+
232+
func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
233+
n, oobn, err = fd.pfd.WriteMsg(p, oob, sa)
234+
runtime.KeepAlive(fd)
235+
return n, oobn, wrapSyscallError("wsasendmsg", err)
236+
}
237+
226238
// Unimplemented functions.
227239

228240
func (fd *netFD) dup() (*os.File, error) {
229241
// TODO: Implement this
230242
return nil, syscall.EWINDOWS
231243
}
232-
233-
func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
234-
return 0, 0, 0, nil, syscall.EWINDOWS
235-
}
236-
237-
func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
238-
return 0, 0, syscall.EWINDOWS
239-
}

src/net/iprawsock.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
// change the behavior of these methods; use Read or ReadMsgIP
2222
// instead.
2323

24-
// BUG(mikio): On NaCl, Plan 9 and Windows, the ReadMsgIP and
24+
// BUG(mikio): On NaCl and Plan 9, the ReadMsgIP and
2525
// WriteMsgIP methods of IPConn are not implemented.
2626

2727
// BUG(mikio): On Windows, the File method of IPConn is not

src/net/platform_test.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,18 @@ func testableListenArgs(network, address, client string) bool {
149149
return true
150150
}
151151

152-
var condFatalf = func() func(*testing.T, string, ...interface{}) {
153-
// A few APIs, File, Read/WriteMsg{UDP,IP}, are not
154-
// implemented yet on both Plan 9 and Windows.
152+
func condFatalf(t *testing.T, api string, format string, args ...interface{}) {
153+
// A few APIs like File and Read/WriteMsg{UDP,IP} are not
154+
// fully implemented yet on Plan 9 and Windows.
155155
switch runtime.GOOS {
156-
case "plan9", "windows":
157-
return (*testing.T).Logf
156+
case "windows":
157+
if api == "file" {
158+
t.Logf(format, args...)
159+
return
160+
}
161+
case "plan9":
162+
t.Logf(format, args...)
163+
return
158164
}
159-
return (*testing.T).Fatalf
160-
}()
165+
t.Fatalf(format, args...)
166+
}

src/net/protoconn_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func TestTCPListenerSpecificMethods(t *testing.T) {
5454
}
5555

5656
if f, err := ln.File(); err != nil {
57-
condFatalf(t, "%v", err)
57+
condFatalf(t, "file", "%v", err)
5858
} else {
5959
f.Close()
6060
}
@@ -139,14 +139,14 @@ func TestUDPConnSpecificMethods(t *testing.T) {
139139
t.Fatal(err)
140140
}
141141
if _, _, err := c.WriteMsgUDP(wb, nil, c.LocalAddr().(*UDPAddr)); err != nil {
142-
condFatalf(t, "%v", err)
142+
condFatalf(t, "udp", "%v", err)
143143
}
144144
if _, _, _, _, err := c.ReadMsgUDP(rb, nil); err != nil {
145-
condFatalf(t, "%v", err)
145+
condFatalf(t, "udp", "%v", err)
146146
}
147147

148148
if f, err := c.File(); err != nil {
149-
condFatalf(t, "%v", err)
149+
condFatalf(t, "file", "%v", err)
150150
} else {
151151
f.Close()
152152
}
@@ -184,7 +184,7 @@ func TestIPConnSpecificMethods(t *testing.T) {
184184
c.SetWriteBuffer(2048)
185185

186186
if f, err := c.File(); err != nil {
187-
condFatalf(t, "%v", err)
187+
condFatalf(t, "file", "%v", err)
188188
} else {
189189
f.Close()
190190
}

src/net/udpsock.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"syscall"
1010
)
1111

12-
// BUG(mikio): On NaCl, Plan 9 and Windows, the ReadMsgUDP and
12+
// BUG(mikio): On NaCl and Plan 9, the ReadMsgUDP and
1313
// WriteMsgUDP methods of UDPConn are not implemented.
1414

1515
// BUG(mikio): On Windows, the File method of UDPConn is not

src/net/udpsock_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ func testWriteToConn(t *testing.T, raddr string) {
161161
}
162162
_, _, err = c.(*UDPConn).WriteMsgUDP(b, nil, nil)
163163
switch runtime.GOOS {
164-
case "nacl", "windows": // see golang.org/issue/9252
164+
case "nacl": // see golang.org/issue/9252
165165
t.Skipf("not implemented yet on %s", runtime.GOOS)
166166
default:
167167
if err != nil {
@@ -204,7 +204,7 @@ func testWriteToPacketConn(t *testing.T, raddr string) {
204204
}
205205
_, _, err = c.(*UDPConn).WriteMsgUDP(b, nil, ra)
206206
switch runtime.GOOS {
207-
case "nacl", "windows": // see golang.org/issue/9252
207+
case "nacl": // see golang.org/issue/9252
208208
t.Skipf("not implemented yet on %s", runtime.GOOS)
209209
default:
210210
if err != nil {

0 commit comments

Comments
 (0)