tor/controller: add tor version number check
In this commit, we add a check for the Tor server's version number to ensure it supports creating v3 onion services through its control port.
This commit is contained in:
parent
e38174c7ce
commit
9ae0ac53a2
@ -11,6 +11,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
)
|
||||
@ -30,6 +31,11 @@ const (
|
||||
// ProtocolInfoVersion is the `protocolinfo` version currently supported
|
||||
// by the Tor server.
|
||||
ProtocolInfoVersion = 1
|
||||
|
||||
// MinTorVersion is the minimum supported version that the Tor server
|
||||
// must be running on. This is needed in order to create v3 onion
|
||||
// services through Tor's control port.
|
||||
MinTorVersion = "0.3.3.6"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -72,6 +78,9 @@ type Controller struct {
|
||||
// controlAddr is the host:port the Tor server is listening locally for
|
||||
// controller connections on.
|
||||
controlAddr string
|
||||
|
||||
// version is the current version of the Tor server.
|
||||
version string
|
||||
}
|
||||
|
||||
// NewController returns a new Tor controller that will be able to interact with
|
||||
@ -251,15 +260,19 @@ func (c *Controller) authenticate() error {
|
||||
}
|
||||
|
||||
// getAuthCookie retrieves the authentication cookie in bytes from the Tor
|
||||
// server. Cookie authentication must be enabled for this to work.
|
||||
// server. Cookie authentication must be enabled for this to work. The boolean
|
||||
func (c *Controller) getAuthCookie() ([]byte, error) {
|
||||
// Retrieve the authentication methods currently supported by the Tor
|
||||
// server.
|
||||
authMethods, cookieFilePath, _, err := c.ProtocolInfo()
|
||||
authMethods, cookieFilePath, version, err := c.ProtocolInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// With the version retrieved, we'll cache it now in case it needs to be
|
||||
// used later on.
|
||||
c.version = version
|
||||
|
||||
// Ensure that the Tor server supports the SAFECOOKIE authentication
|
||||
// method.
|
||||
safeCookieSupport := false
|
||||
@ -294,6 +307,47 @@ func computeHMAC256(key, message []byte) []byte {
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
// supportsV3 is a helper function that parses the current version of the Tor
|
||||
// server and determines whether it supports creationg v3 onion services through
|
||||
// Tor's control port. The version string should be of the format:
|
||||
// major.minor.revision.build
|
||||
func supportsV3(version string) error {
|
||||
// We'll split the minimum Tor version that's supported and the given
|
||||
// version in order to individually compare each number.
|
||||
requiredParts := strings.Split(MinTorVersion, ".")
|
||||
parts := strings.Split(version, ".")
|
||||
if len(parts) != 4 {
|
||||
return errors.New("version string is not of the format " +
|
||||
"major.minor.revision.build")
|
||||
}
|
||||
|
||||
// It's possible that the build number (the last part of the version
|
||||
// string) includes a pre-release string, e.g. rc, beta, etc., so we'll
|
||||
// parse that as well.
|
||||
build := strings.Split(parts[len(parts)-1], "-")
|
||||
parts[len(parts)-1] = build[0]
|
||||
|
||||
// Convert them each number from its string representation to integers
|
||||
// and check that they respect the minimum version.
|
||||
for i := range parts {
|
||||
n, err := strconv.Atoi(parts[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requiredN, err := strconv.Atoi(requiredParts[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n < requiredN {
|
||||
return fmt.Errorf("version %v below minimum version "+
|
||||
"supported %v", version, MinTorVersion)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProtocolInfo returns the different authentication methods supported by the
|
||||
// Tor server and the version of the Tor server.
|
||||
func (c *Controller) ProtocolInfo() ([]string, string, string, error) {
|
||||
@ -375,6 +429,15 @@ type AddOnionConfig struct {
|
||||
// created, the new onion service will remain active until the connection
|
||||
// between the controller and the Tor server is closed.
|
||||
func (c *Controller) AddOnion(cfg AddOnionConfig) (*OnionAddr, error) {
|
||||
// Before sending the request to create an onion service to the Tor
|
||||
// server, we'll make sure that it supports V3 onion services if that
|
||||
// was the type requested.
|
||||
if cfg.Type == V3 {
|
||||
if err := supportsV3(c.version); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// We'll start off by checking if the file containing the private key
|
||||
// exists. If it does not, then we should request the server to create
|
||||
// a new onion service and return its private key. Otherwise, we'll
|
||||
|
68
tor/controller_test.go
Normal file
68
tor/controller_test.go
Normal file
@ -0,0 +1,68 @@
|
||||
package tor
|
||||
|
||||
import "testing"
|
||||
|
||||
// TestParseTorVersion is a series of tests for different version strings that
|
||||
// check the correctness of determining whether they support creating v3 onion
|
||||
// services through Tor control's port.
|
||||
func TestParseTorVersion(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
version string
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
version: "0.3.3.6",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
version: "0.3.3.7",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
version: "0.3.4.6",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
version: "0.4.3.6",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
version: "1.3.3.6",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
version: "0.3.3.6-rc",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
version: "0.3.3.7-rc",
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
version: "0.3.3.5-rc",
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
version: "0.3.3.5",
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
version: "0.3.2.6",
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
version: "0.1.3.6",
|
||||
valid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
err := supportsV3(test.version)
|
||||
if test.valid != (err == nil) {
|
||||
t.Fatalf("test %d with version string %v failed: %v", i,
|
||||
test.version, err)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user