From 7bde8eecd8b414a6b6373727380d42a2cccc75de Mon Sep 17 00:00:00 2001 From: Christoph Ostarek Date: Sun, 2 Apr 2023 19:58:33 +0200 Subject: [PATCH 1/7] Revert "Revert "build(deps): bump github.com/opencontainers/runc in /pkg/pillar"" This reverts commit 34a39266390e3118d0bde1d38f8ce651db46040b. Signed-off-by: Christoph Ostarek --- pkg/pillar/go.mod | 3 +- pkg/pillar/go.sum | 3 - .../Focinfi/go-dns-resolver/.gitignore | 26 ------ .../Focinfi/go-dns-resolver/LICENSE | 30 ------- .../Focinfi/go-dns-resolver/README.md | 89 ------------------- .../Focinfi/go-dns-resolver/config.go | 16 ---- .../Focinfi/go-dns-resolver/dns-resolver.go | 76 ---------------- .../Focinfi/go-dns-resolver/query-type.go | 38 -------- .../Focinfi/go-dns-resolver/query.go | 32 ------- .../Focinfi/go-dns-resolver/result.go | 62 ------------- pkg/pillar/vendor/modules.txt | 3 - 11 files changed, 1 insertion(+), 377 deletions(-) delete mode 100644 pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/.gitignore delete mode 100644 pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/LICENSE delete mode 100644 pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/README.md delete mode 100644 pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/config.go delete mode 100644 pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/dns-resolver.go delete mode 100644 pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/query-type.go delete mode 100644 pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/query.go delete mode 100644 pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/result.go diff --git a/pkg/pillar/go.mod b/pkg/pillar/go.mod index 43b9d95c09..62ff73a096 100644 --- a/pkg/pillar/go.mod +++ b/pkg/pillar/go.mod @@ -3,7 +3,6 @@ module github.com/lf-edge/eve/pkg/pillar go 1.20 require ( - github.com/Focinfi/go-dns-resolver v1.0.1 github.com/anatol/smart.go v0.0.0-20220615232124-371056cd18c3 github.com/bicomsystems/go-libzfs v0.4.0 github.com/containerd/cgroups v1.0.4 @@ -29,6 +28,7 @@ require ( github.com/lf-edge/eve/api/go v0.0.0-00010101000000-000000000000 github.com/lf-edge/eve/libs v0.0.0-20230303013136-e890ce9ee8a3 github.com/linuxkit/linuxkit/src/cmd/linuxkit v0.0.0-20220913135124-e532e7310810 + github.com/miekg/dns v1.1.41 github.com/moby/sys/mountinfo v0.6.0 github.com/onsi/gomega v1.24.2 github.com/opencontainers/go-digest v1.0.0 @@ -102,7 +102,6 @@ require ( github.com/mdlayher/netlink v1.7.1 // indirect github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 // indirect github.com/mdlayher/socket v0.4.0 // indirect - github.com/miekg/dns v1.1.41 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/sys/signal v0.7.0 // indirect diff --git a/pkg/pillar/go.sum b/pkg/pillar/go.sum index 2396e650fa..e8aa30383b 100644 --- a/pkg/pillar/go.sum +++ b/pkg/pillar/go.sum @@ -155,8 +155,6 @@ github.com/Code-Hex/vz v0.0.4/go.mod h1:UeHKXSv3hP7BzU6IaVE/a7VHSHUHpqbS3oVko4O5 github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Djarvur/go-err113 v0.0.0-20200410182137-af658d038157/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/Djarvur/go-err113 v0.1.0/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/Focinfi/go-dns-resolver v1.0.1 h1:fSNrahh/8Ge06z6HtTEbagPy/WGdV2O1VOEm9+S9vds= -github.com/Focinfi/go-dns-resolver v1.0.1/go.mod h1:DDoAgG4IEfaLksj8/CrTwT1Y/T1tJ2Hg4jznkTL8W0g= github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo= github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= @@ -1206,7 +1204,6 @@ github.com/mibk/dupl v1.0.0/go.mod h1:pCr4pNxxIbFGvtyCOi0c7LVjmV6duhKWV+ex5vh38M github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= diff --git a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/.gitignore b/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/.gitignore deleted file mode 100644 index 59610b561a..0000000000 --- a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof - -.idea \ No newline at end of file diff --git a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/LICENSE b/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/LICENSE deleted file mode 100644 index 55f12ab777..0000000000 --- a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -As this is fork of the official Go code the same license applies. -Extensions of the original work are copyright (c) 2011 Miek Gieben diff --git a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/README.md b/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/README.md deleted file mode 100644 index b38a7dd78b..0000000000 --- a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# go-dns-resolver - -DNS resolver in Golang, based on [miekg/dns](https://github.com/miekg/dns). - -### Goal - -1. Simple interface -2. Rich and neat output -3. Easy to figure -4. High performance - -### Install -```shell -go get github.com/Focinfi/go-dns-resolver -``` - -### Example - -```go -package main - -import ( - dns "github.com/Focinfi/go-dns-resolver" - "log" -) - -func main() { - domains := []string{"google.com", "twitter.com"} - types := []dns.QueryType{dns.TypeA, dns.TypeNS, dns.TypeMX, dns.TypeTXT} - - // Set timeout and retry times - dns.Config.SetTimeout(uint(2)) - dns.Config.RetryTimes = uint(4) - - // Simple usage - if results, err := dns.Exchange("google.com", "119.29.29.29", dns.TypeA); err == nil { - for _, r := range results { - log.Println(r.Record, r.Type, r.Ttl, r.Priority, r.Content) - } - } else { - log.Fatal(err) - } - - // Create and setup resolver with domains and types - resolver := dns.NewResolver("119.29.29.29") - resolver.Targets(domains...).Types(types...) - // Lookup - res := resolver.Lookup() - - //res.ResMap is a map[string]*ResultItem, key is the domain - for target := range res.ResMap { - log.Printf("%v: \n", target) - for _, r := range res.ResMap[target] { - log.Println(r.Record, r.Type, r.Ttl, r.Priority, r.Content) - } - } -} - -``` - -Output: -``` shell -google.com A 2m31s 0 216.58.197.110 - -twitter.com: -twitter.com A 10m3s 0 78.16.49.15 -twitter.com NS 11h49m58s 0 ns1.p34.dynect.net. -twitter.com NS 11h49m58s 0 ns4.p34.dynect.net. -twitter.com NS 11h49m58s 0 ns3.p34.dynect.net. -twitter.com NS 11h49m58s 0 ns2.p34.dynect.net. -google.com: -google.com TXT 19m26s 0 v=spf1 include:_spf.google.com ~all -google.com A 2m31s 0 216.58.197.110 -google.com NS 7h40m6s 0 ns1.google.com. -google.com NS 7h40m6s 0 ns3.google.com. -google.com NS 7h40m6s 0 ns2.google.com. -google.com NS 7h40m6s 0 ns4.google.com. -google.com MX 10m0s 20 alt1.aspmx.l.google.com. -google.com MX 10m0s 10 aspmx.l.google.com. -google.com MX 10m0s 50 alt4.aspmx.l.google.com. -google.com MX 10m0s 40 alt3.aspmx.l.google.com. -google.com MX 10m0s 30 alt2.aspmx.l.google.com. -[Finished in 2.3s] -``` - -### Todo - -1. Support more DNS record types - diff --git a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/config.go b/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/config.go deleted file mode 100644 index bc747d7c98..0000000000 --- a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/config.go +++ /dev/null @@ -1,16 +0,0 @@ -package resolver - -import ( - "time" -) - -type Configuration struct { - Timeout time.Duration - RetryTimes uint -} - -func (c *Configuration) SetTimeout(seconds uint) { - c.Timeout = time.Second * time.Duration(seconds) -} - -var Config = Configuration{Timeout: time.Second * time.Duration(2), RetryTimes: uint(0)} diff --git a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/dns-resolver.go b/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/dns-resolver.go deleted file mode 100644 index c9746117a6..0000000000 --- a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/dns-resolver.go +++ /dev/null @@ -1,76 +0,0 @@ -package resolver - -import ( - "sync" - - "github.com/miekg/dns" -) - -type Resolver struct { - Server string - Query *Query -} - -func NewResolver(server string) *Resolver { - resolver := Resolver{Server: server + ":53"} - return &resolver -} - -func (r *Resolver) Targets(targets ...string) *Query { - query := NewQueryWithTargets(targets...) - r.Query = query - return query -} - -func (r *Resolver) Lookup() *Result { - result := Result{Server: r.Server, ResMap: map[string][]*ResultItem{}} - - resultsChan := make(chan []*ResultItem, r.Query.Count()) - var wg sync.WaitGroup - for _, queryItem := range r.Query.Items { - target := queryItem.Target - for _, t := range queryItem.Types { - wg.Add(1) - go func(queryType QueryType) { - defer wg.Done() - goExchange(target, r.Server, queryType, resultsChan) - }(t) - } - } - - wg.Wait() - close(resultsChan) - - for res := range resultsChan { - if len(res) > 0 { - target := res[0].Record - result.ResMap[target] = append(result.ResMap[target], res...) - } - } - return &result -} - -func goExchange(target string, server string, queryType QueryType, resultsChan chan []*ResultItem) { - for i := -1; i < int(Config.RetryTimes); i++ { - if results, err := Exchange(target, server, queryType); err == nil { - resultsChan <- results - return - } - } -} - -func Exchange(target string, server string, queryType QueryType) ([]*ResultItem, error) { - var results []*ResultItem - msg := &dns.Msg{} - msg.SetQuestion(target+".", uint16(queryType)) - client := &dns.Client{DialTimeout: Config.Timeout} - res, _, err := client.Exchange(msg, server) - if err == nil && len(res.Answer) > 0 { - for _, answer := range res.Answer { - result := NewResultItemWithDnsRR(queryType, answer) - result.Record = target - results = append(results, result) - } - } - return results, err -} diff --git a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/query-type.go b/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/query-type.go deleted file mode 100644 index 43b5b856e8..0000000000 --- a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/query-type.go +++ /dev/null @@ -1,38 +0,0 @@ -package resolver - -import ( - "github.com/miekg/dns" -) - -type QueryType uint16 - -const ( - TypeA = QueryType(dns.TypeA) - TypeAAAA = QueryType(dns.TypeAAAA) - TypeNS = QueryType(dns.TypeNS) - TypeMX = QueryType(dns.TypeMX) - TypeSOA = QueryType(dns.TypeSOA) - TypeCNAME = QueryType(dns.TypeCNAME) - TypeTXT = QueryType(dns.TypeTXT) -) - -func (q QueryType) String() string { - switch q { - case TypeA: - return "A" - case TypeAAAA: - return "AAAA" - case TypeNS: - return "NS" - case TypeMX: - return "MX" - case TypeSOA: - return "SOA" - case TypeCNAME: - return "CNAME" - case TypeTXT: - return "TXT" - default: - return "Unknown Type" - } -} diff --git a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/query.go b/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/query.go deleted file mode 100644 index 24a479a0e5..0000000000 --- a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/query.go +++ /dev/null @@ -1,32 +0,0 @@ -package resolver - -type QueryItem struct { - Target string - Types []QueryType -} - -type Query struct { - Items []*QueryItem -} - -func NewQueryWithTargets(targets ...string) *Query { - query := Query{Items: make([]*QueryItem, len(targets), len(targets))} - for i, target := range targets { - query.Items[i] = &QueryItem{Target: target} - } - return &query -} - -func (query *Query) Types(types ...QueryType) *Query { - for _, queryItem := range query.Items { - queryItem.Types = types - } - return query -} - -func (query *Query) Count() int { - if len(query.Items) == 0 { - return 0 - } - return len(query.Items) * len(query.Items[0].Types) -} diff --git a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/result.go b/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/result.go deleted file mode 100644 index 00f018bd6e..0000000000 --- a/pkg/pillar/vendor/github.com/Focinfi/go-dns-resolver/result.go +++ /dev/null @@ -1,62 +0,0 @@ -package resolver - -import ( - "github.com/miekg/dns" - "strings" - "time" -) - -type Result struct { - Server string - ResMap map[string][]*ResultItem -} - -type ResultItem struct { - Record string - Type string - Ttl time.Duration - Priority uint16 - Content string -} - -func (resultItem *ResultItem) setTtl(rr dns.RR_Header) { - resultItem.Ttl = time.Second * time.Duration(rr.Ttl) -} - -func NewResultItemWithDnsRR(queryType QueryType, answer dns.RR) (resultItem *ResultItem) { - resultItem = &ResultItem{Type: queryType.String()} - switch queryType { - case TypeA: - if a, ok := answer.(*dns.A); ok { - resultItem.setTtl(a.Hdr) - resultItem.Content = a.A.String() - } - case TypeAAAA: - if a, ok := answer.(*dns.AAAA); ok { - resultItem.setTtl(a.Hdr) - resultItem.Content = a.AAAA.String() - } - case TypeCNAME: - if cname, ok := answer.(*dns.CNAME); ok { - resultItem.setTtl(cname.Hdr) - resultItem.Content = cname.Target - } - case TypeMX: - if mx, ok := answer.(*dns.MX); ok { - resultItem.setTtl(mx.Hdr) - resultItem.Content = mx.Mx - resultItem.Priority = mx.Preference - } - case TypeNS: - if ns, ok := answer.(*dns.NS); ok { - resultItem.setTtl(ns.Hdr) - resultItem.Content = ns.Ns - } - case TypeTXT: - if txt, ok := answer.(*dns.TXT); ok { - resultItem.setTtl(txt.Hdr) - resultItem.Content = strings.Join(txt.Txt, " ") - } - } - return -} diff --git a/pkg/pillar/vendor/modules.txt b/pkg/pillar/vendor/modules.txt index 5711769558..dd905963ca 100644 --- a/pkg/pillar/vendor/modules.txt +++ b/pkg/pillar/vendor/modules.txt @@ -26,9 +26,6 @@ github.com/Azure/azure-storage-blob-go/azblob ## explicit; go 1.16 github.com/Azure/go-ansiterm github.com/Azure/go-ansiterm/winterm -# github.com/Focinfi/go-dns-resolver v1.0.1 -## explicit; go 1.14 -github.com/Focinfi/go-dns-resolver # github.com/Microsoft/go-winio v0.5.2 ## explicit; go 1.13 github.com/Microsoft/go-winio From 21d6035e86764f8130a0a2e602294e42b89d8d87 Mon Sep 17 00:00:00 2001 From: Christoph Ostarek Date: Sun, 2 Apr 2023 19:58:44 +0200 Subject: [PATCH 2/7] Revert "Revert "nim: split controllerDNSCache into pieces"" This reverts commit 6a7b7695ff188c6e68c200b04fc0ac90bb571719. Signed-off-by: Christoph Ostarek --- pkg/pillar/cmd/nim/controllerdns.go | 116 ++++++++++++++++++---------- 1 file changed, 74 insertions(+), 42 deletions(-) diff --git a/pkg/pillar/cmd/nim/controllerdns.go b/pkg/pillar/cmd/nim/controllerdns.go index 62feb73df8..8875df9208 100644 --- a/pkg/pillar/cmd/nim/controllerdns.go +++ b/pkg/pillar/cmd/nim/controllerdns.go @@ -5,7 +5,9 @@ package nim import ( "bytes" + "errors" "fmt" + "io/fs" "net" "os" "time" @@ -72,41 +74,26 @@ func (n *nim) queryControllerDNS() { // periodical cache the controller DNS resolution into /etc/hosts file // it returns the cached ip string, and TTL setting from the server -func (n *nim) controllerDNSCache(etchosts, controllerServer []byte, ipaddrCached string) (string, int) { - if len(etchosts) == 0 || len(controllerServer) == 0 { - return ipaddrCached, maxTTLSec - } - +func (n *nim) controllerDNSCache( + etchosts, controllerServer []byte, + ipaddrCached string, +) (string, int) { // Check to see if the server domain is already in the /etc/hosts as in eden, // then skip this DNS queries - if ipaddrCached == "" { - hostsEntries := bytes.Split(etchosts, []byte("\n")) - for _, entry := range hostsEntries { - fields := bytes.Fields(entry) - if len(fields) == 2 { - if bytes.Compare(fields[1], controllerServer) == 0 { - n.Log.Tracef("server entry %s already in /etc/hosts, skip", controllerServer) - return ipaddrCached, maxTTLSec - } - } - } + isCached, ipAddrCached, ttlCached := n.checkCachedEntry( + etchosts, + controllerServer, + ipaddrCached, + ) + if isCached { + return ipAddrCached, ttlCached } - var nameServers []string - dnsServer, _ := os.ReadFile(resolvFileName) - dnsRes := bytes.Split(dnsServer, []byte("\n")) - for _, d := range dnsRes { - d1 := bytes.Split(d, []byte("nameserver ")) - if len(d1) == 2 { - nameServers = append(nameServers, string(d1[1])) - } - } - if len(nameServers) == 0 { - nameServers = append(nameServers, "8.8.8.8") - } + nameServers := n.readNameservers() - if _, err := os.Stat(tmpHostFileName); err == nil { - _ = os.Remove(tmpHostFileName) + err := os.Remove(tmpHostFileName) + if err != nil && !errors.Is(err, fs.ErrNotExist) { + n.Log.Warnf("%s exists but removing failed: %+v", tmpHostFileName, err) } var newhosts []byte @@ -156,24 +143,69 @@ func (n *nim) controllerDNSCache(etchosts, controllerServer []byte, ipaddrCached newhosts = append(newhosts, etchosts...) } - ipaddrCached = "" - err := os.WriteFile(tmpHostFileName, newhosts, 0644) - if err == nil { - if err := os.Rename(tmpHostFileName, etcHostFileName); err != nil { - n.Log.Errorf("can not rename /etc/hosts file %v", err) - } else { - if gotipentry { - ipaddrCached = lookupIPaddr - } - n.Log.Tracef("append controller IP %s to /etc/hosts", lookupIPaddr) - } + if n.writeTmpHostsFile(newhosts) && gotipentry { + n.Log.Tracef("append controller IP %s to /etc/hosts", lookupIPaddr) + ipaddrCached = lookupIPaddr } else { - n.Log.Errorf("can not write /tmp/etchosts file %v", err) + ipaddrCached = "" } return ipaddrCached, ttlSec } +func (n *nim) writeHostsFile(newhosts []byte) bool { + err := os.WriteFile(tmpHostFileName, newhosts, 0644) + if err != nil { + n.Log.Errorf("can not write /tmp/etchosts file %v", err) + return false + } + if err := os.Rename(tmpHostFileName, etcHostFileName); err != nil { + n.Log.Errorf("can not rename /etc/hosts file %v", err) + return false + } + return true +} + +func (*nim) readNameservers() []string { + var nameServers []string + dnsServer, _ := os.ReadFile(resolvFileName) + dnsRes := bytes.Split(dnsServer, []byte("\n")) + for _, d := range dnsRes { + d1 := bytes.Split(d, []byte("nameserver ")) + if len(d1) == 2 { + nameServers = append(nameServers, string(d1[1])) + } + } + if len(nameServers) == 0 { + nameServers = append(nameServers, "8.8.8.8") + } + return nameServers +} + +func (n *nim) checkCachedEntry( + etchosts []byte, + controllerServer []byte, + ipaddrCached string, +) (bool, string, int) { + if len(etchosts) == 0 || len(controllerServer) == 0 { + return true, ipaddrCached, maxTTLSec + } + + if ipaddrCached == "" { + hostsEntries := bytes.Split(etchosts, []byte("\n")) + for _, entry := range hostsEntries { + fields := bytes.Fields(entry) + if len(fields) == 2 { + if bytes.Compare(fields[1], controllerServer) == 0 { + n.Log.Tracef("server entry %s already in /etc/hosts, skip", controllerServer) + return true, ipaddrCached, maxTTLSec + } + } + } + } + return false, "", 0 +} + func getTTL(ttl time.Duration) int { ttlSec := int(ttl.Seconds()) if ttlSec < minTTLSec { From 59003d0a3efd8604dc0f7fe2707589316ec41c0b Mon Sep 17 00:00:00 2001 From: Christoph Ostarek Date: Sun, 2 Apr 2023 19:58:53 +0200 Subject: [PATCH 3/7] Revert "Revert "nim: switch from go-dns-resolver to miekg/dns"" This reverts commit 4569202eac2236613c997a0d722d6b04c52b1785. Signed-off-by: Christoph Ostarek --- pkg/pillar/cmd/nim/controllerdns.go | 66 +++------ pkg/pillar/devicenetwork/dns.go | 137 ++++++++++++++++++ pkg/pillar/devicenetwork/dns_test.go | 201 +++++++++++++++++++++++++++ 3 files changed, 359 insertions(+), 45 deletions(-) create mode 100644 pkg/pillar/devicenetwork/dns_test.go diff --git a/pkg/pillar/cmd/nim/controllerdns.go b/pkg/pillar/cmd/nim/controllerdns.go index 8875df9208..62cf755389 100644 --- a/pkg/pillar/cmd/nim/controllerdns.go +++ b/pkg/pillar/cmd/nim/controllerdns.go @@ -8,11 +8,10 @@ import ( "errors" "fmt" "io/fs" - "net" "os" "time" - dns "github.com/Focinfi/go-dns-resolver" + "github.com/lf-edge/eve/pkg/pillar/devicenetwork" "github.com/lf-edge/eve/pkg/pillar/types" ) @@ -72,6 +71,18 @@ func (n *nim) queryControllerDNS() { } } +func (n *nim) resolveWithPorts(domain string) []devicenetwork.DNSResponse { + dnsResponse, errs := devicenetwork.ResolveWithPortsLambda( + domain, + n.dpcManager.GetDNS(), + devicenetwork.ResolveWithSrcIP, + ) + if len(errs) > 0 { + n.Log.Warnf("resolveWithPortsLambda failed: %+v", errs) + } + return dnsResponse +} + // periodical cache the controller DNS resolution into /etc/hosts file // it returns the cached ip string, and TTL setting from the server func (n *nim) controllerDNSCache( @@ -89,61 +100,26 @@ func (n *nim) controllerDNSCache( return ipAddrCached, ttlCached } - nameServers := n.readNameservers() - err := os.Remove(tmpHostFileName) if err != nil && !errors.Is(err, fs.ErrNotExist) { n.Log.Warnf("%s exists but removing failed: %+v", tmpHostFileName, err) } var newhosts []byte - var gotipentry bool - var lookupIPaddr string var ttlSec int + var lookupIPaddr string - domains := []string{string(controllerServer)} - dtypes := []dns.QueryType{dns.TypeA} - for _, nameServer := range nameServers { - resolver := dns.NewResolver(nameServer) - resolver.Targets(domains...).Types(dtypes...) - - res := resolver.Lookup() - for target := range res.ResMap { - for _, r := range res.ResMap[target] { - dIP := net.ParseIP(r.Content) - if dIP == nil { - continue - } - lookupIPaddr = dIP.String() - ttlSec = getTTL(r.Ttl) - if ipaddrCached == lookupIPaddr { - n.Log.Tracef("same IP address %s, return", lookupIPaddr) - return ipaddrCached, ttlSec - } - serverEntry := fmt.Sprintf("%s %s\n", lookupIPaddr, controllerServer) - newhosts = append(etchosts, []byte(serverEntry)...) - gotipentry = true - // a rare event for dns address change, log it - n.Log.Noticef("dnsServer %s, ttl %d, entry add to /etc/hosts: %s", nameServer, ttlSec, serverEntry) - break - } - if gotipentry { - break - } - } - if gotipentry { - break - } - } + dnsResponses := n.resolveWithPorts(string(controllerServer)) + ttlSec = int(dnsResponses[0].TTL) + lookupIPaddr = dnsResponses[0].IP.String() + serverEntry := fmt.Sprintf("%s %s\n", lookupIPaddr, controllerServer) + newhosts = append(etchosts, []byte(serverEntry)...) - if ipaddrCached == lookupIPaddr { - return ipaddrCached, minTTLSec - } - if !gotipentry { // put original /etc/hosts file back + if len(dnsResponses) == 0 { newhosts = append(newhosts, etchosts...) } - if n.writeTmpHostsFile(newhosts) && gotipentry { + if len(dnsResponses) > 0 && n.writeHostsFile(newhosts) { n.Log.Tracef("append controller IP %s to /etc/hosts", lookupIPaddr) ipaddrCached = lookupIPaddr } else { diff --git a/pkg/pillar/devicenetwork/dns.go b/pkg/pillar/devicenetwork/dns.go index e2877aae9e..9c00e031cf 100644 --- a/pkg/pillar/devicenetwork/dns.go +++ b/pkg/pillar/devicenetwork/dns.go @@ -5,14 +5,33 @@ package devicenetwork import ( "fmt" + "net" "os" "path/filepath" "strings" + "sync" + "time" + + "github.com/lf-edge/eve/pkg/pillar/types" + "github.com/miekg/dns" ) // ResolveConfDirs : directories where resolv.conf for an interface could be found. var ResolveConfDirs = []string{"/run/dhcpcd/resolv.conf", "/run/wwan/resolv.conf"} +const ( + // DNSMaxParallelRequests is the maximum amount of parallel DNS requests + DNSMaxParallelRequests = 5 + maxTTLSec int = 3600 + dnsTimeout = 30 * time.Second +) + +// DNSResponse represents a response from a DNS server (A Record) +type DNSResponse struct { + IP net.IP + TTL uint32 +} + // IfnameToResolvConf : Look for a file created by dhcpcd func IfnameToResolvConf(ifname string) string { for _, d := range ResolveConfDirs { @@ -39,3 +58,121 @@ func ResolvConfToIfname(resolvConf string) string { } return "" } + +// ResolveWithSrcIP resolves a domain with a given dns server and source Ip +func ResolveWithSrcIP(domain string, dnsServerIP net.IP, srcIP net.IP) ([]DNSResponse, error) { + var response []DNSResponse + sourceUDPAddr := net.UDPAddr{IP: srcIP} + dialer := net.Dialer{LocalAddr: &sourceUDPAddr} + dnsClient := dns.Client{Dialer: &dialer} + msg := dns.Msg{} + if domain[len(domain)-1] != '.' { + domain = domain + "." + } + msg.SetQuestion(domain, dns.TypeA) + dnsClient.Timeout = time.Duration(dnsTimeout) + reply, _, err := dnsClient.Exchange(&msg, net.JoinHostPort(dnsServerIP.String(), "53")) + if err != nil { + return response, fmt.Errorf("dns exchange failed: %v", err) + } + for _, answer := range reply.Answer { + if aRecord, ok := answer.(*dns.A); ok { + response = append(response, DNSResponse{ + IP: aRecord.A, + TTL: aRecord.Header().Ttl, + }) + } + } + + return response, nil +} + +// ResolveWithPortsLambda resolves a domain by using source IPs and dns servers from DeviceNetworkStatus +// As a resolver func ResolveWithSrcIP can be used +func ResolveWithPortsLambda(domain string, + dns types.DeviceNetworkStatus, + resolve func(string, net.IP, net.IP) ([]DNSResponse, error)) ([]DNSResponse, []error) { + + quit := make(chan struct{}) + work := make(chan struct{}, DNSMaxParallelRequests) + resolvedIPsChan := make(chan []DNSResponse) + countDNSRequests := 0 + var errs []error + var errsMutex sync.Mutex + var wg sync.WaitGroup + + for _, port := range dns.Ports { + if port.Cost > 0 { + continue + } + + var srcIPs []net.IP + for _, addrInfo := range port.AddrInfoList { + srcIPs = append(srcIPs, addrInfo.Addr) + } + + for _, dnsIP := range port.DNSServers { + for _, srcIP := range srcIPs { + wg.Add(1) + dnsIPCopy := make(net.IP, len(dnsIP)) + copy(dnsIPCopy, dnsIP) + srcIPCopy := make(net.IP, len(srcIP)) + copy(srcIPCopy, srcIP) + countDNSRequests++ + go func(dnsIP, srcIP net.IP) { + select { + case work <- struct{}{}: + // if writable, means less than dnsMaxParallelRequests goroutines are currently running + } + select { + case <-quit: + // will return in case the quit chan has been closed, + // meaning another dns server already resolved the IP + return + default: + // do not wait for receiving a quit + } + response, err := resolve(domain, dnsIP, srcIP) + if err != nil { + errsMutex.Lock() + defer errsMutex.Unlock() + errs = append(errs, err) + } + if response != nil { + resolvedIPsChan <- response + } + <-work + wg.Done() + }(dnsIPCopy, srcIPCopy) + } + } + } + + wgChan := make(chan struct{}) + go func() { + wg.Wait() + close(wgChan) + }() + + select { + case <-wgChan: + var responses []DNSResponse + if countDNSRequests == 0 { + // fallback in case no resolver is configured + ips, err := net.LookupIP(domain) + if err != nil { + return nil, append(errs, fmt.Errorf("fallback resolver failed: %+v", err)) + } + for _, ip := range ips { + responses = append(responses, DNSResponse{ + IP: ip, + TTL: uint32(maxTTLSec), + }) + } + } + return responses, nil + case ip := <-resolvedIPsChan: + close(quit) + return ip, errs + } +} diff --git a/pkg/pillar/devicenetwork/dns_test.go b/pkg/pillar/devicenetwork/dns_test.go new file mode 100644 index 0000000000..af02f43dbd --- /dev/null +++ b/pkg/pillar/devicenetwork/dns_test.go @@ -0,0 +1,201 @@ +// Copyright (c) 2023 Zededa, Inc. +// SPDX-License-Identifier: Apache-2.0 + +package devicenetwork_test + +import ( + "net" + "sync/atomic" + "testing" + "time" + + "github.com/lf-edge/eve/pkg/pillar/devicenetwork" + "github.com/lf-edge/eve/pkg/pillar/netmonitor" + "github.com/lf-edge/eve/pkg/pillar/types" +) + +func createNetmonitorMockInterface() []netmonitor.MockInterface { + mockInterface := []netmonitor.MockInterface{ + { + Attrs: netmonitor.IfAttrs{ + IfIndex: 0, + IfName: "if0", + }, + IPAddrs: []*net.IPNet{ + { + IP: []byte{192, 168, 0, 1}, + Mask: []byte{255, 255, 255, 0}, + }, + { + IP: []byte{192, 168, 0, 2}, + Mask: []byte{255, 255, 255, 0}, + }, + }, + HwAddr: []byte{}, + DNS: netmonitor.DNSInfo{ + ResolvConfPath: "/etc/resolv.conf", + Domains: []string{}, + DNSServers: []net.IP{ + {208, 67, 220, 220}, + {208, 67, 222, 222}, + {141, 1, 1, 1}, + {1, 1, 1, 1}, + {9, 9, 9, 9}, + }, + }, + }, + { + Attrs: netmonitor.IfAttrs{ + IfIndex: 1, + IfName: "if1", + }, + IPAddrs: []*net.IPNet{ + { + IP: []byte{192, 168, 1, 1}, + Mask: []byte{255, 255, 255, 0}, + }, + { + IP: []byte{192, 168, 1, 2}, + Mask: []byte{255, 255, 255, 0}, + }, + }, + HwAddr: []byte{}, + DNS: netmonitor.DNSInfo{ + ResolvConfPath: "/etc/resolv.conf", + Domains: []string{}, + DNSServers: []net.IP{ + {1, 0, 0, 1}, + {8, 8, 8, 8}, + }, + }, + }, + { + Attrs: netmonitor.IfAttrs{ + IfIndex: 2, + IfName: "ExpensiveIf", + }, + IPAddrs: []*net.IPNet{{ + IP: []byte{6, 6, 6, 6}, + Mask: []byte{255, 255, 255, 0}, + }}, + HwAddr: []byte{}, + DNS: netmonitor.DNSInfo{ + ResolvConfPath: "/etc/resolv.conf", + Domains: []string{}, + DNSServers: []net.IP{ + {0, 6, 6, 6}, + {0, 7, 7, 7}, + }, + }, + }, + } + return mockInterface +} + +func createDeviceNetworkStatus() types.DeviceNetworkStatus { + mockInterface := createNetmonitorMockInterface() + deviceNetworkStatusPorts := make([]types.NetworkPortStatus, len(mockInterface)) + for i := range deviceNetworkStatusPorts { + deviceNetworkStatusPorts[i].IfName = mockInterface[i].Attrs.IfName + deviceNetworkStatusPorts[i].DNSServers = mockInterface[i].DNS.DNSServers + addrInfos := make([]types.AddrInfo, len(mockInterface[i].IPAddrs)) + for j := range mockInterface[i].IPAddrs { + addrInfos[j] = types.AddrInfo{ + Addr: mockInterface[i].IPAddrs[j].IP, + } + } + + deviceNetworkStatusPorts[i].AddrInfoList = addrInfos + } + + deviceNetworkStatus := types.DeviceNetworkStatus{ + CurrentIndex: 0, + Ports: deviceNetworkStatusPorts, + } + return deviceNetworkStatus +} + +func TestDnsResolve(t *testing.T) { + t.Parallel() + + testHost := "255.255.255.255.nip.io" + expectedIP := net.IP{255, 255, 255, 255} + if testing.Short() { + t.Skipf( + "Skipping as connecting to the internet would take too much time and short tests are enabled", + ) + } + + res, errs := devicenetwork.ResolveWithSrcIP(testHost, net.IP{1, 1, 1, 1}, net.IP{0, 0, 0, 0}) + if errs != nil { + panic(errs) + } + if res == nil { + t.Skipf( + "could not resolve, skipping as probably the tests don't have internet connection of %s is down", + testHost, + ) + } + if !res[0].IP.Equal(expectedIP) { + t.Fatalf( + "resolving returned wrong IP address %+v, but should have been %+v", + res, + expectedIP, + ) + } +} + +func TestDnsResolveTimeout(t *testing.T) { + t.Parallel() + + if testing.Short() { + t.Skipf("Skipping as timing out would take too much time and short tests are enabled") + } + exampleCom := net.IP{93, 184, 216, 34} // example.com, they drop packets on 53/udp + res, _ := devicenetwork.ResolveWithSrcIP("www.google.com", exampleCom, net.IP{0, 0, 0, 0}) + if res != nil { + t.Fatalf("resolving with dns server %+v should fail, but succeeded: %+v", exampleCom, res) + } +} + +func TestResolveWithPortsLambda(t *testing.T) { + t.Parallel() + + expectedIP := net.IP{1, 2, 3, 4} + + var first atomic.Bool + first.Store(true) + var countCalls atomic.Int32 + resolverFunc := func(domain string, dnsServer net.IP, srcIP net.IP) ([]devicenetwork.DNSResponse, error) { + countCalls.Add(1) + if !first.Swap(false) { + time.Sleep(1 * time.Second) + return []devicenetwork.DNSResponse{}, nil + } + return []devicenetwork.DNSResponse{ + { + IP: expectedIP, + TTL: 3600, + }, + }, nil + } + + deviceNetworkStatus := createDeviceNetworkStatus() + res, err := devicenetwork.ResolveWithPortsLambda( + "example.com", + deviceNetworkStatus, + resolverFunc, + ) + if err != nil { + panic(err) + } + if !res[0].IP.Equal(expectedIP) { + t.Errorf("wrong result, expected IP 1.2.3.4, but got: %+v", res) + } + if countCalls.Load() > devicenetwork.DNSMaxParallelRequests+1 { + // checking for +1 as the first call immediately returns + t.Errorf( + "more calls to resolverFunc than dnsMaxParallelRequests+1, but first call should already succeed", + ) + } +} From c6cbde5bddd9b6728ce55da13e0904e2170951be Mon Sep 17 00:00:00 2001 From: Christoph Ostarek Date: Sun, 2 Apr 2023 20:29:27 +0200 Subject: [PATCH 4/7] controllerdns: regression test for OOB Signed-off-by: Christoph Ostarek --- pkg/pillar/cmd/nim/controllerdns_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 pkg/pillar/cmd/nim/controllerdns_test.go diff --git a/pkg/pillar/cmd/nim/controllerdns_test.go b/pkg/pillar/cmd/nim/controllerdns_test.go new file mode 100644 index 0000000000..f2b2955d5a --- /dev/null +++ b/pkg/pillar/cmd/nim/controllerdns_test.go @@ -0,0 +1,22 @@ +package nim + +import ( + "github.com/lf-edge/eve/pkg/pillar/base" + "github.com/lf-edge/eve/pkg/pillar/dpcmanager" + "github.com/sirupsen/logrus" + "testing" +) + +func TestControllerDNSCacheIndexOutOfRange(t *testing.T) { + // Regression test for bug introduced by switching to miekg/dns + var n nim + + dpcManager := dpcmanager.DpcManager{} + n.dpcManager = &dpcManager + logger := logrus.StandardLogger() + log := base.NewSourceLogObject(logger, "zedagent", 1234) + n.Logger = logger + n.Log = log + + n.controllerDNSCache([]byte("/etc/hosts"), []byte("1.1"), "") +} From 796c0c440f64d47d532bac33b302f7fa1df6bd3b Mon Sep 17 00:00:00 2001 From: Christoph Ostarek Date: Sun, 2 Apr 2023 20:34:00 +0200 Subject: [PATCH 5/7] controllerdns: fix OOB if the host cannot be resolved and empty array would be returned. In the following line the first element would be dereferenced, but that does not exist and so the process would panic Signed-off-by: Christoph Ostarek --- pkg/pillar/cmd/nim/controllerdns.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/pillar/cmd/nim/controllerdns.go b/pkg/pillar/cmd/nim/controllerdns.go index 62cf755389..641cab1eb5 100644 --- a/pkg/pillar/cmd/nim/controllerdns.go +++ b/pkg/pillar/cmd/nim/controllerdns.go @@ -110,13 +110,14 @@ func (n *nim) controllerDNSCache( var lookupIPaddr string dnsResponses := n.resolveWithPorts(string(controllerServer)) - ttlSec = int(dnsResponses[0].TTL) - lookupIPaddr = dnsResponses[0].IP.String() - serverEntry := fmt.Sprintf("%s %s\n", lookupIPaddr, controllerServer) - newhosts = append(etchosts, []byte(serverEntry)...) if len(dnsResponses) == 0 { newhosts = append(newhosts, etchosts...) + } else { + ttlSec = int(dnsResponses[0].TTL) + lookupIPaddr = dnsResponses[0].IP.String() + serverEntry := fmt.Sprintf("%s %s\n", lookupIPaddr, controllerServer) + newhosts = append(etchosts, []byte(serverEntry)...) } if len(dnsResponses) > 0 && n.writeHostsFile(newhosts) { From d30506ec9fd5e5c7c63ee22b3916278c2cfde484 Mon Sep 17 00:00:00 2001 From: Christoph Ostarek Date: Mon, 3 Apr 2023 14:18:44 +0200 Subject: [PATCH 6/7] controllerdns: move creating hosts file content into writeHostsFile method Signed-off-by: Christoph Ostarek --- pkg/pillar/cmd/nim/controllerdns.go | 57 +++++++++++++++--------- pkg/pillar/cmd/nim/controllerdns_test.go | 55 +++++++++++++++++++++-- 2 files changed, 88 insertions(+), 24 deletions(-) diff --git a/pkg/pillar/cmd/nim/controllerdns.go b/pkg/pillar/cmd/nim/controllerdns.go index 641cab1eb5..a83aeac71e 100644 --- a/pkg/pillar/cmd/nim/controllerdns.go +++ b/pkg/pillar/cmd/nim/controllerdns.go @@ -105,42 +105,59 @@ func (n *nim) controllerDNSCache( n.Log.Warnf("%s exists but removing failed: %+v", tmpHostFileName, err) } - var newhosts []byte + dnsResponses := n.resolveWithPorts(string(controllerServer)) + + lookupIPaddr := n.writeHostsFile(dnsResponses, etchosts, controllerServer) + if lookupIPaddr != "" { + n.Log.Tracef("append controller IP %s to /etc/hosts", lookupIPaddr) + } + var ttlSec int - var lookupIPaddr string - dnsResponses := n.resolveWithPorts(string(controllerServer)) + if len(dnsResponses) > 0 { + ipaddrCached = dnsResponses[0].IP.String() + ttlSec = getTTL(time.Duration(dnsResponses[0].TTL)) + return ipaddrCached, ttlSec + } else { + return "", ttlSec + } +} + +func (n *nim) writeHostsFile( + dnsResponses []devicenetwork.DNSResponse, + etchosts, controllerServer []byte, +) string { + return n.writeHostsFileToDestination(dnsResponses, etchosts, controllerServer, etcHostFileName) +} + +func (n *nim) writeHostsFileToDestination( + dnsResponses []devicenetwork.DNSResponse, + etchosts, controllerServer []byte, + destination string, +) string { + var newhosts []byte + + var lookupIPaddr string if len(dnsResponses) == 0 { newhosts = append(newhosts, etchosts...) } else { - ttlSec = int(dnsResponses[0].TTL) lookupIPaddr = dnsResponses[0].IP.String() serverEntry := fmt.Sprintf("%s %s\n", lookupIPaddr, controllerServer) newhosts = append(etchosts, []byte(serverEntry)...) } - if len(dnsResponses) > 0 && n.writeHostsFile(newhosts) { - n.Log.Tracef("append controller IP %s to /etc/hosts", lookupIPaddr) - ipaddrCached = lookupIPaddr - } else { - ipaddrCached = "" - } - - return ipaddrCached, ttlSec -} - -func (n *nim) writeHostsFile(newhosts []byte) bool { err := os.WriteFile(tmpHostFileName, newhosts, 0644) if err != nil { n.Log.Errorf("can not write /tmp/etchosts file %v", err) - return false + return "" } - if err := os.Rename(tmpHostFileName, etcHostFileName); err != nil { - n.Log.Errorf("can not rename /etc/hosts file %v", err) - return false + if err := os.Rename(tmpHostFileName, destination); err != nil { + n.Log.Errorf("can not rename %s file %v", destination, err) + return "" } - return true + + return lookupIPaddr } func (*nim) readNameservers() []string { diff --git a/pkg/pillar/cmd/nim/controllerdns_test.go b/pkg/pillar/cmd/nim/controllerdns_test.go index f2b2955d5a..8c1215cdeb 100644 --- a/pkg/pillar/cmd/nim/controllerdns_test.go +++ b/pkg/pillar/cmd/nim/controllerdns_test.go @@ -1,14 +1,17 @@ package nim import ( + "io" + "os" + "testing" + "github.com/lf-edge/eve/pkg/pillar/base" + "github.com/lf-edge/eve/pkg/pillar/devicenetwork" "github.com/lf-edge/eve/pkg/pillar/dpcmanager" "github.com/sirupsen/logrus" - "testing" ) -func TestControllerDNSCacheIndexOutOfRange(t *testing.T) { - // Regression test for bug introduced by switching to miekg/dns +func createTestNim() *nim { var n nim dpcManager := dpcmanager.DpcManager{} @@ -18,5 +21,49 @@ func TestControllerDNSCacheIndexOutOfRange(t *testing.T) { n.Logger = logger n.Log = log - n.controllerDNSCache([]byte("/etc/hosts"), []byte("1.1"), "") + return &n +} + +func TestControllerDNSCacheIndexOutOfRange(t *testing.T) { + // Regression test for bug introduced by switching to miekg/dns + n := createTestNim() + + n.controllerDNSCache([]byte(""), []byte("1.1"), "") +} + +func TestWriteHostsFile(t *testing.T) { + n := createTestNim() + + dnsResponses := []devicenetwork.DNSResponse{ + { + IP: []byte{1, 1, 1, 1}, + }, + { + IP: []byte{1, 0, 0, 1}, + }, + } + + f, err := os.CreateTemp("", "writeHostsFile.*.etchosts") + if err != nil { + panic(err) + } + defer os.Remove(f.Name()) + f.Close() + + n.writeHostsFileToDestination(dnsResponses, []byte{}, []byte("one.one.one.one"), f.Name()) + + // reopen the file to be able to read what has been written by writeHostsFileToDestination; f.Seek(0, 0) unfortunately is not enough + f, err = os.Open(f.Name()) + if err != nil { + panic(err) + } + content, err := io.ReadAll(f) + if err != nil { + panic(err) + } + + expectedContent := "1.1.1.1 one.one.one.one\n" + if string(content) != expectedContent { + t.Fatalf("writing to hosts file failed, expected: '%s', got: '%s'", expectedContent, content) + } } From 08f27e192a4cafdc7f509af14f8203744f8188ae Mon Sep 17 00:00:00 2001 From: Christoph Ostarek Date: Mon, 3 Apr 2023 19:18:55 +0200 Subject: [PATCH 7/7] controllerdns: save second DNS A-record as well if as host has two ip addresses, both will be written to /etc/hosts; use `getent ahostsv4 ` to query them Signed-off-by: Christoph Ostarek --- pkg/pillar/cmd/nim/controllerdns.go | 14 +++++++++++--- pkg/pillar/cmd/nim/controllerdns_test.go | 18 ++++++++++++++---- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/pkg/pillar/cmd/nim/controllerdns.go b/pkg/pillar/cmd/nim/controllerdns.go index a83aeac71e..6e30f3dce7 100644 --- a/pkg/pillar/cmd/nim/controllerdns.go +++ b/pkg/pillar/cmd/nim/controllerdns.go @@ -106,6 +106,11 @@ func (n *nim) controllerDNSCache( } dnsResponses := n.resolveWithPorts(string(controllerServer)) + for _, dnsResponse := range dnsResponses { + if dnsResponse.IP.String() == ipAddrCached { + return ipAddrCached, getTTL(time.Duration(dnsResponse.TTL)) + } + } lookupIPaddr := n.writeHostsFile(dnsResponses, etchosts, controllerServer) if lookupIPaddr != "" { @@ -142,9 +147,12 @@ func (n *nim) writeHostsFileToDestination( if len(dnsResponses) == 0 { newhosts = append(newhosts, etchosts...) } else { - lookupIPaddr = dnsResponses[0].IP.String() - serverEntry := fmt.Sprintf("%s %s\n", lookupIPaddr, controllerServer) - newhosts = append(etchosts, []byte(serverEntry)...) + newhosts = append([]byte{}, etchosts...) + for _, dnsResponse := range dnsResponses { + lookupIPaddr = dnsResponse.IP.String() + serverEntry := fmt.Sprintf("%s %s\n", lookupIPaddr, controllerServer) + newhosts = append(newhosts, []byte(serverEntry)...) + } } err := os.WriteFile(tmpHostFileName, newhosts, 0644) diff --git a/pkg/pillar/cmd/nim/controllerdns_test.go b/pkg/pillar/cmd/nim/controllerdns_test.go index 8c1215cdeb..75c3e39579 100644 --- a/pkg/pillar/cmd/nim/controllerdns_test.go +++ b/pkg/pillar/cmd/nim/controllerdns_test.go @@ -1,8 +1,10 @@ package nim import ( + "fmt" "io" "os" + "strings" "testing" "github.com/lf-edge/eve/pkg/pillar/base" @@ -43,6 +45,8 @@ func TestWriteHostsFile(t *testing.T) { }, } + dnsName := "one.one.one.one" + f, err := os.CreateTemp("", "writeHostsFile.*.etchosts") if err != nil { panic(err) @@ -50,7 +54,7 @@ func TestWriteHostsFile(t *testing.T) { defer os.Remove(f.Name()) f.Close() - n.writeHostsFileToDestination(dnsResponses, []byte{}, []byte("one.one.one.one"), f.Name()) + n.writeHostsFileToDestination(dnsResponses, []byte{}, []byte(dnsName), f.Name()) // reopen the file to be able to read what has been written by writeHostsFileToDestination; f.Seek(0, 0) unfortunately is not enough f, err = os.Open(f.Name()) @@ -62,8 +66,14 @@ func TestWriteHostsFile(t *testing.T) { panic(err) } - expectedContent := "1.1.1.1 one.one.one.one\n" - if string(content) != expectedContent { - t.Fatalf("writing to hosts file failed, expected: '%s', got: '%s'", expectedContent, content) + for _, dnsResponse := range dnsResponses { + expectedContent := fmt.Sprintf("%s %s\n", dnsResponse.IP.String(), dnsName) + if !strings.Contains(string(content), expectedContent) { + t.Fatalf( + "writing to hosts file failed, expected: '%s', got: '%s'", + expectedContent, + content, + ) + } } }