1// +build linux
2
3package dns
4
5// See:
6// * http://stackoverflow.com/questions/3062205/setting-the-source-ip-for-a-udp-socket and
7// * http://blog.powerdns.com/2012/10/08/on-binding-datagram-udp-sockets-to-the-any-addresses/
8//
9// Why do we need this: When listening on 0.0.0.0 with UDP so kernel decides what is the outgoing
10// interface, this might not always be the correct one. This code will make sure the egress
11// packet's interface matched the ingress' one.
12
13import (
14	"net"
15	"syscall"
16)
17
18// setUDPSocketOptions4 prepares the v4 socket for sessions.
19func setUDPSocketOptions4(conn *net.UDPConn) error {
20	file, err := conn.File()
21	if err != nil {
22		return err
23	}
24	if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil {
25		return err
26	}
27	// Calling File() above results in the connection becoming blocking, we must fix that.
28	// See https://github.com/miekg/dns/issues/279
29	err = syscall.SetNonblock(int(file.Fd()), true)
30	if err != nil {
31		return err
32	}
33	return nil
34}
35
36// setUDPSocketOptions6 prepares the v6 socket for sessions.
37func setUDPSocketOptions6(conn *net.UDPConn) error {
38	file, err := conn.File()
39	if err != nil {
40		return err
41	}
42	if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil {
43		return err
44	}
45	err = syscall.SetNonblock(int(file.Fd()), true)
46	if err != nil {
47		return err
48	}
49	return nil
50}
51
52// getUDPSocketOption6Only return true if the socket is v6 only and false when it is v4/v6 combined
53// (dualstack).
54func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) {
55	file, err := conn.File()
56	if err != nil {
57		return false, err
58	}
59	// dual stack. See http://stackoverflow.com/questions/1618240/how-to-support-both-ipv4-and-ipv6-connections
60	v6only, err := syscall.GetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY)
61	if err != nil {
62		return false, err
63	}
64	return v6only == 1, nil
65}
66
67func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) {
68	file, err := conn.File()
69	if err != nil {
70		return nil, err
71	}
72	return syscall.Getsockname(int(file.Fd()))
73}
74