ebc5db2710
By putting each DNS seed in its own go routine, btcd can start connecting to nodes as they are found instead of waiting for all seeds to respond. This significantly speeds up startup time. Additionally, logging was added to show how many addresses were fetched from each seed.
136 lines
3.3 KiB
Go
136 lines
3.3 KiB
Go
// Copyright (c) 2013-2014 Conformal Systems LLC.
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"net"
|
|
)
|
|
|
|
const (
|
|
torSucceeded = 0x00
|
|
torGeneralError = 0x01
|
|
torNotAllowed = 0x02
|
|
torNetUnreachable = 0x03
|
|
torHostUnreachable = 0x04
|
|
torConnectionRefused = 0x05
|
|
torTTLExpired = 0x06
|
|
torCmdNotSupported = 0x07
|
|
torAddrNotSupported = 0x08
|
|
)
|
|
|
|
var (
|
|
ErrTorInvalidAddressResponse = errors.New("invalid address response")
|
|
ErrTorInvalidProxyResponse = errors.New("invalid proxy response")
|
|
ErrTorUnrecognizedAuthMethod = errors.New("invalid proxy authentication method")
|
|
|
|
torStatusErrors = map[byte]error{
|
|
torSucceeded: errors.New("tor succeeded"),
|
|
torGeneralError: errors.New("tor general error"),
|
|
torNotAllowed: errors.New("tor not allowed"),
|
|
torNetUnreachable: errors.New("tor network is unreachable"),
|
|
torHostUnreachable: errors.New("tor host is unreachable"),
|
|
torConnectionRefused: errors.New("tor connection refused"),
|
|
torTTLExpired: errors.New("tor TTL expired"),
|
|
torCmdNotSupported: errors.New("tor command not supported"),
|
|
torAddrNotSupported: errors.New("tor address type not supported"),
|
|
}
|
|
)
|
|
|
|
// torLookupIP uses Tor to resolve DNS via the SOCKS extension they provide for
|
|
// resolution over the Tor network. Tor itself doesnt support ipv6 so this
|
|
// doesn't either.
|
|
func torLookupIP(host, proxy string) ([]net.IP, error) {
|
|
conn, err := net.Dial("tcp", proxy)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer conn.Close()
|
|
|
|
buf := []byte{'\x05', '\x01', '\x00'}
|
|
_, err = conn.Write(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf = make([]byte, 2)
|
|
_, err = conn.Read(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if buf[0] != '\x05' {
|
|
return nil, ErrTorInvalidProxyResponse
|
|
}
|
|
if buf[1] != '\x00' {
|
|
return nil, ErrTorUnrecognizedAuthMethod
|
|
}
|
|
|
|
buf = make([]byte, 7+len(host))
|
|
buf[0] = 5 // protocol version
|
|
buf[1] = '\xF0' // Tor Resolve
|
|
buf[2] = 0 // reserved
|
|
buf[3] = 3 // Tor Resolve
|
|
buf[4] = byte(len(host))
|
|
copy(buf[5:], host)
|
|
buf[5+len(host)] = 0 // Port 0
|
|
|
|
_, err = conn.Write(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
buf = make([]byte, 4)
|
|
_, err = conn.Read(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if buf[0] != 5 {
|
|
return nil, ErrTorInvalidProxyResponse
|
|
}
|
|
if buf[1] != 0 {
|
|
if int(buf[1]) > len(torStatusErrors) {
|
|
err = ErrTorInvalidProxyResponse
|
|
} else {
|
|
err := torStatusErrors[buf[1]]
|
|
if err == nil {
|
|
err = ErrTorInvalidProxyResponse
|
|
}
|
|
}
|
|
return nil, err
|
|
}
|
|
if buf[3] != 1 {
|
|
err := torStatusErrors[torGeneralError]
|
|
return nil, err
|
|
}
|
|
|
|
buf = make([]byte, 4)
|
|
bytes, err := conn.Read(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if bytes != 4 {
|
|
return nil, ErrTorInvalidAddressResponse
|
|
}
|
|
|
|
r := binary.BigEndian.Uint32(buf)
|
|
|
|
addr := make([]net.IP, 1)
|
|
addr[0] = net.IPv4(byte(r>>24), byte(r>>16), byte(r>>8), byte(r))
|
|
|
|
return addr, nil
|
|
}
|
|
|
|
// dnsDiscover looks up the list of peers resolved by DNS for all hosts in
|
|
// seeders. If proxy is not "" then it is used as a tor proxy for the
|
|
// resolution.
|
|
func dnsDiscover(seeder string) ([]net.IP, error) {
|
|
peers, err := btcdLookup(seeder)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return peers, nil
|
|
}
|