diff options
| author | jwijenbergh <jeroenwijenbergh@protonmail.com> | 2022-09-14 13:56:49 +0200 |
|---|---|---|
| committer | jwijenbergh <jeroenwijenbergh@protonmail.com> | 2022-09-14 13:56:49 +0200 |
| commit | da83f54606c9c1d2786d87074ee17ed972d2e1b2 (patch) | |
| tree | 0be57934f9f467c87576abb0b457fb54b2d25d52 /exports/servers.go | |
| parent | fd34e72da8c604517050ada7e883ba982829d985 (diff) | |
Refactor: Return without json
Diffstat (limited to 'exports/servers.go')
| -rw-r--r-- | exports/servers.go | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/exports/servers.go b/exports/servers.go new file mode 100644 index 0000000..f92c08e --- /dev/null +++ b/exports/servers.go @@ -0,0 +1,273 @@ +package main + +/* +// for free +#include <stdlib.h> +#include "c/servers.h" +*/ +import "C" + +import ( + "unsafe" + + "github.com/jwijenbergh/eduvpn-common" + "github.com/jwijenbergh/eduvpn-common/internal/server" +) + +// Get the pointer to the C struct for the profile +// We allocate the struct, the profile ID and the display name +func getCPtrProfile(profile *server.ServerProfile) *C.serverProfile { + // Allocate the struct using malloc and the size of the struct + cProfile := (*C.serverProfile)(C.malloc(C.size_t(unsafe.Sizeof(C.serverProfile{})))) + cProfile.id = C.CString(profile.ID) + cProfile.display_name = C.CString(profile.DisplayName) + if profile.DefaultGateway { + cProfile.default_gateway = C.int(1) + } else { + cProfile.default_gateway = C.int(0) + } + + return cProfile +} + +// Get the pointer to the C struct for the profiles +// We allocate the struct and the struct inside it for the profiles +func getCPtrProfiles(serverProfiles *server.ServerProfileInfo) *C.serverProfiles { + goProfiles := serverProfiles.Info.ProfileList + // Allocate the profles struct using malloc and the size of a pointer + cProfiles := (*C.serverProfiles)(C.malloc(C.size_t(uintptr(0)))) + totalProfiles := C.size_t(len(goProfiles)) + // Defaults if we have no profiles + cProfiles.current = C.int(0) + cProfiles.profiles = nil + cProfiles.total_profiles = totalProfiles + // If we have profiles (which we should), we allocate the struct with malloc and the size of a pointer + // We then fill the struct by converting it to a go slice and get a C pointer for each profile + if totalProfiles > 0 { + profilesPtr := C.malloc(totalProfiles * C.size_t(unsafe.Sizeof(uintptr(0)))) + profiles := (*[1<<30 - 1]*C.serverProfile)(unsafe.Pointer(profilesPtr))[:totalProfiles:totalProfiles] + index := 0 + for _, profile := range goProfiles { + profiles[index] = getCPtrProfile(&profile) + index += 1 + } + // TODO: DO CURRENT PROFILE + cProfiles.profiles = (**C.serverProfile)(profilesPtr) + } + return cProfiles +} + +// Free the profiles by looping through them if there are any +// Also free the pointer itself +func freeCProfiles(profiles *C.serverProfiles) { + // We should only free the profiles if we have them (which we should) + if profiles.total_profiles > 0 { + // Convert it to a go slice + profilesSlice := (*[1<<30 - 1]*C.serverProfile)(unsafe.Pointer(profiles.profiles))[:profiles.total_profiles:profiles.total_profiles] + // Loop through the pointers and free th allocated strings and the struct itself + for i := C.size_t(0); i < profiles.total_profiles; i++ { + C.free(unsafe.Pointer(profilesSlice[i].id)) + C.free(unsafe.Pointer(profilesSlice[i].display_name)) + C.free(unsafe.Pointer(profilesSlice[i])) + } + // Free the inner profiles struct + C.free(unsafe.Pointer(profiles.profiles)) + } + // Free the profiles struct itself + C.free(unsafe.Pointer(profiles)) +} + +// Get a list of strings with a size as a c structure +// Returns the size in size_t and the list of strings as a double pointer char +func getCPtrListStrings(allStrings []string) (C.size_t, **C.char) { + // Get the total strings in size_t + totalStrings := C.size_t(len(allStrings)) + + // If we have strings + // Allocate memory for the strings array + if totalStrings > 0 { + stringsPtr := C.malloc(totalStrings * C.size_t(unsafe.Sizeof(uintptr(0)))) + // Go slice conversion + cStrings := (*[1<<30 - 1]*C.char)(unsafe.Pointer(stringsPtr))[:totalStrings:totalStrings] + + // Loop through and allocate the string for each contact + for index, string := range allStrings { + cStrings[index] = C.CString(string) + } + return totalStrings, (**C.char)(stringsPtr) + } + + // No strings then the length is zero and the char array is nil + return C.size_t(0), nil +} + +// Function for freeing an array/list of strings +// It takes the strings as a pointer to a string and the total strings in size_t +func freeCListStrings(allStrings **C.char, totalStrings C.size_t) { + // If we have strings we should free them + // By converting to a Go slice, and freeing them ony by one + // At last free the pointer itself + if totalStrings > 0 { + stringsSlice := (*[1<<30 - 1]*C.char)(unsafe.Pointer(allStrings))[:totalStrings:totalStrings] + for i := C.size_t(0); i < totalStrings; i++ { + C.free(unsafe.Pointer(stringsSlice[i])) + } + C.free(unsafe.Pointer(allStrings)) + } +} + +// Function for getting the server, +// It gets the main state as a pointer as we need to convert some string maps to localized strings +// It gets the base information for a server as well +func getServer(state *eduvpn.VPNState, base *eduvpn.VPNServerBase) *C.server { + // Allocation using malloc and the size of the struct + server := (*C.server)(C.malloc(C.size_t(unsafe.Sizeof(C.server{})))) + // String allocation and translate the display name + server.identifier = C.CString(base.URL) + server.display_name = C.CString(state.GetTranslated(base.DisplayName)) + // Call the helper to get the list of support contacts + server.total_support_contact, server.support_contact = getCPtrListStrings( + base.SupportContact, + ) + server.profiles = getCPtrProfiles(&base.Profiles) + // No endtime is given if we get servers when it has been partially initialised + if base.EndTime.IsZero() { + server.expire_time = C.ulonglong(0) + } + // The expire time should be stored as an unsigned long long in unix itme + server.expire_time = C.ulonglong(base.EndTime.Unix()) + return server +} + +// Function for freeing a single server +// Gets the pointer to C struct +func freeServer(info *C.server) { + // Free strings + C.free(unsafe.Pointer(info.identifier)) + C.free(unsafe.Pointer(info.display_name)) + + // Free arrays + freeCListStrings(info.support_contact, info.total_support_contact) + freeCProfiles(info.profiles) + + // Free the struct itself + C.free(unsafe.Pointer(info)) +} + +// Get the C ptr to the servers, returns the length in size_t and the double pointer to the struct +func getCPtrServers( + state *eduvpn.VPNState, + serverMap map[string]*server.InstituteAccessServer, +) (C.size_t, **C.server) { + totalServers := C.size_t(len(serverMap)) + // If we have servers, which is not always the case + if totalServers > 0 { + serversPtr := (**C.server)(C.malloc(totalServers * C.size_t(unsafe.Sizeof(uintptr(0))))) + servers := (*[1<<30 - 1]*C.server)(unsafe.Pointer(serversPtr))[:totalServers:totalServers] + index := 0 + for _, server := range serverMap { + cServer := getServer(state, &server.Base) + servers[index] = cServer + index += 1 + } + } + return C.size_t(0), nil +} + +//export FreeServers +// This function takes the servers as a C struct pointer as input +// It frees all allocated memory for the server +func FreeServers(cServers *C.servers) { + // Free the custom servers if there are any + if cServers.total_custom > 0 { + customServers := (*[1<<30 - 1]*C.server)(unsafe.Pointer(cServers.custom_servers))[:cServers.total_custom:cServers.total_custom] + for i := C.size_t(0); i < cServers.total_custom; i++ { + freeServer(customServers[i]) + } + C.free(unsafe.Pointer(cServers.custom_servers)) + } + // Free the institute access servers if there are any + if cServers.total_institute > 0 { + instituteServers := (*[1<<30 - 1]*C.server)(unsafe.Pointer(cServers.institute_servers))[:cServers.total_institute:cServers.total_institute] + + for i := C.size_t(0); i < cServers.total_institute; i++ { + freeServer(instituteServers[i]) + } + C.free(unsafe.Pointer(cServers.institute_servers)) + } + // Free the secure internet server if there is one + if cServers.secure_internet_server != nil { + C.free(unsafe.Pointer(cServers.secure_internet_server.country_code)) + freeServer(cServers.secure_internet_server) + } + // Free the structure itself + C.free(unsafe.Pointer(cServers)) +} + +// Return the servers as a C struct pointer +// It takes the state as a pointer as we need to translate some strings +// It also takes the servers as a pointer that belongs to the main state or gathered from the callback +func getSavedServersWithOptions(state *eduvpn.VPNState, servers *server.Servers) *C.servers { + // Allocate the struct that we will return + // With the size of the c struct + returnedStruct := (*C.servers)(C.malloc(C.size_t(unsafe.Sizeof(C.servers{})))) + + // Get the different categories of servers + totalCustom, customPtr := getCPtrServers(state, servers.CustomServers.Map) + totalInstitute, institutePtr := getCPtrServers(state, servers.InstituteServers.Map) + var secureServerPtr *C.server = nil + secureInternetBase, secureInternetBaseErr := servers.SecureInternetHomeServer.GetBase() + if secureInternetBaseErr == nil && secureInternetBase != nil { + // FIXME: log error? + secureServerPtr = getServer(state, secureInternetBase) + // Give a new identifier + C.free(unsafe.Pointer(secureServerPtr.identifier)) + secureServerPtr.identifier = C.CString(servers.SecureInternetHomeServer.HomeOrganizationID) + secureServerPtr.country_code = C.CString(servers.SecureInternetHomeServer.CurrentLocation) + } + + // Fill the struct and return + returnedStruct.custom_servers = customPtr + returnedStruct.total_custom = totalCustom + returnedStruct.institute_servers = institutePtr + returnedStruct.total_institute = totalInstitute + returnedStruct.secure_internet_server = secureServerPtr + return returnedStruct +} + +//export GetSavedServers +// This function takes the name as input which is the name of the client +// It gets the state by name and then returns the saved servers as a c struct belonging to it +func GetSavedServers(name *C.char) *C.servers { + nameStr := C.GoString(name) + state, stateErr := GetVPNState(nameStr) + if stateErr != nil { + // TODO: Remove this panic + panic(stateErr) + } + return getSavedServersWithOptions(state, &state.Servers) +} + +// This function takes the state as input which is the main state +// It also takes the data as an interface and if it has the servers type gets the data as a c struct otherwise nil +func getTransitionDataServers(state *eduvpn.VPNState, data interface{}) *C.servers { + if converted, ok := data.(server.Servers); ok { + return getSavedServersWithOptions(state, &converted) + } + return nil +} + +//export FreeSecureLocations +func FreeSecureLocations(locations *C.serverLocations) { + freeCListStrings(locations.locations, locations.total_locations); + C.free(unsafe.Pointer(locations)) +} + +func getTransitionSecureLocations(data interface{}) (*C.serverLocations) { + if locations, ok := data.([]string); ok { + returnedStruct := (*C.serverLocations)(C.malloc(C.size_t(unsafe.Sizeof(C.servers{})))) + returnedStruct.total_locations, returnedStruct.locations = getCPtrListStrings(locations) + return returnedStruct + } + return nil +} |
