udp_linux.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. // +build linux,!appengine
  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. // setUDPSocketOptions sets the UDP socket options.
  15. // This function is implemented on a per platform basis. See udp_*.go for more details
  16. func setUDPSocketOptions(conn *net.UDPConn) error {
  17. sa, err := getUDPSocketName(conn)
  18. if err != nil {
  19. return err
  20. }
  21. switch sa.(type) {
  22. case *syscall.SockaddrInet6:
  23. v6only, err := getUDPSocketOptions6Only(conn)
  24. if err != nil {
  25. return err
  26. }
  27. setUDPSocketOptions6(conn)
  28. if !v6only {
  29. setUDPSocketOptions4(conn)
  30. }
  31. case *syscall.SockaddrInet4:
  32. setUDPSocketOptions4(conn)
  33. }
  34. return nil
  35. }
  36. // setUDPSocketOptions4 prepares the v4 socket for sessions.
  37. func setUDPSocketOptions4(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_IP, syscall.IP_PKTINFO, 1); err != nil {
  43. file.Close()
  44. return err
  45. }
  46. // Calling File() above results in the connection becoming blocking, we must fix that.
  47. // See https://github.com/miekg/dns/issues/279
  48. err = syscall.SetNonblock(int(file.Fd()), true)
  49. if err != nil {
  50. file.Close()
  51. return err
  52. }
  53. file.Close()
  54. return nil
  55. }
  56. // setUDPSocketOptions6 prepares the v6 socket for sessions.
  57. func setUDPSocketOptions6(conn *net.UDPConn) error {
  58. file, err := conn.File()
  59. if err != nil {
  60. return err
  61. }
  62. if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil {
  63. file.Close()
  64. return err
  65. }
  66. err = syscall.SetNonblock(int(file.Fd()), true)
  67. if err != nil {
  68. file.Close()
  69. return err
  70. }
  71. file.Close()
  72. return nil
  73. }
  74. // getUDPSocketOption6Only return true if the socket is v6 only and false when it is v4/v6 combined
  75. // (dualstack).
  76. func getUDPSocketOptions6Only(conn *net.UDPConn) (bool, error) {
  77. file, err := conn.File()
  78. if err != nil {
  79. return false, err
  80. }
  81. // dual stack. See http://stackoverflow.com/questions/1618240/how-to-support-both-ipv4-and-ipv6-connections
  82. v6only, err := syscall.GetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY)
  83. if err != nil {
  84. file.Close()
  85. return false, err
  86. }
  87. file.Close()
  88. return v6only == 1, nil
  89. }
  90. func getUDPSocketName(conn *net.UDPConn) (syscall.Sockaddr, error) {
  91. file, err := conn.File()
  92. if err != nil {
  93. return nil, err
  94. }
  95. defer file.Close()
  96. return syscall.Getsockname(int(file.Fd()))
  97. }