Skip to content

Commit

Permalink
Pull request: UDP OOB darwin
Browse files Browse the repository at this point in the history
Merge in DNS/dnsproxy from 2807-bind-addr to master

Updates AdguardTeam/AdGuardHome#2807.

Squashed commit of the following:

commit 3cfff15
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Aug 3 17:47:48 2021 +0300

    proxyutil: imp docs

commit 54fbad0
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Aug 3 16:59:31 2021 +0300

    proxyutil: fix comments

commit 722a382
Author: Eugene Burkov <e.burkov@adguard.com>
Date:   Tue Aug 3 16:41:05 2021 +0300

    proxyutil: rm oob writing on darwin
  • Loading branch information
EugeneOne1 committed Aug 3, 2021
1 parent 7b49e7c commit 1b7a9bf
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 57 deletions.
28 changes: 28 additions & 0 deletions proxyutil/oob_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//go:build darwin
// +build darwin

package proxyutil

import (
"net"

"golang.org/x/net/ipv6"
)

// udpMakeOOBWithSrc makes the OOB data with a specified source IP.
func udpMakeOOBWithSrc(ip net.IP) []byte {
if ip4 := ip.To4(); ip4 != nil {
// Do not set the IPv4 source address via OOB, because it can
// cause the address to become unspecified on darwin.
//
// See https://github.com/AdguardTeam/AdGuardHome/issues/2807.
//
// TODO(e.burkov): Develop a workaround to make it write OOB
// only when listening on an unspecified address.
return []byte{}
}

return (&ipv6.ControlMessage{
Src: ip,
}).Marshal()
}
24 changes: 24 additions & 0 deletions proxyutil/oob_others.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//go:build aix || dragonfly || linux || netbsd || openbsd || freebsd || solaris
// +build aix dragonfly linux netbsd openbsd freebsd solaris

package proxyutil

import (
"net"

"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)

// udpMakeOOBWithSrc makes the OOB data with a specified source IP.
func udpMakeOOBWithSrc(ip net.IP) []byte {
if ip4 := ip.To4(); ip4 != nil {
return (&ipv4.ControlMessage{
Src: ip,
}).Marshal()
}

return (&ipv6.ControlMessage{
Src: ip,
}).Marshal()
}
36 changes: 36 additions & 0 deletions proxyutil/udp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package proxyutil

import "net"

// UDPGetOOBSize returns maximum size of the received OOB data.
func UDPGetOOBSize() (oobSize int) {
return udpGetOOBSize()
}

// UDPSetOptions sets flag options on a UDP socket to be able to receive the
// necessary OOB data.
func UDPSetOptions(c *net.UDPConn) (err error) {
return udpSetOptions(c)
}

// UDPRead udpRead reads the message from c using buf receives payload of size
// udpOOBSize from the UDP socket. It returns the number of bytes copied into
// buf, the number of bytes copied with OOB and the source address of the
// message.
func UDPRead(
c *net.UDPConn,
buf []byte,
udpOOBSize int,
) (n int, localIP net.IP, remoteAddr *net.UDPAddr, err error) {
return udpRead(c, buf, udpOOBSize)
}

// UDPWrite writes the data to the remoteAddr using conn.
func UDPWrite(
data []byte,
conn *net.UDPConn,
remoteAddr *net.UDPAddr,
localIP net.IP,
) (n int, err error) {
return udpWrite(data, conn, remoteAddr, localIP)
}
100 changes: 53 additions & 47 deletions proxyutil/udp_unix.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// +build aix darwin dragonfly linux netbsd openbsd solaris freebsd
//go:build aix || dragonfly || linux || netbsd || openbsd || freebsd || solaris || darwin
// +build aix dragonfly linux netbsd openbsd freebsd solaris darwin

package proxyutil

Expand All @@ -10,52 +11,42 @@ import (
"golang.org/x/net/ipv6"
)

// UDPGetOOBSize - get max. size of received OOB data
// It will then be used in the ReadMsgUDP function
func UDPGetOOBSize() int {
oob4 := ipv4.NewControlMessage(ipv4.FlagDst | ipv4.FlagInterface)
oob6 := ipv6.NewControlMessage(ipv6.FlagDst | ipv6.FlagInterface)
// ipv*Flags is the set of socket option flags for configuring IPv* UDP
// connection to receive an appropriate OOB data. For both versions the flags
// are:
//
// FlagDst
// FlagInterface
//
const (
ipv4Flags ipv4.ControlFlags = ipv4.FlagDst | ipv4.FlagInterface
ipv6Flags ipv6.ControlFlags = ipv6.FlagDst | ipv6.FlagInterface
)

if len(oob4) > len(oob6) {
return len(oob4)
}
return len(oob6)
}
// udpGetOOBSize obtains the destination IP from OOB data.
func udpGetOOBSize() (oobSize int) {
l4, l6 :=
len(ipv4.NewControlMessage(ipv4Flags)),
len(ipv6.NewControlMessage(ipv6Flags))

// UDPSetOptions - set options on a UDP socket to be able to receive the necessary OOB data
func UDPSetOptions(c *net.UDPConn) error {
err6 := ipv6.NewPacketConn(c).SetControlMessage(ipv6.FlagDst|ipv6.FlagInterface, true)
err4 := ipv4.NewPacketConn(c).SetControlMessage(ipv4.FlagDst|ipv4.FlagInterface, true)
if err6 != nil && err4 != nil {
return fmt.Errorf("failed to call SetControlMessage: ipv4: %v ipv6: %v", err4, err6)
if l4 >= l6 {
return l4
}
return nil

return l6
}

// UDPRead - receive payload and OOB data from the UDP socket
func UDPRead(c *net.UDPConn, buf []byte, udpOOBSize int) (int, net.IP, *net.UDPAddr, error) {
var oobn int
oob := make([]byte, udpOOBSize)
var err error
var n int
var remoteAddr *net.UDPAddr
n, oobn, _, remoteAddr, err = c.ReadMsgUDP(buf, oob)
if err != nil {
return -1, nil, nil, err
func udpSetOptions(c *net.UDPConn) (err error) {
err6 := ipv6.NewPacketConn(c).SetControlMessage(ipv6Flags, true)
err4 := ipv4.NewPacketConn(c).SetControlMessage(ipv4Flags, true)
if err6 != nil && err4 != nil {
return fmt.Errorf("failed to call SetControlMessage: ipv4: %v; ipv6: %v", err4, err6)
}

localIP := udpGetDstFromOOB(oob[:oobn])
return n, localIP, remoteAddr, nil
}

// UDPWrite - writes to the UDP socket and sets local IP to OOB data
func UDPWrite(bytes []byte, conn *net.UDPConn, remoteAddr *net.UDPAddr, localIP net.IP) (int, error) {
n, _, err := conn.WriteMsgUDP(bytes, udpMakeOOBWithSrc(localIP), remoteAddr)
return n, err
return nil
}

// udpGetDstFromOOB - get destination IP from OOB data
func udpGetDstFromOOB(oob []byte) net.IP {
func udpGetDstFromOOB(oob []byte) (dst net.IP) {
cm6 := &ipv6.ControlMessage{}
if cm6.Parse(oob) == nil && cm6.Dst != nil {
return cm6.Dst
Expand All @@ -69,15 +60,30 @@ func udpGetDstFromOOB(oob []byte) net.IP {
return nil
}

// udpMakeOOBWithSrc - make OOB data with a specified source IP
func udpMakeOOBWithSrc(ip net.IP) []byte {
if ip.To4() == nil {
cm := &ipv6.ControlMessage{}
cm.Src = ip
return cm.Marshal()
func udpRead(
c *net.UDPConn,
buf []byte,
udpOOBSize int,
) (n int, localIP net.IP, remoteAddr *net.UDPAddr, err error) {
var oobn int
oob := make([]byte, udpOOBSize)
n, oobn, _, remoteAddr, err = c.ReadMsgUDP(buf, oob)
if err != nil {
return -1, nil, nil, err
}

cm := &ipv4.ControlMessage{}
cm.Src = ip
return cm.Marshal()
localIP = udpGetDstFromOOB(oob[:oobn])

return n, localIP, remoteAddr, nil
}

func udpWrite(
data []byte,
conn *net.UDPConn,
remoteAddr *net.UDPAddr,
localIP net.IP,
) (n int, err error) {
n, _, err = conn.WriteMsgUDP(data, udpMakeOOBWithSrc(localIP), remoteAddr)

return n, err
}
18 changes: 8 additions & 10 deletions proxyutil/udp_windows.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
//go:build windows
// +build windows

package proxyutil

import "net"

// UDPGetOOBSize - get max. size of received OOB data
// Does nothing on Windows
func UDPGetOOBSize() int {
func udpGetOOBSize() int {
return 0
}

// UDPSetOptions - set options on a UDP socket to be able to receive the necessary OOB data
// Does nothing on Windows
func UDPSetOptions(c *net.UDPConn) error {
func udpSetOptions(c *net.UDPConn) error {
return nil
}

// UDPRead - receive payload from the UDP socket
func UDPRead(c *net.UDPConn, buf []byte, _ int) (int, net.IP, *net.UDPAddr, error) {
func udpRead(c *net.UDPConn, buf []byte, _ int) (int, net.IP, *net.UDPAddr, error) {
n, addr, err := c.ReadFrom(buf)
var udpAddr *net.UDPAddr
if addr != nil {
udpAddr = addr.(*net.UDPAddr)
}

return n, nil, udpAddr, err
}

// UDPWrite - writes to the UDP socket
func UDPWrite(bytes []byte, conn *net.UDPConn, remoteAddr *net.UDPAddr, _ net.IP) (int, error) {
func udpWrite(bytes []byte, conn *net.UDPConn, remoteAddr *net.UDPAddr, _ net.IP) (int, error) {
return conn.WriteTo(bytes, remoteAddr)
}

0 comments on commit 1b7a9bf

Please sign in to comment.