diff --git a/udp.go b/udp.go index c8ea23a3..ef3280b8 100644 --- a/udp.go +++ b/udp.go @@ -18,70 +18,56 @@ func (session *UDPSession) RemoteAddr() net.Addr { return session.raddr } +// UDPConn wrap a net.UDPConn with dns.UDPConn struct type UDPConn struct { *net.UDPConn } -// Wrap a net.UDPConn with dns.UDPConn struct +// NewUDPConn return a new UDPConn. // Initialize the underlying net.UDPConn for supporting "sessions" // Sessions solve https://github.com/miekg/dns/issues/95 -func NewUDPConn(conn *net.UDPConn) (newconn *UDPConn, err error) { +func NewUDPConn(conn *net.UDPConn) (*UDPConn, error) { // this function is implemented on a per platform basis. See udp_*.go for more details - err = udpPatchSocket(conn) - - if err != nil { - return - } - - return &UDPConn{conn}, nil -} - -// Just like net.UDPConn.ReadFrom(), but returns a session object instead of net.UDPAddr -// (RemoteAddr() is available from the UDPSession object) -func (conn *UDPConn) ReadFromSessionUDP(b []byte) (n int, session *UDPSession, err error) { - oob := make([]byte, 40) - - n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob) - if err != nil { - return - } - - session = &UDPSession{raddr, oob[:oobn]} - - return -} - -// Just like net.UDPConn.WritetTo(), but uses a session object instead of net.Addr -func (conn *UDPConn) WriteToSessionUDP(b []byte, session *UDPSession) (n int, err error) { - n, _, err = conn.WriteMsgUDP(b, session.context, session.raddr) - return -} - -func udpPatchSocket(conn *net.UDPConn) (err error) { + conn := new(net.UDPConn) file, err := conn.File() if err != nil { return } sa, err := syscall.Getsockname(int(file.Fd())) - - ipv4, ipv6 := false, false switch sa.(type) { case *syscall.SockaddrInet6: - ipv6 = true - // dual stack. See http://stackoverflow.com/questions/1618240/how-to-support-both-ipv4-and-ipv6-connections v6only, err := syscall.GetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY) if err != nil { return err } + SetUDPSocketOptions6(conn) if v6only == 0 { - ipv4 = true + SetUDPSocketOptions4(conn) } case *syscall.SockaddrInet4: - ipv4 = true + SetUDPSocketOptions4(conn) + } + return &UDPConn{conn}, nil +} + +// ReadFromSessionUDP ... Just like net.UDPConn.ReadFrom(), but returns a session object instead of net.UDPAddr +// (RemoteAddr() is available from the UDPSession object) +func (conn *UDPConn) ReadFromSessionUDP(b []byte) (int, *UDPSession, error) { + oob := make([]byte, 40) + n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob) + if err != nil { + return } - return udpPatchSocketTypes(conn, ipv4, ipv6) + session := &UDPSession{raddr, oob[:oobn]} + return n, session, err +} + +// WriteToSessionUDP Just like net.UDPConn.WritetTo(), but uses a session object instead of net.Addr +func (conn *UDPConn) WriteToSessionUDP(b []byte, session *UDPSession) (int, error) { + n, _, err = conn.WriteMsgUDP(b, session.context, session.raddr) + return n, err } diff --git a/udp_linux.go b/udp_linux.go index 10769ab8..123d92aa 100644 --- a/udp_linux.go +++ b/udp_linux.go @@ -1,43 +1,42 @@ -// Copyright 2009 The Go Authors. All rights reserved. +// Copyright 2011 Miek Gieben. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Extensions of the original work are copyright (c) 2011 Miek Gieben // +build linux package dns +// See: +// * http://stackoverflow.com/questions/3062205/setting-the-source-ip-for-a-udp-socket and +// * http://blog.powerdns.com/2012/10/08/on-binding-datagram-udp-sockets-to-the-any-addresses/ +// +// Why do we need this: When listening on 0.0.0.0 with UDP so kernel decides what is the outoging +// interface, this might not always be the correct one. This code will make sure the egress +// packet's interface matched the ingress' one. + import ( "net" "syscall" ) -// Linux implementation for preparing the socket for sessions -// Based on http://stackoverflow.com/questions/3062205/setting-the-source-ip-for-a-udp-socket -// and http://blog.powerdns.com/2012/10/08/on-binding-datagram-udp-sockets-to-the-any-addresses/ -func udpPatchSocketTypes(conn *net.UDPConn, ipv4, ipv6 bool) (err error) { - file, err := conn.File() +// SetUDPSocketOptions4 prepares the v4 socket for sessions. +func SetUDPSocketOptions4(conn *net.UDPConn) error { + // We got the .File() in NewUDPConn, this this will work + file, _ := conn.File() + err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1) if err != nil { - return + return err + } + return nil +} + +// SetUDPSocketOptions6 prepares the v6 socket for sessions. +func SetUDPSocketOptions6(conn *net.UDPConn) error { + // We got the .File() in NewUDPConn, this this will work + file, _ := conn.File() + err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1) + if err != nil { + return err } - - if ipv4 { - // socket supports IPv4 - - err = syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1) - if err != nil { - return err - } - } - - if ipv6 { - // socket supports IPv6 - - err = syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1) - if err != nil { - return err - } - } - return nil } diff --git a/udp_other.go b/udp_other.go index 94d58480..b0bb6402 100644 --- a/udp_other.go +++ b/udp_other.go @@ -1,7 +1,6 @@ -// Copyright 2009 The Go Authors. All rights reserved. +// Copyright 2011 Miek Gieben. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Extensions of the original work are copyright (c) 2011 Miek Gieben // +build !linux @@ -11,9 +10,7 @@ import ( "net" ) -// Default implementation for preparing the socket for sessions -// This actually does nothing. See udp_linux.go for an example of how to implement this. -// Make sure you edit the comment on the top of this file accordingly when adding implementations -func udpPatchSocketTypes(conn *net.UDPConn, ipv4, ipv6 bool) (err error) { - return nil -} +// These actually do nothing. See udp_linux.go for an example of how to implement this. + +func SetUDPSocketOptions4(conn *net.UDPConn) error { return nil } +func SetUDPSocketOptions6(conn *net.UDPConn) error { return nil }