btcd/discovery.go
David Hill ebc5db2710 Lookup each DNS seed in its own go routine.
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.
2014-07-04 16:27:32 -04:00

137 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
}