Merge branch 'detports' of openprivacy/asaur into master
This commit is contained in:
commit
ff89f06c93
|
@ -3,3 +3,4 @@
|
|||
examples/basic/basic
|
||||
examples/listener/listener
|
||||
examples/dialer/dialer
|
||||
.idea/*
|
||||
|
|
95
cmd_onion.go
95
cmd_onion.go
|
@ -14,7 +14,11 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"errors"
|
||||
"github.com/yawning/bulb/utils/pkcs1"
|
||||
"golang.org/x/net/proxy"
|
||||
"log"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// OnionInfo is the result of the AddOnion command.
|
||||
|
@ -39,6 +43,7 @@ type OnionPortSpec struct {
|
|||
|
||||
// NewOnionConfig is a configuration for NewOnion command.
|
||||
type NewOnionConfig struct {
|
||||
Onion string
|
||||
PortSpecs []OnionPortSpec
|
||||
PrivateKey crypto.PrivateKey
|
||||
DiscardPK bool
|
||||
|
@ -49,7 +54,7 @@ type NewOnionConfig struct {
|
|||
|
||||
// NewOnion issues an ADD_ONION command using configuration config and
|
||||
// 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"
|
||||
var err error
|
||||
|
||||
|
@ -109,9 +114,40 @@ func (c *Conn) NewOnion(config *NewOnionConfig) (*OnionInfo, error) {
|
|||
flagsStr = " Flags="
|
||||
flagsStr += strings.Join(flags, ",")
|
||||
}
|
||||
|
||||
request := fmt.Sprintf("ADD_ONION %s:%s%s%s", hsKeyType, hsKeyStr, portStr, flagsStr)
|
||||
resp, err := c.Request(request)
|
||||
if err != nil {
|
||||
if err == nil && !dontCheckDescriptor {
|
||||
pi, _ := c.ProtocolInfo()
|
||||
torversion := strings.Split(pi.TorVersion, ".") //eg: 0.3.4.8 or 0.3.5.1-alpha
|
||||
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("this onion service will not be visible to others until the counters are synced.\n")
|
||||
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")
|
||||
}
|
||||
}
|
||||
} 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
|
||||
}
|
||||
|
||||
|
@ -153,11 +189,6 @@ func (c *Conn) NewOnion(config *NewOnionConfig) (*OnionInfo, error) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if serviceID == "" {
|
||||
// This should *NEVER* happen, since the command succeded, and the spec
|
||||
// guarantees that this will always be present.
|
||||
return nil, newProtocolError("failed to determine service ID")
|
||||
}
|
||||
|
||||
oi := new(OnionInfo)
|
||||
oi.RawResponse = resp
|
||||
|
@ -176,7 +207,7 @@ func (c *Conn) AddOnion(ports []OnionPortSpec, key crypto.PrivateKey, oneshot bo
|
|||
cfg.PrivateKey = key
|
||||
}
|
||||
cfg.DiscardPK = oneshot
|
||||
return c.NewOnion(cfg)
|
||||
return c.NewOnion(cfg, false)
|
||||
}
|
||||
|
||||
// DeleteOnion issues a DEL_ONION command and returns the parsed response.
|
||||
|
@ -184,3 +215,51 @@ func (c *Conn) DeleteOnion(serviceID string) error {
|
|||
_, err := c.Request("DEL_ONION %s", serviceID)
|
||||
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
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ func main() {
|
|||
log.Printf("Expected ID: %v", id)
|
||||
|
||||
cfg := &bulb.NewOnionConfig{
|
||||
DiscardPK: true,
|
||||
DiscardPK: true,
|
||||
PrivateKey: pk,
|
||||
}
|
||||
l, err := c.NewListener(cfg, 80)
|
||||
|
|
29
listener.go
29
listener.go
|
@ -9,7 +9,11 @@ package bulb
|
|||
|
||||
import (
|
||||
"crypto"
|
||||
goodrand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
@ -55,7 +59,13 @@ func (l *onionListener) Addr() net.Addr {
|
|||
// All of virtual ports specified in vports will be mapped to the port to which
|
||||
// the underlying TCP listener binded. PortSpecs in config will be ignored since
|
||||
// there is only one mapping for a vports set is possible.
|
||||
|
||||
// [DEPRECATED] generate a random port to listen on (this is for backwards compatibility)
|
||||
func (c *Conn) NewListener(config *NewOnionConfig, vports ...uint16) (net.Listener, error) {
|
||||
return c.RecoverListener(config, "", vports...)
|
||||
}
|
||||
|
||||
func (c *Conn) RecoverListener(config *NewOnionConfig, onion string, vports ...uint16) (net.Listener, error) {
|
||||
var cfg NewOnionConfig
|
||||
if config == nil {
|
||||
cfg = NewOnionConfig{
|
||||
|
@ -65,7 +75,20 @@ func (c *Conn) NewListener(config *NewOnionConfig, vports ...uint16) (net.Listen
|
|||
cfg = *config
|
||||
}
|
||||
|
||||
const loopbackAddr = "127.0.0.1:0"
|
||||
var port int
|
||||
if onion == "" { // generate a random port for ephemeral programs
|
||||
seedbytes := make([]byte, 2)
|
||||
goodrand.Read(seedbytes)
|
||||
port = int(binary.LittleEndian.Uint16(seedbytes))
|
||||
} else { // generate a deterministic port for resumptive programs
|
||||
seedbytes := sha3.New224().Sum([]byte(onion))
|
||||
port = int(seedbytes[0]) + (int(seedbytes[1]) << 8)
|
||||
}
|
||||
if port < 1024 { // this is not uniformly random, but we don't need it to be
|
||||
port += 1024
|
||||
}
|
||||
log.Printf("using local port: %d\n", port)
|
||||
var loopbackAddr = "127.0.0.1:" + strconv.Itoa(port)
|
||||
|
||||
// Listen on the loopback interface.
|
||||
tcpListener, err := net.Listen("tcp4", loopbackAddr)
|
||||
|
@ -79,7 +102,7 @@ func (c *Conn) NewListener(config *NewOnionConfig, vports ...uint16) (net.Listen
|
|||
}
|
||||
|
||||
if len(vports) < 1 {
|
||||
return nil, newProtocolError("no virual ports specified")
|
||||
return nil, newProtocolError("no virtual ports specified")
|
||||
}
|
||||
targetPortStr := strconv.FormatUint((uint64)(tAddr.Port), 10)
|
||||
var portSpecs []OnionPortSpec
|
||||
|
@ -91,7 +114,7 @@ func (c *Conn) NewListener(config *NewOnionConfig, vports ...uint16) (net.Listen
|
|||
}
|
||||
cfg.PortSpecs = portSpecs
|
||||
// Create the onion.
|
||||
oi, err := c.NewOnion(&cfg)
|
||||
oi, err := c.NewOnion(&cfg, false)
|
||||
if err != nil {
|
||||
tcpListener.Close()
|
||||
return nil, err
|
||||
|
|
|
@ -9,8 +9,8 @@ package bulb
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"net/textproto"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// The various control port StatusCode constants.
|
||||
|
|
|
@ -18,19 +18,19 @@ import (
|
|||
// SplitQuoted splits s by sep if it is found outside substring
|
||||
// quoted by quote.
|
||||
func SplitQuoted(s string, quote, sep rune) (splitted []string) {
|
||||
quoteFlag := false
|
||||
quoteFlag := false
|
||||
NewSubstring:
|
||||
for i, c := range s {
|
||||
if c == quote {
|
||||
quoteFlag = !quoteFlag
|
||||
}
|
||||
if c == sep && !quoteFlag {
|
||||
splitted = append(splitted, s[:i])
|
||||
s = s[i+1:]
|
||||
goto NewSubstring
|
||||
}
|
||||
}
|
||||
return append(splitted, s)
|
||||
for i, c := range s {
|
||||
if c == quote {
|
||||
quoteFlag = !quoteFlag
|
||||
}
|
||||
if c == sep && !quoteFlag {
|
||||
splitted = append(splitted, s[:i])
|
||||
s = s[i+1:]
|
||||
goto NewSubstring
|
||||
}
|
||||
}
|
||||
return append(splitted, s)
|
||||
}
|
||||
|
||||
// ParseControlPortString parses a string representation of a control port
|
||||
|
|
Reference in New Issue