diff --git a/server.go b/server.go index e6772549..445ad14f 100644 --- a/server.go +++ b/server.go @@ -44,10 +44,10 @@ type response struct { tsigTimersOnly bool tsigRequestMAC string tsigSecret map[string]string // the tsig secrets - udp *UDPConn // i/o connection if UDP was used - udpSession *UDPSession - tcp *net.TCPConn // i/o connection if TCP was used - remoteAddr net.Addr // address of the client + udp *net.UDPConn // i/o connection if UDP was used + tcp *net.TCPConn // i/o connection if TCP was used + udpSession *sessionUDP // oob data to get egress interface right + remoteAddr net.Addr // address of the client } // ServeMux is an DNS request multiplexer. It matches the @@ -244,12 +244,10 @@ func (srv *Server) ListenAndServe() error { return e } - ll, e := NewUDPConn(l) - if e != nil { + if e := setUDPSocketOptions(l); e != nil { return e } - - return srv.serveUDP(ll) + return srv.serveUDP(l) } return &Error{err: "bad network"} } @@ -282,7 +280,7 @@ func (srv *Server) serveTCP(l *net.TCPListener) error { // serveUDP starts a UDP listener for the server. // Each request is handled in a seperate goroutine. -func (srv *Server) serveUDP(l *UDPConn) error { +func (srv *Server) serveUDP(l *net.UDPConn) error { defer l.Close() handler := srv.Handler if handler == nil { @@ -304,7 +302,7 @@ func (srv *Server) serveUDP(l *UDPConn) error { } // Serve a new connection. -func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *UDPConn, s *UDPSession, t *net.TCPConn) { +func (srv *Server) serve(a net.Addr, h Handler, m []byte, u *net.UDPConn, s *sessionUDP, t *net.TCPConn) { w := &response{tsigSecret: srv.TsigSecret, udp: u, tcp: t, remoteAddr: a, udpSession: s} q := 0 Redo: @@ -392,9 +390,9 @@ func (srv *Server) readTCP(conn *net.TCPConn, timeout time.Duration) ([]byte, er return m, nil } -func (srv *Server) readUDP(conn *UDPConn, timeout time.Duration) ([]byte, *UDPSession, error) { +func (srv *Server) readUDP(conn *net.UDPConn, timeout time.Duration) ([]byte, *sessionUDP, error) { m := make([]byte, srv.UDPSize) - n, s, e := conn.ReadFromSessionUDP(m) + n, s, e := readFromSessionUDP(conn, m) if e != nil || n == 0 { if e != nil { return nil, nil, e @@ -430,7 +428,7 @@ func (w *response) WriteMsg(m *Msg) (err error) { func (w *response) Write(m []byte) (int, error) { switch { case w.udp != nil: - n, err := w.udp.WriteToSessionUDP(m, w.udpSession) + n, err := writeToSessionUDP(w.udp, m, w.udpSession) return n, err case w.tcp != nil: lm := len(m) diff --git a/udp.go b/udp.go index 2d99cd33..2b524e00 100644 --- a/udp.go +++ b/udp.go @@ -9,28 +9,19 @@ import ( "syscall" ) -type UDPSession struct { +type sessionUDP struct { raddr *net.UDPAddr context []byte } -func (session *UDPSession) RemoteAddr() net.Addr { - return session.raddr -} +func (s *sessionUDP) RemoteAddr() net.Addr { return s.raddr } -// UDPConn wrap a net.UDPConn with dns.UDPConn struct -type UDPConn struct { - *net.UDPConn -} - -// 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) (*UDPConn, error) { - // this function is implemented on a per platform basis. See udp_*.go for more details +// setUDPSocketOptions sets the UDP socket options. +// This function is implemented on a per platform basis. See udp_*.go for more details +func setUDPSocketOptions(conn *net.UDPConn) error { file, err := conn.File() if err != nil { - return nil,err + return err } sa, err := syscall.Getsockname(int(file.Fd())) @@ -39,34 +30,34 @@ func NewUDPConn(conn *net.UDPConn) (*UDPConn, error) { // 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 nil, err + return err } - SetUDPSocketOptions6(conn) + setUDPSocketOptions6(conn) if v6only == 0 { - SetUDPSocketOptions4(conn) + setUDPSocketOptions4(conn) } case *syscall.SockaddrInet4: - SetUDPSocketOptions4(conn) + setUDPSocketOptions4(conn) } - return &UDPConn{conn}, nil + return nil } -// ReadFromSessionUDP ... Just like net.UDPConn.ReadFrom(), but returns a session object instead of net.UDPAddr +// 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) { +func readFromSessionUDP(conn *net.UDPConn, b []byte) (int, *sessionUDP, error) { oob := make([]byte, 40) n, oobn, _, raddr, err := conn.ReadMsgUDP(b, oob) if err != nil { return n, nil, err } - session := &UDPSession{raddr, oob[:oobn]} + session := &sessionUDP{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) { +// writeToSessionUDP Just like net.UDPConn.WritetTo(), but uses a session object instead of net.Addr +func writeToSessionUDP(conn *net.UDPConn, b []byte, session *sessionUDP) (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 123d92aa..83189aa6 100644 --- a/udp_linux.go +++ b/udp_linux.go @@ -10,7 +10,7 @@ package dns // * 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 +// Why do we need this: When listening on 0.0.0.0 with UDP so kernel decides what is the outgoing // interface, this might not always be the correct one. This code will make sure the egress // packet's interface matched the ingress' one. @@ -19,9 +19,9 @@ import ( "syscall" ) -// SetUDPSocketOptions4 prepares the v4 socket for sessions. -func SetUDPSocketOptions4(conn *net.UDPConn) error { - // We got the .File() in NewUDPConn, this this will work +// 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 { @@ -30,9 +30,9 @@ func SetUDPSocketOptions4(conn *net.UDPConn) error { 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 +// 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 { diff --git a/udp_other.go b/udp_other.go index b0bb6402..168c9add 100644 --- a/udp_other.go +++ b/udp_other.go @@ -12,5 +12,5 @@ import ( // 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 } +func setUDPSocketOptions4(conn *net.UDPConn) error { return nil } +func setUDPSocketOptions6(conn *net.UDPConn) error { return nil }