From 464611bae6290b12b611025dde6c52b551bf11ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Sodr=C3=A9?= Date: Sat, 5 Aug 2017 01:08:06 -0400 Subject: [PATCH 01/13] Update docs to include support for TXT records - Add explanation to the difference between RFC1035 and RFC1464 queries. --- website/source/docs/agent/dns.html.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/website/source/docs/agent/dns.html.md b/website/source/docs/agent/dns.html.md index 7e33ade73..9c6fd1fcf 100644 --- a/website/source/docs/agent/dns.html.md +++ b/website/source/docs/agent/dns.html.md @@ -57,8 +57,9 @@ we can instead use `foo.node.consul.` This convention allows for terse syntax where appropriate while supporting queries of nodes in remote datacenters as necessary. -For a node lookup, the only records returned are A records containing -the IP address of the node. +For a node lookup, the only records returned are A and AAAA records +containing the IP address, and TXT records containing the +`node_meta` values of the node. ```text $ dig @127.0.0.1 -p 8600 foo.node.consul ANY @@ -76,11 +77,19 @@ $ dig @127.0.0.1 -p 8600 foo.node.consul ANY ;; ANSWER SECTION: foo.node.consul. 0 IN A 10.1.10.12 +foo.node.consul. 0 IN TXT "meta_key=meta_value" +foo.node.consul. 0 IN TXT "value only" + ;; AUTHORITY SECTION: consul. 0 IN SOA ns.consul. postmaster.consul. 1392836399 3600 600 86400 0 ``` +By default the TXT records value will match the node's metadata key-value +pairs according to [RFC1464](https://www.ietf.org/rfc/rfc1464.txt). +Alternatively, the TXT record will only include the node's metadata value when the +node's metadata key starts with `rfc1035-`. + ## Service Lookups A service lookup is used to query for service providers. Service queries support From 354765c5495ae501cde3b4542e247292b7a0986b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Sodre=CC=81?= Date: Tue, 1 Aug 2017 00:37:39 -0400 Subject: [PATCH 02/13] Add test for querying Node.Meta with DNS TXT - Lookup TXT records using recursive lookups - Expect TXT record equal to value if key starts with rfc1035- - Expect TXT record in rfc1464 otherwise, i.e. (k=v) ref #2709 --- agent/dns_test.go | 64 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/agent/dns_test.go b/agent/dns_test.go index 02247db2a..c4e3651b7 100644 --- a/agent/dns_test.go +++ b/agent/dns_test.go @@ -78,6 +78,18 @@ func dnsA(src, dest string) *dns.A { } } +// dnsTXT returns a DNS TXT record struct +func dnsTXT(src string, txt []string) *dns.TXT { + return &dns.TXT{ + Hdr: dns.RR_Header{ + Name: dns.Fqdn(src), + Rrtype: dns.TypeTXT, + Class: dns.ClassINET, + }, + Txt: txt, + } +} + func TestRecursorAddr(t *testing.T) { t.Parallel() addr, err := recursorAddr("8.8.8.8") @@ -300,6 +312,7 @@ func TestDNS_NodeLookup_CNAME(t *testing.T) { Answer: []dns.RR{ dnsCNAME("www.google.com", "google.com"), dnsA("google.com", "1.2.3.4"), + dnsTXT("google.com", []string{"my_txt_value"}), }, }) defer recursor.Shutdown() @@ -330,8 +343,8 @@ func TestDNS_NodeLookup_CNAME(t *testing.T) { t.Fatalf("err: %v", err) } - // Should have the service record, CNAME record + A record - if len(in.Answer) != 3 { + // Should have the service record, CNAME record + A + TXT record + if len(in.Answer) != 4 { t.Fatalf("Bad: %#v", in) } @@ -347,6 +360,53 @@ func TestDNS_NodeLookup_CNAME(t *testing.T) { } } +func TestDNS_NodeLookup_TXT(t *testing.T) { + cfg := TestConfig() + a := NewTestAgent(t.Name(), cfg) + defer a.Shutdown() + + args := &structs.RegisterRequest{ + Datacenter: "dc1", + Node: "google", + Address: "127.0.0.1", + NodeMeta: map[string]string{ + "rfc1035-00": "value0", + "key0": "value1", + }, + } + + var out struct{} + if err := a.RPC("Catalog.Register", args, &out); err != nil { + t.Fatalf("err: %v", err) + } + + m := new(dns.Msg) + m.SetQuestion("google.node.consul.", dns.TypeTXT) + + c := new(dns.Client) + addr, _ := a.Config.ClientListener("", a.Config.Ports.DNS) + in, _, err := c.Exchange(m, addr.String()) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Should have the 1 TXT record reply + if len(in.Answer) != 2 { + t.Fatalf("Bad: %#v", in) + } + + txtRec, ok := in.Answer[0].(*dns.TXT) + if !ok { + t.Fatalf("Bad: %#v", in.Answer[0]) + } + if len(txtRec.Txt) != 1 { + t.Fatalf("Bad: %#v", in.Answer[0]) + } + if txtRec.Txt[0] != "value0" && txtRec.Txt[0] != "key0=value1" { + t.Fatalf("Bad: %#v", in.Answer[0]) + } +} + func TestDNS_EDNS0(t *testing.T) { t.Parallel() a := NewTestAgent(t.Name(), "") From b8905dd06585e2da63c3c3cd7637f968b3f2be5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Sodre=CC=81?= Date: Thu, 3 Aug 2017 02:56:24 -0400 Subject: [PATCH 03/13] Add test for NoteLookup ANY request --- agent/dns_test.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/agent/dns_test.go b/agent/dns_test.go index c4e3651b7..128bb5323 100644 --- a/agent/dns_test.go +++ b/agent/dns_test.go @@ -407,6 +407,41 @@ func TestDNS_NodeLookup_TXT(t *testing.T) { } } +func TestDNS_NodeLookup_ANY(t *testing.T) { + cfg := TestConfig() + a := NewTestAgent(t.Name(), cfg) + defer a.Shutdown() + + args := &structs.RegisterRequest{ + Datacenter: "dc1", + Node: "bar", + Address: "127.0.0.1", + NodeMeta: map[string]string{ + "key": "value", + }, + } + + var out struct{} + if err := a.RPC("Catalog.Register", args, &out); err != nil { + t.Fatalf("err: %v", err) + } + + m := new(dns.Msg) + m.SetQuestion("bar.node.consul.", dns.TypeANY) + + c := new(dns.Client) + addr, _ := a.Config.ClientListener("", a.Config.Ports.DNS) + in, _, err := c.Exchange(m, addr.String()) + if err != nil { + t.Fatalf("err: %v", err) + } + + // Should have 1 A and 1 TXT record + if len(in.Answer) != 2 { + t.Fatalf("Bad: %#v", in) + } +} + func TestDNS_EDNS0(t *testing.T) { t.Parallel() a := NewTestAgent(t.Name(), "") From b8369b54fb298f69ff07e3d3bc4501d441b609c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Sodre=CC=81?= Date: Tue, 1 Aug 2017 03:01:49 -0400 Subject: [PATCH 04/13] Return Node.Meta info using the DNS interface --- agent/dns.go | 56 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/agent/dns.go b/agent/dns.go index c2ccd5249..af605d292 100644 --- a/agent/dns.go +++ b/agent/dns.go @@ -341,7 +341,7 @@ func (d *DNSServer) nameservers(edns bool) (ns []dns.RR, extra []dns.RR) { } ns = append(ns, nsrr) - glue := d.formatNodeRecord(addr, fqdn, dns.TypeANY, d.config.NodeTTL, edns) + glue := d.formatNodeRecord(nil, addr, fqdn, dns.TypeANY, d.config.NodeTTL, edns) extra = append(extra, glue...) // don't provide more than 3 servers @@ -485,9 +485,9 @@ INVALID: // nodeLookup is used to handle a node query func (d *DNSServer) nodeLookup(network, datacenter, node string, req, resp *dns.Msg) { - // Only handle ANY, A and AAAA type requests + // Only handle ANY, A, AAAA and TXT type requests qType := req.Question[0].Qtype - if qType != dns.TypeANY && qType != dns.TypeA && qType != dns.TypeAAAA { + if qType != dns.TypeANY && qType != dns.TypeA && qType != dns.TypeAAAA && qType != dns.TypeTXT { return } @@ -530,23 +530,36 @@ RPC: n := out.NodeServices.Node edns := req.IsEdns0() != nil addr := d.agent.TranslateAddress(datacenter, n.Address, n.TaggedAddresses) - records := d.formatNodeRecord(addr, req.Question[0].Name, qType, d.config.NodeTTL, edns) + records := d.formatNodeRecord(out.NodeServices.Node, addr, req.Question[0].Name, qType, d.config.NodeTTL, edns) if records != nil { resp.Answer = append(resp.Answer, records...) } } -// formatNodeRecord takes a Node and returns an A, AAAA, or CNAME record -func (d *DNSServer) formatNodeRecord(addr, qName string, qType uint16, ttl time.Duration, edns bool) (records []dns.RR) { +// formatTxtRecords takes a kv-map and returns it as "[k=]v" for non-empty k not starting with rfc1035- +func (d *DNSServer) formatTxtRecords(meta map[string]string) (txt []string) { + for k, v := range meta { + if strings.HasPrefix(k, "rfc1035-") { + txt = append(txt, v) + } else { + txt = append(txt, k+"="+v) + } + } + return txt +} + +// formatNodeRecord takes a Node and returns an A, AAAA, TXT or CNAME record +func (d *DNSServer) formatNodeRecord(node *structs.Node, addr, qName string, qType uint16, ttl time.Duration, edns bool) (records []dns.RR) { // Parse the IP ip := net.ParseIP(addr) var ipv4 net.IP if ip != nil { ipv4 = ip.To4() } + switch { case ipv4 != nil && (qType == dns.TypeANY || qType == dns.TypeA): - return []dns.RR{&dns.A{ + records = append(records, &dns.A{ Hdr: dns.RR_Header{ Name: qName, Rrtype: dns.TypeA, @@ -554,10 +567,10 @@ func (d *DNSServer) formatNodeRecord(addr, qName string, qType uint16, ttl time. Ttl: uint32(ttl / time.Second), }, A: ip, - }} + }) case ip != nil && ipv4 == nil && (qType == dns.TypeANY || qType == dns.TypeAAAA): - return []dns.RR{&dns.AAAA{ + records = append(records, &dns.AAAA{ Hdr: dns.RR_Header{ Name: qName, Rrtype: dns.TypeAAAA, @@ -565,10 +578,10 @@ func (d *DNSServer) formatNodeRecord(addr, qName string, qType uint16, ttl time. Ttl: uint32(ttl / time.Second), }, AAAA: ip, - }} + }) case ip == nil && (qType == dns.TypeANY || qType == dns.TypeCNAME || - qType == dns.TypeA || qType == dns.TypeAAAA): + qType == dns.TypeA || qType == dns.TypeAAAA || qType == dns.TypeTXT): // Get the CNAME cnRec := &dns.CNAME{ Hdr: dns.RR_Header{ @@ -587,7 +600,7 @@ func (d *DNSServer) formatNodeRecord(addr, qName string, qType uint16, ttl time. MORE_REC: for _, rr := range more { switch rr.Header().Rrtype { - case dns.TypeCNAME, dns.TypeA, dns.TypeAAAA: + case dns.TypeCNAME, dns.TypeA, dns.TypeAAAA, dns.TypeTXT: records = append(records, rr) extra++ if extra == maxRecurseRecords && !edns { @@ -596,6 +609,21 @@ func (d *DNSServer) formatNodeRecord(addr, qName string, qType uint16, ttl time. } } } + + if node != nil && len(node.Meta) > 0 && (qType == dns.TypeANY || qType == dns.TypeTXT) { + for _, txt := range d.formatTxtRecords(node.Meta) { + records = append(records, &dns.TXT{ + Hdr: dns.RR_Header{ + Name: qName, + Rrtype: dns.TypeTXT, + Class: dns.ClassINET, + Ttl: uint32(ttl / time.Second), + }, + Txt: []string{txt}, + }) + } + } + return records } @@ -929,7 +957,7 @@ func (d *DNSServer) serviceNodeRecords(dc string, nodes structs.CheckServiceNode handled[addr] = struct{}{} // Add the node record - records := d.formatNodeRecord(addr, qName, qType, ttl, edns) + records := d.formatNodeRecord(node.Node, addr, qName, qType, ttl, edns) if records != nil { resp.Answer = append(resp.Answer, records...) } @@ -973,7 +1001,7 @@ func (d *DNSServer) serviceSRVRecords(dc string, nodes structs.CheckServiceNodes } // Add the extra record - records := d.formatNodeRecord(addr, srvRec.Target, dns.TypeANY, ttl, edns) + records := d.formatNodeRecord(node.Node, addr, srvRec.Target, dns.TypeANY, ttl, edns) if len(records) > 0 { // Use the node address if it doesn't differ from the service address if addr == node.Node.Address { From 4b2d1546fa7eb92027da194c76d09a5becf44e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Sodre=CC=81?= Date: Thu, 10 Aug 2017 00:11:43 -0400 Subject: [PATCH 05/13] Remove redundant check of Node.Meta size --- agent/dns.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/dns.go b/agent/dns.go index af605d292..fa93b51c4 100644 --- a/agent/dns.go +++ b/agent/dns.go @@ -610,7 +610,7 @@ func (d *DNSServer) formatNodeRecord(node *structs.Node, addr, qName string, qTy } } - if node != nil && len(node.Meta) > 0 && (qType == dns.TypeANY || qType == dns.TypeTXT) { + if node != nil && (qType == dns.TypeANY || qType == dns.TypeTXT) { for _, txt := range d.formatTxtRecords(node.Meta) { records = append(records, &dns.TXT{ Hdr: dns.RR_Header{ From a16e0f741912cc87d6458557e62b893f65214336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Sodre=CC=81?= Date: Thu, 10 Aug 2017 00:43:24 -0400 Subject: [PATCH 06/13] Fix editorial suggestions --- agent/dns.go | 2 +- agent/dns_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/dns.go b/agent/dns.go index fa93b51c4..5259ae24a 100644 --- a/agent/dns.go +++ b/agent/dns.go @@ -485,7 +485,7 @@ INVALID: // nodeLookup is used to handle a node query func (d *DNSServer) nodeLookup(network, datacenter, node string, req, resp *dns.Msg) { - // Only handle ANY, A, AAAA and TXT type requests + // Only handle ANY, A, AAAA, and TXT type requests qType := req.Question[0].Qtype if qType != dns.TypeANY && qType != dns.TypeA && qType != dns.TypeAAAA && qType != dns.TypeTXT { return diff --git a/agent/dns_test.go b/agent/dns_test.go index 128bb5323..1ce92484f 100644 --- a/agent/dns_test.go +++ b/agent/dns_test.go @@ -343,7 +343,7 @@ func TestDNS_NodeLookup_CNAME(t *testing.T) { t.Fatalf("err: %v", err) } - // Should have the service record, CNAME record + A + TXT record + // Should have the service, CNAME, A, and TXT records if len(in.Answer) != 4 { t.Fatalf("Bad: %#v", in) } From 8982719f5b8b6c24b4b8873e23550190354afbf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Sodre=CC=81?= Date: Thu, 10 Aug 2017 01:00:06 -0400 Subject: [PATCH 07/13] Refactor formatTxtRecords as encodeKVasRFC1464 - Move the logic of rfc1035 out of the encoding function - Left basic version of encodingKV as 'k=v' --- agent/dns.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/agent/dns.go b/agent/dns.go index 5259ae24a..ce281de15 100644 --- a/agent/dns.go +++ b/agent/dns.go @@ -536,15 +536,9 @@ RPC: } } -// formatTxtRecords takes a kv-map and returns it as "[k=]v" for non-empty k not starting with rfc1035- -func (d *DNSServer) formatTxtRecords(meta map[string]string) (txt []string) { - for k, v := range meta { - if strings.HasPrefix(k, "rfc1035-") { - txt = append(txt, v) - } else { - txt = append(txt, k+"="+v) - } - } +// encodeKVasRFC1464 encodes a key-value pair according to RFC1464 +func (d *DNSServer) encodeKVasRFC1464(key, value string) (txt string) { + txt = key + "=" + value return txt } @@ -611,7 +605,11 @@ func (d *DNSServer) formatNodeRecord(node *structs.Node, addr, qName string, qTy } if node != nil && (qType == dns.TypeANY || qType == dns.TypeTXT) { - for _, txt := range d.formatTxtRecords(node.Meta) { + for key, value := range node.Meta { + txt := value + if !strings.HasPrefix(strings.ToLower(key), "rfc1035-") { + txt = d.encodeKVasRFC1464(key, value) + } records = append(records, &dns.TXT{ Hdr: dns.RR_Header{ Name: qName, From be258a331526c306f4e1051c8084fe3948c5e276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Sodre=CC=81?= Date: Thu, 10 Aug 2017 02:02:39 -0400 Subject: [PATCH 08/13] Use verify for NodeLookup CNAME, and TXT tests --- agent/dns_test.go | 46 +++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/agent/dns_test.go b/agent/dns_test.go index 1ce92484f..dcf8a5438 100644 --- a/agent/dns_test.go +++ b/agent/dns_test.go @@ -343,21 +343,25 @@ func TestDNS_NodeLookup_CNAME(t *testing.T) { t.Fatalf("err: %v", err) } - // Should have the service, CNAME, A, and TXT records - if len(in.Answer) != 4 { - t.Fatalf("Bad: %#v", in) - } - - cnRec, ok := in.Answer[0].(*dns.CNAME) - if !ok { - t.Fatalf("Bad: %#v", in.Answer[0]) - } - if cnRec.Target != "www.google.com." { - t.Fatalf("Bad: %#v", in.Answer[0]) - } - if cnRec.Hdr.Ttl != 0 { - t.Fatalf("Bad: %#v", in.Answer[0]) + wantAnswer := []dns.RR{ + &dns.CNAME{ + Hdr: dns.RR_Header{Name: "google.node.consul.", Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Ttl: 0, Rdlength: 0x10}, + Target: "www.google.com.", + }, + &dns.CNAME{ + Hdr: dns.RR_Header{Name: "www.google.com.", Rrtype: dns.TypeCNAME, Class: dns.ClassINET, Rdlength: 0x2}, + Target: "google.com.", + }, + &dns.A{ + Hdr: dns.RR_Header{Name: "google.com.", Rrtype: dns.TypeA, Class: dns.ClassINET, Rdlength: 0x4}, + A: []byte{0x1, 0x2, 0x3, 0x4}, // 1.2.3.4 + }, + &dns.TXT{ + Hdr: dns.RR_Header{Name: "google.com.", Rrtype: dns.TypeTXT, Class: dns.ClassINET, Rdlength: 0xd}, + Txt: []string{"my_txt_value"}, + }, } + verify.Values(t, "answer", in.Answer, wantAnswer) } func TestDNS_NodeLookup_TXT(t *testing.T) { @@ -436,10 +440,18 @@ func TestDNS_NodeLookup_ANY(t *testing.T) { t.Fatalf("err: %v", err) } - // Should have 1 A and 1 TXT record - if len(in.Answer) != 2 { - t.Fatalf("Bad: %#v", in) + wantAnswer := []dns.RR{ + &dns.A{ + Hdr: dns.RR_Header{Name: "bar.node.consul.", Rrtype: dns.TypeA, Class: dns.ClassINET, Rdlength: 0x4}, + A: []byte{0x7f, 0x0, 0x0, 0x1}, // 127.0.0.1 + }, + &dns.TXT{ + Hdr: dns.RR_Header{Name: "bar.node.consul.", Rrtype: dns.TypeTXT, Class: dns.ClassINET, Rdlength: 0xa}, + Txt: []string{"key=value"}, + }, } + verify.Values(t, "answer", in.Answer, wantAnswer) + } func TestDNS_EDNS0(t *testing.T) { From 7083f9fb1418de29db63f420b5ccce2c9291762e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Sodre=CC=81?= Date: Thu, 10 Aug 2017 17:55:50 -0400 Subject: [PATCH 09/13] Turn encodeKVasRFC1464 into a plain function --- agent/dns.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agent/dns.go b/agent/dns.go index ce281de15..9830934d4 100644 --- a/agent/dns.go +++ b/agent/dns.go @@ -537,7 +537,7 @@ RPC: } // encodeKVasRFC1464 encodes a key-value pair according to RFC1464 -func (d *DNSServer) encodeKVasRFC1464(key, value string) (txt string) { +func encodeKVasRFC1464(key, value string) (txt string) { txt = key + "=" + value return txt } @@ -608,7 +608,7 @@ func (d *DNSServer) formatNodeRecord(node *structs.Node, addr, qName string, qTy for key, value := range node.Meta { txt := value if !strings.HasPrefix(strings.ToLower(key), "rfc1035-") { - txt = d.encodeKVasRFC1464(key, value) + txt = encodeKVasRFC1464(key, value) } records = append(records, &dns.TXT{ Hdr: dns.RR_Header{ From d880634cfaf252c89a086c2aaf610ec088268bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Sodre=CC=81?= Date: Thu, 10 Aug 2017 21:36:50 -0400 Subject: [PATCH 10/13] Add RFC1464 tests --- agent/dns_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/agent/dns_test.go b/agent/dns_test.go index dcf8a5438..a7ec449fd 100644 --- a/agent/dns_test.go +++ b/agent/dns_test.go @@ -101,6 +101,35 @@ func TestRecursorAddr(t *testing.T) { } } +func TestEncodeKVasRFC1464(t *testing.T) { + // Test cases are from rfc1464 + type rfc1464Test struct { + key, value, internalForm, externalForm string + } + tests := []rfc1464Test{ + {"color", "blue", "color=blue", "color=blue"}, + {"equation", "a=4", "equation=a=4", "equation=a=4"}, + {"a=a", "true", "a`=a=true", "a`=a=true"}, + {"a\\=a", "false", "a\\`=a=false", "a\\`=a=false"}, + {"=", "\\=", "`==\\=", "`==\\="}, + + {"string", "\"Cat\"", "string=\"Cat\"", "string=\"Cat\""}, + {"string2", "`abc`", "string2=``abc``", "string2=``abc``"}, + {"novalue", "", "novalue=", "novalue="}, + {"a b", "c d", "a b=c d", "a b=c d"}, + {"abc ", "123 ", "abc` =123 ", "abc` =123 "}, + + // Additional tests + {" abc", " 321", "` abc= 321", "` abc= 321"}, + {"`a", "b", "``a=b", "``a=b"}, + } + + for _, test := range tests { + answer := encodeKVasRFC1464(test.key, test.value) + verify.Values(t, "internalForm", answer, test.internalForm) + } +} + func TestDNS_NodeLookup(t *testing.T) { t.Parallel() a := NewTestAgent(t.Name(), "") From 55c2746963bcaa44e356acdcb190c80b04845d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Sodre=CC=81?= Date: Thu, 10 Aug 2017 21:37:17 -0400 Subject: [PATCH 11/13] Implement encodeKVasRFC1464 function --- agent/dns.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/agent/dns.go b/agent/dns.go index 9830934d4..6a2f74896 100644 --- a/agent/dns.go +++ b/agent/dns.go @@ -538,8 +538,23 @@ RPC: // encodeKVasRFC1464 encodes a key-value pair according to RFC1464 func encodeKVasRFC1464(key, value string) (txt string) { - txt = key + "=" + value - return txt + // For details on these replacements c.f. https://www.ietf.org/rfc/rfc1464.txt + key = strings.Replace(key, "`", "``", -1) + key = strings.Replace(key, "=", "`=", -1) + + // Backquote the leading spaces + leadingSpacesRE := regexp.MustCompile("^ +") + numLeadingSpaces := len(leadingSpacesRE.FindString(key)) + key = leadingSpacesRE.ReplaceAllString(key, strings.Repeat("` ", numLeadingSpaces)) + + // Backquote the trailing spaces + trailingSpacesRE := regexp.MustCompile(" +$") + numTrailingSpaces := len(trailingSpacesRE.FindString(key)) + key = trailingSpacesRE.ReplaceAllString(key, strings.Repeat("` ", numTrailingSpaces)) + + value = strings.Replace(value, "`", "``", -1) + + return key + "=" + value } // formatNodeRecord takes a Node and returns an A, AAAA, TXT or CNAME record From 92546f9231c131c63b1ee9cf08b030bbc696dce1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Sodre=CC=81?= Date: Thu, 10 Aug 2017 23:04:08 -0400 Subject: [PATCH 12/13] Update docs on RFC1464 vs RFC1035 options --- website/source/docs/agent/options.html.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/source/docs/agent/options.html.md b/website/source/docs/agent/options.html.md index 172b5d3af..0001bba17 100644 --- a/website/source/docs/agent/options.html.md +++ b/website/source/docs/agent/options.html.md @@ -432,6 +432,8 @@ will exit with an error at startup. - Metadata keys must contain only alphanumeric, `-`, and `_` characters. - Metadata keys must not begin with the `consul-` prefix; that is reserved for internal use by Consul. - Metadata values must be between 0 and 512 (inclusive) characters in length. + - Metadata values for keys begining with `rfc1035-` are encoded verbatim in DNS TXT requests, otherwise + the metadata kv-pair is encoded according [RFC1464](https://www.ietf.org/rfc/rfc1464.txt). * `-pid-file` - This flag provides the file path for the agent to store its PID. This is useful for sending signals (for example, `SIGINT` From f0efe2a3de05505f00b664f1481c561c72199ca9 Mon Sep 17 00:00:00 2001 From: Frank Schroeder Date: Tue, 26 Sep 2017 14:39:47 +0200 Subject: [PATCH 13/13] Fix tests after config refactor --- agent/dns_test.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/agent/dns_test.go b/agent/dns_test.go index a7ec449fd..6097eb253 100644 --- a/agent/dns_test.go +++ b/agent/dns_test.go @@ -394,8 +394,7 @@ func TestDNS_NodeLookup_CNAME(t *testing.T) { } func TestDNS_NodeLookup_TXT(t *testing.T) { - cfg := TestConfig() - a := NewTestAgent(t.Name(), cfg) + a := NewTestAgent(t.Name(), ``) defer a.Shutdown() args := &structs.RegisterRequest{ @@ -417,8 +416,7 @@ func TestDNS_NodeLookup_TXT(t *testing.T) { m.SetQuestion("google.node.consul.", dns.TypeTXT) c := new(dns.Client) - addr, _ := a.Config.ClientListener("", a.Config.Ports.DNS) - in, _, err := c.Exchange(m, addr.String()) + in, _, err := c.Exchange(m, a.DNSAddr()) if err != nil { t.Fatalf("err: %v", err) } @@ -441,8 +439,7 @@ func TestDNS_NodeLookup_TXT(t *testing.T) { } func TestDNS_NodeLookup_ANY(t *testing.T) { - cfg := TestConfig() - a := NewTestAgent(t.Name(), cfg) + a := NewTestAgent(t.Name(), ``) defer a.Shutdown() args := &structs.RegisterRequest{ @@ -463,8 +460,7 @@ func TestDNS_NodeLookup_ANY(t *testing.T) { m.SetQuestion("bar.node.consul.", dns.TypeANY) c := new(dns.Client) - addr, _ := a.Config.ClientListener("", a.Config.Ports.DNS) - in, _, err := c.Exchange(m, addr.String()) + in, _, err := c.Exchange(m, a.DNSAddr()) if err != nil { t.Fatalf("err: %v", err) }