Skip to content

Commit

Permalink
ipv4: implement control messages on windows
Browse files Browse the repository at this point in the history
Fixes golang/go#7175

Change-Id: I583c5713bedf073cf5abe25976275fc6b9193379
  • Loading branch information
tmm1 committed Nov 7, 2017
1 parent a337091 commit e5b81d9
Show file tree
Hide file tree
Showing 22 changed files with 214 additions and 37 deletions.
2 changes: 1 addition & 1 deletion internal/socket/cmsghdr.go
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build darwin dragonfly freebsd linux netbsd openbsd solaris
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows

package socket

Expand Down
2 changes: 1 addition & 1 deletion internal/socket/cmsghdr_stub.go
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows

package socket

Expand Down
20 changes: 20 additions & 0 deletions internal/socket/cmsghdr_windows.go
@@ -0,0 +1,20 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package socket

// WSACMSGHDR
type cmsghdr struct {
Len uintptr
Level int32
Type int32
}

const sizeofCmsghdr = 0xc

func (h *cmsghdr) set(l, lvl, typ int) {
h.Len = uintptr(l)
h.Level = int32(lvl)
h.Type = int32(typ)
}
2 changes: 1 addition & 1 deletion internal/socket/iovec_stub.go
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows

package socket

Expand Down
21 changes: 21 additions & 0 deletions internal/socket/iovec_windows.go
@@ -0,0 +1,21 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package socket

import (
"syscall"
"unsafe"
)

type iovec syscall.WSABuf

func (v *iovec) set(b []byte) {
l := len(b)
if l == 0 {
return
}
v.Buf = (*byte)(unsafe.Pointer(&b[0]))
v.Len = uint32(l)
}
2 changes: 1 addition & 1 deletion internal/socket/msghdr_stub.go
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris,!windows

package socket

Expand Down
51 changes: 51 additions & 0 deletions internal/socket/msghdr_windows.go
@@ -0,0 +1,51 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package socket

import (
"syscall"
"unsafe"
)

type msghdr syscall.WSAMsg

func (h *msghdr) pack(vs []iovec, bs [][]byte, oob []byte, sa []byte) {
for i := range vs {
vs[i].set(bs[i])
}
h.setIov(vs)
if len(oob) > 0 {
h.Control.Buf = (*byte)(unsafe.Pointer(&oob[0]))
h.Control.Len = uint32(len(oob))
}
if sa != nil {
h.Name = uintptr(unsafe.Pointer(&sa[0]))
h.Namelen = int32(len(sa))
}
}

func (h *msghdr) name() []byte {
if h.Name != 0 && h.Namelen > 0 {
return (*[sizeofSockaddrInet6]byte)(unsafe.Pointer(h.Name))[:h.Namelen]
}
return nil
}

func (h *msghdr) controllen() int {
return int(h.Control.Len)
}

func (h *msghdr) flags() int {
return int(h.Flags)
}

func (h *msghdr) setIov(vs []iovec) {
l := len(vs)
if l == 0 {
return
}
h.Buffers = (*syscall.WSABuf)(unsafe.Pointer(&vs[0]))
h.Bufferslen = uint32(l)
}
10 changes: 8 additions & 2 deletions internal/socket/sys_windows.go
Expand Up @@ -54,11 +54,17 @@ func setsockopt(s uintptr, level, name int, b []byte) error {
}

func recvmsg(s uintptr, h *msghdr, flags int) (int, error) {
return 0, errors.New("not implemented")
var bytesReceived uint32
msg := (*syscall.WSAMsg)(h)
msg.Flags = uint32(flags)
err := syscall.WSARecvMsg(syscall.Handle(s), msg, &bytesReceived, nil, 0)
return int(bytesReceived), err
}

func sendmsg(s uintptr, h *msghdr, flags int) (int, error) {
return 0, errors.New("not implemented")
var bytesSent uint32
err := syscall.WSASendMsg(syscall.Handle(s), (*syscall.WSAMsg)(h), uint32(flags), &bytesSent, nil, 0)
return int(bytesSent), err
}

func recvmmsg(s uintptr, hs []mmsghdr, flags int) (int, error) {
Expand Down
50 changes: 46 additions & 4 deletions ipv4/control_windows.go
Expand Up @@ -5,12 +5,54 @@
package ipv4

import (
"syscall"

"golang.org/x/net/internal/socket"
)

func setControlMessage(c *socket.Conn, opt *rawOpt, cf ControlFlags, on bool) error {
// TODO(mikio): implement this
return syscall.EWINDOWS
opt.Lock()
defer opt.Unlock()
if so, ok := sockOpts[ssoReceiveTTL]; ok && cf&FlagTTL != 0 {
if err := so.SetInt(c, boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagTTL)
} else {
opt.clear(FlagTTL)
}
}
if so, ok := sockOpts[ssoPacketInfo]; ok {
if cf&(FlagSrc|FlagDst|FlagInterface) != 0 {
if err := so.SetInt(c, boolint(on)); err != nil {
return err
}
if on {
opt.set(cf & (FlagSrc | FlagDst | FlagInterface))
} else {
opt.clear(cf & (FlagSrc | FlagDst | FlagInterface))
}
}
} else {
if so, ok := sockOpts[ssoReceiveDst]; ok && cf&FlagDst != 0 {
if err := so.SetInt(c, boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagDst)
} else {
opt.clear(FlagDst)
}
}
if so, ok := sockOpts[ssoReceiveInterface]; ok && cf&FlagInterface != 0 {
if err := so.SetInt(c, boolint(on)); err != nil {
return err
}
if on {
opt.set(FlagInterface)
} else {
opt.clear(FlagInterface)
}
}
}
return nil
}
11 changes: 9 additions & 2 deletions ipv4/multicast_test.go
Expand Up @@ -29,7 +29,7 @@ var packetConnReadWriteMulticastUDPTests = []struct {

func TestPacketConnReadWriteMulticastUDP(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "solaris", "windows":
case "nacl", "plan9", "solaris":
t.Skipf("not supported on %s", runtime.GOOS)
}
ifi := nettest.RoutedInterface("ip4", net.FlagUp|net.FlagMulticast|net.FlagLoopback)
Expand Down Expand Up @@ -117,7 +117,9 @@ var packetConnReadWriteMulticastICMPTests = []struct {

func TestPacketConnReadWriteMulticastICMP(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "solaris", "windows":
case "windows":
t.Skipf("test hangs on windows when run as Administrator")
case "nacl", "plan9", "solaris":
t.Skipf("not supported on %s", runtime.GOOS)
}
if m, ok := nettest.SupportsRawIPSocket(); !ok {
Expand Down Expand Up @@ -227,6 +229,11 @@ var rawConnReadWriteMulticastICMPTests = []struct {
}

func TestRawConnReadWriteMulticastICMP(t *testing.T) {
switch runtime.GOOS {
case "windows":
t.Skipf("test hangs on windows when run as Administrator")
}

if testing.Short() {
t.Skip("to avoid external network")
}
Expand Down
10 changes: 5 additions & 5 deletions ipv4/multicastlistener_test.go
Expand Up @@ -21,7 +21,7 @@ var udpMultipleGroupListenerTests = []net.Addr{

func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if testing.Short() {
Expand Down Expand Up @@ -61,7 +61,7 @@ func TestUDPSinglePacketConnWithMultipleGroupListeners(t *testing.T) {

func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if testing.Short() {
Expand Down Expand Up @@ -116,7 +116,7 @@ func TestUDPMultiplePacketConnWithMultipleGroupListeners(t *testing.T) {

func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if testing.Short() {
Expand Down Expand Up @@ -172,7 +172,7 @@ func TestUDPPerInterfaceSinglePacketConnWithSingleGroupListener(t *testing.T) {

func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if testing.Short() {
Expand Down Expand Up @@ -217,7 +217,7 @@ func TestIPSingleRawConnWithSingleGroupListener(t *testing.T) {

func TestIPPerInterfaceSingleRawConnWithSingleGroupListener(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}
if testing.Short() {
Expand Down
3 changes: 0 additions & 3 deletions ipv4/packet.go
Expand Up @@ -11,9 +11,6 @@ import (
"golang.org/x/net/internal/socket"
)

// BUG(mikio): On Windows, the ReadFrom and WriteTo methods of RawConn
// are not implemented.

// A packetHandler represents the IPv4 datagram handler.
type packetHandler struct {
*net.IPConn
Expand Down
3 changes: 0 additions & 3 deletions ipv4/payload.go
Expand Up @@ -10,9 +10,6 @@ import (
"golang.org/x/net/internal/socket"
)

// BUG(mikio): On Windows, the ControlMessage for ReadFrom and WriteTo
// methods of PacketConn is not implemented.

// A payloadHandler represents the IPv4 datagram payload handler.
type payloadHandler struct {
net.PacketConn
Expand Down
2 changes: 1 addition & 1 deletion ipv4/payload_cmsg.go
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build !nacl,!plan9,!windows
// +build !nacl,!plan9

package ipv4

Expand Down
2 changes: 1 addition & 1 deletion ipv4/payload_cmsg_go1_9.go
Expand Up @@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.

// +build go1.9
// +build !nacl,!plan9,!windows
// +build !nacl,!plan9

package ipv4

Expand Down
2 changes: 1 addition & 1 deletion ipv4/payload_nocmsg.go
Expand Up @@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build nacl plan9 windows
// +build nacl plan9

package ipv4

Expand Down
6 changes: 4 additions & 2 deletions ipv4/readwrite_go1_9_test.go
Expand Up @@ -22,7 +22,7 @@ import (

func BenchmarkPacketConnReadWriteUnicast(b *testing.B) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "nacl", "plan9":
b.Skipf("not supported on %s", runtime.GOOS)
}

Expand Down Expand Up @@ -172,7 +172,9 @@ func BenchmarkPacketConnReadWriteUnicast(b *testing.B) {

func TestPacketConnConcurrentReadWriteUnicast(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "windows":
t.Skipf("recvmsg failures on windows with concurrent access")
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}

Expand Down
4 changes: 3 additions & 1 deletion ipv4/readwrite_test.go
Expand Up @@ -61,7 +61,9 @@ func BenchmarkReadWriteUnicast(b *testing.B) {

func TestPacketConnConcurrentReadWriteUnicastUDP(t *testing.T) {
switch runtime.GOOS {
case "nacl", "plan9", "windows":
case "windows":
t.Skipf("recvmsg failures on windows with concurrent access")
case "nacl", "plan9":
t.Skipf("not supported on %s", runtime.GOOS)
}

Expand Down
2 changes: 1 addition & 1 deletion ipv4/sockopt.go
Expand Up @@ -16,7 +16,7 @@ const (
ssoReceiveTTL // header field on received packet
ssoReceiveDst // header field on received packet
ssoReceiveInterface // inbound interface on received packet
ssoPacketInfo // incbound or outbound packet path
ssoPacketInfo // inbound or outbound packet path
ssoHeaderPrepend // ipv4 header prepend
ssoStripHeader // strip ipv4 header
ssoICMPFilter // icmp filter
Expand Down

0 comments on commit e5b81d9

Please sign in to comment.