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