Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

p2p: Add delete/create to algons dnsaddr command #5631

Merged
merged 33 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
53e8c94
Initial
Eric-Warehime Jul 11, 2023
f2363a4
Initial dnsaddr commit
Eric-Warehime Jul 17, 2023
55be21b
Add dnsaddr cmd to algons
Eric-Warehime Jul 18, 2023
c8d9910
Update algons dns check for txt records
Eric-Warehime Jul 18, 2023
c8b8f73
import order
Eric-Warehime Jul 18, 2023
fc461f7
remove dns cmd changes
Eric-Warehime Jul 18, 2023
6a976bc
Rename, add comments
Eric-Warehime Jul 18, 2023
c84298b
Fix lint errors
Eric-Warehime Jul 18, 2023
f515757
make tidy
Eric-Warehime Jul 18, 2023
6f8a0d9
Add configs for PeerID, P2PEnable
Eric-Warehime Jul 19, 2023
709c43f
Add peerID fetching/generation
Eric-Warehime Jul 20, 2023
c62972c
Fix lint
Eric-Warehime Jul 20, 2023
04a25e2
Add license, partitiontest, parallel
Eric-Warehime Jul 20, 2023
df5b0de
Update error logic in dnsaddr resolve
Eric-Warehime Jul 21, 2023
6c92169
Minor change
Eric-Warehime Jul 21, 2023
8b7ddd3
Change fallback dns resolver address
Eric-Warehime Jul 26, 2023
0ed14e4
Update config version to 29
Eric-Warehime Jul 26, 2023
c9f56eb
Update config comment to mention ed25519 limitation
Eric-Warehime Jul 26, 2023
ad89a95
Run gci
Eric-Warehime Jul 26, 2023
ff9e968
Use io util FileExists
Eric-Warehime Jul 26, 2023
89efbcc
Add testdata config
Eric-Warehime Jul 26, 2023
9efdd65
Remove v28 config changes
Eric-Warehime Jul 26, 2023
ad0a831
Merge remote-tracking branch 'upstream/master' into add-peerID
Eric-Warehime Jul 28, 2023
e8ef66b
Merge remote-tracking branch 'upstream/master' into multiaddr-dns
Eric-Warehime Jul 28, 2023
40d4b94
Merge branch 'add-peerID' into multiaddr-dns
Eric-Warehime Jul 28, 2023
5838813
Omit fallback controller if not specified
Eric-Warehime Jul 31, 2023
a6e67ba
Merge remote-tracking branch 'upstream/master' into multiaddr-dns
Eric-Warehime Jul 31, 2023
887c4b0
Initial commit of algons dnsaddr tree create|delete
Eric-Warehime Aug 2, 2023
60c402b
Merge remote-tracking branch 'upstream/master' into dnsaddr-add-del
Eric-Warehime Aug 2, 2023
f122b68
Fix lint
Eric-Warehime Aug 2, 2023
c2655e0
Move nodeSize to arg, provide high default
Eric-Warehime Aug 8, 2023
70cf8ff
PR comments
Eric-Warehime Aug 11, 2023
95efdb7
Small refactoring
Eric-Warehime Aug 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 64 additions & 27 deletions cmd/algons/dnsCmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,9 @@ var listRecordsCmd = &cobra.Command{
Long: "List the A/SRV entries of the given network",
Run: func(cmd *cobra.Command, args []string) {
recordType = strings.ToUpper(recordType)
if recordType == "" || recordType == "A" || recordType == "CNAME" || recordType == "SRV" {
listEntries(listNetwork, recordType)
} else {
fmt.Fprintf(os.Stderr, "Invalid recordType specified.\n")
err := listEntries(listNetwork, recordType)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
},
Expand Down Expand Up @@ -181,6 +180,19 @@ var exportCmd = &cobra.Command{
},
}

func doAddTXT(from string, to string) error {
cfZoneID, cfToken, err := getClouldflareCredentials()
if err != nil {
return fmt.Errorf("error getting DNS credentials: %v", err)
}

cloudflareDNS := cloudflare.NewDNS(cfZoneID, cfToken)

const priority = 1
const proxied = false
return cloudflareDNS.CreateDNSRecord(context.Background(), "TXT", from, to, cloudflare.AutomaticTTL, priority, proxied)
Eric-Warehime marked this conversation as resolved.
Show resolved Hide resolved
}

func doAddDNS(from string, to string) (err error) {
cfZoneID, cfToken, err := getClouldflareCredentials()
if err != nil {
Expand Down Expand Up @@ -315,7 +327,7 @@ func doDeleteDNS(network string, noPrompt bool, excludePattern string, includePa

cloudflareDNS := cloudflare.NewDNS(cfZoneID, cfToken)

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

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

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

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

if includeRegex == nil || includeRegex.MatchString(r.Name) {
fmt.Printf("Found DNS '%s' record: %s\n", recordType, r.Name)
idsToDelete[r.ID] = r.Name
idsToDelete = append(idsToDelete, r)
}
}
}
}

if len(idsToDelete) == 0 {
err = checkedDelete(idsToDelete, cloudflareDNS)
if err != nil {
fmt.Fprintf(os.Stderr, "Error deleting: %s\n", err)
}
return true
}

func checkedDelete(toDelete []cloudflare.DNSRecordResponseEntry, cloudflareDNS *cloudflare.DNS) error {
if len(toDelete) == 0 {
fmt.Printf("No DNS/SRV records found\n")
return true
return nil
}

var text string
if !noPrompt {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("Delete these %d entries (type 'yes' to delete)? ", len(idsToDelete))
fmt.Printf("Delete these %d entries (type 'yes' to delete)? ", len(toDelete))
text, _ = reader.ReadString('\n')
text = strings.Replace(text, "\n", "", -1)
} else {
text = "yes"
}

if text == "yes" {
for id, name := range idsToDelete {
fmt.Fprintf(os.Stdout, "Deleting %s\n", name)
err = cloudflareDNS.DeleteDNSRecord(context.Background(), id)
for _, entry := range toDelete {
fmt.Fprintf(os.Stdout, "Deleting %s\n", entry.Name)
err := cloudflareDNS.DeleteDNSRecord(context.Background(), entry.ID)
if err != nil {
fmt.Fprintf(os.Stderr, " !! error deleting %s: %v\n", name, err)
return fmt.Errorf(" !! error deleting %s: %v", entry.Name, err)
}
}
}
return true
return nil
}

func listEntries(listNetwork string, recordType string) {
func getEntries(getNetwork string, recordType string) ([]cloudflare.DNSRecordResponseEntry, error) {
recordTypes := []string{"A", "CNAME", "SRV", "TXT"}
isKnown := false
for _, known := range append(recordTypes, "") {
if recordType == known {
isKnown = true
break
}
}
if !isKnown {
return nil, fmt.Errorf("invalid recordType specified %s", recordType)
}
cfZoneID, cfToken, err := getClouldflareCredentials()
if err != nil {
fmt.Fprintf(os.Stderr, "error getting DNS credentials: %v", err)
return
return nil, fmt.Errorf("error getting DNS credentials: %v", err)
}

cloudflareDNS := cloudflare.NewDNS(cfZoneID, cfToken)
recordTypes := []string{"A", "CNAME", "SRV"}
if recordType != "" {
recordTypes = []string{recordType}
}
var records []cloudflare.DNSRecordResponseEntry
for _, recType := range recordTypes {
records, err := cloudflareDNS.ListDNSRecord(context.Background(), recType, "", "", "", "", "")
records, err = cloudflareDNS.ListDNSRecord(context.Background(), recType, getNetwork, "", "", "", "")
if err != nil {
fmt.Fprintf(os.Stderr, "Error listing DNS entries: %v\n", err)
os.Exit(1)
return nil, fmt.Errorf("error listing DNS entries %w", err)
}
}
return records, nil
}

for _, record := range records {
if strings.HasSuffix(record.Name, listNetwork) {
fmt.Printf("%v\n", record.Name)
}
func listEntries(listNetwork string, recordType string) error {
records, err := getEntries("", recordType)
if err != nil {
return err
}
for _, record := range records {
if strings.HasSuffix(record.Name, listNetwork) {
fmt.Printf("%v\n", record.Name)
}
}
return nil
}

func doExportZone(network string, outputFilename string) bool {
Expand Down
119 changes: 119 additions & 0 deletions cmd/algons/dnsaddrCmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,22 @@
package main

import (
"context"
"fmt"
"os"

"github.com/multiformats/go-multiaddr"
"github.com/spf13/cobra"

"github.com/algorand/go-algorand/network/p2p/dnsaddr"
"github.com/algorand/go-algorand/tools/network/cloudflare"
)

var (
dnsaddrDomain string
secure bool
cmdMultiaddrs []string
nodeSize int
)

func init() {
Expand All @@ -35,6 +41,17 @@ func init() {
dnsaddrTreeCmd.Flags().StringVarP(&dnsaddrDomain, "domain", "d", "", "Top level domain")
dnsaddrTreeCmd.MarkFlagRequired("domain")
dnsaddrTreeCmd.Flags().BoolVarP(&secure, "secure", "s", true, "Enable dnssec")

dnsaddrTreeCmd.AddCommand(dnsaddrTreeCreateCmd)
dnsaddrTreeCreateCmd.Flags().StringArrayVarP(&cmdMultiaddrs, "multiaddrs", "m", []string{}, "multiaddrs to add")
dnsaddrTreeCreateCmd.Flags().StringVarP(&dnsaddrDomain, "domain", "d", "", "Top level domain")
dnsaddrTreeCreateCmd.Flags().IntVarP(&nodeSize, "node-size", "n", 50, "Number of multiaddrs entries per TXT record")
dnsaddrTreeCreateCmd.MarkFlagRequired("domain")
dnsaddrTreeCreateCmd.MarkFlagRequired("multiaddrs")

dnsaddrTreeCmd.AddCommand(dnsaddrTreeDeleteCmd)
dnsaddrTreeDeleteCmd.Flags().StringVarP(&dnsaddrDomain, "domain", "d", "", "Top level domain")
dnsaddrTreeDeleteCmd.MarkFlagRequired("domain")
}

var dnsaddrCmd = &cobra.Command{
Expand Down Expand Up @@ -63,3 +80,105 @@ var dnsaddrTreeCmd = &cobra.Command{
}
},
}
var dnsaddrTreeDeleteCmd = &cobra.Command{
Use: "delete",
Short: "Recursively resolves and deletes the dnsaddr entries of the given domain",
Long: "Recursively resolves and deletes the dnsaddr entries of the given domain",
Run: func(cmd *cobra.Command, args []string) {
addr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/dnsaddr/%s", dnsaddrDomain))
if err != nil {
fmt.Printf("unable to construct multiaddr for %s : %v\n", dnsaddrDomain, err)
return
}
controller := dnsaddr.NewMultiaddrDNSResolveController(secure, "")
cfZoneID, cfToken, err := getClouldflareCredentials()
if err != nil {
fmt.Fprintf(os.Stderr, "error getting DNS credentials: %v", err)
return
}
cloudflareDNS := cloudflare.NewDNS(cfZoneID, cfToken)
var recordsToDelete []cloudflare.DNSRecordResponseEntry
err = dnsaddr.Iterate(addr, controller, func(entryFrom multiaddr.Multiaddr, entries []multiaddr.Multiaddr) error {
domain, _ := entryFrom.ValueForProtocol(multiaddr.P_DNSADDR)
name := fmt.Sprintf("_dnsaddr.%s", domain)
fmt.Printf("listing records for %s\n", name)
records, err0 := cloudflareDNS.ListDNSRecord(context.Background(), "TXT", name, "", "", "", "")
if err0 != nil {
fmt.Printf("erroring listing dns records for %s %s\n", domain, err)
return err
}
for _, record := range records {
fmt.Printf("found record to delete %s:%s\n", record.Name, record.Content)
recordsToDelete = append(recordsToDelete, record)
}
return nil
})
if err != nil {
fmt.Printf("%s\n", err.Error())
return
}
err = checkedDelete(recordsToDelete, cloudflareDNS)
Eric-Warehime marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
fmt.Printf("error deleting records: %s\n", err)
}
},
}

var dnsaddrTreeCreateCmd = &cobra.Command{
Use: "create",
Eric-Warehime marked this conversation as resolved.
Show resolved Hide resolved
Short: "Creates a tree of entries containing the multiaddrs at the provided root domain",
Long: "Creates a tree of entries containing the multiaddrs at the provided root domain",
Run: func(cmd *cobra.Command, args []string) {
if len(cmdMultiaddrs) == 0 {
fmt.Printf("must provide multiaddrs to put in the DNS records")
return
}
// Generate the dnsaddr entries required for the full tree
var dnsaddrsTo []string
for i := 0; i < len(cmdMultiaddrs)/nodeSize; i++ {
Eric-Warehime marked this conversation as resolved.
Show resolved Hide resolved
dnsaddrsTo = append(dnsaddrsTo, fmt.Sprintf("%d%s", i, dnsaddrDomain))
}
dnsaddrsFrom := []string{fmt.Sprintf("_dnsaddr.%s", dnsaddrDomain)}
entries, err := getEntries(dnsaddrsFrom[0], "TXT")
if err != nil {
fmt.Printf("failed fetching entries for %s\n", dnsaddrsFrom[0])
os.Exit(1)
}
if len(entries) > 0 {
for _, entry := range entries {
fmt.Printf("found entry %s => %s\n", entry.Name, entry.Content)
}
fmt.Printf("found entries already existing at %s, bailing out\n", dnsaddrsFrom[0])
os.Exit(1)
}
for _, addrTo := range dnsaddrsTo {
dnsaddrsFrom = append(dnsaddrsFrom, fmt.Sprintf("_dnsaddr.%s", addrTo))
}
for _, from := range dnsaddrsFrom {
for i := 0; i < nodeSize; i++ {
if len(dnsaddrsTo) > 0 {
newDnsaddr := fmt.Sprintf("dnsaddr=/dnsaddr/%s", dnsaddrsTo[len(dnsaddrsTo)-1])
fmt.Printf("writing %s => %s\n", from, newDnsaddr)
Eric-Warehime marked this conversation as resolved.
Show resolved Hide resolved
err := doAddTXT(from, newDnsaddr)
if err != nil {
fmt.Printf("failed writing dnsaddr entry %s: %s\n", newDnsaddr, err)
os.Exit(1)
}
dnsaddrsTo = dnsaddrsTo[:len(dnsaddrsTo)-1]
continue
}
newDnsaddr := fmt.Sprintf("dnsaddr=%s", cmdMultiaddrs[len(cmdMultiaddrs)-1])
fmt.Printf("writing %s => %s\n", from, newDnsaddr)
err := doAddTXT(from, newDnsaddr)
if err != nil {
fmt.Printf("failed writing dns entry %s\n", err)
os.Exit(1)
}
cmdMultiaddrs = cmdMultiaddrs[:len(cmdMultiaddrs)-1]
if len(cmdMultiaddrs) == 0 {
return
}
}
}
},
}
46 changes: 30 additions & 16 deletions network/p2p/dnsaddr/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,40 +29,54 @@
return first.Protocol().Code == multiaddr.P_DNSADDR
}

// MultiaddrsFromResolver attempts to recurse through dnsaddrs starting at domain.
// Any further dnsaddrs will be looked up until all TXT records have been fetched,
// and the full list of resulting Multiaddrs is returned.
// It uses the MultiaddrDNSResolveController to cycle through DNS resolvers on failure.
func MultiaddrsFromResolver(domain string, controller *MultiaddrDNSResolveController) ([]multiaddr.Multiaddr, error) {
// Iterate runs through the resolvable dnsaddrs in the tree using the resolveController and invokes f for each dnsaddr node lookup
func Iterate(initial multiaddr.Multiaddr, controller *MultiaddrDNSResolveController, f func(dnsaddr multiaddr.Multiaddr, entries []multiaddr.Multiaddr) error) error {
Eric-Warehime marked this conversation as resolved.
Show resolved Hide resolved
resolver := controller.Resolver()
if resolver == nil {
return nil, errors.New("passed controller has no resolvers MultiaddrsFromResolver")
}
dnsaddr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/dnsaddr/%s", domain))
if err != nil {
return nil, fmt.Errorf("unable to construct multiaddr for %s : %v", domain, err)
return errors.New("passed controller has no resolvers Iterate")
}
var resolved []multiaddr.Multiaddr
var toResolve = []multiaddr.Multiaddr{dnsaddr}
var toResolve = []multiaddr.Multiaddr{initial}
for resolver != nil && len(toResolve) > 0 {
curr := toResolve[0]
maddrs, resolveErr := resolver.Resolve(context.Background(), curr)
if resolveErr != nil {
resolver = controller.NextResolver()
// If we errored, and have exhausted all resolvers, just return
if resolver == nil {
return resolved, resolveErr
return resolveErr
}
continue
}
for _, maddr := range maddrs {
if isDnsaddr(maddr) {
toResolve = append(toResolve, maddr)
} else {
resolved = append(resolved, maddr)
}
}
if err := f(curr, maddrs); err != nil {
return err

Check warning on line 56 in network/p2p/dnsaddr/resolve.go

View check run for this annotation

Codecov / codecov/patch

network/p2p/dnsaddr/resolve.go#L56

Added line #L56 was not covered by tests
}
toResolve = toResolve[1:]
}
return resolved, nil
return nil
}

// MultiaddrsFromResolver attempts to recurse through dnsaddrs starting at domain.
// Any further dnsaddrs will be looked up until all TXT records have been fetched,
// and the full list of resulting Multiaddrs is returned.
// It uses the MultiaddrDNSResolveController to cycle through DNS resolvers on failure.
func MultiaddrsFromResolver(domain string, controller *MultiaddrDNSResolveController) ([]multiaddr.Multiaddr, error) {
dnsaddr, err := multiaddr.NewMultiaddr(fmt.Sprintf("/dnsaddr/%s", domain))
if err != nil {
return nil, fmt.Errorf("unable to construct multiaddr for %s : %v", domain, err)
}
var resolved []multiaddr.Multiaddr
err = Iterate(dnsaddr, controller, func(_ multiaddr.Multiaddr, entries []multiaddr.Multiaddr) error {
for _, maddr := range entries {
if !isDnsaddr(maddr) {
resolved = append(resolved, maddr)
}
}
return nil
})
return resolved, err
}
Loading