bff2ba70fd
This commit introduces package connmgr which contains connection management related functionality. The following is an overview of the features the package provides: - Maintain fixed number of outbound connections - Optional connect-only mode - Retry persistent connections with increasing back-off - Source peers from DNS seeds - Use Tor to resolve DNS - Dynamic ban scores - Test coverage In addition, btcd has been refactored to make use of the new package by extending the connection manager to work with the server to source and maintain peer connections. The following is a broad overview of the changes to integrate the package: - Simplify peer state by removing pending, retry peers - Refactor to remove retries which are now handled by connmgr - Use callback to add addresses sourced from the DNS seed Finally the following connection-related things have been improved as a part of this refactor: - Fixes 100% cpu usage when network is down (#129) - Fixes issues with max peers (#577) - Simplify outbound peer connections management
132 lines
3.3 KiB
Go
132 lines
3.3 KiB
Go
// Copyright (c) 2013-2016 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package connmgr
|
|
|
|
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 indicates an invalid address was
|
|
// returned by the Tor DNS resolver.
|
|
ErrTorInvalidAddressResponse = errors.New("invalid address response")
|
|
|
|
// ErrTorInvalidProxyResponse indicates the Tor proxy returned a
|
|
// response in an unexpected format.
|
|
ErrTorInvalidProxyResponse = errors.New("invalid proxy response")
|
|
|
|
// ErrTorUnrecognizedAuthMethod indicates the authentication method
|
|
// provided is not recognized.
|
|
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 doesn't 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
|
|
}
|