Skip to content

Commit 0f2ab92

Browse files
p2p: Add delete/create to algons dnsaddr command (#5631)
1 parent 0c4f253 commit 0f2ab92

File tree

4 files changed

+215
-45
lines changed

4 files changed

+215
-45
lines changed

cmd/algons/dnsCmd.go

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,9 @@ var listRecordsCmd = &cobra.Command{
105105
Long: "List the A/SRV entries of the given network",
106106
Run: func(cmd *cobra.Command, args []string) {
107107
recordType = strings.ToUpper(recordType)
108-
if recordType == "" || recordType == "A" || recordType == "CNAME" || recordType == "SRV" {
109-
listEntries(listNetwork, recordType)
110-
} else {
111-
fmt.Fprintf(os.Stderr, "Invalid recordType specified.\n")
108+
err := listEntries(listNetwork, recordType)
109+
if err != nil {
110+
fmt.Fprintf(os.Stderr, "%v\n", err)
112111
os.Exit(1)
113112
}
114113
},
@@ -181,6 +180,19 @@ var exportCmd = &cobra.Command{
181180
},
182181
}
183182

183+
func doAddTXT(from string, to string) error {
184+
cfZoneID, cfToken, err := getClouldflareCredentials()
185+
if err != nil {
186+
return fmt.Errorf("error getting DNS credentials: %v", err)
187+
}
188+
189+
cloudflareDNS := cloudflare.NewDNS(cfZoneID, cfToken)
190+
191+
const priority = 1
192+
const proxied = false
193+
return cloudflareDNS.CreateDNSRecord(context.Background(), "TXT", from, to, cloudflare.AutomaticTTL, priority, proxied)
194+
}
195+
184196
func doAddDNS(from string, to string) (err error) {
185197
cfZoneID, cfToken, err := getClouldflareCredentials()
186198
if err != nil {
@@ -315,7 +327,7 @@ func doDeleteDNS(network string, noPrompt bool, excludePattern string, includePa
315327

316328
cloudflareDNS := cloudflare.NewDNS(cfZoneID, cfToken)
317329

318-
idsToDelete := make(map[string]string) // Maps record ID to Name
330+
var idsToDelete []cloudflare.DNSRecordResponseEntry
319331
services := []string{"_algobootstrap", "_metrics"}
320332
servicesRegexp, err := regexp.Compile("^(_algobootstrap|_metrics)\\._tcp\\..*algodev.network$")
321333

@@ -355,7 +367,7 @@ func doDeleteDNS(network string, noPrompt bool, excludePattern string, includePa
355367

356368
if includeRegex == nil || (includeRegex.MatchString(r.Name) && servicesRegexp.MatchString(r.Name)) {
357369
fmt.Printf("Found SRV record: %s\n", r.Name)
358-
idsToDelete[r.ID] = r.Name
370+
idsToDelete = append(idsToDelete, r)
359371
}
360372
}
361373
}
@@ -367,7 +379,7 @@ func doDeleteDNS(network string, noPrompt bool, excludePattern string, includePa
367379
networkSuffix = "." + network + ".algodev.network"
368380
}
369381

370-
for _, recordType := range []string{"A", "CNAME"} {
382+
for _, recordType := range []string{"A", "CNAME", "TXT"} {
371383
records, err := cloudflareDNS.ListDNSRecord(context.Background(), recordType, "", "", "", "", "")
372384
if err != nil {
373385
fmt.Fprintf(os.Stderr, "Error listing DNS '%s' entries: %v\n", recordType, err)
@@ -384,64 +396,89 @@ func doDeleteDNS(network string, noPrompt bool, excludePattern string, includePa
384396

385397
if includeRegex == nil || includeRegex.MatchString(r.Name) {
386398
fmt.Printf("Found DNS '%s' record: %s\n", recordType, r.Name)
387-
idsToDelete[r.ID] = r.Name
399+
idsToDelete = append(idsToDelete, r)
388400
}
389401
}
390402
}
391403
}
392404

393-
if len(idsToDelete) == 0 {
405+
err = checkedDelete(idsToDelete, cloudflareDNS)
406+
if err != nil {
407+
fmt.Fprintf(os.Stderr, "Error deleting: %s\n", err)
408+
}
409+
return true
410+
}
411+
412+
func checkedDelete(toDelete []cloudflare.DNSRecordResponseEntry, cloudflareDNS *cloudflare.DNS) error {
413+
if len(toDelete) == 0 {
394414
fmt.Printf("No DNS/SRV records found\n")
395-
return true
415+
return nil
396416
}
397417

398418
var text string
399419
if !noPrompt {
400420
reader := bufio.NewReader(os.Stdin)
401-
fmt.Printf("Delete these %d entries (type 'yes' to delete)? ", len(idsToDelete))
421+
fmt.Printf("Delete these %d entries (type 'yes' to delete)? ", len(toDelete))
402422
text, _ = reader.ReadString('\n')
403423
text = strings.Replace(text, "\n", "", -1)
404424
} else {
405425
text = "yes"
406426
}
407427

408428
if text == "yes" {
409-
for id, name := range idsToDelete {
410-
fmt.Fprintf(os.Stdout, "Deleting %s\n", name)
411-
err = cloudflareDNS.DeleteDNSRecord(context.Background(), id)
429+
for _, entry := range toDelete {
430+
fmt.Fprintf(os.Stdout, "Deleting %s\n", entry.Name)
431+
err := cloudflareDNS.DeleteDNSRecord(context.Background(), entry.ID)
412432
if err != nil {
413-
fmt.Fprintf(os.Stderr, " !! error deleting %s: %v\n", name, err)
433+
return fmt.Errorf(" !! error deleting %s: %v", entry.Name, err)
414434
}
415435
}
416436
}
417-
return true
437+
return nil
418438
}
419439

420-
func listEntries(listNetwork string, recordType string) {
440+
func getEntries(getNetwork string, recordType string) ([]cloudflare.DNSRecordResponseEntry, error) {
441+
recordTypes := []string{"A", "CNAME", "SRV", "TXT"}
442+
isKnown := false
443+
for _, known := range append(recordTypes, "") {
444+
if recordType == known {
445+
isKnown = true
446+
break
447+
}
448+
}
449+
if !isKnown {
450+
return nil, fmt.Errorf("invalid recordType specified %s", recordType)
451+
}
421452
cfZoneID, cfToken, err := getClouldflareCredentials()
422453
if err != nil {
423-
fmt.Fprintf(os.Stderr, "error getting DNS credentials: %v", err)
424-
return
454+
return nil, fmt.Errorf("error getting DNS credentials: %v", err)
425455
}
426456

427457
cloudflareDNS := cloudflare.NewDNS(cfZoneID, cfToken)
428-
recordTypes := []string{"A", "CNAME", "SRV"}
429458
if recordType != "" {
430459
recordTypes = []string{recordType}
431460
}
461+
var records []cloudflare.DNSRecordResponseEntry
432462
for _, recType := range recordTypes {
433-
records, err := cloudflareDNS.ListDNSRecord(context.Background(), recType, "", "", "", "", "")
463+
records, err = cloudflareDNS.ListDNSRecord(context.Background(), recType, getNetwork, "", "", "", "")
434464
if err != nil {
435-
fmt.Fprintf(os.Stderr, "Error listing DNS entries: %v\n", err)
436-
os.Exit(1)
465+
return nil, fmt.Errorf("error listing DNS entries %w", err)
437466
}
467+
}
468+
return records, nil
469+
}
438470

439-
for _, record := range records {
440-
if strings.HasSuffix(record.Name, listNetwork) {
441-
fmt.Printf("%v\n", record.Name)
442-
}
471+
func listEntries(listNetwork string, recordType string) error {
472+
records, err := getEntries("", recordType)
473+
if err != nil {
474+
return err
475+
}
476+
for _, record := range records {
477+
if strings.HasSuffix(record.Name, listNetwork) {
478+
fmt.Printf("%v\n", record.Name)
443479
}
444480
}
481+
return nil
445482
}
446483

447484
func doExportZone(network string, outputFilename string) bool {

cmd/algons/dnsaddrCmd.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,22 @@
1717
package main
1818

1919
import (
20+
"context"
2021
"fmt"
22+
"os"
2123

24+
"github.com/multiformats/go-multiaddr"
2225
"github.com/spf13/cobra"
2326

2427
"github.com/algorand/go-algorand/network/p2p/dnsaddr"
28+
"github.com/algorand/go-algorand/tools/network/cloudflare"
2529
)
2630

2731
var (
2832
dnsaddrDomain string
2933
secure bool
34+
cmdMultiaddrs []string
35+
nodeSize int
3036
)
3137

3238
func init() {
@@ -35,6 +41,17 @@ func init() {
3541
dnsaddrTreeCmd.Flags().StringVarP(&dnsaddrDomain, "domain", "d", "", "Top level domain")
3642
dnsaddrTreeCmd.MarkFlagRequired("domain")
3743
dnsaddrTreeCmd.Flags().BoolVarP(&secure, "secure", "s", true, "Enable dnssec")
44+
45+
dnsaddrTreeCmd.AddCommand(dnsaddrTreeCreateCmd)
46+
dnsaddrTreeCreateCmd.Flags().StringArrayVarP(&cmdMultiaddrs, "multiaddrs", "m", []string{}, "multiaddrs to add")
47+
dnsaddrTreeCreateCmd.Flags().StringVarP(&dnsaddrDomain, "domain", "d", "", "Top level domain")
48+
dnsaddrTreeCreateCmd.Flags().IntVarP(&nodeSize, "node-size", "n", 50, "Number of multiaddrs entries per TXT record")
49+
dnsaddrTreeCreateCmd.MarkFlagRequired("domain")
50+
dnsaddrTreeCreateCmd.MarkFlagRequired("multiaddrs")
51+
52+
dnsaddrTreeCmd.AddCommand(dnsaddrTreeDeleteCmd)
53+
dnsaddrTreeDeleteCmd.Flags().StringVarP(&dnsaddrDomain, "domain", "d", "", "Top level domain")
54+
dnsaddrTreeDeleteCmd.MarkFlagRequired("domain")
3855
}
3956

4057
var dnsaddrCmd = &cobra.Command{
@@ -63,3 +80,105 @@ var dnsaddrTreeCmd = &cobra.Command{
6380
}
6481
},
6582
}
83+
var dnsaddrTreeDeleteCmd = &cobra.Command{
84+
Use: "delete",
85+
Short: "Recursively resolves and deletes the dnsaddr entries of the given domain",
86+
Long: "Recursively resolves and deletes the dnsaddr entries of the given domain",
87+
Run: func(cmd *cobra.Command, args []string) {
88+
addr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/dnsaddr/%s", dnsaddrDomain))
89+
if err != nil {
90+
fmt.Printf("unable to construct multiaddr for %s : %v\n", dnsaddrDomain, err)
91+
return
92+
}
93+
controller := dnsaddr.NewMultiaddrDNSResolveController(secure, "")
94+
cfZoneID, cfToken, err := getClouldflareCredentials()
95+
if err != nil {
96+
fmt.Fprintf(os.Stderr, "error getting DNS credentials: %v", err)
97+
return
98+
}
99+
cloudflareDNS := cloudflare.NewDNS(cfZoneID, cfToken)
100+
var recordsToDelete []cloudflare.DNSRecordResponseEntry
101+
err = dnsaddr.Iterate(addr, controller, func(entryFrom multiaddr.Multiaddr, entries []multiaddr.Multiaddr) error {
102+
domain, _ := entryFrom.ValueForProtocol(multiaddr.P_DNSADDR)
103+
name := fmt.Sprintf("_dnsaddr.%s", domain)
104+
fmt.Printf("listing records for %s\n", name)
105+
records, err0 := cloudflareDNS.ListDNSRecord(context.Background(), "TXT", name, "", "", "", "")
106+
if err0 != nil {
107+
fmt.Printf("erroring listing dns records for %s %s\n", domain, err)
108+
return err
109+
}
110+
for _, record := range records {
111+
fmt.Printf("found record to delete %s:%s\n", record.Name, record.Content)
112+
recordsToDelete = append(recordsToDelete, record)
113+
}
114+
return nil
115+
})
116+
if err != nil {
117+
fmt.Printf("%s\n", err.Error())
118+
return
119+
}
120+
err = checkedDelete(recordsToDelete, cloudflareDNS)
121+
if err != nil {
122+
fmt.Printf("error deleting records: %s\n", err)
123+
}
124+
},
125+
}
126+
127+
var dnsaddrTreeCreateCmd = &cobra.Command{
128+
Use: "create",
129+
Short: "Creates a tree of entries containing the multiaddrs at the provided root domain",
130+
Long: "Creates a tree of entries containing the multiaddrs at the provided root domain",
131+
Run: func(cmd *cobra.Command, args []string) {
132+
if len(cmdMultiaddrs) == 0 {
133+
fmt.Printf("must provide multiaddrs to put in the DNS records")
134+
return
135+
}
136+
// Generate the dnsaddr entries required for the full tree
137+
var dnsaddrsTo []string
138+
for i := 0; i < len(cmdMultiaddrs)/nodeSize; i++ {
139+
dnsaddrsTo = append(dnsaddrsTo, fmt.Sprintf("%d%s", i, dnsaddrDomain))
140+
}
141+
dnsaddrsFrom := []string{fmt.Sprintf("_dnsaddr.%s", dnsaddrDomain)}
142+
entries, err := getEntries(dnsaddrsFrom[0], "TXT")
143+
if err != nil {
144+
fmt.Printf("failed fetching entries for %s\n", dnsaddrsFrom[0])
145+
os.Exit(1)
146+
}
147+
if len(entries) > 0 {
148+
for _, entry := range entries {
149+
fmt.Printf("found entry %s => %s\n", entry.Name, entry.Content)
150+
}
151+
fmt.Printf("found entries already existing at %s, bailing out\n", dnsaddrsFrom[0])
152+
os.Exit(1)
153+
}
154+
for _, addrTo := range dnsaddrsTo {
155+
dnsaddrsFrom = append(dnsaddrsFrom, fmt.Sprintf("_dnsaddr.%s", addrTo))
156+
}
157+
for _, from := range dnsaddrsFrom {
158+
for i := 0; i < nodeSize; i++ {
159+
if len(dnsaddrsTo) > 0 {
160+
newDnsaddr := fmt.Sprintf("dnsaddr=/dnsaddr/%s", dnsaddrsTo[len(dnsaddrsTo)-1])
161+
fmt.Printf("writing %s => %s\n", from, newDnsaddr)
162+
err := doAddTXT(from, newDnsaddr)
163+
if err != nil {
164+
fmt.Printf("failed writing dnsaddr entry %s: %s\n", newDnsaddr, err)
165+
os.Exit(1)
166+
}
167+
dnsaddrsTo = dnsaddrsTo[:len(dnsaddrsTo)-1]
168+
continue
169+
}
170+
newDnsaddr := fmt.Sprintf("dnsaddr=%s", cmdMultiaddrs[len(cmdMultiaddrs)-1])
171+
fmt.Printf("writing %s => %s\n", from, newDnsaddr)
172+
err := doAddTXT(from, newDnsaddr)
173+
if err != nil {
174+
fmt.Printf("failed writing dns entry %s\n", err)
175+
os.Exit(1)
176+
}
177+
cmdMultiaddrs = cmdMultiaddrs[:len(cmdMultiaddrs)-1]
178+
if len(cmdMultiaddrs) == 0 {
179+
return
180+
}
181+
}
182+
}
183+
},
184+
}

network/p2p/dnsaddr/resolve.go

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,40 +29,54 @@ func isDnsaddr(maddr multiaddr.Multiaddr) bool {
2929
return first.Protocol().Code == multiaddr.P_DNSADDR
3030
}
3131

32-
// MultiaddrsFromResolver attempts to recurse through dnsaddrs starting at domain.
33-
// Any further dnsaddrs will be looked up until all TXT records have been fetched,
34-
// and the full list of resulting Multiaddrs is returned.
35-
// It uses the MultiaddrDNSResolveController to cycle through DNS resolvers on failure.
36-
func MultiaddrsFromResolver(domain string, controller *MultiaddrDNSResolveController) ([]multiaddr.Multiaddr, error) {
32+
// Iterate runs through the resolvable dnsaddrs in the tree using the resolveController and invokes f for each dnsaddr node lookup
33+
func Iterate(initial multiaddr.Multiaddr, controller *MultiaddrDNSResolveController, f func(dnsaddr multiaddr.Multiaddr, entries []multiaddr.Multiaddr) error) error {
3734
resolver := controller.Resolver()
3835
if resolver == nil {
39-
return nil, errors.New("passed controller has no resolvers MultiaddrsFromResolver")
40-
}
41-
dnsaddr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/dnsaddr/%s", domain))
42-
if err != nil {
43-
return nil, fmt.Errorf("unable to construct multiaddr for %s : %v", domain, err)
36+
return errors.New("passed controller has no resolvers Iterate")
4437
}
45-
var resolved []multiaddr.Multiaddr
46-
var toResolve = []multiaddr.Multiaddr{dnsaddr}
38+
var toResolve = []multiaddr.Multiaddr{initial}
4739
for resolver != nil && len(toResolve) > 0 {
4840
curr := toResolve[0]
4941
maddrs, resolveErr := resolver.Resolve(context.Background(), curr)
5042
if resolveErr != nil {
5143
resolver = controller.NextResolver()
5244
// If we errored, and have exhausted all resolvers, just return
5345
if resolver == nil {
54-
return resolved, resolveErr
46+
return resolveErr
5547
}
5648
continue
5749
}
5850
for _, maddr := range maddrs {
5951
if isDnsaddr(maddr) {
6052
toResolve = append(toResolve, maddr)
61-
} else {
62-
resolved = append(resolved, maddr)
6353
}
6454
}
55+
if err := f(curr, maddrs); err != nil {
56+
return err
57+
}
6558
toResolve = toResolve[1:]
6659
}
67-
return resolved, nil
60+
return nil
61+
}
62+
63+
// MultiaddrsFromResolver attempts to recurse through dnsaddrs starting at domain.
64+
// Any further dnsaddrs will be looked up until all TXT records have been fetched,
65+
// and the full list of resulting Multiaddrs is returned.
66+
// It uses the MultiaddrDNSResolveController to cycle through DNS resolvers on failure.
67+
func MultiaddrsFromResolver(domain string, controller *MultiaddrDNSResolveController) ([]multiaddr.Multiaddr, error) {
68+
dnsaddr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/dnsaddr/%s", domain))
69+
if err != nil {
70+
return nil, fmt.Errorf("unable to construct multiaddr for %s : %v", domain, err)
71+
}
72+
var resolved []multiaddr.Multiaddr
73+
err = Iterate(dnsaddr, controller, func(_ multiaddr.Multiaddr, entries []multiaddr.Multiaddr) error {
74+
for _, maddr := range entries {
75+
if !isDnsaddr(maddr) {
76+
resolved = append(resolved, maddr)
77+
}
78+
}
79+
return nil
80+
})
81+
return resolved, err
6882
}

0 commit comments

Comments
 (0)