<commit_before>package tor

import (
	"bytes"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"io/ioutil"
	"net/textproto"
	"os"
	"strconv"
	"strings"
	"sync/atomic"
)

const (
	// success is the Tor Control response code representing a successful
	// request.
	success = 250

	// nonceLen is the length of a nonce generated by either the controller
	// or the Tor server
	nonceLen = 32

	// cookieLen is the length of the authentication cookie.
	cookieLen = 32

	// 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 (
	// serverKey is the key used when computing the HMAC-SHA256 of a message
	// from the server.
	serverKey = []byte("Tor safe cookie authentication " +
		"server-to-controller hash")

	// controllerKey is the key used when computing the HMAC-SHA256 of a
	// message from the controller.
	controllerKey = []byte("Tor safe cookie authentication " +
		"controller-to-server hash")
)

// Controller is an implementation of the Tor Control protocol. This is used in
// order to communicate with a Tor server. Its only supported method of
// authentication is the SAFECOOKIE method.
//
// NOTE: The connection to the Tor server must be authenticated before
// proceeding to send commands. Otherwise, the connection will be closed.
//
// TODO:
//   * if adding support for more commands, extend this with a command queue?
//   * place under sub-package?
//   * support async replies from the server
type Controller struct {
	// started is used atomically in order to prevent multiple calls to
	// Start.
	started int32

	// stopped is used atomically in order to prevent multiple calls to
	// Stop.
	stopped int32

	// conn is the underlying connection between the controller and the
	// Tor server. It provides read and write methods to simplify the
	// text-based messages within the connection.
	conn *textproto.Conn

	// 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
// a Tor server.
func NewController(controlAddr string) *Controller {
	return &Controller{controlAddr: controlAddr}
}

// Start establishes and authenticates the connection between the controller and
// a Tor server. Once done, the controller will be able to send commands and
// expect responses.
func (c *Controller) Start() error {
	if !atomic.CompareAndSwapInt32(&c.started, 0, 1) {
		return nil
	}

	conn, err := textproto.Dial("tcp", c.controlAddr)
	if err != nil {
		return fmt.Errorf("unable to connect to Tor server: %v", err)
	}

	c.conn = conn

	return c.authenticate()
}

// Stop closes the connection between the controller and the Tor server.
func (c *Controller) Stop() error {
	if !atomic.CompareAndSwapInt32(&c.stopped, 0, 1) {
		return nil
	}

	return c.conn.Close()
}

// sendCommand sends a command to the Tor server and returns its response, as a
// single space-delimited string, and code.
func (c *Controller) sendCommand(command string) (int, string, error) {
	if err := c.conn.Writer.PrintfLine(command); err != nil {
		return 0, "", err
	}

	// We'll use ReadResponse as it has built-in support for multi-line
	// text protocol responses.
	code, reply, err := c.conn.Reader.ReadResponse(success)
	if err != nil {
		return code, reply, err
	}

	return code, reply, nil
}

// parseTorReply parses the reply from the Tor server after receving a command
// from a controller. This will parse the relevent reply parameters into a map
// of keys and values.
func parseTorReply(reply string) map[string]string {
	params := make(map[string]string)

	// Replies can either span single or multiple lines, so we'll default
	// to stripping whitespace and newlines in order to retrieve the
	// individual contents of it. The -1 indicates that we want this to span
	// across all instances of a newline.
	contents := strings.Split(strings.Replace(reply, "\n", " ", -1), " ")
	for _, content := range contents {
		// Each parameter within the reply should be of the form
		// "KEY=VALUE". If the parameter doesn't contain "=", then we
		// can assume it does not provide any other relevant information
		// already known.
		keyValue := strings.Split(content, "=")
		if len(keyValue) != 2 {
			continue
		}

		key := keyValue[0]
		value := keyValue[1]
		params[key] = value<commit_msg>tor/controller: fix response parsing for values containing the '=' character<commit_after>package tor

import (
	"bytes"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"fmt"
	"io/ioutil"
	"net/textproto"
	"os"
	"strconv"
	"strings"
	"sync/atomic"
)

const (
	// success is the Tor Control response code representing a successful
	// request.
	success = 250

	// nonceLen is the length of a nonce generated by either the controller
	// or the Tor server
	nonceLen = 32

	// cookieLen is the length of the authentication cookie.
	cookieLen = 32

	// 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 (
	// serverKey is the key used when computing the HMAC-SHA256 of a message
	// from the server.
	serverKey = []byte("Tor safe cookie authentication " +
		"server-to-controller hash")

	// controllerKey is the key used when computing the HMAC-SHA256 of a
	// message from the controller.
	controllerKey = []byte("Tor safe cookie authentication " +
		"controller-to-server hash")
)

// Controller is an implementation of the Tor Control protocol. This is used in
// order to communicate with a Tor server. Its only supported method of
// authentication is the SAFECOOKIE method.
//
// NOTE: The connection to the Tor server must be authenticated before
// proceeding to send commands. Otherwise, the connection will be closed.
//
// TODO:
//   * if adding support for more commands, extend this with a command queue?
//   * place under sub-package?
//   * support async replies from the server
type Controller struct {
	// started is used atomically in order to prevent multiple calls to
	// Start.
	started int32

	// stopped is used atomically in order to prevent multiple calls to
	// Stop.
	stopped int32

	// conn is the underlying connection between the controller and the
	// Tor server. It provides read and write methods to simplify the
	// text-based messages within the connection.
	conn *textproto.Conn

	// 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
// a Tor server.
func NewController(controlAddr string) *Controller {
	return &Controller{controlAddr: controlAddr}
}

// Start establishes and authenticates the connection between the controller and
// a Tor server. Once done, the controller will be able to send commands and
// expect responses.
func (c *Controller) Start() error {
	if !atomic.CompareAndSwapInt32(&c.started, 0, 1) {
		return nil
	}

	conn, err := textproto.Dial("tcp", c.controlAddr)
	if err != nil {
		return fmt.Errorf("unable to connect to Tor server: %v", err)
	}

	c.conn = conn

	return c.authenticate()
}

// Stop closes the connection between the controller and the Tor server.
func (c *Controller) Stop() error {
	if !atomic.CompareAndSwapInt32(&c.stopped, 0, 1) {
		return nil
	}

	return c.conn.Close()
}

// sendCommand sends a command to the Tor server and returns its response, as a
// single space-delimited string, and code.
func (c *Controller) sendCommand(command string) (int, string, error) {
	if err := c.conn.Writer.PrintfLine(command); err != nil {
		return 0, "", err
	}

	// We'll use ReadResponse as it has built-in support for multi-line
	// text protocol responses.
	code, reply, err := c.conn.Reader.ReadResponse(success)
	if err != nil {
		return code, reply, err
	}

	return code, reply, nil
}

// parseTorReply parses the reply from the Tor server after receving a command
// from a controller. This will parse the relevent reply parameters into a map
// of keys and values.
func parseTorReply(reply string) map[string]string {
	params := make(map[string]string)

	// Replies can either span single or multiple lines, so we'll default
	// to stripping whitespace and newlines in order to retrieve the
	// individual contents of it. The -1 indicates that we want this to span
	// across all instances of a newline.
	contents := strings.Split(strings.Replace(reply, "\n", " ", -1), " ")
	for _, content := range contents {
		// Each parameter within the reply should be of the form
		// "KEY=VALUE". If the parameter doesn't contain "=", then we
		// can assume it does not provide any other relevant information
		// already known.
		keyValue := strings.SplitN(content, "=", 2)
		if len(keyValue) != 2 {
			continue
		}

		key := keyValue[0]
		value := keyValue[1]
		params[key] = value
{'fraction_non_alphanumeric': 0.0923141625486279, 'fraction_numerical': 0.008516454631479339, 'mean_word_length': 3.503787878787879, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1828763', 'n_tokens_mistral': 2841, 'n_tokens_neox': 2688, 'n_words': 1202}