GitLab is used only for code review, issue tracking and project management. Canonical locations for source code are still https://gitweb.torproject.org/ https://git.torproject.org/ and git-rw.torproject.org.

tor.go 8.2 KB
Newer Older
1 2 3 4
package main

import (
	"context"
5
	"errors"
6
	"fmt"
7
	"io"
8 9 10 11 12
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
13
	"strings"
14
	"sync"
15
	"time"
16 17

	"github.com/yawning/bulb"
18 19 20
)

const (
21 22 23 24 25 26 27 28 29
	// We're using the following default bridges to bootstrap our Tor instance.
	// Once it's bootstrapped, we no longer need them.
	DefaultBridge1 = "obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1"
	DefaultBridge2 = "obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0"
	DefaultBridge3 = "obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0"
	// The amount of time we give Tor to test a batch of bridges.
	TorTestTimeout = time.Minute
	// The maximum amount of bridges per batch.
	MaxBridgesPerReq = 100
30 31
)

32 33 34 35 36
// getBridgeIdentifier turns the given bridgeLine into a canonical identifier
// that we use to look for relevant ORCONN events.  If the given bridge line
// contains a fingerprint, the function returns $FINGERPRINT.  If it doesn't,
// the function returns the address:port tuple of the given bridge line.
func getBridgeIdentifier(bridgeLine string) (string, error) {
37

38 39 40
	re := regexp.MustCompile(`([A-F0-9]{40})`)
	if result := string(re.Find([]byte(bridgeLine))); result != "" {
		return "$" + result, nil
41
	}
42

43 44
	if result := string(AddrPortBridgeLine.Find([]byte(bridgeLine))); result != "" {
		return result, nil
45
	}
46

47
	return "", errors.New("could not extract bridge identifier")
48 49 50 51 52 53
}

// getDomainSocketPath takes as input the path to our data directory and
// returns the path to the domain socket for tor's control port.
func getDomainSocketPath(dataDir string) string {
	return fmt.Sprintf("%s/control-socket", dataDir)
54 55
}

56 57
// writeConfigToTorrc writes a Tor config file to the given file handle.
func writeConfigToTorrc(tmpFh io.Writer, dataDir string) error {
58 59

	_, err := fmt.Fprintf(tmpFh, "UseBridges 1\n"+
60
		"ControlPort unix:%s\n"+
61
		"SocksPort auto\n"+
62
		"SafeLogging 0\n"+
63
		"Log info file %s/tor.log\n"+
64
		"DataDirectory %s\n"+
65 66 67 68 69
		"ClientTransportPlugin obfs2,obfs3,obfs4,scramblesuit exec /usr/bin/obfs4proxy\n"+
		"Bridge %s\n"+
		"Bridge %s\n"+
		"Bridge %s\n", getDomainSocketPath(dataDir), dataDir, dataDir,
		DefaultBridge1, DefaultBridge2, DefaultBridge3)
70 71 72 73

	return err
}

74 75 76 77 78 79 80 81 82 83 84 85 86
// makeControlConnection attempts to establish a control connection with Tor's
// given domain socket.  If successful, it returns the connection.  Otherwise,
// it returns an error.
func makeControlConnection(domainSocket string) (*bulb.Conn, error) {

	var torCtrl *bulb.Conn
	var err error

	// Try connecting to tor's control socket.  It may take a second or two for
	// it to be ready.
	for attempts := 0; attempts < 10; attempts++ {
		torCtrl, err = bulb.Dial("unix", domainSocket)
		if err == nil {
87
			torCtrl.Debug(true)
88 89 90 91 92
			if err := torCtrl.Authenticate(""); err != nil {
				return nil, fmt.Errorf("authentication with tor's control port failed: %v", err)
			}
			return torCtrl, nil
		} else {
93
			time.Sleep(time.Second)
94 95 96 97 98 99
		}
	}

	return nil, fmt.Errorf("unable to connect to domain socket")
}

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
// TorContext represents the data structures and methods we need to control a
// Tor process.
type TorContext struct {
	sync.Mutex
	Ctrl      *bulb.Conn
	DataDir   string
	Cancel    context.CancelFunc
	Context   context.Context
	eventChan chan *bulb.Response
}

// Stop stops the Tor process.  Errors during cleanup are logged and the last
// occuring error is returned.
func (c *TorContext) Stop() error {
	c.Lock()
	defer c.Unlock()

	var err error
	log.Println("Stopping Tor process.")
	c.Cancel()

	if c.Ctrl != nil {
		if err = c.Ctrl.Close(); err != nil {
			log.Printf("Failed to close control connection: %s", err)
124
		}
125
	}
126

127 128 129
	if err = os.RemoveAll(c.DataDir); err != nil {
		log.Printf("Failed to remove data directory: %s", err)
	}
130 131 132
	return err
}

133 134 135 136 137
// Start starts the Tor process.
func (c *TorContext) Start() error {
	c.Lock()
	defer c.Unlock()
	log.Println("Starting Tor process.")
138

139 140 141 142 143
	c.eventChan = make(chan *bulb.Response, 100)

	// Create Tor's data directory.
	var err error
	c.DataDir, err = ioutil.TempDir(os.TempDir(), "tor-datadir-")
144 145 146
	if err != nil {
		return err
	}
147
	log.Printf("Created data directory %q.", c.DataDir)
148

149 150
	// Create our torrc.
	tmpFh, err := ioutil.TempFile(c.DataDir, "torrc-")
151 152 153
	if err != nil {
		return err
	}
154
	if err = writeConfigToTorrc(tmpFh, c.DataDir); err != nil {
Philipp Winter's avatar
Philipp Winter committed
155 156
		return err
	}
157
	log.Println("Wrote Tor config file.")
158

159 160 161
	// Start our Tor process.
	c.Context, c.Cancel = context.WithCancel(context.Background())
	cmd := exec.CommandContext(c.Context, "tor", "-f", tmpFh.Name())
162 163 164
	if err = cmd.Start(); err != nil {
		return err
	}
165
	log.Println("Started Tor process.")
166

167 168
	// Start a control connection with our Tor process.
	c.Ctrl, err = makeControlConnection(getDomainSocketPath(c.DataDir))
169
	if err != nil {
170 171 172 173 174 175
		return nil
	}
	c.Ctrl.StartAsyncReader()
	go c.eventReader()

	if _, err := c.Ctrl.Request("SETEVENTS ORCONN NEWDESC"); err != nil {
176 177
		return err
	}
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

	return nil
}

// TestBridgeLines takes as input a list of bridge lines, tells Tor to test
// them, and returns the resulting TestResult.
func (c *TorContext) TestBridgeLines(bridgeLines []string) *TestResult {
	c.Lock()
	defer c.Unlock()

	if len(bridgeLines) == 0 {
		return NewTestResult()
	}

	result := NewTestResult()
	log.Printf("Testing %d bridge lines.", len(bridgeLines))

	// Create our SETCONF line, which tells Tor what bridges it should test.
	// It has the following format:
	//   SETCONF Bridge="BRIDGE1" Bridge="BRIDGE2" ...
	cmdPieces := []string{"SETCONF"}
	for _, bridgeLine := range bridgeLines {
		cmdPieces = append(cmdPieces, fmt.Sprintf("Bridge=%q", bridgeLine))
201
	}
202
	cmd := strings.Join(cmdPieces, " ")
203

204 205 206 207 208 209 210 211 212
	if _, err := c.Ctrl.Request(cmd); err != nil {
		result.Error = err.Error()
		return result
	}

	// We maintain per-bridge state machines that parse Tor's event output.
	eventParsers := make(map[string]*TorEventState)
	for _, bridgeLine := range bridgeLines {
		identifier, err := getBridgeIdentifier(bridgeLine)
213
		if err != nil {
214 215
			log.Printf("Bug: Could not extract identifier from bridge line %q.", bridgeLine)
			continue
216
		}
217 218 219 220 221 222 223 224
		eventParsers[bridgeLine] = NewTorEventState(identifier)
	}

	log.Printf("Waiting for Tor to give us test results.")
	timeout := time.After(TorTestTimeout)
	for {
		select {
		case ev := <-c.eventChan:
Philipp Winter's avatar
Philipp Winter committed
225 226 227 228 229
			// Our channel is closed.
			if ev == nil {
				result.Error = "test aborted because bridgestrap is shutting down"
				return result
			}
230 231 232 233 234 235 236 237 238
			for _, line := range ev.RawLines {
				for bridgeLine, parser := range eventParsers {
					// Skip bridges that are done testing.
					if parser.State != BridgeStatePending {
						continue
					}
					parser.Feed(line)
					if parser.State == BridgeStateSuccess {
						log.Printf("Setting %s to 'true'", bridgeLine)
239 240 241 242
						result.Bridges[bridgeLine] = &BridgeTest{
							Functional: true,
							LastTested: time.Now().UTC(),
						}
243 244
					} else if parser.State == BridgeStateFailure {
						log.Printf("Setting %s to 'false'", bridgeLine)
245 246 247 248 249
						result.Bridges[bridgeLine] = &BridgeTest{
							Functional: false,
							Error:      parser.Reason,
							LastTested: time.Now().UTC(),
						}
250 251 252 253 254 255
					}
				}

				// Do we have test results for all bridges?  If so, we're done.
				if len(result.Bridges) == len(bridgeLines) {
					return result
256 257
				}
			}
258 259 260 261 262 263
		case <-timeout:
			log.Printf("Tor process timed out.")

			// Mark whatever bridge results we're missing as nonfunctional.
			for _, bridgeLine := range bridgeLines {
				if _, exists := result.Bridges[bridgeLine]; !exists {
264 265 266 267 268
					result.Bridges[bridgeLine] = &BridgeTest{
						Functional: false,
						Error:      "timed out waiting for bridge descriptor",
						LastTested: time.Now().UTC(),
					}
269 270 271
				}
			}
			return result
272
		}
273
	}
274

275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
	return result
}

// eventReader reads events from Tor's control port and writes them to
// c.eventChan, allowing TestBridgeLines to read Tor's events in a select
// statement.
func (c *TorContext) eventReader() {
	log.Println("Starting event reader.")
	defer log.Printf("Stopping event reader.")
	for {
		ev, err := c.Ctrl.NextEvent()
		if err != nil {
			close(c.eventChan)
			return
		}
		c.eventChan <- ev
	}
292
}