diff options
author | 2017-09-14 09:36:06 +0100 | |
---|---|---|
committer | 2017-09-14 09:36:06 +0100 | |
commit | d8714e64e400ef873c2adc4d929a07d7890727b9 (patch) | |
tree | c9fa4c157e6af12eb1517654f8d23ca5d5619513 /plugin/proxy/google.go | |
parent | b984aa45595dc95253b91191afe7d3ee29e71b48 (diff) | |
download | coredns-d8714e64e400ef873c2adc4d929a07d7890727b9.tar.gz coredns-d8714e64e400ef873c2adc4d929a07d7890727b9.tar.zst coredns-d8714e64e400ef873c2adc4d929a07d7890727b9.zip |
Remove the word middleware (#1067)
* Rename middleware to plugin
first pass; mostly used 'sed', few spots where I manually changed
text.
This still builds a coredns binary.
* fmt error
* Rename AddMiddleware to AddPlugin
* Readd AddMiddleware to remain backwards compat
Diffstat (limited to 'plugin/proxy/google.go')
-rw-r--r-- | plugin/proxy/google.go | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/plugin/proxy/google.go b/plugin/proxy/google.go new file mode 100644 index 000000000..ecc5e6dfd --- /dev/null +++ b/plugin/proxy/google.go @@ -0,0 +1,244 @@ +package proxy + +import ( + "context" + "crypto/tls" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net" + "net/http" + "net/url" + "sync/atomic" + "time" + + "github.com/coredns/coredns/plugin/pkg/healthcheck" + "github.com/coredns/coredns/request" + + "github.com/miekg/dns" +) + +type google struct { + client *http.Client + + endpoint string // Name to resolve via 'bootstrapProxy' + + bootstrapProxy Proxy + quit chan bool +} + +func newGoogle(endpoint string, bootstrap []string) *google { + if endpoint == "" { + endpoint = ghost + } + tls := &tls.Config{ServerName: endpoint} + client := &http.Client{ + Timeout: time.Second * defaultTimeout, + Transport: &http.Transport{TLSClientConfig: tls}, + } + + boot := NewLookup(bootstrap) + + return &google{client: client, endpoint: dns.Fqdn(endpoint), bootstrapProxy: boot, quit: make(chan bool)} +} + +func (g *google) Exchange(ctx context.Context, addr string, state request.Request) (*dns.Msg, error) { + v := url.Values{} + + v.Set("name", state.Name()) + v.Set("type", fmt.Sprintf("%d", state.QType())) + + buf, backendErr := g.exchangeJSON(addr, v.Encode()) + + if backendErr == nil { + gm := new(googleMsg) + if err := json.Unmarshal(buf, gm); err != nil { + return nil, err + } + + m, err := toMsg(gm) + if err != nil { + return nil, err + } + + m.Id = state.Req.Id + return m, nil + } + + log.Printf("[WARNING] Failed to connect to HTTPS backend %q: %s", g.endpoint, backendErr) + return nil, backendErr +} + +func (g *google) exchangeJSON(addr, json string) ([]byte, error) { + url := "https://" + addr + "/resolve?" + json + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return nil, err + } + + req.Host = g.endpoint // TODO(miek): works with the extra dot at the end? + + resp, err := g.client.Do(req) + if err != nil { + return nil, err + } + + buf, err := ioutil.ReadAll(resp.Body) + resp.Body.Close() + if err != nil { + return nil, err + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to get 200 status code, got %d", resp.StatusCode) + } + + return buf, nil +} + +func (g *google) Transport() string { return "tcp" } +func (g *google) Protocol() string { return "https_google" } + +func (g *google) OnShutdown(p *Proxy) error { + g.quit <- true + return nil +} + +func (g *google) OnStartup(p *Proxy) error { + // We fake a state because normally the proxy is called after we already got a incoming query. + // This is a non-edns0, udp request to g.endpoint. + req := new(dns.Msg) + req.SetQuestion(g.endpoint, dns.TypeA) + state := request.Request{W: new(fakeBootWriter), Req: req} + + if len(*p.Upstreams) == 0 { + return fmt.Errorf("no upstreams defined") + } + + oldUpstream := (*p.Upstreams)[0] + + log.Printf("[INFO] Bootstrapping A records %q", g.endpoint) + + new, err := g.bootstrapProxy.Lookup(state, g.endpoint, dns.TypeA) + if err != nil { + log.Printf("[WARNING] Failed to bootstrap A records %q: %s", g.endpoint, err) + } else { + addrs, err1 := extractAnswer(new) + if err1 != nil { + log.Printf("[WARNING] Failed to bootstrap A records %q: %s", g.endpoint, err1) + } else { + + up := newUpstream(addrs, oldUpstream.(*staticUpstream)) + p.Upstreams = &[]Upstream{up} + + log.Printf("[INFO] Bootstrapping A records %q found: %v", g.endpoint, addrs) + } + } + + go func() { + tick := time.NewTicker(120 * time.Second) + + for { + select { + case <-tick.C: + + log.Printf("[INFO] Resolving A records %q", g.endpoint) + + new, err := g.bootstrapProxy.Lookup(state, g.endpoint, dns.TypeA) + if err != nil { + log.Printf("[WARNING] Failed to resolve A records %q: %s", g.endpoint, err) + continue + } + + addrs, err1 := extractAnswer(new) + if err1 != nil { + log.Printf("[WARNING] Failed to resolve A records %q: %s", g.endpoint, err1) + continue + } + + up := newUpstream(addrs, oldUpstream.(*staticUpstream)) + p.Upstreams = &[]Upstream{up} + + log.Printf("[INFO] Resolving A records %q found: %v", g.endpoint, addrs) + + case <-g.quit: + return + } + } + }() + + return nil +} + +func extractAnswer(m *dns.Msg) ([]string, error) { + if len(m.Answer) == 0 { + return nil, fmt.Errorf("no answer section in response") + } + ret := []string{} + for _, an := range m.Answer { + if a, ok := an.(*dns.A); ok { + ret = append(ret, net.JoinHostPort(a.A.String(), "443")) + } + } + if len(ret) > 0 { + return ret, nil + } + + return nil, fmt.Errorf("no address records in answer section") +} + +// newUpstream returns an upstream initialized with hosts. +func newUpstream(hosts []string, old *staticUpstream) Upstream { + upstream := &staticUpstream{ + from: old.from, + HealthCheck: healthcheck.HealthCheck{ + FailTimeout: 10 * time.Second, + MaxFails: 3, + Future: 60 * time.Second, + }, + ex: old.ex, + WithoutPathPrefix: old.WithoutPathPrefix, + IgnoredSubDomains: old.IgnoredSubDomains, + } + + upstream.Hosts = make([]*healthcheck.UpstreamHost, len(hosts)) + for i, h := range hosts { + uh := &healthcheck.UpstreamHost{ + Name: h, + Conns: 0, + Fails: 0, + FailTimeout: upstream.FailTimeout, + + CheckDown: func(upstream *staticUpstream) healthcheck.UpstreamHostDownFunc { + return func(uh *healthcheck.UpstreamHost) bool { + + down := false + + uh.CheckMu.Lock() + until := uh.OkUntil + uh.CheckMu.Unlock() + + if !until.IsZero() && time.Now().After(until) { + down = true + } + + fails := atomic.LoadInt32(&uh.Fails) + if fails >= upstream.MaxFails && upstream.MaxFails != 0 { + down = true + } + return down + } + }(upstream), + WithoutPathPrefix: upstream.WithoutPathPrefix, + } + + upstream.Hosts[i] = uh + } + return upstream +} + +const ( + // Default endpoint for this service. + ghost = "dns.google.com." +) |