├── .appveyor.yml ├── .gitattributes ├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE ├── README.md ├── build.go └── build_test.go /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | platform: x64 3 | configuration: Release 4 | os: Visual Studio 2015 5 | 6 | clone_depth: 50 7 | clone_folder: c:\gopath\src\github.com\cretz\tor-static 8 | 9 | environment: 10 | GOPATH: c:\gopath 11 | 12 | install: 13 | - set PATH=C:\msys64\usr\bin;%PATH% 14 | - set MSYSTEM=MINGW64 15 | - bash -lc "pacman -Sy --noconfirm --needed base-devel mingw-w64-i686-toolchain mingw-w64-x86_64-toolchain git mingw-w64-i686-cmake mingw-w64-x86_64-cmake" 16 | 17 | before_build: 18 | - set MSYSTEM=MINGW64 19 | - cd c:\gopath\src\github.com\cretz\tor-static 20 | - git submodule update --init --recursive 21 | 22 | build_script: 23 | - set MSYSTEM=MINGW64 24 | - bash -lc "export PATH=/mingw64/bin:$PATH && cd /c/gopath/src/github.com/cretz/tor-static && /c/go/bin/go run build.go -verbose build-all" 25 | 26 | test_script: 27 | - set PATH=C:\msys64\mingw64\bin;%PATH% 28 | - go get -u github.com/cretz/bine/tor 29 | - cd c:\gopath\src\github.com\cretz\tor-static 30 | - go test -v build_test.go -tor.verbose 31 | 32 | after_build: 33 | - go run build.go package-libs 34 | - cp libs.zip tor-static-windows-amd64.zip 35 | artifacts: 36 | - path: tor-static-windows-amd64.zip 37 | 38 | deploy: 39 | description: 'New tor-static release' 40 | provider: GitHub 41 | auth_token: 42 | secure: 6FeJJsz3att0ChUBBFt+Llk8vr32vvZVDbkicQfpRn0mJrome0mIPbkLbH2J5ejC 43 | artifact: tor-static-windows-amd64.zip 44 | draft: true 45 | on: 46 | branch: master 47 | APPVEYOR_REPO_TAG: true 48 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Need LF line endings for all files here 2 | * text eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /libs.zip 2 | /libs.tar.gz 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "openssl"] 2 | path = openssl 3 | url = https://github.com/openssl/openssl.git 4 | [submodule "libevent"] 5 | path = libevent 6 | url = https://github.com/libevent/libevent.git 7 | [submodule "zlib"] 8 | path = zlib 9 | url = https://github.com/madler/zlib.git 10 | [submodule "xz"] 11 | path = xz 12 | url = https://git.tukaani.org/xz.git 13 | [submodule "tor"] 14 | path = tor 15 | url = https://gitlab.torproject.org/tpo/core/tor.git 16 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 1.11.x 3 | sudo: false 4 | 5 | stages: 6 | - name: deploy 7 | if: branch = master 8 | 9 | matrix: 10 | include: 11 | - name: linux-amd64 12 | os: linux 13 | dist: xenial 14 | env: CGO_ENABLED=1 GOOS=linux GOARCH=amd64 15 | install: 16 | - sudo apt-get install build-essential 17 | - sudo apt-get install libtool 18 | - sudo apt-get install autopoint 19 | - name: darwin-amd64 20 | os: osx 21 | env: CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 22 | install: 23 | - brew install libtool 24 | - brew install gettext 25 | 26 | before_script: 27 | - go run build.go -verbose build-all 28 | 29 | script: 30 | - go get -u github.com/cretz/bine/tor 31 | - go test -v build_test.go -tor.verbose 32 | 33 | after_script: 34 | - go run build.go package-libs 35 | 36 | after_success: 37 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then mv libs.tar.gz tor-static-linux-amd64.tar.gz; fi 38 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mv libs.tar.gz tor-static-darwin-amd64.tar.gz; fi 39 | 40 | deploy: 41 | provider: releases 42 | api_key: "${GITHUB_PAT}" 43 | file: 44 | - tor-static-linux-amd64.tar.gz 45 | - tor-static-darwin-amd64.tar.gz 46 | skip_cleanup: true 47 | draft: true 48 | on: 49 | repo: cretz/tor-static 50 | tags: true -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Chad Retz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tor-static [![Build Status](https://travis-ci.org/cretz/tor-static.svg?branch=master)](https://travis-ci.org/cretz/tor-static) [![Build status](https://ci.appveyor.com/api/projects/status/su4pkdrmlki6jd7n?svg=true)](https://ci.appveyor.com/project/cretz/tor-static) 2 | 3 | This project helps compile Tor into a static lib for use in other projects. 4 | 5 | The dependencies are in this repository as submodules so this repository needs to be cloned with `--recursive`. The 6 | submodules are: 7 | 8 | - [OpenSSL](https://github.com/openssl/openssl/) - Checked out at tag `OpenSSL_1_1_1w` 9 | - [Libevent](https://github.com/libevent/libevent) - Checked out at tag `release-2.1.12-stable` 10 | - [zlib](https://github.com/madler/zlib) - Checked out at tag `v1.3.1` 11 | - [XZ Utils](https://git.tukaani.org/?p=xz.git) - Checked out at tag `v5.6.2` 12 | - [Tor](https://github.com/torproject/tor) - Checked out at tag `tor-0.4.8.12` 13 | 14 | Many many bugs and quirks were hit while deriving these steps. Also many other repos, mailing lists, etc were leveraged 15 | to get some of the pieces right. They are not listed here for brevity reasons. 16 | 17 | **Note: Other versions of Tor may be available via tags (for current or previous versions) and branches (for future 18 | versions)** 19 | 20 | ## Building 21 | 22 | ### Prerequisites 23 | 24 | All platforms need Go installed and on the PATH. 25 | 26 | #### Linux 27 | 28 | Need: 29 | 30 | - Normal build tools (e.g. `sudo apt-get install build-essential`) 31 | - Libtool (e.g. `sudo apt-get install libtool`) 32 | - autopoint (e.g. `sudo apt-get install autopoint`) 33 | 34 | #### macOS 35 | 36 | Need: 37 | 38 | - Normal build tools (e.g. Xcode command line tools) 39 | - go (e.g. `brew install go`) 40 | - Libtool (e.g. `brew install libtool`) 41 | - Autoconf and Automake (e.g. `brew install automake`) 42 | - autopoint (can be found in gettext, e.g. `brew install gettext`) 43 | - Note, by default this is assumed to be at `/usr/local/opt/gettext/bin`. Use `-autopoint-path` to change it. 44 | - po4a (e.g. `brew install po4a`) 45 | 46 | #### Windows 47 | 48 | Tor is not really designed to work well with MSVC so we use MinGW instead. In order to compile the dependencies, 49 | Msys2 + MinGW should be installed. 50 | 51 | Download and install the latest [MSYS2 64-bit](http://www.msys2.org/) that uses the `MinGW-w64` toolchains. Once 52 | installed, open the "MSYS MinGW 64-bit" shell link that was created. Once in the shell, run: 53 | 54 | pacman -Syuu 55 | 56 | Terminate and restart the shell if asked. Rerun this command as many times as needed until it reports that everything is 57 | up to date. Then in the same mingw-64 shell, run: 58 | 59 | pacman -Sy --needed base-devel mingw-w64-i686-toolchain mingw-w64-x86_64-toolchain \ 60 | git subversion mercurial libtool automake autoconf automake-wrapper \ 61 | mingw-w64-i686-cmake mingw-w64-x86_64-cmake 62 | 63 | This will install all the tools needed for building and will take a while. Once complete, MinGW is now setup to build 64 | the dependencies. 65 | 66 | ### Executing the build 67 | 68 | In the cloned directory, run: 69 | 70 | go run build.go build-all 71 | 72 | This will take a long time. Pieces can be built individually by changing the command from `build-all` to 73 | `build-`. To clean, run either `clean-all` or `clean-`. To see the output of all the commands as they 74 | are being run, add `-verbose` before the command. 75 | 76 | ## Using 77 | 78 | Once the libs have been compiled, they can be used to link with your program. Due to recent refactorings within the Tor 79 | source, the libraries are not listed here but instead listed when executing: 80 | 81 | go run build.go show-libs 82 | 83 | This lists directories (relative, prefixed with `-L`) followed by lib names (file sans `lib` prefix and sans `.a` 84 | extension, prefixed with `-l`) as might be used in `ld`. 85 | 86 | The OS-specific system libs that have to be referenced (i.e. `-l`) are: 87 | 88 | - Linux/macOS - `m` 89 | - Windows (MinGW) - `ws2_32`, `crypt32`, `gdi32`, `iphlpapi`, and `shlwapi` 90 | 91 | The OS-specific system libs that have to be explicitly statically linked (i.e. `-Wl,-Bstatic -l`) are: 92 | 93 | - Windows (MinGW) - `pthread` 94 | -------------------------------------------------------------------------------- /build.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "archive/tar" 5 | "archive/zip" 6 | "bytes" 7 | "compress/gzip" 8 | "errors" 9 | "flag" 10 | "fmt" 11 | "io" 12 | "io/ioutil" 13 | "log" 14 | "os" 15 | "os/exec" 16 | "path" 17 | "path/filepath" 18 | "runtime" 19 | "strings" 20 | ) 21 | 22 | var verbose bool 23 | var host string 24 | var autopointPath string 25 | var folders = []string{"openssl", "libevent", "zlib", "xz", "tor"} 26 | var absCurrDir = getAbsCurrDir() 27 | var numJobs = fmt.Sprintf("-j%d", runtime.NumCPU()) 28 | 29 | func main() { 30 | flag.BoolVar(&verbose, "verbose", false, "Whether to show command output") 31 | flag.StringVar(&host, "host", "", "Host option, useful for cross-compilation") 32 | flag.StringVar(&autopointPath, "autopoint-path", "/usr/local/opt/gettext/bin", "OSX: Directory that contains autopoint binary") 33 | flag.Parse() 34 | if len(flag.Args()) != 1 { 35 | log.Fatal("Missing command. Can be build-all, build-, clean-all, clean-, show-libs, or package-libs") 36 | } 37 | if err := run(flag.Args()[0]); err != nil { 38 | log.Fatal(err) 39 | } 40 | } 41 | 42 | func run(cmd string) error { 43 | if err := validateEnvironment(); err != nil { 44 | return err 45 | } 46 | switch { 47 | case strings.HasPrefix(cmd, "build-"): 48 | return build(cmd[6:]) 49 | case strings.HasPrefix(cmd, "clean-"): 50 | return clean(cmd[6:]) 51 | case cmd == "show-libs": 52 | return showLibs() 53 | case cmd == "package-libs": 54 | return packageLibs() 55 | default: 56 | return fmt.Errorf("Invalid command: %v. Should be build-all, build-, clean-all, clean-, show-libs, or package-libs", cmd) 57 | } 58 | } 59 | 60 | func getAbsCurrDir() string { 61 | var err error 62 | absCurrDir, err := filepath.Abs(".") 63 | if err != nil { 64 | panic(err) 65 | } 66 | if runtime.GOOS == "windows" { 67 | volume := filepath.VolumeName(absCurrDir) 68 | absCurrDir = "/" + strings.TrimSuffix(volume, ":") + "/" + filepath.ToSlash(absCurrDir[len(volume)+1:]) 69 | } 70 | return absCurrDir 71 | } 72 | 73 | func validateEnvironment() error { 74 | // Make sure all the folders are there 75 | for _, folder := range folders { 76 | if info, err := os.Stat(folder); err != nil || !info.IsDir() { 77 | return fmt.Errorf("%v is not a dir", folder) 78 | } 79 | } 80 | switch runtime.GOOS { 81 | // On windows, have to verify MinGW 82 | case "windows": 83 | // Confirm it is MinGW 64 84 | if byts, err := exec.Command("uname", "-a").CombinedOutput(); err != nil { 85 | return fmt.Errorf("This has to be run in a MSYS or MinGW shell, uname failed: %v", err) 86 | } else if !bytes.HasPrefix(byts, []byte("MINGW64")) && !bytes.HasPrefix(byts, []byte("MSYS2")) { 87 | return fmt.Errorf("This has to be run in a MSYS or MinGW64 shell, uname output: %v", string(byts)) 88 | } 89 | case "linux": 90 | // Make sure it's not MinGW 91 | if byts, err := exec.Command("uname", "-a").CombinedOutput(); err != nil { 92 | return fmt.Errorf("Failed running uname -a") 93 | } else if bytes.HasPrefix(byts, []byte("MINGW")) { 94 | return fmt.Errorf("MinGW should not use Linux Go binary, but instead a Windows go.exe to run the build") 95 | } 96 | } 97 | return nil 98 | } 99 | 100 | func build(folder string) error { 101 | log.Printf("*** Building %v ***", folder) 102 | defer log.Printf("*** Done building %v ***", folder) 103 | pwd := absCurrDir + "/" + folder 104 | switch folder { 105 | case "all": 106 | for _, subFolder := range folders { 107 | if err := build(subFolder); err != nil { 108 | return err 109 | } 110 | } 111 | return nil 112 | case "openssl": 113 | prefix := pwd + "/dist" 114 | cmds := [][]string{ 115 | {"sh", "./config", "--prefix=" + prefix, "--openssldir=" + prefix, "no-shared", "no-dso", "no-zlib"}, 116 | {"make", "depend"}, 117 | {"make", numJobs}, 118 | {"make", "install_sw"}, 119 | } 120 | if runtime.GOOS == "windows" { 121 | cmds[0] = append(cmds[0], "mingw64") 122 | cmds[0][0] = "perl" 123 | cmds[0][1] = "./Configure" 124 | } else if runtime.GOOS == "darwin" { 125 | cmds[0][0] = "perl" 126 | cmds[0][1] = "./Configure" 127 | 128 | byts, err := exec.Command("uname", "-m").CombinedOutput() 129 | if err != nil { 130 | return err 131 | } 132 | 133 | var defaultOSCompiler string 134 | switch string(byts) { 135 | case "x86_64\n": 136 | defaultOSCompiler = "darwin64-x86_64-cc" 137 | case "arm64\n": 138 | defaultOSCompiler = "darwin64-arm64-cc" 139 | } 140 | 141 | if host != "" { 142 | switch { 143 | case strings.HasPrefix(host, "x86_64"): 144 | defaultOSCompiler = "darwin64-x86_64-cc" 145 | case strings.HasPrefix(host, "arm64"): 146 | defaultOSCompiler = "darwin64-arm64-cc" 147 | default: 148 | return errors.New("unsupported architecture") 149 | } 150 | } 151 | 152 | cmds[0] = append(cmds[0], defaultOSCompiler) 153 | } 154 | 155 | return runCmds(folder, nil, cmds) 156 | case "libevent": 157 | cmds := [][]string{ 158 | {"sh", "-l", "./autogen.sh"}, 159 | {"sh", "./configure", "--prefix=" + pwd + "/dist", 160 | "--disable-shared", "--enable-static", "--with-pic", "--disable-samples", "--disable-libevent-regress", 161 | "CPPFLAGS=-I../openssl/dist/include", "LDFLAGS=-L../openssl/dist/lib"}, 162 | {"make", numJobs}, 163 | {"make", "install"}, 164 | } 165 | 166 | if host != "" { 167 | cmds[1] = append(cmds[1], "--host="+host) 168 | } 169 | return runCmds(folder, nil, cmds) 170 | case "zlib": 171 | var env []string 172 | cmds := [][]string{ 173 | {"sh", "./configure", "--prefix=" + pwd + "/dist", "--static"}, 174 | {"make", numJobs}, 175 | {"make", "install"}, 176 | } 177 | if runtime.GOOS == "windows" { 178 | env = []string{"PREFIX=" + pwd + "/dist", "BINARY_PATH=" + pwd + "/dist/bin", 179 | "INCLUDE_PATH=" + pwd + "/dist/include", "LIBRARY_PATH=" + pwd + "/dist/lib"} 180 | cmds = [][]string{{"make", "-fwin32/Makefile.gcc"}, {"make", "install", "-fwin32/Makefile.gcc"}} 181 | } 182 | return runCmds(folder, env, cmds) 183 | case "xz": 184 | var env []string 185 | if runtime.GOOS == "darwin" { 186 | env = []string{"PATH=" + autopointPath + ":" + os.Getenv("PATH")} 187 | } 188 | cmds := [][]string{ 189 | {"sh", "-l", "./autogen.sh"}, 190 | {"sh", "./configure", "--prefix=" + pwd + "/dist", "--disable-shared", "--enable-static", 191 | "--disable-doc", "--disable-scripts", "--disable-xz", "--disable-xzdec", "--disable-lzmadec", 192 | "--disable-lzmainfo", "--disable-lzma-links"}, 193 | {"make", numJobs}, 194 | {"make", "install"}, 195 | } 196 | if host != "" { 197 | cmds[1] = append(cmds[1], "--host="+host) 198 | } 199 | return runCmds(folder, env, cmds) 200 | case "tor": 201 | var env = []string{"LDFLAGS=-s"} 202 | var torConf []string 203 | if runtime.GOOS == "windows" { 204 | env = append(env, "LIBS=-lcrypt32 -lgdi32") 205 | } 206 | torConf = []string{"sh", "./configure", "--prefix=" + pwd + "/dist", 207 | "--disable-gcc-hardening", "--disable-system-torrc", "--disable-asciidoc", 208 | "--enable-static-libevent", "--with-libevent-dir=" + pwd + "/../libevent/dist", 209 | "--enable-static-openssl", "--with-openssl-dir=" + pwd + "/../openssl/dist", 210 | "--enable-static-zlib", "--with-zlib-dir=" + pwd + "/../zlib/dist", 211 | "--disable-systemd", "--disable-lzma", "--disable-seccomp"} 212 | 213 | if host != "" { 214 | torConf = append(torConf, "--host="+host) 215 | } 216 | 217 | if runtime.GOOS == "darwin" { 218 | torConf = append(torConf, []string{"--disable-zstd", "--disable-libscrypt"}...) 219 | if host != "" { 220 | torConf = append(torConf, "--disable-tool-name-check") 221 | } 222 | } 223 | 224 | if runtime.GOOS != "darwin" { 225 | torConf = append(torConf, "--enable-static-tor") 226 | } 227 | 228 | if runtime.GOOS == "windows" { 229 | torConf = append(torConf, "--disable-zstd") 230 | } 231 | return runCmds(folder, env, [][]string{ 232 | {"sh", "-l", "./autogen.sh"}, 233 | torConf, 234 | {"make", numJobs}, 235 | {"make", "install"}, 236 | }) 237 | default: 238 | return fmt.Errorf("Unrecognized folder: %v", folder) 239 | } 240 | } 241 | 242 | func clean(folder string) (err error) { 243 | log.Printf("*** Cleaning %v ***", folder) 244 | defer log.Printf("*** Done cleaning %v ***", folder) 245 | switch folder { 246 | case "all": 247 | for _, subFolder := range folders { 248 | if err = clean(subFolder); err != nil { 249 | break 250 | } 251 | } 252 | default: 253 | args := []string{"clean"} 254 | env := []string{} 255 | makefile := "Makefile" 256 | switch folder { 257 | // OpenSSL needs to have the dist folder removed first 258 | case "openssl": 259 | if err := os.RemoveAll("openssl/dist/lib"); err != nil { 260 | return fmt.Errorf("Unable to remove openssl/dist/lib: %v", err) 261 | } 262 | // Zlib needs to have a prefix and needs a special windows makefile 263 | case "zlib": 264 | env = append(env, "PREFIX="+absCurrDir+"/zlib/dist") 265 | if runtime.GOOS == "windows" { 266 | makefile = "win32/Makefile.gcc" 267 | args = append(args, "-fwin32/Makefile.gcc") 268 | } 269 | } 270 | if dir, err := os.Stat(folder); err != nil || !dir.IsDir() { 271 | return fmt.Errorf("%v is not a directory", folder) 272 | } else if _, err := os.Stat(path.Join(folder, makefile)); os.IsNotExist(err) { 273 | log.Printf("Skipping clean, makefile not present") 274 | return nil 275 | } 276 | err = runCmd(folder, env, "make", args...) 277 | } 278 | return err 279 | } 280 | 281 | func runCmds(folder string, env []string, cmdsAndArgs [][]string) error { 282 | for _, cmdAndArgs := range cmdsAndArgs { 283 | if err := runCmd(folder, env, cmdAndArgs[0], cmdAndArgs[1:]...); err != nil { 284 | return err 285 | } 286 | } 287 | return nil 288 | } 289 | 290 | func runCmd(folder string, env []string, cmd string, args ...string) error { 291 | log.Printf("Running in folder %v: %v %v", folder, cmd, strings.Join(args, " ")) 292 | c := exec.Command(cmd, args...) 293 | if len(env) > 0 { 294 | c.Env = append(os.Environ(), env...) 295 | } 296 | c.Dir = folder 297 | if verbose { 298 | c.Stdout = os.Stdout 299 | c.Stderr = os.Stderr 300 | } 301 | return c.Run() 302 | } 303 | 304 | type libSet struct { 305 | dir string 306 | libs []string 307 | } 308 | 309 | // Results in recommended linker order 310 | func getLibSets() ([]*libSet, error) { 311 | // Ask Tor for their libs 312 | cmd := exec.Command("make", "show-libs") 313 | cmd.Dir = "tor" 314 | out, err := cmd.Output() 315 | if err != nil { 316 | return nil, fmt.Errorf("Failed 'make show-libs' in tor: %v", err) 317 | } 318 | // Load them all 319 | libSets := []*libSet{} 320 | libSetsByDir := map[string]*libSet{} 321 | for _, lib := range strings.Split(strings.TrimSpace(string(out)), " ") { 322 | dir, file := path.Split(lib) 323 | dir = path.Join("tor", dir) 324 | set := libSetsByDir[dir] 325 | if set == nil { 326 | set = &libSet{dir: dir} 327 | libSets = append(libSets, set) 328 | libSetsByDir[dir] = set 329 | } 330 | set.libs = append(set.libs, strings.TrimPrefix(strings.TrimSuffix(file, ".a"), "lib")) 331 | } 332 | // Add the rest of the known libs 333 | libSets = append(libSets, 334 | &libSet{"libevent/dist/lib", []string{"event"}}, 335 | &libSet{"xz/dist/lib", []string{"lzma"}}, 336 | &libSet{"zlib/dist/lib", []string{"z"}}, 337 | &libSet{"openssl/dist/lib", []string{"ssl", "crypto"}}, 338 | ) 339 | return libSets, nil 340 | } 341 | 342 | func showLibs() error { 343 | libSets, err := getLibSets() 344 | if err != nil { 345 | return err 346 | } 347 | for _, libSet := range libSets { 348 | fmt.Print("-L" + libSet.dir) 349 | for _, lib := range libSet.libs { 350 | fmt.Print(" -l" + lib) 351 | } 352 | fmt.Println() 353 | } 354 | return nil 355 | } 356 | 357 | func packageLibs() error { 358 | // Make both a libs.tar.gz and a libs.zip... 359 | // Get lib sets 360 | libSets, err := getLibSets() 361 | if err != nil { 362 | return err 363 | } 364 | // Create tar writer 365 | var tw *tar.Writer 366 | if tf, err := os.Create("libs.tar.gz"); err != nil { 367 | return err 368 | } else { 369 | defer tf.Close() 370 | gw := gzip.NewWriter(tf) 371 | defer gw.Close() 372 | tw = tar.NewWriter(gw) 373 | defer tw.Close() 374 | } 375 | tarWrite := func(path string, b []byte, i os.FileInfo) error { 376 | th, err := tar.FileInfoHeader(i, "") 377 | if err == nil { 378 | th.Name = path 379 | if err = tw.WriteHeader(th); err == nil { 380 | _, err = tw.Write(b) 381 | } 382 | } 383 | return err 384 | } 385 | // Create zip writer 386 | var zw *zip.Writer 387 | if zf, err := os.Create("libs.zip"); err != nil { 388 | return err 389 | } else { 390 | defer zf.Close() 391 | zw = zip.NewWriter(zf) 392 | defer zw.Close() 393 | } 394 | zipWrite := func(path string, b []byte, i os.FileInfo) error { 395 | zh, err := zip.FileInfoHeader(i) 396 | if err == nil { 397 | zh.Name = path 398 | zh.Method = zip.Deflate 399 | var w io.Writer 400 | if w, err = zw.CreateHeader(zh); err == nil { 401 | _, err = w.Write(b) 402 | } 403 | } 404 | return err 405 | } 406 | // Copy over each lib 407 | fileBytesAndInfo := func(name string) (b []byte, i os.FileInfo, err error) { 408 | f, err := os.Open(name) 409 | if err != nil { 410 | return nil, nil, err 411 | } 412 | defer f.Close() 413 | if i, err = f.Stat(); err == nil { 414 | b, err = ioutil.ReadAll(f) 415 | } 416 | return 417 | } 418 | copyFile := func(filePath string) error { 419 | if b, i, err := fileBytesAndInfo(filePath); err != nil { 420 | return err 421 | } else if err := tarWrite(filePath, b, i); err != nil { 422 | return err 423 | } else { 424 | return zipWrite(filePath, b, i) 425 | } 426 | } 427 | for _, libSet := range libSets { 428 | for _, lib := range libSet.libs { 429 | if err := copyFile(path.Join(libSet.dir, "lib"+lib+".a")); err != nil { 430 | return err 431 | } 432 | } 433 | } 434 | // Also copy over tor_api.h 435 | return copyFile("tor/src/feature/api/tor_api.h") 436 | } 437 | -------------------------------------------------------------------------------- /build_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "flag" 7 | "io/ioutil" 8 | "net/http" 9 | "os" 10 | "testing" 11 | "time" 12 | 13 | "github.com/cretz/bine/process/embedded" 14 | "github.com/cretz/bine/tor" 15 | ) 16 | 17 | var torVerbose bool 18 | 19 | func TestMain(m *testing.M) { 20 | flag.BoolVar(&torVerbose, "tor.verbose", false, "Show verbose test info") 21 | flag.Parse() 22 | os.Exit(m.Run()) 23 | } 24 | 25 | func TestDialierSimpleHTTP(t *testing.T) { 26 | // Give the whole thing 5 minutes just in case Tor is slow at the time 27 | ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Minute) 28 | defer cancelFn() 29 | // Start embedded Tor 30 | conf := &tor.StartConf{ProcessCreator: embedded.NewCreator()} 31 | if torVerbose { 32 | conf.DebugWriter = os.Stdout 33 | conf.NoHush = true 34 | } else { 35 | conf.ExtraArgs = append(conf.ExtraArgs, "--quiet") 36 | } 37 | tr, err := tor.Start(ctx, conf) 38 | fatalIfErr(t, err) 39 | defer tr.Close() 40 | // Create HTTP client 41 | dialer, err := tr.Dialer(ctx, nil) 42 | fatalIfErr(t, err) 43 | client := &http.Client{Transport: &http.Transport{DialContext: dialer.DialContext}} 44 | // Make simple Get call to check if we're on Tor 45 | req, err := http.NewRequest("GET", "https://check.torproject.org/api/ip", nil) 46 | fatalIfErr(t, err) 47 | resp, err := client.Do(req.WithContext(ctx)) 48 | fatalIfErr(t, err) 49 | respBytes, err := ioutil.ReadAll(resp.Body) 50 | resp.Body.Close() 51 | fatalIfErr(t, err) 52 | // Parse out the JSON and confirm the response 53 | jsn := map[string]interface{}{} 54 | fatalIfErr(t, json.Unmarshal(respBytes, &jsn)) 55 | if !jsn["IsTor"].(bool) { 56 | t.Fatal("IsTor returned false") 57 | } 58 | } 59 | 60 | func fatalIfErr(t *testing.T, err error) { 61 | if err != nil { 62 | t.Fatal(err) 63 | } 64 | } 65 | --------------------------------------------------------------------------------