Go Language dns seeder for Bitcoin based networks
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

223 lines
6.0 KiB

/*
*/
package main
import (
"flag"
"fmt"
"log"
"os"
"os/signal"
"strings"
"sync"
"sync/atomic"
"syscall"
"time"
"github.com/miekg/dns"
)
9 years ago
// NodeCounts holds various statistics about the running system for use in html templates
type NodeCounts struct {
NdStatus []uint32 // number of nodes at each of the 4 statuses - RG, CG, WG, NG
NdStarts []uint32 // number of crawles started last startcrawlers run
DNSCounts []uint32 // number of dns requests for each dns type - dnsV4Std, dnsV4Non, dnsV6Std, dnsV6Non
mtx sync.RWMutex // protect the structures
}
// configData holds information on the application
type configData struct {
uptime time.Time // application start time
port string // port for the dns server to listen on
http string // port for the web server to listen on
version string // application version
verbose bool // verbose output cmdline option
debug bool // debug cmdline option
stats bool // stats cmdline option
seeders map[string]*dnsseeder // holds a pointer to all the current seeders
smtx sync.RWMutex // protect the seeders map
order []string // the order of loading the netfiles so we can display in this order
dns map[string][]dns.RR // holds details of all the currently served dns records
dnsmtx sync.RWMutex // protect the dns map
dnsUnknown uint64 // the number of dns requests for we are not configured to handle
}
var config configData
var netfile string
func main() {
var j bool
// FIXME - update with git hash during build
config.version = "0.8.0"
config.uptime = time.Now()
flag.StringVar(&netfile, "netfile", "", "List of json config files to load")
flag.StringVar(&config.port, "p", "8053", "DNS Port to listen on")
flag.StringVar(&config.http, "w", "", "Web Port to listen on. No port specified & no web server running")
flag.BoolVar(&j, "j", false, "Write network template file (dnsseeder.json) and exit")
flag.BoolVar(&config.verbose, "v", false, "Display verbose output")
flag.BoolVar(&config.debug, "d", false, "Display debug output")
flag.BoolVar(&config.stats, "s", false, "Display stats output")
flag.Parse()
if j == true {
createNetFile()
fmt.Printf("Template file has been created\n")
os.Exit(0)
}
// configure the network options so we can start crawling
netwFiles := strings.Split(netfile, ",")
if len(netwFiles) == 0 {
fmt.Printf("Error - No filenames specified. Please add -net=<file[, file2]> to load these files\n")
os.Exit(1)
}
config.seeders = make(map[string]*dnsseeder)
config.dns = make(map[string][]dns.RR)
config.order = []string{}
for _, nwFile := range netwFiles {
nnw, err := loadNetwork(nwFile)
if err != nil {
fmt.Printf("Error loading data from netfile %s - %v\n", nwFile, err)
os.Exit(1)
}
if nnw != nil {
// FIXME - lock this
config.seeders[nnw.name] = nnw
config.order = append(config.order, nnw.name)
}
}
if config.debug == true {
config.verbose = true
config.stats = true
}
if config.verbose == true {
config.stats = true
}
for _, v := range config.seeders {
log.Printf("status - system is configured for network: %s\n", v.name)
}
if config.verbose == false {
log.Printf("status - Running in quiet mode with limited output produced\n")
}
// start the web interface if we want it running
if config.http != "" {
go startHTTP(config.http)
}
// start dns server
dns.HandleFunc(".", handleDNS)
go serve("udp", config.port)
//go serve("tcp", config.port)
// seed the seeder with some ip addresses
for _, s := range config.seeders {
s.initCrawlers()
s.startCrawlers()
}
sig := make(chan os.Signal)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
// extract good dns records from all nodes on regular basis
dnsChan := time.NewTicker(time.Second * dnsDelay).C
// used to start crawlers on a regular basis
crawlChan := time.NewTicker(time.Second * crawlDelay).C
// used to remove old statusNG nodes that have reached fail count
auditChan := time.NewTicker(time.Minute * auditDelay).C
dowhile := true
for dowhile == true {
select {
case <-sig:
dowhile = false
case <-auditChan:
if config.debug {
log.Printf("debug - Audit nodes timer triggered\n")
}
for _, s := range config.seeders {
// FIXME goroutines for these
s.auditNodes()
}
case <-dnsChan:
if config.debug {
log.Printf("debug - DNS - Updating latest ip addresses timer triggered\n")
}
for _, s := range config.seeders {
s.loadDNS()
}
case <-crawlChan:
if config.debug {
log.Printf("debug - Start crawlers timer triggered\n")
}
for _, s := range config.seeders {
s.startCrawlers()
}
}
}
// FIXME - call dns server.Shutdown()
fmt.Printf("\nProgram exiting. Bye\n")
}
// updateNodeCounts runs in a goroutine and updates the global stats with the latest
// counts from a startCrawlers run
func updateNodeCounts(s *dnsseeder, status, total, started uint32) {
// update the stats counters
s.counts.mtx.Lock()
s.counts.NdStatus[status] = total
s.counts.NdStarts[status] = started
s.counts.mtx.Unlock()
}
// updateDNSCounts runs in a goroutine and updates the global stats for the number of DNS requests
func updateDNSCounts(name, qtype string) {
var ndType uint32
var counted bool
nonstd := strings.HasPrefix(name, "nonstd.")
switch qtype {
case "A":
if nonstd {
ndType = dnsV4Non
} else {
ndType = dnsV4Std
}
case "AAAA":
if nonstd {
ndType = dnsV6Non
} else {
ndType = dnsV6Std
}
default:
ndType = dnsInvalid
}
// for DNS requests we do not have a reference to a seeder so we have to find it
for _, s := range config.seeders {
s.counts.mtx.Lock()
if name == s.dnsHost+"." || name == "nonstd."+s.dnsHost+"." {
s.counts.DNSCounts[ndType]++
counted = true
}
s.counts.mtx.Unlock()
}
if counted != true {
atomic.AddUint64(&config.dnsUnknown, 1)
}
}
/*
*/