Просмотр исходного кода

Merge branch 'master' of https://github.com/Psiphon-Labs/psiphon-tunnel-core

Rod Hynes 7 лет назад
Родитель
Сommit
a4933c41e3

+ 31 - 0
ClientLibrary/Dockerfile

@@ -0,0 +1,31 @@
+# Dockerfile to build the Psiphon Client Library for multiple platforms.
+#
+# See README.md for usage instructions.
+
+FROM ubuntu:16.04
+
+# Install system-level dependencies.
+ENV DEBIAN_FRONTEND=noninteractive
+RUN apt-get update -y && apt-get install -y --no-install-recommends \
+    build-essential \
+    ca-certificates \
+    curl \
+    gcc-mingw-w64-i686 \
+    gcc-mingw-w64-x86-64 \
+    gcc-multilib \
+    git \
+    python \
+    unzip \
+  && apt-get clean \
+  && rm -rf /var/lib/apt/lists/*
+
+# Install Go.
+# NOTE: Go 1.10+ is required to build c-shared for windows (https://github.com/golang/go/commit/bb0bfd002ada7e3eb9198d4287b32c2fed6e8da6)
+ENV GOVERSION=go1.10.3 GOROOT=/usr/local/go GOPATH=/go PATH=$PATH:/usr/local/go/bin:/go/bin CGO_ENABLED=1
+
+RUN curl -L https://storage.googleapis.com/golang/$GOVERSION.linux-amd64.tar.gz -o /tmp/go.tar.gz \
+   && tar -C /usr/local -xzf /tmp/go.tar.gz \
+   && rm /tmp/go.tar.gz \
+   && echo $GOVERSION > $GOROOT/VERSION
+
+WORKDIR $GOPATH/src/github.com/Psiphon-Labs/psiphon-tunnel-core/ClientLibrary

+ 33 - 4
ClientLibrary/PsiphonTunnel.go

@@ -1,5 +1,6 @@
 package main
 
+// #include <stdlib.h>
 import "C"
 
 import (
@@ -9,6 +10,7 @@ import (
 	"fmt"
 	"sync"
 	"time"
+	"unsafe"
 
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
@@ -46,7 +48,17 @@ type psiphonTunnel struct {
 
 var tunnel psiphonTunnel
 
+// Memory managed by PsiphonTunnel which is allocated in Start and freed in Stop
+var managedStartResult *C.char
+
 //export Start
+//
+// ******************************* WARNING ********************************
+// The underlying memory referenced by the return value of Start is managed
+// by PsiphonTunnel and attempting to free it explicitly will cause the
+// program to crash. This memory is freed once Stop is called.
+// ************************************************************************
+//
 // Start starts the controller and returns once either of the following has occured: an active tunnel has been
 // established, the timeout has elapsed before an active tunnel could be established or an error has occured.
 //
@@ -213,9 +225,13 @@ func Start(configJSON, embeddedServerEntryList, networkID string, timeout int64)
 		tunnel.stopController()
 	}
 
+	// Free previous result
+	freeManagedStartResult()
+
 	// Return result
+	managedStartResult = marshalStartResult(result)
 
-	return marshalstartResult(result)
+	return managedStartResult
 }
 
 //export Stop
@@ -224,9 +240,12 @@ func Start(configJSON, embeddedServerEntryList, networkID string, timeout int64)
 // Stop should always be called after a successful call to Start to ensure the
 // controller is not left running.
 func Stop() {
+	freeManagedStartResult()
+
 	if tunnel.stopController != nil {
 		tunnel.stopController()
 	}
+
 	tunnel.controllerWaitGroup.Wait()
 }
 
@@ -236,9 +255,9 @@ func secondsBeforeNow(startTime time.Time) float64 {
 	return delta.Seconds()
 }
 
-// marshalstartResult serializes a startResult object as a JSON string in the form
+// marshalStartResult serializes a startResult object as a JSON string in the form
 // of a null-terminated buffer of C chars.
-func marshalstartResult(result startResult) *C.char {
+func marshalStartResult(result startResult) *C.char {
 	resultJSON, err := json.Marshal(result)
 	if err != nil {
 		return C.CString(fmt.Sprintf("{\"result_code\":%d, \"error\": \"%s\"}", startResultCodeOtherError, err.Error()))
@@ -261,7 +280,17 @@ func startErrorJson(err error) *C.char {
 	result.Code = startResultCodeOtherError
 	result.ErrorString = err.Error()
 
-	return marshalstartResult(result)
+	return marshalStartResult(result)
+}
+
+// freeManagedStartResult frees the memory on the heap pointed to by managedStartResult.
+func freeManagedStartResult() {
+	if managedStartResult != nil {
+		managedMemory := unsafe.Pointer(managedStartResult)
+		if managedMemory != nil {
+			C.free(managedMemory)
+		}
+	}
 }
 
 // main is a stub required by cgo.

+ 80 - 0
ClientLibrary/README.md

@@ -7,3 +7,83 @@ If you are planning to embed Psiphon in a mobile application, please use the [Mo
 ## Usage
 
 If you are using the Library in your app, please read the [USAGE.md](USAGE.md) instructions.
+
+## Building for Darwin (iOS, MacOS)
+
+Note that you will need to have Xcode installed on a machine running MacOS.
+
+##### Run the build:
+
+*Ensure that the command below is run from within the `ClientLibrary` directory*
+
+```
+./build-darwin.sh all
+```
+
+This command can also be modified by:
+ - replacing `all` with `ios` or `macos` as the first parameter to `build-darwin.sh` (as in `./build-darwin.sh ios`) to only build binaries for the operating system of choice
+
+When that command completes, the compiled binaries will be located in the `build` directory. The structure will be:
+
+```
+build
+└── darwin
+    └── ios
+    │   └── PsiphonTunnel-ios-arm.h
+    │   └── PsiphonTunnel-ios-arm.dylib
+    │   └── PsiphonTunnel-ios-arm64.h
+    │   └── PsiphonTunnel-ios-arm64.dylib
+    └── macos
+        └── PsiphonTunnel-macos-386.dylib
+        └── PsiphonTunnel-macos-386.dylib
+        └── PsiphonTunnel-macos-amd64.dylib
+        └── PsiphonTunnel-macos-amd64.dylib
+```
+
+## Building with Docker (Android, Linux, Windows)
+
+Note that you may need to use `sudo docker` below, depending on your OS.
+
+##### Create the build image:
+
+1. While in the `ClientLibrary` directory, run the command: `docker build --no-cache=true -t psiclientlibrary-builder .`
+
+2. Once completed, verify that you see an image named `psiclientlibrary-builder` when running: `docker images`
+
+##### Run the build:
+
+*Ensure that the command below is run from within the `ClientLibrary` directory*
+
+```bash
+cd .. && \
+  docker run \
+  --rm \
+  -v $PWD:/go/src/github.com/Psiphon-Labs/psiphon-tunnel-core \
+  psiclientlibrary-builder \
+  /bin/bash -c './make.bash all' \
+; cd -
+```
+
+This command can also be modified by:
+ - replacing `all` with `android`, `linux`, or `windows` as the first parameter to `make.bash` (as in `./make.bash windows`) to only build binaries for the operating system of choice
+
+When that command completes, the compiled binaries will be located in the `build` directory (`./build`, and everything under it will likely be owned by root, so be sure to `chown` to an appropriate user) under the current directory. The structure will be:
+
+```
+build
+├── android
+│   └── PsiphonTunnel-android-arm7.h
+│   └── PsiphonTunnel-android-arm7.so
+│   └── PsiphonTunnel-android-arm64.h
+│   └── PsiphonTunnel-android-arm64.so
+├── linux
+│   └── PsiphonTunnel-linux-386.h
+│   └── PsiphonTunnel-linux-386.so
+│   └── PsiphonTunnel-linux-amd64.h
+│   └── PsiphonTunnel-linux-amd64.so
+└── windows
+    └── PsiphonTunnel-windows-386.h
+    └── PsiphonTunnel-windows-386.dll
+    └── PsiphonTunnel-windows-amd64.h
+    └── PsiphonTunnel-windows-amd64.dll
+```

+ 223 - 0
ClientLibrary/build-darwin.sh

@@ -0,0 +1,223 @@
+#!/usr/bin/env bash
+
+# -x echos commands. 
+# -e exits if a command returns an error.
+set -x -e
+
+# This script takes an optional second argument: 'private', if private plugins should
+# be used. It should be omitted if private plugins are not desired.
+if [[ $2 == "private" ]]; then
+  FORCE_PRIVATE_PLUGINS=true
+  echo "TRUE"
+else
+  FORCE_PRIVATE_PLUGINS=false
+  echo "FALSE"
+fi
+
+# Modify this value as we use newer Go versions.
+GO_VERSION_REQUIRED="1.9.6"
+
+BASE_DIR=$(cd "$(dirname "$0")" ; pwd -P)
+cd ${BASE_DIR}
+
+# The location of the final build products
+BUILD_DIR="${BASE_DIR}/build/darwin"
+TEMP_DIR="${BUILD_DIR}/tmp"
+
+# Clean previous output
+rm -rf "${BUILD_DIR}"
+
+mkdir -p ${TEMP_DIR}
+if [[ $? != 0 ]]; then
+  echo "FAILURE: mkdir -p ${TEMP_DIR}"
+  exit 1
+fi
+
+# Ensure go is installed
+which go 2>&1 > /dev/null
+if [[ $? != 0 ]]; then
+  echo "Go is not installed in the path, aborting"
+  exit 1
+fi
+
+PRIVATE_PLUGINS_TAG=""
+if [[ ${FORCE_PRIVATE_PLUGINS} == true ]]; then PRIVATE_PLUGINS_TAG="PRIVATE_PLUGINS"; fi
+
+# Exporting these seems necessary for subcommands to pick them up.
+export GOPATH=${TEMP_DIR}/go-darwin-build
+export PATH=${GOPATH}/bin:${PATH}
+
+# The GOPATH we're using is temporary, so make sure there isn't one from a previous run.
+rm -rf ${GOPATH}
+
+TUNNEL_CORE_SRC_DIR=${GOPATH}/src/github.com/Psiphon-Labs/psiphon-tunnel-core
+
+mkdir -p ${GOPATH}
+if [[ $? != 0 ]]; then
+  echo "FAILURE: mkdir -p ${GOPATH}"
+  exit 1
+fi
+
+# Symlink the current source directory into GOPATH, so that we're building the
+# code in this local repo, rather than pulling from Github and building that.
+mkdir -p ${GOPATH}/src/github.com/Psiphon-Labs
+if [[ $? != 0 ]]; then
+  echo "mkdir -p ${GOPATH}/src/github.com/Psiphon-Labs"
+  exit 1
+fi
+ln -s "${BASE_DIR}/.." "${GOPATH}/src/github.com/Psiphon-Labs/psiphon-tunnel-core"
+if [[ $? != 0 ]]; then
+  echo "ln -s ../.. ${GOPATH}/src/github.com/Psiphon-Labs/psiphon-tunnel-core"
+  exit 1
+fi
+
+# Check Go version
+GO_VERSION=$(go version | sed -E -n 's/.*go([0-9]\.[0-9]+(\.[0-9]+)?).*/\1/p')
+if [[ ${GO_VERSION} != ${GO_VERSION_REQUIRED} ]]; then
+  echo "FAILURE: go version mismatch; require ${GO_VERSION_REQUIRED}; got ${GO_VERSION}"
+  exit 1
+fi
+
+prepare_build () {
+
+  # Ensure BUILD* variables reflect the tunnel-core repo
+  cd ${TUNNEL_CORE_SRC_DIR}
+
+  BUILDDATE=$(date +%Y-%m-%dT%H:%M:%S%z)
+  BUILDREPO=$(git config --get remote.origin.url)
+  BUILDREV=$(git rev-parse --short HEAD)
+  GOVERSION=$(go version | perl -ne '/go version (.*?) / && print $1')
+
+  # see DEPENDENCIES comment in MobileLibrary/Android/make.bash
+  cd ${GOPATH}/src/github.com/Psiphon-Labs/psiphon-tunnel-core/ClientLibrary
+  DEPENDENCIES=$(echo -n "{" && go list -tags "$1" -f '{{range $dep := .Deps}}{{printf "%s\n" $dep}}{{end}}' | xargs go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' | xargs -I pkg bash -c 'cd $GOPATH/src/pkg && echo -n "\"pkg\":\"$(git rev-parse --short HEAD)\","' | sed 's/,$/}/')
+
+  LDFLAGS="\
+  -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common.buildDate=$BUILDDATE \
+  -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common.buildRepo=$BUILDREPO \
+  -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common.buildRev=$BUILDREV \
+  -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common.goVersion=$GOVERSION \
+  -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common.dependencies=$DEPENDENCIES \
+  "
+
+  echo "Variables for ldflags:"
+  echo " Build date: ${BUILDDATE}"
+  echo " Build repo: ${BUILDREPO}"
+  echo " Build revision: ${BUILDREV}"
+  echo " Go version: ${GOVERSION}"
+  echo " Dependencies: ${DEPENDENCIES}"
+  echo ""
+
+}
+
+
+build_for_ios () {
+
+  IOS_BUILD_TAGS="IOS ${PRIVATE_PLUGINS_TAG}"
+  IOS_BUILD_DIR="${BUILD_DIR}/ios"
+  rm -rf "${IOS_BUILD_DIR}"
+
+  echo "...Getting project dependencies (via go get) for iOS."
+  cd ${BASE_DIR}
+  GOOS=darwin go get -d -v -tags "$IOS_BUILD_TAGS" ./...
+  prepare_build "$IOS_BUILD_TAGS"
+  if [ $? != 0 ]; then
+    echo "....'go get' failed, exiting"
+    exit $?
+  fi
+
+  curl https://raw.githubusercontent.com/golang/go/master/misc/ios/clangwrap.sh -o ${TEMP_DIR}/clangwrap.sh
+  chmod 555 ${TEMP_DIR}/clangwrap.sh
+
+  CC=${TEMP_DIR}/clangwrap.sh \
+  CXX=${TEMP_DIR}/clangwrap.sh \
+  CGO_LDFLAGS="-arch armv7 -isysroot $(xcrun --sdk iphoneos --show-sdk-path)" \
+  CGO_CFLAGS=-isysroot$(xcrun --sdk iphoneos --show-sdk-path) \
+  CGO_ENABLED=1 GOOS=darwin GOARCH=arm GOARM=7 go build -buildmode=c-archive -ldflags "$LDFLAGS" -tags "${IOS_BUILD_TAGS}" -o ${IOS_BUILD_DIR}/PsiphonTunnel-ios-arm.dylib PsiphonTunnel.go
+
+  CC=${TEMP_DIR}/clangwrap.sh \
+  CXX=${TEMP_DIR}/clangwrap.sh \
+  CGO_LDFLAGS="-arch arm64 -isysroot $(xcrun --sdk iphoneos --show-sdk-path)" \
+  CGO_CFLAGS=-isysroot$(xcrun --sdk iphoneos --show-sdk-path) \
+  CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -buildmode=c-archive -ldflags "$LDFLAGS" -tags "${IOS_BUILD_TAGS}" -o ${IOS_BUILD_DIR}/PsiphonTunnel-ios-arm64.dylib PsiphonTunnel.go
+
+}
+
+build_for_macos () {
+
+  MACOS_BUILD_TAGS="${PRIVATE_PLUGINS_TAG}"
+  MACOS_BUILD_DIR="${BUILD_DIR}/macos"
+  rm -rf "${MACOS_BUILD_DIR}"
+
+  echo "...Getting project dependencies (via go get) for MacOS"
+  cd ${BASE_DIR}
+  GOOS=darwin go get -d -v -tags "$MACOS_BUILD_TAGS" ./...
+  prepare_build "$MACOS_BUILD_TAGS"
+  if [ $? != 0 ]; then
+    echo "....'go get' failed, exiting"
+    exit $?
+  fi
+
+  TARGET_ARCH=386
+  CGO_ENABLED=1 GOOS=darwin GOARCH="${TARGET_ARCH}" go build -buildmode=c-archive -ldflags "$LDFLAGS" -tags "${MACOS_BUILD_TAGS}" -o "${MACOS_BUILD_DIR}/PsiphonTunnel-macos-${TARGET_ARCH}.dylib" PsiphonTunnel.go
+
+  TARGET_ARCH=amd64
+  CGO_ENABLED=1 GOOS=darwin GOARCH="${TARGET_ARCH}" go build -buildmode=c-archive -ldflags "$LDFLAGS" -tags "${MACOS_BUILD_TAGS}" -o "${MACOS_BUILD_DIR}/PsiphonTunnel-macos-${TARGET_ARCH}.dylib" PsiphonTunnel.go
+
+}
+
+cleanup () {
+  # Remove temporary build artifacts
+  rm -rf ${TEMP_DIR}
+}
+
+
+TARGET=$1
+case $TARGET in
+  macos)
+    echo "..Building for MacOS"
+    build_for_macos
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    ;;
+  ios)
+    echo "..Building for iOS"
+    build_for_ios
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    ;;
+  all)
+    echo "..Building all"
+    build_for_ios
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    build_for_macos
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    ;;
+  *)
+    echo "..No selection made, building all"
+    build_for_ios
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    build_for_macos
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    ;;
+
+esac
+
+cleanup
+echo "BUILD DONE"

+ 2 - 2
ClientLibrary/example/main.c

@@ -62,7 +62,7 @@ int main(int argc, char *argv[]) {
     // print results
     printf("Result: %s\n", result);
 
-    // cleanup
-    free(result);
+    // The underlying memory of `result` is managed by PsiphonTunnel and will
+    // have been freed in Stop.
 }
 

+ 274 - 0
ClientLibrary/make.bash

@@ -0,0 +1,274 @@
+#!/usr/bin/env bash
+
+# -x echos commands.
+# -e exits if a command returns an error.
+set -x -e
+
+if [ ! -f make.bash ]; then
+  echo "make.bash must be run from $GOPATH/src/github.com/Psiphon-Labs/psiphon-tunnel-core/ClientLibrary"
+  exit 1
+fi
+
+# This script takes an optional second argument: 'private', if private plugins should
+# be used. It should be omitted if private plugins are not desired.
+if [[ $2 == "private" ]]; then
+  FORCE_PRIVATE_PLUGINS=true
+  echo "TRUE"
+else
+  FORCE_PRIVATE_PLUGINS=false
+  echo "FALSE"
+fi
+
+# BUILD_TAGS needs to be outside of prepare_build because it determines what's fetched by go-get.
+
+PRIVATE_PLUGINS_TAG=""
+if [[ ${FORCE_PRIVATE_PLUGINS} == true ]]; then PRIVATE_PLUGINS_TAG="PRIVATE_PLUGINS"; fi
+BUILD_TAGS="${PRIVATE_PLUGINS_TAG}"
+WINDOWS_BUILD_TAGS="${BUILD_TAGS}"
+LINUX_BUILD_TAGS="${BUILD_TAGS}"
+ANDROID_BUILD_TAGS="${BUILD_TAGS}"
+
+BUILD_DIR=build
+
+if [ ! -d ${BUILD_DIR} ]; then
+  mkdir ${BUILD_DIR}
+fi
+
+
+prepare_build () {
+
+  BUILDDATE=$(date --iso-8601=seconds)
+  BUILDREPO=$(git config --get remote.origin.url)
+  BUILDREV=$(git rev-parse --short HEAD)
+  GOVERSION=$(go version | perl -ne '/go version (.*?) / && print $1')
+
+  # - starts the string with a `{`
+  # - uses the `go list` command and passes it a template string (using the Go template syntax) saying I want all the dependencies of the package in the current directory, printing 1/line via printf
+  # - pipes to `xargs` to run a command on each line output from the first command
+  #  - uses `go list` with a template string to print the "Import Path" (from just below `$GOPATH/src`) if the package is not part of the standard library
+  # - pipes to `xargs` again, specifiying `pkg` as the placeholder name for each item being operated on (which is the list of non standard library import paths from the previous step)
+  #  - `xargs` runs a bash script (via `-c`) which changes to each import path in sequence, then echoes out `"<import path>":"<subshell output of getting the short git revision>",`
+  # - this leaves a trailing `,` at the end, and no close to the JSON object, so simply `sed` replace the comma before the end of the line with `}` and you now have valid JSON
+  DEPENDENCIES=$(echo -n "{" && go list -tags "$1" -f '{{range $dep := .Deps}}{{printf "%s\n" $dep}}{{end}}' | xargs go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' | xargs -I pkg bash -c 'cd $GOPATH/src/pkg && echo -n "\"pkg\":\"$(git rev-parse --short HEAD)\","' | sed 's/,$/}/')
+
+  LDFLAGS="\
+  -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common.buildDate=$BUILDDATE \
+  -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common.buildRepo=$BUILDREPO \
+  -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common.buildRev=$BUILDREV \
+  -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common.goVersion=$GOVERSION \
+  -X github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common.dependencies=$DEPENDENCIES \
+  "
+
+  echo "Variables for ldflags:"
+  echo " Build date: ${BUILDDATE}"
+  echo " Build repo: ${BUILDREPO}"
+  echo " Build revision: ${BUILDREV}"
+  echo " Go version: ${GOVERSION}"
+  echo " Dependencies: ${DEPENDENCIES}"
+  echo ""
+
+}
+
+
+build_for_android () {
+
+  TARGET_OS=android
+  OUTPUT_DIR="${BUILD_DIR}/${TARGET_OS}"
+
+  echo "...Getting project dependencies (via go get) for Android."
+  GOOS=windows go get -d -v -tags "$ANDROID_BUILD_TAGS" ./...
+  prepare_build "$ANDROID_BUILD_TAGS"
+  if [ $? != 0 ]; then
+    echo "....'go get' failed, exiting"
+    exit $?
+  fi
+
+  TARGET_NDK=android-ndk-r17b
+  curl https://dl.google.com/android/repository/${TARGET_NDK}-linux-x86_64.zip -o ~/android-ndk.zip
+  unzip ~/android-ndk.zip -d ~/
+
+  NDK_TOOLCHAIN_DIR=~/android-ndk-toolchain
+  mkdir -p ${NDK_TOOLCHAIN_DIR}
+
+  TARGET_ARCH=arm
+  ARMV=7
+  ~/${TARGET_NDK}/build/tools/make_standalone_toolchain.py --arch "${TARGET_ARCH}" --install-dir "${NDK_TOOLCHAIN_DIR}/${TARGET_ARCH}"
+
+  CC="${NDK_TOOLCHAIN_DIR}/${TARGET_ARCH}/bin/arm-linux-androideabi-clang" \
+  CXX="${NDK_TOOLCHAIN_DIR}/${TARGET_ARCH}/bin/arm-linux-androideabi-clang++" \
+  GOARM=${ARMV} \
+  GOOS=${TARGET_OS} GOARCH=${TARGET_ARCH} go build -buildmode=c-shared -ldflags "$LDFLAGS" -tags "$ANDROID_BUILD_TAGS" -o "${OUTPUT_DIR}/PsiphonTunnel-${TARGET_OS}-${TARGET_ARCH}${ARMV}.so" PsiphonTunnel.go
+
+
+  TARGET_ARCH=arm64
+  ~/${TARGET_NDK}/build/tools/make_standalone_toolchain.py --arch "${TARGET_ARCH}" --install-dir "${NDK_TOOLCHAIN_DIR}/${TARGET_ARCH}"
+
+  CC="${NDK_TOOLCHAIN_DIR}/${TARGET_ARCH}/bin/aarch64-linux-android-clang" \
+  CXX="${NDK_TOOLCHAIN_DIR}/${TARGET_ARCH}/bin/aarch64-linux-android-clang++" \
+  GOOS=${TARGET_OS} GOARCH=${TARGET_ARCH} go build -buildmode=c-shared -ldflags "$LDFLAGS" -tags "$ANDROID_BUILD_TAGS" -o "${OUTPUT_DIR}/PsiphonTunnel-${TARGET_OS}-${TARGET_ARCH}.so" PsiphonTunnel.go
+
+}
+
+
+build_for_linux () {
+
+  TARGET_OS=linux
+  OUTPUT_DIR="${BUILD_DIR}/${TARGET_OS}"
+
+  echo "...Getting project dependencies (via go get) for Linux."
+  GOOS=linux go get -d -v -tags "$LINUX_BUILD_TAGS" ./...
+  prepare_build "$LINUX_BUILD_TAGS"
+  if [ $? != 0 ]; then
+    echo "....'go get' failed, exiting"
+    exit $?
+  fi
+
+  TARGET_ARCH=386
+  # TODO: is "CFLAGS=-m32" required?
+  CFLAGS=-m32 \
+  GOOS=${TARGET_OS} GOARCH=${TARGET_ARCH} go build -buildmode=c-shared -ldflags "$LDFLAGS" -tags "$LINUX_BUILD_TAGS" -o "${OUTPUT_DIR}/PsiphonTunnel-${TARGET_OS}-${TARGET_ARCH}.so" PsiphonTunnel.go
+
+
+  TARGET_ARCH=amd64
+  GOOS=${TARGET_OS} GOARCH=${TARGET_ARCH} go build -buildmode=c-shared -ldflags "$LDFLAGS" -tags "$LINUX_BUILD_TAGS" -o "${OUTPUT_DIR}/PsiphonTunnel-${TARGET_OS}-${TARGET_ARCH}.so" PsiphonTunnel.go
+
+}
+
+
+build_for_windows () {
+
+  TARGET_OS=windows
+  OUTPUT_DIR="${BUILD_DIR}/${TARGET_OS}"
+
+  echo "...Getting project dependencies (via go get) for Windows."
+  GOOS=windows go get -d -v -tags "$WINDOWS_BUILD_TAGS" ./...
+  prepare_build "$WINDOWS_BUILD_TAGS"
+  if [ $? != 0 ]; then
+    echo "....'go get' failed, exiting"
+    exit $?
+  fi
+
+  TARGET_ARCH=386
+
+  CGO_ENABLED=1 \
+  CGO_LDFLAGS="-static-libgcc -L /usr/i686-w64-mingw32/lib/ -lwsock32 -lcrypt32 -lgdi32" \
+  CC=/usr/bin/i686-w64-mingw32-gcc \
+  GOOS=${TARGET_OS} GOARCH=${TARGET_ARCH} go build -buildmode=c-shared -ldflags "$LDFLAGS" -tags "$WINDOWS_BUILD_TAGS" -o "${OUTPUT_DIR}/PsiphonTunnel-${TARGET_OS}-${TARGET_ARCH}.dll" PsiphonTunnel.go
+
+
+  TARGET_ARCH=amd64
+
+  CGO_ENABLED=1 \
+  CGO_LDFLAGS="-static-libgcc -L /usr/x86_64-w64-mingw32/lib/ -lwsock32 -lcrypt32 -lgdi32" \
+  CC=/usr/bin/x86_64-w64-mingw32-gcc \
+  GOOS=${TARGET_OS} GOARCH=${TARGET_ARCH} go build -buildmode=c-shared -ldflags "$LDFLAGS" -tags "$WINDOWS_BUILD_TAGS" -o "${OUTPUT_DIR}/PsiphonTunnel-${TARGET_OS}-${TARGET_ARCH}.dll" PsiphonTunnel.go
+
+}
+
+
+build_for_ios () {
+
+  echo "To build for iOS please use build-darwin.sh"
+
+}
+
+
+build_for_macos () {
+
+  echo "To build for macos please use build-darwin.sh"
+
+}
+
+
+TARGET=$1
+case $TARGET in
+  windows)
+    echo "..Building for Windows"
+    build_for_windows $2
+    exit $?
+
+    ;;
+  linux)
+    echo "..Building for Linux"
+    build_for_linux $2
+    exit $?
+
+    ;;
+  macos)
+    echo "..Building for MacOS"
+    build_for_macos
+    exit $?
+
+    ;;
+  android)
+    echo "..Building for Android"
+    build_for_android
+    exit $?
+
+    ;;
+  ios)
+    echo "..Building for iOS"
+    build_for_ios
+    exit $?
+
+    ;;
+  all)
+    echo "..Building all"
+    build_for_windows $2
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    build_for_linux $2
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    build_for_macos
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    build_for_android
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    build_for_ios
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    ;;
+  *)
+    echo "..No selection made, building all"
+    build_for_windows $2
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    build_for_linux $2
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    build_for_macos
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    build_for_android
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    build_for_ios
+    if [ $? != 0 ]; then
+      exit $?
+    fi
+
+    ;;
+
+esac
+
+echo "BUILD DONE"

+ 27 - 0
ClientLibrary/privatePlugins.go

@@ -0,0 +1,27 @@
+// +build PRIVATE_PLUGINS
+
+/*
+ * Copyright (c) 2015, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package main
+
+import (
+	_ "github.com/Psiphon-Inc/psiphon-tunnel-core-private-plugins/client_plugins"
+	_ "github.com/Psiphon-Inc/psiphon-tunnel-core-private-plugins/common_plugins"
+)