diff options
Diffstat (limited to 'server/server.go')
-rw-r--r-- | server/server.go | 467 |
1 files changed, 0 insertions, 467 deletions
diff --git a/server/server.go b/server/server.go deleted file mode 100644 index b0f89468e..000000000 --- a/server/server.go +++ /dev/null @@ -1,467 +0,0 @@ -// Package server implements a configurable, general-purpose web server. -// It relies on configurations obtained from the adjacent config package -// and can execute middleware as defined by the adjacent middleware package. -package server - -import ( - "crypto/tls" - "crypto/x509" - "fmt" - "io/ioutil" - "log" - "net" - "os" - "runtime" - "sync" - "time" - - "github.com/miekg/coredns/middleware" - "github.com/miekg/coredns/middleware/metrics" - - "github.com/miekg/dns" - "golang.org/x/net/context" -) - -// Server represents an instance of a server, which serves -// DNS requests at a particular address (host and port). A -// server is capable of serving numerous zones on -// the same address and the listener may be stopped for -// graceful termination (POSIX only). -type Server struct { - Addr string // Address we listen on - mux *dns.ServeMux - server [2]*dns.Server // by convention 0 is tcp and 1 is udp - listenerMu sync.Mutex // protects listener and packetconn inside server - - tls bool // whether this server is serving all HTTPS hosts or not - TLSConfig *tls.Config - OnDemandTLS bool // whether this server supports on-demand TLS (load certs at handshake-time) - zones map[string]zone // zones keyed by their address - dnsWg sync.WaitGroup // used to wait on outstanding connections - startChan chan struct{} // used to block until server is finished starting - connTimeout time.Duration // the maximum duration of a graceful shutdown - ReqCallback OptionalCallback // if non-nil, is executed at the beginning of every request - SNICallback func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) -} - -// OptionalCallback is a function that may or may not handle a request. -// It returns whether or not it handled the request. If it handled the -// request, it is presumed that no further request handling should occur. -type OptionalCallback func(dns.ResponseWriter, *dns.Msg) bool - -// New creates a new Server which will bind to addr and serve -// the sites/hosts configured in configs. Its listener will -// gracefully close when the server is stopped which will take -// no longer than gracefulTimeout. -// -// This function does not start serving. -// -// Do not re-use a server (start, stop, then start again). We -// could probably add more locking to make this possible, but -// as it stands, you should dispose of a server after stopping it. -// The behavior of serving with a spent server is undefined. -func New(addr string, configs []Config, gracefulTimeout time.Duration) (*Server, error) { - var useTLS, useOnDemandTLS bool - if len(configs) > 0 { - useTLS = configs[0].TLS.Enabled - useOnDemandTLS = configs[0].TLS.OnDemand - } - - s := &Server{ - Addr: addr, - TLSConfig: new(tls.Config), - // TODO: Make these values configurable? - // ReadTimeout: 2 * time.Minute, - // WriteTimeout: 2 * time.Minute, - // MaxHeaderBytes: 1 << 16, - tls: useTLS, - OnDemandTLS: useOnDemandTLS, - zones: make(map[string]zone), - startChan: make(chan struct{}), - connTimeout: gracefulTimeout, - } - mux := dns.NewServeMux() - mux.Handle(".", s) // wildcard handler, everything will go through here - s.mux = mux - - // We have to bound our wg with one increment - // to prevent a "race condition" that is hard-coded - // into sync.WaitGroup.Wait() - basically, an add - // with a positive delta must be guaranteed to - // occur before Wait() is called on the wg. - // In a way, this kind of acts as a safety barrier. - s.dnsWg.Add(1) - - // Set up each zone - for _, conf := range configs { - if _, exists := s.zones[conf.Host]; exists { - return nil, fmt.Errorf("cannot serve %s - host already defined for address %s", conf.Address(), s.Addr) - } - - z := zone{config: conf} - - // Build middleware stack - err := z.buildStack() - if err != nil { - return nil, err - } - - s.zones[conf.Host] = z - } - - return s, nil -} - -// Serve starts the server with an existing listener. It blocks until the server stops. -func (s *Server) Serve(ln net.Listener, pc net.PacketConn) error { - err := s.setup() - if err != nil { - close(s.startChan) // MUST defer so error is properly reported, same with all cases in this file - return err - } - s.listenerMu.Lock() - s.server[0] = &dns.Server{Listener: ln, Net: "tcp", Handler: s.mux} - s.server[1] = &dns.Server{PacketConn: pc, Net: "udp", Handler: s.mux} - s.listenerMu.Unlock() - - go func() { - s.server[0].ActivateAndServe() - }() - close(s.startChan) - return s.server[1].ActivateAndServe() -} - -// ListenAndServe starts the server with a new listener. It blocks until the server stops. -func (s *Server) ListenAndServe() error { - err := s.setup() - // defer close(s.startChan) // Don't understand why defer wouldn't actually work in this method (prolly cause the last ActivateAndServe does not actually return? - if err != nil { - close(s.startChan) - return err - } - - l, err := net.Listen("tcp", s.Addr) - if err != nil { - close(s.startChan) - return err - } - pc, err := net.ListenPacket("udp", s.Addr) - if err != nil { - close(s.startChan) - return err - } - - s.listenerMu.Lock() - s.server[0] = &dns.Server{Listener: l, Net: "tcp", Handler: s.mux} - s.server[1] = &dns.Server{PacketConn: pc, Net: "udp", Handler: s.mux} - s.listenerMu.Unlock() - - go func() { - s.server[0].ActivateAndServe() - }() - close(s.startChan) - return s.server[1].ActivateAndServe() -} - -// setup prepares the server s to begin listening; it should be -// called just before the listener announces itself on the network -// and should only be called when the server is just starting up. -func (s *Server) setup() error { - // Execute startup functions now - for _, z := range s.zones { - for _, startupFunc := range z.config.Startup { - err := startupFunc() - if err != nil { - return err - } - } - } - - return nil -} - -/* -TODO(miek): no such thing in the glorious Go DNS. -// serveTLS serves TLS with SNI and client auth support if s has them enabled. It -// blocks until s quits. -func serveTLS(s *Server, ln net.Listener, tlsConfigs []TLSConfig) error { - // Customize our TLS configuration - s.TLSConfig.MinVersion = tlsConfigs[0].ProtocolMinVersion - s.TLSConfig.MaxVersion = tlsConfigs[0].ProtocolMaxVersion - s.TLSConfig.CipherSuites = tlsConfigs[0].Ciphers - s.TLSConfig.PreferServerCipherSuites = tlsConfigs[0].PreferServerCipherSuites - - // TLS client authentication, if user enabled it - err := setupClientAuth(tlsConfigs, s.TLSConfig) - if err != nil { - defer close(s.startChan) - return err - } - - // Create TLS listener - note that we do not replace s.listener - // with this TLS listener; tls.listener is unexported and does - // not implement the File() method we need for graceful restarts - // on POSIX systems. - ln = tls.NewListener(ln, s.TLSConfig) - - close(s.startChan) // unblock anyone waiting for this to start listening - return s.Serve(ln) -} -*/ - -// Stop stops the server. It blocks until the server is -// totally stopped. On POSIX systems, it will wait for -// connections to close (up to a max timeout of a few -// seconds); on Windows it will close the listener -// immediately. -func (s *Server) Stop() (err error) { - - if runtime.GOOS != "windows" { - // force connections to close after timeout - done := make(chan struct{}) - go func() { - s.dnsWg.Done() // decrement our initial increment used as a barrier - s.dnsWg.Wait() - close(done) - }() - - // Wait for remaining connections to finish or - // force them all to close after timeout - select { - case <-time.After(s.connTimeout): - case <-done: - } - } - - // Close the listener now; this stops the server without delay - s.listenerMu.Lock() - defer s.listenerMu.Unlock() - - for _, s1 := range s.server { - if s1.Listener != nil { - err = s1.Listener.Close() - } - if s1.PacketConn != nil { - err = s1.PacketConn.Close() - } - err = s1.Shutdown() - } - - return -} - -// WaitUntilStarted blocks until the server s is started, meaning -// that practically the next instruction is to start the server loop. -// It also unblocks if the server encounters an error during startup. -func (s *Server) WaitUntilStarted() { - <-s.startChan -} - -// ListenerFd gets a dup'ed file of the listener. If there -// is no underlying file, the return value will be nil. It -// is the caller's responsibility to close the file. -func (s *Server) ListenerFd() *os.File { - s.listenerMu.Lock() - defer s.listenerMu.Unlock() - if s.server[0].Listener != nil { - file, _ := s.server[0].Listener.(*net.TCPListener).File() - return file - } - return nil -} - -// PacketConnFd gets a dup'ed file of the packetconn. If there -// is no underlying file, the return value will be nil. It -// is the caller's responsibility to close the file. -func (s *Server) PacketConnFd() *os.File { - s.listenerMu.Lock() - defer s.listenerMu.Unlock() - if s.server[1].PacketConn != nil { - file, _ := s.server[1].PacketConn.(*net.UDPConn).File() - return file - } - return nil -} - -// ServeDNS is the entry point for every request to the address that s -// is bound to. It acts as a multiplexer for the requests zonename as -// defined in the request so that the correct zone -// (configuration and middleware stack) will handle the request. -func (s *Server) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { - defer func() { - // In case the user doesn't enable error middleware, we still - // need to make sure that we stay alive up here - if rec := recover(); rec != nil { - DefaultErrorFunc(w, r, dns.RcodeServerFailure) - } - }() - - if m, err := middleware.Edns0Version(r); err != nil { // Wrong EDNS version, return at once. - rc := middleware.RcodeToString(dns.RcodeBadVers) - state := middleware.State{W: w, Req: r} - - metrics.Report(state, metrics.Dropped, rc, m.Len(), time.Now()) - w.WriteMsg(m) - return - } - - // Execute the optional request callback if it exists - if s.ReqCallback != nil && s.ReqCallback(w, r) { - return - } - - q := r.Question[0].Name - b := make([]byte, len(q)) - off, end := 0, false - ctx := context.Background() - - for { - l := len(q[off:]) - for i := 0; i < l; i++ { - b[i] = q[off+i] - // normalize the name for the lookup - if b[i] >= 'A' && b[i] <= 'Z' { - b[i] |= ('a' - 'A') - } - } - - if h, ok := s.zones[string(b[:l])]; ok { - if r.Question[0].Qtype != dns.TypeDS { - rcode, _ := h.stack.ServeDNS(ctx, w, r) - if RcodeNoClientWrite(rcode) { - DefaultErrorFunc(w, r, rcode) - } - return - } - } - off, end = dns.NextLabel(q, off) - if end { - break - } - } - // Wildcard match, if we have found nothing try the root zone as a last resort. - if h, ok := s.zones["."]; ok { - rcode, _ := h.stack.ServeDNS(ctx, w, r) - if RcodeNoClientWrite(rcode) { - DefaultErrorFunc(w, r, rcode) - } - return - } - - // Still here? Error out with REFUSED and some logging - remoteHost := w.RemoteAddr().String() - DefaultErrorFunc(w, r, dns.RcodeRefused) - log.Printf("[INFO] \"%s %s %s\" - No such zone at %s (Remote: %s)", dns.Type(r.Question[0].Qtype), dns.Class(r.Question[0].Qclass), q, s.Addr, remoteHost) -} - -// DefaultErrorFunc responds to an DNS request with an error. -func DefaultErrorFunc(w dns.ResponseWriter, r *dns.Msg, rcode int) { - state := middleware.State{W: w, Req: r} - rc := middleware.RcodeToString(rcode) - - answer := new(dns.Msg) - answer.SetRcode(r, rcode) - state.SizeAndDo(answer) - - metrics.Report(state, metrics.Dropped, rc, answer.Len(), time.Now()) - w.WriteMsg(answer) -} - -// setupClientAuth sets up TLS client authentication only if -// any of the TLS configs specified at least one cert file. -func setupClientAuth(tlsConfigs []TLSConfig, config *tls.Config) error { - var clientAuth bool - for _, cfg := range tlsConfigs { - if len(cfg.ClientCerts) > 0 { - clientAuth = true - break - } - } - - if clientAuth { - pool := x509.NewCertPool() - for _, cfg := range tlsConfigs { - for _, caFile := range cfg.ClientCerts { - caCrt, err := ioutil.ReadFile(caFile) // Anyone that gets a cert from this CA can connect - if err != nil { - return err - } - if !pool.AppendCertsFromPEM(caCrt) { - return fmt.Errorf("error loading client certificate '%s': no certificates were successfully parsed", caFile) - } - } - } - config.ClientCAs = pool - config.ClientAuth = tls.RequireAndVerifyClientCert - } - - return nil -} - -// RunFirstStartupFuncs runs all of the server's FirstStartup -// callback functions unless one of them returns an error first. -// It is the caller's responsibility to call this only once and -// at the correct time. The functions here should not be executed -// at restarts or where the user does not explicitly start a new -// instance of the server. -func (s *Server) RunFirstStartupFuncs() error { - for _, z := range s.zones { - for _, f := range z.config.FirstStartup { - if err := f(); err != nil { - return err - } - } - } - return nil -} - -// ShutdownCallbacks executes all the shutdown callbacks -// for all the virtualhosts in servers, and returns all the -// errors generated during their execution. In other words, -// an error executing one shutdown callback does not stop -// execution of others. Only one shutdown callback is executed -// at a time. You must protect the servers that are passed in -// if they are shared across threads. -func ShutdownCallbacks(servers []*Server) []error { - var errs []error - for _, s := range servers { - for _, zone := range s.zones { - for _, shutdownFunc := range zone.config.Shutdown { - err := shutdownFunc() - if err != nil { - errs = append(errs, err) - } - } - } - } - return errs -} - -func StartupCallbacks(servers []*Server) []error { - var errs []error - for _, s := range servers { - for _, zone := range s.zones { - for _, startupFunc := range zone.config.Startup { - err := startupFunc() - if err != nil { - errs = append(errs, err) - } - } - } - } - return errs -} - -func RcodeNoClientWrite(rcode int) bool { - switch rcode { - case dns.RcodeServerFailure: - fallthrough - case dns.RcodeRefused: - fallthrough - case dns.RcodeFormatError: - fallthrough - case dns.RcodeNotImplemented: - return true - } - return false -} |