Browse Source

Start writing CXX bindings as a main

Arturo Filastò 7 years ago
parent
commit
9ed15d1b63
5 changed files with 188 additions and 314 deletions
  1. 5 0
      Bindings/CXX/.gitignore
  2. 8 0
      Bindings/CXX/Makefile
  3. 111 0
      Bindings/CXX/main.go
  4. 0 314
      Bindings/CXX/psi.go
  5. 64 0
      Bindings/CXX/psi.h

+ 5 - 0
Bindings/CXX/.gitignore

@@ -0,0 +1,5 @@
+psi.a
+main
+psiphon.boltdb
+server_list_compressed
+psiphon-config.json

+ 8 - 0
Bindings/CXX/Makefile

@@ -0,0 +1,8 @@
+shared:
+	go build -buildmode=c-shared -o psi.dylib psi.go
+.PHONY: shared
+
+static:
+	go build -buildmode=c-archive -o psi.a psi.go
+
+.PHONY: static

+ 111 - 0
Bindings/CXX/main.go

@@ -0,0 +1,111 @@
+package main
+
+import "C"
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"time"
+
+	"github.com/Psiphon-Labs/psiphon-tunnel-core/MobileLibrary/psi"
+)
+
+type NoticeEvent struct {
+	Data       struct{} `json:"data"`
+	NoticeType string   `json:"noticeType"`
+}
+
+type PsiphonProvider struct {
+}
+
+func (pp PsiphonProvider) Notice(noticeJSON string) {
+	var event NoticeEvent
+	err := json.Unmarshal([]byte(noticeJSON), &event)
+	if err != nil {
+		fmt.Printf("Failed to unmarshal: %v", err)
+	}
+	if event.NoticeType == "Tunnels" {
+		fmt.Printf("Connected!!")
+	}
+	fmt.Println("💣")
+	fmt.Printf("%s\n", event.NoticeType)
+	fmt.Printf("%s\n", noticeJSON)
+}
+
+func (pp PsiphonProvider) HasNetworkConnectivity() int {
+	return 1
+}
+
+func (pp PsiphonProvider) BindToDevice(fileDescriptor int) (string, error) {
+	return "", nil
+}
+
+func (pp PsiphonProvider) IPv6Synthesize(IPv4Addr string) string {
+	return "::1"
+}
+
+func (pp PsiphonProvider) GetPrimaryDnsServer() string {
+	return "8.8.8.8"
+}
+
+func (pp PsiphonProvider) GetSecondaryDnsServer() string {
+	return "8.8.8.8"
+}
+
+func (pp PsiphonProvider) GetNetworkID() string {
+	return ""
+}
+
+const runtimeTimeout = 90 * time.Second
+
+var provider PsiphonProvider
+
+type StartResult struct {
+	BootstrapTime float64 `json:"bootstrap_time"`
+	ErrorString   string  `json:"error"`
+}
+
+//export Start
+func Start(configJSON,
+	embeddedServerEntryList string) string {
+
+	var result StartResult
+
+	startTime := time.Now().UTC()
+	connectedCtx, cancel := context.WithTimeout(context.Background(), runtimeTimeout)
+	defer cancel()
+
+	fmt.Printf("Passing: %s\n", configJSON)
+
+	err := psi.Start(configJSON, embeddedServerEntryList, "", provider, true, false)
+	if err != nil {
+		fmt.Println(err)
+	}
+	select {
+	case <-connectedCtx.Done():
+		err := connectedCtx.Err()
+		if err != nil {
+			result.ErrorString = err.Error()
+			Stop()
+		}
+		delta := time.Now().UTC().Sub(startTime)
+		result.BootstrapTime = delta.Seconds()
+		b, err := json.Marshal(result)
+		if err != nil {
+			return "{\"error\":\"json_serializitation\"}"
+		}
+		return string(b)
+	}
+}
+
+//export Stop
+func Stop() bool {
+	psi.Stop()
+	return true
+}
+
+func main() {
+	configJSON := ``
+	Start(configJSON, "")
+}

+ 0 - 314
Bindings/CXX/psi.go

@@ -1,314 +0,0 @@
-package psi
-
-// This package is a shim between Java/Obj-C and the "psiphon" package. Due to limitations
-// on what Go types may be exposed (http://godoc.org/golang.org/x/mobile/cmd/gobind),
-// a psiphon.Controller cannot be directly used by Java. This shim exposes a trivial
-// Start/Stop interface on top of a single Controller instance.
-
-import (
-	"context"
-	"encoding/json"
-	"fmt"
-	"os"
-	"strings"
-	"sync"
-
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
-	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/tun"
-)
-
-type PsiphonProvider interface {
-	Notice(noticeJSON string)
-	HasNetworkConnectivity() int
-	BindToDevice(fileDescriptor int) (string, error)
-	IPv6Synthesize(IPv4Addr string) string
-	GetPrimaryDnsServer() string
-	GetSecondaryDnsServer() string
-	GetNetworkID() string
-}
-
-func SetNoticeFiles(
-	homepageFilename,
-	rotatingFilename string,
-	rotatingFileSize,
-	rotatingSyncFrequency int) error {
-
-	return psiphon.SetNoticeFiles(
-		homepageFilename,
-		rotatingFilename,
-		rotatingFileSize,
-		rotatingSyncFrequency)
-}
-
-func NoticeUserLog(message string) {
-	psiphon.NoticeUserLog(message)
-}
-
-var controllerMutex sync.Mutex
-var controller *psiphon.Controller
-var controllerCtx context.Context
-var stopController context.CancelFunc
-var controllerWaitGroup *sync.WaitGroup
-
-func Start(
-	configJson,
-	embeddedServerEntryList,
-	embeddedServerEntryListFilename string,
-	provider PsiphonProvider,
-	useDeviceBinder,
-	useIPv6Synthesizer bool) error {
-
-	controllerMutex.Lock()
-	defer controllerMutex.Unlock()
-
-	if controller != nil {
-		return fmt.Errorf("already started")
-	}
-
-	// Clients may toggle Stop/Start immediately to apply new config settings
-	// such as EgressRegion or Authorizations. When this restart is within the
-	// same process and in a memory contrained environment, it is useful to
-	// force garbage collection here to reclaim memory used by the previous
-	// Controller.
-	psiphon.DoGarbageCollection()
-
-	// Wrap the provider in a layer that locks a mutex before calling a provider function.
-	// The the provider callbacks are Java/Obj-C via gomobile, they are cgo calls that
-	// can cause OS threads to be spawned. The mutex prevents many calling goroutines from
-	// causing unbounded numbers of OS threads to be spawned.
-	// TODO: replace the mutex with a semaphore, to allow a larger but still bounded concurrent
-	// number of calls to the provider?
-	provider = newMutexPsiphonProvider(provider)
-
-	config, err := psiphon.LoadConfig([]byte(configJson))
-	if err != nil {
-		return fmt.Errorf("error loading configuration file: %s", err)
-	}
-
-	config.NetworkConnectivityChecker = provider
-
-	config.NetworkIDGetter = provider
-
-	if useDeviceBinder {
-		config.DeviceBinder = provider
-		config.DnsServerGetter = provider
-	}
-
-	if useIPv6Synthesizer {
-		config.IPv6Synthesizer = provider
-	}
-
-	// All config fields should be set before calling Commit.
-
-	err = config.Commit()
-	if err != nil {
-		return fmt.Errorf("error committing configuration file: %s", err)
-	}
-
-	psiphon.SetNoticeWriter(psiphon.NewNoticeReceiver(
-		func(notice []byte) {
-			provider.Notice(string(notice))
-		}))
-
-	// BuildInfo is a diagnostic notice, so emit only after config.Commit
-	// sets EmitDiagnosticNotices.
-
-	psiphon.NoticeBuildInfo()
-
-	err = psiphon.InitDataStore(config)
-	if err != nil {
-		return fmt.Errorf("error initializing datastore: %s", err)
-	}
-
-	// Stores list of server entries.
-	err = storeServerEntries(
-		config,
-		embeddedServerEntryListFilename,
-		embeddedServerEntryList)
-	if err != nil {
-		return err
-	}
-
-	controller, err = psiphon.NewController(config)
-	if err != nil {
-		return fmt.Errorf("error initializing controller: %s", err)
-	}
-
-	controllerCtx, stopController = context.WithCancel(context.Background())
-
-	controllerWaitGroup = new(sync.WaitGroup)
-	controllerWaitGroup.Add(1)
-	go func() {
-		defer controllerWaitGroup.Done()
-		controller.Run(controllerCtx)
-	}()
-
-	return nil
-}
-
-func Stop() {
-
-	controllerMutex.Lock()
-	defer controllerMutex.Unlock()
-
-	if controller != nil {
-		stopController()
-		controllerWaitGroup.Wait()
-		controller = nil
-		controllerCtx = nil
-		stopController = nil
-		controllerWaitGroup = nil
-	}
-}
-
-// ReconnectTunnel initiates a reconnect of the current tunnel, if one is
-// running.
-func ReconnectTunnel() {
-
-	controllerMutex.Lock()
-	defer controllerMutex.Unlock()
-
-	if controller != nil {
-		controller.TerminateNextActiveTunnel()
-	}
-}
-
-// SetDynamicConfig overrides the sponsor ID and authorizations fields set in
-// the config passed to Start. SetDynamicConfig has no effect if no Controller
-// is started.
-//
-// The input newAuthorizationsList is a space-delimited list of base64
-// authorizations. This is a workaround for gobind type limitations.
-func SetDynamicConfig(newSponsorID, newAuthorizationsList string) {
-
-	controllerMutex.Lock()
-	defer controllerMutex.Unlock()
-
-	if controller != nil {
-		controller.SetDynamicConfig(
-			newSponsorID,
-			strings.Split(newAuthorizationsList, " "))
-	}
-}
-
-// Encrypt and upload feedback.
-func SendFeedback(configJson, diagnosticsJson, b64EncodedPublicKey, uploadServer, uploadPath, uploadServerHeaders string) error {
-	return psiphon.SendFeedback(configJson, diagnosticsJson, b64EncodedPublicKey, uploadServer, uploadPath, uploadServerHeaders)
-}
-
-// Get build info from tunnel-core
-func GetBuildInfo() string {
-	buildInfo, err := json.Marshal(common.GetBuildInfo())
-	if err != nil {
-		return ""
-	}
-	return string(buildInfo)
-}
-
-func GetPacketTunnelMTU() int {
-	return tun.DEFAULT_MTU
-}
-
-func GetPacketTunnelDNSResolverIPv4Address() string {
-	return tun.GetTransparentDNSResolverIPv4Address().String()
-}
-
-func GetPacketTunnelDNSResolverIPv6Address() string {
-	return tun.GetTransparentDNSResolverIPv6Address().String()
-}
-
-// Helper function to store a list of server entries.
-// if embeddedServerEntryListFilename is not empty, embeddedServerEntryList will be ignored.
-func storeServerEntries(
-	config *psiphon.Config,
-	embeddedServerEntryListFilename, embeddedServerEntryList string) error {
-
-	if embeddedServerEntryListFilename != "" {
-
-		file, err := os.Open(embeddedServerEntryListFilename)
-		if err != nil {
-			return fmt.Errorf("error reading embedded server list file: %s", common.ContextError(err))
-		}
-		defer file.Close()
-
-		err = psiphon.StreamingStoreServerEntries(
-			config,
-			protocol.NewStreamingServerEntryDecoder(
-				file,
-				common.GetCurrentTimestamp(),
-				protocol.SERVER_ENTRY_SOURCE_EMBEDDED),
-			false)
-		if err != nil {
-			return fmt.Errorf("error storing embedded server list: %s", common.ContextError(err))
-		}
-
-	} else {
-
-		serverEntries, err := protocol.DecodeServerEntryList(
-			embeddedServerEntryList,
-			common.GetCurrentTimestamp(),
-			protocol.SERVER_ENTRY_SOURCE_EMBEDDED)
-		if err != nil {
-			return fmt.Errorf("error decoding embedded server list: %s", err)
-		}
-		err = psiphon.StoreServerEntries(config, serverEntries, false)
-		if err != nil {
-			return fmt.Errorf("error storing embedded server list: %s", err)
-		}
-	}
-
-	return nil
-}
-
-type mutexPsiphonProvider struct {
-	sync.Mutex
-	p PsiphonProvider
-}
-
-func newMutexPsiphonProvider(p PsiphonProvider) *mutexPsiphonProvider {
-	return &mutexPsiphonProvider{p: p}
-}
-
-func (p *mutexPsiphonProvider) Notice(noticeJSON string) {
-	p.Lock()
-	defer p.Unlock()
-	p.p.Notice(noticeJSON)
-}
-
-func (p *mutexPsiphonProvider) HasNetworkConnectivity() int {
-	p.Lock()
-	defer p.Unlock()
-	return p.p.HasNetworkConnectivity()
-}
-
-func (p *mutexPsiphonProvider) BindToDevice(fileDescriptor int) (string, error) {
-	p.Lock()
-	defer p.Unlock()
-	return p.p.BindToDevice(fileDescriptor)
-}
-
-func (p *mutexPsiphonProvider) IPv6Synthesize(IPv4Addr string) string {
-	p.Lock()
-	defer p.Unlock()
-	return p.p.IPv6Synthesize(IPv4Addr)
-}
-
-func (p *mutexPsiphonProvider) GetPrimaryDnsServer() string {
-	p.Lock()
-	defer p.Unlock()
-	return p.p.GetPrimaryDnsServer()
-}
-
-func (p *mutexPsiphonProvider) GetSecondaryDnsServer() string {
-	p.Lock()
-	defer p.Unlock()
-	return p.p.GetSecondaryDnsServer()
-}
-
-func (p *mutexPsiphonProvider) GetNetworkID() string {
-	p.Lock()
-	defer p.Unlock()
-	return p.p.GetNetworkID()
-}

+ 64 - 0
Bindings/CXX/psi.h

@@ -0,0 +1,64 @@
+/* Created by "go tool cgo" - DO NOT EDIT. */
+
+/* package command-line-arguments */
+
+/* Start of preamble from import "C" comments.  */
+
+
+
+
+/* End of preamble from import "C" comments.  */
+
+
+/* Start of boilerplate cgo prologue.  */
+#line 1 "cgo-gcc-export-header-prolog"
+
+#ifndef GO_CGO_PROLOGUE_H
+#define GO_CGO_PROLOGUE_H
+
+typedef signed char GoInt8;
+typedef unsigned char GoUint8;
+typedef short GoInt16;
+typedef unsigned short GoUint16;
+typedef int GoInt32;
+typedef unsigned int GoUint32;
+typedef long long GoInt64;
+typedef unsigned long long GoUint64;
+typedef GoInt64 GoInt;
+typedef GoUint64 GoUint;
+typedef __SIZE_TYPE__ GoUintptr;
+typedef float GoFloat32;
+typedef double GoFloat64;
+typedef float _Complex GoComplex64;
+typedef double _Complex GoComplex128;
+
+/*
+  static assertion to make sure the file is being used on architecture
+  at least with matching size of GoInt.
+*/
+typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
+
+typedef struct { const char *p; GoInt n; } GoString;
+typedef void *GoMap;
+typedef void *GoChan;
+typedef struct { void *t; void *v; } GoInterface;
+typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
+
+#endif
+
+/* End of boilerplate cgo prologue.  */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern GoUint8 Start(GoString p0);
+
+extern GoUint8 Stop();
+
+extern GoUint8 RegisterCallback(GoString p0, void* p1);
+
+#ifdef __cplusplus
+}
+#endif