CoreDns 采用插件机制,所有功能都是插件形式编写,用户可以扩展自己的插件
1. init 函数
路径: core/dnsserver/register.go
调用 caddy.RegisterServerType 注册了 dns 类型的服务,context,这里有两个参数
- Directives,插件名称的 string 列表,也代表了代码中的目录
- NewContext,是初始化 context 的
// Any flags defined here, need to be namespaced to the serverType other
// wise they potentially clash with other server types.
func init() {
flag.StringVar(&Port, serverType+".port", DefaultPort, "Default port")
caddy.RegisterServerType(serverType, caddy.ServerType{
Directives: func() []string { return Directives },
DefaultInput: func() caddy.Input {
return caddy.CaddyfileInput{
Filepath: "Corefile",
Contents: []byte(".:" + Port + " {\nwhoami\n}\n"),
ServerTypeName: serverType,
}
},
NewContext: newContext,
})
}
1.1 newContext 函数实例化 dnsContext
实现了接口 caddy.Context,Context 接口包含的方法
- InspectServerBlocks(string, []caddyfile.ServerBlock) ([]caddyfile.ServerBlock, error)
- MakeServers() ([]Server, error)
func newContext(i *caddy.Instance) caddy.Context {
return &dnsContext{keysToConfigs: make(map[string]*Config)}
}
2. Run 函数
路径 coredns/coremain/run.go
caddy.LoadCaddyfile 加载配置文件,caddy.start 看看这个方法主要执行的操作
// Run is CoreDNS's main() function.
func Run() {
caddy.TrapSignals()
// Get Corefile input
corefile, err := caddy.LoadCaddyfile(serverType)
if err != nil {
mustLogFatal(err)
}
// Start your engines
instance, err := caddy.Start(corefile)
if err != nil {
mustLogFatal(err)
}
// Twiddle your thumbs
instance.Wait()
2.1 Start 函数
以 caddy file 配置文件启动服务,具体 caddy 不详细分析,知道调用了 context 的 MakeServers 方法
func Start(cdyfile Input) (*Instance, error)
--> func startWithListenerFds
--> ValidateAndExecuteDirectives
--> inst.context.InspectServerBlocks
--> executeDirectives
--> setup 调用每一插件目录下的 setup,加入插件列表
--> inst.context.MakeServers()
--> OnStartup
--> startServers
--> s.Listen
--> s.ListenPacket
--> s.Serve
--> s.ServePacket
--> srv.OnStartupComplete
2.2 MakeServers 函数
创建一系列的服务实例,可以看到包括四种方式对外提供 DNS 服务,分别是 TCP/UDP、GRPC、HTTPS 和 TLS
// MakeServers uses the newly-created siteConfigs to create and return a list of server instances.
func (h *dnsContext) MakeServers() ([]caddy.Server, error) {
// Now that all Keys and Directives are parsed and initialized
// lets verify that there is no overlap on the zones and addresses to listen for
errValid := h.validateZonesAndListeningAddresses()
if errValid != nil {
return nil, errValid
}
// we must map (group) each config to a bind address
groups, err := groupConfigsByListenAddr(h.configs)
if err != nil {
return nil, err
}
3 NewServer 函数
路径 core/dnsserver/server.go
注册插件以及 handler,实现了 ServerDNS 和 Name 方法
// NewServer returns a new CoreDNS server and compiles all plugins in to it. By default CH class
// queries are blocked unless queries from enableChaos are loaded.
func NewServer(addr string, group []*Config) (*Server, error) {
s := &Server{
Addr: addr,
zones: make(map[string]*Config),
graceTimeout: 5 * time.Second,
}
TCPServer 接口
// TCPServer is a type that can listen and serve connections.
// A TCPServer must associate with exactly zero or one net.Listeners.
type TCPServer interface {
// Listen starts listening by creating a new listener
// and returning it. It does not start accepting
// connections. For UDP-only servers, this method
// can be a no-op that returns (nil, nil).
Listen() (net.Listener, error)// Serve starts serving using the provided listener.
// Serve must start the server loop nearly immediately,
// or at least not return any errors before the server
// loop begins. Serve blocks indefinitely, or in other
// words, until the server is stopped. For UDP-only
// servers, this method can be a no-op that returns nil.
Serve(net.Listener) error
}
3.1 Listen 实现了 caddy TCP 服务的 接口
// Listen implements caddy.TCPServer interface.
func (s *Server) Listen() (net.Listener, error) {
l, err := listen("tcp", s.Addr[len(transport.DNS+"://"):])
if err != nil {
return nil, err
}
return l, nil
}
3.2 ListenPacket 实现了 caddy UDP 服务的接口
// ListenPacket implements caddy.UDPServer interface.
func (s *Server) ListenPacket() (net.PacketConn, error) {
p, err := listenPacket("udp", s.Addr[len(transport.DNS+"://"):])
if err != nil {
return nil, err
}
return p, nil
}
3.3 Serve 函数实现了 caddy TCP 服务的接口
可以看到注册的 handler,最终调用 ServerDNS 方法
// Serve starts the server with an existing listener. It blocks until the server stops.
// This implements caddy.TCPServer interface.
func (s *Server) Serve(l net.Listener) error {
s.m.Lock()
s.server[tcp] = &dns.Server{Listener: l, Net: "tcp", Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
ctx := context.WithValue(context.Background(), Key{}, s)
s.ServeDNS(ctx, w, r)
})}
s.m.Unlock()
return s.server[tcp].ActivateAndServe()
}
3.4 ServePacket 函数实现了 caddy UDP 服务的接口
// ServePacket starts the server with an existing packetconn. It blocks until the server stops.
// This implements caddy.UDPServer interface.
func (s *Server) ServePacket(p net.PacketConn) error {
s.m.Lock()
s.server[udp] = &dns.Server{PacketConn: p, Net: "udp", Handler: dns.HandlerFunc(func(w dns.ResponseWriter, r *dns.Msg) {
ctx := context.WithValue(context.Background(), Key{}, s)
s.ServeDNS(ctx, w, r)
})}
s.m.Unlock()
return s.server[udp].ActivateAndServe()
}
3.5 ServerDNS 函数
每个请求都会发送给 core/dnsserver/server.go 的 ServeDNS 进行处理
// 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 plugin stack) will handle the request.
func (s *Server) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) {
// The default dns.Mux checks the question section size, but we have our
// own mux here. Check if we have a question section. If not drop them here.
if r == nil || len(r.Question) == 0 {
errorAndMetricsFunc(s.Addr, w, r, dns.RcodeServerFailure)
return
}
3.5.1 找到匹配的 zone,分别调用插件的 ServerDNS 处理
example.io:53 {
log
errors
file db.example.io
}
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 {
if h.FilterFunc == nil {
rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
if !plugin.ClientWrite(rcode) {
errorFunc(s.Addr, w, r, rcode)
}
return
}
// FilterFunc is set, call it to see if we should use this handler.
// This is given to full query name.
if h.FilterFunc(q) {
rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
if !plugin.ClientWrite(rcode) {
errorFunc(s.Addr, w, r, rcode)
}
return
}
}
// The type is DS, keep the handler, but keep on searching as maybe we are serving
// the parent as well and the DS should be routed to it - this will probably *misroute* DS
// queries to a possibly grand parent, but there is no way for us to know at this point
// if there is an actually delegation from grandparent -> parent -> zone.
// In all fairness: direct DS queries should not be needed.
dshandler = h
}
off, end = dns.NextLabel(q, off)
if end {
break
}
}
3.5.2 DS 请求
if r.Question[0].Qtype == dns.TypeDS && dshandler != nil && dshandler.pluginChain != nil {
// DS request, and we found a zone, use the handler for the query.
rcode, _ := dshandler.pluginChain.ServeDNS(ctx, w, r)
if !plugin.ClientWrite(rcode) {
errorFunc(s.Addr, w, r, rcode)
}
return
}
3.5.3 未匹配则尝试默认 zone .
.:53 {
kubernetes
forward . 8.8.8.8
log
errors
cache
}
// Wildcard match, if we have found nothing try the root zone as a last resort.
if h, ok := s.zones["."]; ok && h.pluginChain != nil {
rcode, _ := h.pluginChain.ServeDNS(ctx, w, r)
if !plugin.ClientWrite(rcode) {
errorFunc(s.Addr, w, r, rcode)
}
return
}
总结:
init 初始化调用 RegisterServerType 注册服务类型 dns
init 初始化调用 caddy.RegisterPlugin 注册一系列类型为 dns 的插件
caddy 加载配置 caddyFile 文件,并启动 Start 方法,启动四种 server 提供 DNS 服务