dnsseed: connect ServeDNS to the Zcash seeder
This commit is contained in:
parent
f1e7d75e28
commit
77728106cd
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/coredns/coredns/plugin"
|
||||
"github.com/coredns/coredns/request"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/zcashfoundation/dnsseeder/zcash"
|
||||
)
|
||||
|
@ -11,7 +12,7 @@ import (
|
|||
// ZcashSeeder discovers IP addresses by asking Zcash peers for them.
|
||||
type ZcashSeeder struct {
|
||||
Next plugin.Handler
|
||||
|
||||
Zones []string
|
||||
seeder *zcash.Seeder
|
||||
}
|
||||
|
||||
|
@ -26,5 +27,46 @@ func (zs ZcashSeeder) Ready() bool {
|
|||
}
|
||||
|
||||
func (zs ZcashSeeder) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) {
|
||||
// Check if it's a question for us
|
||||
state := request.Request{W: w, Req: r}
|
||||
zone := plugin.Zones(zs.Zones).Matches(state.Name())
|
||||
if zone == "" {
|
||||
return plugin.NextOrFailure(zs.Name(), zs.Next, ctx, w, r)
|
||||
}
|
||||
|
||||
peerIPs := zs.seeder.Addresses(25)
|
||||
|
||||
a := new(dns.Msg)
|
||||
a.SetReply(r)
|
||||
a.Authoritative = true
|
||||
|
||||
a.Extra = make([]dns.RR, 0, len(peerIPs))
|
||||
|
||||
for i := 0; i < len(peerIPs); i++ {
|
||||
var rr dns.RR
|
||||
|
||||
ip := peerIPs[i]
|
||||
|
||||
switch state.QType() {
|
||||
case dns.TypeA:
|
||||
rr = new(dns.A)
|
||||
rr.(*dns.A).Hdr = dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeA, Class: state.QClass()}
|
||||
rr.(*dns.A).A = ip.To4()
|
||||
case dns.TypeAAAA:
|
||||
if ip.To4() != nil {
|
||||
rr = new(dns.AAAA)
|
||||
rr.(*dns.AAAA).Hdr = dns.RR_Header{Name: state.QName(), Rrtype: dns.TypeAAAA, Class: state.QClass()}
|
||||
rr.(*dns.AAAA).AAAA = ip
|
||||
}
|
||||
default:
|
||||
return dns.RcodeNotImplemented, nil
|
||||
}
|
||||
|
||||
// TODO: why don't we offer SRV records? Zcash has a configurable port.
|
||||
|
||||
a.Extra = append(a.Extra, rr)
|
||||
}
|
||||
|
||||
w.WriteMsg(a)
|
||||
return dns.RcodeSuccess, nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package dnsseed
|
|||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/caddyserver/caddy"
|
||||
"github.com/coredns/coredns/core/dnsserver"
|
||||
|
@ -12,18 +13,21 @@ import (
|
|||
"github.com/zcashfoundation/dnsseeder/zcash/network"
|
||||
)
|
||||
|
||||
var log = clog.NewWithPlugin("dnsseed")
|
||||
var (
|
||||
log = clog.NewWithPlugin("dnsseed")
|
||||
updateInterval = 15 * time.Minute
|
||||
)
|
||||
|
||||
func init() { plugin.Register("dnsseed", setup) }
|
||||
|
||||
// setup is the function that gets called when the config parser see the token "dnsseed". Setup is responsible
|
||||
// for parsing any extra options the example plugin may have. The first token this function sees is "dnsseed".
|
||||
func setup(c *caddy.Controller) error {
|
||||
var networkArg, hostArg string
|
||||
var zoneArg, networkArg, hostArg string
|
||||
|
||||
c.Next() // Ignore "dnsseed" and give us the next token.
|
||||
|
||||
if !c.Args(&networkArg, &hostArg) {
|
||||
if !c.Args(&zoneArg, &networkArg, &hostArg) {
|
||||
return plugin.Error("dnsseed", c.ArgErr())
|
||||
}
|
||||
|
||||
|
@ -49,17 +53,37 @@ func setup(c *caddy.Controller) error {
|
|||
return plugin.Error("dnsseed", err)
|
||||
}
|
||||
|
||||
// Connect to the bootstrap peer
|
||||
err = seeder.Connect(address, port)
|
||||
if err != nil {
|
||||
return plugin.Error("dnsseed", err)
|
||||
}
|
||||
|
||||
// TODO: make initial addr request
|
||||
// TODO: begin update timer
|
||||
// Send the initial request for more addresses; spawns goroutines to process the responses.
|
||||
// Ready() will flip to true once we've received and confirmed at least 10 peers.
|
||||
seeder.RequestAddresses()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-time.After(updateInterval):
|
||||
seeder.RequestAddresses()
|
||||
err := seeder.WaitForAddresses(10, 30*time.Second)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to refresh addresses: %v", err)
|
||||
}
|
||||
// XXX: If we wanted to crawl independently, this would be the place.
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Add the Plugin to CoreDNS, so Servers can use it in their plugin chain.
|
||||
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
||||
return ZcashSeeder{Next: next, seeder: seeder}
|
||||
return ZcashSeeder{
|
||||
Next: next,
|
||||
Zones: []string{zoneArg},
|
||||
seeder: seeder,
|
||||
}
|
||||
})
|
||||
|
||||
// All OK, return a nil error.
|
||||
|
|
|
@ -9,12 +9,12 @@ import (
|
|||
|
||||
// TestSetup tests the various things that should be parsed by setup.
|
||||
func TestSetup(t *testing.T) {
|
||||
c := caddy.NewTestController("dns", "dnsseed mainnet 127.0.0.1:8233")
|
||||
c := caddy.NewTestController("dns", "dnsseed mainnet.seeder.yolo.money mainnet 127.0.0.1:8233")
|
||||
if err := setup(c); err != nil {
|
||||
if strings.Contains(err.Error(), "connection refused") {
|
||||
// No local peer running
|
||||
// TODO: mock a local peer, which will be easier with an API rework in the zcash package
|
||||
t.Skip()
|
||||
t.SkipNow()
|
||||
}
|
||||
t.Fatalf("Expected no errors, but got: %v", err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package zcash
|
||||
|
||||
import (
|
||||
mrand "math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
@ -170,7 +171,30 @@ func (bk *AddressBook) waitForAddresses(n int, done chan struct{}) {
|
|||
return
|
||||
}
|
||||
|
||||
// GetShuffledAddressList returns a slice of n valid addresses in random order.
|
||||
// func (bk *AddressBook) GetShuffledAddressList(n int) []*Address {
|
||||
// GetAddressList returns a slice of n valid addresses in random order.
|
||||
// If there aren't enough known addresses, it returns as many as we have.
|
||||
func (bk *AddressBook) shuffleAddressList(n int) []net.IP {
|
||||
bk.addrState.RLock()
|
||||
defer bk.addrState.RUnlock()
|
||||
|
||||
// }
|
||||
resp := make([]net.IP, 0, len(bk.addrs))
|
||||
|
||||
for _, v := range bk.addrs {
|
||||
if v.blacklisted {
|
||||
continue
|
||||
}
|
||||
|
||||
resp = append(resp, v.netaddr.IP)
|
||||
}
|
||||
|
||||
mrand.Seed(time.Now().UnixNano())
|
||||
mrand.Shuffle(len(resp), func(i, j int) {
|
||||
resp[i], resp[j] = resp[j], resp[i]
|
||||
})
|
||||
|
||||
if len(resp) > n {
|
||||
return resp[:n]
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
|
|
@ -358,3 +358,8 @@ func (s *Seeder) WaitForAddresses(n int, timeout time.Duration) error {
|
|||
func (s *Seeder) Ready() bool {
|
||||
return s.WaitForAddresses(minimumReadyAddresses, 1*time.Millisecond) == nil
|
||||
}
|
||||
|
||||
// Addresses returns a slice of n addresses or as many as we have if it's less than that.
|
||||
func (s *Seeder) Addresses(n int) []net.IP {
|
||||
return s.addrBook.shuffleAddressList(n)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue