Merge branch 'detports' of openprivacy/asaur into master

This commit is contained in:
Sarah Jamie Lewis 2018-10-09 21:36:52 +00:00 committed by Gogs
commit ff89f06c93
6 changed files with 128 additions and 25 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
examples/basic/basic
examples/listener/listener
examples/dialer/dialer
.idea/*

View File

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

View File

@ -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)

View File

@ -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

View File

@ -9,8 +9,8 @@ package bulb
import (
"fmt"
"strings"
"net/textproto"
"strings"
)
// The various control port StatusCode constants.

View File

@ -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