Statically compiled Tor and examples

This commit is contained in:
Chad Retz 2018-05-17 14:37:22 -05:00
parent 3ed91b141c
commit dba7e8856b
6 changed files with 244 additions and 14 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Chad Retz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,72 @@
package main
import (
"context"
"flag"
"fmt"
"log"
"net/http"
"os"
"path/filepath"
"time"
"github.com/cretz/bine/process/embedded"
"github.com/cretz/bine/tor"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
// Parse flags. By default, non-verbose served in the current working dir.
var verbose bool
flag.BoolVar(&verbose, "verbose", false, "Whether to have verbose logging")
var directory string
flag.StringVar(&directory, "dir", ".", "The directory to serve (current dir is default)")
flag.Parse()
var err error
if directory, err = filepath.Abs(directory); err != nil {
return err
}
// Start tor
startConf := &tor.StartConf{ProcessCreator: embedded.NewCreator()}
if verbose {
startConf.DebugWriter = os.Stdout
} else {
startConf.ExtraArgs = []string{"--quiet"}
}
fmt.Printf("Starting and registering onion service to serve files from %v\n", directory)
fmt.Println("Please wait a couple of minutes...")
t, err := tor.Start(nil, startConf)
if err != nil {
return err
}
defer t.Close()
// Wait at most a few minutes to publish the service
listenCtx, listenCancel := context.WithTimeout(context.Background(), 3*time.Minute)
defer listenCancel()
// Create an onion service to listen on a random local port but show as 80
onion, err := t.Listen(listenCtx, &tor.ListenConf{RemotePorts: []int{80}})
if err != nil {
return err
}
defer onion.Close()
// Start server asynchronously
fmt.Printf("Open Tor browser and navigate to http://%v.onion\n", onion.ID)
fmt.Println("Press enter to exit")
server := &http.Server{Handler: http.FileServer(http.Dir(directory))}
defer server.Shutdown(context.Background())
errCh := make(chan error, 1)
go func() { errCh <- server.Serve(onion) }()
// Wait for key asynchronously
go func() {
fmt.Scanln()
errCh <- nil
}()
// Stop when one happens
defer fmt.Println("Closing")
return <-errCh
}

View File

@ -0,0 +1,28 @@
package main
import (
"context"
"fmt"
"log"
"github.com/cretz/bine/process/embedded"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
p, err := embedded.NewCreator().New(context.Background(), "--version")
if err != nil {
return err
}
fmt.Printf("Starting...\n")
if err = p.Start(); err != nil {
return err
}
fmt.Printf("Waiting...\n")
return p.Wait()
}

View File

@ -1,13 +1,124 @@
// Package embedded implements process interfaces for statically linked,
// embedded Tor.
// embedded Tor. Note, processes created here are not killed when a context is
// done like w/ os.Exec.
//
// TODO: not finished yet
// Usage
//
// This package can be used with CGO to statically compile Tor. This package
// expects https://github.com/cretz/tor-static to be cloned at
// $GOPATH/src/github.com/cretz/tor-static as if it was fetched with go get. To
// build the needed static libs, follow the README in that project. Once the
// static libs are built, this uses CGO to statically link them here. For
// Windows this means something like http://tdm-gcc.tdragon.net/ needs to be
// present with gcc.exe on the PATH.
//
// NOTE: Other OSs besides Windows have not been tested but likely require an
// LDFLAGS setting here. Pull requests are welcomed.
package embedded
import "github.com/cretz/bine/process"
import (
"context"
"fmt"
"github.com/cretz/bine/process"
)
/*
#cgo CFLAGS: -I${SRCDIR}/../../../tor-static/tor/src/or
#cgo LDFLAGS: -L${SRCDIR}/../../../tor-static/tor/src/or -ltor
#cgo LDFLAGS: -L${SRCDIR}/../../../tor-static/tor/src/common -lor -lor-crypto -lcurve25519_donna -lor-ctime -lor-event
#cgo LDFLAGS: -L${SRCDIR}/../../../tor-static/tor/src/trunnel -lor-trunnel
#cgo LDFLAGS: -L${SRCDIR}/../../../tor-static/tor/src/ext/keccak-tiny -lkeccak-tiny
#cgo LDFLAGS: -L${SRCDIR}/../../../tor-static/tor/src/ext/ed25519/ref10 -led25519_ref10
#cgo LDFLAGS: -L${SRCDIR}/../../../tor-static/tor/src/ext/ed25519/donna -led25519_donna
#cgo LDFLAGS: -L${SRCDIR}/../../../tor-static/libevent/dist/lib -levent
#cgo LDFLAGS: -L${SRCDIR}/../../../tor-static/xz/dist/lib -llzma
#cgo LDFLAGS: -L${SRCDIR}/../../../tor-static/zlib/dist/lib -lz
#cgo LDFLAGS: -L${SRCDIR}/../../../tor-static/openssl/dist/lib -lssl -lcrypto
#cgo windows LDFLAGS: -lws2_32 -lcrypt32 -lgdi32
#include <stdlib.h>
#include <tor_api.h>
// Ref: https://stackoverflow.com/questions/45997786/passing-array-of-string-as-parameter-from-go-to-c-function
static char** makeCharArray(int size) {
return calloc(sizeof(char*), size);
}
static void setArrayString(char **a, char *s, int n) {
a[n] = s;
}
static void freeCharArray(char **a, int size) {
int i;
for (i = 0; i < size; i++)
free(a[i]);
free(a);
}
*/
import "C"
type embeddedCreator struct{}
// NewCreator creates a process.Creator for statically-linked Tor embedded in
// the binary.
func NewCreator() process.Creator {
panic("TODO: embedding not implemented yet")
return embeddedCreator{}
}
type embeddedProcess struct {
ctx context.Context
args []string
doneCh chan int
}
func (embeddedCreator) New(ctx context.Context, args ...string) (process.Process, error) {
return &embeddedProcess{ctx: ctx, args: args}, nil
}
func (e *embeddedProcess) Start() error {
if e.doneCh != nil {
return fmt.Errorf("Already started")
}
// Create the char array for the args
args := append([]string{"tor"}, e.args...)
charArray := C.makeCharArray(C.int(len(args)))
for i, a := range args {
C.setArrayString(charArray, C.CString(a), C.int(i))
}
// Build the conf
conf := C.tor_main_configuration_new()
if code := C.tor_main_configuration_set_command_line(conf, C.int(len(args)), charArray); code != 0 {
C.tor_main_configuration_free(conf)
C.freeCharArray(charArray, C.int(len(args)))
return fmt.Errorf("Failed to set command line args, code: %v", int(code))
}
// Run it async
e.doneCh = make(chan int, 1)
go func() {
defer C.freeCharArray(charArray, C.int(len(args)))
defer C.tor_main_configuration_free(conf)
e.doneCh <- int(C.tor_run_main(conf))
}()
return nil
}
func (e *embeddedProcess) Wait() error {
if e.doneCh == nil {
return fmt.Errorf("Not started")
}
ctx := e.ctx
if ctx == nil {
ctx = context.Background()
}
select {
case <-ctx.Done():
return ctx.Err()
case code := <-e.doneCh:
if code == 0 {
return nil
}
return fmt.Errorf("Command completed with error exit code: %v", code)
}
}

View File

@ -24,7 +24,7 @@ type Process interface {
// analagous to os/exec.Cmd.Start.
Start() error
// Wait waits for the Tor process to exit and returns error if it was not a
// successful exit. It is analagous to os/exec.Cmd.Wait.
// successful exit. It is analagous to os/exec.Cmd.Wait.
Wait() error
}

View File

@ -12,7 +12,6 @@ import (
"time"
"github.com/cretz/bine/control"
"github.com/cretz/bine/process/embedded"
"github.com/cretz/bine/process"
)
@ -53,12 +52,13 @@ type Tor struct {
// default instance with no fields set is the default used for Start.
type StartConf struct {
// ExePath is the path to the Tor executable. If it is not present, "tor" is
// used either locally or on the PATH.
// used either locally or on the PATH. This is ignored if ProcessCreator is
// set.
ExePath string
// Embedded is true if Tor is statically compiled. If true, ExePath is
// ignored.
Embedded bool
// ProcessCreator is the override to use a specific process creator. If set,
// ExePath is ignored.
ProcessCreator process.Creator
// ControlPort is the port to use for the Tor controller. If it is 0, Tor
// picks a port for use.
@ -159,10 +159,8 @@ func Start(ctx context.Context, conf *StartConf) (*Tor, error) {
func (t *Tor) startProcess(ctx context.Context, conf *StartConf) error {
// Get the creator
var creator process.Creator
if conf.Embedded {
creator = embedded.NewCreator()
} else {
creator := conf.ProcessCreator
if creator == nil {
torPath := conf.ExePath
if torPath == "" {
torPath = "tor"