Ai
1 Star 0 Fork 0

lqinggang/psiphon-tunnel-core

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
PsiphonTunnel.go 10.54 KB
一键复制 编辑 原始数据 按行查看 历史
adam-p 提交于 2018-11-29 04:26 +08:00 . Refactor client library into a separate package
/*
* Copyright (c) 2018, 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
/*
#include <stdlib.h>
#include <stdint.h>
// For descriptions of fields, see below.
// Additional information can also be found in the Parameters structure in clientlib.go.
struct Parameters {
size_t sizeofStruct; // Must be set to sizeof(Parameters); helps with ABI compatibiity
char *dataRootDirectory;
char *clientPlatform;
char *networkID;
int32_t *establishTunnelTimeoutSeconds;
};
*/
import "C"
import (
"context"
"encoding/json"
"fmt"
"time"
"unsafe"
"github.com/Psiphon-Labs/psiphon-tunnel-core/ClientLibrary/clientlib"
"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
)
/*
If/when new fields are added to the C Parameters struct, we can use this code to ensure
ABI compatibility. We'll take these steps:
1. Copy the old struct into a new `ParametersV1`. The new struct will be `Parameters`.
2. Uncomment the code below. It will not compile (link, specifically) if the size of
`Parameters` is the same as the size of `ParametersV1`.
- If the compile fails, padding may need to be added to `Parameters` to force it to be
a different size than `ParametersV1`.
3. In `Start`, we'll check the value of `sizeofStruct` to determine which version of
`Parameters` the caller is using, and behave according.
4. Do similar kinds of things for V2, V3, etc.
*/
/*
func nonexistentFunction()
func init() {
if C.sizeof_struct_Parameters == C.sizeof_struct_ParametersV1 {
// There is only an attempt to link this nonexistent function if the struct sizes
// are the same. So they must not be.
nonexistentFunction()
}
}
*/
type startResultCode int
const (
startResultCodeSuccess startResultCode = 0
startResultCodeTimeout = 1
startResultCodeOtherError = 2
)
type noticeEvent struct {
Data map[string]interface{}
NoticeType string
}
type startResult struct {
Code startResultCode
ConnectTimeMS int64 `json:",omitempty"`
Error string `json:",omitempty"`
HTTPProxyPort int `json:",omitempty"`
SOCKSProxyPort int `json:",omitempty"`
}
var tunnel *clientlib.PsiphonTunnel
// Memory managed by PsiphonTunnel which is allocated in Start and freed in Stop
var managedStartResult *C.char
//export PsiphonTunnelStart
//
// ******************************* 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, or if Start
// is called again.
// ************************************************************************
//
// Start starts the controller and returns once one 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.
//
// Start returns a startResult object serialized as a JSON string in the form of a
// null-terminated buffer of C chars.
// Start will return,
// On success:
// {
// "Code": 0,
// "ConnectTimeMS": <milliseconds to establish tunnel>,
// "HTTPProxyPort": <http proxy port number>,
// "SOCKSProxyPort": <socks proxy port number>
// }
//
// On timeout:
// {
// "Code": 1,
// "Error": <error message>
// }
//
// On other error:
// {
// "Code": 2,
// "Error": <error message>
// }
//
// Parameters.clientPlatform should be of the form OS_OSVersion_BundleIdentifier where
// both the OSVersion and BundleIdentifier fields are optional. If clientPlatform is set
// to an empty string the "ClientPlatform" field in the provided JSON config will be
// used instead.
//
// Provided below are links to platform specific code which can be used to find some of the above fields:
// Android:
// - OSVersion: https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/3d344194d21b250e0f18ededa4b4459a373b0690/MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java#L573
// - BundleIdentifier: https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/3d344194d21b250e0f18ededa4b4459a373b0690/MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java#L575
// iOS:
// - OSVersion: https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/3d344194d21b250e0f18ededa4b4459a373b0690/MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m#L612
// - BundleIdentifier: https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/3d344194d21b250e0f18ededa4b4459a373b0690/MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m#L622
//
// Some examples of valid client platform strings are:
//
// "Android_4.2.2_com.example.exampleApp"
// "iOS_11.4_com.example.exampleApp"
// "Windows"
//
// Parameters.networkID must be a non-empty string and follow the format specified by:
// https://godoc.org/github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon#NetworkIDGetter.
// Provided below are links to platform specific code which can be used to generate
// valid network identifier strings:
// Android:
// - https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/3d344194d21b250e0f18ededa4b4459a373b0690/MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java#L371
// iOS:
// - https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/3d344194d21b250e0f18ededa4b4459a373b0690/MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/PsiphonTunnel.m#L1105
//
// Parameters.establishTunnelTimeoutSeconds specifies a time limit after which to stop
// attempting to connect and return an error if an active tunnel has not been established.
// A timeout of 0 will result in no timeout condition and the controller will attempt to
// establish an active tunnel indefinitely (or until PsiphonTunnelStop is called).
// Timeout values >= 0 override the optional `EstablishTunnelTimeoutSeconds` config field;
// null causes the config value to be used.
func PsiphonTunnelStart(cConfigJSON, cEmbeddedServerEntryList *C.char, cParams *C.struct_Parameters) *C.char {
// Stop any active tunnels
PsiphonTunnelStop()
if cConfigJSON == nil {
err := common.ContextError(fmt.Errorf("configJSON is required"))
managedStartResult = startErrorJSON(err)
return managedStartResult
}
if cParams == nil {
err := common.ContextError(fmt.Errorf("params is required"))
managedStartResult = startErrorJSON(err)
return managedStartResult
}
if cParams.sizeofStruct != C.sizeof_struct_Parameters {
err := common.ContextError(fmt.Errorf("sizeofStruct does not match sizeof(Parameters)"))
managedStartResult = startErrorJSON(err)
return managedStartResult
}
// NOTE: all arguments which may be referenced once Start returns must be copied onto
// the Go heap to ensure that they don't disappear later on and cause Go to crash.
configJSON := []byte(C.GoString(cConfigJSON))
embeddedServerEntryList := C.GoString(cEmbeddedServerEntryList)
params := clientlib.Parameters{}
if cParams.dataRootDirectory != nil {
v := C.GoString(cParams.dataRootDirectory)
params.DataRootDirectory = &v
}
if cParams.clientPlatform != nil {
v := C.GoString(cParams.clientPlatform)
params.ClientPlatform = &v
}
if cParams.networkID != nil {
v := C.GoString(cParams.networkID)
params.NetworkID = &v
}
if cParams.establishTunnelTimeoutSeconds != nil {
v := int(*cParams.establishTunnelTimeoutSeconds)
params.EstablishTunnelTimeoutSeconds = &v
}
startTime := time.Now()
// Start the tunnel connection
var err error
tunnel, err = clientlib.StartTunnel(
context.Background(), configJSON, embeddedServerEntryList, params, nil, nil)
if err != nil {
if err == clientlib.ErrTimeout {
managedStartResult = marshalStartResult(startResult{
Code: startResultCodeTimeout,
Error: fmt.Sprintf("Timeout occurred before Psiphon connected: %s", err.Error()),
})
} else {
managedStartResult = marshalStartResult(startResult{
Code: startResultCodeOtherError,
Error: err.Error(),
})
}
return managedStartResult
}
// Success
managedStartResult = marshalStartResult(startResult{
Code: startResultCodeSuccess,
ConnectTimeMS: int64(time.Now().Sub(startTime) / time.Millisecond),
HTTPProxyPort: tunnel.HTTPProxyPort,
SOCKSProxyPort: tunnel.SOCKSProxyPort,
})
return managedStartResult
}
//export PsiphonTunnelStop
//
// Stop stops the controller if it is running and waits for it to clean up and exit.
//
// Stop should always be called after a successful call to Start to ensure the
// controller is not left running and memory is released.
// It is safe to call this function when the tunnel is not running.
func PsiphonTunnelStop() {
freeManagedStartResult()
if tunnel != nil {
tunnel.Stop()
}
}
// 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 {
resultJSON, err := json.Marshal(result)
if err != nil {
err = common.ContextErrorMsg(err, "json.Marshal failed")
// Fail back to manually constructing the JSON
return C.CString(fmt.Sprintf("{\"Code\":%d, \"Error\": \"%s\"}",
startResultCodeOtherError, err.Error()))
}
return C.CString(string(resultJSON))
}
// startErrorJSON returns a startResult object serialized as a JSON string in the form of
// a null-terminated buffer of C chars. The object's return result code will be set to
// startResultCodeOtherError (2) and its error string set to the error string of the
// provided error.
//
// The JSON will be in the form of:
// {
// "Code": 2,
// "Error": <error message>
// }
func startErrorJSON(err error) *C.char {
var result startResult
result.Code = startResultCodeOtherError
result.Error = err.Error()
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)
}
managedStartResult = nil
}
}
// main is a stub required by cgo.
func main() {}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/lqinggang/psiphon-tunnel-core.git
git@gitee.com:lqinggang/psiphon-tunnel-core.git
lqinggang
psiphon-tunnel-core
psiphon-tunnel-core
v2.0.2

搜索帮助