Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions addrmgr/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,42 @@ const (
// ErrMismatchedAddressType indicates that a network address was expected to
// be a certain type, but the derived type does not match.
ErrMismatchedAddressType = ErrorKind("ErrMismatchedAddressType")

// ErrTorInvalidAddressResponse indicates an invalid address was
// returned by the Tor DNS resolver.
ErrTorInvalidAddressResponse = ErrorKind("ErrTorInvalidAddressResponse")

// ErrTorInvalidProxyResponse indicates the Tor proxy returned a
// response in an unexpected format.
ErrTorInvalidProxyResponse = ErrorKind("ErrTorInvalidProxyResponse")

// ErrTorUnrecognizedAuthMethod indicates the authentication method
// provided is not recognized.
ErrTorUnrecognizedAuthMethod = ErrorKind("ErrTorUnrecognizedAuthMethod")

// ErrTorGeneralError indicates a general tor error.
ErrTorGeneralError = ErrorKind("ErrTorGeneralError")

// ErrTorNotAllowed indicates tor connections are not allowed.
ErrTorNotAllowed = ErrorKind("ErrTorNotAllowed")

// ErrTorNetUnreachable indicates the tor network is unreachable.
ErrTorNetUnreachable = ErrorKind("ErrTorNetUnreachable")

// ErrTorHostUnreachable indicates the tor host is unreachable.
ErrTorHostUnreachable = ErrorKind("ErrTorHostUnreachable")

// ErrTorConnectionRefused indicates the tor connection was refused.
ErrTorConnectionRefused = ErrorKind("ErrTorConnectionRefused")

// ErrTorTTLExpired indicates the tor request Time-To-Live (TTL) expired.
ErrTorTTLExpired = ErrorKind("ErrTorTTLExpired")

// ErrTorCmdNotSupported indicates the tor command is not supported.
ErrTorCmdNotSupported = ErrorKind("ErrTorCmdNotSupported")

// ErrTorAddrNotSupported indicates the tor address type is not supported.
ErrTorAddrNotSupported = ErrorKind("ErrTorAddrNotSupported")
)

// Error satisfies the error interface and prints human-readable errors.
Expand Down
2 changes: 1 addition & 1 deletion addrmgr/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ require (
github.com/decred/dcrd/crypto/rand v1.0.1
github.com/decred/dcrd/wire v1.7.1
github.com/decred/slog v1.2.0
golang.org/x/crypto v0.33.0
)

require (
github.com/decred/dcrd/crypto/blake256 v1.1.0 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/sys v0.30.0 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
)
137 changes: 137 additions & 0 deletions addrmgr/tordns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Copyright (c) 2015-2026 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package addrmgr

import (
"context"
"encoding/binary"
"net"
)

const (
torGeneralError = 0x01
torNotAllowed = 0x02
torNetUnreachable = 0x03
torHostUnreachable = 0x04
torConnectionRefused = 0x05
torTTLExpired = 0x06
torCmdNotSupported = 0x07
torAddrNotSupported = 0x08

torATypeIPv4 = 1
torATypeDomainName = 3
torATypeIPv6 = 4

torCmdResolve = 240
)

var (
torStatusErrors = map[byte]error{
torGeneralError: makeError(ErrTorGeneralError, "tor general error"),
torNotAllowed: makeError(ErrTorNotAllowed, "tor not allowed"),
torNetUnreachable: makeError(ErrTorNetUnreachable, "tor network is unreachable"),
torHostUnreachable: makeError(ErrTorHostUnreachable, "tor host is unreachable"),
torConnectionRefused: makeError(ErrTorConnectionRefused, "tor connection refused"),
torTTLExpired: makeError(ErrTorTTLExpired, "tor TTL expired"),
torCmdNotSupported: makeError(ErrTorCmdNotSupported, "tor command not supported"),
torAddrNotSupported: makeError(ErrTorAddrNotSupported, "tor address type not supported"),
}
)

// TorLookupIP uses Tor to resolve DNS via the passed SOCKS proxy.
func TorLookupIP(ctx context.Context, host, proxy string) ([]net.IP, error) {
var dialer net.Dialer
conn, err := dialer.DialContext(ctx, "tcp", proxy)
if err != nil {
return nil, err
}
defer conn.Close()

buf := []byte{0x05, 0x01, 0x00}
_, 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] != 0x05 {
const str = "invalid SOCKS proxy version"
return nil, makeError(ErrTorInvalidProxyResponse, str)
}
if buf[1] != 0x00 {
const str = "invalid proxy authentication method"
return nil, makeError(ErrTorUnrecognizedAuthMethod, str)
}

buf = make([]byte, 7+len(host))
buf[0] = 5 // socks protocol version
buf[1] = torCmdResolve
buf[2] = 0 // reserved
buf[3] = torATypeDomainName
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 {
const str = "invalid SOCKS proxy version"
return nil, makeError(ErrTorInvalidProxyResponse, str)
}
if buf[1] != 0 {
err, exists := torStatusErrors[buf[1]]
if !exists {
const str = "invalid SOCKS proxy version"
err = makeError(ErrTorInvalidProxyResponse, str)
}
return nil, err
}
if buf[3] != torATypeIPv4 && buf[3] != torATypeIPv6 {
const str = "invalid IP address"
return nil, makeError(ErrTorInvalidAddressResponse, str)
}

var reply [32 + 2]byte
replyLen, err := conn.Read(reply[:])
if err != nil {
return nil, err
}

var addr net.IP
switch buf[3] {
case torATypeIPv4:
if replyLen != 4+2 {
const str = "invalid IPV4 address"
return nil, makeError(ErrTorInvalidAddressResponse, str)
}
r := binary.BigEndian.Uint32(reply[0:4])
addr = net.IPv4(byte(r>>24), byte(r>>16),
byte(r>>8), byte(r))
case torATypeIPv6:
if replyLen <= 4+2 {
const str = "invalid IPV6 address"
return nil, makeError(ErrTorInvalidAddressResponse, str)
}
addr = net.IP(reply[0 : replyLen-2])
default:
const str = "unknown address type"
return nil, makeError(ErrTorInvalidAddressResponse, str)
}

return []net.IP{addr}, nil
}
6 changes: 3 additions & 3 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"strings"
"time"

"github.com/decred/dcrd/connmgr/v3"
"github.com/decred/dcrd/addrmgr/v4"
"github.com/decred/dcrd/database/v3"
_ "github.com/decred/dcrd/database/v3/ffldb"
"github.com/decred/dcrd/dcrutil/v4"
Expand Down Expand Up @@ -1272,7 +1272,7 @@ func loadConfig(appName string) (*config, []string, error) {
cfg.dial = proxy.DialContext
if !cfg.NoOnion {
cfg.lookup = func(host string) ([]net.IP, error) {
return connmgr.TorLookupIP(context.Background(), host, cfg.Proxy)
return addrmgr.TorLookupIP(context.Background(), host, cfg.Proxy)
}
}
}
Expand Down Expand Up @@ -1312,7 +1312,7 @@ func loadConfig(appName string) (*config, []string, error) {
return proxy.DialContext(ctx, a, b)
}
cfg.onionlookup = func(host string) ([]net.IP, error) {
return connmgr.TorLookupIP(context.Background(), host, cfg.OnionProxy)
return addrmgr.TorLookupIP(context.Background(), host, cfg.OnionProxy)
}
} else {
cfg.oniondial = cfg.dial
Expand Down