check current onion descriptors on old versions of tor to see if they're out-of-sync
This commit is contained in:
parent
9c1c5ef98a
commit
6258396dc7
91
cmd_onion.go
91
cmd_onion.go
|
@ -14,7 +14,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"errors"
|
||||||
"github.com/yawning/bulb/utils/pkcs1"
|
"github.com/yawning/bulb/utils/pkcs1"
|
||||||
|
"golang.org/x/net/proxy"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OnionInfo is the result of the AddOnion command.
|
// OnionInfo is the result of the AddOnion command.
|
||||||
|
@ -39,6 +43,7 @@ type OnionPortSpec struct {
|
||||||
|
|
||||||
// NewOnionConfig is a configuration for NewOnion command.
|
// NewOnionConfig is a configuration for NewOnion command.
|
||||||
type NewOnionConfig struct {
|
type NewOnionConfig struct {
|
||||||
|
Onion string
|
||||||
PortSpecs []OnionPortSpec
|
PortSpecs []OnionPortSpec
|
||||||
PrivateKey crypto.PrivateKey
|
PrivateKey crypto.PrivateKey
|
||||||
DiscardPK bool
|
DiscardPK bool
|
||||||
|
@ -49,7 +54,7 @@ type NewOnionConfig struct {
|
||||||
|
|
||||||
// NewOnion issues an ADD_ONION command using configuration config and
|
// NewOnion issues an ADD_ONION command using configuration config and
|
||||||
// returns the parsed response.
|
// returns the parsed response.
|
||||||
func (c *Conn) NewOnion(config *NewOnionConfig) (*OnionInfo, error) {
|
func (c *Conn) NewOnion(config *NewOnionConfig, dontCheckDescriptor bool) (*OnionInfo, error) {
|
||||||
const keyTypeRSA = "RSA1024"
|
const keyTypeRSA = "RSA1024"
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
@ -109,9 +114,41 @@ func (c *Conn) NewOnion(config *NewOnionConfig) (*OnionInfo, error) {
|
||||||
flagsStr = " Flags="
|
flagsStr = " Flags="
|
||||||
flagsStr += strings.Join(flags, ",")
|
flagsStr += strings.Join(flags, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
request := fmt.Sprintf("ADD_ONION %s:%s%s%s", hsKeyType, hsKeyStr, portStr, flagsStr)
|
request := fmt.Sprintf("ADD_ONION %s:%s%s%s", hsKeyType, hsKeyStr, portStr, flagsStr)
|
||||||
resp, err := c.Request(request)
|
resp, err := c.Request(request)
|
||||||
if err != nil && fmt.Sprintf("%v", err) != "550 Unspecified Tor error: Onion address collision" {
|
if err == nil && !dontCheckDescriptor {
|
||||||
|
pi, _ := c.ProtocolInfo()
|
||||||
|
// 0.3.5.1
|
||||||
|
torversion := strings.Split(pi.TorVersion, ".")
|
||||||
|
tva, _ := strconv.Atoi(torversion[0])
|
||||||
|
tvb, _ := strconv.Atoi(torversion[1])
|
||||||
|
tvc, _ := strconv.Atoi(torversion[2])
|
||||||
|
if tva == 0 && (tvb < 3 || (tvb == 3 && tvc < 5)) {
|
||||||
|
// here we need to check if the descriptor upload succeeded
|
||||||
|
log.Println("running a descriptor check because you are on tor version < 0.3.5.1-alpha. this will take a little while but only happens once per tor process--onion service pair")
|
||||||
|
HSFetch(config.Onion)
|
||||||
|
crc, err := c.GetClientRevisionCounter(config.Onion)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("%v\n", err)
|
||||||
|
}
|
||||||
|
src, err := c.GetServiceRevisionCounter(config.Onion)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("%v\n", err)
|
||||||
|
}
|
||||||
|
if crc > src {
|
||||||
|
log.Printf("uh oh: your tor process is using a service descriptor revision counter of %d\n", src)
|
||||||
|
log.Printf("whereas the HSDirs have revision %d. this is a bug in tor < 0.3.5.1-alpha\n", crc)
|
||||||
|
log.Printf("your have two options to fix it:\n")
|
||||||
|
log.Printf("- upgrade to tor >= 0.3.5.1-alpha [recommended]\n")
|
||||||
|
log.Printf("- run this listener %d more times [not a great idea but it'll work]\n", crc-src)
|
||||||
|
log.Printf("the revision counter is only reset when the tor process restarts, so try not to do that in order to prevent this problem from reoccurring\n")
|
||||||
|
return nil, errors.New("client descriptor is newer than service descriptor")
|
||||||
|
}
|
||||||
|
fmt.Printf("crc: %v src: %v\n", crc, src)
|
||||||
|
}
|
||||||
|
} else if fmt.Sprintf("%v", err) != "550 Unspecified Tor error: Onion address collision" {
|
||||||
|
// (don't worry if there is an address collision -- it's a good thing and means we're using the cached circuits)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,7 +208,7 @@ func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bo
|
||||||
cfg.PrivateKey = key
|
cfg.PrivateKey = key
|
||||||
}
|
}
|
||||||
cfg.DiscardPK = oneshot
|
cfg.DiscardPK = oneshot
|
||||||
return c.NewOnion(cfg)
|
return c.NewOnion(cfg, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteOnion issues a DEL_ONION command and returns the parsed response.
|
// DeleteOnion issues a DEL_ONION command and returns the parsed response.
|
||||||
|
@ -179,3 +216,51 @@ func (c *Conn) DeleteOnion(serviceID string) error {
|
||||||
_, err := c.Request("DEL_ONION %s", serviceID)
|
_, err := c.Request("DEL_ONION %s", serviceID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// run HSFETCH for an onion, in order to put its onion service descriptor into the client cache
|
||||||
|
// unfortunately we don't actually do this, because HSFETCH doesn't support v3 yet
|
||||||
|
// instead we make a shortlived connection to it, which has a side effect of putting the descriptor into cache
|
||||||
|
// see https://trac.torproject.org/projects/tor/ticket/25417 for details
|
||||||
|
func HSFetch(onion string) error {
|
||||||
|
torDialer, err := proxy.SOCKS5("tcp", "127.0.0.1:9050", nil, proxy.Direct)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
conn, err := torDialer.Dial("tcp", onion+".onion:9878")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
conn.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) GetClientRevisionCounter(onion string) (uint64, error) {
|
||||||
|
return c.getRevisionCounterHelper(onion, "client")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) GetServiceRevisionCounter(onion string) (uint64, error) {
|
||||||
|
return c.getRevisionCounterHelper(onion, "service")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) getRevisionCounterHelper(onion string, cachetype string) (uint64, error) {
|
||||||
|
_, err := c.conn.Cmd("GETINFO hs/%v/desc/id/%v", cachetype, onion)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var rc uint64
|
||||||
|
reterr := errors.New("could not find revision counter in onion descriptor")
|
||||||
|
for {
|
||||||
|
line, err := c.conn.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(line, "250 OK") {
|
||||||
|
return rc, reterr
|
||||||
|
} else if strings.HasPrefix(line, "revision-counter ") {
|
||||||
|
rc, reterr = strconv.ParseUint(line[17:], 10, 64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc, reterr
|
||||||
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ func (c *Conn) RecoverListener(config *NewOnionConfig, onion string, vports ...u
|
||||||
}
|
}
|
||||||
cfg.PortSpecs = portSpecs
|
cfg.PortSpecs = portSpecs
|
||||||
// Create the onion.
|
// Create the onion.
|
||||||
oi, err := c.NewOnion(&cfg)
|
oi, err := c.NewOnion(&cfg, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tcpListener.Close()
|
tcpListener.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
Reference in New Issue