From 513257c9a953c674f855af179915e6376a5f7f47 Mon Sep 17 00:00:00 2001 From: Aman Gupta Date: Mon, 6 Nov 2017 20:35:10 -0800 Subject: [PATCH] ipv4: implement control messages on windows Fixes golang/go#7175 Change-Id: I583c5713bedf073cf5abe25976275fc6b9193379 --- internal/socket/cmsghdr.go | 2 +- internal/socket/cmsghdr_stub.go | 2 +- internal/socket/cmsghdr_windows.go | 20 ++++++++++++ internal/socket/iovec_stub.go | 2 +- internal/socket/iovec_windows.go | 21 ++++++++++++ internal/socket/msghdr_stub.go | 2 +- internal/socket/msghdr_windows.go | 51 ++++++++++++++++++++++++++++++ internal/socket/sys_windows.go | 10 ++++-- ipv4/control_windows.go | 50 ++++++++++++++++++++++++++--- ipv4/packet.go | 3 -- ipv4/payload.go | 3 -- ipv4/payload_cmsg.go | 2 +- ipv4/payload_cmsg_go1_9.go | 2 +- ipv4/payload_nocmsg.go | 2 +- ipv4/sockopt.go | 2 +- ipv4/sys_windows.go | 32 ++++++++++++++++++- 16 files changed, 185 insertions(+), 21 deletions(-) create mode 100644 internal/socket/cmsghdr_windows.go create mode 100644 internal/socket/iovec_windows.go create mode 100644 internal/socket/msghdr_windows.go diff --git a/internal/socket/cmsghdr.go b/internal/socket/cmsghdr.go index 1eb07d26de..c64f61a491 100644 --- a/internal/socket/cmsghdr.go +++ b/internal/socket/cmsghdr.go @@ -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 diff --git a/internal/socket/cmsghdr_stub.go b/internal/socket/cmsghdr_stub.go index a4e71226f8..6ca4a5e4cf 100644 --- a/internal/socket/cmsghdr_stub.go +++ b/internal/socket/cmsghdr_stub.go @@ -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 diff --git a/internal/socket/cmsghdr_windows.go b/internal/socket/cmsghdr_windows.go new file mode 100644 index 0000000000..fc3356d83a --- /dev/null +++ b/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) +} diff --git a/internal/socket/iovec_stub.go b/internal/socket/iovec_stub.go index c87d2a9339..fc2bb0f2b5 100644 --- a/internal/socket/iovec_stub.go +++ b/internal/socket/iovec_stub.go @@ -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 diff --git a/internal/socket/iovec_windows.go b/internal/socket/iovec_windows.go new file mode 100644 index 0000000000..de4ab5e44d --- /dev/null +++ b/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) +} diff --git a/internal/socket/msghdr_stub.go b/internal/socket/msghdr_stub.go index 64e8173352..48eb2310f2 100644 --- a/internal/socket/msghdr_stub.go +++ b/internal/socket/msghdr_stub.go @@ -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 diff --git a/internal/socket/msghdr_windows.go b/internal/socket/msghdr_windows.go new file mode 100644 index 0000000000..1131b52456 --- /dev/null +++ b/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) +} diff --git a/internal/socket/sys_windows.go b/internal/socket/sys_windows.go index 54a470ebe3..a9131074bc 100644 --- a/internal/socket/sys_windows.go +++ b/internal/socket/sys_windows.go @@ -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) { diff --git a/ipv4/control_windows.go b/ipv4/control_windows.go index ce55c66447..1f24de4be5 100644 --- a/ipv4/control_windows.go +++ b/ipv4/control_windows.go @@ -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 } diff --git a/ipv4/packet.go b/ipv4/packet.go index f00f5b052f..a90b7fb8a5 100644 --- a/ipv4/packet.go +++ b/ipv4/packet.go @@ -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 diff --git a/ipv4/payload.go b/ipv4/payload.go index f95f811acd..c5d8b7b9b9 100644 --- a/ipv4/payload.go +++ b/ipv4/payload.go @@ -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 diff --git a/ipv4/payload_cmsg.go b/ipv4/payload_cmsg.go index 3f06d76063..1347a1f464 100644 --- a/ipv4/payload_cmsg.go +++ b/ipv4/payload_cmsg.go @@ -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 diff --git a/ipv4/payload_cmsg_go1_9.go b/ipv4/payload_cmsg_go1_9.go index 2f19311839..a45a094246 100644 --- a/ipv4/payload_cmsg_go1_9.go +++ b/ipv4/payload_cmsg_go1_9.go @@ -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 diff --git a/ipv4/payload_nocmsg.go b/ipv4/payload_nocmsg.go index 3926de70b8..e3789ba378 100644 --- a/ipv4/payload_nocmsg.go +++ b/ipv4/payload_nocmsg.go @@ -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 diff --git a/ipv4/sockopt.go b/ipv4/sockopt.go index 22e90c0392..cdb74653b4 100644 --- a/ipv4/sockopt.go +++ b/ipv4/sockopt.go @@ -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 diff --git a/ipv4/sys_windows.go b/ipv4/sys_windows.go index b0913d539c..ced3657b2d 100644 --- a/ipv4/sys_windows.go +++ b/ipv4/sys_windows.go @@ -5,6 +5,9 @@ package ipv4 import ( + "net" + "unsafe" + "golang.org/x/net/internal/iana" "golang.org/x/net/internal/socket" ) @@ -48,7 +51,9 @@ type ipMreqSource struct { // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms738586(v=vs.85).aspx var ( - ctlOpts = [ctlMax]ctlOpt{} + ctlOpts = [ctlMax]ctlOpt{ + ctlPacketInfo: {sysIP_PKTINFO, sizeofInetPktinfo, marshalPacketInfo, parsePacketInfo}, + } sockOpts = map[int]*sockOpt{ ssoTOS: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_TOS, Len: 4}}, @@ -59,9 +64,34 @@ var ( ssoHeaderPrepend: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_HDRINCL, Len: 4}}, ssoJoinGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_ADD_MEMBERSHIP, Len: sizeofIPMreq}, typ: ssoTypeIPMreq}, ssoLeaveGroup: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_DROP_MEMBERSHIP, Len: sizeofIPMreq}, typ: ssoTypeIPMreq}, + ssoPacketInfo: {Option: socket.Option{Level: iana.ProtocolIP, Name: sysIP_PKTINFO, Len: 4}}, } ) func (pi *inetPktinfo) setIfindex(i int) { pi.Ifindex = int32(i) } + +func marshalPacketInfo(b []byte, cm *ControlMessage) []byte { + m := socket.ControlMessage(b) + m.MarshalHeader(iana.ProtocolIP, sysIP_PKTINFO, sizeofInetPktinfo) + if cm != nil { + pi := (*inetPktinfo)(unsafe.Pointer(&m.Data(sizeofInetPktinfo)[0])) + if ip := cm.Src.To4(); ip != nil { + copy(pi.Addr[:], ip) + } + if cm.IfIndex > 0 { + pi.setIfindex(cm.IfIndex) + } + } + return m.Next(sizeofInetPktinfo) +} + +func parsePacketInfo(cm *ControlMessage, b []byte) { + pi := (*inetPktinfo)(unsafe.Pointer(&b[0])) + cm.IfIndex = int(pi.Ifindex) + if len(cm.Dst) < net.IPv4len { + cm.Dst = make(net.IP, net.IPv4len) + } + copy(cm.Dst, pi.Addr[:]) +}