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: multiaddr dns bootstrapping utils #5575

Merged
merged 27 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
27 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
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/algons/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (

func init() {
rootCmd.AddCommand(dnsCmd)
rootCmd.AddCommand(dnsaddrCmd)
}

var rootCmd = &cobra.Command{
Expand Down
67 changes: 67 additions & 0 deletions cmd/algons/dnsaddrCmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (C) 2019-2023 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.

package main

import (
"fmt"

"github.com/spf13/cobra"

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

var (
dnsaddrDomain string
secure bool
)

func init() {
dnsaddrCmd.AddCommand(dnsaddrTreeCmd)

dnsaddrTreeCmd.Flags().StringVarP(&dnsaddrDomain, "domain", "d", "", "Top level domain")
dnsaddrTreeCmd.MarkFlagRequired("domain")
dnsaddrTreeCmd.Flags().BoolVarP(&secure, "secure", "s", true, "Enable dnssec")
}

var dnsaddrCmd = &cobra.Command{
Use: "dnsaddr",
Short: "Get, Set, and List Dnsaddr entries",
Long: "Get, Set, and List Dnsaddr entries",
Run: func(cmd *cobra.Command, args []string) {
// Fall back
cmd.HelpFunc()(cmd, args)
},
}

var dnsaddrTreeCmd = &cobra.Command{
Use: "tree",
Short: "Recursively resolves and lists the dnsaddr entries of the given domain",
Long: "Recursively resolves and lists the dnsaddr entries of the given domain",
Run: func(cmd *cobra.Command, args []string) {
controller := dnsaddr.NewMultiaddrDNSResolveController(network.NewResolveController(secure, "", log.Base()))
addrs, err := dnsaddr.MultiaddrsFromResolver(dnsaddrDomain, controller)
if err != nil {
fmt.Printf("%s\n", err.Error())
return
}
for _, addr := range addrs {
fmt.Printf("%s\n", addr.String())
}
},
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ require (
github.com/mattn/go-sqlite3 v1.10.0
github.com/miekg/dns v1.1.55
github.com/multiformats/go-multiaddr v0.10.1
github.com/multiformats/go-multiaddr-dns v0.3.1
github.com/olivere/elastic v6.2.14+incompatible
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.3.0
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -522,8 +522,11 @@ github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYg
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4=
github.com/multiformats/go-multiaddr v0.10.1 h1:HghtFrWyZEPrpTvgAMFJi6gFdgHfs2cb0pyfDsk+lqU=
github.com/multiformats/go-multiaddr v0.10.1/go.mod h1:jLEZsA61rwWNZQTHHnqq2HNa+4os/Hz54eqiRnsRqYQ=
github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A=
github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk=
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
Expand All @@ -535,6 +538,7 @@ github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7B
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo=
github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q=
github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
Expand Down
68 changes: 68 additions & 0 deletions network/p2p/dnsaddr/resolve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (C) 2019-2023 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.

package dnsaddr

import (
"context"
"errors"
"fmt"

"github.com/multiformats/go-multiaddr"
)

func isDnsaddr(maddr multiaddr.Multiaddr) bool {
first, _ := multiaddr.SplitFirst(maddr)
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) {
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)
}
var resolved []multiaddr.Multiaddr
var toResolve = []multiaddr.Multiaddr{dnsaddr}
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
}
continue

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
Eric-Warehime marked this conversation as resolved.
Show resolved Hide resolved
}
for _, maddr := range maddrs {
if isDnsaddr(maddr) {
toResolve = append(toResolve, maddr)
} else {
resolved = append(resolved, maddr)
}
}
toResolve = toResolve[1:]
}
return resolved, nil
}
58 changes: 58 additions & 0 deletions network/p2p/dnsaddr/resolveController.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (C) 2019-2023 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.

package dnsaddr

import (
madns "github.com/multiformats/go-multiaddr-dns"

"github.com/algorand/go-algorand/tools/network"
)

// MultiaddrDNSResolveController returns a madns.Resolver, cycling through underlying net.Resolvers
type MultiaddrDNSResolveController struct {
resolver *madns.Resolver
nextResolvers []func() *madns.Resolver
controller network.ResolveController
}
Eric-Warehime marked this conversation as resolved.
Show resolved Hide resolved

// NewMultiaddrDNSResolveController constructs a MultiaddrDNSResolveController
func NewMultiaddrDNSResolveController(controller network.ResolveController) *MultiaddrDNSResolveController {
return &MultiaddrDNSResolveController{
resolver: nil,
nextResolvers: []func() *madns.Resolver{controller.SystemDnsaddrResolver, controller.FallbackDnsaddrResolver, controller.DefaultDnsaddrResolver},
controller: controller,
}
}

// NextResolver applies the nextResolvers functions in order and returns the most recent result
func (c *MultiaddrDNSResolveController) NextResolver() *madns.Resolver {
if len(c.nextResolvers) == 0 {
c.resolver = nil
} else {
c.resolver = c.nextResolvers[0]()
c.nextResolvers = c.nextResolvers[1:]
}
return c.resolver
}

// Resolver returns the current resolver, invokes NextResolver if the resolver is nil
func (c *MultiaddrDNSResolveController) Resolver() *madns.Resolver {
if c.resolver == nil {
c.resolver = c.NextResolver()
}
return c.resolver
}
44 changes: 44 additions & 0 deletions network/p2p/dnsaddr/resolveController_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (C) 2019-2023 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.

package dnsaddr

import (
"testing"

"github.com/stretchr/testify/assert"

log "github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/test/partitiontest"
"github.com/algorand/go-algorand/tools/network"
)

func TestDnsAddrResolveController(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

controller := network.NewResolveController(true, "127.0.0.1", log.Base())
dnsaddrCont := NewMultiaddrDNSResolveController(controller)

// Assert that the dnsaddr resolver cycles through the dns resolvers properly
assert.Equal(t, controller.SystemDnsaddrResolver(), dnsaddrCont.Resolver())
assert.Equal(t, controller.FallbackDnsaddrResolver(), dnsaddrCont.NextResolver())
assert.Equal(t, controller.DefaultDnsaddrResolver(), dnsaddrCont.NextResolver())
// It should return nil once all the resolvers have been tried
assert.Nil(t, dnsaddrCont.NextResolver())
assert.Nil(t, dnsaddrCont.NextResolver())

}
113 changes: 113 additions & 0 deletions network/p2p/dnsaddr/resolve_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright (C) 2019-2023 Algorand, Inc.
// This file is part of go-algorand
//
// go-algorand is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// go-algorand is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with go-algorand. If not, see <https://www.gnu.org/licenses/>.

package dnsaddr

import (
"context"
"fmt"
"net"
"testing"

"github.com/multiformats/go-multiaddr"
madns "github.com/multiformats/go-multiaddr-dns"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

log "github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/test/partitiontest"
"github.com/algorand/go-algorand/tools/network"
)

func TestIsDnsaddr(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

testcases := []struct {
name string
addr string
expected bool
}{
{name: "DnsAddr", addr: "/dnsaddr/foobar.com", expected: true},
{name: "DnsAddrWithPeerId", addr: "/dnsaddr/foobar.com/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", expected: true},
{name: "DnsAddrWithIPPeerId", addr: "/dnsaddr/foobar.com/ip4/127.0.0.1/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", expected: true},
{name: "Dns4Addr", addr: "/dns4/foobar.com/", expected: false},
{name: "Dns6Addr", addr: "/dns6/foobar.com/", expected: false},
{name: "Dns4AddrWithPeerId", addr: "/dns4/foobar.com/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", expected: false},
}
for _, testcase := range testcases {
t.Run(testcase.name, func(t *testing.T) {
maddr, err := multiaddr.NewMultiaddr(testcase.addr)
require.NoError(t, err)
require.Equal(t, testcase.expected, isDnsaddr(maddr))
})
}
}

func TestMultiaddrsFromResolver(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

controller := network.NewResolveController(false, "", log.Base())
dnsaddrCont := NewMultiaddrDNSResolveController(controller)

// Fail on bad dnsaddr domain
maddrs, err := MultiaddrsFromResolver("/bogus/foobar", dnsaddrCont)
assert.Empty(t, maddrs)
assert.ErrorContains(t, err, fmt.Sprintf("unable to construct multiaddr for %s", "/bogus/foobar"))

// Success on a dnsaddr that needs to resolve recursively
maddrs, err = MultiaddrsFromResolver("bootstrap.libp2p.io", dnsaddrCont)
assert.NoError(t, err)
assert.NotEmpty(t, maddrs)
// bootstrap.libp2p.io's dnsaddr record contains 4 more dnsaddrs to resolve
assert.Greater(t, len(maddrs), 4)
}

type failureResolver struct{}

func (f *failureResolver) LookupIPAddr(context.Context, string) ([]net.IPAddr, error) {
return nil, fmt.Errorf("always errors")
}
func (f *failureResolver) LookupTXT(context.Context, string) ([]string, error) {
return nil, fmt.Errorf("always errors")
}

func TestMultiaddrsFromResolverDnsFailure(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

dnsaddrCont := &MultiaddrDNSResolveController{
resolver: nil,
nextResolvers: nil,
}

// Fail on no resolver
maddrs, err := MultiaddrsFromResolver("", dnsaddrCont)
assert.Empty(t, maddrs)
assert.ErrorContains(t, err, fmt.Sprintf("passed controller has no resolvers MultiaddrsFromResolver"))

resolver, _ := madns.NewResolver(madns.WithDefaultResolver(&failureResolver{}))
dnsaddrCont = &MultiaddrDNSResolveController{
resolver: resolver,
nextResolvers: nil,
controller: network.ResolveController{},
}
// Fail on resolver error
maddrs, err = MultiaddrsFromResolver("bootstrap.libp2p.io", dnsaddrCont)
assert.Empty(t, maddrs)
assert.ErrorContains(t, err, "always errors")
}