From 56a6ea60152e97ff540b3a71253f089805e42edd Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 29 Sep 2014 16:34:39 +0100 Subject: [PATCH 001/145] Initial commit --- .gitignore | 24 +++++++ LICENSE | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..daf913b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# 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 diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..e06d2081 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + From 2041f18fa19b7132667cc4120bd05779a656b5e8 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 29 Sep 2014 16:14:37 +0100 Subject: [PATCH 002/145] About the noddiest thing that works. --- dns/main.go | 34 +++++++++++++++++++++++++ src/weavedns/http.go | 26 +++++++++++++++++++ src/weavedns/zone.go | 60 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 dns/main.go create mode 100644 src/weavedns/http.go create mode 100644 src/weavedns/zone.go diff --git a/dns/main.go b/dns/main.go new file mode 100644 index 00000000..8107bc38 --- /dev/null +++ b/dns/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "github.com/miekg/dns" + "net" + "weavedns" + "log" +) + +var zone = new(weavedns.ZoneDb) + +func handleLocal(w dns.ResponseWriter, r *dns.Msg) { + m := new(dns.Msg) + m.SetReply(r) + q := r.Question[0] + ip, err := zone.MatchLocal(q.Name) + if err == nil { + hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeA, + Class: dns.ClassINET, Ttl: 3600} + a := &dns.A{hdr, net.ParseIP(ip)} + m.Answer = append(m.Answer, a) + } else { + log.Printf("Failed lookup for %s", q.Name) + } + w.WriteMsg(m) + return +} + +func main() { + LocalServeMux := dns.NewServeMux() + LocalServeMux.HandleFunc("local", handleLocal) + go weavedns.ListenHttp(zone) + dns.ListenAndServe(":5300", "udp", LocalServeMux) +} diff --git a/src/weavedns/http.go b/src/weavedns/http.go new file mode 100644 index 00000000..3f598ee3 --- /dev/null +++ b/src/weavedns/http.go @@ -0,0 +1,26 @@ +package weavedns + +import ( + "net/http" + "fmt" + "log" +) + +const ( + UPDATE_PORT = 6785 +) + +func ListenHttp(db Zone) { + http.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) { + name := r.FormValue("name") + addr := r.FormValue("ip") + weave_ip := r.FormValue("weave_ip") + log.Printf("Adding %s %s %s", name, addr, weave_ip) + db.AddRecord(name, addr, weave_ip) + }) + address := fmt.Sprintf(":%d", UPDATE_PORT) + err := http.ListenAndServe(address, nil) + if err != nil { + log.Fatal("Unable to create http listener: ", err) + } +} diff --git a/src/weavedns/zone.go b/src/weavedns/zone.go new file mode 100644 index 00000000..605f5621 --- /dev/null +++ b/src/weavedns/zone.go @@ -0,0 +1,60 @@ +package weavedns + +import ( + "sync" + "log" + "github.com/miekg/dns" +) + +type Zone interface { + AddRecord(name string, ip string, weave_ip string) error + MatchLocal(name string) (string, error) + MatchGlobal(name string) (string, error) +} + +type Record struct { + Name string + Ip string + WeaveIp string +} + +type ZoneDb struct { + mx sync.RWMutex + recs []Record +} + +type LookupError string + +func (ops LookupError) Error() string { + return string(ops) +} + +// Stop gap. +func (zone ZoneDb) match(name string) (string, error) { + for _, r := range zone.recs { + log.Printf("%s == %s ?", r.Name, name) + if r.Name == name { + return r.WeaveIp, nil + } + } + return "", LookupError(name) +} + +func (zone *ZoneDb) MatchLocal(name string) (string, error) { + zone.mx.Lock() + defer zone.mx.Unlock() + return zone.match(name) +} + +func (zone *ZoneDb) MatchGlobal(name string) (string, error) { + zone.mx.Lock() + defer zone.mx.Unlock() + return zone.match(name) +} + +func (zone *ZoneDb) AddRecord(name string, ip string, weave_ip string) error { + zone.mx.Lock() + defer zone.mx.Unlock() + zone.recs = append(zone.recs, Record{dns.Fqdn(name), ip, weave_ip}) + return nil +} From 7c4e4c871684e2302011b4ba134cb246941573c1 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 30 Sep 2014 16:52:46 +0100 Subject: [PATCH 003/145] Rearranged files to live in GOPATH properly --- dns/main.go | 2 +- src/weavedns/http.go => http.go | 0 src/weavedns/zone.go => zone.go | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename src/weavedns/http.go => http.go (100%) rename src/weavedns/zone.go => zone.go (100%) diff --git a/dns/main.go b/dns/main.go index 8107bc38..57d62722 100644 --- a/dns/main.go +++ b/dns/main.go @@ -3,7 +3,7 @@ package main import ( "github.com/miekg/dns" "net" - "weavedns" + "github.com/zettio/weavedns" "log" ) diff --git a/src/weavedns/http.go b/http.go similarity index 100% rename from src/weavedns/http.go rename to http.go diff --git a/src/weavedns/zone.go b/zone.go similarity index 100% rename from src/weavedns/zone.go rename to zone.go From 2039ede5113b39b4b023054c1f271852f409d259 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 1 Oct 2014 14:52:02 +0100 Subject: [PATCH 004/145] gofmt --- http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http.go b/http.go index 3f598ee3..47928355 100644 --- a/http.go +++ b/http.go @@ -1,9 +1,9 @@ package weavedns import ( - "net/http" "fmt" "log" + "net/http" ) const ( From 10421b501c3d9b4bd29c0e5cbc1de7aa21d37b25 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 1 Oct 2014 14:52:29 +0100 Subject: [PATCH 005/145] Sanity-check request --- http.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/http.go b/http.go index 47928355..aaa49e0b 100644 --- a/http.go +++ b/http.go @@ -15,8 +15,12 @@ func ListenHttp(db Zone) { name := r.FormValue("name") addr := r.FormValue("ip") weave_ip := r.FormValue("weave_ip") - log.Printf("Adding %s %s %s", name, addr, weave_ip) - db.AddRecord(name, addr, weave_ip) + if name != "" && addr != "" && weave_ip != "" { + log.Printf("Adding %s, %s, %s", name, addr, weave_ip) + db.AddRecord(name, addr, weave_ip) + } else { + log.Printf("Invalid request: %s, %s, %s", name, addr, weave_ip) + } }) address := fmt.Sprintf(":%d", UPDATE_PORT) err := http.ListenAndServe(address, nil) From 721038cbef8896b52011eca8aec6ceb9c98e790f Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 1 Oct 2014 14:59:07 +0100 Subject: [PATCH 006/145] gofmt --- dns/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns/main.go b/dns/main.go index 57d62722..b050b89c 100644 --- a/dns/main.go +++ b/dns/main.go @@ -2,9 +2,9 @@ package main import ( "github.com/miekg/dns" - "net" "github.com/zettio/weavedns" "log" + "net" ) var zone = new(weavedns.ZoneDb) From f17c684cc907fae4d0f563e583ddd2c16e74a85b Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 1 Oct 2014 14:59:38 +0100 Subject: [PATCH 007/145] Only send response if we have one --- dns/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns/main.go b/dns/main.go index b050b89c..a50815bd 100644 --- a/dns/main.go +++ b/dns/main.go @@ -19,10 +19,10 @@ func handleLocal(w dns.ResponseWriter, r *dns.Msg) { Class: dns.ClassINET, Ttl: 3600} a := &dns.A{hdr, net.ParseIP(ip)} m.Answer = append(m.Answer, a) + w.WriteMsg(m) } else { log.Printf("Failed lookup for %s", q.Name) } - w.WriteMsg(m) return } From dddb542a83885231ad3490c07c0509d5e1ad14d6 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 2 Oct 2014 14:19:47 +0100 Subject: [PATCH 008/145] go fmt --- zone.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zone.go b/zone.go index 605f5621..001e84d3 100644 --- a/zone.go +++ b/zone.go @@ -1,9 +1,9 @@ package weavedns import ( - "sync" - "log" "github.com/miekg/dns" + "log" + "sync" ) type Zone interface { @@ -13,13 +13,13 @@ type Zone interface { } type Record struct { - Name string - Ip string + Name string + Ip string WeaveIp string } type ZoneDb struct { - mx sync.RWMutex + mx sync.RWMutex recs []Record } From b87a4efff72285c464b72f5fe84812d663a3590b Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 2 Oct 2014 14:20:41 +0100 Subject: [PATCH 009/145] Pass zone by pointer not value in match() --- zone.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone.go b/zone.go index 001e84d3..1336adb1 100644 --- a/zone.go +++ b/zone.go @@ -30,7 +30,7 @@ func (ops LookupError) Error() string { } // Stop gap. -func (zone ZoneDb) match(name string) (string, error) { +func (zone *ZoneDb) match(name string) (string, error) { for _, r := range zone.recs { log.Printf("%s == %s ?", r.Name, name) if r.Name == name { From 0c4b5105fcec45694c7c17cb6e485e54607814a2 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 2 Oct 2014 15:01:14 +0100 Subject: [PATCH 010/145] Added multicast DNS client --- mdns_client.go | 117 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 mdns_client.go diff --git a/mdns_client.go b/mdns_client.go new file mode 100644 index 00000000..c9ee138f --- /dev/null +++ b/mdns_client.go @@ -0,0 +1,117 @@ +package weavedns + +import ( + "github.com/miekg/dns" + "log" + "net" + "sync" +) + +// Portions of this code taken from github.com/armon/mdns + +const ( + ipv4mdns = "224.0.0.251" // link-local multicast address + mdnsPort = 5353 // mDNS assigned port +) + +var ( + ipv4Addr = &net.UDPAddr{ + IP: net.ParseIP(ipv4mdns), + Port: mdnsPort, + } +) + +type ResponseA struct { + Name string + Addr net.IP +} + +type responseInfo struct { + ch chan<- *ResponseA +} + +// Represents one query that we have sent for one name. +// If we, internally, get several requests for the same name while we have +// a query in flight, then we don't want to send more queries out. +type inflightQuery struct { + name string + Id uint16 // the DNS message ID + responseInfos []*responseInfo + // add timeout here ? +} + +type MDNSClient struct { + sync.RWMutex + conn *net.UDPConn + addr *net.UDPAddr + inflight map[string]*inflightQuery +} + +func NewMDNSClient() (*MDNSClient, error) { + conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) + if err != nil { + log.Println(err) + return nil, err + } + retval := &MDNSClient{ + conn: conn, + addr: ipv4Addr, + inflight: make(map[string]*inflightQuery)} + return retval, nil +} + +func LinkLocalMulticastListener() (net.PacketConn, error) { + conn, err := net.ListenMulticastUDP("udp", nil, ipv4Addr) + return conn, err +} + +func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- *ResponseA) error { + m := new(dns.Msg) + m.SetQuestion(name, querytype) + m.RecursionDesired = false + + c.Lock() + query, found := c.inflight[name] + if !found { + query = &inflightQuery{name: name} + c.inflight[name] = query + } + info := &responseInfo{ch: responseCh} + query.responseInfos = append(query.responseInfos, info) + c.Unlock() + + buf, err := m.Pack() + if err != nil { + return err + } + _, err = c.conn.WriteTo(buf, c.addr) + return err +} + +func (c *MDNSClient) SendResponse(m *dns.Msg) error { + buf, err := m.Pack() + if err != nil { + return err + } + _, err = c.conn.WriteTo(buf, c.addr) + return err +} + +func (c *MDNSClient) HandleResponse(r *dns.Msg) { + for _, answer := range r.Answer { + switch rr := answer.(type) { + case *dns.A: + c.Lock() + if query, found := c.inflight[rr.Hdr.Name]; found { + for _, resp := range query.responseInfos { + resp.ch <- &ResponseA{Name: rr.Hdr.Name, Addr: rr.A} + } + // To be simple for now, assume this is the only response coming + delete(c.inflight, rr.Hdr.Name) + } else { + log.Println("Response received that didn't match query", r) + } + c.Unlock() + } + } +} From 673b420afae413e1548e3d7ea610a4d79872d482 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 2 Oct 2014 15:01:35 +0100 Subject: [PATCH 011/145] Ignore Emacs backup files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index daf913b1..24f25015 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ _testmain.go *.exe *.test *.prof + +# Emacs backup files +*~ From b49eb5c2ac75fb13ed7acf36dcdc26a52f61abf6 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 2 Oct 2014 16:12:10 +0100 Subject: [PATCH 012/145] Extract makeDNSReply function --- dns/main.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dns/main.go b/dns/main.go index a50815bd..2de4225b 100644 --- a/dns/main.go +++ b/dns/main.go @@ -9,16 +9,21 @@ import ( var zone = new(weavedns.ZoneDb) -func handleLocal(w dns.ResponseWriter, r *dns.Msg) { +func makeDNSReply(r *dns.Msg, name string, addr net.IP) *dns.Msg { m := new(dns.Msg) m.SetReply(r) + hdr := dns.RR_Header{Name: name, Rrtype: dns.TypeA, + Class: dns.ClassINET, Ttl: 3600} + a := &dns.A{hdr, addr} + m.Answer = append(m.Answer, a) + return m +} + +func handleLocal(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] ip, err := zone.MatchLocal(q.Name) if err == nil { - hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeA, - Class: dns.ClassINET, Ttl: 3600} - a := &dns.A{hdr, net.ParseIP(ip)} - m.Answer = append(m.Answer, a) + m := makeDNSReply(r, q.Name, net.ParseIP(ip)) w.WriteMsg(m) } else { log.Printf("Failed lookup for %s", q.Name) From 953a577ba29a553c22a08aefe0e1ecec7a4189d0 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 2 Oct 2014 18:06:16 +0100 Subject: [PATCH 013/145] Add multicast --- dns/main.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/dns/main.go b/dns/main.go index 2de4225b..f9182274 100644 --- a/dns/main.go +++ b/dns/main.go @@ -19,7 +19,26 @@ func makeDNSReply(r *dns.Msg, name string, addr net.IP) *dns.Msg { return m } +var mdnsClient *weavedns.MDNSClient + +func handleMDNS(w dns.ResponseWriter, r *dns.Msg) { + // Here we have to handle both requests incoming, responses to queries + // we sent out, and responses to queries that other nodes sent + // And we also get to see our own queries! + if len(r.Answer) > 0 { + mdnsClient.HandleResponse(r) + } else if len(r.Question) > 0 { + q := r.Question[0] + ip, err := zone.MatchLocal(q.Name) + if err == nil { + m := makeDNSReply(r, q.Name, net.ParseIP(ip)) + mdnsClient.SendResponse(m) + } + } +} + func handleLocal(w dns.ResponseWriter, r *dns.Msg) { + log.Printf("Received message:", r) q := r.Question[0] ip, err := zone.MatchLocal(q.Name) if err == nil { @@ -27,6 +46,17 @@ func handleLocal(w dns.ResponseWriter, r *dns.Msg) { w.WriteMsg(m) } else { log.Printf("Failed lookup for %s", q.Name) + // We don't know the answer; see if someone else does + channel := make(chan *weavedns.ResponseA, 4) + go func() { + for resp := range channel { + log.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) + m := makeDNSReply(r, resp.Name, resp.Addr) + w.WriteMsg(m) + } + // FIXME: need to add a time-out either here or in mdnsClient + }() + mdnsClient.SendQuery(q.Name, dns.TypeA, channel) } return } @@ -35,5 +65,20 @@ func main() { LocalServeMux := dns.NewServeMux() LocalServeMux.HandleFunc("local", handleLocal) go weavedns.ListenHttp(zone) + + MDNSServeMux := dns.NewServeMux() + MDNSServeMux.HandleFunc("local", handleMDNS) + + conn, err := weavedns.LinkLocalMulticastListener() + if err != nil { + log.Fatal(err) + } + mdnsClient, err = weavedns.NewMDNSClient() + if err != nil { + log.Fatal(err) + } + + go dns.ActivateAndServe(nil, conn, MDNSServeMux) + dns.ListenAndServe(":5300", "udp", LocalServeMux) } From f309afcd18f0dd7541094d0c3c48c94dd6118c0f Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 2 Oct 2014 18:07:09 +0100 Subject: [PATCH 014/145] Fix port to listen on, and add simple Dockerfile --- dns/Dockerfile | 3 +++ dns/main.go | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 dns/Dockerfile diff --git a/dns/Dockerfile b/dns/Dockerfile new file mode 100644 index 00000000..b4feddc1 --- /dev/null +++ b/dns/Dockerfile @@ -0,0 +1,3 @@ +FROM ubuntu +EXPOSE 53/udp +ADD ./dns /home/dns diff --git a/dns/main.go b/dns/main.go index f9182274..feab7ad1 100644 --- a/dns/main.go +++ b/dns/main.go @@ -80,5 +80,5 @@ func main() { go dns.ActivateAndServe(nil, conn, MDNSServeMux) - dns.ListenAndServe(":5300", "udp", LocalServeMux) + dns.ListenAndServe(":53", "udp", LocalServeMux) } From dcb89369c3dc8d95215bbf71ab4844d8487297b4 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 3 Oct 2014 13:50:12 +0100 Subject: [PATCH 015/145] Usability improvements --- dns/main.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dns/main.go b/dns/main.go index feab7ad1..76319a0f 100644 --- a/dns/main.go +++ b/dns/main.go @@ -38,7 +38,7 @@ func handleMDNS(w dns.ResponseWriter, r *dns.Msg) { } func handleLocal(w dns.ResponseWriter, r *dns.Msg) { - log.Printf("Received message:", r) + log.Printf("Received message: %s", r) q := r.Question[0] ip, err := zone.MatchLocal(q.Name) if err == nil { @@ -80,5 +80,8 @@ func main() { go dns.ActivateAndServe(nil, conn, MDNSServeMux) - dns.ListenAndServe(":53", "udp", LocalServeMux) + err = dns.ListenAndServe(":53", "udp", LocalServeMux) + if err != nil { + log.Fatal(err) + } } From eedb6a1c30b5316a972d49a138003361664e736b Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 3 Oct 2014 14:12:16 +0100 Subject: [PATCH 016/145] Add command-line flag to use a different port --- dns/main.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/dns/main.go b/dns/main.go index 76319a0f..9f1c00ea 100644 --- a/dns/main.go +++ b/dns/main.go @@ -1,6 +1,8 @@ package main import ( + "flag" + "fmt" "github.com/miekg/dns" "github.com/zettio/weavedns" "log" @@ -62,6 +64,13 @@ func handleLocal(w dns.ResponseWriter, r *dns.Msg) { } func main() { + var ( + dnsPort int + ) + + flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to dns requests (defaults to 53)") + flag.Parse() + LocalServeMux := dns.NewServeMux() LocalServeMux.HandleFunc("local", handleLocal) go weavedns.ListenHttp(zone) @@ -80,7 +89,8 @@ func main() { go dns.ActivateAndServe(nil, conn, MDNSServeMux) - err = dns.ListenAndServe(":53", "udp", LocalServeMux) + address := fmt.Sprintf(":%d", dnsPort) + err = dns.ListenAndServe(address, "udp", LocalServeMux) if err != nil { log.Fatal(err) } From c3f4c1a85f6b05c2e86139b6470078f39c954652 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 3 Oct 2014 16:42:02 +0100 Subject: [PATCH 017/145] Cleaner handling of our own multicast queries --- dns/main.go | 5 ++++- mdns_client.go | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/dns/main.go b/dns/main.go index 9f1c00ea..a1ee520c 100644 --- a/dns/main.go +++ b/dns/main.go @@ -26,7 +26,6 @@ var mdnsClient *weavedns.MDNSClient func handleMDNS(w dns.ResponseWriter, r *dns.Msg) { // Here we have to handle both requests incoming, responses to queries // we sent out, and responses to queries that other nodes sent - // And we also get to see our own queries! if len(r.Answer) > 0 { mdnsClient.HandleResponse(r) } else if len(r.Question) > 0 { @@ -35,6 +34,10 @@ func handleMDNS(w dns.ResponseWriter, r *dns.Msg) { if err == nil { m := makeDNSReply(r, q.Name, net.ParseIP(ip)) mdnsClient.SendResponse(m) + } else if mdnsClient.IsInflightQuery(r) { + // ignore this - it's our own query received via multicast + } else { + log.Printf("Failed MDNS lookup for %s", q.Name) } } } diff --git a/mdns_client.go b/mdns_client.go index c9ee138f..f8d8b479 100644 --- a/mdns_client.go +++ b/mdns_client.go @@ -73,7 +73,7 @@ func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- c.Lock() query, found := c.inflight[name] if !found { - query = &inflightQuery{name: name} + query = &inflightQuery{name: name, Id: m.Id} c.inflight[name] = query } info := &responseInfo{ch: responseCh} @@ -97,6 +97,22 @@ func (c *MDNSClient) SendResponse(m *dns.Msg) error { return err } +func (c *MDNSClient) IsInflightQuery(m *dns.Msg) bool { + if len(m.Question) == 1 { + q := m.Question[0] + if q.Qtype == dns.TypeA { + c.Lock() + defer c.Unlock() + if query, found := c.inflight[q.Name]; found { + if query.Id == m.Id { + return true + } + } + } + } + return false +} + func (c *MDNSClient) HandleResponse(r *dns.Msg) { for _, answer := range r.Answer { switch rr := answer.(type) { From d29781f9c267065880b1f6b357b87f8380ecc206 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 3 Oct 2014 16:43:33 +0100 Subject: [PATCH 018/145] One fewer Println --- mdns_client.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mdns_client.go b/mdns_client.go index f8d8b479..145cbf08 100644 --- a/mdns_client.go +++ b/mdns_client.go @@ -125,7 +125,8 @@ func (c *MDNSClient) HandleResponse(r *dns.Msg) { // To be simple for now, assume this is the only response coming delete(c.inflight, rr.Hdr.Name) } else { - log.Println("Response received that didn't match query", r) + // We've received a response that didn't match a query + // Do we want to cache it? } c.Unlock() } From 59b1db76c8bf8124f3cf92c92f5ef2140c6220c0 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Fri, 3 Oct 2014 20:08:32 +0100 Subject: [PATCH 019/145] Added Makefile and updated Dockerfile The Dockerfile is now more like the weave one, with a scratch layer underneath. Makefile taken more or less from weave, too; except, it uses environment variables to cross-compile the binary (which should probably be done for weave as well) --- dns/Dockerfile | 8 +++++--- dns/Makefile | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 dns/Makefile diff --git a/dns/Dockerfile b/dns/Dockerfile index b4feddc1..181bcbbc 100644 --- a/dns/Dockerfile +++ b/dns/Dockerfile @@ -1,3 +1,5 @@ -FROM ubuntu -EXPOSE 53/udp -ADD ./dns /home/dns +FROM scratch +# EXPOSE 53/udp +WORKDIR /home/weave +ADD ./dns /home/weave/weavedns +ENTRYPOINT ["/home/weave/weavedns"] diff --git a/dns/Makefile b/dns/Makefile new file mode 100644 index 00000000..63939da5 --- /dev/null +++ b/dns/Makefile @@ -0,0 +1,20 @@ +IMAGE:=/tmp/weavedns.tar + +.DEFAULT: docker-image + +all: docker-image + +dns: ../*.go main.go + go get && GOARCH=amd64 GOOS=linux go build -v + +.PHONY: docker-image + +docker-image: $(IMAGE) + +$(IMAGE): Dockerfile dns + sudo -E docker build --rm -t zettio/weavedns . + sudo -E docker save zettio/weavedns > $(IMAGE) + +clean: + -sudo docker rmi zettio/weavedns + rm -f dns $(IMAGE) From 9dcb7e2d73944d702f7cde5301de1ea6f703a4e5 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Fri, 3 Oct 2014 20:55:53 +0100 Subject: [PATCH 020/145] Separated dockerising from code --- {dns => docker}/Dockerfile | 0 {dns => docker}/Makefile | 0 {dns => main}/main.go | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {dns => docker}/Dockerfile (100%) rename {dns => docker}/Makefile (100%) rename {dns => main}/main.go (100%) diff --git a/dns/Dockerfile b/docker/Dockerfile similarity index 100% rename from dns/Dockerfile rename to docker/Dockerfile diff --git a/dns/Makefile b/docker/Makefile similarity index 100% rename from dns/Makefile rename to docker/Makefile diff --git a/dns/main.go b/main/main.go similarity index 100% rename from dns/main.go rename to main/main.go From 1ff9f7e9b757e3d13e911b872ba9e09689c84670 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Fri, 3 Oct 2014 21:01:45 +0100 Subject: [PATCH 021/145] Reformed Dockerfile and Makefile to account for moves --- docker/Dockerfile | 2 +- docker/Makefile | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 181bcbbc..de94f53e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ FROM scratch # EXPOSE 53/udp WORKDIR /home/weave -ADD ./dns /home/weave/weavedns +ADD ./weavedns /home/weave/ ENTRYPOINT ["/home/weave/weavedns"] diff --git a/docker/Makefile b/docker/Makefile index 63939da5..d7f24eb0 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -1,20 +1,21 @@ IMAGE:=/tmp/weavedns.tar +MAIN:=../main .DEFAULT: docker-image all: docker-image -dns: ../*.go main.go - go get && GOARCH=amd64 GOOS=linux go build -v +dns: ../*.go ../main/main.go + go get $(MAIN) && GOARCH=amd64 GOOS=linux go build -o weavedns -v $(MAIN) .PHONY: docker-image docker-image: $(IMAGE) $(IMAGE): Dockerfile dns - sudo -E docker build --rm -t zettio/weavedns . + sudo -E docker build -t zettio/weavedns . sudo -E docker save zettio/weavedns > $(IMAGE) clean: -sudo docker rmi zettio/weavedns - rm -f dns $(IMAGE) + rm -f weavedns $(IMAGE) From 3976e5ceaf63621365f200accee776f015ee962a Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Sat, 4 Oct 2014 14:51:46 +0100 Subject: [PATCH 022/145] Moved main to top, libs to subpackage --- main/main.go => main.go | 0 http.go => server/http.go | 0 mdns_client.go => server/mdns_client.go | 0 zone.go => server/zone.go | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename main/main.go => main.go (100%) rename http.go => server/http.go (100%) rename mdns_client.go => server/mdns_client.go (100%) rename zone.go => server/zone.go (100%) diff --git a/main/main.go b/main.go similarity index 100% rename from main/main.go rename to main.go diff --git a/http.go b/server/http.go similarity index 100% rename from http.go rename to server/http.go diff --git a/mdns_client.go b/server/mdns_client.go similarity index 100% rename from mdns_client.go rename to server/mdns_client.go diff --git a/zone.go b/server/zone.go similarity index 100% rename from zone.go rename to server/zone.go From bc5bbd349a588e0b11acdb0a2c9605600d288f5b Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Sat, 4 Oct 2014 15:23:32 +0100 Subject: [PATCH 023/145] Fix up Makefile and main.go for file moves --- docker/Makefile | 8 ++++---- main.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/Makefile b/docker/Makefile index d7f24eb0..c4e1a207 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -1,18 +1,18 @@ IMAGE:=/tmp/weavedns.tar -MAIN:=../main +MAIN:=../ .DEFAULT: docker-image all: docker-image -dns: ../*.go ../main/main.go - go get $(MAIN) && GOARCH=amd64 GOOS=linux go build -o weavedns -v $(MAIN) +weavedns: ../server/*.go ../main.go + go get $(MAIN) && GOARCH=amd64 GOOS=linux go build -o $@ -v $(MAIN) .PHONY: docker-image docker-image: $(IMAGE) -$(IMAGE): Dockerfile dns +$(IMAGE): Dockerfile weavedns sudo -E docker build -t zettio/weavedns . sudo -E docker save zettio/weavedns > $(IMAGE) diff --git a/main.go b/main.go index a1ee520c..076d2bcf 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,7 @@ import ( "flag" "fmt" "github.com/miekg/dns" - "github.com/zettio/weavedns" + "github.com/zettio/weavedns/server" "log" "net" ) From 61969ae92ca0a8682097f297f2587e60ea807ae7 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 6 Oct 2014 07:35:21 +0100 Subject: [PATCH 024/145] Wait for interface --- dns/main.go | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/dns/main.go b/dns/main.go index a1ee520c..3343f8e0 100644 --- a/dns/main.go +++ b/dns/main.go @@ -7,6 +7,7 @@ import ( "github.com/zettio/weavedns" "log" "net" + "time" ) var zone = new(weavedns.ZoneDb) @@ -24,6 +25,7 @@ func makeDNSReply(r *dns.Msg, name string, addr net.IP) *dns.Msg { var mdnsClient *weavedns.MDNSClient func handleMDNS(w dns.ResponseWriter, r *dns.Msg) { + log.Println("MDNS received:", r) // Here we have to handle both requests incoming, responses to queries // we sent out, and responses to queries that other nodes sent if len(r.Answer) > 0 { @@ -50,7 +52,7 @@ func handleLocal(w dns.ResponseWriter, r *dns.Msg) { m := makeDNSReply(r, q.Name, net.ParseIP(ip)) w.WriteMsg(m) } else { - log.Printf("Failed lookup for %s", q.Name) + log.Printf("Failed lookup for %s; sending mDNS query", q.Name) // We don't know the answer; see if someone else does channel := make(chan *weavedns.ResponseA, 4) go func() { @@ -66,14 +68,49 @@ func handleLocal(w dns.ResponseWriter, r *dns.Msg) { return } +func ensureInterface(ifaceName string, wait int) (iface *net.Interface, err error) { + iface, err = findInterface(ifaceName) + if err == nil || wait == 0 { + return + } + log.Println("Waiting for interface", ifaceName, "to come up") + for ; err != nil && wait > 0; wait -= 1 { + time.Sleep(1 * time.Second) + iface, err = findInterface(ifaceName) + } + if err == nil { + log.Println("Interface", ifaceName, "is up") + } + return +} + +func findInterface(ifaceName string) (iface *net.Interface, err error) { + iface, err = net.InterfaceByName(ifaceName) + if err != nil { + return iface, fmt.Errorf("Unable to find interface %s", ifaceName) + } + if 0 == (net.FlagUp & iface.Flags) { + return iface, fmt.Errorf("Interface %s is not up", ifaceName) + } + return +} + func main() { var ( dnsPort int + wait int ) + flag.IntVar(&wait, "wait", 5, "number of seconds to wait for interface to be created and come up (defaults to 5)") flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to dns requests (defaults to 53)") flag.Parse() + ifaceName := "ethwe" + _, err := ensureInterface(ifaceName, wait) + if err != nil { + log.Fatal(err) + } + LocalServeMux := dns.NewServeMux() LocalServeMux.HandleFunc("local", handleLocal) go weavedns.ListenHttp(zone) From 88618b38de9fd87935bebaab9f69db05c9371c0e Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 6 Oct 2014 07:36:18 +0100 Subject: [PATCH 025/145] Make container start automatically --- dns/Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dns/Dockerfile b/dns/Dockerfile index b4feddc1..99f92503 100644 --- a/dns/Dockerfile +++ b/dns/Dockerfile @@ -1,3 +1,6 @@ FROM ubuntu +MAINTAINER Zettio weave@zett.io +WORKDIR /home/weave +ADD ./dns /home/weave/ EXPOSE 53/udp -ADD ./dns /home/dns +CMD ["/home/weave/dns"] From 69c415741ec1ebeb02e7a5dcdc15946284efe520 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 6 Oct 2014 13:37:09 +0100 Subject: [PATCH 026/145] Take interface name as parameter; don't assume ethwe --- main.go | 21 +++++++++++++-------- server/mdns_client.go | 4 ++-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/main.go b/main.go index b647f477..ad2ab95b 100644 --- a/main.go +++ b/main.go @@ -97,18 +97,23 @@ func findInterface(ifaceName string) (iface *net.Interface, err error) { func main() { var ( - dnsPort int - wait int + ifaceName string + dnsPort int + wait int ) - flag.IntVar(&wait, "wait", 5, "number of seconds to wait for interface to be created and come up (defaults to 5)") + flag.StringVar(&ifaceName, "iface", "", "name of interface to use for multicast") + flag.IntVar(&wait, "wait", 0, "number of seconds to wait for interface to be created and come up (defaults to 0)") flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to dns requests (defaults to 53)") flag.Parse() - ifaceName := "ethwe" - _, err := ensureInterface(ifaceName, wait) - if err != nil { - log.Fatal(err) + var iface *net.Interface = nil + if ifaceName != "" { + var err error + iface, err = ensureInterface(ifaceName, wait) + if err != nil { + log.Fatal(err) + } } LocalServeMux := dns.NewServeMux() @@ -118,7 +123,7 @@ func main() { MDNSServeMux := dns.NewServeMux() MDNSServeMux.HandleFunc("local", handleMDNS) - conn, err := weavedns.LinkLocalMulticastListener() + conn, err := weavedns.LinkLocalMulticastListener(iface) if err != nil { log.Fatal(err) } diff --git a/server/mdns_client.go b/server/mdns_client.go index 145cbf08..7a813288 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -60,8 +60,8 @@ func NewMDNSClient() (*MDNSClient, error) { return retval, nil } -func LinkLocalMulticastListener() (net.PacketConn, error) { - conn, err := net.ListenMulticastUDP("udp", nil, ipv4Addr) +func LinkLocalMulticastListener(ifi *net.Interface) (net.PacketConn, error) { + conn, err := net.ListenMulticastUDP("udp", ifi, ipv4Addr) return conn, err } From 574bc2cb9ee0ebaa17e493fad3f78a10f6637195 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 6 Oct 2014 17:56:50 +0100 Subject: [PATCH 027/145] Refactor MDNSClient as Actor --- main.go | 3 +- server/mdns_client.go | 140 ++++++++++++++++++++++++++++++++---------- 2 files changed, 109 insertions(+), 34 deletions(-) diff --git a/main.go b/main.go index ad2ab95b..757242b7 100644 --- a/main.go +++ b/main.go @@ -29,7 +29,7 @@ func handleMDNS(w dns.ResponseWriter, r *dns.Msg) { // Here we have to handle both requests incoming, responses to queries // we sent out, and responses to queries that other nodes sent if len(r.Answer) > 0 { - mdnsClient.HandleResponse(r) + mdnsClient.ResponseCallback(r) } else if len(r.Question) > 0 { q := r.Question[0] ip, err := zone.MatchLocal(q.Name) @@ -131,6 +131,7 @@ func main() { if err != nil { log.Fatal(err) } + mdnsClient.Start() go dns.ActivateAndServe(nil, conn, MDNSServeMux) diff --git a/server/mdns_client.go b/server/mdns_client.go index 7a813288..36f40211 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -4,7 +4,6 @@ import ( "github.com/miekg/dns" "log" "net" - "sync" ) // Portions of this code taken from github.com/armon/mdns @@ -41,10 +40,16 @@ type inflightQuery struct { } type MDNSClient struct { - sync.RWMutex - conn *net.UDPConn - addr *net.UDPAddr - inflight map[string]*inflightQuery + conn *net.UDPConn + addr *net.UDPAddr + inflight map[string]*inflightQuery + queryChan chan<- *MDNSInteraction +} + +type mDNSQueryInfo struct { + name string + querytype uint16 + responseCh chan<- *ResponseA } func NewMDNSClient() (*MDNSClient, error) { @@ -60,34 +65,17 @@ func NewMDNSClient() (*MDNSClient, error) { return retval, nil } +func (c *MDNSClient) Start() { + queryChan := make(chan *MDNSInteraction, 4) + c.queryChan = queryChan + go c.queryLoop(queryChan) +} + func LinkLocalMulticastListener(ifi *net.Interface) (net.PacketConn, error) { conn, err := net.ListenMulticastUDP("udp", ifi, ipv4Addr) return conn, err } -func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- *ResponseA) error { - m := new(dns.Msg) - m.SetQuestion(name, querytype) - m.RecursionDesired = false - - c.Lock() - query, found := c.inflight[name] - if !found { - query = &inflightQuery{name: name, Id: m.Id} - c.inflight[name] = query - } - info := &responseInfo{ch: responseCh} - query.responseInfos = append(query.responseInfos, info) - c.Unlock() - - buf, err := m.Pack() - if err != nil { - return err - } - _, err = c.conn.WriteTo(buf, c.addr) - return err -} - func (c *MDNSClient) SendResponse(m *dns.Msg) error { buf, err := m.Pack() if err != nil { @@ -97,12 +85,100 @@ func (c *MDNSClient) SendResponse(m *dns.Msg) error { return err } +// ACTOR client API + +const ( + CSendQuery = iota + CShutdown = iota + CIsInflightQuery = iota + CMessageReceived = iota +) + +type Interaction struct { + code int + resultChan chan<- interface{} +} + +type MDNSInteraction struct { + Interaction + payload interface{} +} + +// Async +func (c *MDNSClient) Shutdown() { + c.queryChan <- &MDNSInteraction{Interaction: Interaction{code: CShutdown}} +} + +// Async +func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- *ResponseA) { + c.queryChan <- &MDNSInteraction{Interaction: Interaction{code: CSendQuery}, payload: mDNSQueryInfo{name, querytype, responseCh}} +} + +// Sync func (c *MDNSClient) IsInflightQuery(m *dns.Msg) bool { + resultChan := make(chan interface{}) + c.queryChan <- &MDNSInteraction{Interaction: Interaction{code: CIsInflightQuery, resultChan: resultChan}, payload: m} + result := <-resultChan + return result.(bool) +} + +// Async - called from dns library multiplexer +func (c *MDNSClient) ResponseCallback(r *dns.Msg) { + c.queryChan <- &MDNSInteraction{Interaction: Interaction{code: CMessageReceived}, payload: r} +} + +// ACTOR server + +func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { + var err error + terminate := false + for !terminate { + if err != nil { + log.Printf("encountered error", err) + break + } + query, ok := <-queryChan + if !ok { + break + } + switch query.code { + case CShutdown: + terminate = true + case CSendQuery: + err = c.handleSendQuery(query.payload.(mDNSQueryInfo)) + case CIsInflightQuery: + query.resultChan <- c.handleIsInflightQuery(query.payload.(*dns.Msg)) + case CMessageReceived: + c.handleResponse(query.payload.(*dns.Msg)) + } + } + // handle shutdown here +} + +func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) error { + m := new(dns.Msg) + m.SetQuestion(q.name, q.querytype) + m.RecursionDesired = false + query, found := c.inflight[q.name] + if !found { + query = &inflightQuery{name: q.name, Id: m.Id} + c.inflight[q.name] = query + } + info := &responseInfo{ch: q.responseCh} + query.responseInfos = append(query.responseInfos, info) + + buf, err := m.Pack() + if err != nil { + return err + } + _, err = c.conn.WriteTo(buf, c.addr) + return err +} + +func (c *MDNSClient) handleIsInflightQuery(m *dns.Msg) bool { if len(m.Question) == 1 { q := m.Question[0] if q.Qtype == dns.TypeA { - c.Lock() - defer c.Unlock() if query, found := c.inflight[q.Name]; found { if query.Id == m.Id { return true @@ -113,11 +189,10 @@ func (c *MDNSClient) IsInflightQuery(m *dns.Msg) bool { return false } -func (c *MDNSClient) HandleResponse(r *dns.Msg) { +func (c *MDNSClient) handleResponse(r *dns.Msg) { for _, answer := range r.Answer { switch rr := answer.(type) { case *dns.A: - c.Lock() if query, found := c.inflight[rr.Hdr.Name]; found { for _, resp := range query.responseInfos { resp.ch <- &ResponseA{Name: rr.Hdr.Name, Addr: rr.A} @@ -128,7 +203,6 @@ func (c *MDNSClient) HandleResponse(r *dns.Msg) { // We've received a response that didn't match a query // Do we want to cache it? } - c.Unlock() } } } From 2d142abd3ebd7cb733b9aec6c065e165a34704b0 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 6 Oct 2014 17:57:35 +0100 Subject: [PATCH 028/145] Close channel --- server/mdns_client.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/mdns_client.go b/server/mdns_client.go index 36f40211..5ba0b944 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -196,6 +196,7 @@ func (c *MDNSClient) handleResponse(r *dns.Msg) { if query, found := c.inflight[rr.Hdr.Name]; found { for _, resp := range query.responseInfos { resp.ch <- &ResponseA{Name: rr.Hdr.Name, Addr: rr.A} + close(resp.ch) } // To be simple for now, assume this is the only response coming delete(c.inflight, rr.Hdr.Name) From a78766524b0408ec6df1d6b96633df18430a2f5e Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 7 Oct 2014 11:48:56 +0100 Subject: [PATCH 029/145] Time out mDNS queries --- server/mdns_client.go | 89 +++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 29 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index 5ba0b944..069cea1c 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -3,14 +3,18 @@ package weavedns import ( "github.com/miekg/dns" "log" + "math" "net" + "time" ) // Portions of this code taken from github.com/armon/mdns const ( - ipv4mdns = "224.0.0.251" // link-local multicast address - mdnsPort = 5353 // mDNS assigned port + ipv4mdns = "224.0.0.251" // link-local multicast address + mdnsPort = 5353 // mDNS assigned port + mDNSTimeout = time.Second + MaxDuration = time.Duration(math.MaxInt64) ) var ( @@ -36,7 +40,7 @@ type inflightQuery struct { name string Id uint16 // the DNS message ID responseInfos []*responseInfo - // add timeout here ? + timeout time.Time // if no answer by this time, give up } type MDNSClient struct { @@ -130,6 +134,26 @@ func (c *MDNSClient) ResponseCallback(r *dns.Msg) { // ACTOR server func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { + timer := time.NewTimer(MaxDuration) + run := func() { + now := time.Now() + after := MaxDuration + for name, query := range c.inflight { + switch duration := query.timeout.Sub(now); { + case duration <= 0: // timed out + log.Println("Query timed out:", name) + for _, item := range query.responseInfos { + close(item.ch) + } + delete(c.inflight, name) + case duration < after: + after = duration + } + } + log.Println("Next timeout:", after) + timer.Reset(after) + } + var err error terminate := false for !terminate { @@ -137,41 +161,51 @@ func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { log.Printf("encountered error", err) break } - query, ok := <-queryChan - if !ok { - break - } - switch query.code { - case CShutdown: - terminate = true - case CSendQuery: - err = c.handleSendQuery(query.payload.(mDNSQueryInfo)) - case CIsInflightQuery: - query.resultChan <- c.handleIsInflightQuery(query.payload.(*dns.Msg)) - case CMessageReceived: - c.handleResponse(query.payload.(*dns.Msg)) + select { + case query, ok := <-queryChan: + if !ok { + break + } + switch query.code { + case CShutdown: + terminate = true + case CSendQuery: + err = c.handleSendQuery(query.payload.(mDNSQueryInfo)) + run() + case CIsInflightQuery: + query.resultChan <- c.handleIsInflightQuery(query.payload.(*dns.Msg)) + case CMessageReceived: + c.handleResponse(query.payload.(*dns.Msg)) + run() + } + case <-timer.C: + run() } } // handle shutdown here } -func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) error { - m := new(dns.Msg) - m.SetQuestion(q.name, q.querytype) - m.RecursionDesired = false +func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) (err error) { query, found := c.inflight[q.name] if !found { - query = &inflightQuery{name: q.name, Id: m.Id} + m := new(dns.Msg) + m.SetQuestion(q.name, q.querytype) + m.RecursionDesired = false + buf, err := m.Pack() + if err != nil { + return err + } + query = &inflightQuery{ + name: q.name, + Id: m.Id, + timeout: time.Now().Add(mDNSTimeout), + } c.inflight[q.name] = query + _, err = c.conn.WriteTo(buf, c.addr) } info := &responseInfo{ch: q.responseCh} query.responseInfos = append(query.responseInfos, info) - buf, err := m.Pack() - if err != nil { - return err - } - _, err = c.conn.WriteTo(buf, c.addr) return err } @@ -196,10 +230,7 @@ func (c *MDNSClient) handleResponse(r *dns.Msg) { if query, found := c.inflight[rr.Hdr.Name]; found { for _, resp := range query.responseInfos { resp.ch <- &ResponseA{Name: rr.Hdr.Name, Addr: rr.A} - close(resp.ch) } - // To be simple for now, assume this is the only response coming - delete(c.inflight, rr.Hdr.Name) } else { // We've received a response that didn't match a query // Do we want to cache it? From 2cab8415f2e3c866e8dcb1ae8ae1f8ed8fbd27b8 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 7 Oct 2014 12:10:19 +0100 Subject: [PATCH 030/145] Tidy up slightly --- server/mdns_client.go | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index 069cea1c..dab9884f 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -98,37 +98,40 @@ const ( CMessageReceived = iota ) -type Interaction struct { +type MDNSInteraction struct { code int resultChan chan<- interface{} -} - -type MDNSInteraction struct { - Interaction - payload interface{} + payload interface{} } // Async func (c *MDNSClient) Shutdown() { - c.queryChan <- &MDNSInteraction{Interaction: Interaction{code: CShutdown}} + c.queryChan <- &MDNSInteraction{code: CShutdown} } // Async func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- *ResponseA) { - c.queryChan <- &MDNSInteraction{Interaction: Interaction{code: CSendQuery}, payload: mDNSQueryInfo{name, querytype, responseCh}} + c.queryChan <- &MDNSInteraction{ + code: CSendQuery, + payload: mDNSQueryInfo{name, querytype, responseCh}, + } } // Sync func (c *MDNSClient) IsInflightQuery(m *dns.Msg) bool { resultChan := make(chan interface{}) - c.queryChan <- &MDNSInteraction{Interaction: Interaction{code: CIsInflightQuery, resultChan: resultChan}, payload: m} + c.queryChan <- &MDNSInteraction{ + code: CIsInflightQuery, + resultChan: resultChan, + payload: m, + } result := <-resultChan return result.(bool) } // Async - called from dns library multiplexer func (c *MDNSClient) ResponseCallback(r *dns.Msg) { - c.queryChan <- &MDNSInteraction{Interaction: Interaction{code: CMessageReceived}, payload: r} + c.queryChan <- &MDNSInteraction{code: CMessageReceived, payload: r} } // ACTOR server From e7ac6dafb3182f670bea276093f964db387d3568 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 6 Oct 2014 13:56:17 +0100 Subject: [PATCH 031/145] Made the weave-local domain "weave" a constant Plus some logging on startup --- main.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 757242b7..a05382c8 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,10 @@ import ( "time" ) +const ( + LOCAL_DOMAIN = "weave" +) + var zone = new(weavedns.ZoneDb) func makeDNSReply(r *dns.Msg, name string, addr net.IP) *dns.Msg { @@ -117,11 +121,11 @@ func main() { } LocalServeMux := dns.NewServeMux() - LocalServeMux.HandleFunc("local", handleLocal) + LocalServeMux.HandleFunc(LOCAL_DOMAIN, handleLocal) go weavedns.ListenHttp(zone) MDNSServeMux := dns.NewServeMux() - MDNSServeMux.HandleFunc("local", handleMDNS) + MDNSServeMux.HandleFunc(LOCAL_DOMAIN, handleMDNS) conn, err := weavedns.LinkLocalMulticastListener(iface) if err != nil { @@ -130,6 +134,8 @@ func main() { mdnsClient, err = weavedns.NewMDNSClient() if err != nil { log.Fatal(err) + } else { + log.Printf("Using mDNS on %s", ifaceName) } mdnsClient.Start() @@ -139,5 +145,7 @@ func main() { err = dns.ListenAndServe(address, "udp", LocalServeMux) if err != nil { log.Fatal(err) + } else { + log.Printf("Listening for DNS on %s", address) } } From a545d2738cf16e74ee6604b851e113bd079db243 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 7 Oct 2014 12:47:18 +0100 Subject: [PATCH 032/145] Move timeout to incoming queries --- server/mdns_client.go | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index dab9884f..87f463f7 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -30,7 +30,8 @@ type ResponseA struct { } type responseInfo struct { - ch chan<- *ResponseA + timeout time.Time // if no answer by this time, give up + ch chan<- *ResponseA } // Represents one query that we have sent for one name. @@ -40,7 +41,6 @@ type inflightQuery struct { name string Id uint16 // the DNS message ID responseInfos []*responseInfo - timeout time.Time // if no answer by this time, give up } type MDNSClient struct { @@ -142,15 +142,25 @@ func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { now := time.Now() after := MaxDuration for name, query := range c.inflight { - switch duration := query.timeout.Sub(now); { - case duration <= 0: // timed out - log.Println("Query timed out:", name) - for _, item := range query.responseInfos { + // Count down from end of slice to beginning + length := len(query.responseInfos) + for i := length - 1; i >= 0; i-- { + item := query.responseInfos[i] + switch duration := item.timeout.Sub(now); { + case duration <= 0: // timed out close(item.ch) + // Swap item from the end of the slice + length-- + if i < length { + query.responseInfos[i] = query.responseInfos[length] + } + case duration < after: + after = duration } + } + query.responseInfos = query.responseInfos[:length] + if length == 0 { delete(c.inflight, name) - case duration < after: - after = duration } } log.Println("Next timeout:", after) @@ -199,14 +209,16 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) (err error) { return err } query = &inflightQuery{ - name: q.name, - Id: m.Id, - timeout: time.Now().Add(mDNSTimeout), + name: q.name, + Id: m.Id, } c.inflight[q.name] = query _, err = c.conn.WriteTo(buf, c.addr) } - info := &responseInfo{ch: q.responseCh} + info := &responseInfo{ + ch: q.responseCh, + timeout: time.Now().Add(mDNSTimeout), + } query.responseInfos = append(query.responseInfos, info) return err From e8e86dadc0e71f8ca7fe92acad297f68bc855ebe Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 7 Oct 2014 12:49:52 +0100 Subject: [PATCH 033/145] Changed to accept a CIDR address and use net.IP * In general we'll have an IP address and a subnet mask for the weave network address; account for this in the zone DB and in the HTTP server. * Use the net.IP type rather than string, which gives us a bit more generality (in theory it'll deal with Ipv6 too) and validation (we attempt to parse the input) --- main.go | 4 ++-- server/http.go | 19 ++++++++++++++----- server/zone.go | 27 +++++++++++---------------- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/main.go b/main.go index a05382c8..4f739cec 100644 --- a/main.go +++ b/main.go @@ -38,7 +38,7 @@ func handleMDNS(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] ip, err := zone.MatchLocal(q.Name) if err == nil { - m := makeDNSReply(r, q.Name, net.ParseIP(ip)) + m := makeDNSReply(r, q.Name, ip) mdnsClient.SendResponse(m) } else if mdnsClient.IsInflightQuery(r) { // ignore this - it's our own query received via multicast @@ -53,7 +53,7 @@ func handleLocal(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] ip, err := zone.MatchLocal(q.Name) if err == nil { - m := makeDNSReply(r, q.Name, net.ParseIP(ip)) + m := makeDNSReply(r, q.Name, ip) w.WriteMsg(m) } else { log.Printf("Failed lookup for %s; sending mDNS query", q.Name) diff --git a/server/http.go b/server/http.go index aaa49e0b..4981aaad 100644 --- a/server/http.go +++ b/server/http.go @@ -3,6 +3,7 @@ package weavedns import ( "fmt" "log" + "net" "net/http" ) @@ -14,12 +15,20 @@ func ListenHttp(db Zone) { http.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") addr := r.FormValue("ip") - weave_ip := r.FormValue("weave_ip") - if name != "" && addr != "" && weave_ip != "" { - log.Printf("Adding %s, %s, %s", name, addr, weave_ip) - db.AddRecord(name, addr, weave_ip) + weave_cidr := r.FormValue("weave_cidr") + if name != "" && addr != "" && weave_cidr != "" { + ip := net.ParseIP(addr) + weave_ip, subnet, err := net.ParseCIDR(weave_cidr) + if err == nil && ip != nil { + log.Printf("Adding %s (%s) -> %s", name, addr, weave_cidr) + db.AddRecord(name, ip, weave_ip, subnet) + } else if err != nil { + log.Printf("Invalid CIDR in request: %s", weave_cidr) + } else { + log.Printf("Invalid IP in request: %s", addr) + } } else { - log.Printf("Invalid request: %s, %s, %s", name, addr, weave_ip) + log.Printf("Invalid request: %s, %s, %s", name, addr, weave_cidr) } }) address := fmt.Sprintf(":%d", UPDATE_PORT) diff --git a/server/zone.go b/server/zone.go index 1336adb1..824dd8d8 100644 --- a/server/zone.go +++ b/server/zone.go @@ -3,19 +3,20 @@ package weavedns import ( "github.com/miekg/dns" "log" + "net" "sync" ) type Zone interface { - AddRecord(name string, ip string, weave_ip string) error - MatchLocal(name string) (string, error) - MatchGlobal(name string) (string, error) + AddRecord(string, net.IP, net.IP, *net.IPNet) error + MatchLocal(string) (net.IP, error) } type Record struct { Name string - Ip string - WeaveIp string + Ip net.IP + WeaveIp net.IP + Subnet *net.IPNet } type ZoneDb struct { @@ -30,31 +31,25 @@ func (ops LookupError) Error() string { } // Stop gap. -func (zone *ZoneDb) match(name string) (string, error) { +func (zone *ZoneDb) match(name string) (net.IP, error) { for _, r := range zone.recs { log.Printf("%s == %s ?", r.Name, name) if r.Name == name { return r.WeaveIp, nil } } - return "", LookupError(name) + return nil, LookupError(name) } -func (zone *ZoneDb) MatchLocal(name string) (string, error) { +func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { zone.mx.Lock() defer zone.mx.Unlock() return zone.match(name) } -func (zone *ZoneDb) MatchGlobal(name string) (string, error) { +func (zone *ZoneDb) AddRecord(name string, ip net.IP, weave_ip net.IP, weave_subnet *net.IPNet) error { zone.mx.Lock() defer zone.mx.Unlock() - return zone.match(name) -} - -func (zone *ZoneDb) AddRecord(name string, ip string, weave_ip string) error { - zone.mx.Lock() - defer zone.mx.Unlock() - zone.recs = append(zone.recs, Record{dns.Fqdn(name), ip, weave_ip}) + zone.recs = append(zone.recs, Record{dns.Fqdn(name), ip, weave_ip, weave_subnet}) return nil } From ff6f54488faae2ac8c3b9088253ca42d072b169b Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 7 Oct 2014 13:18:07 +0100 Subject: [PATCH 034/145] Tidy up --- main.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/main.go b/main.go index 757242b7..5c9f2345 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,6 @@ func makeDNSReply(r *dns.Msg, name string, addr net.IP) *dns.Msg { var mdnsClient *weavedns.MDNSClient func handleMDNS(w dns.ResponseWriter, r *dns.Msg) { - log.Println("MDNS received:", r) // Here we have to handle both requests incoming, responses to queries // we sent out, and responses to queries that other nodes sent if len(r.Answer) > 0 { @@ -45,7 +44,6 @@ func handleMDNS(w dns.ResponseWriter, r *dns.Msg) { } func handleLocal(w dns.ResponseWriter, r *dns.Msg) { - log.Printf("Received message: %s", r) q := r.Question[0] ip, err := zone.MatchLocal(q.Name) if err == nil { @@ -56,12 +54,12 @@ func handleLocal(w dns.ResponseWriter, r *dns.Msg) { // We don't know the answer; see if someone else does channel := make(chan *weavedns.ResponseA, 4) go func() { + // Loop terminates when channel is closed by MDNSClient on timeout for resp := range channel { log.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) m := makeDNSReply(r, resp.Name, resp.Addr) w.WriteMsg(m) } - // FIXME: need to add a time-out either here or in mdnsClient }() mdnsClient.SendQuery(q.Name, dns.TypeA, channel) } From 930a6ff939d34e916ebd68989eb2ddfddeae1c1b Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 7 Oct 2014 16:29:17 +0100 Subject: [PATCH 035/145] Remove log message --- server/mdns_client.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index 87f463f7..1164dcb6 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -163,7 +163,6 @@ func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { delete(c.inflight, name) } } - log.Println("Next timeout:", after) timer.Reset(after) } From 9ec95e4fa87ab26e3f752f133c9724950eb3bac0 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 7 Oct 2014 16:29:58 +0100 Subject: [PATCH 036/145] Unit test --- server/mdns_client_test.go | 109 +++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 server/mdns_client_test.go diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go new file mode 100644 index 00000000..7201152b --- /dev/null +++ b/server/mdns_client_test.go @@ -0,0 +1,109 @@ +package weavedns + +import ( + "github.com/miekg/dns" + "log" + "net" + "testing" + "time" +) + +var ( + test_addr = net.ParseIP("9.8.7.6") + sendconn *net.UDPConn + sendAddr = &net.UDPAddr{ + IP: net.ParseIP(ipv4mdns), + Port: 0, + } +) + +func minimalServer(w dns.ResponseWriter, req *dns.Msg) { + //log.Println("minimalServer received:", req) + if len(req.Answer) > 0 { + return // Only interested in questions. + } + + m := new(dns.Msg) + m.SetReply(req) + + hdr := dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeA, + Class: dns.ClassINET, Ttl: 3600} + a := &dns.A{hdr, test_addr} + m.Answer = append(m.Answer, a) + + buf, err := m.Pack() + if err != nil { + log.Fatal(err) + } + if buf == nil { + log.Fatal("Nil buffer") + } + //log.Println("minimalServer sending:", buf) + _, err = sendconn.WriteTo(buf, ipv4Addr) + if err != nil { + log.Fatal(err) + } +} + +func RunLocalMulticastServer() error { + mux := dns.NewServeMux() + mux.HandleFunc("weave", minimalServer) + multicast, err := net.ListenMulticastUDP("udp", nil, ipv4Addr) + if err != nil { + return err + } + go dns.ActivateAndServe(nil, multicast, mux) + return nil +} + +func TestSimpleQuery(t *testing.T) { + log.Println("TestSimpleQuery starting") + mdnsClient, err := NewMDNSClient() + if err != nil { + t.Fatal(err) + } + mdnsClient.Start() + mux := dns.NewServeMux() + + handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) { + //log.Println("test received:", r) + // Only handle responses here + if len(r.Answer) > 0 { + mdnsClient.ResponseCallback(r) + } + } + + mux.HandleFunc("weave", handleMDNS) + multicast, err := net.ListenMulticastUDP("udp", nil, ipv4Addr) + if err != nil { + t.Fatal(err) + } + go dns.ActivateAndServe(nil, multicast, mux) + sendconn, err = net.ListenUDP("udp", sendAddr) + if err != nil { + t.Fatal(err) + } + + err = RunLocalMulticastServer() + if err != nil { + t.Fatalf("Unable to run test server: %s", err) + } + + var received_addr net.IP + + channel := make(chan *ResponseA, 4) + go func() { + for resp := range channel { + log.Printf("Got address response %s addr %s", resp.Name, resp.Addr) + received_addr = resp.Addr + } + }() + mdnsClient.SendQuery("test.weave.", dns.TypeA, channel) + + time.Sleep(1 * time.Second) + + if !received_addr.Equal(test_addr) { + t.Log("Unexpected result for test.weave", received_addr) + t.Fail() + } +} From 696bb7f43f7b0ba9d2b77d268d07b8857a3cbbd5 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 7 Oct 2014 17:59:07 +0100 Subject: [PATCH 037/145] Added design sketch --- design.md | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 design.md diff --git a/design.md b/design.md new file mode 100644 index 00000000..f581ac44 --- /dev/null +++ b/design.md @@ -0,0 +1,109 @@ +# Weave DNS (service discovery) design + +The model is that each host has a service that is notified of +hostnames and weave addresses for containers on the host. It binds to +the host bridge to answer DNS queries from local containers; anything +it can't answer, it asks with mDNS on the weave network. + +The service is divided into two components: the first is the DNS +server, which serves the records it is told about, and otherwise asks +via mDNS on the weave network. The other component is the DNS updater, +specific to Docker, which monitors the running containers so to add +and remove records from the DNS server. + +## DNS server API + +The DNS server accepts HTTP requests on the following URL (patterns) +and methods: + +PUT /name// + +Put a record for an IP, bound to a host-scoped identifier (e.g., a +container ID), in the DNS database. + +DELETE /name// + +Remove a specific record for an IP and host-scoped identifier. + +DELETE /name/ + +Remove all records for the host-scoped identifier. + +GET /name/ + +List all the records in the database. + +GET /name/ + +List all the records for a host-scoped identifier. + +GET /status + +Give the server status. + +### Record structure + +The record structure is *either* form fields (useful for scripting +PUTs), or a JSON object with the same fields. The fields are: + +```js + { + "domain": string, + "routing_prefix": number + } +``` + +> Also TTL? + +## DNS server behaviour + +The DNS server listens for the HTTP requests as above, and maintains a +database of host-local names accordingly. + +It also listens on the host-local network for DNS queries (port +53). Anything that is in its database it responds to immediately. For +other requests, it asks using multicast DNS on the weave network. + +Meanwhile, the DNS server is listening for mDNS queries, and responds +if it the queried name is in its database. + +### Caching mDNS answers + +The DNS server will hear the answers to its own and other servers' +mDNS queries sent on the weave network. It can cache these, to respond +to local DNS requests; the cache is consulted before using mDNS. + +## DNS updater + +The updater component uses the Docker remote API to monitor containers +coming and going, and tells the DNS server to update its records via +its HTTP interface. It does not need to be attached to the weave +network. + +The updater starts by subscribing to the events, and getting a list of +the current containers. Any containers given a domain ending with +".weave" are considered for inclusion in the name database. + +When it sees a container start or stop, the updater checks the weave +network attachment of the container, and updates the DNS server. + +> How does it check the network attachment from within a container? + +> Will it need to delay slightly so that `attach` has a chance to run? +> Perhaps it could put containers on a watch list when it's noticed +> them. + +## Container startup + +> TBD + +## Grouping and load balancing + +> TBD + +## Extensions + + * An updater that checks health more specifically, e.g., by testing + that a particular port is being listened to, or that it can get a + 200 OK. These could be encoded in the container environment + variables, as per registrator. From a228bed7444d6cc39ee8f462d425fd181e057a60 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 7 Oct 2014 18:02:00 +0100 Subject: [PATCH 038/145] Marked up API URLs as code --- design.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/design.md b/design.md index f581ac44..919a2bd3 100644 --- a/design.md +++ b/design.md @@ -16,28 +16,28 @@ and remove records from the DNS server. The DNS server accepts HTTP requests on the following URL (patterns) and methods: -PUT /name// +`PUT /name//` Put a record for an IP, bound to a host-scoped identifier (e.g., a container ID), in the DNS database. -DELETE /name// +`DELETE /name//` Remove a specific record for an IP and host-scoped identifier. -DELETE /name/ +`DELETE /name/` Remove all records for the host-scoped identifier. -GET /name/ +`GET /name/` List all the records in the database. -GET /name/ +`GET /name/` List all the records for a host-scoped identifier. -GET /status +`GET /status` Give the server status. From ddccc29b0ecb3e155c341d6e14205231efe9ecfa Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 09:58:28 +0100 Subject: [PATCH 039/145] Static linking --- docker/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Makefile b/docker/Makefile index c4e1a207..43de299d 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -6,7 +6,7 @@ MAIN:=../ all: docker-image weavedns: ../server/*.go ../main.go - go get $(MAIN) && GOARCH=amd64 GOOS=linux go build -o $@ -v $(MAIN) + go get $(MAIN) && GOARCH=amd64 GOOS=linux go build -ldflags '-linkmode external -extldflags "-static"' -o $@ -v $(MAIN) .PHONY: docker-image From c3eed5d8073404a61f47262f8c7e002e53d0412a Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 09:58:56 +0100 Subject: [PATCH 040/145] Wait 5 seconds for interface at startup --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index de94f53e..db378fc3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,4 +2,4 @@ FROM scratch # EXPOSE 53/udp WORKDIR /home/weave ADD ./weavedns /home/weave/ -ENTRYPOINT ["/home/weave/weavedns"] +ENTRYPOINT ["/home/weave/weavedns", "-wait", "5"] From 37924f9f9bc97788e258bab79272bec7a0b3a13d Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 11:30:42 +0100 Subject: [PATCH 041/145] Refactor into separate client and server classes --- main.go | 24 ++++++++++++++++-------- server/mdns_client.go | 29 ++++++++++++++++++----------- server/mdns_client_test.go | 35 ++++++++++------------------------- server/mdns_server.go | 30 ++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 44 deletions(-) create mode 100644 server/mdns_server.go diff --git a/main.go b/main.go index d954dee5..e764517a 100644 --- a/main.go +++ b/main.go @@ -26,20 +26,21 @@ func makeDNSReply(r *dns.Msg, name string, addr net.IP) *dns.Msg { return m } -var mdnsClient *weavedns.MDNSClient +var ( + mdnsClient *weavedns.MDNSClient + mdnsServer *weavedns.MDNSServer +) func handleMDNS(w dns.ResponseWriter, r *dns.Msg) { - // Here we have to handle both requests incoming, responses to queries - // we sent out, and responses to queries that other nodes sent - if len(r.Answer) > 0 { - mdnsClient.ResponseCallback(r) - } else if len(r.Question) > 0 { + // Ignore answers to other questions + if len(r.Answer) == 0 && len(r.Question) > 0 { q := r.Question[0] ip, err := zone.MatchLocal(q.Name) if err == nil { m := makeDNSReply(r, q.Name, ip) - mdnsClient.SendResponse(m) + mdnsServer.SendResponse(m) } else if mdnsClient.IsInflightQuery(r) { + log.Println("Inflight Query", w.RemoteAddr(), w.LocalAddr()) // ignore this - it's our own query received via multicast } else { log.Printf("Failed MDNS lookup for %s", q.Name) @@ -135,7 +136,14 @@ func main() { } else { log.Printf("Using mDNS on %s", ifaceName) } - mdnsClient.Start() + err = mdnsClient.Start(iface) + if err != nil { + log.Fatal(err) + } + mdnsServer, err = weavedns.NewMDNSServer() + if err != nil { + log.Fatal(err) + } go dns.ActivateAndServe(nil, conn, MDNSServeMux) diff --git a/server/mdns_client.go b/server/mdns_client.go index 1164dcb6..cc541c11 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -59,7 +59,6 @@ type mDNSQueryInfo struct { func NewMDNSClient() (*MDNSClient, error) { conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) if err != nil { - log.Println(err) return nil, err } retval := &MDNSClient{ @@ -69,10 +68,27 @@ func NewMDNSClient() (*MDNSClient, error) { return retval, nil } -func (c *MDNSClient) Start() { +func (c *MDNSClient) Start(ifi *net.Interface) error { + multicast, err := LinkLocalMulticastListener(ifi) + if err != nil { + return err + } + + handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) { + //log.Println("client received:", r) + // Only handle responses here + if len(r.Answer) > 0 { + c.ResponseCallback(r) + } + } + + go dns.ActivateAndServe(nil, multicast, dns.HandlerFunc(handleMDNS)) + queryChan := make(chan *MDNSInteraction, 4) c.queryChan = queryChan go c.queryLoop(queryChan) + + return nil } func LinkLocalMulticastListener(ifi *net.Interface) (net.PacketConn, error) { @@ -80,15 +96,6 @@ func LinkLocalMulticastListener(ifi *net.Interface) (net.PacketConn, error) { return conn, err } -func (c *MDNSClient) SendResponse(m *dns.Msg) error { - buf, err := m.Pack() - if err != nil { - return err - } - _, err = c.conn.WriteTo(buf, c.addr) - return err -} - // ACTOR client API const ( diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index 7201152b..a3956002 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -10,11 +10,6 @@ import ( var ( test_addr = net.ParseIP("9.8.7.6") - sendconn *net.UDPConn - sendAddr = &net.UDPAddr{ - IP: net.ParseIP(ipv4mdns), - Port: 0, - } ) func minimalServer(w dns.ResponseWriter, req *dns.Msg) { @@ -39,7 +34,13 @@ func minimalServer(w dns.ResponseWriter, req *dns.Msg) { log.Fatal("Nil buffer") } //log.Println("minimalServer sending:", buf) - _, err = sendconn.WriteTo(buf, ipv4Addr) + // This is a bit of a kludge - per the RFC we should send responses from 5353, but that doesn't seem to work + sendconn, err := net.DialUDP("udp", nil, ipv4Addr) + if err != nil { + log.Fatal(err) + } + _, err = sendconn.Write(buf) + sendconn.Close() if err != nil { log.Fatal(err) } @@ -62,24 +63,7 @@ func TestSimpleQuery(t *testing.T) { if err != nil { t.Fatal(err) } - mdnsClient.Start() - mux := dns.NewServeMux() - - handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) { - //log.Println("test received:", r) - // Only handle responses here - if len(r.Answer) > 0 { - mdnsClient.ResponseCallback(r) - } - } - - mux.HandleFunc("weave", handleMDNS) - multicast, err := net.ListenMulticastUDP("udp", nil, ipv4Addr) - if err != nil { - t.Fatal(err) - } - go dns.ActivateAndServe(nil, multicast, mux) - sendconn, err = net.ListenUDP("udp", sendAddr) + err = mdnsClient.Start(nil) if err != nil { t.Fatal(err) } @@ -98,9 +82,10 @@ func TestSimpleQuery(t *testing.T) { received_addr = resp.Addr } }() + mdnsClient.SendQuery("test.weave.", dns.TypeA, channel) - time.Sleep(1 * time.Second) + time.Sleep(200 * time.Millisecond) if !received_addr.Equal(test_addr) { t.Log("Unexpected result for test.weave", received_addr) diff --git a/server/mdns_server.go b/server/mdns_server.go new file mode 100644 index 00000000..89aaa340 --- /dev/null +++ b/server/mdns_server.go @@ -0,0 +1,30 @@ +package weavedns + +import ( + "github.com/miekg/dns" + "net" +) + +type MDNSServer struct { + sendconn *net.UDPConn +} + +func NewMDNSServer() (*MDNSServer, error) { + //log.Println("minimalServer sending:", buf) + // This is a bit of a kludge - per the RFC we should send responses from 5353, but that doesn't seem to work + sendconn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) + if err != nil { + return nil, err + } + retval := &MDNSServer{sendconn: sendconn} + return retval, nil +} + +func (s *MDNSServer) SendResponse(m *dns.Msg) error { + buf, err := m.Pack() + if err != nil { + return err + } + _, err = s.sendconn.WriteTo(buf, ipv4Addr) + return err +} From d234fe950647c7f5373c7284bafb71573502c831 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 12:31:20 +0100 Subject: [PATCH 042/145] Move handleMDNS into server file --- main.go | 33 ++++------------------ server/mdns_server.go | 65 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 30 deletions(-) diff --git a/main.go b/main.go index e764517a..c787dc2e 100644 --- a/main.go +++ b/main.go @@ -31,23 +31,6 @@ var ( mdnsServer *weavedns.MDNSServer ) -func handleMDNS(w dns.ResponseWriter, r *dns.Msg) { - // Ignore answers to other questions - if len(r.Answer) == 0 && len(r.Question) > 0 { - q := r.Question[0] - ip, err := zone.MatchLocal(q.Name) - if err == nil { - m := makeDNSReply(r, q.Name, ip) - mdnsServer.SendResponse(m) - } else if mdnsClient.IsInflightQuery(r) { - log.Println("Inflight Query", w.RemoteAddr(), w.LocalAddr()) - // ignore this - it's our own query received via multicast - } else { - log.Printf("Failed MDNS lookup for %s", q.Name) - } - } -} - func handleLocal(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] ip, err := zone.MatchLocal(q.Name) @@ -123,13 +106,7 @@ func main() { LocalServeMux.HandleFunc(LOCAL_DOMAIN, handleLocal) go weavedns.ListenHttp(zone) - MDNSServeMux := dns.NewServeMux() - MDNSServeMux.HandleFunc(LOCAL_DOMAIN, handleMDNS) - - conn, err := weavedns.LinkLocalMulticastListener(iface) - if err != nil { - log.Fatal(err) - } + var err error mdnsClient, err = weavedns.NewMDNSClient() if err != nil { log.Fatal(err) @@ -140,12 +117,14 @@ func main() { if err != nil { log.Fatal(err) } - mdnsServer, err = weavedns.NewMDNSServer() + mdnsServer, err = weavedns.NewMDNSServer(zone) + if err != nil { + log.Fatal(err) + } + err = mdnsServer.Start(iface) if err != nil { log.Fatal(err) } - - go dns.ActivateAndServe(nil, conn, MDNSServeMux) address := fmt.Sprintf(":%d", dnsPort) err = dns.ListenAndServe(address, "udp", LocalServeMux) diff --git a/server/mdns_server.go b/server/mdns_server.go index 89aaa340..4716d1df 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -2,24 +2,83 @@ package weavedns import ( "github.com/miekg/dns" + "log" "net" ) type MDNSServer struct { - sendconn *net.UDPConn + localAddrs []net.Addr + sendconn *net.UDPConn + zone *ZoneDb } -func NewMDNSServer() (*MDNSServer, error) { +func NewMDNSServer(zone *ZoneDb) (*MDNSServer, error) { //log.Println("minimalServer sending:", buf) // This is a bit of a kludge - per the RFC we should send responses from 5353, but that doesn't seem to work sendconn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) if err != nil { return nil, err } - retval := &MDNSServer{sendconn: sendconn} + retval := &MDNSServer{sendconn: sendconn, zone: zone} + return retval, nil } +func makeDNSReply(r *dns.Msg, name string, addr net.IP) *dns.Msg { + m := new(dns.Msg) + m.SetReply(r) + hdr := dns.RR_Header{Name: name, Rrtype: dns.TypeA, + Class: dns.ClassINET, Ttl: 3600} + a := &dns.A{hdr, addr} + m.Answer = append(m.Answer, a) + return m +} + +// Return true if testaddr is a UDP address with IP matching my local i/f +func (s *MDNSServer) addrIsLocal(testaddr net.Addr) bool { + if udpaddr, ok := testaddr.(*net.UDPAddr); ok { + for _, localaddr := range s.localAddrs { + if ipnetlocal, ok := localaddr.(*net.IPNet); ok { + if ipnetlocal.IP.Equal(udpaddr.IP) { + return true + } + } + } + } + return false +} + +func (s *MDNSServer) Start(ifi *net.Interface) error { + handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) { + // Ignore answers to other questions + if len(r.Answer) == 0 && len(r.Question) > 0 { + q := r.Question[0] + ip, err := s.zone.MatchLocal(q.Name) + if err == nil { + m := makeDNSReply(r, q.Name, ip) + s.SendResponse(m) + } else if s.addrIsLocal(w.RemoteAddr()) { + // ignore this - it's our own query received via multicast + } else { + log.Printf("Failed MDNS lookup for %s", q.Name) + } + } + } + + conn, err := LinkLocalMulticastListener(ifi) + if err != nil { + return err + } + s.localAddrs, err = ifi.Addrs() + if err != nil { + return err + } + + go dns.ActivateAndServe(nil, conn, dns.HandlerFunc(handleMDNS)) + + return err +} + func (s *MDNSServer) SendResponse(m *dns.Msg) error { buf, err := m.Pack() if err != nil { From 6506b63520ef7e701805207ea531b3890c230d7f Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 13:49:20 +0100 Subject: [PATCH 043/145] Removed IsInflightQuery that is done by IP address now --- server/mdns_client.go | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index cc541c11..ffbdf82a 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -101,7 +101,6 @@ func LinkLocalMulticastListener(ifi *net.Interface) (net.PacketConn, error) { const ( CSendQuery = iota CShutdown = iota - CIsInflightQuery = iota CMessageReceived = iota ) @@ -124,18 +123,6 @@ func (c *MDNSClient) SendQuery(name string, querytype uint16, responseCh chan<- } } -// Sync -func (c *MDNSClient) IsInflightQuery(m *dns.Msg) bool { - resultChan := make(chan interface{}) - c.queryChan <- &MDNSInteraction{ - code: CIsInflightQuery, - resultChan: resultChan, - payload: m, - } - result := <-resultChan - return result.(bool) -} - // Async - called from dns library multiplexer func (c *MDNSClient) ResponseCallback(r *dns.Msg) { c.queryChan <- &MDNSInteraction{code: CMessageReceived, payload: r} @@ -191,8 +178,6 @@ func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { case CSendQuery: err = c.handleSendQuery(query.payload.(mDNSQueryInfo)) run() - case CIsInflightQuery: - query.resultChan <- c.handleIsInflightQuery(query.payload.(*dns.Msg)) case CMessageReceived: c.handleResponse(query.payload.(*dns.Msg)) run() @@ -230,20 +215,6 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) (err error) { return err } -func (c *MDNSClient) handleIsInflightQuery(m *dns.Msg) bool { - if len(m.Question) == 1 { - q := m.Question[0] - if q.Qtype == dns.TypeA { - if query, found := c.inflight[q.Name]; found { - if query.Id == m.Id { - return true - } - } - } - } - return false -} - func (c *MDNSClient) handleResponse(r *dns.Msg) { for _, answer := range r.Answer { switch rr := answer.(type) { From dbc77b29432abb7ff7d9c36493f17ffc34729abb Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 14:13:05 +0100 Subject: [PATCH 044/145] Don't crash on nil (default) interface --- server/mdns_server.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/mdns_server.go b/server/mdns_server.go index 4716d1df..ac605a66 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -69,7 +69,11 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { if err != nil { return err } - s.localAddrs, err = ifi.Addrs() + if ifi == nil { + s.localAddrs, err = net.InterfaceAddrs() + } else { + s.localAddrs, err = ifi.Addrs() + } if err != nil { return err } From 6a9e0bb23f34064c2b2008824812f53e47c68c53 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 14:14:10 +0100 Subject: [PATCH 045/145] Shut down minimal server at end of test --- server/mdns_client_test.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index a3956002..24d819dc 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -46,15 +46,16 @@ func minimalServer(w dns.ResponseWriter, req *dns.Msg) { } } -func RunLocalMulticastServer() error { +func RunLocalMulticastServer() (*dns.Server, error) { mux := dns.NewServeMux() mux.HandleFunc("weave", minimalServer) multicast, err := net.ListenMulticastUDP("udp", nil, ipv4Addr) if err != nil { - return err + return nil, err } - go dns.ActivateAndServe(nil, multicast, mux) - return nil + server := &dns.Server{Listener: nil, PacketConn: multicast, Handler: mux} + go server.ActivateAndServe() + return server, nil } func TestSimpleQuery(t *testing.T) { @@ -68,10 +69,11 @@ func TestSimpleQuery(t *testing.T) { t.Fatal(err) } - err = RunLocalMulticastServer() + server, err := RunLocalMulticastServer() if err != nil { t.Fatalf("Unable to run test server: %s", err) } + defer server.Shutdown() var received_addr net.IP From 6a60b7b628753de54280c831f57afca5dd1379d6 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 14:16:13 +0100 Subject: [PATCH 046/145] Don't need multiplexer --- server/mdns_client_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index 24d819dc..4ad28abb 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -47,13 +47,11 @@ func minimalServer(w dns.ResponseWriter, req *dns.Msg) { } func RunLocalMulticastServer() (*dns.Server, error) { - mux := dns.NewServeMux() - mux.HandleFunc("weave", minimalServer) multicast, err := net.ListenMulticastUDP("udp", nil, ipv4Addr) if err != nil { return nil, err } - server := &dns.Server{Listener: nil, PacketConn: multicast, Handler: mux} + server := &dns.Server{Listener: nil, PacketConn: multicast, Handler: dns.HandlerFunc(minimalServer)} go server.ActivateAndServe() return server, nil } From de94b06e89f8667891ab0f77e5b45cb96e0521c9 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 14:19:31 +0100 Subject: [PATCH 047/145] Wait for response synchronously --- server/mdns_client_test.go | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index 4ad28abb..02288bd2 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -5,7 +5,6 @@ import ( "log" "net" "testing" - "time" ) var ( @@ -76,16 +75,11 @@ func TestSimpleQuery(t *testing.T) { var received_addr net.IP channel := make(chan *ResponseA, 4) - go func() { - for resp := range channel { - log.Printf("Got address response %s addr %s", resp.Name, resp.Addr) - received_addr = resp.Addr - } - }() - mdnsClient.SendQuery("test.weave.", dns.TypeA, channel) - - time.Sleep(200 * time.Millisecond) + for resp := range channel { + log.Printf("Got address response %s addr %s", resp.Name, resp.Addr) + received_addr = resp.Addr + } if !received_addr.Equal(test_addr) { t.Log("Unexpected result for test.weave", received_addr) From 3175e64b6362ad553e266e877238d0d0f9af5a0d Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 14:24:29 +0100 Subject: [PATCH 048/145] Shut down multicast listener when client is shut down --- server/mdns_client.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index ffbdf82a..a6d9f544 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -44,6 +44,7 @@ type inflightQuery struct { } type MDNSClient struct { + server *dns.Server conn *net.UDPConn addr *net.UDPAddr inflight map[string]*inflightQuery @@ -82,7 +83,8 @@ func (c *MDNSClient) Start(ifi *net.Interface) error { } } - go dns.ActivateAndServe(nil, multicast, dns.HandlerFunc(handleMDNS)) + c.server = &dns.Server{Listener: nil, PacketConn: multicast, Handler: dns.HandlerFunc(handleMDNS)} + go c.server.ActivateAndServe() queryChan := make(chan *MDNSInteraction, 4) c.queryChan = queryChan @@ -174,6 +176,7 @@ func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { } switch query.code { case CShutdown: + c.server.Shutdown() terminate = true case CSendQuery: err = c.handleSendQuery(query.payload.(mDNSQueryInfo)) @@ -186,7 +189,6 @@ func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { run() } } - // handle shutdown here } func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) (err error) { From 6fc817104e50e191b8382f8cb241f0c449010b12 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 14:40:18 +0100 Subject: [PATCH 049/145] Shut down client when test finished --- server/mdns_client_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index 02288bd2..42acb9c3 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -65,6 +65,7 @@ func TestSimpleQuery(t *testing.T) { if err != nil { t.Fatal(err) } + defer mdnsClient.Shutdown() server, err := RunLocalMulticastServer() if err != nil { From f30ecb4a4861c6bbcde515697aa9fac57fcc2bd8 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 14:40:38 +0100 Subject: [PATCH 050/145] MDNS Server test --- server/mdns_server_test.go | 78 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 server/mdns_server_test.go diff --git a/server/mdns_server_test.go b/server/mdns_server_test.go new file mode 100644 index 00000000..df041adb --- /dev/null +++ b/server/mdns_server_test.go @@ -0,0 +1,78 @@ +package weavedns + +import ( + "github.com/miekg/dns" + "log" + "net" + "testing" + "time" +) + +var ( + test_addr1 = "10.0.2.1/24" +) + +func sendQuery(name string, querytype uint16) error { + m := new(dns.Msg) + m.SetQuestion(name, querytype) + m.RecursionDesired = false + buf, err := m.Pack() + if err != nil { + return err + } + conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) + if err != nil { + return err + } + _, err = conn.WriteTo(buf, ipv4Addr) + return err +} + +func TestServerSimpleQuery(t *testing.T) { + log.Println("TestServerSimpleQuery starting") + var zone = new(ZoneDb) + docker_ip := net.ParseIP("9.8.7.6") + weave_ip, subnet, _ := net.ParseCIDR(test_addr1) + zone.AddRecord("test.weave.", docker_ip, weave_ip, subnet) + + mdnsServer, err := NewMDNSServer(zone) + if err != nil { + t.Fatal(err) + } + err = mdnsServer.Start(nil) + if err != nil { + t.Fatal(err) + } + + var received_addr net.IP + + // Implement a minimal listener for responses + multicast, err := LinkLocalMulticastListener(nil) + if err != nil { + t.Fatal(err) + } + + handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) { + // Only handle responses here + if len(r.Answer) > 0 { + for _, answer := range r.Answer { + switch rr := answer.(type) { + case *dns.A: + received_addr = rr.A + } + } + } + } + + server := &dns.Server{Listener: nil, PacketConn: multicast, Handler: dns.HandlerFunc(handleMDNS)} + go server.ActivateAndServe() + + sendQuery("test.weave.", dns.TypeA) + + time.Sleep(time.Second) + + if !received_addr.Equal(weave_ip) { + t.Log("Unexpected result for test.weave", received_addr) + t.Fail() + } +} From 8295ecfe4645e862a65bd333f5cbd88c2a950525 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 14:46:10 +0100 Subject: [PATCH 051/145] Shut down listener when test finished --- server/mdns_server_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/mdns_server_test.go b/server/mdns_server_test.go index df041adb..215fcf1c 100644 --- a/server/mdns_server_test.go +++ b/server/mdns_server_test.go @@ -66,6 +66,7 @@ func TestServerSimpleQuery(t *testing.T) { server := &dns.Server{Listener: nil, PacketConn: multicast, Handler: dns.HandlerFunc(handleMDNS)} go server.ActivateAndServe() + defer server.Shutdown() sendQuery("test.weave.", dns.TypeA) From 97077339f41243966b4774c581f1c977002b70a3 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 15:19:30 +0100 Subject: [PATCH 052/145] Send back errors for SendQuery --- server/mdns_client.go | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index a6d9f544..45edef45 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -2,7 +2,6 @@ package weavedns import ( "github.com/miekg/dns" - "log" "math" "net" "time" @@ -27,6 +26,7 @@ var ( type ResponseA struct { Name string Addr net.IP + err error } type responseInfo struct { @@ -162,13 +162,8 @@ func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { timer.Reset(after) } - var err error terminate := false for !terminate { - if err != nil { - log.Printf("encountered error", err) - break - } select { case query, ok := <-queryChan: if !ok { @@ -179,7 +174,7 @@ func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { c.server.Shutdown() terminate = true case CSendQuery: - err = c.handleSendQuery(query.payload.(mDNSQueryInfo)) + c.handleSendQuery(query.payload.(mDNSQueryInfo)) run() case CMessageReceived: c.handleResponse(query.payload.(*dns.Msg)) @@ -189,9 +184,16 @@ func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { run() } } + + // Close all response channels + for _, query := range c.inflight { + for _, item := range query.responseInfos { + close(item.ch) + } + } } -func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) (err error) { +func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) error { query, found := c.inflight[q.name] if !found { m := new(dns.Msg) @@ -199,6 +201,8 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) (err error) { m.RecursionDesired = false buf, err := m.Pack() if err != nil { + q.responseCh <- &ResponseA{err: err} + close(q.responseCh) return err } query = &inflightQuery{ @@ -207,6 +211,11 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) (err error) { } c.inflight[q.name] = query _, err = c.conn.WriteTo(buf, c.addr) + if err != nil { + q.responseCh <- &ResponseA{err: err} + close(q.responseCh) + return err + } } info := &responseInfo{ ch: q.responseCh, @@ -214,7 +223,7 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) (err error) { } query.responseInfos = append(query.responseInfos, info) - return err + return nil } func (c *MDNSClient) handleResponse(r *dns.Msg) { From eec53d6c4dfeaabe3e57f7739dd04f5c2124c343 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 15:20:12 +0100 Subject: [PATCH 053/145] Test failure condition --- server/mdns_client_test.go | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index 42acb9c3..485f895a 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -8,7 +8,9 @@ import ( ) var ( - test_addr = net.ParseIP("9.8.7.6") + success_test_name = "test1.weave." + fail_test_name = "test2.weave." + test_addr = net.ParseIP("9.8.7.6") ) func minimalServer(w dns.ResponseWriter, req *dns.Msg) { @@ -16,6 +18,12 @@ func minimalServer(w dns.ResponseWriter, req *dns.Msg) { if len(req.Answer) > 0 { return // Only interested in questions. } + if len(req.Question) != 1 { + return // We only handle single-question messages + } + if req.Question[0].Name != success_test_name { + return // This is not the DNS record you are looking for + } m := new(dns.Msg) m.SetReply(req) @@ -76,14 +84,37 @@ func TestSimpleQuery(t *testing.T) { var received_addr net.IP channel := make(chan *ResponseA, 4) - mdnsClient.SendQuery("test.weave.", dns.TypeA, channel) + + // First, a test we expect to succeed + mdnsClient.SendQuery(success_test_name, dns.TypeA, channel) for resp := range channel { + if resp.err != nil { + t.Fatal(resp.err) + } log.Printf("Got address response %s addr %s", resp.Name, resp.Addr) received_addr = resp.Addr } if !received_addr.Equal(test_addr) { - t.Log("Unexpected result for test.weave", received_addr) + t.Log("Unexpected result for", success_test_name, received_addr) + t.Fail() + } + + received_response := false + // Now, a test we expect to time out with no responses + channel2 := make(chan *ResponseA, 4) + mdnsClient.SendQuery("test2.weave.", dns.TypeA, channel2) + for resp := range channel2 { + if resp.err != nil { + t.Fatal(resp.err) + } + log.Printf("Got address response %s addr %s", resp.Name, resp.Addr) + received_addr = resp.Addr + received_response = true + } + + if received_response { + t.Log("Unexpected result for test2.weave", received_addr) t.Fail() } } From 64e9121e5b263d6c5b402157c015b88b0212886e Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 17:00:41 +0100 Subject: [PATCH 054/145] Simple test of http function --- server/http_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 server/http_test.go diff --git a/server/http_test.go b/server/http_test.go new file mode 100644 index 00000000..53b7cbf3 --- /dev/null +++ b/server/http_test.go @@ -0,0 +1,47 @@ +package weavedns + +import ( + "net" + "net/http" + "net/url" + "testing" + "time" +) + +func TestHttp(t *testing.T) { + var ( + success_test_name = "test1.weave." + test_addr1 = "10.0.2.1/24" + docker_ip = "9.8.7.6" + ) + + var zone = new(ZoneDb) + go ListenHttp(zone) + + time.Sleep(100 * time.Millisecond) // Allow for http server to get going + + resp, err := http.PostForm("http://localhost:6785/add", + url.Values{"name": {success_test_name}, "ip": {docker_ip}, "weave_cidr": {test_addr1}}) + if err != nil { + t.Fatal(err) + } + if resp.StatusCode != 200 { + t.Log("Unexpected http response", resp.Status) + t.Fail() + } + + ip, err := zone.MatchLocal(success_test_name) + if err != nil { + t.Log(zone) + t.Fatal(err) + } + weave_ip, _, _ := net.ParseCIDR(test_addr1) + if !ip.Equal(weave_ip) { + t.Log("Unexpected result for", success_test_name, ip) + t.Fail() + } + + // Would like to shut down the http server at the end of this test + // but it's complicated. + // See https://groups.google.com/forum/#!topic/golang-nuts/vLHWa5sHnCE +} From 0f8932fbcd06c69593ab7cebc23b04ea67941e2a Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 8 Oct 2014 17:16:57 +0100 Subject: [PATCH 055/145] Add test for name that we don't know --- server/mdns_server_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/server/mdns_server_test.go b/server/mdns_server_test.go index 215fcf1c..68e2f387 100644 --- a/server/mdns_server_test.go +++ b/server/mdns_server_test.go @@ -45,6 +45,7 @@ func TestServerSimpleQuery(t *testing.T) { } var received_addr net.IP + received_count := 0 // Implement a minimal listener for responses multicast, err := LinkLocalMulticastListener(nil) @@ -59,6 +60,7 @@ func TestServerSimpleQuery(t *testing.T) { switch rr := answer.(type) { case *dns.A: received_addr = rr.A + received_count++ } } } @@ -68,12 +70,27 @@ func TestServerSimpleQuery(t *testing.T) { go server.ActivateAndServe() defer server.Shutdown() + time.Sleep(100 * time.Millisecond) // Allow for server to get going + sendQuery("test.weave.", dns.TypeA) time.Sleep(time.Second) + if received_count != 1 { + t.Log("Unexpected result count for test.weave", received_count) + t.Fail() + } if !received_addr.Equal(weave_ip) { t.Log("Unexpected result for test.weave", received_addr) t.Fail() } + + received_count = 0 + + sendQuery("testfail.weave.", dns.TypeA) + + if received_count != 0 { + t.Log("Unexpected result count for testfail.weave", received_count) + t.Fail() + } } From d41284f66df349e395b12a63f1a1dd9f7286ea7c Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 9 Oct 2014 13:08:58 +0100 Subject: [PATCH 056/145] Refactor test setup --- server/mdns_client_test.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index 485f895a..fb613e7a 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -63,8 +63,7 @@ func RunLocalMulticastServer() (*dns.Server, error) { return server, nil } -func TestSimpleQuery(t *testing.T) { - log.Println("TestSimpleQuery starting") +func setup(t *testing.T) (*MDNSClient, *dns.Server, error) { mdnsClient, err := NewMDNSClient() if err != nil { t.Fatal(err) @@ -73,12 +72,18 @@ func TestSimpleQuery(t *testing.T) { if err != nil { t.Fatal(err) } - defer mdnsClient.Shutdown() server, err := RunLocalMulticastServer() if err != nil { t.Fatalf("Unable to run test server: %s", err) } + return mdnsClient, server, err +} + +func TestSimpleQuery(t *testing.T) { + log.Println("TestSimpleQuery starting") + mdnsClient, server, _ := setup(t) + defer mdnsClient.Shutdown() defer server.Shutdown() var received_addr net.IP From 5b32fa58e640467506321357c86afd4065eeaecd Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 9 Oct 2014 14:11:16 +0100 Subject: [PATCH 057/145] factor out response checking --- server/mdns_client_test.go | 40 +++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index fb613e7a..6adcb835 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -80,46 +80,50 @@ func setup(t *testing.T) (*MDNSClient, *dns.Server, error) { return mdnsClient, server, err } +type testContext struct { + received_addr net.IP + received_count int +} + +func (c *testContext) checkResponse(t *testing.T, resp *ResponseA) { + if resp.err != nil { + t.Fatal(resp.err) + } + log.Printf("Got address response %s addr %s", resp.Name, resp.Addr) + c.received_addr = resp.Addr + c.received_count++ +} + func TestSimpleQuery(t *testing.T) { log.Println("TestSimpleQuery starting") mdnsClient, server, _ := setup(t) defer mdnsClient.Shutdown() defer server.Shutdown() - var received_addr net.IP - + var context testContext channel := make(chan *ResponseA, 4) // First, a test we expect to succeed mdnsClient.SendQuery(success_test_name, dns.TypeA, channel) for resp := range channel { - if resp.err != nil { - t.Fatal(resp.err) - } - log.Printf("Got address response %s addr %s", resp.Name, resp.Addr) - received_addr = resp.Addr + context.checkResponse(t, resp) } - if !received_addr.Equal(test_addr) { - t.Log("Unexpected result for", success_test_name, received_addr) + if !context.received_addr.Equal(test_addr) { + t.Log("Unexpected result for", success_test_name, context.received_addr) t.Fail() } - received_response := false // Now, a test we expect to time out with no responses + context.received_count = 0 channel2 := make(chan *ResponseA, 4) mdnsClient.SendQuery("test2.weave.", dns.TypeA, channel2) for resp := range channel2 { - if resp.err != nil { - t.Fatal(resp.err) - } - log.Printf("Got address response %s addr %s", resp.Name, resp.Addr) - received_addr = resp.Addr - received_response = true + context.checkResponse(t, resp) } - if received_response { - t.Log("Unexpected result for test2.weave", received_addr) + if context.received_count > 0 { + t.Log("Unexpected result for test2.weave", context.received_addr) t.Fail() } } From 17d192caee8c12a8fff830b57cd96bd6125ddebe Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 9 Oct 2014 14:15:02 +0100 Subject: [PATCH 058/145] Add test for two parallel queries --- server/mdns_client_test.go | 41 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index 6adcb835..f682921a 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -5,6 +5,7 @@ import ( "log" "net" "testing" + "time" ) var ( @@ -127,3 +128,43 @@ func TestSimpleQuery(t *testing.T) { t.Fail() } } + +func TestParallelQuery(t *testing.T) { + log.Println("TestParallelQuery starting") + mdnsClient, server, _ := setup(t) + defer mdnsClient.Shutdown() + defer server.Shutdown() + + var context1 testContext + var context2 testContext + channel1 := make(chan *ResponseA, 4) + channel2 := make(chan *ResponseA, 4) + + go mdnsClient.SendQuery(success_test_name, dns.TypeA, channel1) + go mdnsClient.SendQuery(success_test_name, dns.TypeA, channel2) + timeout := time.After(2 * time.Second) +outerloop: + for channel1 != nil || channel2 != nil { + select { + case resp, ok := <-channel1: + if !ok { + channel1 = nil + continue + } + context1.checkResponse(t, resp) + case resp, ok := <-channel2: + if !ok { + channel2 = nil + continue + } + context2.checkResponse(t, resp) + case <-timeout: + break outerloop + } + } + + if !context1.received_addr.Equal(test_addr) || !context2.received_addr.Equal(test_addr) || context1.received_count != 1 || context2.received_count != 1 { + t.Log("Unexpected result for", success_test_name, context1.received_addr, context2.received_addr, context1.received_count, context2.received_count) + t.Fail() + } +} From 65e95cd46a2f01ec7effdf4ab4db2026fe412be3 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 9 Oct 2014 14:46:53 +0100 Subject: [PATCH 059/145] Factor out channel --- server/mdns_client_test.go | 57 ++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index f682921a..2c70a479 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -84,9 +84,18 @@ func setup(t *testing.T) (*MDNSClient, *dns.Server, error) { type testContext struct { received_addr net.IP received_count int + channel chan *ResponseA } -func (c *testContext) checkResponse(t *testing.T, resp *ResponseA) { +func newTestContext() *testContext { + return &testContext{channel: make(chan *ResponseA, 4)} +} + +func (c *testContext) checkResponse(t *testing.T, channel_ok bool, resp *ResponseA) { + if !channel_ok { + c.channel = nil + return + } if resp.err != nil { t.Fatal(resp.err) } @@ -101,13 +110,12 @@ func TestSimpleQuery(t *testing.T) { defer mdnsClient.Shutdown() defer server.Shutdown() - var context testContext - channel := make(chan *ResponseA, 4) + context := newTestContext() // First, a test we expect to succeed - mdnsClient.SendQuery(success_test_name, dns.TypeA, channel) - for resp := range channel { - context.checkResponse(t, resp) + mdnsClient.SendQuery(success_test_name, dns.TypeA, context.channel) + for resp := range context.channel { + context.checkResponse(t, true, resp) } if !context.received_addr.Equal(test_addr) { @@ -116,11 +124,10 @@ func TestSimpleQuery(t *testing.T) { } // Now, a test we expect to time out with no responses - context.received_count = 0 - channel2 := make(chan *ResponseA, 4) - mdnsClient.SendQuery("test2.weave.", dns.TypeA, channel2) - for resp := range channel2 { - context.checkResponse(t, resp) + context = newTestContext() + mdnsClient.SendQuery("test2.weave.", dns.TypeA, context.channel) + for resp := range context.channel { + context.checkResponse(t, true, resp) } if context.received_count > 0 { @@ -135,29 +142,19 @@ func TestParallelQuery(t *testing.T) { defer mdnsClient.Shutdown() defer server.Shutdown() - var context1 testContext - var context2 testContext - channel1 := make(chan *ResponseA, 4) - channel2 := make(chan *ResponseA, 4) + context1 := newTestContext() + context2 := newTestContext() - go mdnsClient.SendQuery(success_test_name, dns.TypeA, channel1) - go mdnsClient.SendQuery(success_test_name, dns.TypeA, channel2) + go mdnsClient.SendQuery(success_test_name, dns.TypeA, context1.channel) + go mdnsClient.SendQuery(success_test_name, dns.TypeA, context2.channel) timeout := time.After(2 * time.Second) outerloop: - for channel1 != nil || channel2 != nil { + for context1.channel != nil || context2.channel != nil { select { - case resp, ok := <-channel1: - if !ok { - channel1 = nil - continue - } - context1.checkResponse(t, resp) - case resp, ok := <-channel2: - if !ok { - channel2 = nil - continue - } - context2.checkResponse(t, resp) + case resp, ok := <-context1.channel: + context1.checkResponse(t, ok, resp) + case resp, ok := <-context2.channel: + context2.checkResponse(t, ok, resp) case <-timeout: break outerloop } From d48548b1b7f758882d85e776e0bdaab3722c538e Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Thu, 9 Oct 2014 15:17:36 +0100 Subject: [PATCH 060/145] Included -iface in container args .. since it's now a command line argument, with no default. --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index db378fc3..cb93e00c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,4 +2,4 @@ FROM scratch # EXPOSE 53/udp WORKDIR /home/weave ADD ./weavedns /home/weave/ -ENTRYPOINT ["/home/weave/weavedns", "-wait", "5"] +ENTRYPOINT ["/home/weave/weavedns", "-wait", "5", "-iface", "ethwe"] From b1b75c4a8a4d42cc0dadbe4cff00d42af1d7cfa8 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Fri, 10 Oct 2014 11:53:21 +0100 Subject: [PATCH 061/145] Moved the server startup code out of main.go --- main.go | 117 ++--------------------------------------------- server/server.go | 117 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 113 deletions(-) create mode 100644 server/server.go diff --git a/main.go b/main.go index c787dc2e..5ebcc087 100644 --- a/main.go +++ b/main.go @@ -2,135 +2,26 @@ package main import ( "flag" - "fmt" - "github.com/miekg/dns" "github.com/zettio/weavedns/server" "log" - "net" - "time" ) -const ( - LOCAL_DOMAIN = "weave" -) - -var zone = new(weavedns.ZoneDb) - -func makeDNSReply(r *dns.Msg, name string, addr net.IP) *dns.Msg { - m := new(dns.Msg) - m.SetReply(r) - hdr := dns.RR_Header{Name: name, Rrtype: dns.TypeA, - Class: dns.ClassINET, Ttl: 3600} - a := &dns.A{hdr, addr} - m.Answer = append(m.Answer, a) - return m -} - -var ( - mdnsClient *weavedns.MDNSClient - mdnsServer *weavedns.MDNSServer -) - -func handleLocal(w dns.ResponseWriter, r *dns.Msg) { - q := r.Question[0] - ip, err := zone.MatchLocal(q.Name) - if err == nil { - m := makeDNSReply(r, q.Name, ip) - w.WriteMsg(m) - } else { - log.Printf("Failed lookup for %s; sending mDNS query", q.Name) - // We don't know the answer; see if someone else does - channel := make(chan *weavedns.ResponseA, 4) - go func() { - // Loop terminates when channel is closed by MDNSClient on timeout - for resp := range channel { - log.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) - m := makeDNSReply(r, resp.Name, resp.Addr) - w.WriteMsg(m) - } - }() - mdnsClient.SendQuery(q.Name, dns.TypeA, channel) - } - return -} - -func ensureInterface(ifaceName string, wait int) (iface *net.Interface, err error) { - iface, err = findInterface(ifaceName) - if err == nil || wait == 0 { - return - } - log.Println("Waiting for interface", ifaceName, "to come up") - for ; err != nil && wait > 0; wait -= 1 { - time.Sleep(1 * time.Second) - iface, err = findInterface(ifaceName) - } - if err == nil { - log.Println("Interface", ifaceName, "is up") - } - return -} - -func findInterface(ifaceName string) (iface *net.Interface, err error) { - iface, err = net.InterfaceByName(ifaceName) - if err != nil { - return iface, fmt.Errorf("Unable to find interface %s", ifaceName) - } - if 0 == (net.FlagUp & iface.Flags) { - return iface, fmt.Errorf("Interface %s is not up", ifaceName) - } - return -} - func main() { var ( ifaceName string dnsPort int + httpPort int wait int ) flag.StringVar(&ifaceName, "iface", "", "name of interface to use for multicast") flag.IntVar(&wait, "wait", 0, "number of seconds to wait for interface to be created and come up (defaults to 0)") flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to dns requests (defaults to 53)") + flag.IntVar(&httpPort, "httpport", 6785, "port to listen to HTTP requests (defaults to 6785)") flag.Parse() - var iface *net.Interface = nil - if ifaceName != "" { - var err error - iface, err = ensureInterface(ifaceName, wait) - if err != nil { - log.Fatal(err) - } - } - - LocalServeMux := dns.NewServeMux() - LocalServeMux.HandleFunc(LOCAL_DOMAIN, handleLocal) - go weavedns.ListenHttp(zone) - - var err error - mdnsClient, err = weavedns.NewMDNSClient() + err := weavedns.StartServer(ifaceName, dnsPort, httpPort, wait) if err != nil { - log.Fatal(err) - } else { - log.Printf("Using mDNS on %s", ifaceName) - } - err = mdnsClient.Start(iface) - if err != nil { - log.Fatal(err) - } - mdnsServer, err = weavedns.NewMDNSServer(zone) - if err != nil { - log.Fatal(err) - } - err = mdnsServer.Start(iface) - if err != nil { - log.Fatal(err) - } - - address := fmt.Sprintf(":%d", dnsPort) - err = dns.ListenAndServe(address, "udp", LocalServeMux) - if err != nil { - log.Fatal(err) - } else { - log.Printf("Listening for DNS on %s", address) + log.Fatalf("Failed to start server: %s", err) } } diff --git a/server/server.go b/server/server.go new file mode 100644 index 00000000..3c02e9a8 --- /dev/null +++ b/server/server.go @@ -0,0 +1,117 @@ +package weavedns + +import ( + "fmt" + "github.com/miekg/dns" + "log" + "net" + "time" +) + +const ( + LOCAL_DOMAIN = "weave" +) + +func ensureInterface(ifaceName string, wait int) (iface *net.Interface, err error) { + iface, err = findInterface(ifaceName) + if err == nil || wait == 0 { + return + } + log.Println("Waiting for interface", ifaceName, "to come up") + for ; err != nil && wait > 0; wait -= 1 { + time.Sleep(1 * time.Second) + iface, err = findInterface(ifaceName) + } + if err == nil { + log.Println("Interface", ifaceName, "is up") + } + return +} + +func findInterface(ifaceName string) (iface *net.Interface, err error) { + iface, err = net.InterfaceByName(ifaceName) + if err != nil { + return iface, fmt.Errorf("Unable to find interface %s", ifaceName) + } + if 0 == (net.FlagUp & iface.Flags) { + return iface, fmt.Errorf("Interface %s is not up", ifaceName) + } + return +} + +func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { + return func(w dns.ResponseWriter, r *dns.Msg) { + q := r.Question[0] + ip, err := zone.MatchLocal(q.Name) + if err == nil { + m := makeDNSReply(r, q.Name, ip) + w.WriteMsg(m) + } else { + log.Printf("Failed lookup for %s; sending mDNS query", q.Name) + // We don't know the answer; see if someone else does + channel := make(chan *ResponseA, 4) + go func() { + // Loop terminates when channel is closed by MDNSClient on timeout + for resp := range channel { + log.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) + m := makeDNSReply(r, resp.Name, resp.Addr) + w.WriteMsg(m) + } + }() + mdnsClient.SendQuery(q.Name, dns.TypeA, channel) + } + return + } +} + +func StartServer(ifaceName string, dnsPort int, httpPort int, wait int) error { + + var zone = new(ZoneDb) + go ListenHttp(zone) + + var ( + mdnsClient *MDNSClient + mdnsServer *MDNSServer + ) + + var iface *net.Interface = nil + if ifaceName != "" { + var err error + iface, err = ensureInterface(ifaceName, wait) + if err != nil { + log.Fatal(err) + } + } + + LocalServeMux := dns.NewServeMux() + LocalServeMux.HandleFunc(LOCAL_DOMAIN, queryHandler(zone, mdnsClient)) + + var err error + mdnsClient, err = NewMDNSClient() + if err != nil { + log.Fatal(err) + } else { + log.Printf("Using mDNS on %s", ifaceName) + } + err = mdnsClient.Start(iface) + if err != nil { + log.Fatal(err) + } + mdnsServer, err = NewMDNSServer(zone) + if err != nil { + log.Fatal(err) + } + err = mdnsServer.Start(iface) + if err != nil { + log.Fatal(err) + } + + address := fmt.Sprintf(":%d", dnsPort) + err = dns.ListenAndServe(address, "udp", LocalServeMux) + if err != nil { + log.Fatal(err) + } else { + log.Printf("Listening for DNS on %s", address) + } + return nil +} From c384bee2267829a0ebb705c54a701103bc00c37d Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 10 Oct 2014 14:27:52 +0100 Subject: [PATCH 062/145] Add -tags netgo --- docker/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Makefile b/docker/Makefile index 43de299d..18e9da9a 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -6,7 +6,7 @@ MAIN:=../ all: docker-image weavedns: ../server/*.go ../main.go - go get $(MAIN) && GOARCH=amd64 GOOS=linux go build -ldflags '-linkmode external -extldflags "-static"' -o $@ -v $(MAIN) + go get -tags netgo $(MAIN) && GOARCH=amd64 GOOS=linux go build -tags netgo -ldflags '-linkmode external -extldflags "-static"' -o $@ -v $(MAIN) .PHONY: docker-image From de65dc7996f2e4f7a71d854d0adf35410a4bca46 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 10 Oct 2014 15:34:54 +0100 Subject: [PATCH 063/145] Move usage of mdnsClient after initialization --- server/server.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/server/server.go b/server/server.go index 3c02e9a8..e90cd99e 100644 --- a/server/server.go +++ b/server/server.go @@ -69,11 +69,6 @@ func StartServer(ifaceName string, dnsPort int, httpPort int, wait int) error { var zone = new(ZoneDb) go ListenHttp(zone) - var ( - mdnsClient *MDNSClient - mdnsServer *MDNSServer - ) - var iface *net.Interface = nil if ifaceName != "" { var err error @@ -83,11 +78,7 @@ func StartServer(ifaceName string, dnsPort int, httpPort int, wait int) error { } } - LocalServeMux := dns.NewServeMux() - LocalServeMux.HandleFunc(LOCAL_DOMAIN, queryHandler(zone, mdnsClient)) - - var err error - mdnsClient, err = NewMDNSClient() + mdnsClient, err := NewMDNSClient() if err != nil { log.Fatal(err) } else { @@ -97,7 +88,11 @@ func StartServer(ifaceName string, dnsPort int, httpPort int, wait int) error { if err != nil { log.Fatal(err) } - mdnsServer, err = NewMDNSServer(zone) + + LocalServeMux := dns.NewServeMux() + LocalServeMux.HandleFunc(LOCAL_DOMAIN, queryHandler(zone, mdnsClient)) + + mdnsServer, err := NewMDNSServer(zone) if err != nil { log.Fatal(err) } From b5214f65e7828960259766670326c1f50126a15c Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 10 Oct 2014 15:41:09 +0100 Subject: [PATCH 064/145] Forward DNS requests that aren't for our 'weave' domain --- server/mdns_server.go | 1 + server/server.go | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/server/mdns_server.go b/server/mdns_server.go index ac605a66..b9ccd6ba 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -27,6 +27,7 @@ func NewMDNSServer(zone *ZoneDb) (*MDNSServer, error) { func makeDNSReply(r *dns.Msg, name string, addr net.IP) *dns.Msg { m := new(dns.Msg) m.SetReply(r) + m.RecursionAvailable = true hdr := dns.RR_Header{Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600} a := &dns.A{hdr, addr} diff --git a/server/server.go b/server/server.go index e90cd99e..36adf65c 100644 --- a/server/server.go +++ b/server/server.go @@ -39,6 +39,14 @@ func findInterface(ifaceName string) (iface *net.Interface, err error) { return } +func makeDNSFailResponse(r *dns.Msg) *dns.Msg { + m := new(dns.Msg) + m.SetReply(r) + m.RecursionAvailable = true + m.Rcode = dns.RcodeNameError + return m +} + func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] @@ -64,6 +72,20 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { } } +func notUsHandler() dns.HandlerFunc { + return func(w dns.ResponseWriter, r *dns.Msg) { + q := r.Question[0] + addrs, err := net.LookupIP(q.Name) + if err == nil { + m := makeDNSReply(r, q.Name, addrs[0]) + w.WriteMsg(m) + } else { + m := makeDNSFailResponse(r) + w.WriteMsg(m) + } + } +} + func StartServer(ifaceName string, dnsPort int, httpPort int, wait int) error { var zone = new(ZoneDb) @@ -91,6 +113,7 @@ func StartServer(ifaceName string, dnsPort int, httpPort int, wait int) error { LocalServeMux := dns.NewServeMux() LocalServeMux.HandleFunc(LOCAL_DOMAIN, queryHandler(zone, mdnsClient)) + LocalServeMux.HandleFunc(".", notUsHandler()) mdnsServer, err := NewMDNSServer(zone) if err != nil { From 8521fdea0e4cff0cb1e41c2ca62c3e9960b60c9d Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 10 Oct 2014 16:31:09 +0100 Subject: [PATCH 065/145] Reply with array of addresses for delegated query --- server/mdns_server.go | 16 ++++++++++------ server/server.go | 13 ++++++++++--- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/server/mdns_server.go b/server/mdns_server.go index b9ccd6ba..d289b642 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -24,14 +24,18 @@ func NewMDNSServer(zone *ZoneDb) (*MDNSServer, error) { return retval, nil } -func makeDNSReply(r *dns.Msg, name string, addr net.IP) *dns.Msg { +func makeDNSReply(r *dns.Msg, name string, addrs []net.IP) *dns.Msg { m := new(dns.Msg) m.SetReply(r) m.RecursionAvailable = true - hdr := dns.RR_Header{Name: name, Rrtype: dns.TypeA, - Class: dns.ClassINET, Ttl: 3600} - a := &dns.A{hdr, addr} - m.Answer = append(m.Answer, a) + m.Answer = make([]dns.RR, len(addrs)) + for i, addr := range addrs { + if ip4 := addr.To4(); ip4 != nil { + hdr := dns.RR_Header{Name: name, Rrtype: dns.TypeA, + Class: dns.ClassINET, Ttl: 3600} + m.Answer[i] = &dns.A{hdr, addr} + } + } return m } @@ -56,7 +60,7 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { q := r.Question[0] ip, err := s.zone.MatchLocal(q.Name) if err == nil { - m := makeDNSReply(r, q.Name, ip) + m := makeDNSReply(r, q.Name, []net.IP{ip}) s.SendResponse(m) } else if s.addrIsLocal(w.RemoteAddr()) { // ignore this - it's our own query received via multicast diff --git a/server/server.go b/server/server.go index 36adf65c..9a67701f 100644 --- a/server/server.go +++ b/server/server.go @@ -52,7 +52,7 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { q := r.Question[0] ip, err := zone.MatchLocal(q.Name) if err == nil { - m := makeDNSReply(r, q.Name, ip) + m := makeDNSReply(r, q.Name, []net.IP{ip}) w.WriteMsg(m) } else { log.Printf("Failed lookup for %s; sending mDNS query", q.Name) @@ -62,7 +62,7 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { // Loop terminates when channel is closed by MDNSClient on timeout for resp := range channel { log.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) - m := makeDNSReply(r, resp.Name, resp.Addr) + m := makeDNSReply(r, resp.Name, []net.IP{resp.Addr}) w.WriteMsg(m) } }() @@ -77,7 +77,14 @@ func notUsHandler() dns.HandlerFunc { q := r.Question[0] addrs, err := net.LookupIP(q.Name) if err == nil { - m := makeDNSReply(r, q.Name, addrs[0]) + // Filter out ipv6 addresses + filtered := make([]net.IP, 0) + for _, addr := range addrs { + if ip4 := addr.To4(); ip4 != nil { + filtered = append(filtered, ip4) + } + } + m := makeDNSReply(r, q.Name, filtered) w.WriteMsg(m) } else { m := makeDNSFailResponse(r) From ed6c7cb78468a2ceeb495b10fbdc302cff8027fd Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 10 Oct 2014 17:14:06 +0100 Subject: [PATCH 066/145] Concatenate multiple replies received over mDNS --- server/server.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server/server.go b/server/server.go index 9a67701f..fb7bf61b 100644 --- a/server/server.go +++ b/server/server.go @@ -58,11 +58,18 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { log.Printf("Failed lookup for %s; sending mDNS query", q.Name) // We don't know the answer; see if someone else does channel := make(chan *ResponseA, 4) + replies := make([]net.IP, 0) go func() { // Loop terminates when channel is closed by MDNSClient on timeout for resp := range channel { log.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) - m := makeDNSReply(r, resp.Name, []net.IP{resp.Addr}) + replies = append(replies, resp.Addr) + } + if len(replies) > 0 { + m := makeDNSReply(r, q.Name, replies) + w.WriteMsg(m) + } else { + m := makeDNSFailResponse(r) w.WriteMsg(m) } }() From be3d0894108a431fd2426b3f602881afb192c19c Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 10 Oct 2014 17:14:39 +0100 Subject: [PATCH 067/145] Shorten mDNS timeout --- server/mdns_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index 45edef45..48d2aa7b 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -12,7 +12,7 @@ import ( const ( ipv4mdns = "224.0.0.251" // link-local multicast address mdnsPort = 5353 // mDNS assigned port - mDNSTimeout = time.Second + mDNSTimeout = 200 * time.Millisecond MaxDuration = time.Duration(math.MaxInt64) ) From 03cd04249cac8c488177906a640b70b37d64a158 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 10 Oct 2014 17:36:23 +0100 Subject: [PATCH 068/145] Check number of results received --- server/mdns_client_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index 2c70a479..0eebf251 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -118,8 +118,8 @@ func TestSimpleQuery(t *testing.T) { context.checkResponse(t, true, resp) } - if !context.received_addr.Equal(test_addr) { - t.Log("Unexpected result for", success_test_name, context.received_addr) + if !context.received_addr.Equal(test_addr) || context.received_count != 1 { + t.Log("Unexpected result for", success_test_name, context.received_addr, context.received_count) t.Fail() } From 50d343e2fb8e628c87cbdc901895efcbd3ff7297 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 10 Oct 2014 17:36:46 +0100 Subject: [PATCH 069/145] Use specified port for http listener --- server/http.go | 8 ++------ server/http_test.go | 8 ++++++-- server/server.go | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/server/http.go b/server/http.go index 4981aaad..e0eb40e9 100644 --- a/server/http.go +++ b/server/http.go @@ -7,11 +7,7 @@ import ( "net/http" ) -const ( - UPDATE_PORT = 6785 -) - -func ListenHttp(db Zone) { +func ListenHttp(db Zone, port int) { http.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") addr := r.FormValue("ip") @@ -31,7 +27,7 @@ func ListenHttp(db Zone) { log.Printf("Invalid request: %s, %s, %s", name, addr, weave_cidr) } }) - address := fmt.Sprintf(":%d", UPDATE_PORT) + address := fmt.Sprintf(":%d", port) err := http.ListenAndServe(address, nil) if err != nil { log.Fatal("Unable to create http listener: ", err) diff --git a/server/http_test.go b/server/http_test.go index 53b7cbf3..7a1cfe78 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -1,6 +1,8 @@ package weavedns import ( + "fmt" + "math/rand" "net" "net/http" "net/url" @@ -16,11 +18,13 @@ func TestHttp(t *testing.T) { ) var zone = new(ZoneDb) - go ListenHttp(zone) + port := rand.Intn(10000) + 32768 + fmt.Println("Http test on port", port) + go ListenHttp(zone, port) time.Sleep(100 * time.Millisecond) // Allow for http server to get going - resp, err := http.PostForm("http://localhost:6785/add", + resp, err := http.PostForm(fmt.Sprintf("http://localhost:%d/add", port), url.Values{"name": {success_test_name}, "ip": {docker_ip}, "weave_cidr": {test_addr1}}) if err != nil { t.Fatal(err) diff --git a/server/server.go b/server/server.go index fb7bf61b..beee303e 100644 --- a/server/server.go +++ b/server/server.go @@ -103,7 +103,7 @@ func notUsHandler() dns.HandlerFunc { func StartServer(ifaceName string, dnsPort int, httpPort int, wait int) error { var zone = new(ZoneDb) - go ListenHttp(zone) + go ListenHttp(zone, httpPort) var iface *net.Interface = nil if ifaceName != "" { From 5935159d9d22ae42c49d77e9cc03f0dc49ab9bfe Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 13 Oct 2014 15:43:38 +0100 Subject: [PATCH 070/145] Removed cross-compilation bobbins Cross-compilation isn't working, and it's misleading to suggest it does by having these here. --- docker/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Makefile b/docker/Makefile index 18e9da9a..b1ca2b2b 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -6,7 +6,7 @@ MAIN:=../ all: docker-image weavedns: ../server/*.go ../main.go - go get -tags netgo $(MAIN) && GOARCH=amd64 GOOS=linux go build -tags netgo -ldflags '-linkmode external -extldflags "-static"' -o $@ -v $(MAIN) + go get -tags netgo $(MAIN) && go build -tags netgo -ldflags '-linkmode external -extldflags "-static"' -o $@ -v $(MAIN) .PHONY: docker-image From 16732c9e0ce769dd4b6bc081c7d7717e74ec33e2 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 13 Oct 2014 16:32:59 +0100 Subject: [PATCH 071/145] Changed payload format We need the docker (host network) IP, and the FQDN rather than just the domain. --- design.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/design.md b/design.md index 919a2bd3..67c44214 100644 --- a/design.md +++ b/design.md @@ -48,8 +48,9 @@ PUTs), or a JSON object with the same fields. The fields are: ```js { - "domain": string, - "routing_prefix": number + "fqdn": string, + "routing_prefix": number, + "local_ip": string } ``` From 6bff651e87a6182fd2533e108ad5fd41add12e29 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 11:55:45 +0100 Subject: [PATCH 072/145] Changed to new URL scheme with container identifier Implementation matches design.md better: - http action changes from POST /add to PUT /name - each record now has the container identifier --- server/http.go | 44 +++++++++++++++++++++++++++----------- server/http_test.go | 13 +++++++++-- server/mdns_server_test.go | 5 +++-- server/zone.go | 7 +++--- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/server/http.go b/server/http.go index e0eb40e9..35697c5c 100644 --- a/server/http.go +++ b/server/http.go @@ -1,30 +1,48 @@ package weavedns import ( + "errors" "fmt" "log" "net" "net/http" + "strings" ) +// Parse a URL of the form PUT /name// +func parseUrl(url string) (identifier string, ipaddr string, err error) { + parts := strings.Split(url, "/") + if len(parts) != 4 { + return "", "", errors.New("Unable to parse url: " + url) + } + return parts[2], parts[3], nil +} + func ListenHttp(db Zone, port int) { - http.HandleFunc("/add", func(w http.ResponseWriter, r *http.Request) { - name := r.FormValue("name") - addr := r.FormValue("ip") - weave_cidr := r.FormValue("weave_cidr") - if name != "" && addr != "" && weave_cidr != "" { - ip := net.ParseIP(addr) + http.HandleFunc("/name/", func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case "PUT": + identifier, weave_ipstr, err := parseUrl(r.URL.Path) + name := r.FormValue("fqdn") + prefix := r.FormValue("routing_prefix") + local_ip := r.FormValue("local_ip") + if identifier == "" || weave_ipstr == "" || name == "" || prefix == "" || local_ip == "" { + log.Printf("Invalid request: %s, %s", r.URL, r.Form) + return + } + ip := net.ParseIP(local_ip) + if ip == nil { + log.Printf("Invalid IP in request: %s", local_ip) + return + } + weave_cidr := weave_ipstr + "/" + prefix weave_ip, subnet, err := net.ParseCIDR(weave_cidr) - if err == nil && ip != nil { - log.Printf("Adding %s (%s) -> %s", name, addr, weave_cidr) - db.AddRecord(name, ip, weave_ip, subnet) + if err == nil { + log.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) + db.AddRecord(identifier, name, ip, weave_ip, subnet) } else if err != nil { log.Printf("Invalid CIDR in request: %s", weave_cidr) - } else { - log.Printf("Invalid IP in request: %s", addr) } - } else { - log.Printf("Invalid request: %s, %s, %s", name, addr, weave_cidr) } }) address := fmt.Sprintf(":%d", port) diff --git a/server/http_test.go b/server/http_test.go index 7a1cfe78..328bafe2 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -6,12 +6,20 @@ import ( "net" "net/http" "net/url" + "strings" "testing" "time" ) +func putForm(url string, data url.Values) (resp *http.Response, err error) { + req, err := http.NewRequest("PUT", url, strings.NewReader(data.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + return http.DefaultClient.Do(req) +} + func TestHttp(t *testing.T) { var ( + container_id = "deadbeef" success_test_name = "test1.weave." test_addr1 = "10.0.2.1/24" docker_ip = "9.8.7.6" @@ -24,8 +32,9 @@ func TestHttp(t *testing.T) { time.Sleep(100 * time.Millisecond) // Allow for http server to get going - resp, err := http.PostForm(fmt.Sprintf("http://localhost:%d/add", port), - url.Values{"name": {success_test_name}, "ip": {docker_ip}, "weave_cidr": {test_addr1}}) + addr_parts := strings.Split(test_addr1, "/") + resp, err := putForm(fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]), + url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) if err != nil { t.Fatal(err) } diff --git a/server/mdns_server_test.go b/server/mdns_server_test.go index 68e2f387..aafe821e 100644 --- a/server/mdns_server_test.go +++ b/server/mdns_server_test.go @@ -9,7 +9,8 @@ import ( ) var ( - test_addr1 = "10.0.2.1/24" + container_id = "deadbeef" + test_addr1 = "10.0.2.1/24" ) func sendQuery(name string, querytype uint16) error { @@ -33,7 +34,7 @@ func TestServerSimpleQuery(t *testing.T) { var zone = new(ZoneDb) docker_ip := net.ParseIP("9.8.7.6") weave_ip, subnet, _ := net.ParseCIDR(test_addr1) - zone.AddRecord("test.weave.", docker_ip, weave_ip, subnet) + zone.AddRecord(container_id, "test.weave.", docker_ip, weave_ip, subnet) mdnsServer, err := NewMDNSServer(zone) if err != nil { diff --git a/server/zone.go b/server/zone.go index 824dd8d8..6b8a8d1f 100644 --- a/server/zone.go +++ b/server/zone.go @@ -8,11 +8,12 @@ import ( ) type Zone interface { - AddRecord(string, net.IP, net.IP, *net.IPNet) error + AddRecord(string, string, net.IP, net.IP, *net.IPNet) error MatchLocal(string) (net.IP, error) } type Record struct { + Ident string Name string Ip net.IP WeaveIp net.IP @@ -47,9 +48,9 @@ func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { return zone.match(name) } -func (zone *ZoneDb) AddRecord(name string, ip net.IP, weave_ip net.IP, weave_subnet *net.IPNet) error { +func (zone *ZoneDb) AddRecord(identifier string, name string, ip net.IP, weave_ip net.IP, weave_subnet *net.IPNet) error { zone.mx.Lock() defer zone.mx.Unlock() - zone.recs = append(zone.recs, Record{dns.Fqdn(name), ip, weave_ip, weave_subnet}) + zone.recs = append(zone.recs, Record{identifier, dns.Fqdn(name), ip, weave_ip, weave_subnet}) return nil } From 36fa3cca7343ca4b903ef16a9e5e7c935c4212ff Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 14:12:42 +0100 Subject: [PATCH 073/145] Tidy up --- server/http_test.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/server/http_test.go b/server/http_test.go index 328bafe2..f54a2d2d 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -32,6 +32,7 @@ func TestHttp(t *testing.T) { time.Sleep(100 * time.Millisecond) // Allow for http server to get going + // Ask the http server to add our test address into the database addr_parts := strings.Split(test_addr1, "/") resp, err := putForm(fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]), url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) @@ -39,19 +40,17 @@ func TestHttp(t *testing.T) { t.Fatal(err) } if resp.StatusCode != 200 { - t.Log("Unexpected http response", resp.Status) - t.Fail() + t.Fatal("Unexpected http response", resp.Status) } + // Check that the address is now there. ip, err := zone.MatchLocal(success_test_name) if err != nil { - t.Log(zone) t.Fatal(err) } weave_ip, _, _ := net.ParseCIDR(test_addr1) if !ip.Equal(weave_ip) { - t.Log("Unexpected result for", success_test_name, ip) - t.Fail() + t.Fatal("Unexpected result for", success_test_name, ip) } // Would like to shut down the http server at the end of this test From 7f6e0a1e428eaea3fe5dbecdf391a22c2715b1ae Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 15:19:20 +0100 Subject: [PATCH 074/145] Make error message more helpful --- server/zone.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/zone.go b/server/zone.go index 6b8a8d1f..d80fcfbd 100644 --- a/server/zone.go +++ b/server/zone.go @@ -28,7 +28,7 @@ type ZoneDb struct { type LookupError string func (ops LookupError) Error() string { - return string(ops) + return "Unable to find " + string(ops) } // Stop gap. From 9bd464a00393e718f439cf6ef3faa61d741e44b1 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 15:23:20 +0100 Subject: [PATCH 075/145] Make putForm more generic --- server/http_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/http_test.go b/server/http_test.go index f54a2d2d..ac81f70c 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -11,8 +11,8 @@ import ( "time" ) -func putForm(url string, data url.Values) (resp *http.Response, err error) { - req, err := http.NewRequest("PUT", url, strings.NewReader(data.Encode())) +func genForm(method string, url string, data url.Values) (resp *http.Response, err error) { + req, err := http.NewRequest(method, url, strings.NewReader(data.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") return http.DefaultClient.Do(req) } @@ -34,7 +34,7 @@ func TestHttp(t *testing.T) { // Ask the http server to add our test address into the database addr_parts := strings.Split(test_addr1, "/") - resp, err := putForm(fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]), + resp, err := genForm("PUT", fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]), url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) if err != nil { t.Fatal(err) From 8ebc06315c4609a4c3e9264c8dda4dd45676ede9 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 16:06:08 +0100 Subject: [PATCH 076/145] Detect duplicate add --- server/zone.go | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/server/zone.go b/server/zone.go index d80fcfbd..6957b040 100644 --- a/server/zone.go +++ b/server/zone.go @@ -31,6 +31,15 @@ func (ops LookupError) Error() string { return "Unable to find " + string(ops) } +type DuplicateError struct { + Name string + WeaveIp net.IP +} + +func (err DuplicateError) Error() string { + return "Duplicate " + err.Name + "," + err.WeaveIp.String() +} + // Stop gap. func (zone *ZoneDb) match(name string) (net.IP, error) { for _, r := range zone.recs { @@ -42,6 +51,15 @@ func (zone *ZoneDb) match(name string) (net.IP, error) { return nil, LookupError(name) } +func (zone *ZoneDb) indexOfNameAddr(name string, addr net.IP) int { + for i, r := range zone.recs { + if r.Name == name && r.WeaveIp.Equal(addr) { + return i + } + } + return -1 +} + func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { zone.mx.Lock() defer zone.mx.Unlock() @@ -51,6 +69,10 @@ func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { func (zone *ZoneDb) AddRecord(identifier string, name string, ip net.IP, weave_ip net.IP, weave_subnet *net.IPNet) error { zone.mx.Lock() defer zone.mx.Unlock() - zone.recs = append(zone.recs, Record{identifier, dns.Fqdn(name), ip, weave_ip, weave_subnet}) + fqdn := dns.Fqdn(name) + if zone.indexOfNameAddr(fqdn, weave_ip) != -1 { + return DuplicateError{fqdn, weave_ip} + } + zone.recs = append(zone.recs, Record{identifier, fqdn, ip, weave_ip, weave_subnet}) return nil } From bc87c7c94b91a6d4718f74fb2be62b65670439c9 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 16:06:25 +0100 Subject: [PATCH 077/145] Initial Zone tests --- server/zone_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 server/zone_test.go diff --git a/server/zone_test.go b/server/zone_test.go new file mode 100644 index 00000000..6f392c9d --- /dev/null +++ b/server/zone_test.go @@ -0,0 +1,39 @@ +package weavedns + +import ( + "net" + "testing" +) + +func TestZone(t *testing.T) { + var ( + container_id = "deadbeef" + success_test_name = "test1.weave." + test_addr1 = "10.0.2.1/24" + docker_ip = "9.8.7.6" + ) + + var zone = new(ZoneDb) + + ip := net.ParseIP(docker_ip) + weave_ip, subnet, _ := net.ParseCIDR(test_addr1) + err := zone.AddRecord(container_id, success_test_name, ip, weave_ip, subnet) + if err != nil { + t.Fatal(err) + } + + // Check that the address is now there. + found_ip, err := zone.MatchLocal(success_test_name) + if err != nil { + t.Fatal(err) + } + if !found_ip.Equal(weave_ip) { + t.Fatal("Unexpected result for", success_test_name, ip) + } + + // Now try to add the same address again + err = zone.AddRecord(container_id, success_test_name, ip, weave_ip, subnet) + if _, ok := err.(DuplicateError); !ok { + t.Fatal(err) + } +} From 10772f828902987e4a52cd68c29f1e450db1252c Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 16:12:50 +0100 Subject: [PATCH 078/145] Better error-checking --- server/http.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/server/http.go b/server/http.go index 35697c5c..910651fb 100644 --- a/server/http.go +++ b/server/http.go @@ -37,12 +37,20 @@ func ListenHttp(db Zone, port int) { } weave_cidr := weave_ipstr + "/" + prefix weave_ip, subnet, err := net.ParseCIDR(weave_cidr) - if err == nil { - log.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) - db.AddRecord(identifier, name, ip, weave_ip, subnet) - } else if err != nil { + if err != nil { log.Printf("Invalid CIDR in request: %s", weave_cidr) + http.Error(w, fmt.Sprintf("Invalid CIDR: %s", weave_cidr), http.StatusBadRequest) + return } + log.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) + err = db.AddRecord(identifier, name, ip, weave_ip, subnet) + if err != nil { + http.Error(w, err.Error(), http.StatusConflict) + return + } + default: + log.Println("Unexpected http method", r.Method) + http.Error(w, "Unexpected http method: "+r.Method, http.StatusBadRequest) } }) address := fmt.Sprintf(":%d", port) From ff540265a98ce7d59a05d0ebe2880c6d411b9855 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 16:49:27 +0100 Subject: [PATCH 079/145] Better error-checking --- server/http.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/http.go b/server/http.go index 910651fb..fffef8ad 100644 --- a/server/http.go +++ b/server/http.go @@ -28,6 +28,7 @@ func ListenHttp(db Zone, port int) { local_ip := r.FormValue("local_ip") if identifier == "" || weave_ipstr == "" || name == "" || prefix == "" || local_ip == "" { log.Printf("Invalid request: %s, %s", r.URL, r.Form) + http.Error(w, "Invalid Request", http.StatusBadRequest) return } ip := net.ParseIP(local_ip) From 6d32ac80b78ad4ed613f9a41ba59efe672e2668d Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 16:50:20 +0100 Subject: [PATCH 080/145] Better error reporting --- server/http.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/http.go b/server/http.go index fffef8ad..29484521 100644 --- a/server/http.go +++ b/server/http.go @@ -34,6 +34,7 @@ func ListenHttp(db Zone, port int) { ip := net.ParseIP(local_ip) if ip == nil { log.Printf("Invalid IP in request: %s", local_ip) + http.Error(w, "Invalid IP in request", http.StatusBadRequest) return } weave_cidr := weave_ipstr + "/" + prefix From 4709f852c3428ac6ad53cbdba86ecf33cb8eaa27 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 16:59:00 +0100 Subject: [PATCH 081/145] Better error reporting --- server/zone.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/zone.go b/server/zone.go index 6957b040..bc33fd5c 100644 --- a/server/zone.go +++ b/server/zone.go @@ -34,10 +34,11 @@ func (ops LookupError) Error() string { type DuplicateError struct { Name string WeaveIp net.IP + Ident string } func (err DuplicateError) Error() string { - return "Duplicate " + err.Name + "," + err.WeaveIp.String() + return "Duplicate " + err.Name + "," + err.WeaveIp.String() + " in container " + err.Ident } // Stop gap. @@ -70,8 +71,8 @@ func (zone *ZoneDb) AddRecord(identifier string, name string, ip net.IP, weave_i zone.mx.Lock() defer zone.mx.Unlock() fqdn := dns.Fqdn(name) - if zone.indexOfNameAddr(fqdn, weave_ip) != -1 { - return DuplicateError{fqdn, weave_ip} + if index := zone.indexOfNameAddr(fqdn, weave_ip); index != -1 { + return DuplicateError{fqdn, weave_ip, zone.recs[index].Ident} } zone.recs = append(zone.recs, Record{identifier, fqdn, ip, weave_ip, weave_subnet}) return nil From 11ec66111a146fd2c894a0d8a11766dc3d06e600 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 17:00:14 +0100 Subject: [PATCH 082/145] Check duplicate add --- server/http_test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/http_test.go b/server/http_test.go index ac81f70c..662f86d8 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -34,7 +34,8 @@ func TestHttp(t *testing.T) { // Ask the http server to add our test address into the database addr_parts := strings.Split(test_addr1, "/") - resp, err := genForm("PUT", fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]), + addr_url := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]) + resp, err := genForm("PUT", addr_url, url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) if err != nil { t.Fatal(err) @@ -53,6 +54,15 @@ func TestHttp(t *testing.T) { t.Fatal("Unexpected result for", success_test_name, ip) } + // Now try adding the same address again - should fail + resp, err = genForm("PUT", addr_url, + url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) + if err != nil { + t.Fatal(err) + } + if resp.StatusCode != http.StatusConflict { + t.Fatal("Unexpected http response", resp.Status) + } // Would like to shut down the http server at the end of this test // but it's complicated. // See https://groups.google.com/forum/#!topic/golang-nuts/vLHWa5sHnCE From 6e3e7dcd681ae161de6e64a6b11f69655915c302 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 14 Oct 2014 17:01:13 +0100 Subject: [PATCH 083/145] Support DELETE function --- server/http.go | 19 +++++++++++++++++++ server/http_test.go | 16 ++++++++++++++++ server/zone.go | 22 ++++++++++++++++++++++ server/zone_test.go | 12 ++++++++++++ 4 files changed, 69 insertions(+) diff --git a/server/http.go b/server/http.go index 29484521..34c929be 100644 --- a/server/http.go +++ b/server/http.go @@ -50,6 +50,25 @@ func ListenHttp(db Zone, port int) { http.Error(w, err.Error(), http.StatusConflict) return } + case "DELETE": + identifier, weave_ipstr, err := parseUrl(r.URL.Path) + if identifier == "" || weave_ipstr == "" { + log.Printf("Invalid request: %s, %s", r.URL, r.Form) + http.Error(w, "Invalid Request", http.StatusBadRequest) + return + } + weave_ip := net.ParseIP(weave_ipstr) + if weave_ip == nil { + log.Printf("Invalid IP in request: %s", weave_ipstr) + http.Error(w, "Invalid IP in request", http.StatusBadRequest) + return + } + log.Printf("Deleting %s (%s)", identifier, weave_ipstr) + err = db.DeleteRecord(identifier, weave_ip) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } default: log.Println("Unexpected http method", r.Method) http.Error(w, "Unexpected http method: "+r.Method, http.StatusBadRequest) diff --git a/server/http_test.go b/server/http_test.go index 662f86d8..483dc920 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -63,6 +63,22 @@ func TestHttp(t *testing.T) { if resp.StatusCode != http.StatusConflict { t.Fatal("Unexpected http response", resp.Status) } + + // Delete the address + resp, err = genForm("DELETE", addr_url, nil) + if err != nil { + t.Fatal(err) + } + if resp.StatusCode != http.StatusOK { + t.Fatal("Unexpected http response", resp.Status) + } + + // Check that the address is not there now. + _, err = zone.MatchLocal(success_test_name) + if _, ok := err.(LookupError); !ok { + t.Fatal(err) + } + // Would like to shut down the http server at the end of this test // but it's complicated. // See https://groups.google.com/forum/#!topic/golang-nuts/vLHWa5sHnCE diff --git a/server/zone.go b/server/zone.go index bc33fd5c..d3173949 100644 --- a/server/zone.go +++ b/server/zone.go @@ -9,6 +9,7 @@ import ( type Zone interface { AddRecord(string, string, net.IP, net.IP, *net.IPNet) error + DeleteRecord(ident string, weave_ip net.IP) error MatchLocal(string) (net.IP, error) } @@ -61,6 +62,15 @@ func (zone *ZoneDb) indexOfNameAddr(name string, addr net.IP) int { return -1 } +func (zone *ZoneDb) indexOfIdentAddr(ident string, addr net.IP) int { + for i, r := range zone.recs { + if r.Ident == ident && r.WeaveIp.Equal(addr) { + return i + } + } + return -1 +} + func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { zone.mx.Lock() defer zone.mx.Unlock() @@ -77,3 +87,15 @@ func (zone *ZoneDb) AddRecord(identifier string, name string, ip net.IP, weave_i zone.recs = append(zone.recs, Record{identifier, fqdn, ip, weave_ip, weave_subnet}) return nil } + +func (zone *ZoneDb) DeleteRecord(ident string, weave_ip net.IP) error { + zone.mx.Lock() + defer zone.mx.Unlock() + if index := zone.indexOfIdentAddr(ident, weave_ip); index == -1 { + return LookupError(ident) + } else { + zone.recs = append(zone.recs[:index], zone.recs[index+1:]...) + } + + return nil +} diff --git a/server/zone_test.go b/server/zone_test.go index 6f392c9d..a3c7b252 100644 --- a/server/zone_test.go +++ b/server/zone_test.go @@ -36,4 +36,16 @@ func TestZone(t *testing.T) { if _, ok := err.(DuplicateError); !ok { t.Fatal(err) } + + // Now delete the record + err = zone.DeleteRecord(container_id, weave_ip) + if err != nil { + t.Fatal(err) + } + + // Check that the address is not there now. + _, err = zone.MatchLocal(success_test_name) + if _, ok := err.(LookupError); !ok { + t.Fatal("Unexpected result when deleting record", success_test_name, err) + } } From 4949f344619dfd4afae8901b8ac5b5a6ff37b227 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 13 Oct 2014 10:50:27 +0100 Subject: [PATCH 084/145] Added container updates watcher --- updater/main.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 updater/main.go diff --git a/updater/main.go b/updater/main.go new file mode 100644 index 00000000..0b66d1cc --- /dev/null +++ b/updater/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "flag" + "github.com/fsouza/go-dockerclient" + "log" +) + +func main() { + var ( + serverAddr string + serverPort int + apiPath string + ) + flag.StringVar(&serverAddr, "host", "", "address or hostname of server to update") + flag.IntVar(&serverPort, "port", 6785, "port on server to send updates to") + flag.StringVar(&apiPath, "api", "unix:///var/run/docker.sock", + "Path to Docker API socket") + flag.Parse() + + client, err := docker.NewClient(apiPath) + if err != nil { + log.Fatalf("Unable to connect to Docker API on %s: %s", apiPath, err) + } + + events := make(chan *docker.APIEvents) + done := make(chan bool) + client.AddEventListener(events) + + log.Printf("Using Docker API on %s", apiPath) + log.Printf("Posting updates to server %s:%d", serverAddr, serverPort) + + go func() { + for event := range events { + log.Printf("Event %s", *event) + } + done <- true + }() + <-done +} From d8c31fb150f586ea0a1df14985aeb8f203b2d911 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 13 Oct 2014 16:46:37 +0100 Subject: [PATCH 085/145] Simplified args and added minimal event handling --- updater/main.go | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/updater/main.go b/updater/main.go index 0b66d1cc..63fdcefc 100644 --- a/updater/main.go +++ b/updater/main.go @@ -2,22 +2,23 @@ package main import ( "flag" + "fmt" "github.com/fsouza/go-dockerclient" "log" + "net/http" ) func main() { var ( - serverAddr string - serverPort int + updatePath string apiPath string ) - flag.StringVar(&serverAddr, "host", "", "address or hostname of server to update") - flag.IntVar(&serverPort, "port", 6785, "port on server to send updates to") - flag.StringVar(&apiPath, "api", "unix:///var/run/docker.sock", - "Path to Docker API socket") + flag.StringVar(&updatePath, "endpoint", "", "Endpoint to which to send updates") + flag.StringVar(&apiPath, "api", "unix:///var/run/docker.sock", "Path to Docker API socket") flag.Parse() + // TODO: check status on endpoint + client, err := docker.NewClient(apiPath) if err != nil { log.Fatalf("Unable to connect to Docker API on %s: %s", apiPath, err) @@ -28,13 +29,25 @@ func main() { client.AddEventListener(events) log.Printf("Using Docker API on %s", apiPath) - log.Printf("Posting updates to server %s:%d", serverAddr, serverPort) + log.Printf("Posting updates to ", updatePath) go func() { for event := range events { - log.Printf("Event %s", *event) + handleEvent(event, client, updatePath) } done <- true }() <-done } + +func handleEvent(event *docker.APIEvents, client *docker.Client, endpoint string) error { + switch event.Status { + case "die": + id := event.ID + url := fmt.Sprintf("%s/name/%s", endpoint, id) + client := &http.Client{} + req, _ := http.NewRequest("DELETE", url, nil) + client.Do(req) + } + return nil +} From 0fb4bce24d40056419b5b365ef1dd6d4a86d4517 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 15 Oct 2014 15:28:17 +0100 Subject: [PATCH 086/145] Put both image builds in docker/Makefile --- docker/Makefile | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/docker/Makefile b/docker/Makefile index b1ca2b2b..2a164f43 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -1,21 +1,28 @@ -IMAGE:=/tmp/weavedns.tar -MAIN:=../ +.DEFAULT: all -.DEFAULT: docker-image - -all: docker-image +all: server-image updater-image weavedns: ../server/*.go ../main.go - go get -tags netgo $(MAIN) && go build -tags netgo -ldflags '-linkmode external -extldflags "-static"' -o $@ -v $(MAIN) + go get -tags netgo ../ && go build -tags netgo -ldflags '-linkmode external -extldflags "-static"' -o $@ -v ../ -.PHONY: docker-image +updater: ../updater/main.go + go get -tags netgo ../updater/ && go build -tags netgo -ldflags '-linkmode external -extldflags "-static"' -o $@ -v ../updater/ -docker-image: $(IMAGE) +.PHONY: server-image updater-image -$(IMAGE): Dockerfile weavedns +updater-image: /var/tmp/updater.tar +server-image: /var/tmp/weavedns.tar + +/var/tmp/weavedns.tar: Dockerfile weavedns sudo -E docker build -t zettio/weavedns . - sudo -E docker save zettio/weavedns > $(IMAGE) + sudo -E docker save zettio/weavedns > $@ + +/var/tmp/updater.tar: ../updater/Dockerfile updater + sudo -E docker build -t zettio/updater ../updater/ + sudo -E docker save zettio/updater > $@ clean: -sudo docker rmi zettio/weavedns - rm -f weavedns $(IMAGE) + -sudo docker rmi zettio/updater + rm -f weavedns updater + rm -f /var/tmp/{updater,weavedns}.tar From 0749460652463222de98d67b6b688af2eed1d858 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Thu, 16 Oct 2014 15:38:32 +0100 Subject: [PATCH 087/145] Moved updater into server While we're only reacting to containers going away, it's more convenient to bundle the docker events watcher into the server. I've added a flag to switch it on; and switched it on by default in the container. I also implemented `Zone.DeleteRecordsFor(ident)`, which removes all records for a particular container. One minor change to the structure of the code is that the Zone is now constructed *outside* the server, and given to it and the updater. --- docker/Dockerfile | 2 +- docker/Makefile | 17 ++++---------- main.go | 15 +++++++++++- server/mdns_server.go | 4 ++-- server/server.go | 3 +-- server/updater.go | 34 +++++++++++++++++++++++++++ server/zone.go | 16 +++++++++++++ server/zone_test.go | 30 ++++++++++++++++++++++++ updater/main.go | 53 ------------------------------------------- 9 files changed, 102 insertions(+), 72 deletions(-) create mode 100644 server/updater.go delete mode 100644 updater/main.go diff --git a/docker/Dockerfile b/docker/Dockerfile index cb93e00c..1ce7b72a 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,4 +2,4 @@ FROM scratch # EXPOSE 53/udp WORKDIR /home/weave ADD ./weavedns /home/weave/ -ENTRYPOINT ["/home/weave/weavedns", "-wait", "5", "-iface", "ethwe"] +ENTRYPOINT ["/home/weave/weavedns", "-wait", "5", "-iface", "ethwe", "--watch", "--api", "unix:///var/run/docker.sock"] diff --git a/docker/Makefile b/docker/Makefile index 2a164f43..fa46ea64 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -1,28 +1,19 @@ .DEFAULT: all -all: server-image updater-image +all: server-image weavedns: ../server/*.go ../main.go go get -tags netgo ../ && go build -tags netgo -ldflags '-linkmode external -extldflags "-static"' -o $@ -v ../ -updater: ../updater/main.go - go get -tags netgo ../updater/ && go build -tags netgo -ldflags '-linkmode external -extldflags "-static"' -o $@ -v ../updater/ +.PHONY: server-image -.PHONY: server-image updater-image - -updater-image: /var/tmp/updater.tar server-image: /var/tmp/weavedns.tar /var/tmp/weavedns.tar: Dockerfile weavedns sudo -E docker build -t zettio/weavedns . sudo -E docker save zettio/weavedns > $@ -/var/tmp/updater.tar: ../updater/Dockerfile updater - sudo -E docker build -t zettio/updater ../updater/ - sudo -E docker save zettio/updater > $@ - clean: -sudo docker rmi zettio/weavedns - -sudo docker rmi zettio/updater - rm -f weavedns updater - rm -f /var/tmp/{updater,weavedns}.tar + rm -f weavedns + rm -f /var/tmp/weavedns.tar diff --git a/main.go b/main.go index 5ebcc087..b88917ab 100644 --- a/main.go +++ b/main.go @@ -9,18 +9,31 @@ import ( func main() { var ( ifaceName string + apiPath string dnsPort int httpPort int wait int + watch bool ) flag.StringVar(&ifaceName, "iface", "", "name of interface to use for multicast") + flag.StringVar(&apiPath, "api", "unix:///var/run/docker.sock", "Path to Docker API socket") flag.IntVar(&wait, "wait", 0, "number of seconds to wait for interface to be created and come up (defaults to 0)") flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to dns requests (defaults to 53)") flag.IntVar(&httpPort, "httpport", 6785, "port to listen to HTTP requests (defaults to 6785)") + flag.BoolVar(&watch, "watch", false, "watch the docker socket for container events") flag.Parse() - err := weavedns.StartServer(ifaceName, dnsPort, httpPort, wait) + var zone = new(weavedns.ZoneDb) + + if watch { + err := weavedns.StartUpdater(apiPath, zone) + if err != nil { + log.Fatal("Unable to start watcher", err) + } + } + + err := weavedns.StartServer(zone, ifaceName, dnsPort, httpPort, wait) if err != nil { log.Fatalf("Failed to start server: %s", err) } diff --git a/server/mdns_server.go b/server/mdns_server.go index d289b642..42249b09 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -9,10 +9,10 @@ import ( type MDNSServer struct { localAddrs []net.Addr sendconn *net.UDPConn - zone *ZoneDb + zone Zone } -func NewMDNSServer(zone *ZoneDb) (*MDNSServer, error) { +func NewMDNSServer(zone Zone) (*MDNSServer, error) { //log.Println("minimalServer sending:", buf) // This is a bit of a kludge - per the RFC we should send responses from 5353, but that doesn't seem to work sendconn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) diff --git a/server/server.go b/server/server.go index beee303e..51148bb5 100644 --- a/server/server.go +++ b/server/server.go @@ -100,9 +100,8 @@ func notUsHandler() dns.HandlerFunc { } } -func StartServer(ifaceName string, dnsPort int, httpPort int, wait int) error { +func StartServer(zone Zone, ifaceName string, dnsPort int, httpPort int, wait int) error { - var zone = new(ZoneDb) go ListenHttp(zone, httpPort) var iface *net.Interface = nil diff --git a/server/updater.go b/server/updater.go new file mode 100644 index 00000000..f9527648 --- /dev/null +++ b/server/updater.go @@ -0,0 +1,34 @@ +package weavedns + +import ( + "github.com/fsouza/go-dockerclient" + "log" +) + +func StartUpdater(apiPath string, zone Zone) error { + client, err := docker.NewClient(apiPath) + if err != nil { + log.Fatalf("Unable to connect to Docker API on %s: %s", apiPath, err) + } + + events := make(chan *docker.APIEvents) + client.AddEventListener(events) + + log.Printf("Using Docker API on %s", apiPath) + + go func() { + for event := range events { + handleEvent(zone, event, client) + } + }() + return nil +} + +func handleEvent(zone Zone, event *docker.APIEvents, client *docker.Client) error { + switch event.Status { + case "die": + id := event.ID + zone.DeleteRecordsFor(id) + } + return nil +} diff --git a/server/zone.go b/server/zone.go index d3173949..0cd2adc5 100644 --- a/server/zone.go +++ b/server/zone.go @@ -10,6 +10,7 @@ import ( type Zone interface { AddRecord(string, string, net.IP, net.IP, *net.IPNet) error DeleteRecord(ident string, weave_ip net.IP) error + DeleteRecordsFor(ident string) error MatchLocal(string) (net.IP, error) } @@ -99,3 +100,18 @@ func (zone *ZoneDb) DeleteRecord(ident string, weave_ip net.IP) error { return nil } + +func (zone *ZoneDb) DeleteRecordsFor(ident string) error { + zone.mx.Lock() + defer zone.mx.Unlock() + w := 0 // write index + + for _, r := range zone.recs { + if r.Ident != ident { + zone.recs[w] = r + w++ + } + } + zone.recs = zone.recs[:w] + return nil +} diff --git a/server/zone_test.go b/server/zone_test.go index a3c7b252..1e2bc77c 100644 --- a/server/zone_test.go +++ b/server/zone_test.go @@ -48,4 +48,34 @@ func TestZone(t *testing.T) { if _, ok := err.(LookupError); !ok { t.Fatal("Unexpected result when deleting record", success_test_name, err) } + +} + +func TestDeleteFor(t *testing.T) { + var ( + id = "foobar" + name = "foo.weave." + addr1 = "10.1.2.3/24" + addr2 = "10.2.7.8/24" + docker_ip = "172.16.0.4" + ) + zone := new(ZoneDb) + ip := net.ParseIP(docker_ip) + for _, addr := range []string{addr1, addr2} { + weave_ip, subnet, _ := net.ParseCIDR(addr) + err := zone.AddRecord(id, name, ip, weave_ip, subnet) + if err != nil { + t.Fatal(err) + } + } + + _, err := zone.MatchLocal(name) + if err != nil { + t.Fatal("Did not get result for lookup") + } + err = zone.DeleteRecordsFor(id) + _, err = zone.MatchLocal(name) + if _, ok := err.(LookupError); !ok { + t.Fatal("Expected no results after deleting records for ident") + } } diff --git a/updater/main.go b/updater/main.go deleted file mode 100644 index 63fdcefc..00000000 --- a/updater/main.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "github.com/fsouza/go-dockerclient" - "log" - "net/http" -) - -func main() { - var ( - updatePath string - apiPath string - ) - flag.StringVar(&updatePath, "endpoint", "", "Endpoint to which to send updates") - flag.StringVar(&apiPath, "api", "unix:///var/run/docker.sock", "Path to Docker API socket") - flag.Parse() - - // TODO: check status on endpoint - - client, err := docker.NewClient(apiPath) - if err != nil { - log.Fatalf("Unable to connect to Docker API on %s: %s", apiPath, err) - } - - events := make(chan *docker.APIEvents) - done := make(chan bool) - client.AddEventListener(events) - - log.Printf("Using Docker API on %s", apiPath) - log.Printf("Posting updates to ", updatePath) - - go func() { - for event := range events { - handleEvent(event, client, updatePath) - } - done <- true - }() - <-done -} - -func handleEvent(event *docker.APIEvents, client *docker.Client, endpoint string) error { - switch event.Status { - case "die": - id := event.ID - url := fmt.Sprintf("%s/name/%s", endpoint, id) - client := &http.Client{} - req, _ := http.NewRequest("DELETE", url, nil) - client.Do(req) - } - return nil -} From c6a74d23e0de4ef530a72415e929194099b3fbbc Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 16 Oct 2014 17:21:15 +0100 Subject: [PATCH 088/145] Print out command-line args, for debugging --- main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main.go b/main.go index b88917ab..23c2c08c 100644 --- a/main.go +++ b/main.go @@ -4,9 +4,12 @@ import ( "flag" "github.com/zettio/weavedns/server" "log" + "os" ) func main() { + log.Println(os.Args) + var ( ifaceName string apiPath string From 0eac2e60ed32fc78991b5896c28c01ebb7bdcb57 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Thu, 16 Oct 2014 17:46:46 +0100 Subject: [PATCH 089/145] Added README --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..949a52ba --- /dev/null +++ b/README.md @@ -0,0 +1,45 @@ +# Weave DNS server + +The Weave DNS server answers name queries in a Weave network. It is +run per-host, to be supplied as the nameserver for containers on that +host. It is then told about hostnames for the local containers. For +other names it will ask the other weave hosts, or fall back to using +the host's configured name server. + +## Use + +The Weave DNS container should be started after the Weave router -- +i.e., after running `weave launch` -- and joined to the Weave network. + +In general, it should be given an IP in the same subnet as the +router. To watch for container events it must have the docker socker +mounted. + +```bash +$ weave launch 10.0.0.1/16 +$ weave run 10.0.0.2/16 --name=weavedns -v /var/run/docker.sock:/var/run/docker.sock zettio/weavedns +``` + +The docker IP for the container can then be provided as the `--dns` value +for other containers: + +```bash +$ dns_ip=$(docker inspect --format='{{ .NetworkSettings.IPAddress }}' weavedns) +$ shell1=$(weave run 10.0.1.2/24 --dns=$dns_ip -t ubuntu /bin/bash) +``` + +To associate a hostname with an IP, use an HTTP PUT to the DNS server: + +```bash +$ shell1_ip=$(docker inspect --format='{{ .NetworkSettings.IPAddress }}' $shell1) +$ curl -X PUT "http://$dns_ip:6785/name/$shell1/10.0.1.2?fqdn=shell1.weave&routing_prefix=24&local_ip=$shell1_ip" +``` + +You should now be able to look up the name: + +```bash +$ dig @$dns_ip +short shell1.weave +``` + +By default, the server will also watch docker events and remove +entries for any containers that die. From 8c0c252bf7c9de9262a211031436b705ad888305 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Fri, 17 Oct 2014 11:05:56 +0100 Subject: [PATCH 090/145] Made watch=true the default It's now left out of the entrypoint arguments, so can be supplied as an argument when running it. --- README.md | 7 ++++++- docker/Dockerfile | 2 +- main.go | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 949a52ba..473cf951 100644 --- a/README.md +++ b/README.md @@ -42,4 +42,9 @@ $ dig @$dns_ip +short shell1.weave ``` By default, the server will also watch docker events and remove -entries for any containers that die. +entries for any containers that die. You can tell it not to by adding +`--watch=false` to the container args: + +```bash +$ weave run 10.0.0.2/16 --name=weavedns -v /var/run/docker.sock:/var/run/docker.sock zettio/weavedns --watch=false +``` diff --git a/docker/Dockerfile b/docker/Dockerfile index 1ce7b72a..cb93e00c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,4 +2,4 @@ FROM scratch # EXPOSE 53/udp WORKDIR /home/weave ADD ./weavedns /home/weave/ -ENTRYPOINT ["/home/weave/weavedns", "-wait", "5", "-iface", "ethwe", "--watch", "--api", "unix:///var/run/docker.sock"] +ENTRYPOINT ["/home/weave/weavedns", "-wait", "5", "-iface", "ethwe"] diff --git a/main.go b/main.go index 23c2c08c..dfec361e 100644 --- a/main.go +++ b/main.go @@ -24,7 +24,7 @@ func main() { flag.IntVar(&wait, "wait", 0, "number of seconds to wait for interface to be created and come up (defaults to 0)") flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to dns requests (defaults to 53)") flag.IntVar(&httpPort, "httpport", 6785, "port to listen to HTTP requests (defaults to 6785)") - flag.BoolVar(&watch, "watch", false, "watch the docker socket for container events") + flag.BoolVar(&watch, "watch", true, "watch the docker socket for container events") flag.Parse() var zone = new(weavedns.ZoneDb) From 9d1f2023b587c65495e22bb89b7f4e5b927fd825 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Fri, 17 Oct 2014 12:29:43 +0100 Subject: [PATCH 091/145] Documented the more egregious limitations --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 473cf951..4bd95a25 100644 --- a/README.md +++ b/README.md @@ -48,3 +48,14 @@ entries for any containers that die. You can tell it not to by adding ```bash $ weave run 10.0.0.2/16 --name=weavedns -v /var/run/docker.sock:/var/run/docker.sock zettio/weavedns --watch=false ``` + +## Present limitations + + * The server will not know about restarted containers, unless you + re-PUT the name. + * The server will forget names if it is itself restarted. + * The server may give unreachable IPs as answers, since it + doesn't try to filter by subnet yet. + * We use UDP broadcast to find out about remote names (from Weave DNS + servers on other hosts); this likely won't scale well beyond a + certain point T.B.D., so we'll have to come up with another scheme. From a3cede302755d8095c575994460453b74bdac1a2 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Fri, 17 Oct 2014 15:24:51 +0100 Subject: [PATCH 092/145] Tidied up a few printfs --- main.go | 2 +- server/updater.go | 1 + server/zone.go | 2 -- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index dfec361e..f53a4cdf 100644 --- a/main.go +++ b/main.go @@ -38,6 +38,6 @@ func main() { err := weavedns.StartServer(zone, ifaceName, dnsPort, httpPort, wait) if err != nil { - log.Fatalf("Failed to start server: %s", err) + log.Fatal("Failed to start server", err) } } diff --git a/server/updater.go b/server/updater.go index f9527648..dee375c7 100644 --- a/server/updater.go +++ b/server/updater.go @@ -28,6 +28,7 @@ func handleEvent(zone Zone, event *docker.APIEvents, client *docker.Client) erro switch event.Status { case "die": id := event.ID + log.Printf("Container %s down. Removing records", id) zone.DeleteRecordsFor(id) } return nil diff --git a/server/zone.go b/server/zone.go index 0cd2adc5..cdb37cc8 100644 --- a/server/zone.go +++ b/server/zone.go @@ -2,7 +2,6 @@ package weavedns import ( "github.com/miekg/dns" - "log" "net" "sync" ) @@ -46,7 +45,6 @@ func (err DuplicateError) Error() string { // Stop gap. func (zone *ZoneDb) match(name string) (net.IP, error) { for _, r := range zone.recs { - log.Printf("%s == %s ?", r.Name, name) if r.Name == name { return r.WeaveIp, nil } From 8e2af9eec5ae818aecf3784e6c616561dc202f0d Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 20 Oct 2014 12:41:40 +0100 Subject: [PATCH 093/145] Tidy up exported/non-exported fields --- server/mdns_client.go | 10 +++++----- server/mdns_client_test.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index 48d2aa7b..5f70321e 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -26,7 +26,7 @@ var ( type ResponseA struct { Name string Addr net.IP - err error + Err error } type responseInfo struct { @@ -39,7 +39,7 @@ type responseInfo struct { // a query in flight, then we don't want to send more queries out. type inflightQuery struct { name string - Id uint16 // the DNS message ID + id uint16 // the DNS message ID responseInfos []*responseInfo } @@ -201,18 +201,18 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) error { m.RecursionDesired = false buf, err := m.Pack() if err != nil { - q.responseCh <- &ResponseA{err: err} + q.responseCh <- &ResponseA{Err: err} close(q.responseCh) return err } query = &inflightQuery{ name: q.name, - Id: m.Id, + id: m.Id, } c.inflight[q.name] = query _, err = c.conn.WriteTo(buf, c.addr) if err != nil { - q.responseCh <- &ResponseA{err: err} + q.responseCh <- &ResponseA{Err: err} close(q.responseCh) return err } diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index 0eebf251..8d5795f0 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -96,8 +96,8 @@ func (c *testContext) checkResponse(t *testing.T, channel_ok bool, resp *Respons c.channel = nil return } - if resp.err != nil { - t.Fatal(resp.err) + if resp.Err != nil { + t.Fatal(resp.Err) } log.Printf("Got address response %s addr %s", resp.Name, resp.Addr) c.received_addr = resp.Addr From dcce09de520946ea14887fcdaad432ea3bca3e74 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 20 Oct 2014 14:21:29 +0100 Subject: [PATCH 094/145] Clean up some code-review issues --- server/mdns_client.go | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index 5f70321e..55a058a3 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -10,9 +10,11 @@ import ( // Portions of this code taken from github.com/armon/mdns const ( - ipv4mdns = "224.0.0.251" // link-local multicast address - mdnsPort = 5353 // mDNS assigned port - mDNSTimeout = 200 * time.Millisecond + ipv4mdns = "224.0.0.251" // link-local multicast address + mdnsPort = 5353 // mDNS assigned port + // We wait this long to hear responses from other mDNS servers on the network. + // TODO: introduce caching so we don't have to wait this long on every call. + mDNSTimeout = 250 * time.Millisecond MaxDuration = time.Duration(math.MaxInt64) ) @@ -62,11 +64,10 @@ func NewMDNSClient() (*MDNSClient, error) { if err != nil { return nil, err } - retval := &MDNSClient{ + return &MDNSClient{ conn: conn, addr: ipv4Addr, - inflight: make(map[string]*inflightQuery)} - return retval, nil + inflight: make(map[string]*inflightQuery)}, nil } func (c *MDNSClient) Start(ifi *net.Interface) error { @@ -77,7 +78,7 @@ func (c *MDNSClient) Start(ifi *net.Interface) error { handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) { //log.Println("client received:", r) - // Only handle responses here + // Don't want to handle queries here, so filter anything out that isn't a response if len(r.Answer) > 0 { c.ResponseCallback(r) } @@ -193,7 +194,7 @@ func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { } } -func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) error { +func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) { query, found := c.inflight[q.name] if !found { m := new(dns.Msg) @@ -203,7 +204,7 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) error { if err != nil { q.responseCh <- &ResponseA{Err: err} close(q.responseCh) - return err + return } query = &inflightQuery{ name: q.name, @@ -214,7 +215,7 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) error { if err != nil { q.responseCh <- &ResponseA{Err: err} close(q.responseCh) - return err + return } } info := &responseInfo{ @@ -222,8 +223,6 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) error { timeout: time.Now().Add(mDNSTimeout), } query.responseInfos = append(query.responseInfos, info) - - return nil } func (c *MDNSClient) handleResponse(r *dns.Msg) { From 81c5690329cf800acbf9950c1ee12c4042f5eb6e Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 20 Oct 2014 15:00:59 +0100 Subject: [PATCH 095/145] Hoist WriteMsg calls up --- server/server.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/server/server.go b/server/server.go index 51148bb5..37368789 100644 --- a/server/server.go +++ b/server/server.go @@ -65,13 +65,13 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { log.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) replies = append(replies, resp.Addr) } + var response_msg *dns.Msg if len(replies) > 0 { - m := makeDNSReply(r, q.Name, replies) - w.WriteMsg(m) + response_msg = makeDNSReply(r, q.Name, replies) } else { - m := makeDNSFailResponse(r) - w.WriteMsg(m) + response_msg = makeDNSFailResponse(r) } + w.WriteMsg(response_msg) }() mdnsClient.SendQuery(q.Name, dns.TypeA, channel) } @@ -83,6 +83,7 @@ func notUsHandler() dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] addrs, err := net.LookupIP(q.Name) + var response_msg *dns.Msg if err == nil { // Filter out ipv6 addresses filtered := make([]net.IP, 0) @@ -91,12 +92,11 @@ func notUsHandler() dns.HandlerFunc { filtered = append(filtered, ip4) } } - m := makeDNSReply(r, q.Name, filtered) - w.WriteMsg(m) + response_msg = makeDNSReply(r, q.Name, filtered) } else { - m := makeDNSFailResponse(r) - w.WriteMsg(m) + response_msg = makeDNSFailResponse(r) } + w.WriteMsg(response_msg) } } From 217e5e921e431e76ccaf7770885fb3e11d7b3272 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 20 Oct 2014 15:19:11 +0100 Subject: [PATCH 096/145] Extract checkInFlightQueries method --- server/mdns_client.go | 58 ++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index 55a058a3..960862bc 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -131,36 +131,42 @@ func (c *MDNSClient) ResponseCallback(r *dns.Msg) { c.queryChan <- &MDNSInteraction{code: CMessageReceived, payload: r} } +// Check all in-flight queries, close all that have already timed out, +// and return the duration until the next timeout +func (c *MDNSClient) checkInFlightQueries() time.Duration { + now := time.Now() + after := MaxDuration + for name, query := range c.inflight { + // Count down from end of slice to beginning + length := len(query.responseInfos) + for i := length - 1; i >= 0; i-- { + item := query.responseInfos[i] + switch duration := item.timeout.Sub(now); { + case duration <= 0: // timed out + close(item.ch) + // Swap item from the end of the slice + length-- + if i < length { + query.responseInfos[i] = query.responseInfos[length] + } + case duration < after: + after = duration + } + } + query.responseInfos = query.responseInfos[:length] + if length == 0 { + delete(c.inflight, name) + } + } + return after +} + // ACTOR server func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { timer := time.NewTimer(MaxDuration) run := func() { - now := time.Now() - after := MaxDuration - for name, query := range c.inflight { - // Count down from end of slice to beginning - length := len(query.responseInfos) - for i := length - 1; i >= 0; i-- { - item := query.responseInfos[i] - switch duration := item.timeout.Sub(now); { - case duration <= 0: // timed out - close(item.ch) - // Swap item from the end of the slice - length-- - if i < length { - query.responseInfos[i] = query.responseInfos[length] - } - case duration < after: - after = duration - } - } - query.responseInfos = query.responseInfos[:length] - if length == 0 { - delete(c.inflight, name) - } - } - timer.Reset(after) + timer.Reset(c.checkInFlightQueries()) } terminate := false @@ -186,7 +192,7 @@ func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { } } - // Close all response channels + // Close all response channels at termination for _, query := range c.inflight { for _, item := range query.responseInfos { close(item.ch) From 9cfb93ab7b7d3118eee33f8343f1847121ee54c1 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 20 Oct 2014 15:43:13 +0100 Subject: [PATCH 097/145] Simplified algorithm in checkInFlightQueries() --- server/mdns_client.go | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index 960862bc..21d97808 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -2,6 +2,7 @@ package weavedns import ( "github.com/miekg/dns" + "log" "math" "net" "time" @@ -39,6 +40,7 @@ type responseInfo struct { // Represents one query that we have sent for one name. // If we, internally, get several requests for the same name while we have // a query in flight, then we don't want to send more queries out. +// Invariant on responseInfos: they are in non-descending order of timeout. type inflightQuery struct { name string id uint16 // the DNS message ID @@ -131,38 +133,37 @@ func (c *MDNSClient) ResponseCallback(r *dns.Msg) { c.queryChan <- &MDNSInteraction{code: CMessageReceived, payload: r} } +// ACTOR server + // Check all in-flight queries, close all that have already timed out, // and return the duration until the next timeout func (c *MDNSClient) checkInFlightQueries() time.Duration { now := time.Now() after := MaxDuration for name, query := range c.inflight { - // Count down from end of slice to beginning - length := len(query.responseInfos) - for i := length - 1; i >= 0; i-- { - item := query.responseInfos[i] - switch duration := item.timeout.Sub(now); { - case duration <= 0: // timed out + // Invariant on responseInfos: they are in non-descending order of timeout. + num_closed := 0 + for _, item := range query.responseInfos { + duration := item.timeout.Sub(now) + if duration <= 0 { // timed out close(item.ch) - // Swap item from the end of the slice - length-- - if i < length { - query.responseInfos[i] = query.responseInfos[length] + num_closed++ + } else { + if duration < after { + after = duration } - case duration < after: - after = duration + break // don't need to look at any more for this query } } - query.responseInfos = query.responseInfos[:length] - if length == 0 { + // Remove timed-out items from the slice + query.responseInfos = query.responseInfos[num_closed:] + if len(query.responseInfos) == 0 { delete(c.inflight, name) } } return after } -// ACTOR server - func (c *MDNSClient) queryLoop(queryChan <-chan *MDNSInteraction) { timer := time.NewTimer(MaxDuration) run := func() { @@ -228,6 +229,8 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) { ch: q.responseCh, timeout: time.Now().Add(mDNSTimeout), } + // Invariant on responseInfos: they are in non-descending order of timeout. + // Since we use a fixed interval from Now(), this must be after all existing timeouts. query.responseInfos = append(query.responseInfos, info) } From b2dc08fd25aac62830eddbe19427e901598f65ac Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 20 Oct 2014 16:00:56 +0100 Subject: [PATCH 098/145] forgot to remove 'dns' --- server/mdns_client.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index 21d97808..ad90083d 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -2,7 +2,6 @@ package weavedns import ( "github.com/miekg/dns" - "log" "math" "net" "time" From 624db7bdff015f7320813189217097021d8a9c67 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 20 Oct 2014 16:59:39 +0100 Subject: [PATCH 099/145] Replace hard-coded 4 with const for channel size --- server/mdns_client.go | 3 ++- server/server.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index ad90083d..213bb61e 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -16,6 +16,7 @@ const ( // TODO: introduce caching so we don't have to wait this long on every call. mDNSTimeout = 250 * time.Millisecond MaxDuration = time.Duration(math.MaxInt64) + ChannelSize = 16 ) var ( @@ -88,7 +89,7 @@ func (c *MDNSClient) Start(ifi *net.Interface) error { c.server = &dns.Server{Listener: nil, PacketConn: multicast, Handler: dns.HandlerFunc(handleMDNS)} go c.server.ActivateAndServe() - queryChan := make(chan *MDNSInteraction, 4) + queryChan := make(chan *MDNSInteraction, ChannelSize) c.queryChan = queryChan go c.queryLoop(queryChan) diff --git a/server/server.go b/server/server.go index 37368789..ab38a9d4 100644 --- a/server/server.go +++ b/server/server.go @@ -57,7 +57,7 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { } else { log.Printf("Failed lookup for %s; sending mDNS query", q.Name) // We don't know the answer; see if someone else does - channel := make(chan *ResponseA, 4) + channel := make(chan *ResponseA, ChannelSize) replies := make([]net.IP, 0) go func() { // Loop terminates when channel is closed by MDNSClient on timeout From 3af058481e2265bd3af525c9ffe439ace6300405 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 20 Oct 2014 17:18:17 +0100 Subject: [PATCH 100/145] Support returning AAAA records from delegated DNS queries --- server/mdns_server.go | 20 ++++++++++++-------- server/server.go | 17 +++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/server/mdns_server.go b/server/mdns_server.go index 42249b09..4bb8fe10 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -24,16 +24,20 @@ func NewMDNSServer(zone Zone) (*MDNSServer, error) { return retval, nil } -func makeDNSReply(r *dns.Msg, name string, addrs []net.IP) *dns.Msg { +func makeDNSReply(r *dns.Msg, name string, qtype uint16, addrs []net.IP) *dns.Msg { m := new(dns.Msg) m.SetReply(r) m.RecursionAvailable = true - m.Answer = make([]dns.RR, len(addrs)) - for i, addr := range addrs { - if ip4 := addr.To4(); ip4 != nil { - hdr := dns.RR_Header{Name: name, Rrtype: dns.TypeA, - Class: dns.ClassINET, Ttl: 3600} - m.Answer[i] = &dns.A{hdr, addr} + hdr := dns.RR_Header{Name: name, Rrtype: qtype, Class: dns.ClassINET, Ttl: 3600} + for _, addr := range addrs { + if qtype == dns.TypeA { + if ip4 := addr.To4(); ip4 != nil { + m.Answer = append(m.Answer, &dns.A{hdr, addr}) + } + } else if qtype == dns.TypeAAAA { + if ip4 := addr.To4(); ip4 == nil { + m.Answer = append(m.Answer, &dns.AAAA{hdr, addr}) + } } } return m @@ -60,7 +64,7 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { q := r.Question[0] ip, err := s.zone.MatchLocal(q.Name) if err == nil { - m := makeDNSReply(r, q.Name, []net.IP{ip}) + m := makeDNSReply(r, q.Name, dns.TypeA, []net.IP{ip}) s.SendResponse(m) } else if s.addrIsLocal(w.RemoteAddr()) { // ignore this - it's our own query received via multicast diff --git a/server/server.go b/server/server.go index ab38a9d4..357ac196 100644 --- a/server/server.go +++ b/server/server.go @@ -52,7 +52,7 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { q := r.Question[0] ip, err := zone.MatchLocal(q.Name) if err == nil { - m := makeDNSReply(r, q.Name, []net.IP{ip}) + m := makeDNSReply(r, q.Name, dns.TypeA, []net.IP{ip}) w.WriteMsg(m) } else { log.Printf("Failed lookup for %s; sending mDNS query", q.Name) @@ -67,7 +67,7 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { } var response_msg *dns.Msg if len(replies) > 0 { - response_msg = makeDNSReply(r, q.Name, replies) + response_msg = makeDNSReply(r, q.Name, dns.TypeA, replies) } else { response_msg = makeDNSFailResponse(r) } @@ -79,20 +79,17 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { } } +/* When we receive a request for a name outside of our '.weave' domain, call + the underlying lookup mechanism and return the answer(s) it gives. + Unfortunately, this means that TTLs from a real DNS server are lost - FIXME. +*/ func notUsHandler() dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] addrs, err := net.LookupIP(q.Name) var response_msg *dns.Msg if err == nil { - // Filter out ipv6 addresses - filtered := make([]net.IP, 0) - for _, addr := range addrs { - if ip4 := addr.To4(); ip4 != nil { - filtered = append(filtered, ip4) - } - } - response_msg = makeDNSReply(r, q.Name, filtered) + response_msg = makeDNSReply(r, q.Name, q.Qtype, addrs) } else { response_msg = makeDNSFailResponse(r) } From b8a06521c072f74df2b7e220ed69e7695abb664c Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 20 Oct 2014 17:21:35 +0100 Subject: [PATCH 101/145] Extract interface util functions to their own file --- server/if_utils.go | 35 +++++++++++++++++++++++++++++++++++ server/server.go | 28 ---------------------------- 2 files changed, 35 insertions(+), 28 deletions(-) create mode 100644 server/if_utils.go diff --git a/server/if_utils.go b/server/if_utils.go new file mode 100644 index 00000000..fff51d97 --- /dev/null +++ b/server/if_utils.go @@ -0,0 +1,35 @@ +package weavedns + +import ( + "fmt" + "log" + "net" + "time" +) + +func ensureInterface(ifaceName string, wait int) (iface *net.Interface, err error) { + iface, err = findInterface(ifaceName) + if err == nil || wait == 0 { + return + } + log.Println("Waiting for interface", ifaceName, "to come up") + for ; err != nil && wait > 0; wait -= 1 { + time.Sleep(1 * time.Second) + iface, err = findInterface(ifaceName) + } + if err == nil { + log.Println("Interface", ifaceName, "is up") + } + return +} + +func findInterface(ifaceName string) (iface *net.Interface, err error) { + iface, err = net.InterfaceByName(ifaceName) + if err != nil { + return iface, fmt.Errorf("Unable to find interface %s", ifaceName) + } + if 0 == (net.FlagUp & iface.Flags) { + return iface, fmt.Errorf("Interface %s is not up", ifaceName) + } + return +} diff --git a/server/server.go b/server/server.go index 357ac196..2753ec14 100644 --- a/server/server.go +++ b/server/server.go @@ -5,40 +5,12 @@ import ( "github.com/miekg/dns" "log" "net" - "time" ) const ( LOCAL_DOMAIN = "weave" ) -func ensureInterface(ifaceName string, wait int) (iface *net.Interface, err error) { - iface, err = findInterface(ifaceName) - if err == nil || wait == 0 { - return - } - log.Println("Waiting for interface", ifaceName, "to come up") - for ; err != nil && wait > 0; wait -= 1 { - time.Sleep(1 * time.Second) - iface, err = findInterface(ifaceName) - } - if err == nil { - log.Println("Interface", ifaceName, "is up") - } - return -} - -func findInterface(ifaceName string) (iface *net.Interface, err error) { - iface, err = net.InterfaceByName(ifaceName) - if err != nil { - return iface, fmt.Errorf("Unable to find interface %s", ifaceName) - } - if 0 == (net.FlagUp & iface.Flags) { - return iface, fmt.Errorf("Interface %s is not up", ifaceName) - } - return -} - func makeDNSFailResponse(r *dns.Msg) *dns.Msg { m := new(dns.Msg) m.SetReply(r) From bce06d2e9ba3bf36e9304cbf47eef39f8c78f3a5 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 20 Oct 2014 17:26:11 +0100 Subject: [PATCH 102/145] Wait for interface in main --- main.go | 12 +++++++++++- server/if_utils.go | 2 +- server/server.go | 14 ++------------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/main.go b/main.go index f53a4cdf..906e6fd3 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "flag" "github.com/zettio/weavedns/server" "log" + "net" "os" ) @@ -36,7 +37,16 @@ func main() { } } - err := weavedns.StartServer(zone, ifaceName, dnsPort, httpPort, wait) + var iface *net.Interface = nil + if ifaceName != "" { + var err error + iface, err = weavedns.EnsureInterface(ifaceName, wait) + if err != nil { + log.Fatal(err) + } + } + + err := weavedns.StartServer(zone, iface, dnsPort, httpPort, wait) if err != nil { log.Fatal("Failed to start server", err) } diff --git a/server/if_utils.go b/server/if_utils.go index fff51d97..86e30eea 100644 --- a/server/if_utils.go +++ b/server/if_utils.go @@ -7,7 +7,7 @@ import ( "time" ) -func ensureInterface(ifaceName string, wait int) (iface *net.Interface, err error) { +func EnsureInterface(ifaceName string, wait int) (iface *net.Interface, err error) { iface, err = findInterface(ifaceName) if err == nil || wait == 0 { return diff --git a/server/server.go b/server/server.go index 2753ec14..b219c4b9 100644 --- a/server/server.go +++ b/server/server.go @@ -69,24 +69,14 @@ func notUsHandler() dns.HandlerFunc { } } -func StartServer(zone Zone, ifaceName string, dnsPort int, httpPort int, wait int) error { - +func StartServer(zone Zone, iface *net.Interface, dnsPort int, httpPort int, wait int) error { go ListenHttp(zone, httpPort) - var iface *net.Interface = nil - if ifaceName != "" { - var err error - iface, err = ensureInterface(ifaceName, wait) - if err != nil { - log.Fatal(err) - } - } - mdnsClient, err := NewMDNSClient() if err != nil { log.Fatal(err) } else { - log.Printf("Using mDNS on %s", ifaceName) + log.Printf("Using mDNS on %s", iface.Name) } err = mdnsClient.Start(iface) if err != nil { From 1b0066cc9889d2c4fb072e150401bc44c7b0764b Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 20 Oct 2014 17:51:00 +0100 Subject: [PATCH 103/145] Commented on stop-gap data structure --- server/zone.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/zone.go b/server/zone.go index cdb37cc8..9c7cf9f3 100644 --- a/server/zone.go +++ b/server/zone.go @@ -21,6 +21,8 @@ type Record struct { Subnet *net.IPNet } +// Very simple data structure for now, with linear searching. +// TODO: make more sophisticated to improve performance. type ZoneDb struct { mx sync.RWMutex recs []Record @@ -42,7 +44,6 @@ func (err DuplicateError) Error() string { return "Duplicate " + err.Name + "," + err.WeaveIp.String() + " in container " + err.Ident } -// Stop gap. func (zone *ZoneDb) match(name string) (net.IP, error) { for _, r := range zone.recs { if r.Name == name { From d3d07deec637385c3dc9e9739f3a5bc55a5ad1a2 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 20 Oct 2014 18:11:14 +0100 Subject: [PATCH 104/145] Changed local domain handling 1. The local domain is now ".weave.local", which is safer from TLD pollution 2. If the name given for an IP isn't a subdomain of the local domain, ignore it --- server/http.go | 17 +++++++++++------ server/server.go | 4 ++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/server/http.go b/server/http.go index 34c929be..dedb8234 100644 --- a/server/http.go +++ b/server/http.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "log" + "github.com/miekg/dns" "net" "net/http" "strings" @@ -18,7 +19,7 @@ func parseUrl(url string) (identifier string, ipaddr string, err error) { return parts[2], parts[3], nil } -func ListenHttp(db Zone, port int) { +func ListenHttp(domain string, db Zone, port int) { http.HandleFunc("/name/", func(w http.ResponseWriter, r *http.Request) { switch r.Method { case "PUT": @@ -44,11 +45,15 @@ func ListenHttp(db Zone, port int) { http.Error(w, fmt.Sprintf("Invalid CIDR: %s", weave_cidr), http.StatusBadRequest) return } - log.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) - err = db.AddRecord(identifier, name, ip, weave_ip, subnet) - if err != nil { - http.Error(w, err.Error(), http.StatusConflict) - return + if dns.IsSubDomain(domain, name) { + log.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) + err = db.AddRecord(identifier, name, ip, weave_ip, subnet) + if err != nil { + http.Error(w, err.Error(), http.StatusConflict) + return + } + } else { + log.Printf("Ignoring name %s, not in %s", name, domain) } case "DELETE": identifier, weave_ipstr, err := parseUrl(r.URL.Path) diff --git a/server/server.go b/server/server.go index b219c4b9..975ccac1 100644 --- a/server/server.go +++ b/server/server.go @@ -8,7 +8,7 @@ import ( ) const ( - LOCAL_DOMAIN = "weave" + LOCAL_DOMAIN = "weave.local." ) func makeDNSFailResponse(r *dns.Msg) *dns.Msg { @@ -70,7 +70,7 @@ func notUsHandler() dns.HandlerFunc { } func StartServer(zone Zone, iface *net.Interface, dnsPort int, httpPort int, wait int) error { - go ListenHttp(zone, httpPort) + go ListenHttp(LOCAL_DOMAIN, zone, httpPort) mdnsClient, err := NewMDNSClient() if err != nil { From 337919d741acb86d8a47fdd0906dc0da9e1ac7ac Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 20 Oct 2014 18:18:16 +0100 Subject: [PATCH 105/145] Replaced ad-hoc printf with log levels --- main.go | 18 ++++++++++++------ server/http.go | 21 ++++++++++----------- server/mdns_client.go | 1 - server/mdns_server.go | 4 +--- server/server.go | 22 ++++++++++++---------- server/updater.go | 7 +++---- 6 files changed, 38 insertions(+), 35 deletions(-) diff --git a/main.go b/main.go index 906e6fd3..847b8e72 100644 --- a/main.go +++ b/main.go @@ -3,14 +3,12 @@ package main import ( "flag" "github.com/zettio/weavedns/server" - "log" + "io/ioutil" "net" "os" ) func main() { - log.Println(os.Args) - var ( ifaceName string apiPath string @@ -18,6 +16,7 @@ func main() { httpPort int wait int watch bool + debug bool ) flag.StringVar(&ifaceName, "iface", "", "name of interface to use for multicast") @@ -26,14 +25,21 @@ func main() { flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to dns requests (defaults to 53)") flag.IntVar(&httpPort, "httpport", 6785, "port to listen to HTTP requests (defaults to 6785)") flag.BoolVar(&watch, "watch", true, "watch the docker socket for container events") + flag.BoolVar(&debug, "debug", false, "output debugging info to stderr") flag.Parse() + debugOut := ioutil.Discard + if debug { + debugOut = os.Stderr + } + weavedns.InitLogging(debugOut, os.Stdout, os.Stdout, os.Stderr) + var zone = new(weavedns.ZoneDb) if watch { err := weavedns.StartUpdater(apiPath, zone) if err != nil { - log.Fatal("Unable to start watcher", err) + weavedns.Error.Fatal("Unable to start watcher", err) } } @@ -42,12 +48,12 @@ func main() { var err error iface, err = weavedns.EnsureInterface(ifaceName, wait) if err != nil { - log.Fatal(err) + weavedns.Error.Fatal(err) } } err := weavedns.StartServer(zone, iface, dnsPort, httpPort, wait) if err != nil { - log.Fatal("Failed to start server", err) + weavedns.Error.Fatal("Failed to start server", err) } } diff --git a/server/http.go b/server/http.go index dedb8234..ae5be5ce 100644 --- a/server/http.go +++ b/server/http.go @@ -3,7 +3,6 @@ package weavedns import ( "errors" "fmt" - "log" "github.com/miekg/dns" "net" "net/http" @@ -28,60 +27,60 @@ func ListenHttp(domain string, db Zone, port int) { prefix := r.FormValue("routing_prefix") local_ip := r.FormValue("local_ip") if identifier == "" || weave_ipstr == "" || name == "" || prefix == "" || local_ip == "" { - log.Printf("Invalid request: %s, %s", r.URL, r.Form) + Warning.Printf("Invalid request: %s, %s", r.URL, r.Form) http.Error(w, "Invalid Request", http.StatusBadRequest) return } ip := net.ParseIP(local_ip) if ip == nil { - log.Printf("Invalid IP in request: %s", local_ip) + Warning.Printf("Invalid IP in request: %s", local_ip) http.Error(w, "Invalid IP in request", http.StatusBadRequest) return } weave_cidr := weave_ipstr + "/" + prefix weave_ip, subnet, err := net.ParseCIDR(weave_cidr) if err != nil { - log.Printf("Invalid CIDR in request: %s", weave_cidr) + Warning.Printf("Invalid CIDR in request: %s", weave_cidr) http.Error(w, fmt.Sprintf("Invalid CIDR: %s", weave_cidr), http.StatusBadRequest) return } if dns.IsSubDomain(domain, name) { - log.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) + Info.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) err = db.AddRecord(identifier, name, ip, weave_ip, subnet) if err != nil { http.Error(w, err.Error(), http.StatusConflict) return } } else { - log.Printf("Ignoring name %s, not in %s", name, domain) + Info.Printf("Ignoring name %s, not in %s", name, domain) } case "DELETE": identifier, weave_ipstr, err := parseUrl(r.URL.Path) if identifier == "" || weave_ipstr == "" { - log.Printf("Invalid request: %s, %s", r.URL, r.Form) + Warning.Printf("Invalid request: %s, %s", r.URL, r.Form) http.Error(w, "Invalid Request", http.StatusBadRequest) return } weave_ip := net.ParseIP(weave_ipstr) if weave_ip == nil { - log.Printf("Invalid IP in request: %s", weave_ipstr) + Warning.Printf("Invalid IP in request: %s", weave_ipstr) http.Error(w, "Invalid IP in request", http.StatusBadRequest) return } - log.Printf("Deleting %s (%s)", identifier, weave_ipstr) + Info.Printf("Deleting %s (%s)", identifier, weave_ipstr) err = db.DeleteRecord(identifier, weave_ip) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } default: - log.Println("Unexpected http method", r.Method) + Warning.Println("Unexpected http method", r.Method) http.Error(w, "Unexpected http method: "+r.Method, http.StatusBadRequest) } }) address := fmt.Sprintf(":%d", port) err := http.ListenAndServe(address, nil) if err != nil { - log.Fatal("Unable to create http listener: ", err) + Error.Fatal("Unable to create http listener: ", err) } } diff --git a/server/mdns_client.go b/server/mdns_client.go index 213bb61e..26c23bbe 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -79,7 +79,6 @@ func (c *MDNSClient) Start(ifi *net.Interface) error { } handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) { - //log.Println("client received:", r) // Don't want to handle queries here, so filter anything out that isn't a response if len(r.Answer) > 0 { c.ResponseCallback(r) diff --git a/server/mdns_server.go b/server/mdns_server.go index 4bb8fe10..8c13de8b 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -2,7 +2,6 @@ package weavedns import ( "github.com/miekg/dns" - "log" "net" ) @@ -13,7 +12,6 @@ type MDNSServer struct { } func NewMDNSServer(zone Zone) (*MDNSServer, error) { - //log.Println("minimalServer sending:", buf) // This is a bit of a kludge - per the RFC we should send responses from 5353, but that doesn't seem to work sendconn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) if err != nil { @@ -69,7 +67,7 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { } else if s.addrIsLocal(w.RemoteAddr()) { // ignore this - it's our own query received via multicast } else { - log.Printf("Failed MDNS lookup for %s", q.Name) + Debug.Printf("Failed MDNS lookup for %s", q.Name) } } } diff --git a/server/server.go b/server/server.go index 975ccac1..239d0bdd 100644 --- a/server/server.go +++ b/server/server.go @@ -3,7 +3,6 @@ package weavedns import ( "fmt" "github.com/miekg/dns" - "log" "net" ) @@ -22,19 +21,20 @@ func makeDNSFailResponse(r *dns.Msg) *dns.Msg { func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] + Debug.Printf("Local query: %s", q) ip, err := zone.MatchLocal(q.Name) if err == nil { m := makeDNSReply(r, q.Name, dns.TypeA, []net.IP{ip}) w.WriteMsg(m) } else { - log.Printf("Failed lookup for %s; sending mDNS query", q.Name) + Debug.Printf("Failed lookup for %s; sending mDNS query", q.Name) // We don't know the answer; see if someone else does channel := make(chan *ResponseA, ChannelSize) replies := make([]net.IP, 0) go func() { // Loop terminates when channel is closed by MDNSClient on timeout for resp := range channel { - log.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) + Debug.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) replies = append(replies, resp.Addr) } var response_msg *dns.Msg @@ -58,12 +58,14 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { func notUsHandler() dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] + Debug.Printf("Non-local query: %s", q) addrs, err := net.LookupIP(q.Name) var response_msg *dns.Msg if err == nil { response_msg = makeDNSReply(r, q.Name, q.Qtype, addrs) } else { response_msg = makeDNSFailResponse(r) + Debug.Print("Failed fallback lookup", err) } w.WriteMsg(response_msg) } @@ -74,13 +76,13 @@ func StartServer(zone Zone, iface *net.Interface, dnsPort int, httpPort int, wai mdnsClient, err := NewMDNSClient() if err != nil { - log.Fatal(err) + Error.Fatal(err) } else { - log.Printf("Using mDNS on %s", iface.Name) + Info.Printf("Using mDNS on %s", iface.Name) } err = mdnsClient.Start(iface) if err != nil { - log.Fatal(err) + Error.Fatal(err) } LocalServeMux := dns.NewServeMux() @@ -89,19 +91,19 @@ func StartServer(zone Zone, iface *net.Interface, dnsPort int, httpPort int, wai mdnsServer, err := NewMDNSServer(zone) if err != nil { - log.Fatal(err) + Error.Fatal(err) } err = mdnsServer.Start(iface) if err != nil { - log.Fatal(err) + Error.Fatal(err) } address := fmt.Sprintf(":%d", dnsPort) err = dns.ListenAndServe(address, "udp", LocalServeMux) if err != nil { - log.Fatal(err) + Error.Fatal(err) } else { - log.Printf("Listening for DNS on %s", address) + Info.Printf("Listening for DNS on %s", address) } return nil } diff --git a/server/updater.go b/server/updater.go index dee375c7..c0d71f04 100644 --- a/server/updater.go +++ b/server/updater.go @@ -2,19 +2,18 @@ package weavedns import ( "github.com/fsouza/go-dockerclient" - "log" ) func StartUpdater(apiPath string, zone Zone) error { client, err := docker.NewClient(apiPath) if err != nil { - log.Fatalf("Unable to connect to Docker API on %s: %s", apiPath, err) + Error.Fatalf("Unable to connect to Docker API on %s: %s", apiPath, err) } events := make(chan *docker.APIEvents) client.AddEventListener(events) - log.Printf("Using Docker API on %s", apiPath) + Info.Printf("Using Docker API on %s", apiPath) go func() { for event := range events { @@ -28,7 +27,7 @@ func handleEvent(zone Zone, event *docker.APIEvents, client *docker.Client) erro switch event.Status { case "die": id := event.ID - log.Printf("Container %s down. Removing records", id) + Info.Printf("Container %s down. Removing records", id) zone.DeleteRecordsFor(id) } return nil From 55d21d0ec19e4b805bb27dd8d4c866b73047ed6d Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 20 Oct 2014 18:22:25 +0100 Subject: [PATCH 106/145] Replaced another log.Printf (in a file I hadn't noticed) --- server/if_utils.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/if_utils.go b/server/if_utils.go index 86e30eea..9406c533 100644 --- a/server/if_utils.go +++ b/server/if_utils.go @@ -2,7 +2,6 @@ package weavedns import ( "fmt" - "log" "net" "time" ) @@ -12,13 +11,13 @@ func EnsureInterface(ifaceName string, wait int) (iface *net.Interface, err erro if err == nil || wait == 0 { return } - log.Println("Waiting for interface", ifaceName, "to come up") + Info.Println("Waiting for interface", ifaceName, "to come up") for ; err != nil && wait > 0; wait -= 1 { time.Sleep(1 * time.Second) iface, err = findInterface(ifaceName) } if err == nil { - log.Println("Interface", ifaceName, "is up") + Info.Println("Interface", ifaceName, "is up") } return } From 35c09f37b992870174d2ec23db5914dd738d02c8 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 20 Oct 2014 18:27:24 +0100 Subject: [PATCH 107/145] Added logging.go, required by prior changes --- server/logging.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 server/logging.go diff --git a/server/logging.go b/server/logging.go new file mode 100644 index 00000000..674c05be --- /dev/null +++ b/server/logging.go @@ -0,0 +1,26 @@ +package weavedns + +import ( + "io" + "log" +) + +// Inspired by http://www.goinggo.net/2013/11/using-log-package-in-go.html + +var ( + Debug *log.Logger + Info *log.Logger + Warning *log.Logger + Error *log.Logger +) + +func InitLogging(debugHandle io.Writer, + infoHandle io.Writer, + warningHandle io.Writer, + errorHandle io.Writer) { + + Debug = log.New(debugHandle, "DEBUG: ", log.Ldate|log.Ltime|log.Lshortfile) + Info = log.New(infoHandle, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) + Warning = log.New(warningHandle, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile) + Error = log.New(errorHandle, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) +} From e8f970521137bf83d530d4af2a059ad8f36d591e Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 20 Oct 2014 18:39:40 +0100 Subject: [PATCH 108/145] Updated README re local domain --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4bd95a25..45e05318 100644 --- a/README.md +++ b/README.md @@ -25,20 +25,20 @@ for other containers: ```bash $ dns_ip=$(docker inspect --format='{{ .NetworkSettings.IPAddress }}' weavedns) -$ shell1=$(weave run 10.0.1.2/24 --dns=$dns_ip -t ubuntu /bin/bash) +$ shell1=$(weave run 10.0.1.2/24 -h shell.weave.local --dns=$dns_ip -t ubuntu /bin/bash) ``` To associate a hostname with an IP, use an HTTP PUT to the DNS server: ```bash $ shell1_ip=$(docker inspect --format='{{ .NetworkSettings.IPAddress }}' $shell1) -$ curl -X PUT "http://$dns_ip:6785/name/$shell1/10.0.1.2?fqdn=shell1.weave&routing_prefix=24&local_ip=$shell1_ip" +$ curl -X PUT "http://$dns_ip:6785/name/$shell1/10.0.1.2?fqdn=shell1.weave.local&routing_prefix=24&local_ip=$shell1_ip" ``` You should now be able to look up the name: ```bash -$ dig @$dns_ip +short shell1.weave +$ dig @$dns_ip +short shell1.weave.local ``` By default, the server will also watch docker events and remove From 699b685969ef4231ca32de5f020ea857a8bfe195 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 21 Oct 2014 11:55:13 +0100 Subject: [PATCH 109/145] Adjust test to match change in ListenHttp --- server/http_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/http_test.go b/server/http_test.go index 483dc920..e33bfe20 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -20,7 +20,8 @@ func genForm(method string, url string, data url.Values) (resp *http.Response, e func TestHttp(t *testing.T) { var ( container_id = "deadbeef" - success_test_name = "test1.weave." + test_domain = "weave.local." + success_test_name = "test1." + test_domain test_addr1 = "10.0.2.1/24" docker_ip = "9.8.7.6" ) @@ -28,7 +29,7 @@ func TestHttp(t *testing.T) { var zone = new(ZoneDb) port := rand.Intn(10000) + 32768 fmt.Println("Http test on port", port) - go ListenHttp(zone, port) + go ListenHttp(test_domain, zone, port) time.Sleep(100 * time.Millisecond) // Allow for http server to get going From 10685a495e984532ca683288685bf83b708277bb Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Tue, 21 Oct 2014 11:57:12 +0100 Subject: [PATCH 110/145] Set up defaults so it doesn't crash in tests --- server/logging.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/server/logging.go b/server/logging.go index 674c05be..428db03f 100644 --- a/server/logging.go +++ b/server/logging.go @@ -2,16 +2,22 @@ package weavedns import ( "io" + "io/ioutil" "log" + "os" +) + +const ( + standard_log_flags = log.Ldate | log.Ltime | log.Lshortfile ) // Inspired by http://www.goinggo.net/2013/11/using-log-package-in-go.html var ( - Debug *log.Logger - Info *log.Logger - Warning *log.Logger - Error *log.Logger + Debug *log.Logger = log.New(ioutil.Discard, "DEBUG: ", standard_log_flags) + Info *log.Logger = log.New(os.Stdout, "INFO: ", standard_log_flags) + Warning *log.Logger = log.New(os.Stdout, "WARNING: ", standard_log_flags) + Error *log.Logger = log.New(os.Stdout, "ERROR: ", standard_log_flags) ) func InitLogging(debugHandle io.Writer, @@ -19,8 +25,8 @@ func InitLogging(debugHandle io.Writer, warningHandle io.Writer, errorHandle io.Writer) { - Debug = log.New(debugHandle, "DEBUG: ", log.Ldate|log.Ltime|log.Lshortfile) - Info = log.New(infoHandle, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile) - Warning = log.New(warningHandle, "WARNING: ", log.Ldate|log.Ltime|log.Lshortfile) - Error = log.New(errorHandle, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile) + Debug = log.New(debugHandle, "DEBUG: ", standard_log_flags) + Info = log.New(infoHandle, "INFO: ", standard_log_flags) + Warning = log.New(warningHandle, "WARNING: ", standard_log_flags) + Error = log.New(errorHandle, "ERROR: ", standard_log_flags) } From 1a593fc81feb5ae5a6cf78e33d5171ec811891c4 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 21 Oct 2014 12:25:05 +0100 Subject: [PATCH 111/145] Used an init func instead of initialisers Ends up slightly neater. (Also note Error goes from os.Stdout to os.Stderr) --- server/logging.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/server/logging.go b/server/logging.go index 428db03f..1fad63d3 100644 --- a/server/logging.go +++ b/server/logging.go @@ -14,10 +14,10 @@ const ( // Inspired by http://www.goinggo.net/2013/11/using-log-package-in-go.html var ( - Debug *log.Logger = log.New(ioutil.Discard, "DEBUG: ", standard_log_flags) - Info *log.Logger = log.New(os.Stdout, "INFO: ", standard_log_flags) - Warning *log.Logger = log.New(os.Stdout, "WARNING: ", standard_log_flags) - Error *log.Logger = log.New(os.Stdout, "ERROR: ", standard_log_flags) + Debug *log.Logger + Info *log.Logger + Warning *log.Logger + Error *log.Logger ) func InitLogging(debugHandle io.Writer, @@ -30,3 +30,7 @@ func InitLogging(debugHandle io.Writer, Warning = log.New(warningHandle, "WARNING: ", standard_log_flags) Error = log.New(errorHandle, "ERROR: ", standard_log_flags) } + +func init() { + InitLogging(ioutil.Discard, os.Stdout, os.Stdout, os.Stderr) +} From 6eb7d71499e726ffbdce90254bfa531d9234fa44 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 21 Oct 2014 13:57:42 +0100 Subject: [PATCH 112/145] Substituted checkFatal for repeated if err == nil --- server/server.go | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/server/server.go b/server/server.go index 239d0bdd..ba5f8cb0 100644 --- a/server/server.go +++ b/server/server.go @@ -10,6 +10,18 @@ const ( LOCAL_DOMAIN = "weave.local." ) +func checkFatal(e error) { + if e != nil { + Error.Fatal(e) + } +} + +func checkWarn(e error) { + if e != nil { + Warning.Println(e) + } +} + func makeDNSFailResponse(r *dns.Msg) *dns.Msg { m := new(dns.Msg) m.SetReply(r) @@ -75,35 +87,26 @@ func StartServer(zone Zone, iface *net.Interface, dnsPort int, httpPort int, wai go ListenHttp(LOCAL_DOMAIN, zone, httpPort) mdnsClient, err := NewMDNSClient() - if err != nil { - Error.Fatal(err) - } else { - Info.Printf("Using mDNS on %s", iface.Name) - } + checkFatal(err) + + Info.Printf("Using mDNS on %s", iface.Name) err = mdnsClient.Start(iface) - if err != nil { - Error.Fatal(err) - } + checkFatal(err) LocalServeMux := dns.NewServeMux() LocalServeMux.HandleFunc(LOCAL_DOMAIN, queryHandler(zone, mdnsClient)) LocalServeMux.HandleFunc(".", notUsHandler()) mdnsServer, err := NewMDNSServer(zone) - if err != nil { - Error.Fatal(err) - } + checkFatal(err) + err = mdnsServer.Start(iface) - if err != nil { - Error.Fatal(err) - } + checkFatal(err) address := fmt.Sprintf(":%d", dnsPort) err = dns.ListenAndServe(address, "udp", LocalServeMux) - if err != nil { - Error.Fatal(err) - } else { - Info.Printf("Listening for DNS on %s", address) - } + checkFatal(err) + + Info.Printf("Listening for DNS on %s", address) return nil } From 23edd79177a31f56aa5a6fd52fcd4a7b3083c1bb Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 21 Oct 2014 14:01:07 +0100 Subject: [PATCH 113/145] Changed Lock to RLock when matching --- server/zone.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/zone.go b/server/zone.go index 9c7cf9f3..240f423a 100644 --- a/server/zone.go +++ b/server/zone.go @@ -72,8 +72,8 @@ func (zone *ZoneDb) indexOfIdentAddr(ident string, addr net.IP) int { } func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { - zone.mx.Lock() - defer zone.mx.Unlock() + zone.mx.RLock() + defer zone.mx.RUnlock() return zone.match(name) } From 5c6410a3c6d00494f793420e4cdc91cc81db5f76 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 21 Oct 2014 15:49:42 +0100 Subject: [PATCH 114/145] Refactored indexOf* --- server/zone.go | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/server/zone.go b/server/zone.go index 240f423a..61b34476 100644 --- a/server/zone.go +++ b/server/zone.go @@ -44,27 +44,9 @@ func (err DuplicateError) Error() string { return "Duplicate " + err.Name + "," + err.WeaveIp.String() + " in container " + err.Ident } -func (zone *ZoneDb) match(name string) (net.IP, error) { - for _, r := range zone.recs { - if r.Name == name { - return r.WeaveIp, nil - } - } - return nil, LookupError(name) -} - -func (zone *ZoneDb) indexOfNameAddr(name string, addr net.IP) int { +func (zone *ZoneDb) indexOf(match func(Record) bool) int { for i, r := range zone.recs { - if r.Name == name && r.WeaveIp.Equal(addr) { - return i - } - } - return -1 -} - -func (zone *ZoneDb) indexOfIdentAddr(ident string, addr net.IP) int { - for i, r := range zone.recs { - if r.Ident == ident && r.WeaveIp.Equal(addr) { + if match(r) { return i } } @@ -74,14 +56,20 @@ func (zone *ZoneDb) indexOfIdentAddr(ident string, addr net.IP) int { func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { zone.mx.RLock() defer zone.mx.RUnlock() - return zone.match(name) + for _, r := range zone.recs { + if r.Name == name { + return r.WeaveIp, nil + } + } + return nil, LookupError(name) } func (zone *ZoneDb) AddRecord(identifier string, name string, ip net.IP, weave_ip net.IP, weave_subnet *net.IPNet) error { zone.mx.Lock() defer zone.mx.Unlock() fqdn := dns.Fqdn(name) - if index := zone.indexOfNameAddr(fqdn, weave_ip); index != -1 { + if index := zone.indexOf( + func(r Record) bool { return r.Name == fqdn && r.WeaveIp.Equal(weave_ip) }); index != -1 { return DuplicateError{fqdn, weave_ip, zone.recs[index].Ident} } zone.recs = append(zone.recs, Record{identifier, fqdn, ip, weave_ip, weave_subnet}) @@ -91,12 +79,12 @@ func (zone *ZoneDb) AddRecord(identifier string, name string, ip net.IP, weave_i func (zone *ZoneDb) DeleteRecord(ident string, weave_ip net.IP) error { zone.mx.Lock() defer zone.mx.Unlock() - if index := zone.indexOfIdentAddr(ident, weave_ip); index == -1 { + if index := zone.indexOf( + func(r Record) bool { return r.Ident == ident && r.WeaveIp.Equal(weave_ip) }); index == -1 { return LookupError(ident) } else { zone.recs = append(zone.recs[:index], zone.recs[index+1:]...) } - return nil } From 0615d122e92ccdb7cc090841ba8203c13ac797a2 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 21 Oct 2014 17:05:03 +0100 Subject: [PATCH 115/145] Made zone return errors and http idempotent The Zone DB tells you if you have a duplicate or missing record, but the HTTP server smoothes these over. --- server/http.go | 15 ++++++++++++--- server/http_test.go | 24 ++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/server/http.go b/server/http.go index ae5be5ce..69755620 100644 --- a/server/http.go +++ b/server/http.go @@ -48,7 +48,13 @@ func ListenHttp(domain string, db Zone, port int) { Info.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) err = db.AddRecord(identifier, name, ip, weave_ip, subnet) if err != nil { - http.Error(w, err.Error(), http.StatusConflict) + dup, ok := err.(DuplicateError) + if !ok { + Error.Print("Unexpected error from DB", err) + http.Error(w, "Internal error", http.StatusInternalServerError) + } else if dup.Ident != identifier { + http.Error(w, err.Error(), http.StatusConflict) + } return } } else { @@ -70,8 +76,11 @@ func ListenHttp(domain string, db Zone, port int) { Info.Printf("Deleting %s (%s)", identifier, weave_ipstr) err = db.DeleteRecord(identifier, weave_ip) if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return + if _, ok := err.(LookupError); !ok { + Error.Print("Unexpected error from DB", err) + http.Error(w, "Internal error", http.StatusInternalServerError) + return + } } default: Warning.Println("Unexpected http method", r.Method) diff --git a/server/http_test.go b/server/http_test.go index e33bfe20..9f08bdc1 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -41,7 +41,7 @@ func TestHttp(t *testing.T) { if err != nil { t.Fatal(err) } - if resp.StatusCode != 200 { + if resp.StatusCode != http.StatusOK { t.Fatal("Unexpected http response", resp.Status) } @@ -55,12 +55,23 @@ func TestHttp(t *testing.T) { t.Fatal("Unexpected result for", success_test_name, ip) } - // Now try adding the same address again - should fail + // Adding exactly the same address should be OK resp, err = genForm("PUT", addr_url, url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) if err != nil { t.Fatal(err) } + if resp.StatusCode != http.StatusOK { + t.Fatal("Expected duplicate add to succeed with 200 OK") + } + + // Now try adding the same address again with a different ident - should fail + other_url := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, "other", addr_parts[0]) + resp, err = genForm("PUT", other_url, + url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) + if err != nil { + t.Fatal(err) + } if resp.StatusCode != http.StatusConflict { t.Fatal("Unexpected http response", resp.Status) } @@ -80,6 +91,15 @@ func TestHttp(t *testing.T) { t.Fatal(err) } + // Delete the address again, it should accept this + resp, err = genForm("DELETE", addr_url, nil) + if err != nil { + t.Fatal(err) + } + if resp.StatusCode != http.StatusOK { + t.Fatal("Unexpected http response", resp.Status) + } + // Would like to shut down the http server at the end of this test // but it's complicated. // See https://groups.google.com/forum/#!topic/golang-nuts/vLHWa5sHnCE From 1d5a55ea758d1cc8082bd36279ee00e7a2b37176 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 21 Oct 2014 17:06:46 +0100 Subject: [PATCH 116/145] Factored out more (but different) err != nil code --- server/zone_test.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/server/zone_test.go b/server/zone_test.go index 1e2bc77c..fbd67e04 100644 --- a/server/zone_test.go +++ b/server/zone_test.go @@ -5,6 +5,12 @@ import ( "testing" ) +func assertNoErr(t *testing.T, err error) { + if err != nil { + t.Fatal(err) + } +} + func TestZone(t *testing.T) { var ( container_id = "deadbeef" @@ -18,15 +24,12 @@ func TestZone(t *testing.T) { ip := net.ParseIP(docker_ip) weave_ip, subnet, _ := net.ParseCIDR(test_addr1) err := zone.AddRecord(container_id, success_test_name, ip, weave_ip, subnet) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) // Check that the address is now there. found_ip, err := zone.MatchLocal(success_test_name) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) + if !found_ip.Equal(weave_ip) { t.Fatal("Unexpected result for", success_test_name, ip) } @@ -39,9 +42,7 @@ func TestZone(t *testing.T) { // Now delete the record err = zone.DeleteRecord(container_id, weave_ip) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) // Check that the address is not there now. _, err = zone.MatchLocal(success_test_name) @@ -49,6 +50,11 @@ func TestZone(t *testing.T) { t.Fatal("Unexpected result when deleting record", success_test_name, err) } + // Delete a record that isn't there + err = zone.DeleteRecord(container_id, net.ParseIP("0.0.0.0")) + if _, ok := err.(LookupError); !ok { + t.Fatal("Expected a LookupError when deleting a record that doesn't exist") + } } func TestDeleteFor(t *testing.T) { @@ -64,15 +70,12 @@ func TestDeleteFor(t *testing.T) { for _, addr := range []string{addr1, addr2} { weave_ip, subnet, _ := net.ParseCIDR(addr) err := zone.AddRecord(id, name, ip, weave_ip, subnet) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) } _, err := zone.MatchLocal(name) - if err != nil { - t.Fatal("Did not get result for lookup") - } + assertNoErr(t, err) + err = zone.DeleteRecordsFor(id) _, err = zone.MatchLocal(name) if _, ok := err.(LookupError); !ok { From e21069f3ab2ff6b8956011327466ec75697a4605 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 22 Oct 2014 11:58:55 +0100 Subject: [PATCH 117/145] Factored out HTTP error + log --- server/http.go | 55 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/server/http.go b/server/http.go index 69755620..7bdce9ce 100644 --- a/server/http.go +++ b/server/http.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/miekg/dns" + "log" "net" "net/http" "strings" @@ -18,75 +19,93 @@ func parseUrl(url string) (identifier string, ipaddr string, err error) { return parts[2], parts[3], nil } +func httpErrorAndLog(level *log.Logger, w http.ResponseWriter, msg string, + status int, logmsg string, logargs ...interface{}) { + http.Error(w, msg, status) + level.Printf(logmsg, logargs...) +} + func ListenHttp(domain string, db Zone, port int) { http.HandleFunc("/name/", func(w http.ResponseWriter, r *http.Request) { + + reqError := func(msg string, logmsg string, logargs ...interface{}) { + httpErrorAndLog(Warning, w, msg, http.StatusBadRequest, + logmsg, logargs...) + } + switch r.Method { case "PUT": identifier, weave_ipstr, err := parseUrl(r.URL.Path) name := r.FormValue("fqdn") prefix := r.FormValue("routing_prefix") local_ip := r.FormValue("local_ip") + if identifier == "" || weave_ipstr == "" || name == "" || prefix == "" || local_ip == "" { - Warning.Printf("Invalid request: %s, %s", r.URL, r.Form) - http.Error(w, "Invalid Request", http.StatusBadRequest) + reqError("Invalid request", "Invalid request: %s, %s", r.URL, r.Form) return } + ip := net.ParseIP(local_ip) if ip == nil { - Warning.Printf("Invalid IP in request: %s", local_ip) - http.Error(w, "Invalid IP in request", http.StatusBadRequest) + reqError("Invalid IP in request", "Invalid IP in request: %s", local_ip) return } + weave_cidr := weave_ipstr + "/" + prefix weave_ip, subnet, err := net.ParseCIDR(weave_cidr) if err != nil { - Warning.Printf("Invalid CIDR in request: %s", weave_cidr) - http.Error(w, fmt.Sprintf("Invalid CIDR: %s", weave_cidr), http.StatusBadRequest) + reqError("Invalid CIDR", "Invalid CIDR in request: %s", weave_cidr) return } + if dns.IsSubDomain(domain, name) { Info.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) err = db.AddRecord(identifier, name, ip, weave_ip, subnet) if err != nil { dup, ok := err.(DuplicateError) if !ok { - Error.Print("Unexpected error from DB", err) - http.Error(w, "Internal error", http.StatusInternalServerError) + httpErrorAndLog( + Error, w, "Internal error", http.StatusInternalServerError, + "Unexpected error from DB", err) + return } else if dup.Ident != identifier { http.Error(w, err.Error(), http.StatusConflict) - } - return + return + } // else we are golden } } else { Info.Printf("Ignoring name %s, not in %s", name, domain) } + case "DELETE": identifier, weave_ipstr, err := parseUrl(r.URL.Path) if identifier == "" || weave_ipstr == "" { - Warning.Printf("Invalid request: %s, %s", r.URL, r.Form) - http.Error(w, "Invalid Request", http.StatusBadRequest) + reqError("Invalid Request", "Invalid request: %s, %s", r.URL, r.Form) return } + weave_ip := net.ParseIP(weave_ipstr) if weave_ip == nil { - Warning.Printf("Invalid IP in request: %s", weave_ipstr) - http.Error(w, "Invalid IP in request", http.StatusBadRequest) + reqError("Invalid IP in request", "Invalid IP in request: %s", weave_ipstr) return } Info.Printf("Deleting %s (%s)", identifier, weave_ipstr) err = db.DeleteRecord(identifier, weave_ip) if err != nil { if _, ok := err.(LookupError); !ok { - Error.Print("Unexpected error from DB", err) - http.Error(w, "Internal error", http.StatusInternalServerError) + httpErrorAndLog( + Error, w, "Internal error", http.StatusInternalServerError, + "Unexpected error from DB", err) return } } default: - Warning.Println("Unexpected http method", r.Method) - http.Error(w, "Unexpected http method: "+r.Method, http.StatusBadRequest) + msg := "Unexpected http method: " + r.Method + reqError(msg, msg) + return } }) + address := fmt.Sprintf(":%d", port) err := http.ListenAndServe(address, nil) if err != nil { From 156314a8027dfb718923b4717aabfa2eb70b1443 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 22 Oct 2014 13:22:50 +0100 Subject: [PATCH 118/145] Complete multicast requests on first response This is the simplest thing that works, is consistent with what happens if there's a local answer, and avoids having all multicast requests last 250ms or more. Since we're no longer waiting *at least* the timeout period, I've bumped the multicast response timeout up to half a second. There was also a bug whereby the query would be entered into the inflight map even if the socket write had failed (and the channel had been closed). --- server/mdns_client.go | 13 ++++++++----- server/server.go | 3 +-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index 26c23bbe..f5d5d672 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -14,9 +14,9 @@ const ( mdnsPort = 5353 // mDNS assigned port // We wait this long to hear responses from other mDNS servers on the network. // TODO: introduce caching so we don't have to wait this long on every call. - mDNSTimeout = 250 * time.Millisecond + mDNSTimeout = 500 * time.Millisecond MaxDuration = time.Duration(math.MaxInt64) - ChannelSize = 16 + MailboxSize = 16 ) var ( @@ -88,7 +88,7 @@ func (c *MDNSClient) Start(ifi *net.Interface) error { c.server = &dns.Server{Listener: nil, PacketConn: multicast, Handler: dns.HandlerFunc(handleMDNS)} go c.server.ActivateAndServe() - queryChan := make(chan *MDNSInteraction, ChannelSize) + queryChan := make(chan *MDNSInteraction, MailboxSize) c.queryChan = queryChan go c.queryLoop(queryChan) @@ -216,13 +216,13 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) { name: q.name, id: m.Id, } - c.inflight[q.name] = query _, err = c.conn.WriteTo(buf, c.addr) if err != nil { q.responseCh <- &ResponseA{Err: err} close(q.responseCh) return } + c.inflight[q.name] = query } info := &responseInfo{ ch: q.responseCh, @@ -237,10 +237,13 @@ func (c *MDNSClient) handleResponse(r *dns.Msg) { for _, answer := range r.Answer { switch rr := answer.(type) { case *dns.A: - if query, found := c.inflight[rr.Hdr.Name]; found { + name := rr.Hdr.Name + if query, found := c.inflight[name]; found { for _, resp := range query.responseInfos { resp.ch <- &ResponseA{Name: rr.Hdr.Name, Addr: rr.A} + close(resp.ch) } + delete(c.inflight, name) } else { // We've received a response that didn't match a query // Do we want to cache it? diff --git a/server/server.go b/server/server.go index ba5f8cb0..e4a338c2 100644 --- a/server/server.go +++ b/server/server.go @@ -41,10 +41,9 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { } else { Debug.Printf("Failed lookup for %s; sending mDNS query", q.Name) // We don't know the answer; see if someone else does - channel := make(chan *ResponseA, ChannelSize) + channel := make(chan *ResponseA) replies := make([]net.IP, 0) go func() { - // Loop terminates when channel is closed by MDNSClient on timeout for resp := range channel { Debug.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) replies = append(replies, resp.Addr) From d84226cc31d7ef1bc517a42b8aeb3350dcb0e74b Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 22 Oct 2014 16:52:35 +0100 Subject: [PATCH 119/145] Factor out testing error checks --- server/http_test.go | 24 ++++++------------------ server/mdns_client_test.go | 12 +++--------- server/mdns_server_test.go | 12 +++--------- server/testing_utils.go | 11 +++++++++++ server/zone_test.go | 6 ------ 5 files changed, 23 insertions(+), 42 deletions(-) create mode 100644 server/testing_utils.go diff --git a/server/http_test.go b/server/http_test.go index 9f08bdc1..879b2a36 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -38,18 +38,14 @@ func TestHttp(t *testing.T) { addr_url := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]) resp, err := genForm("PUT", addr_url, url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) if resp.StatusCode != http.StatusOK { t.Fatal("Unexpected http response", resp.Status) } // Check that the address is now there. ip, err := zone.MatchLocal(success_test_name) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) weave_ip, _, _ := net.ParseCIDR(test_addr1) if !ip.Equal(weave_ip) { t.Fatal("Unexpected result for", success_test_name, ip) @@ -58,9 +54,7 @@ func TestHttp(t *testing.T) { // Adding exactly the same address should be OK resp, err = genForm("PUT", addr_url, url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) if resp.StatusCode != http.StatusOK { t.Fatal("Expected duplicate add to succeed with 200 OK") } @@ -69,18 +63,14 @@ func TestHttp(t *testing.T) { other_url := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, "other", addr_parts[0]) resp, err = genForm("PUT", other_url, url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) if resp.StatusCode != http.StatusConflict { t.Fatal("Unexpected http response", resp.Status) } // Delete the address resp, err = genForm("DELETE", addr_url, nil) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) if resp.StatusCode != http.StatusOK { t.Fatal("Unexpected http response", resp.Status) } @@ -93,9 +83,7 @@ func TestHttp(t *testing.T) { // Delete the address again, it should accept this resp, err = genForm("DELETE", addr_url, nil) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) if resp.StatusCode != http.StatusOK { t.Fatal("Unexpected http response", resp.Status) } diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index 8d5795f0..6145b765 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -66,13 +66,9 @@ func RunLocalMulticastServer() (*dns.Server, error) { func setup(t *testing.T) (*MDNSClient, *dns.Server, error) { mdnsClient, err := NewMDNSClient() - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) err = mdnsClient.Start(nil) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) server, err := RunLocalMulticastServer() if err != nil { @@ -96,9 +92,7 @@ func (c *testContext) checkResponse(t *testing.T, channel_ok bool, resp *Respons c.channel = nil return } - if resp.Err != nil { - t.Fatal(resp.Err) - } + assertNoErr(t, resp.Err) log.Printf("Got address response %s addr %s", resp.Name, resp.Addr) c.received_addr = resp.Addr c.received_count++ diff --git a/server/mdns_server_test.go b/server/mdns_server_test.go index aafe821e..3560a460 100644 --- a/server/mdns_server_test.go +++ b/server/mdns_server_test.go @@ -37,22 +37,16 @@ func TestServerSimpleQuery(t *testing.T) { zone.AddRecord(container_id, "test.weave.", docker_ip, weave_ip, subnet) mdnsServer, err := NewMDNSServer(zone) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) err = mdnsServer.Start(nil) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) var received_addr net.IP received_count := 0 // Implement a minimal listener for responses multicast, err := LinkLocalMulticastListener(nil) - if err != nil { - t.Fatal(err) - } + assertNoErr(t, err) handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) { // Only handle responses here diff --git a/server/testing_utils.go b/server/testing_utils.go new file mode 100644 index 00000000..9a30a59c --- /dev/null +++ b/server/testing_utils.go @@ -0,0 +1,11 @@ +package weavedns + +import ( + "testing" +) + +func assertNoErr(t *testing.T, err error) { + if err != nil { + t.Fatal(err) + } +} diff --git a/server/zone_test.go b/server/zone_test.go index fbd67e04..236b7518 100644 --- a/server/zone_test.go +++ b/server/zone_test.go @@ -5,12 +5,6 @@ import ( "testing" ) -func assertNoErr(t *testing.T, err error) { - if err != nil { - t.Fatal(err) - } -} - func TestZone(t *testing.T) { var ( container_id = "deadbeef" From 0a5990bd6e8c00e963441070b1e0d37117c43d53 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 22 Oct 2014 16:59:28 +0100 Subject: [PATCH 120/145] Factor out testing status checks --- server/http_test.go | 20 +++++--------------- server/testing_utils.go | 6 ++++++ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/server/http_test.go b/server/http_test.go index 879b2a36..a53792fe 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -39,9 +39,7 @@ func TestHttp(t *testing.T) { resp, err := genForm("PUT", addr_url, url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) assertNoErr(t, err) - if resp.StatusCode != http.StatusOK { - t.Fatal("Unexpected http response", resp.Status) - } + assertStatus(t, resp.StatusCode, http.StatusOK, "http response") // Check that the address is now there. ip, err := zone.MatchLocal(success_test_name) @@ -55,25 +53,19 @@ func TestHttp(t *testing.T) { resp, err = genForm("PUT", addr_url, url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) assertNoErr(t, err) - if resp.StatusCode != http.StatusOK { - t.Fatal("Expected duplicate add to succeed with 200 OK") - } + assertStatus(t, resp.StatusCode, http.StatusOK, "http success response for duplicate add") // Now try adding the same address again with a different ident - should fail other_url := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, "other", addr_parts[0]) resp, err = genForm("PUT", other_url, url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) assertNoErr(t, err) - if resp.StatusCode != http.StatusConflict { - t.Fatal("Unexpected http response", resp.Status) - } + assertStatus(t, resp.StatusCode, http.StatusConflict, "http response") // Delete the address resp, err = genForm("DELETE", addr_url, nil) assertNoErr(t, err) - if resp.StatusCode != http.StatusOK { - t.Fatal("Unexpected http response", resp.Status) - } + assertStatus(t, resp.StatusCode, http.StatusOK, "http response") // Check that the address is not there now. _, err = zone.MatchLocal(success_test_name) @@ -84,9 +76,7 @@ func TestHttp(t *testing.T) { // Delete the address again, it should accept this resp, err = genForm("DELETE", addr_url, nil) assertNoErr(t, err) - if resp.StatusCode != http.StatusOK { - t.Fatal("Unexpected http response", resp.Status) - } + assertStatus(t, resp.StatusCode, http.StatusOK, "http response") // Would like to shut down the http server at the end of this test // but it's complicated. diff --git a/server/testing_utils.go b/server/testing_utils.go index 9a30a59c..c5695c93 100644 --- a/server/testing_utils.go +++ b/server/testing_utils.go @@ -9,3 +9,9 @@ func assertNoErr(t *testing.T, err error) { t.Fatal(err) } } + +func assertStatus(t *testing.T, got int, wanted int, desc string) { + if got != wanted { + t.Fatalf("Expected %s %d but got %d", desc, wanted, got) + } +} From 94a812d98f8d22c67f85faf062f95cf08ebf2c7a Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 22 Oct 2014 18:24:02 +0100 Subject: [PATCH 121/145] Factor out error type checks --- server/http_test.go | 4 +--- server/testing_utils.go | 15 +++++++++++++++ server/zone_test.go | 16 ++++------------ 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/server/http_test.go b/server/http_test.go index a53792fe..fe2b1c04 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -69,9 +69,7 @@ func TestHttp(t *testing.T) { // Check that the address is not there now. _, err = zone.MatchLocal(success_test_name) - if _, ok := err.(LookupError); !ok { - t.Fatal(err) - } + assertErrorType(t, err, (*LookupError)(nil), "nonexistent lookup") // Delete the address again, it should accept this resp, err = genForm("DELETE", addr_url, nil) diff --git a/server/testing_utils.go b/server/testing_utils.go index c5695c93..53dab804 100644 --- a/server/testing_utils.go +++ b/server/testing_utils.go @@ -1,6 +1,7 @@ package weavedns import ( + "reflect" "testing" ) @@ -15,3 +16,17 @@ func assertStatus(t *testing.T, got int, wanted int, desc string) { t.Fatalf("Expected %s %d but got %d", desc, wanted, got) } } + +func assertErrorInterface(t *testing.T, got interface{}, wanted interface{}, desc string) { + got_t, wanted_t := reflect.TypeOf(got), reflect.TypeOf(wanted).Elem() + if !got_t.Implements(wanted_t) { + t.Fatalf("Expected %s but got %s (%s)", wanted_t.String(), got_t.String(), desc) + } +} + +func assertErrorType(t *testing.T, got interface{}, wanted interface{}, desc string) { + got_t, wanted_t := reflect.TypeOf(got), reflect.TypeOf(wanted).Elem() + if got_t != wanted_t { + t.Fatalf("Expected %s but got %s (%s)", wanted_t.String(), got_t.String(), desc) + } +} diff --git a/server/zone_test.go b/server/zone_test.go index 236b7518..6fe44094 100644 --- a/server/zone_test.go +++ b/server/zone_test.go @@ -30,9 +30,7 @@ func TestZone(t *testing.T) { // Now try to add the same address again err = zone.AddRecord(container_id, success_test_name, ip, weave_ip, subnet) - if _, ok := err.(DuplicateError); !ok { - t.Fatal(err) - } + assertErrorType(t, err, (*DuplicateError)(nil), "duplicate add") // Now delete the record err = zone.DeleteRecord(container_id, weave_ip) @@ -40,15 +38,11 @@ func TestZone(t *testing.T) { // Check that the address is not there now. _, err = zone.MatchLocal(success_test_name) - if _, ok := err.(LookupError); !ok { - t.Fatal("Unexpected result when deleting record", success_test_name, err) - } + assertErrorType(t, err, (*LookupError)(nil), "after deleting record") // Delete a record that isn't there err = zone.DeleteRecord(container_id, net.ParseIP("0.0.0.0")) - if _, ok := err.(LookupError); !ok { - t.Fatal("Expected a LookupError when deleting a record that doesn't exist") - } + assertErrorType(t, err, (*LookupError)(nil), "when deleting record that doesn't exist") } func TestDeleteFor(t *testing.T) { @@ -72,7 +66,5 @@ func TestDeleteFor(t *testing.T) { err = zone.DeleteRecordsFor(id) _, err = zone.MatchLocal(name) - if _, ok := err.(LookupError); !ok { - t.Fatal("Expected no results after deleting records for ident") - } + assertErrorType(t, err, (*LookupError)(nil), "after deleting records for ident") } From c63b1637032041176398d75045857961780969da Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Wed, 22 Oct 2014 18:27:55 +0100 Subject: [PATCH 122/145] Formatting in error message --- server/http.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/http.go b/server/http.go index 7bdce9ce..e12aef44 100644 --- a/server/http.go +++ b/server/http.go @@ -66,7 +66,7 @@ func ListenHttp(domain string, db Zone, port int) { if !ok { httpErrorAndLog( Error, w, "Internal error", http.StatusInternalServerError, - "Unexpected error from DB", err) + "Unexpected error from DB: %s", err) return } else if dup.Ident != identifier { http.Error(w, err.Error(), http.StatusConflict) @@ -95,7 +95,7 @@ func ListenHttp(domain string, db Zone, port int) { if _, ok := err.(LookupError); !ok { httpErrorAndLog( Error, w, "Internal error", http.StatusInternalServerError, - "Unexpected error from DB", err) + "Unexpected error from DB: %s", err) return } } From 840c0b9f863243fef468ae45ca8777065a6e28ea Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Thu, 23 Oct 2014 15:45:48 +0100 Subject: [PATCH 123/145] Renamed variables for consistency --- server/http.go | 34 ++++++++++++++--------------- server/http_test.go | 44 +++++++++++++++++++------------------- server/logging.go | 10 ++++----- server/mdns_client.go | 6 +++--- server/mdns_client_test.go | 42 ++++++++++++++++++------------------ server/mdns_server_test.go | 32 +++++++++++++-------------- server/server.go | 16 +++++++------- server/testing_utils.go | 12 +++++------ server/zone.go | 29 +++++++++++++------------ server/zone_test.go | 44 +++++++++++++++++++------------------- 10 files changed, 135 insertions(+), 134 deletions(-) diff --git a/server/http.go b/server/http.go index e12aef44..a98fd5d5 100644 --- a/server/http.go +++ b/server/http.go @@ -35,32 +35,32 @@ func ListenHttp(domain string, db Zone, port int) { switch r.Method { case "PUT": - identifier, weave_ipstr, err := parseUrl(r.URL.Path) + identifier, weaveIPstr, err := parseUrl(r.URL.Path) name := r.FormValue("fqdn") prefix := r.FormValue("routing_prefix") - local_ip := r.FormValue("local_ip") + localIP := r.FormValue("local_ip") - if identifier == "" || weave_ipstr == "" || name == "" || prefix == "" || local_ip == "" { + if identifier == "" || weaveIPstr == "" || name == "" || prefix == "" || localIP == "" { reqError("Invalid request", "Invalid request: %s, %s", r.URL, r.Form) return } - ip := net.ParseIP(local_ip) + ip := net.ParseIP(localIP) if ip == nil { - reqError("Invalid IP in request", "Invalid IP in request: %s", local_ip) + reqError("Invalid IP in request", "Invalid IP in request: %s", localIP) return } - weave_cidr := weave_ipstr + "/" + prefix - weave_ip, subnet, err := net.ParseCIDR(weave_cidr) + weaveCIDR := weaveIPstr + "/" + prefix + weaveIP, subnet, err := net.ParseCIDR(weaveCIDR) if err != nil { - reqError("Invalid CIDR", "Invalid CIDR in request: %s", weave_cidr) + reqError("Invalid CIDR", "Invalid CIDR in request: %s", weaveCIDR) return } if dns.IsSubDomain(domain, name) { - Info.Printf("Adding %s (%s) -> %s", name, local_ip, weave_cidr) - err = db.AddRecord(identifier, name, ip, weave_ip, subnet) + Info.Printf("Adding %s (%s) -> %s", name, localIP, weaveCIDR) + err = db.AddRecord(identifier, name, ip, weaveIP, subnet) if err != nil { dup, ok := err.(DuplicateError) if !ok { @@ -78,19 +78,19 @@ func ListenHttp(domain string, db Zone, port int) { } case "DELETE": - identifier, weave_ipstr, err := parseUrl(r.URL.Path) - if identifier == "" || weave_ipstr == "" { + identifier, weaveIPstr, err := parseUrl(r.URL.Path) + if identifier == "" || weaveIPstr == "" { reqError("Invalid Request", "Invalid request: %s, %s", r.URL, r.Form) return } - weave_ip := net.ParseIP(weave_ipstr) - if weave_ip == nil { - reqError("Invalid IP in request", "Invalid IP in request: %s", weave_ipstr) + weaveIP := net.ParseIP(weaveIPstr) + if weaveIP == nil { + reqError("Invalid IP in request", "Invalid IP in request: %s", weaveIPstr) return } - Info.Printf("Deleting %s (%s)", identifier, weave_ipstr) - err = db.DeleteRecord(identifier, weave_ip) + Info.Printf("Deleting %s (%s)", identifier, weaveIPstr) + err = db.DeleteRecord(identifier, weaveIP) if err != nil { if _, ok := err.(LookupError); !ok { httpErrorAndLog( diff --git a/server/http_test.go b/server/http_test.go index fe2b1c04..09144c68 100644 --- a/server/http_test.go +++ b/server/http_test.go @@ -19,60 +19,60 @@ func genForm(method string, url string, data url.Values) (resp *http.Response, e func TestHttp(t *testing.T) { var ( - container_id = "deadbeef" - test_domain = "weave.local." - success_test_name = "test1." + test_domain - test_addr1 = "10.0.2.1/24" - docker_ip = "9.8.7.6" + containerID = "deadbeef" + testDomain = "weave.local." + successTestName = "test1." + testDomain + testAddr1 = "10.0.2.1/24" + dockerIP = "9.8.7.6" ) var zone = new(ZoneDb) port := rand.Intn(10000) + 32768 fmt.Println("Http test on port", port) - go ListenHttp(test_domain, zone, port) + go ListenHttp(testDomain, zone, port) time.Sleep(100 * time.Millisecond) // Allow for http server to get going // Ask the http server to add our test address into the database - addr_parts := strings.Split(test_addr1, "/") - addr_url := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, container_id, addr_parts[0]) - resp, err := genForm("PUT", addr_url, - url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) + addrParts := strings.Split(testAddr1, "/") + addrUrl := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, containerID, addrParts[0]) + resp, err := genForm("PUT", addrUrl, + url.Values{"fqdn": {successTestName}, "local_ip": {dockerIP}, "routing_prefix": {addrParts[1]}}) assertNoErr(t, err) assertStatus(t, resp.StatusCode, http.StatusOK, "http response") // Check that the address is now there. - ip, err := zone.MatchLocal(success_test_name) + ip, err := zone.MatchLocal(successTestName) assertNoErr(t, err) - weave_ip, _, _ := net.ParseCIDR(test_addr1) - if !ip.Equal(weave_ip) { - t.Fatal("Unexpected result for", success_test_name, ip) + weaveIP, _, _ := net.ParseCIDR(testAddr1) + if !ip.Equal(weaveIP) { + t.Fatal("Unexpected result for", successTestName, ip) } // Adding exactly the same address should be OK - resp, err = genForm("PUT", addr_url, - url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) + resp, err = genForm("PUT", addrUrl, + url.Values{"fqdn": {successTestName}, "local_ip": {dockerIP}, "routing_prefix": {addrParts[1]}}) assertNoErr(t, err) assertStatus(t, resp.StatusCode, http.StatusOK, "http success response for duplicate add") // Now try adding the same address again with a different ident - should fail - other_url := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, "other", addr_parts[0]) - resp, err = genForm("PUT", other_url, - url.Values{"fqdn": {success_test_name}, "local_ip": {docker_ip}, "routing_prefix": {addr_parts[1]}}) + otherUrl := fmt.Sprintf("http://localhost:%d/name/%s/%s", port, "other", addrParts[0]) + resp, err = genForm("PUT", otherUrl, + url.Values{"fqdn": {successTestName}, "local_ip": {dockerIP}, "routing_prefix": {addrParts[1]}}) assertNoErr(t, err) assertStatus(t, resp.StatusCode, http.StatusConflict, "http response") // Delete the address - resp, err = genForm("DELETE", addr_url, nil) + resp, err = genForm("DELETE", addrUrl, nil) assertNoErr(t, err) assertStatus(t, resp.StatusCode, http.StatusOK, "http response") // Check that the address is not there now. - _, err = zone.MatchLocal(success_test_name) + _, err = zone.MatchLocal(successTestName) assertErrorType(t, err, (*LookupError)(nil), "nonexistent lookup") // Delete the address again, it should accept this - resp, err = genForm("DELETE", addr_url, nil) + resp, err = genForm("DELETE", addrUrl, nil) assertNoErr(t, err) assertStatus(t, resp.StatusCode, http.StatusOK, "http response") diff --git a/server/logging.go b/server/logging.go index 1fad63d3..7dcb551f 100644 --- a/server/logging.go +++ b/server/logging.go @@ -8,7 +8,7 @@ import ( ) const ( - standard_log_flags = log.Ldate | log.Ltime | log.Lshortfile + standardLogFlags = log.Ldate | log.Ltime | log.Lshortfile ) // Inspired by http://www.goinggo.net/2013/11/using-log-package-in-go.html @@ -25,10 +25,10 @@ func InitLogging(debugHandle io.Writer, warningHandle io.Writer, errorHandle io.Writer) { - Debug = log.New(debugHandle, "DEBUG: ", standard_log_flags) - Info = log.New(infoHandle, "INFO: ", standard_log_flags) - Warning = log.New(warningHandle, "WARNING: ", standard_log_flags) - Error = log.New(errorHandle, "ERROR: ", standard_log_flags) + Debug = log.New(debugHandle, "DEBUG: ", standardLogFlags) + Info = log.New(infoHandle, "INFO: ", standardLogFlags) + Warning = log.New(warningHandle, "WARNING: ", standardLogFlags) + Error = log.New(errorHandle, "ERROR: ", standardLogFlags) } func init() { diff --git a/server/mdns_client.go b/server/mdns_client.go index f5d5d672..a7820ab5 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -141,12 +141,12 @@ func (c *MDNSClient) checkInFlightQueries() time.Duration { after := MaxDuration for name, query := range c.inflight { // Invariant on responseInfos: they are in non-descending order of timeout. - num_closed := 0 + numClosed := 0 for _, item := range query.responseInfos { duration := item.timeout.Sub(now) if duration <= 0 { // timed out close(item.ch) - num_closed++ + numClosed++ } else { if duration < after { after = duration @@ -155,7 +155,7 @@ func (c *MDNSClient) checkInFlightQueries() time.Duration { } } // Remove timed-out items from the slice - query.responseInfos = query.responseInfos[num_closed:] + query.responseInfos = query.responseInfos[numClosed:] if len(query.responseInfos) == 0 { delete(c.inflight, name) } diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index 6145b765..7818a4be 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -9,9 +9,9 @@ import ( ) var ( - success_test_name = "test1.weave." - fail_test_name = "test2.weave." - test_addr = net.ParseIP("9.8.7.6") + successTestName = "test1.weave." + failTestName = "test2.weave." + testAddr = net.ParseIP("9.8.7.6") ) func minimalServer(w dns.ResponseWriter, req *dns.Msg) { @@ -22,7 +22,7 @@ func minimalServer(w dns.ResponseWriter, req *dns.Msg) { if len(req.Question) != 1 { return // We only handle single-question messages } - if req.Question[0].Name != success_test_name { + if req.Question[0].Name != successTestName { return // This is not the DNS record you are looking for } @@ -31,7 +31,7 @@ func minimalServer(w dns.ResponseWriter, req *dns.Msg) { hdr := dns.RR_Header{Name: m.Question[0].Name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600} - a := &dns.A{hdr, test_addr} + a := &dns.A{hdr, testAddr} m.Answer = append(m.Answer, a) buf, err := m.Pack() @@ -78,24 +78,24 @@ func setup(t *testing.T) (*MDNSClient, *dns.Server, error) { } type testContext struct { - received_addr net.IP - received_count int - channel chan *ResponseA + receivedAddr net.IP + receivedCount int + channel chan *ResponseA } func newTestContext() *testContext { return &testContext{channel: make(chan *ResponseA, 4)} } -func (c *testContext) checkResponse(t *testing.T, channel_ok bool, resp *ResponseA) { - if !channel_ok { +func (c *testContext) checkResponse(t *testing.T, channelOk bool, resp *ResponseA) { + if !channelOk { c.channel = nil return } assertNoErr(t, resp.Err) log.Printf("Got address response %s addr %s", resp.Name, resp.Addr) - c.received_addr = resp.Addr - c.received_count++ + c.receivedAddr = resp.Addr + c.receivedCount++ } func TestSimpleQuery(t *testing.T) { @@ -107,13 +107,13 @@ func TestSimpleQuery(t *testing.T) { context := newTestContext() // First, a test we expect to succeed - mdnsClient.SendQuery(success_test_name, dns.TypeA, context.channel) + mdnsClient.SendQuery(successTestName, dns.TypeA, context.channel) for resp := range context.channel { context.checkResponse(t, true, resp) } - if !context.received_addr.Equal(test_addr) || context.received_count != 1 { - t.Log("Unexpected result for", success_test_name, context.received_addr, context.received_count) + if !context.receivedAddr.Equal(testAddr) || context.receivedCount != 1 { + t.Log("Unexpected result for", successTestName, context.receivedAddr, context.receivedCount) t.Fail() } @@ -124,8 +124,8 @@ func TestSimpleQuery(t *testing.T) { context.checkResponse(t, true, resp) } - if context.received_count > 0 { - t.Log("Unexpected result for test2.weave", context.received_addr) + if context.receivedCount > 0 { + t.Log("Unexpected result for test2.weave", context.receivedAddr) t.Fail() } } @@ -139,8 +139,8 @@ func TestParallelQuery(t *testing.T) { context1 := newTestContext() context2 := newTestContext() - go mdnsClient.SendQuery(success_test_name, dns.TypeA, context1.channel) - go mdnsClient.SendQuery(success_test_name, dns.TypeA, context2.channel) + go mdnsClient.SendQuery(successTestName, dns.TypeA, context1.channel) + go mdnsClient.SendQuery(successTestName, dns.TypeA, context2.channel) timeout := time.After(2 * time.Second) outerloop: for context1.channel != nil || context2.channel != nil { @@ -154,8 +154,8 @@ outerloop: } } - if !context1.received_addr.Equal(test_addr) || !context2.received_addr.Equal(test_addr) || context1.received_count != 1 || context2.received_count != 1 { - t.Log("Unexpected result for", success_test_name, context1.received_addr, context2.received_addr, context1.received_count, context2.received_count) + if !context1.receivedAddr.Equal(testAddr) || !context2.receivedAddr.Equal(testAddr) || context1.receivedCount != 1 || context2.receivedCount != 1 { + t.Log("Unexpected result for", successTestName, context1.receivedAddr, context2.receivedAddr, context1.receivedCount, context2.receivedCount) t.Fail() } } diff --git a/server/mdns_server_test.go b/server/mdns_server_test.go index 3560a460..f77ed5ab 100644 --- a/server/mdns_server_test.go +++ b/server/mdns_server_test.go @@ -9,8 +9,8 @@ import ( ) var ( - container_id = "deadbeef" - test_addr1 = "10.0.2.1/24" + containerID = "deadbeef" + testAddr1 = "10.0.2.1/24" ) func sendQuery(name string, querytype uint16) error { @@ -32,17 +32,17 @@ func sendQuery(name string, querytype uint16) error { func TestServerSimpleQuery(t *testing.T) { log.Println("TestServerSimpleQuery starting") var zone = new(ZoneDb) - docker_ip := net.ParseIP("9.8.7.6") - weave_ip, subnet, _ := net.ParseCIDR(test_addr1) - zone.AddRecord(container_id, "test.weave.", docker_ip, weave_ip, subnet) + dockerIP := net.ParseIP("9.8.7.6") + weaveIP, subnet, _ := net.ParseCIDR(testAddr1) + zone.AddRecord(containerID, "test.weave.", dockerIP, weaveIP, subnet) mdnsServer, err := NewMDNSServer(zone) assertNoErr(t, err) err = mdnsServer.Start(nil) assertNoErr(t, err) - var received_addr net.IP - received_count := 0 + var receivedAddr net.IP + receivedCount := 0 // Implement a minimal listener for responses multicast, err := LinkLocalMulticastListener(nil) @@ -54,8 +54,8 @@ func TestServerSimpleQuery(t *testing.T) { for _, answer := range r.Answer { switch rr := answer.(type) { case *dns.A: - received_addr = rr.A - received_count++ + receivedAddr = rr.A + receivedCount++ } } } @@ -71,21 +71,21 @@ func TestServerSimpleQuery(t *testing.T) { time.Sleep(time.Second) - if received_count != 1 { - t.Log("Unexpected result count for test.weave", received_count) + if receivedCount != 1 { + t.Log("Unexpected result count for test.weave", receivedCount) t.Fail() } - if !received_addr.Equal(weave_ip) { - t.Log("Unexpected result for test.weave", received_addr) + if !receivedAddr.Equal(weaveIP) { + t.Log("Unexpected result for test.weave", receivedAddr) t.Fail() } - received_count = 0 + receivedCount = 0 sendQuery("testfail.weave.", dns.TypeA) - if received_count != 0 { - t.Log("Unexpected result count for testfail.weave", received_count) + if receivedCount != 0 { + t.Log("Unexpected result count for testfail.weave", receivedCount) t.Fail() } } diff --git a/server/server.go b/server/server.go index e4a338c2..896f4df4 100644 --- a/server/server.go +++ b/server/server.go @@ -48,13 +48,13 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { Debug.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) replies = append(replies, resp.Addr) } - var response_msg *dns.Msg + var responseMsg *dns.Msg if len(replies) > 0 { - response_msg = makeDNSReply(r, q.Name, dns.TypeA, replies) + responseMsg = makeDNSReply(r, q.Name, dns.TypeA, replies) } else { - response_msg = makeDNSFailResponse(r) + responseMsg = makeDNSFailResponse(r) } - w.WriteMsg(response_msg) + w.WriteMsg(responseMsg) }() mdnsClient.SendQuery(q.Name, dns.TypeA, channel) } @@ -71,14 +71,14 @@ func notUsHandler() dns.HandlerFunc { q := r.Question[0] Debug.Printf("Non-local query: %s", q) addrs, err := net.LookupIP(q.Name) - var response_msg *dns.Msg + var responseMsg *dns.Msg if err == nil { - response_msg = makeDNSReply(r, q.Name, q.Qtype, addrs) + responseMsg = makeDNSReply(r, q.Name, q.Qtype, addrs) } else { - response_msg = makeDNSFailResponse(r) + responseMsg = makeDNSFailResponse(r) Debug.Print("Failed fallback lookup", err) } - w.WriteMsg(response_msg) + w.WriteMsg(responseMsg) } } diff --git a/server/testing_utils.go b/server/testing_utils.go index 53dab804..0b7c37cd 100644 --- a/server/testing_utils.go +++ b/server/testing_utils.go @@ -18,15 +18,15 @@ func assertStatus(t *testing.T, got int, wanted int, desc string) { } func assertErrorInterface(t *testing.T, got interface{}, wanted interface{}, desc string) { - got_t, wanted_t := reflect.TypeOf(got), reflect.TypeOf(wanted).Elem() - if !got_t.Implements(wanted_t) { - t.Fatalf("Expected %s but got %s (%s)", wanted_t.String(), got_t.String(), desc) + gotT, wantedT := reflect.TypeOf(got), reflect.TypeOf(wanted).Elem() + if !gotT.Implements(wantedT) { + t.Fatalf("Expected %s but got %s (%s)", wantedT.String(), gotT.String(), desc) } } func assertErrorType(t *testing.T, got interface{}, wanted interface{}, desc string) { - got_t, wanted_t := reflect.TypeOf(got), reflect.TypeOf(wanted).Elem() - if got_t != wanted_t { - t.Fatalf("Expected %s but got %s (%s)", wanted_t.String(), got_t.String(), desc) + gotT, wantedT := reflect.TypeOf(got), reflect.TypeOf(wanted).Elem() + if gotT != wantedT { + t.Fatalf("Expected %s but got %s (%s)", wantedT.String(), gotT.String(), desc) } } diff --git a/server/zone.go b/server/zone.go index 61b34476..bf7ff5a8 100644 --- a/server/zone.go +++ b/server/zone.go @@ -7,17 +7,18 @@ import ( ) type Zone interface { - AddRecord(string, string, net.IP, net.IP, *net.IPNet) error - DeleteRecord(ident string, weave_ip net.IP) error + AddRecord(ident string, name string, localIP net.IP, + weaveIP net.IP, weaveSubnet *net.IPNet) error + DeleteRecord(ident string, weaveIp net.IP) error DeleteRecordsFor(ident string) error - MatchLocal(string) (net.IP, error) + MatchLocal(name string) (net.IP, error) } type Record struct { Ident string Name string - Ip net.IP - WeaveIp net.IP + LocalIP net.IP + WeaveIP net.IP Subnet *net.IPNet } @@ -36,12 +37,12 @@ func (ops LookupError) Error() string { type DuplicateError struct { Name string - WeaveIp net.IP + WeaveIP net.IP Ident string } func (err DuplicateError) Error() string { - return "Duplicate " + err.Name + "," + err.WeaveIp.String() + " in container " + err.Ident + return "Duplicate " + err.Name + "," + err.WeaveIP.String() + " for identity " + err.Ident } func (zone *ZoneDb) indexOf(match func(Record) bool) int { @@ -58,29 +59,29 @@ func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { defer zone.mx.RUnlock() for _, r := range zone.recs { if r.Name == name { - return r.WeaveIp, nil + return r.WeaveIP, nil } } return nil, LookupError(name) } -func (zone *ZoneDb) AddRecord(identifier string, name string, ip net.IP, weave_ip net.IP, weave_subnet *net.IPNet) error { +func (zone *ZoneDb) AddRecord(ident string, name string, localIP net.IP, weaveIP net.IP, weaveSubnet *net.IPNet) error { zone.mx.Lock() defer zone.mx.Unlock() fqdn := dns.Fqdn(name) if index := zone.indexOf( - func(r Record) bool { return r.Name == fqdn && r.WeaveIp.Equal(weave_ip) }); index != -1 { - return DuplicateError{fqdn, weave_ip, zone.recs[index].Ident} + func(r Record) bool { return r.Name == fqdn && r.WeaveIP.Equal(weaveIP) }); index != -1 { + return DuplicateError{fqdn, weaveIP, zone.recs[index].Ident} } - zone.recs = append(zone.recs, Record{identifier, fqdn, ip, weave_ip, weave_subnet}) + zone.recs = append(zone.recs, Record{ident, fqdn, localIP, weaveIP, weaveSubnet}) return nil } -func (zone *ZoneDb) DeleteRecord(ident string, weave_ip net.IP) error { +func (zone *ZoneDb) DeleteRecord(ident string, weaveIP net.IP) error { zone.mx.Lock() defer zone.mx.Unlock() if index := zone.indexOf( - func(r Record) bool { return r.Ident == ident && r.WeaveIp.Equal(weave_ip) }); index == -1 { + func(r Record) bool { return r.Ident == ident && r.WeaveIP.Equal(weaveIP) }); index == -1 { return LookupError(ident) } else { zone.recs = append(zone.recs[:index], zone.recs[index+1:]...) diff --git a/server/zone_test.go b/server/zone_test.go index 6fe44094..60d06cbd 100644 --- a/server/zone_test.go +++ b/server/zone_test.go @@ -7,57 +7,57 @@ import ( func TestZone(t *testing.T) { var ( - container_id = "deadbeef" - success_test_name = "test1.weave." - test_addr1 = "10.0.2.1/24" - docker_ip = "9.8.7.6" + containerID = "deadbeef" + successTestName = "test1.weave." + testAddr1 = "10.0.2.1/24" + dockerIP = "9.8.7.6" ) var zone = new(ZoneDb) - ip := net.ParseIP(docker_ip) - weave_ip, subnet, _ := net.ParseCIDR(test_addr1) - err := zone.AddRecord(container_id, success_test_name, ip, weave_ip, subnet) + ip := net.ParseIP(dockerIP) + weaveIP, subnet, _ := net.ParseCIDR(testAddr1) + err := zone.AddRecord(containerID, successTestName, ip, weaveIP, subnet) assertNoErr(t, err) // Check that the address is now there. - found_ip, err := zone.MatchLocal(success_test_name) + foundIP, err := zone.MatchLocal(successTestName) assertNoErr(t, err) - if !found_ip.Equal(weave_ip) { - t.Fatal("Unexpected result for", success_test_name, ip) + if !foundIP.Equal(weaveIP) { + t.Fatal("Unexpected result for", successTestName, ip) } // Now try to add the same address again - err = zone.AddRecord(container_id, success_test_name, ip, weave_ip, subnet) + err = zone.AddRecord(containerID, successTestName, ip, weaveIP, subnet) assertErrorType(t, err, (*DuplicateError)(nil), "duplicate add") // Now delete the record - err = zone.DeleteRecord(container_id, weave_ip) + err = zone.DeleteRecord(containerID, weaveIP) assertNoErr(t, err) // Check that the address is not there now. - _, err = zone.MatchLocal(success_test_name) + _, err = zone.MatchLocal(successTestName) assertErrorType(t, err, (*LookupError)(nil), "after deleting record") // Delete a record that isn't there - err = zone.DeleteRecord(container_id, net.ParseIP("0.0.0.0")) + err = zone.DeleteRecord(containerID, net.ParseIP("0.0.0.0")) assertErrorType(t, err, (*LookupError)(nil), "when deleting record that doesn't exist") } func TestDeleteFor(t *testing.T) { var ( - id = "foobar" - name = "foo.weave." - addr1 = "10.1.2.3/24" - addr2 = "10.2.7.8/24" - docker_ip = "172.16.0.4" + id = "foobar" + name = "foo.weave." + addr1 = "10.1.2.3/24" + addr2 = "10.2.7.8/24" + dockerIP = "172.16.0.4" ) zone := new(ZoneDb) - ip := net.ParseIP(docker_ip) + ip := net.ParseIP(dockerIP) for _, addr := range []string{addr1, addr2} { - weave_ip, subnet, _ := net.ParseCIDR(addr) - err := zone.AddRecord(id, name, ip, weave_ip, subnet) + weaveIP, subnet, _ := net.ParseCIDR(addr) + err := zone.AddRecord(id, name, ip, weaveIP, subnet) assertNoErr(t, err) } From ad44a8816f8c85441fa9fc8a4aaa5892199c551a Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Thu, 23 Oct 2014 16:43:41 +0100 Subject: [PATCH 124/145] Remove subnets from API, since we don't use them --- server/http.go | 34 ++++++++++++++++------------------ server/mdns_server_test.go | 4 ++-- server/zone.go | 8 +++----- server/zone_test.go | 10 +++++----- 4 files changed, 26 insertions(+), 30 deletions(-) diff --git a/server/http.go b/server/http.go index a98fd5d5..2e67899a 100644 --- a/server/http.go +++ b/server/http.go @@ -35,32 +35,30 @@ func ListenHttp(domain string, db Zone, port int) { switch r.Method { case "PUT": - identifier, weaveIPstr, err := parseUrl(r.URL.Path) + ident, weaveIPstr, err := parseUrl(r.URL.Path) name := r.FormValue("fqdn") - prefix := r.FormValue("routing_prefix") - localIP := r.FormValue("local_ip") + localIPstr := r.FormValue("local_ip") - if identifier == "" || weaveIPstr == "" || name == "" || prefix == "" || localIP == "" { + if ident == "" || weaveIPstr == "" || name == "" || localIPstr == "" { reqError("Invalid request", "Invalid request: %s, %s", r.URL, r.Form) return } - ip := net.ParseIP(localIP) - if ip == nil { - reqError("Invalid IP in request", "Invalid IP in request: %s", localIP) + localIP := net.ParseIP(localIPstr) + if localIP == nil { + reqError("Invalid IP in request", "Invalid IP in request: %s", localIPstr) return } - weaveCIDR := weaveIPstr + "/" + prefix - weaveIP, subnet, err := net.ParseCIDR(weaveCIDR) - if err != nil { - reqError("Invalid CIDR", "Invalid CIDR in request: %s", weaveCIDR) + weaveIP := net.ParseIP(weaveIPstr) + if weaveIP == nil { + reqError("Invalid weave IP", "Invalid weave IP in request: %s", weaveIPstr) return } if dns.IsSubDomain(domain, name) { - Info.Printf("Adding %s (%s) -> %s", name, localIP, weaveCIDR) - err = db.AddRecord(identifier, name, ip, weaveIP, subnet) + Info.Printf("Adding %s (%s) -> %s", name, localIPstr, weaveIPstr) + err = db.AddRecord(ident, name, localIP, weaveIP) if err != nil { dup, ok := err.(DuplicateError) if !ok { @@ -68,7 +66,7 @@ func ListenHttp(domain string, db Zone, port int) { Error, w, "Internal error", http.StatusInternalServerError, "Unexpected error from DB: %s", err) return - } else if dup.Ident != identifier { + } else if dup.Ident != ident { http.Error(w, err.Error(), http.StatusConflict) return } // else we are golden @@ -78,8 +76,8 @@ func ListenHttp(domain string, db Zone, port int) { } case "DELETE": - identifier, weaveIPstr, err := parseUrl(r.URL.Path) - if identifier == "" || weaveIPstr == "" { + ident, weaveIPstr, err := parseUrl(r.URL.Path) + if ident == "" || weaveIPstr == "" { reqError("Invalid Request", "Invalid request: %s, %s", r.URL, r.Form) return } @@ -89,8 +87,8 @@ func ListenHttp(domain string, db Zone, port int) { reqError("Invalid IP in request", "Invalid IP in request: %s", weaveIPstr) return } - Info.Printf("Deleting %s (%s)", identifier, weaveIPstr) - err = db.DeleteRecord(identifier, weaveIP) + Info.Printf("Deleting %s (%s)", ident, weaveIPstr) + err = db.DeleteRecord(ident, weaveIP) if err != nil { if _, ok := err.(LookupError); !ok { httpErrorAndLog( diff --git a/server/mdns_server_test.go b/server/mdns_server_test.go index f77ed5ab..1fc8afb6 100644 --- a/server/mdns_server_test.go +++ b/server/mdns_server_test.go @@ -33,8 +33,8 @@ func TestServerSimpleQuery(t *testing.T) { log.Println("TestServerSimpleQuery starting") var zone = new(ZoneDb) dockerIP := net.ParseIP("9.8.7.6") - weaveIP, subnet, _ := net.ParseCIDR(testAddr1) - zone.AddRecord(containerID, "test.weave.", dockerIP, weaveIP, subnet) + weaveIP, _, _ := net.ParseCIDR(testAddr1) + zone.AddRecord(containerID, "test.weave.", dockerIP, weaveIP) mdnsServer, err := NewMDNSServer(zone) assertNoErr(t, err) diff --git a/server/zone.go b/server/zone.go index bf7ff5a8..d577917b 100644 --- a/server/zone.go +++ b/server/zone.go @@ -7,8 +7,7 @@ import ( ) type Zone interface { - AddRecord(ident string, name string, localIP net.IP, - weaveIP net.IP, weaveSubnet *net.IPNet) error + AddRecord(ident string, name string, localIP net.IP, weaveIP net.IP) error DeleteRecord(ident string, weaveIp net.IP) error DeleteRecordsFor(ident string) error MatchLocal(name string) (net.IP, error) @@ -19,7 +18,6 @@ type Record struct { Name string LocalIP net.IP WeaveIP net.IP - Subnet *net.IPNet } // Very simple data structure for now, with linear searching. @@ -65,7 +63,7 @@ func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { return nil, LookupError(name) } -func (zone *ZoneDb) AddRecord(ident string, name string, localIP net.IP, weaveIP net.IP, weaveSubnet *net.IPNet) error { +func (zone *ZoneDb) AddRecord(ident string, name string, localIP net.IP, weaveIP net.IP) error { zone.mx.Lock() defer zone.mx.Unlock() fqdn := dns.Fqdn(name) @@ -73,7 +71,7 @@ func (zone *ZoneDb) AddRecord(ident string, name string, localIP net.IP, weaveIP func(r Record) bool { return r.Name == fqdn && r.WeaveIP.Equal(weaveIP) }); index != -1 { return DuplicateError{fqdn, weaveIP, zone.recs[index].Ident} } - zone.recs = append(zone.recs, Record{ident, fqdn, localIP, weaveIP, weaveSubnet}) + zone.recs = append(zone.recs, Record{ident, fqdn, localIP, weaveIP}) return nil } diff --git a/server/zone_test.go b/server/zone_test.go index 60d06cbd..c43b8b79 100644 --- a/server/zone_test.go +++ b/server/zone_test.go @@ -16,8 +16,8 @@ func TestZone(t *testing.T) { var zone = new(ZoneDb) ip := net.ParseIP(dockerIP) - weaveIP, subnet, _ := net.ParseCIDR(testAddr1) - err := zone.AddRecord(containerID, successTestName, ip, weaveIP, subnet) + weaveIP, _, _ := net.ParseCIDR(testAddr1) + err := zone.AddRecord(containerID, successTestName, ip, weaveIP) assertNoErr(t, err) // Check that the address is now there. @@ -29,7 +29,7 @@ func TestZone(t *testing.T) { } // Now try to add the same address again - err = zone.AddRecord(containerID, successTestName, ip, weaveIP, subnet) + err = zone.AddRecord(containerID, successTestName, ip, weaveIP) assertErrorType(t, err, (*DuplicateError)(nil), "duplicate add") // Now delete the record @@ -56,8 +56,8 @@ func TestDeleteFor(t *testing.T) { zone := new(ZoneDb) ip := net.ParseIP(dockerIP) for _, addr := range []string{addr1, addr2} { - weaveIP, subnet, _ := net.ParseCIDR(addr) - err := zone.AddRecord(id, name, ip, weaveIP, subnet) + weaveIP, _, _ := net.ParseCIDR(addr) + err := zone.AddRecord(id, name, ip, weaveIP) assertNoErr(t, err) } From 5f3080b9db5920f8707aadf051501303cfd47f00 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 24 Oct 2014 12:37:28 +0100 Subject: [PATCH 125/145] Don't crash when printing name of nil interface --- server/server.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/server.go b/server/server.go index 896f4df4..c0046352 100644 --- a/server/server.go +++ b/server/server.go @@ -88,7 +88,11 @@ func StartServer(zone Zone, iface *net.Interface, dnsPort int, httpPort int, wai mdnsClient, err := NewMDNSClient() checkFatal(err) - Info.Printf("Using mDNS on %s", iface.Name) + ifaceName := "default interface" + if iface != nil { + ifaceName = iface.Name + } + Info.Printf("Using mDNS on %s", ifaceName) err = mdnsClient.Start(iface) checkFatal(err) From d6f313eac2d40ad67c3e080044daadfbe4919369 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 24 Oct 2014 12:46:02 +0100 Subject: [PATCH 126/145] Improve debug messages --- server/server.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/server.go b/server/server.go index c0046352..7603d3f1 100644 --- a/server/server.go +++ b/server/server.go @@ -33,7 +33,7 @@ func makeDNSFailResponse(r *dns.Msg) *dns.Msg { func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] - Debug.Printf("Local query: %s", q) + Debug.Printf("Local query: %+v", q) ip, err := zone.MatchLocal(q.Name) if err == nil { m := makeDNSReply(r, q.Name, dns.TypeA, []net.IP{ip}) @@ -69,14 +69,14 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { func notUsHandler() dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] - Debug.Printf("Non-local query: %s", q) + Debug.Printf("Non-local query: %+v", q) addrs, err := net.LookupIP(q.Name) var responseMsg *dns.Msg if err == nil { responseMsg = makeDNSReply(r, q.Name, q.Qtype, addrs) } else { responseMsg = makeDNSFailResponse(r) - Debug.Print("Failed fallback lookup", err) + Debug.Print("Failed fallback lookup ", err) } w.WriteMsg(responseMsg) } @@ -107,9 +107,9 @@ func StartServer(zone Zone, iface *net.Interface, dnsPort int, httpPort int, wai checkFatal(err) address := fmt.Sprintf(":%d", dnsPort) + Info.Printf("Listening for DNS on %s", address) err = dns.ListenAndServe(address, "udp", LocalServeMux) checkFatal(err) - Info.Printf("Listening for DNS on %s", address) return nil } From fa0331cb6df4ac6b1e9f10ebb1bee1575d41d626 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 24 Oct 2014 13:03:26 +0100 Subject: [PATCH 127/145] Ensure we only attempt to handle TypeA queries --- server/server.go | 63 ++++++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/server/server.go b/server/server.go index 7603d3f1..369277b6 100644 --- a/server/server.go +++ b/server/server.go @@ -34,29 +34,35 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] Debug.Printf("Local query: %+v", q) - ip, err := zone.MatchLocal(q.Name) - if err == nil { - m := makeDNSReply(r, q.Name, dns.TypeA, []net.IP{ip}) - w.WriteMsg(m) + if q.Qtype == dns.TypeA { + ip, err := zone.MatchLocal(q.Name) + if err == nil { + m := makeDNSReply(r, q.Name, dns.TypeA, []net.IP{ip}) + w.WriteMsg(m) + } else { + Debug.Printf("Failed lookup for %s; sending mDNS query", q.Name) + // We don't know the answer; see if someone else does + channel := make(chan *ResponseA) + replies := make([]net.IP, 0) + go func() { + for resp := range channel { + Debug.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) + replies = append(replies, resp.Addr) + } + var responseMsg *dns.Msg + if len(replies) > 0 { + responseMsg = makeDNSReply(r, q.Name, dns.TypeA, replies) + } else { + responseMsg = makeDNSFailResponse(r) + } + w.WriteMsg(responseMsg) + }() + mdnsClient.SendQuery(q.Name, dns.TypeA, channel) + } } else { - Debug.Printf("Failed lookup for %s; sending mDNS query", q.Name) - // We don't know the answer; see if someone else does - channel := make(chan *ResponseA) - replies := make([]net.IP, 0) - go func() { - for resp := range channel { - Debug.Printf("Got address response %s to query %s addr %s", resp.Name, q.Name, resp.Addr) - replies = append(replies, resp.Addr) - } - var responseMsg *dns.Msg - if len(replies) > 0 { - responseMsg = makeDNSReply(r, q.Name, dns.TypeA, replies) - } else { - responseMsg = makeDNSFailResponse(r) - } - w.WriteMsg(responseMsg) - }() - mdnsClient.SendQuery(q.Name, dns.TypeA, channel) + Warning.Printf("Local query not handled: %+v", q) + m := makeDNSFailResponse(r) + w.WriteMsg(m) } return } @@ -70,13 +76,18 @@ func notUsHandler() dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] Debug.Printf("Non-local query: %+v", q) - addrs, err := net.LookupIP(q.Name) var responseMsg *dns.Msg - if err == nil { - responseMsg = makeDNSReply(r, q.Name, q.Qtype, addrs) + if q.Qtype == dns.TypeA { + addrs, err := net.LookupIP(q.Name) + if err == nil { + responseMsg = makeDNSReply(r, q.Name, q.Qtype, addrs) + } else { + responseMsg = makeDNSFailResponse(r) + Debug.Print("Failed fallback lookup ", err) + } } else { + Warning.Printf("Non-local query not handled: %+v", q) responseMsg = makeDNSFailResponse(r) - Debug.Print("Failed fallback lookup ", err) } w.WriteMsg(responseMsg) } From 5ee60a000311b5c74506400a44c0a5bf4482962b Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 24 Oct 2014 13:37:32 +0100 Subject: [PATCH 128/145] Extend tests slightly --- server/zone_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/server/zone_test.go b/server/zone_test.go index c43b8b79..077cdc20 100644 --- a/server/zone_test.go +++ b/server/zone_test.go @@ -20,12 +20,18 @@ func TestZone(t *testing.T) { err := zone.AddRecord(containerID, successTestName, ip, weaveIP) assertNoErr(t, err) + // Add a few more records to make the job harder + err = zone.AddRecord("abcdef0123", "adummy.weave.", net.ParseIP("1.1.1.1"), net.ParseIP("10.0.0.1")) + assertNoErr(t, err) + err = zone.AddRecord("0123abcdef", "zdummy.weave.", net.ParseIP("2.2.2.2"), net.ParseIP("10.0.0.2")) + assertNoErr(t, err) + // Check that the address is now there. foundIP, err := zone.MatchLocal(successTestName) assertNoErr(t, err) if !foundIP.Equal(weaveIP) { - t.Fatal("Unexpected result for", successTestName, ip) + t.Fatal("Unexpected result for", successTestName, foundIP) } // Now try to add the same address again From d868d2726325b0df7e961fd0196bd8876c6df5da Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 24 Oct 2014 14:47:49 +0100 Subject: [PATCH 129/145] Look up by IP --- server/zone.go | 12 ++++++++++++ server/zone_test.go | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/server/zone.go b/server/zone.go index d577917b..7e4c8a42 100644 --- a/server/zone.go +++ b/server/zone.go @@ -11,6 +11,7 @@ type Zone interface { DeleteRecord(ident string, weaveIp net.IP) error DeleteRecordsFor(ident string) error MatchLocal(name string) (net.IP, error) + MatchLocalIP(ip net.IP) (string, error) } type Record struct { @@ -63,6 +64,17 @@ func (zone *ZoneDb) MatchLocal(name string) (net.IP, error) { return nil, LookupError(name) } +func (zone *ZoneDb) MatchLocalIP(ip net.IP) (string, error) { + zone.mx.RLock() + defer zone.mx.RUnlock() + for _, r := range zone.recs { + if r.WeaveIP.Equal(ip) { + return r.Name, nil + } + } + return "", LookupError(ip.String()) +} + func (zone *ZoneDb) AddRecord(ident string, name string, localIP net.IP, weaveIP net.IP) error { zone.mx.Lock() defer zone.mx.Unlock() diff --git a/server/zone_test.go b/server/zone_test.go index 077cdc20..bbe3a9e8 100644 --- a/server/zone_test.go +++ b/server/zone_test.go @@ -34,6 +34,14 @@ func TestZone(t *testing.T) { t.Fatal("Unexpected result for", successTestName, foundIP) } + // See if we can find the address by IP. + foundName, err := zone.MatchLocalIP(weaveIP) + assertNoErr(t, err) + + if foundName != successTestName { + t.Fatal("Unexpected result for", weaveIP, foundName) + } + // Now try to add the same address again err = zone.AddRecord(containerID, successTestName, ip, weaveIP) assertErrorType(t, err, (*DuplicateError)(nil), "duplicate add") From 510204587bb3e16bad9fe66b2b5a83fa1dc10ac4 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Fri, 24 Oct 2014 14:48:13 +0100 Subject: [PATCH 130/145] Add simple rDNS lookup --- server/server.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/server/server.go b/server/server.go index 369277b6..d6a89379 100644 --- a/server/server.go +++ b/server/server.go @@ -4,10 +4,12 @@ import ( "fmt" "github.com/miekg/dns" "net" + "strings" ) const ( LOCAL_DOMAIN = "weave.local." + RDNS_DOMAIN = "in-addr.arpa." ) func checkFatal(e error) { @@ -68,6 +70,34 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { } } +func rdnsHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { + return func(w dns.ResponseWriter, r *dns.Msg) { + q := r.Question[0] + Debug.Printf("Local rdns query: %+v", q) + if q.Qtype == dns.TypePTR && strings.HasSuffix(q.Name, ".in-addr.arpa.") { + if ip := net.ParseIP(q.Name[:len(q.Name)-14]); ip != nil { + ip4 := ip.To4() + revIP := []byte{ip4[3], ip4[2], ip4[1], ip4[0]} + Debug.Printf("Looking for address: %+v", revIP) + name, err := zone.MatchLocalIP(revIP) + if err == nil { + Debug.Printf("Found name: %s", name) + m := new(dns.Msg) + m.SetReply(r) + m.RecursionAvailable = true + hdr := dns.RR_Header{Name: q.Name, Rrtype: q.Qtype, Class: dns.ClassINET, Ttl: 3600} + m.Answer = []dns.RR{&dns.PTR{hdr, name}} + w.WriteMsg(m) + } else { + Debug.Printf("Failed lookup for %s; sending mDNS query", q.Name) + // We don't know the answer; see if someone else does + // TODO + } + } + } + } +} + /* When we receive a request for a name outside of our '.weave' domain, call the underlying lookup mechanism and return the answer(s) it gives. Unfortunately, this means that TTLs from a real DNS server are lost - FIXME. @@ -109,6 +139,7 @@ func StartServer(zone Zone, iface *net.Interface, dnsPort int, httpPort int, wai LocalServeMux := dns.NewServeMux() LocalServeMux.HandleFunc(LOCAL_DOMAIN, queryHandler(zone, mdnsClient)) + LocalServeMux.HandleFunc(RDNS_DOMAIN, rdnsHandler(zone, mdnsClient)) LocalServeMux.HandleFunc(".", notUsHandler()) mdnsServer, err := NewMDNSServer(zone) From cb05e886a81631e5a632e0a1af83bc0cf6dd31b0 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Mon, 27 Oct 2014 14:13:53 +0000 Subject: [PATCH 131/145] Removed duplicate check for rDNS domain There's no need to check whether the queried address is a subdomain of in-addr.arpa, since this handler is registered with the muxer under that domain anyway. (Also, remove a magic number) --- server/server.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server/server.go b/server/server.go index d6a89379..8ffd03f6 100644 --- a/server/server.go +++ b/server/server.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/miekg/dns" "net" - "strings" ) const ( @@ -12,6 +11,9 @@ const ( RDNS_DOMAIN = "in-addr.arpa." ) +// +1 to also exclude a dot +var rdnsDomainLen = len(RDNS_DOMAIN) + 1 + func checkFatal(e error) { if e != nil { Error.Fatal(e) @@ -74,8 +76,8 @@ func rdnsHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { return func(w dns.ResponseWriter, r *dns.Msg) { q := r.Question[0] Debug.Printf("Local rdns query: %+v", q) - if q.Qtype == dns.TypePTR && strings.HasSuffix(q.Name, ".in-addr.arpa.") { - if ip := net.ParseIP(q.Name[:len(q.Name)-14]); ip != nil { + if q.Qtype == dns.TypePTR { + if ip := net.ParseIP(q.Name[:len(q.Name)-rdnsDomainLen]); ip != nil { ip4 := ip.To4() revIP := []byte{ip4[3], ip4[2], ip4[1], ip4[0]} Debug.Printf("Looking for address: %+v", revIP) From 2cc0e8aaa632d14f9eb7ec647785cf92f4753e04 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 28 Oct 2014 10:54:37 +0000 Subject: [PATCH 132/145] Clarify comments --- server/mdns_client.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/mdns_client.go b/server/mdns_client.go index a7820ab5..64920e8d 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -12,8 +12,8 @@ import ( const ( ipv4mdns = "224.0.0.251" // link-local multicast address mdnsPort = 5353 // mDNS assigned port - // We wait this long to hear responses from other mDNS servers on the network. - // TODO: introduce caching so we don't have to wait this long on every call. + // We wait this long to hear a response from other mDNS servers on + // the network. mDNSTimeout = 500 * time.Millisecond MaxDuration = time.Duration(math.MaxInt64) MailboxSize = 16 @@ -40,7 +40,7 @@ type responseInfo struct { // Represents one query that we have sent for one name. // If we, internally, get several requests for the same name while we have // a query in flight, then we don't want to send more queries out. -// Invariant on responseInfos: they are in non-descending order of timeout. +// Invariant on responseInfos: they are in ascending order of timeout. type inflightQuery struct { name string id uint16 // the DNS message ID @@ -140,7 +140,7 @@ func (c *MDNSClient) checkInFlightQueries() time.Duration { now := time.Now() after := MaxDuration for name, query := range c.inflight { - // Invariant on responseInfos: they are in non-descending order of timeout. + // Invariant on responseInfos: they are in ascending order of timeout. numClosed := 0 for _, item := range query.responseInfos { duration := item.timeout.Sub(now) @@ -228,7 +228,7 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) { ch: q.responseCh, timeout: time.Now().Add(mDNSTimeout), } - // Invariant on responseInfos: they are in non-descending order of timeout. + // Invariant on responseInfos: they are in ascending order of timeout. // Since we use a fixed interval from Now(), this must be after all existing timeouts. query.responseInfos = append(query.responseInfos, info) } From 548039db08fc3fa779d02491fa765b7af7c1b705 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 28 Oct 2014 10:59:30 +0000 Subject: [PATCH 133/145] No need to give defaults in docstrings for options The usage message will include them anyway. --- main.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 847b8e72..2db52c04 100644 --- a/main.go +++ b/main.go @@ -21,9 +21,9 @@ func main() { flag.StringVar(&ifaceName, "iface", "", "name of interface to use for multicast") flag.StringVar(&apiPath, "api", "unix:///var/run/docker.sock", "Path to Docker API socket") - flag.IntVar(&wait, "wait", 0, "number of seconds to wait for interface to be created and come up (defaults to 0)") - flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to dns requests (defaults to 53)") - flag.IntVar(&httpPort, "httpport", 6785, "port to listen to HTTP requests (defaults to 6785)") + flag.IntVar(&wait, "wait", 0, "number of seconds to wait for interface to be created and come up") + flag.IntVar(&dnsPort, "dnsport", 53, "port to listen to dns requests") + flag.IntVar(&httpPort, "httpport", 6785, "port to listen to HTTP requests") flag.BoolVar(&watch, "watch", true, "watch the docker socket for container events") flag.BoolVar(&debug, "debug", false, "output debugging info to stderr") flag.Parse() From a5869e20906892c95d518a4149f30981fc938619 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 28 Oct 2014 11:10:59 +0000 Subject: [PATCH 134/145] Use constant for TTL --- server/mdns_server.go | 7 ++++++- server/server.go | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/server/mdns_server.go b/server/mdns_server.go index 8c13de8b..f335ea6f 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -11,6 +11,11 @@ type MDNSServer struct { zone Zone } +const ( + localTTL uint32 = 300 // somewhat arbitrary; we don't expect anyone + // downstream to cache results +) + func NewMDNSServer(zone Zone) (*MDNSServer, error) { // This is a bit of a kludge - per the RFC we should send responses from 5353, but that doesn't seem to work sendconn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) @@ -26,7 +31,7 @@ func makeDNSReply(r *dns.Msg, name string, qtype uint16, addrs []net.IP) *dns.Ms m := new(dns.Msg) m.SetReply(r) m.RecursionAvailable = true - hdr := dns.RR_Header{Name: name, Rrtype: qtype, Class: dns.ClassINET, Ttl: 3600} + hdr := dns.RR_Header{Name: name, Rrtype: qtype, Class: dns.ClassINET, Ttl: localTTL} for _, addr := range addrs { if qtype == dns.TypeA { if ip4 := addr.To4(); ip4 != nil { diff --git a/server/server.go b/server/server.go index 8ffd03f6..fa24f8e3 100644 --- a/server/server.go +++ b/server/server.go @@ -87,7 +87,7 @@ func rdnsHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { m := new(dns.Msg) m.SetReply(r) m.RecursionAvailable = true - hdr := dns.RR_Header{Name: q.Name, Rrtype: q.Qtype, Class: dns.ClassINET, Ttl: 3600} + hdr := dns.RR_Header{Name: q.Name, Rrtype: q.Qtype, Class: dns.ClassINET, Ttl: localTTL} m.Answer = []dns.RR{&dns.PTR{hdr, name}} w.WriteMsg(m) } else { From 5bb6c85565f434ae3903df9ae950953e2832757e Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 28 Oct 2014 11:26:09 +0000 Subject: [PATCH 135/145] Move DNS utils into their own file --- server/dns.go | 30 ++++++++++++++++++++++++++++++ server/mdns_server.go | 24 ------------------------ 2 files changed, 30 insertions(+), 24 deletions(-) create mode 100644 server/dns.go diff --git a/server/dns.go b/server/dns.go new file mode 100644 index 00000000..24444b71 --- /dev/null +++ b/server/dns.go @@ -0,0 +1,30 @@ +package weavedns + +import ( + "github.com/miekg/dns" + "net" +) + +const ( + localTTL uint32 = 300 // somewhat arbitrary; we don't expect anyone + // downstream to cache results +) + +func makeDNSReply(r *dns.Msg, name string, qtype uint16, addrs []net.IP) *dns.Msg { + m := new(dns.Msg) + m.SetReply(r) + m.RecursionAvailable = true + hdr := dns.RR_Header{Name: name, Rrtype: qtype, Class: dns.ClassINET, Ttl: localTTL} + for _, addr := range addrs { + if qtype == dns.TypeA { + if ip4 := addr.To4(); ip4 != nil { + m.Answer = append(m.Answer, &dns.A{hdr, addr}) + } + } else if qtype == dns.TypeAAAA { + if ip4 := addr.To4(); ip4 == nil { + m.Answer = append(m.Answer, &dns.AAAA{hdr, addr}) + } + } + } + return m +} diff --git a/server/mdns_server.go b/server/mdns_server.go index f335ea6f..f4956ee8 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -11,11 +11,6 @@ type MDNSServer struct { zone Zone } -const ( - localTTL uint32 = 300 // somewhat arbitrary; we don't expect anyone - // downstream to cache results -) - func NewMDNSServer(zone Zone) (*MDNSServer, error) { // This is a bit of a kludge - per the RFC we should send responses from 5353, but that doesn't seem to work sendconn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) @@ -27,25 +22,6 @@ func NewMDNSServer(zone Zone) (*MDNSServer, error) { return retval, nil } -func makeDNSReply(r *dns.Msg, name string, qtype uint16, addrs []net.IP) *dns.Msg { - m := new(dns.Msg) - m.SetReply(r) - m.RecursionAvailable = true - hdr := dns.RR_Header{Name: name, Rrtype: qtype, Class: dns.ClassINET, Ttl: localTTL} - for _, addr := range addrs { - if qtype == dns.TypeA { - if ip4 := addr.To4(); ip4 != nil { - m.Answer = append(m.Answer, &dns.A{hdr, addr}) - } - } else if qtype == dns.TypeAAAA { - if ip4 := addr.To4(); ip4 == nil { - m.Answer = append(m.Answer, &dns.AAAA{hdr, addr}) - } - } - } - return m -} - // Return true if testaddr is a UDP address with IP matching my local i/f func (s *MDNSServer) addrIsLocal(testaddr net.Addr) bool { if udpaddr, ok := testaddr.(*net.UDPAddr); ok { From 126c36cffb89babd1b94f241f4ed073b74adac94 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 28 Oct 2014 14:44:33 +0000 Subject: [PATCH 136/145] Extended makeDNSReply to PTR replies too --- server/dns.go | 39 ++++++++++++++++++++++++++++++++------- server/mdns_server.go | 2 +- server/server.go | 12 ++++-------- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/server/dns.go b/server/dns.go index 24444b71..e4f3daa9 100644 --- a/server/dns.go +++ b/server/dns.go @@ -10,21 +10,46 @@ const ( // downstream to cache results ) -func makeDNSReply(r *dns.Msg, name string, qtype uint16, addrs []net.IP) *dns.Msg { +func makeHeader(r *dns.Msg, q *dns.Question) *dns.RR_Header { + return &dns.RR_Header{ + Name: q.Name, Rrtype: q.Qtype, + Class: dns.ClassINET, Ttl: localTTL} +} + +func makeReply(r *dns.Msg, as []dns.RR) *dns.Msg { m := new(dns.Msg) m.SetReply(r) m.RecursionAvailable = true - hdr := dns.RR_Header{Name: name, Rrtype: qtype, Class: dns.ClassINET, Ttl: localTTL} + m.Answer = as + return m +} + +func makeAReply(r *dns.Msg, q *dns.Question, addrs []net.IP) *dns.Msg { + answers := make([]dns.RR, len(addrs)) + header := makeHeader(r, q) + count := 0 for _, addr := range addrs { - if qtype == dns.TypeA { + switch q.Qtype { + case dns.TypeA: if ip4 := addr.To4(); ip4 != nil { - m.Answer = append(m.Answer, &dns.A{hdr, addr}) + answers[count] = &dns.A{*header, addr} + count++ } - } else if qtype == dns.TypeAAAA { + case dns.TypeAAAA: if ip4 := addr.To4(); ip4 == nil { - m.Answer = append(m.Answer, &dns.AAAA{hdr, addr}) + answers[count] = &dns.AAAA{*header, addr} + count++ } } } - return m + return makeReply(r, answers[:count]) +} + +func makePTRReply(r *dns.Msg, q *dns.Question, names []string) *dns.Msg { + answers := make([]dns.RR, len(names)) + header := makeHeader(r, q) + for i, name := range names { + answers[i] = &dns.PTR{*header, name} + } + return makeReply(r, answers) } diff --git a/server/mdns_server.go b/server/mdns_server.go index f4956ee8..45542bec 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -43,7 +43,7 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { q := r.Question[0] ip, err := s.zone.MatchLocal(q.Name) if err == nil { - m := makeDNSReply(r, q.Name, dns.TypeA, []net.IP{ip}) + m := makeAReply(r, &q, []net.IP{ip}) s.SendResponse(m) } else if s.addrIsLocal(w.RemoteAddr()) { // ignore this - it's our own query received via multicast diff --git a/server/server.go b/server/server.go index fa24f8e3..97bed95d 100644 --- a/server/server.go +++ b/server/server.go @@ -41,7 +41,7 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { if q.Qtype == dns.TypeA { ip, err := zone.MatchLocal(q.Name) if err == nil { - m := makeDNSReply(r, q.Name, dns.TypeA, []net.IP{ip}) + m := makeAReply(r, &q, []net.IP{ip}) w.WriteMsg(m) } else { Debug.Printf("Failed lookup for %s; sending mDNS query", q.Name) @@ -55,7 +55,7 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { } var responseMsg *dns.Msg if len(replies) > 0 { - responseMsg = makeDNSReply(r, q.Name, dns.TypeA, replies) + responseMsg = makeAReply(r, &q, replies) } else { responseMsg = makeDNSFailResponse(r) } @@ -84,11 +84,7 @@ func rdnsHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { name, err := zone.MatchLocalIP(revIP) if err == nil { Debug.Printf("Found name: %s", name) - m := new(dns.Msg) - m.SetReply(r) - m.RecursionAvailable = true - hdr := dns.RR_Header{Name: q.Name, Rrtype: q.Qtype, Class: dns.ClassINET, Ttl: localTTL} - m.Answer = []dns.RR{&dns.PTR{hdr, name}} + m := makePTRReply(r, &q, []string{name}) w.WriteMsg(m) } else { Debug.Printf("Failed lookup for %s; sending mDNS query", q.Name) @@ -112,7 +108,7 @@ func notUsHandler() dns.HandlerFunc { if q.Qtype == dns.TypeA { addrs, err := net.LookupIP(q.Name) if err == nil { - responseMsg = makeDNSReply(r, q.Name, q.Qtype, addrs) + responseMsg = makeAReply(r, &q, addrs) } else { responseMsg = makeDNSFailResponse(r) Debug.Print("Failed fallback lookup ", err) From 96ce10a4d5fef926485a5a3e9b716201b13caac1 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 28 Oct 2014 15:38:53 +0000 Subject: [PATCH 137/145] Use compact if-with-assign statements where scoping allows --- server/http.go | 13 ++++--------- server/if_utils.go | 6 ++---- server/mdns_client.go | 18 ++++++++---------- server/mdns_server.go | 8 +++----- server/server.go | 9 +++------ 5 files changed, 20 insertions(+), 34 deletions(-) diff --git a/server/http.go b/server/http.go index 2e67899a..cecc1d91 100644 --- a/server/http.go +++ b/server/http.go @@ -38,7 +38,6 @@ func ListenHttp(domain string, db Zone, port int) { ident, weaveIPstr, err := parseUrl(r.URL.Path) name := r.FormValue("fqdn") localIPstr := r.FormValue("local_ip") - if ident == "" || weaveIPstr == "" || name == "" || localIPstr == "" { reqError("Invalid request", "Invalid request: %s, %s", r.URL, r.Form) return @@ -58,10 +57,8 @@ func ListenHttp(domain string, db Zone, port int) { if dns.IsSubDomain(domain, name) { Info.Printf("Adding %s (%s) -> %s", name, localIPstr, weaveIPstr) - err = db.AddRecord(ident, name, localIP, weaveIP) - if err != nil { - dup, ok := err.(DuplicateError) - if !ok { + if err = db.AddRecord(ident, name, localIP, weaveIP); err != nil { + if dup, ok := err.(DuplicateError); !ok { httpErrorAndLog( Error, w, "Internal error", http.StatusInternalServerError, "Unexpected error from DB: %s", err) @@ -88,8 +85,7 @@ func ListenHttp(domain string, db Zone, port int) { return } Info.Printf("Deleting %s (%s)", ident, weaveIPstr) - err = db.DeleteRecord(ident, weaveIP) - if err != nil { + if err = db.DeleteRecord(ident, weaveIP); err != nil { if _, ok := err.(LookupError); !ok { httpErrorAndLog( Error, w, "Internal error", http.StatusInternalServerError, @@ -105,8 +101,7 @@ func ListenHttp(domain string, db Zone, port int) { }) address := fmt.Sprintf(":%d", port) - err := http.ListenAndServe(address, nil) - if err != nil { + if err := http.ListenAndServe(address, nil); err != nil { Error.Fatal("Unable to create http listener: ", err) } } diff --git a/server/if_utils.go b/server/if_utils.go index 9406c533..051c57f6 100644 --- a/server/if_utils.go +++ b/server/if_utils.go @@ -7,8 +7,7 @@ import ( ) func EnsureInterface(ifaceName string, wait int) (iface *net.Interface, err error) { - iface, err = findInterface(ifaceName) - if err == nil || wait == 0 { + if iface, err = findInterface(ifaceName); err == nil || wait == 0 { return } Info.Println("Waiting for interface", ifaceName, "to come up") @@ -23,8 +22,7 @@ func EnsureInterface(ifaceName string, wait int) (iface *net.Interface, err erro } func findInterface(ifaceName string) (iface *net.Interface, err error) { - iface, err = net.InterfaceByName(ifaceName) - if err != nil { + if iface, err = net.InterfaceByName(ifaceName); err != nil { return iface, fmt.Errorf("Unable to find interface %s", ifaceName) } if 0 == (net.FlagUp & iface.Flags) { diff --git a/server/mdns_client.go b/server/mdns_client.go index 64920e8d..db5f134a 100644 --- a/server/mdns_client.go +++ b/server/mdns_client.go @@ -62,14 +62,14 @@ type mDNSQueryInfo struct { } func NewMDNSClient() (*MDNSClient, error) { - conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) - if err != nil { + if conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}); err != nil { return nil, err + } else { + return &MDNSClient{ + conn: conn, + addr: ipv4Addr, + inflight: make(map[string]*inflightQuery)}, nil } - return &MDNSClient{ - conn: conn, - addr: ipv4Addr, - inflight: make(map[string]*inflightQuery)}, nil } func (c *MDNSClient) Start(ifi *net.Interface) error { @@ -96,8 +96,7 @@ func (c *MDNSClient) Start(ifi *net.Interface) error { } func LinkLocalMulticastListener(ifi *net.Interface) (net.PacketConn, error) { - conn, err := net.ListenMulticastUDP("udp", ifi, ipv4Addr) - return conn, err + return net.ListenMulticastUDP("udp", ifi, ipv4Addr) } // ACTOR client API @@ -216,8 +215,7 @@ func (c *MDNSClient) handleSendQuery(q mDNSQueryInfo) { name: q.name, id: m.Id, } - _, err = c.conn.WriteTo(buf, c.addr) - if err != nil { + if _, err = c.conn.WriteTo(buf, c.addr); err != nil { q.responseCh <- &ResponseA{Err: err} close(q.responseCh) return diff --git a/server/mdns_server.go b/server/mdns_server.go index 45542bec..7997baaa 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -17,9 +17,7 @@ func NewMDNSServer(zone Zone) (*MDNSServer, error) { if err != nil { return nil, err } - retval := &MDNSServer{sendconn: sendconn, zone: zone} - - return retval, nil + return &MDNSServer{sendconn: sendconn, zone: zone}, nil } // Return true if testaddr is a UDP address with IP matching my local i/f @@ -41,8 +39,7 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { // Ignore answers to other questions if len(r.Answer) == 0 && len(r.Question) > 0 { q := r.Question[0] - ip, err := s.zone.MatchLocal(q.Name) - if err == nil { + if ip, err := s.zone.MatchLocal(q.Name); err == nil { m := makeAReply(r, &q, []net.IP{ip}) s.SendResponse(m) } else if s.addrIsLocal(w.RemoteAddr()) { @@ -57,6 +54,7 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { if err != nil { return err } + if ifi == nil { s.localAddrs, err = net.InterfaceAddrs() } else { diff --git a/server/server.go b/server/server.go index 97bed95d..26ca3672 100644 --- a/server/server.go +++ b/server/server.go @@ -39,8 +39,7 @@ func queryHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { q := r.Question[0] Debug.Printf("Local query: %+v", q) if q.Qtype == dns.TypeA { - ip, err := zone.MatchLocal(q.Name) - if err == nil { + if ip, err := zone.MatchLocal(q.Name); err == nil { m := makeAReply(r, &q, []net.IP{ip}) w.WriteMsg(m) } else { @@ -81,8 +80,7 @@ func rdnsHandler(zone Zone, mdnsClient *MDNSClient) dns.HandlerFunc { ip4 := ip.To4() revIP := []byte{ip4[3], ip4[2], ip4[1], ip4[0]} Debug.Printf("Looking for address: %+v", revIP) - name, err := zone.MatchLocalIP(revIP) - if err == nil { + if name, err := zone.MatchLocalIP(revIP); err == nil { Debug.Printf("Found name: %s", name) m := makePTRReply(r, &q, []string{name}) w.WriteMsg(m) @@ -106,8 +104,7 @@ func notUsHandler() dns.HandlerFunc { Debug.Printf("Non-local query: %+v", q) var responseMsg *dns.Msg if q.Qtype == dns.TypeA { - addrs, err := net.LookupIP(q.Name) - if err == nil { + if addrs, err := net.LookupIP(q.Name); err == nil { responseMsg = makeAReply(r, &q, addrs) } else { responseMsg = makeDNSFailResponse(r) From b3489f6ec15967985eb22fa90e1d885a7396d064 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 28 Oct 2014 15:39:50 +0000 Subject: [PATCH 138/145] Move the handler function closer to its use --- server/mdns_server.go | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/server/mdns_server.go b/server/mdns_server.go index 7997baaa..81753b08 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -35,21 +35,6 @@ func (s *MDNSServer) addrIsLocal(testaddr net.Addr) bool { } func (s *MDNSServer) Start(ifi *net.Interface) error { - handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) { - // Ignore answers to other questions - if len(r.Answer) == 0 && len(r.Question) > 0 { - q := r.Question[0] - if ip, err := s.zone.MatchLocal(q.Name); err == nil { - m := makeAReply(r, &q, []net.IP{ip}) - s.SendResponse(m) - } else if s.addrIsLocal(w.RemoteAddr()) { - // ignore this - it's our own query received via multicast - } else { - Debug.Printf("Failed MDNS lookup for %s", q.Name) - } - } - } - conn, err := LinkLocalMulticastListener(ifi) if err != nil { return err @@ -64,8 +49,21 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { return err } + handleMDNS := func(w dns.ResponseWriter, r *dns.Msg) { + // Ignore answers to other questions + if len(r.Answer) == 0 && len(r.Question) > 0 { + q := r.Question[0] + if ip, err := s.zone.MatchLocal(q.Name); err == nil { + m := makeAReply(r, &q, []net.IP{ip}) + s.SendResponse(m) + } else if s.addrIsLocal(w.RemoteAddr()) { + // ignore this - it's our own query received via multicast + } else { + Debug.Printf("Failed MDNS lookup for %s", q.Name) + } + } + } go dns.ActivateAndServe(nil, conn, dns.HandlerFunc(handleMDNS)) - return err } From 48328dec5032f2da86c59278c98e1f1a747739f6 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 28 Oct 2014 16:52:43 +0000 Subject: [PATCH 139/145] Change logging config to match weave --- server/logging.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/logging.go b/server/logging.go index 7dcb551f..ccd96f30 100644 --- a/server/logging.go +++ b/server/logging.go @@ -8,10 +8,11 @@ import ( ) const ( - standardLogFlags = log.Ldate | log.Ltime | log.Lshortfile + standardLogFlags = log.Ldate | log.Ltime | log.Lmicroseconds ) -// Inspired by http://www.goinggo.net/2013/11/using-log-package-in-go.html +// Largely taken from +// http://www.goinggo.net/2013/11/using-log-package-in-go.html var ( Debug *log.Logger From 03f766bb959bfd3342fa96d1876955741021bc21 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 28 Oct 2014 17:12:46 +0000 Subject: [PATCH 140/145] test.Log;test.Fail -> test.Fatal, and use checkError (in tests) --- server/mdns_client_test.go | 22 +++++++--------------- server/mdns_server_test.go | 9 +++------ 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/server/mdns_client_test.go b/server/mdns_client_test.go index 7818a4be..ce029865 100644 --- a/server/mdns_client_test.go +++ b/server/mdns_client_test.go @@ -35,23 +35,18 @@ func minimalServer(w dns.ResponseWriter, req *dns.Msg) { m.Answer = append(m.Answer, a) buf, err := m.Pack() - if err != nil { - log.Fatal(err) - } + checkFatal(err) if buf == nil { log.Fatal("Nil buffer") } //log.Println("minimalServer sending:", buf) // This is a bit of a kludge - per the RFC we should send responses from 5353, but that doesn't seem to work sendconn, err := net.DialUDP("udp", nil, ipv4Addr) - if err != nil { - log.Fatal(err) - } + checkFatal(err) + _, err = sendconn.Write(buf) sendconn.Close() - if err != nil { - log.Fatal(err) - } + checkFatal(err) } func RunLocalMulticastServer() (*dns.Server, error) { @@ -113,8 +108,7 @@ func TestSimpleQuery(t *testing.T) { } if !context.receivedAddr.Equal(testAddr) || context.receivedCount != 1 { - t.Log("Unexpected result for", successTestName, context.receivedAddr, context.receivedCount) - t.Fail() + t.Fatal("Unexpected result for", successTestName, context.receivedAddr, context.receivedCount) } // Now, a test we expect to time out with no responses @@ -125,8 +119,7 @@ func TestSimpleQuery(t *testing.T) { } if context.receivedCount > 0 { - t.Log("Unexpected result for test2.weave", context.receivedAddr) - t.Fail() + t.Fatal("Unexpected result for test2.weave", context.receivedAddr) } } @@ -155,7 +148,6 @@ outerloop: } if !context1.receivedAddr.Equal(testAddr) || !context2.receivedAddr.Equal(testAddr) || context1.receivedCount != 1 || context2.receivedCount != 1 { - t.Log("Unexpected result for", successTestName, context1.receivedAddr, context2.receivedAddr, context1.receivedCount, context2.receivedCount) - t.Fail() + t.Fatal("Unexpected result for", successTestName, context1.receivedAddr, context2.receivedAddr, context1.receivedCount, context2.receivedCount) } } diff --git a/server/mdns_server_test.go b/server/mdns_server_test.go index 1fc8afb6..d3ecff2d 100644 --- a/server/mdns_server_test.go +++ b/server/mdns_server_test.go @@ -72,12 +72,10 @@ func TestServerSimpleQuery(t *testing.T) { time.Sleep(time.Second) if receivedCount != 1 { - t.Log("Unexpected result count for test.weave", receivedCount) - t.Fail() + t.Fatal("Unexpected result count for test.weave", receivedCount) } if !receivedAddr.Equal(weaveIP) { - t.Log("Unexpected result for test.weave", receivedAddr) - t.Fail() + t.Fatal("Unexpected result for test.weave", receivedAddr) } receivedCount = 0 @@ -85,7 +83,6 @@ func TestServerSimpleQuery(t *testing.T) { sendQuery("testfail.weave.", dns.TypeA) if receivedCount != 0 { - t.Log("Unexpected result count for testfail.weave", receivedCount) - t.Fail() + t.Fatal("Unexpected result count for testfail.weave", receivedCount) } } From fdcb960cdde4b2f143e693247cb17ca38410a5f4 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Tue, 28 Oct 2014 17:23:03 +0000 Subject: [PATCH 141/145] Don't export sendResponse, and pay attention to its retval --- server/mdns_server.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/mdns_server.go b/server/mdns_server.go index 81753b08..0e8fdf85 100644 --- a/server/mdns_server.go +++ b/server/mdns_server.go @@ -55,7 +55,9 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { q := r.Question[0] if ip, err := s.zone.MatchLocal(q.Name); err == nil { m := makeAReply(r, &q, []net.IP{ip}) - s.SendResponse(m) + if err = s.sendResponse(m); err != nil { + Warning.Printf("Error writing to %s", w) + } } else if s.addrIsLocal(w.RemoteAddr()) { // ignore this - it's our own query received via multicast } else { @@ -67,7 +69,7 @@ func (s *MDNSServer) Start(ifi *net.Interface) error { return err } -func (s *MDNSServer) SendResponse(m *dns.Msg) error { +func (s *MDNSServer) sendResponse(m *dns.Msg) error { buf, err := m.Pack() if err != nil { return err From 8469fea2152af48c35919a05f2f13cecfe8b3f69 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 29 Oct 2014 11:34:56 +0000 Subject: [PATCH 142/145] Move files around in preparation for melding into weave code * library code will go in weave/nameserver/ * main.go for the server goes in weavedns/ with the containerising --- {server => nameserver}/dns.go | 0 {server => nameserver}/http.go | 0 {server => nameserver}/http_test.go | 0 {server => nameserver}/if_utils.go | 0 {server => nameserver}/logging.go | 0 {server => nameserver}/mdns_client.go | 0 {server => nameserver}/mdns_client_test.go | 0 {server => nameserver}/mdns_server.go | 0 {server => nameserver}/mdns_server_test.go | 0 {server => nameserver}/server.go | 0 {server => nameserver}/testing_utils.go | 0 {server => nameserver}/updater.go | 0 {server => nameserver}/zone.go | 0 {server => nameserver}/zone_test.go | 0 {docker => weavedns}/Dockerfile | 0 {docker => weavedns}/Makefile | 0 main.go => weavedns/main.go | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename {server => nameserver}/dns.go (100%) rename {server => nameserver}/http.go (100%) rename {server => nameserver}/http_test.go (100%) rename {server => nameserver}/if_utils.go (100%) rename {server => nameserver}/logging.go (100%) rename {server => nameserver}/mdns_client.go (100%) rename {server => nameserver}/mdns_client_test.go (100%) rename {server => nameserver}/mdns_server.go (100%) rename {server => nameserver}/mdns_server_test.go (100%) rename {server => nameserver}/server.go (100%) rename {server => nameserver}/testing_utils.go (100%) rename {server => nameserver}/updater.go (100%) rename {server => nameserver}/zone.go (100%) rename {server => nameserver}/zone_test.go (100%) rename {docker => weavedns}/Dockerfile (100%) rename {docker => weavedns}/Makefile (100%) rename main.go => weavedns/main.go (100%) diff --git a/server/dns.go b/nameserver/dns.go similarity index 100% rename from server/dns.go rename to nameserver/dns.go diff --git a/server/http.go b/nameserver/http.go similarity index 100% rename from server/http.go rename to nameserver/http.go diff --git a/server/http_test.go b/nameserver/http_test.go similarity index 100% rename from server/http_test.go rename to nameserver/http_test.go diff --git a/server/if_utils.go b/nameserver/if_utils.go similarity index 100% rename from server/if_utils.go rename to nameserver/if_utils.go diff --git a/server/logging.go b/nameserver/logging.go similarity index 100% rename from server/logging.go rename to nameserver/logging.go diff --git a/server/mdns_client.go b/nameserver/mdns_client.go similarity index 100% rename from server/mdns_client.go rename to nameserver/mdns_client.go diff --git a/server/mdns_client_test.go b/nameserver/mdns_client_test.go similarity index 100% rename from server/mdns_client_test.go rename to nameserver/mdns_client_test.go diff --git a/server/mdns_server.go b/nameserver/mdns_server.go similarity index 100% rename from server/mdns_server.go rename to nameserver/mdns_server.go diff --git a/server/mdns_server_test.go b/nameserver/mdns_server_test.go similarity index 100% rename from server/mdns_server_test.go rename to nameserver/mdns_server_test.go diff --git a/server/server.go b/nameserver/server.go similarity index 100% rename from server/server.go rename to nameserver/server.go diff --git a/server/testing_utils.go b/nameserver/testing_utils.go similarity index 100% rename from server/testing_utils.go rename to nameserver/testing_utils.go diff --git a/server/updater.go b/nameserver/updater.go similarity index 100% rename from server/updater.go rename to nameserver/updater.go diff --git a/server/zone.go b/nameserver/zone.go similarity index 100% rename from server/zone.go rename to nameserver/zone.go diff --git a/server/zone_test.go b/nameserver/zone_test.go similarity index 100% rename from server/zone_test.go rename to nameserver/zone_test.go diff --git a/docker/Dockerfile b/weavedns/Dockerfile similarity index 100% rename from docker/Dockerfile rename to weavedns/Dockerfile diff --git a/docker/Makefile b/weavedns/Makefile similarity index 100% rename from docker/Makefile rename to weavedns/Makefile diff --git a/main.go b/weavedns/main.go similarity index 100% rename from main.go rename to weavedns/main.go From 822ec9060641837c8af66b6a403f7a13c45b3f7f Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 29 Oct 2014 11:41:58 +0000 Subject: [PATCH 143/145] Rename weavedns package to nameserver, and fix up --- nameserver/dns.go | 2 +- nameserver/http.go | 2 +- nameserver/http_test.go | 2 +- nameserver/if_utils.go | 2 +- nameserver/logging.go | 2 +- nameserver/mdns_client.go | 2 +- nameserver/mdns_client_test.go | 2 +- nameserver/mdns_server.go | 2 +- nameserver/mdns_server_test.go | 2 +- nameserver/server.go | 2 +- nameserver/testing_utils.go | 2 +- nameserver/updater.go | 2 +- nameserver/zone.go | 2 +- nameserver/zone_test.go | 2 +- weavedns/Makefile | 4 ++-- weavedns/main.go | 2 +- 16 files changed, 17 insertions(+), 17 deletions(-) diff --git a/nameserver/dns.go b/nameserver/dns.go index e4f3daa9..bd0466e0 100644 --- a/nameserver/dns.go +++ b/nameserver/dns.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "github.com/miekg/dns" diff --git a/nameserver/http.go b/nameserver/http.go index cecc1d91..b25b9204 100644 --- a/nameserver/http.go +++ b/nameserver/http.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "errors" diff --git a/nameserver/http_test.go b/nameserver/http_test.go index 09144c68..9e41dbd8 100644 --- a/nameserver/http_test.go +++ b/nameserver/http_test.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "fmt" diff --git a/nameserver/if_utils.go b/nameserver/if_utils.go index 051c57f6..2159b125 100644 --- a/nameserver/if_utils.go +++ b/nameserver/if_utils.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "fmt" diff --git a/nameserver/logging.go b/nameserver/logging.go index ccd96f30..03c1529b 100644 --- a/nameserver/logging.go +++ b/nameserver/logging.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "io" diff --git a/nameserver/mdns_client.go b/nameserver/mdns_client.go index db5f134a..9bf9dda2 100644 --- a/nameserver/mdns_client.go +++ b/nameserver/mdns_client.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "github.com/miekg/dns" diff --git a/nameserver/mdns_client_test.go b/nameserver/mdns_client_test.go index ce029865..2e0206a7 100644 --- a/nameserver/mdns_client_test.go +++ b/nameserver/mdns_client_test.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "github.com/miekg/dns" diff --git a/nameserver/mdns_server.go b/nameserver/mdns_server.go index 0e8fdf85..7cc25fae 100644 --- a/nameserver/mdns_server.go +++ b/nameserver/mdns_server.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "github.com/miekg/dns" diff --git a/nameserver/mdns_server_test.go b/nameserver/mdns_server_test.go index d3ecff2d..51fd5f14 100644 --- a/nameserver/mdns_server_test.go +++ b/nameserver/mdns_server_test.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "github.com/miekg/dns" diff --git a/nameserver/server.go b/nameserver/server.go index 26ca3672..bd492a57 100644 --- a/nameserver/server.go +++ b/nameserver/server.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "fmt" diff --git a/nameserver/testing_utils.go b/nameserver/testing_utils.go index 0b7c37cd..0a4835ae 100644 --- a/nameserver/testing_utils.go +++ b/nameserver/testing_utils.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "reflect" diff --git a/nameserver/updater.go b/nameserver/updater.go index c0d71f04..9d73090b 100644 --- a/nameserver/updater.go +++ b/nameserver/updater.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "github.com/fsouza/go-dockerclient" diff --git a/nameserver/zone.go b/nameserver/zone.go index 7e4c8a42..97c88cd5 100644 --- a/nameserver/zone.go +++ b/nameserver/zone.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "github.com/miekg/dns" diff --git a/nameserver/zone_test.go b/nameserver/zone_test.go index bbe3a9e8..3ada988c 100644 --- a/nameserver/zone_test.go +++ b/nameserver/zone_test.go @@ -1,4 +1,4 @@ -package weavedns +package nameserver import ( "net" diff --git a/weavedns/Makefile b/weavedns/Makefile index fa46ea64..237ac97e 100644 --- a/weavedns/Makefile +++ b/weavedns/Makefile @@ -2,8 +2,8 @@ all: server-image -weavedns: ../server/*.go ../main.go - go get -tags netgo ../ && go build -tags netgo -ldflags '-linkmode external -extldflags "-static"' -o $@ -v ../ +weavedns: ../nameserver/*.go ./main.go + go get -tags netgo && go build -tags netgo -ldflags '-linkmode external -extldflags "-static"' -o $@ -v .PHONY: server-image diff --git a/weavedns/main.go b/weavedns/main.go index 2db52c04..79c8a51a 100644 --- a/weavedns/main.go +++ b/weavedns/main.go @@ -2,7 +2,7 @@ package main import ( "flag" - "github.com/zettio/weavedns/server" + weavedns "github.com/zettio/weavedns/nameserver" "io/ioutil" "net" "os" From 747d48749db4be321ba0f255ae905c8441d8cb3b Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 29 Oct 2014 11:44:07 +0000 Subject: [PATCH 144/145] Ignore compiled binary --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 24f25015..a23bb285 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ _testmain.go # Emacs backup files *~ + +# Project specific +weavedns/weavedns From a384e0bc166724cf6195f990eda5f8b3aef87c18 Mon Sep 17 00:00:00 2001 From: Michael Bridgen Date: Wed, 29 Oct 2014 12:23:18 +0000 Subject: [PATCH 145/145] Move more weavedns things out of the way --- LICENSE | 202 -------------------------------- README.md => weavedns/README.md | 0 design.md => weavedns/design.md | 0 3 files changed, 202 deletions(-) delete mode 100644 LICENSE rename README.md => weavedns/README.md (100%) rename design.md => weavedns/design.md (100%) diff --git a/LICENSE b/LICENSE deleted file mode 100644 index e06d2081..00000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/README.md b/weavedns/README.md similarity index 100% rename from README.md rename to weavedns/README.md diff --git a/design.md b/weavedns/design.md similarity index 100% rename from design.md rename to weavedns/design.md