Skip to content
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

Feature: support setSocketOptions on windows #192

Merged
merged 10 commits into from
Nov 26, 2022
2 changes: 1 addition & 1 deletion component/dialer/sockopt_others.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build !linux && !darwin
//go:build !linux && !darwin && !windows

package dialer

Expand Down
69 changes: 69 additions & 0 deletions component/dialer/sockopt_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package dialer

import (
"encoding/binary"
"net"
"syscall"
"unsafe"

"golang.org/x/sys/windows"
)

const (
IP_UNICAST_IF = 31
IPV6_UNICAST_IF = 31
)

func setSocketOptions(network, address string, c syscall.RawConn, opts *Options) (err error) {
if opts == nil || !isTCPSocket(network) && !isUDPSocket(network) {
return
}

var innerErr error
err = c.Control(func(fd uintptr) {
host, _, _ := net.SplitHostPort(address)
ip := net.ParseIP(host)
if ip != nil && !ip.IsGlobalUnicast() {
return
}

if opts.InterfaceIndex == 0 && opts.InterfaceName != "" {
if iface, err := net.InterfaceByName(opts.InterfaceName); err == nil {
opts.InterfaceIndex = iface.Index
}
}

if opts.InterfaceIndex != 0 {
switch network {
case "tcp4", "udp4":
innerErr = bindSocketToInterface4(windows.Handle(fd), uint32(opts.InterfaceIndex))
case "tcp6", "udp6":
innerErr = bindSocketToInterface6(windows.Handle(fd), uint32(opts.InterfaceIndex))
if network == "udp6" && ip == nil {
// the underlying IP net maybe IPv4 even if the 'network' param is 'udp6',
xjasonlyu marked this conversation as resolved.
Show resolved Hide resolved
// so we should bind socket to interface4 at the same time
xjasonlyu marked this conversation as resolved.
Show resolved Hide resolved
innerErr = bindSocketToInterface4(windows.Handle(fd), uint32(opts.InterfaceIndex))
}
}
}
})

if innerErr != nil {
err = innerErr
}
return
}

func bindSocketToInterface4(handle windows.Handle, interfaceIndex uint32) error {
xjasonlyu marked this conversation as resolved.
Show resolved Hide resolved
// For IPv4, this parameter must be an interface index in network byte order.
//
xjasonlyu marked this conversation as resolved.
Show resolved Hide resolved
// Ref: https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options
var bytes [4]byte
binary.BigEndian.PutUint32(bytes[:], interfaceIndex)
interfaceIndex = *(*uint32)(unsafe.Pointer(&bytes[0]))
xjasonlyu marked this conversation as resolved.
Show resolved Hide resolved
return windows.SetsockoptInt(handle, windows.IPPROTO_IP, IP_UNICAST_IF, int(interfaceIndex))
xjasonlyu marked this conversation as resolved.
Show resolved Hide resolved
}

func bindSocketToInterface6(handle windows.Handle, interfaceIndex uint32) error {
xjasonlyu marked this conversation as resolved.
Show resolved Hide resolved
return windows.SetsockoptInt(handle, windows.IPPROTO_IPV6, IPV6_UNICAST_IF, int(interfaceIndex))
}