udp_linux.go 2.1 KB

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