summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--exports/exports.go65
-rw-r--r--src/state.go3
-rw-r--r--wrappers/python/eduvpncommon/__init__.py20
-rw-r--r--wrappers/python/eduvpncommon/auth.py14
-rw-r--r--wrappers/python/eduvpncommon/discovery.py86
-rw-r--r--wrappers/python/eduvpncommon/main.py22
6 files changed, 51 insertions, 159 deletions
diff --git a/exports/exports.go b/exports/exports.go
index e6be488..f0883a8 100644
--- a/exports/exports.go
+++ b/exports/exports.go
@@ -2,27 +2,38 @@ package main
/*
#include <stdlib.h>
+
+typedef void (*PythonCB)(const char* oldstate, const char* newstate);
+
+// FIXME: Remove this, see: https://stackoverflow.com/questions/58606884/multiple-definition-when-using-cgo
+__attribute__((weak))
+void call_callback(PythonCB callback, const char* oldstate, const char* newstate)
+{
+ callback(oldstate, newstate);
+}
*/
import "C"
import "unsafe"
import "github.com/jwijenbergh/eduvpn-common/src"
-// Functions here should probably not take string parameters, see https://pkg.go.dev/cmd/cgo#hdr-C_references_to_Go
-// GetOrganizationsList gets the list of organizations from the disco server.
-// Returns the json data as a string and an error code. This is used as key for looking up data.
-//export GetOrganizationsList
-func GetOrganizationsList() (*C.char, *C.char) {
- body, err := eduvpn.GetOrganizationsList()
- if err != nil {
- return nil, C.CString(err.Error())
+var P_StateCallback C.PythonCB
+
+func StateCallback(old_state string, new_state string) {
+ if P_StateCallback == nil {
+ return
}
- return C.CString(body), nil
+ oldState_c := C.CString(old_state)
+ newState_c := C.CString(new_state)
+ C.call_callback(P_StateCallback, oldState_c, newState_c)
+ C.free(unsafe.Pointer(oldState_c))
+ C.free(unsafe.Pointer(newState_c))
}
//export Register
-func Register(name *C.char, url *C.char) {
- eduvpn.Register(eduvpn.GetVPNState(), C.GoString(name), C.GoString(url))
+func Register(name *C.char, url *C.char, stateCallback C.PythonCB) {
+ P_StateCallback = stateCallback
+ eduvpn.Register(eduvpn.GetVPNState(), C.GoString(name), C.GoString(url), StateCallback)
}
//export InitializeOAuth
@@ -34,42 +45,10 @@ func InitializeOAuth() (*C.char, *C.char) {
return C.CString(url), nil
}
-// GetServersList gets the list of servers from the disco server.
-// Returns the json data as a string and an error code. This is used as key for looking up data.
-//export GetServersList
-func GetServersList() (*C.char, *C.char) {
- body, err := eduvpn.GetServersList()
- if err != nil {
- return nil, C.CString(err.Error())
- }
- return C.CString(body), nil
-}
-
//export FreeString
func FreeString(addr *C.char) {
C.free(unsafe.Pointer(addr))
}
-// Verify verifies a signature on a JSON file. See eduvpn.Verify for more details.
-// It returns 0 for a valid signature and a nonzero eduvpn.VerifyErrorCode otherwise.
-// signatureFileContent must be UTF-8-encoded.
-//export Verify
-func Verify(signatureFileContent []byte, signedJson []byte, expectedFileName []byte, minSignTime uint64) (int8, *C.char) {
- valid, err := eduvpn.Verify(string(signatureFileContent), signedJson, string(expectedFileName), minSignTime, false)
- if valid {
- return 0, nil
- } else {
- return 1, C.CString(err.Error())
- }
-}
-
-// InsecureTestingSetExtraKey adds an extra allowed key for verification with Verify.
-// ONLY USE FOR TESTING. Not Thread-safe. Do not call in parallel to Verify.
-// keyString must be an ASCII Base64-encoded key.
-//export InsecureTestingSetExtraKey
-func InsecureTestingSetExtraKey(keyString []byte) {
- eduvpn.InsecureTestingSetExtraKey(string(keyString))
-}
-
// Not used in library, but needed to compile.
func main() { panic("compile with -buildmode=c-shared") }
diff --git a/src/state.go b/src/state.go
index 5d054bb..be85a45 100644
--- a/src/state.go
+++ b/src/state.go
@@ -13,7 +13,7 @@ type EduVPNState struct {
OAuthSession *EduVPNOAuthSession
}
-func Register(state *EduVPNState, name string, server string) error {
+func Register(state *EduVPNState, name string, server string, stateCallback func(string, string)) error {
state.Name = name
state.Server = server
@@ -24,6 +24,7 @@ func Register(state *EduVPNState, name string, server string) error {
}
state.Endpoints = endpoints
+ stateCallback("START", "REGISTER")
return nil
}
diff --git a/wrappers/python/eduvpncommon/__init__.py b/wrappers/python/eduvpncommon/__init__.py
index 911c671..c21f8d4 100644
--- a/wrappers/python/eduvpncommon/__init__.py
+++ b/wrappers/python/eduvpncommon/__init__.py
@@ -19,27 +19,17 @@ _libfile = f"{_lib_prefixes[_os]}{_libname}{_lib_suffixes[_os]}"
# Library should have been copied to the lib/ folder
lib = cdll.LoadLibrary(str(pathlib.Path(__file__).parent / "lib" / _libfile))
-
-class GoSlice(Structure):
- _fields_ = [("data", POINTER(c_char)), ("len", c_int64), ("cap", c_int64)]
-
- @staticmethod
- def make(bs: bytes) -> "GoSlice":
- return GoSlice((c_char * len(bs))(*bs), len(bs), len(bs)) # type: ignore
-
-
+# Data types
class DataError(Structure):
_fields_ = [('data', c_void_p),
('error', c_int64)]
+GOCB_StateChange = CFUNCTYPE(None, c_char_p, c_char_p)
+
+# Exposed functions
+lib.Register.argtypes, lib.Register.restype = [c_char_p, c_char_p, GOCB_StateChange], None
# We have to use c_void_p instead of c_char_p to free it properly
# See https://stackoverflow.com/questions/13445568/python-ctypes-how-to-free-memory-getting-invalid-pointer-error
-lib.Register.argtypes, lib.Register.restype = [c_char_p, c_char_p], None
lib.InitializeOAuth.argtypes, lib.InitializeOAuth.restype = [], c_void_p
-lib.GetOrganizationsList.argtypes, lib.GetOrganizationsList.restype = [], DataError
-lib.GetServersList.argtypes, lib.GetServersList.restype = [], DataError
lib.FreeString.argtypes, lib.FreeString.restype = [c_void_p], None
-lib.Verify.argtypes, lib.Verify.restype = [GoSlice, GoSlice, GoSlice, c_uint64], c_int64
-lib.InsecureTestingSetExtraKey.argtypes, lib.InsecureTestingSetExtraKey.restype = [GoSlice], None
-
diff --git a/wrappers/python/eduvpncommon/auth.py b/wrappers/python/eduvpncommon/auth.py
deleted file mode 100644
index b0d1410..0000000
--- a/wrappers/python/eduvpncommon/auth.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from . import lib
-from ctypes import *
-
-def Register(name, url):
- name_bytes = name.encode('utf-8')
- url_bytes = url.encode('utf-8')
- lib.Register(name_bytes, url_bytes)
-
-def InitializeOAuth():
- ptr = lib.InitializeOAuth()
- value = cast(ptr, c_char_p).value
- authURL = value.decode()
- lib.FreeString(ptr)
- return authURL
diff --git a/wrappers/python/eduvpncommon/discovery.py b/wrappers/python/eduvpncommon/discovery.py
deleted file mode 100644
index 4820ca2..0000000
--- a/wrappers/python/eduvpncommon/discovery.py
+++ /dev/null
@@ -1,86 +0,0 @@
-from . import lib, GoSlice, DataError
-from .error import GoError
-from ctypes import *
-from typing import Callable, List, Dict, Any
-from enum import Enum
-import json
-
-def getList(func: Callable) -> List[Dict[str, Any]]:
- dataError = func()
- ptr = dataError.data
- error = dataError.error
- body = ""
- if not error:
- body_value = cast(ptr, c_char_p).value
- if body_value:
- body = body_value.decode()
- lib.FreeString(ptr)
- if error:
- raise RequestError(error)
-
- return json.loads(body)
-
-def GetOrganizationsList() -> List[Dict[str, Any]]:
- return getList(lib.GetOrganizationsList)
-
-def GetServersList() -> List[Dict[str, Any]]:
- return getList(lib.GetServersList)
-
-
-class RequestErrorCode(Enum):
- ErrRequestFileError = 1 # The request for the file has failed.
- ErrVerifySigError = 2 # The signature failed to verify.
- Unknown = -1 # Other unknown error.
-
-class RequestError(GoError):
- def __init__(self, err: int):
- super().__init__(RequestErrorCode(err),
- {
- RequestErrorCode.ErrRequestFileError: "file request error",
- RequestErrorCode.ErrVerifySigError: "signature verify error",
- RequestErrorCode.Unknown: "unknown error",
- })
-
-
-class VerifyErrorCode(Enum):
- ErrUnknownExpectedFileName = 1 # Unknown expected file name specified. The signature has not been verified.
- ErrInvalidSignature = 2 # Signature is invalid (for the expected file type).
- ErrInvalidSignatureUnknownKey = 3 # Signature was created with an unknown key and has not been verified.
- ErrTooOld = 4 # Signature timestamp smaller than specified minimum signing time (rollback).
- Unknown = -1 # Other unknown error.
-
-class VerifyError(GoError):
- def __init__(self, err: int):
- super().__init__(VerifyErrorCode(err),
- {
- VerifyErrorCode.ErrUnknownExpectedFileName: "unknown expected file name",
- VerifyErrorCode.ErrInvalidSignature: "invalid signature",
- VerifyErrorCode.ErrInvalidSignatureUnknownKey: "invalid signature (unknown key)",
- VerifyErrorCode.ErrTooOld: "replay of previous signature (rollback)",
- VerifyErrorCode.Unknown: "unknown error",
- })
-
-
-def verify(signature: bytes, signed_json: bytes, expected_file_name: str, min_sign_time: int) -> None:
- """
- Verifies the signature on the JSON server_list.json/organization_list.json file.
- If the function returns, the signature is valid for the given file type.
-
- :param signature: .minisig signature file contents.
- :param signed_json: Signed .json file contents.
- :param expected_file_name: The file type to be verified, one of "server_list.json" or "organization_list.json".
- :param min_sign_time: Minimum time for signature (UNIX timestamp, seconds). Should be set to at least the time of the previous signature.
-
- :raises VerifyException: If signature verification fails or expectedFileName is not one of the allowed values.
- """
-
- err = lib.Verify(GoSlice.make(signature), GoSlice.make(signed_json),
- GoSlice.make(expected_file_name.encode()), min_sign_time)
- if err:
- raise VerifyError(err)
-
-
-def _insecure_testing_set_extra_key(key_string: str) -> None:
- """Use for testing only, see Go documentation."""
-
- lib.InsecureTestingSetExtraKey(GoSlice.make(key_string.encode()))
diff --git a/wrappers/python/eduvpncommon/main.py b/wrappers/python/eduvpncommon/main.py
new file mode 100644
index 0000000..b8278ad
--- /dev/null
+++ b/wrappers/python/eduvpncommon/main.py
@@ -0,0 +1,22 @@
+from . import lib, GOCB_StateChange
+from ctypes import *
+
+@GOCB_StateChange
+def state_change(old, new):
+ print(f"Python: State change {old.decode()} {new.decode()}")
+
+def InitializeOAuth():
+ ptr = lib.InitializeOAuth()
+ value = cast(ptr, c_char_p).value
+ authURL = value.decode()
+ lib.FreeString(ptr)
+ return authURL
+
+# Registers the python app with the GO code
+# name: The name of the app to be registered
+# url: The url of the server to connect to, FIXME: To be removed
+# state_callback: The callback to trigger whenever a state is changed, FIXME: Remove whenever this wrapper has implemented callbacks using function decorations
+def Register(name, url, state_callback):
+ name_bytes = name.encode('utf-8')
+ url_bytes = url.encode('utf-8')
+ lib.Register(name_bytes, url_bytes, state_callback)