美文网首页
coredns源码分析

coredns源码分析

作者: 分享放大价值 | 来源:发表于2022-05-21 15:57 被阅读0次

    CoreDNS是使用go语言编写的快速灵活的DNS服务,采用链式插件模式,每个插件实现独立的功能,底层协议可以是tcp/udp,也可以是TLS,gRPC等。默认监听所有ip地址,可使用bind插件指定监听指定地址。

    配置文件

    格式如下

    [SCHEME://]ZONE [[SCHEME://]ZONE]...[:PORT] {
        [PLUGIN]...
    }
    

    SCHEME是可选的,默认值为dns://,也可以指定为tls://,grpc://或者https://。
    ZONE是可选的,指定了此dnsserver可以服务的域名前缀,如果不指定,则默认为root,表示可以接收所有的dns请求。
    PORT是选项的,指定了监听端口号,默认为53,如果这里指定了端口号,则不能通过参数-dns.port覆盖。

    一块上面格式的配置表示一个dnsserver,称为serverblock,可以配置多个serverblock表示多个dnsserver。

    下面通过一个例子说明,如下配置文件指定了4个serverblock,即4个dnsserver,第一个监听端口5300,后面三个监听同一个端口53,每个dnsserver指定了特定的插件。

    coredns.io:5300 {
        file /etc/coredns/zones/coredns.io.db
    }
    
    example.io:53 {
        log    
        errors
        file /etc/coredns/zones/example.io.db
    }
    
    example.net:53 {
        file /etc/coredns/zones/example.net.db
    }
    
    .:53 {
        kubernetes
        errors
        log
        health
    }
    

    下图为配置的简略图


    image.png

    a. 从图中可看到插件执行顺序不是配置文件中的顺序,这是因为插件执行顺序是在源码目录中的plugin.cfg指定的,一旦编译后,顺序就固定了。
    b. .根serverblock虽然指定了health,但是图中却没有,这是因为health插件不参与dns请求的处理。能处理dns请求的插件必须提供如下两个接口函数。

    Handler interface {
        ServeDNS(context.Context, dns.ResponseWriter, *dns.Msg) (int, error)
        Name() string
    }
    

    dns请求处理流程
    收到dns请求后,首先根据域名匹配zone找到对应的dnsserver(最长匹配优先),如果没有匹配到,则使用默认的root dnsserver。
    找到dnsserver后,就要按照插件顺序执行其中配置的插件,当然并不是配置的插件都会被执行,如果某个插件成功找到记录,则返回成功,否则根据插件是否配置了fallthrough等来决定是否执行下一个插件。

    源码分析

    plugin.cfg
    源码目录下的plugin.cfg指定了插件执行顺序,如果想添加插件,可按格式添加到指定位置。

    metadata:metadata
    geoip:geoip
    cancel:cancel
    tls:tls
    reload:reload
    nsid:nsid
    bufsize:bufsize
    root:root
    bind:bind
    debug:debug
    trace:trace
    ready:ready
    health:health
    pprof:pprof
    prometheus:metrics
    errors:errors
    log:log
    dnstap:dnstap
    local:local
    dns64:dns64
    acl:acl
    any:any
    chaos:chaos
    loadbalance:loadbalance
    cache:cache
    rewrite:rewrite
    header:header
    dnssec:dnssec
    autopath:autopath
    minimal:minimal
    template:template
    transfer:transfer
    hosts:hosts
    route53:route53
    azure:azure
    clouddns:clouddns
    k8s_external:k8s_external
    kubernetes:kubernetes
    file:file
    auto:auto
    secondary:secondary
    etcd:etcd
    loop:loop
    forward:forward
    grpc:grpc
    erratic:erratic
    whoami:whoami
    on:github.com/coredns/caddy/onevent
    sign:sign
    

    源码目录下的Makefile根据plugin.cfg生成了两个go文件:zplugin.go和zdirectives.go。

    core/plugin/zplugin.go core/dnsserver/zdirectives.go: plugin.cfg
        go generate coredns.go
        go get
    
    core/plugin/zplugin.go会导入所有的插件,执行所有插件的init函数。
    import (
        // Include all plugins.
        _ "github.com/coredns/caddy/onevent"
        _ "github.com/coredns/coredns/plugin/acl"
        _ "github.com/coredns/coredns/plugin/any"
        _ "github.com/coredns/coredns/plugin/auto"
        _ "github.com/coredns/coredns/plugin/autopath"
        _ "github.com/coredns/coredns/plugin/azure"
        _ "github.com/coredns/coredns/plugin/bind"
        _ "github.com/coredns/coredns/plugin/bufsize"
        _ "github.com/coredns/coredns/plugin/cache"
        _ "github.com/coredns/coredns/plugin/cancel"
        _ "github.com/coredns/coredns/plugin/chaos"
        _ "github.com/coredns/coredns/plugin/clouddns"
        _ "github.com/coredns/coredns/plugin/debug"
        _ "github.com/coredns/coredns/plugin/dns64"
        _ "github.com/coredns/coredns/plugin/dnssec"
        _ "github.com/coredns/coredns/plugin/dnstap"
        _ "github.com/coredns/coredns/plugin/erratic"
        _ "github.com/coredns/coredns/plugin/errors"
        _ "github.com/coredns/coredns/plugin/etcd"
        _ "github.com/coredns/coredns/plugin/file"
        _ "github.com/coredns/coredns/plugin/forward"
        _ "github.com/coredns/coredns/plugin/geoip"
        _ "github.com/coredns/coredns/plugin/grpc"
        _ "github.com/coredns/coredns/plugin/header"
        _ "github.com/coredns/coredns/plugin/health"
        _ "github.com/coredns/coredns/plugin/hosts"
        _ "github.com/coredns/coredns/plugin/k8s_external"
        _ "github.com/coredns/coredns/plugin/kubernetes"
        _ "github.com/coredns/coredns/plugin/loadbalance"
        _ "github.com/coredns/coredns/plugin/local"
        _ "github.com/coredns/coredns/plugin/log"
        _ "github.com/coredns/coredns/plugin/loop"
        _ "github.com/coredns/coredns/plugin/metadata"
        _ "github.com/coredns/coredns/plugin/metrics"
        _ "github.com/coredns/coredns/plugin/minimal"
        _ "github.com/coredns/coredns/plugin/nsid"
        _ "github.com/coredns/coredns/plugin/pprof"
        _ "github.com/coredns/coredns/plugin/ready"
        _ "github.com/coredns/coredns/plugin/reload"
        _ "github.com/coredns/coredns/plugin/rewrite"
        _ "github.com/coredns/coredns/plugin/root"
        _ "github.com/coredns/coredns/plugin/route53"
        _ "github.com/coredns/coredns/plugin/secondary"
        _ "github.com/coredns/coredns/plugin/sign"
        _ "github.com/coredns/coredns/plugin/template"
        _ "github.com/coredns/coredns/plugin/tls"
        _ "github.com/coredns/coredns/plugin/trace"
        _ "github.com/coredns/coredns/plugin/transfer"
        _ "github.com/coredns/coredns/plugin/whoami"
    )
    

    core/dnsserver/zdirectives.go将所有插件名字放在一个数组中。

    var Directives = []string{
        "metadata",
        "geoip",
        "cancel",
        "tls",
        "reload",
        "nsid",
        "bufsize",
        "root",
        "bind",
        "debug",
        "trace",
        "ready",
        "health",
        "pprof",
        "prometheus",
        "errors",
        "log",
        "dnstap",
        "local",
        "dns64",
        "acl",
        "any",
        "chaos",
        "loadbalance",
        "cache",
        "rewrite",
        "header",
        "dnssec",
        "autopath",
        "minimal",
        "template",
        "transfer",
        "hosts",
        "route53",
        "azure",
        "clouddns",
        "k8s_external",
        "kubernetes",
        "file",
        "auto",
        "secondary",
        "etcd",
        "loop",
        "forward",
        "grpc",
        "erratic",
        "whoami",
        "on",
        "sign",
    }
    

    codedns 主函数

    //coredns/coredns.go
    import (
        _ "github.com/coredns/coredns/core/plugin" // Plug in CoreDNS.
        "github.com/coredns/coredns/coremain"
    )
    
    main
        coremain.Run()
    

    codedns.go 首先导入了包"github.com/coredns/coredns/core/plugin",此包内只有一个文件zplugin.go,此文件为自动生成的,主要导入了所有的插件,执行每个插件的init函数。

    接着执行 run.go Run

    //coredns/coremain/run.go
    import (
        ...
        "github.com/coredns/coredns/core/dnsserver"
    )
    
    func Run()
        //解析参数
        flag.Parse()
    
        //如果指定了参数 version,则打印版本信息后退出
        if version {
            showVersion()
            os.Exit(0)
        }
        
        //如果指定了参数 plugins,则只打印插件信息后退出
        if plugins {
            fmt.Println(caddy.DescribePlugins())
            os.Exit(0)
        }
    
        //解析配置文件
        corefile, err := caddy.LoadCaddyfile(serverType)
            cdyfile, err := loadCaddyfileInput(serverType)
                for _, l := range caddyfileLoaders {
                    //执行 confLoader
                    cdyfile, err := l.loader.Load(serverType)
                }
    
        instance, err := caddy.Start(corefile)
        
        // Twiddle your thumbs
        instance.Wait()
    

    此文件又引入了包"github.com/coredns/coredns/core/dnsserver",其init函数在 dnsserver/register.go 文件中,如下所示,主要是注册了serverType

    const serverType = "dns"
    // DefaultPort is the default port.
    const DefaultPort = transport.Port
    Port = "53"
    
    func init() {
        flag.StringVar(&Port, serverType+".port", DefaultPort, "Default port")
        flag.StringVar(&Port, "p", 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\nlog\n}\n"),
                    ServerTypeName: serverType,
                }
            },
            NewContext: newContext,
        })
    }
    

    剩下的就是解析参数,解析配置文件后,执行caddy.Start。
    这里就是根据配置文件中指定的serverblock,执行插件的setup进行初始化,创建对应的server,开始监听dns请求

    //caddy/caddy.go
    func Start(cdyfile Input) (*Instance, error)
        inst := &Instance{serverType: cdyfile.ServerType(), wg: new(sync.WaitGroup), Storage: make(map[interface{}]interface{})}
        err := startWithListenerFds(cdyfile, inst, nil)
    
    func startWithListenerFds(cdyfile Input, inst *Instance, restartFds map[string]restartTriple) error {
        ValidateAndExecuteDirectives(cdyfile, inst, false)
            //stypeName 为 dns
            stypeName := cdyfile.ServerType()
            //stype 通过 RegisterServerType 注册,在 //coredns/core/dnsserver/register.go init时注册
            stype, err := getServerType(stypeName)
                stype, ok := serverTypes[serverType]
                if ok {
                    return stype, nil
                }
                ...
    
            inst.caddyfileInput = cdyfile
    
            //func loadServerBlocks(serverType, filename string, input io.Reader) ([]caddyfile.ServerBlock, error)
            sblocks, err := loadServerBlocks(stypeName, cdyfile.Path(), bytes.NewReader(cdyfile.Body()))
                validDirectives := ValidDirectives(serverType)
                serverBlocks, err := caddyfile.Parse(filename, input, validDirectives)
                    p := parser{Dispenser: NewDispenser(filename, input), validDirectives: validDirectives}
                        // NewDispenser returns a Dispenser, ready to use for parsing the given input.
                        func NewDispenser(filename string, input io.Reader) Dispenser {
                            tokens, _ := allTokens(input) // ignoring error because nothing to do with it
                            return Dispenser{
                                filename: filename,
                                tokens:   tokens,
                                cursor:   -1,
                            }
                        }
                    return p.parseAll()
    
            //coredns/core/dnsserver/register.go:newContext
            inst.context = stype.NewContext(inst)
    
            //coredns/core/dnsserver/register.go:InspectServerBlocks
            sblocks, err = inst.context.InspectServerBlocks(cdyfile.Path(), sblocks)
    
            return executeDirectives(inst, cdyfile.Path(), stype.Directives(), sblocks, justValidate)
                //遍历执行插件注册的 setup 函数
                for _, dir := range directives {
                    for i, sb := range sblocks {
                        //获取插件的初始化函数 setup
                        setup, err := DirectiveAction(inst.serverType, dir)
                            if stypePlugins, ok := plugins[serverType]; ok {
                                if plugin, ok := stypePlugins[dir]; ok {
                                    return plugin.Action, nil
                                }
                            }
                        //执行插件注册的 setup 函数
                        setup(controller)
                            //将 onStart 添加到数组 c.instance.OnStartup
                            c.OnStartup(onStart)
                            //每个插件的setup函数都会调用如下函数,注册插件handler
                            //AddPlugin -> c.Plugin = append(c.Plugin, m)
                            dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
                                l.Next = next
                                return l
                            })
                    }
                }
    
        slist, err := inst.context.MakeServers()
            errValid := h.validateZonesAndListeningAddresses()
            for _, c := range h.configs {
                c.Plugin = c.firstConfigInBlock.Plugin
                c.ListenHosts = c.firstConfigInBlock.ListenHosts
                c.Debug = c.firstConfigInBlock.Debug
                c.TLSConfig = c.firstConfigInBlock.TLSConfig
            }
    
            //将监听相同地址的config放在同一个group。一个config表示一个 dnsserver
            // we must map (group) each config to a bind address
            groups, err := groupConfigsByListenAddr(h.configs)
                groups := make(map[string][]*Config)
                for _, conf := range configs {
                    for _, h := range conf.ListenHosts {
                        addr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(h, conf.Port))
                        if err != nil {
                            return nil, err
                        }
                        addrstr := conf.Transport + "://" + addr.String()
                        groups[addrstr] = append(groups[addrstr], conf)
                    }
                }
    
                return groups, nil
    
            //为同一个组的config创建一个server,多个插件共享一个底层server,上层通过
            //zone区分请求是到哪个dnsserver的
            // then we create a server for each group
            var servers []caddy.Server
            for addr, group := range groups {
                // switch on addr
                switch tr, _ := parse.Transport(addr); tr {
                case transport.DNS:
                    s, err := NewServer(addr, group)
                        s := &Server{
                            Addr:         addr,
                            zones:        make(map[string]*Config),
                            graceTimeout: 5 * time.Second,
                        }
    
                        //site的类型是 Config,每个site表示一个dnsserver
                        for _, site := range group {
                            // set the config per zone
                            s.zones[site.Zone] = site
    
                            //遍历每个dnsserver配置的插件
                            //site.Plugin 为每个插件初始化setup时调用 dnsserver.GetConfig(c).AddPlugin 生成,
                            //顺序是按照数组 Directives 从前向后
                            // compile custom plugin for everything
                            var stack plugin.Handler
                            //从后向前逆序遍历 site.Plugin
                            for i := len(site.Plugin) - 1; i >= 0; i-- {
                                stack = site.Plugin[i](stack)
    
                                // register the *handler* also
                                site.registerHandler(stack)
                                    c.registry[h.Name()] = h
                                    
                                if s.trace == nil && stack.Name() == "trace" {
                                    // we have to stash away the plugin, not the
                                    // Tracer object, because the Tracer won't be initialized yet
                                    if t, ok := stack.(trace.Trace); ok {
                                        s.trace = t
                                    }
                                }
                                // Unblock CH class queries when any of these plugins are loaded.
                                if _, ok := EnableChaos[stack.Name()]; ok {
                                    s.classChaos = true
                                }
                            }
                            //pluginChain 为第一个插件的 handler,收到dns请求后,先执行第一个插件的 handler
                            //在 ServeDNS(core/dnsserver/server.go) 函数中执行 pluginChain
                            site.pluginChain = stack
                        }
    
                        return s, nil
                    servers = append(servers, s)
                ...
                }
            }
            
            return servers, nil
            
        for _, startupFunc := range inst.OnStartup {
            //比如 kubernetes 的 onStart 函数
            err = startupFunc()
        }
    
        startServers(slist, inst, restartFds)
            for _, s := range serverList {
                if ln == nil {
                    ln, err = s.Listen()
                        //core/dnsserver/server.go:Listen
                        l, err := reuseport.Listen("tcp", s.Addr[len(transport.DNS+"://"):])
                        return l, nil
                }
    
                if pc == nil {
                    pc, err = s.ListenPacket()
                        //core/dnsserver/server.go:ListenPacket
                        p, err := reuseport.ListenPacket("udp", s.Addr[len(transport.DNS+"://"):])
                        return p, nil
                }
                inst.servers = append(inst.servers, ServerListener{server: s, listener: ln, packet: pc})
            }
            
            for _, s := range inst.servers {
                func(s Server, ln net.Listener, pc net.PacketConn, inst *Instance) {
                    go func() {
                        defer func() {
                            inst.wg.Done()
                            stopWg.Done()
                        }()
                        errChan <- s.Serve(ln)
                    }()
    
                    go func() {
                        defer func() {
                            inst.wg.Done()
                            stopWg.Done()
                        }()
                        errChan <- s.ServePacket(pc)
                    }()
                }(s.server, s.listener, s.packet, inst)
            }
    }
    

    tcp协议调用Serve,udp协议调用ServePacket

    //core/dnsserver/server.go
    // 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)
            ctx = context.WithValue(ctx, LoopKey{}, 0)
            s.ServeDNS(ctx, w, r)
        })}
        s.m.Unlock()
    
        return s.server[tcp].ActivateAndServe()
    }
    
    // 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)
            ctx = context.WithValue(ctx, LoopKey{}, 0)
            s.ServeDNS(ctx, w, r)
        })}
        s.m.Unlock()
    
        return s.server[udp].ActivateAndServe()
    }
    

    收到DNS请求后,调用ServeDNS,根据域名匹配dnsserver,如果没有匹配不到则使用根dnsserver,然后执行dnsserver中配置的插件

    // ServeDNS is the entry point for every request to the address that
    // 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
        }
    
        // Wrap the response writer in a ScrubWriter so we automatically make the reply fit in the client's buffer.
        w = request.NewScrubWriter(r, w)
    
        q := strings.ToLower(r.Question[0].Name)
        var (
            off       int
            end       bool
            dshandler *Config
        )
    
        //根据dns请求的域名作为zone(最长匹配优先),遍历 s.zones 进行匹配(每个zone表示一个dnsserver),
        //如果匹配到了,则设置 dshandler = h
        for {
            if h, ok := s.zones[q[off:]]; ok {
                if h.pluginChain == nil { // zone defined, but has not got any plugins
                    errorAndMetricsFunc(s.Addr, w, r, dns.RcodeRefused)
                    return
                }
                if r.Question[0].Qtype != dns.TypeDS {
                    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 actual 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
            }
        }
    
        //匹配到zone,执行dnsserver的插件的 ServeDNS。
        //如果插件的 ServeDNS 直接返回了(比如k8s插件查找成功时),则只执行一个插件,
        //如果插件的 ServeDNS 调用plugin.NextOrFailure,则开始执行下一个插件 ServeDNS 了,
        //依次类推,直到有插件返回成功或者失败。
        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
        }
    
        //如果dnsserver没有匹配的zone,则最后尝试执行根zone,即配置文件中指定的"."
        // 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
        }
    
        // Still here? Error out with REFUSED.
        errorAndMetricsFunc(s.Addr, w, r, dns.RcodeRefused)
    }
    

    以k8s插件为例

    //k8s插件的 ServeDNS 函数
    // ServeDNS implements the plugin.Handler interface.
    func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
        state := request.Request{W: w, Req: r}
    
        qname := state.QName()
        zone := plugin.Zones(k.Zones).Matches(qname)
        if zone == "" {
            return plugin.NextOrFailure(k.Name(), k.Next, ctx, w, r)
        }
        zone = qname[len(qname)-len(zone):] // maintain case of original query
        state.Zone = zone
    
        var (
            records   []dns.RR
            extra     []dns.RR
            truncated bool
            err       error
        )
    
        switch state.QType() {
        case dns.TypeA:
            records, truncated, err = plugin.A(ctx, &k, zone, state, nil, plugin.Options{})
        case dns.TypeAAAA:
            records, truncated, err = plugin.AAAA(ctx, &k, zone, state, nil, plugin.Options{})
        case dns.TypeTXT:
            records, truncated, err = plugin.TXT(ctx, &k, zone, state, nil, plugin.Options{})
        case dns.TypeCNAME:
            records, err = plugin.CNAME(ctx, &k, zone, state, plugin.Options{})
        case dns.TypePTR:
            records, err = plugin.PTR(ctx, &k, zone, state, plugin.Options{})
        case dns.TypeMX:
            records, extra, err = plugin.MX(ctx, &k, zone, state, plugin.Options{})
        case dns.TypeSRV:
            records, extra, err = plugin.SRV(ctx, &k, zone, state, plugin.Options{})
        case dns.TypeSOA:
            if qname == zone {
                records, err = plugin.SOA(ctx, &k, zone, state, plugin.Options{})
            }
        case dns.TypeAXFR, dns.TypeIXFR:
            return dns.RcodeRefused, nil
        case dns.TypeNS:
            if state.Name() == zone {
                records, extra, err = plugin.NS(ctx, &k, zone, state, plugin.Options{})
                break
            }
            fallthrough
        default:
            // Do a fake A lookup, so we can distinguish between NODATA and NXDOMAIN
            fake := state.NewWithQuestion(state.QName(), dns.TypeA)
            fake.Zone = state.Zone
            _, _, err = plugin.A(ctx, &k, zone, fake, nil, plugin.Options{})
        }
    
        //没有查找到 dns 记录时,如果配置了fallthrough,则执行下一个插件,
        //否则返回错误信息
        if k.IsNameError(err) {
            if k.Fall.Through(state.Name()) {
                return plugin.NextOrFailure(k.Name(), k.Next, ctx, w, r)
            }
            if !k.APIConn.HasSynced() {
                // If we haven't synchronized with the kubernetes cluster, return server failure
                return plugin.BackendError(ctx, &k, zone, dns.RcodeServerFailure, state, nil /* err */, plugin.Options{})
            }
            return plugin.BackendError(ctx, &k, zone, dns.RcodeNameError, state, nil /* err */, plugin.Options{})
        }
        if err != nil {
            return dns.RcodeServerFailure, err
        }
    
        if len(records) == 0 {
            return plugin.BackendError(ctx, &k, zone, dns.RcodeSuccess, state, nil, plugin.Options{})
        }
    
        //查到dns记录,返回dns响应
        m := new(dns.Msg)
        m.SetReply(r)
        m.Truncated = truncated
        m.Authoritative = true
        m.Answer = append(m.Answer, records...)
        m.Extra = append(m.Extra, extra...)
        w.WriteMsg(m)
        return dns.RcodeSuccess, nil
    }
    
    // SRV returns SRV records from the Backend.
    // If the Target is not a name but an IP address, a name is created on the fly.
    func SRV(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records, extra []dns.RR, err error) {
        //比如对于 kubernetes 插件来说,b.Services 为 coredns/plugin/kubernetes/kubernetes.go:Services
        services, err := b.Services(ctx, state, false, opt)
    
        dup := make(map[item]struct{})
        lookup := make(map[string]struct{})
    
        // Looping twice to get the right weight vs priority. This might break because we may drop duplicate SRV records latter on.
        w := make(map[int]int)
        for _, serv := range services {
            weight := 100
            if serv.Weight != 0 {
                weight = serv.Weight
            }
            if _, ok := w[serv.Priority]; !ok {
                w[serv.Priority] = weight
                continue
            }
            w[serv.Priority] += weight
        }
        for _, serv := range services {
            // Don't add the entry if the port is -1 (invalid). The kubernetes plugin uses port -1 when a service/endpoint
            // does not have any declared ports.
            if serv.Port == -1 {
                continue
            }
            w1 := 100.0 / float64(w[serv.Priority])
            if serv.Weight == 0 {
                w1 *= 100
            } else {
                w1 *= float64(serv.Weight)
            }
            weight := uint16(math.Floor(w1))
            // weight should be at least 1
            if weight == 0 {
                weight = 1
            }
    
            what, ip := serv.HostType()
    
            switch what {
            case dns.TypeCNAME:
                srv := serv.NewSRV(state.QName(), weight)
                records = append(records, srv)
    
                if _, ok := lookup[srv.Target]; ok {
                    break
                }
    
                lookup[srv.Target] = struct{}{}
    
                if !dns.IsSubDomain(zone, srv.Target) {
                    m1, e1 := b.Lookup(ctx, state, srv.Target, dns.TypeA)
                    if e1 == nil {
                        extra = append(extra, m1.Answer...)
                    }
    
                    m1, e1 = b.Lookup(ctx, state, srv.Target, dns.TypeAAAA)
                    if e1 == nil {
                        // If we have seen CNAME's we *assume* that they are already added.
                        for _, a := range m1.Answer {
                            if _, ok := a.(*dns.CNAME); !ok {
                                extra = append(extra, a)
                            }
                        }
                    }
                    break
                }
                // Internal name, we should have some info on them, either v4 or v6
                // Clients expect a complete answer, because we are a recursor in their view.
                state1 := state.NewWithQuestion(srv.Target, dns.TypeA)
                addr, _, e1 := A(ctx, b, zone, state1, nil, opt)
                if e1 == nil {
                    extra = append(extra, addr...)
                }
                // TODO(miek): AAAA as well here.
    
            case dns.TypeA, dns.TypeAAAA:
                addr := serv.Host
                serv.Host = msg.Domain(serv.Key)
                srv := serv.NewSRV(state.QName(), weight)
    
                if ok := isDuplicate(dup, srv.Target, "", srv.Port); !ok {
                    records = append(records, srv)
                }
    
                if ok := isDuplicate(dup, srv.Target, addr, 0); !ok {
                    extra = append(extra, newAddress(serv, srv.Target, ip, what))
                }
            }
        }
        return records, extra, nil
    }
    
    func (k *Kubernetes) Services(ctx context.Context, state request.Request, exact bool, opt plugin.Options) (svcs []msg.Service, err error) {
        s, e := k.Records(ctx, state, false)
            r, e := parseRequest(state.Name(), state.Zone)
            services, err := k.findServices(r, state.Zone)
                //根据dns请求的service和namespace获取index
                idx := object.ServiceKey(r.service, r.namespace)
                //根据index从缓存获取 service 信息
                serviceList = k.APIConn.SvcIndex(idx)
                endpointsListFunc = func() []*object.Endpoints { return k.APIConn.EpIndex(idx) }
    
                zonePath := msg.Path(zone, coredns)
                for _, svc := range serviceList {
                }
            return services, err
    

    参考
    //如何写coredns插件
    http://dockone.io/article/9620

    //coredns源码分析
    https://wenku.baidu.com/view/34cabc1e346baf1ffc4ffe4733687e21af45ff7c.html
    https://blog.csdn.net/zhonglinzhang/article/details/99679323
    https://www.codercto.com/a/89703.html

    //NodeLocal DNSCache
    https://www.cnblogs.com/sanduzxcvbnm/p/16013560.html
    https://blog.csdn.net/xixihahalelehehe/article/details/118894971

    相关文章

      网友评论

          本文标题:coredns源码分析

          本文链接:https://www.haomeiwen.com/subject/jsbburtx.html