diff --git a/CMakeLists.txt b/CMakeLists.txt index f0f8695..e857801 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -280,7 +280,7 @@ file (GLOB libminiupnpcSrcGlob ${ZTO_SRC_DIR}/ext/miniupnpc/upnpreplyparse.c) if (ZTS_PINVOKE) - set (ZTS_SWIG_WRAPPER_FILE ${PROJ_DIR}/examples/csharp/*.cxx) + set (ZTS_SWIG_WRAPPER_FILE ${LIBZT_SRC_DIR}/bindings/csharp/*.cxx) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DZTS_PINVOKE=1") endif () @@ -544,6 +544,8 @@ if (SHOULD_BUILD_TESTS) target_link_libraries(nonblockingclient ${STATIC_LIB_NAME}) add_executable (nonblockingserver ${PROJ_DIR}/examples/cpp/nonblockingserver.cpp) target_link_libraries(nonblockingserver ${STATIC_LIB_NAME}) + add_executable (keymanagement ${PROJ_DIR}/examples/cpp/keymanagement.cpp) + target_link_libraries(keymanagement ${STATIC_LIB_NAME}) if (CENTRAL_API) add_executable (centralapi ${PROJ_DIR}/examples/cpp/centralapi.cpp) target_link_libraries(centralapi ${STATIC_LIB_NAME}) diff --git a/dist.ps1 b/dist.ps1 index 895155b..0982366 100644 --- a/dist.ps1 +++ b/dist.ps1 @@ -1,5 +1,46 @@ +function CreateNugetPackage +{ + md builds\pkg\nuget -Force + + # runtimes + md pkg\nuget\ZeroTier.Sockets\runtimes\win10-x86\native -Force + md pkg\nuget\ZeroTier.Sockets\runtimes\win10-x64\native -Force + md pkg\nuget\ZeroTier.Sockets\runtimes\win10-x64\lib\uap10.0 -Force + md pkg\nuget\ZeroTier.Sockets\runtimes\win10-arm\native -Force + + # frameworks + md pkg\nuget\ZeroTier.Sockets\lib\net48 -Force + md pkg\nuget\ZeroTier.Sockets\lib\net5.0 -Force + + # Build native libzt with exported P/INVOKE symbols + Build-Library -BuildType "Release" -Arch "x64" -LanguageBinding "csharp" + Build-Library -BuildType "Release" -Arch "x64" -LanguageBinding "" + + Build-Library -BuildType "Release" -Arch "Win32" -LanguageBinding "csharp" + #Build-Library -BuildType "Release" -Arch "ARM" -LanguageBinding "csharp" + + # Copy assemblies into NuGet package tree + Copy-Item .\lib\release\win-x64-pinvoke\*.dll pkg\nuget\ZeroTier.Sockets\runtimes\win10-x64\lib\uap10.0\libzt.dll + Copy-Item .\lib\release\win-x64\*.dll pkg\nuget\ZeroTier.Sockets\runtimes\win10-x64\native\libzt.dll + + Copy-Item .\lib\release\win-x86-pinvoke\*.dll pkg\nuget\ZeroTier.Sockets\runtimes\win10-x86\native\libzt.dll + #Copy-Item .\lib\release\win-arm-pinvoke\*.dll pkg\nuget\ZeroTier.Sockets\runtimes\win10-arm\native\libzt.dll + + # Build wrapper library for C# ZeroTier.Sockets abstraction + csc -target:library -out:pkg\nuget\ZeroTier.Sockets\lib\net5.0\ZeroTier.Sockets.dll .\src\bindings\csharp\*.cs + + # Package everything + Push-Location -Path pkg\nuget\ZeroTier.Sockets + + del ZeroTier.Sockets.*.nupkg + nuget pack ZeroTier.Sockets.nuspec -OutputDirectory ..\..\..\builds\pkg\nuget + + Pop-Location +} + function Clean { + Remove-Item builds -Recurse -Force -Confirm:$false -ErrorAction:'silentlycontinue' Remove-Item tmp -Recurse -Force -Confirm:$false -ErrorAction:'silentlycontinue' Remove-Item lib -Recurse -Force -Confirm:$false -ErrorAction:'silentlycontinue' Remove-Item bin -Recurse -Force -Confirm:$false -ErrorAction:'silentlycontinue' @@ -29,11 +70,11 @@ function Build-Library([string]$BuildType, [string]$Arch, [string]$LanguageBindi $bitCount="64" $archAlias="win-x64" } - if ($Arch -eq "ARM32") { - $bitCount="32" - $archAlias="win-arm" - } - if ($Arch -eq "ARM64") { + #if ($Arch -eq "ARM32") { + # $bitCount="32" + # $archAlias="win-arm" + #} + if ($Arch -eq "ARM") { $bitCount="64" $archAlias="win-arm64" } diff --git a/examples/csharp/Makefile b/examples/csharp/Makefile deleted file mode 100644 index ad83d2a..0000000 --- a/examples/csharp/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -debug: - cd ../../ && make host_pinvoke_debug - cp -f ../../lib/debug/linux-x86_64/libzt.so . - mono-csc -out:example.exe *.cs - -release: - cd ../../ && make host_pinvoke_release - cp -f ../../lib/release/linux-x86_64/libzt.so . - mono-csc -out:example.exe *.cs - -clean: - rm -rf libzt.* example.exe \ No newline at end of file diff --git a/examples/csharp/README.md b/examples/csharp/README.md index 6c8bd99..710cf5d 100644 --- a/examples/csharp/README.md +++ b/examples/csharp/README.md @@ -1,22 +1,28 @@ -ZeroTier Sockets for C# .NET (Work In Progress) +ZeroTier Sockets for C# .NET ===== -This library is a re-implementation of the .NET socket class ([System.Net.Sockets.Socket](https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket)) built atop ZeroTier's SDK using P/INVOKE and is designed to be a direct drop-in replacement. The library consists of three main objects: `ZeroTier.Node`, `ZeroTier.Event`, and `ZeroTier.Socket`. No code change is required in your application beyond a small snippet of startup code, renaming `Socket` to `ZeroTier.Socket` (where applicable) and handling a smattering of events. +A replacement for the [System.Net.Sockets.Socket](https://docs.microsoft.com/en-us/dotnet/api/system.net.sockets.socket) class built atop ZeroTier's SDK using P/INVOKE. It is designed to be a direct drop-in replacement. The library consists of three main objects: `ZeroTier.Node`, `ZeroTier.Event`, and `ZeroTier.Socket`. No code change is required in your application beyond a small snippet of startup code, renaming `Socket` to `ZeroTier.Socket` (where applicable) and handling a smattering of events. +# Overview -tl;dr: - +Add `ZeroTier.Sockets` to your project: +```powershell +Install-Package ZeroTier.Sockets ``` + +See [example.cs](./example.cs) for complete client/server app implementation. + +```csharp using System.Net.Sockets; using ZeroTier; -void myCallback(ZeroTier.Event e) +void OnZeroTierEvent(ZeroTier.Event e) { Console.WriteLine("{0} ({1})", e.EventCode, e.EventName); } ... -ZeroTier.Node node = new ZeroTier.Node("path", myCallback, 9991); +ZeroTier.Node node = new ZeroTier.Node("path", OnZeroTierEvent, 9991); node.Start(); node.Join(0xc287ac0b42a6fb4c); @@ -31,15 +37,28 @@ sock.Connect(remoteEndPoint); node.Stop(); ``` -See [example.cs](./example.cs) for a complete client/server implementation. -## Building and running the example +# Building example without NuGet package (Advanced) + +From top-level repo directory, build `libzt.dll/so/dylib`: + +```bash +make host_pinvoke_release +``` + +Copy `libzt.dll/so/dylib` into this project directory: ``` -make debug|release +cp ../../lib/release/${YOUR_HOST_TUPLE}-pinvoke/libzt.* . +``` +Where `${YOUR_HOST_TUPLE}` is something like: `linux-x86_64`, `macOS-x86_64`, etc. + +Build language binding layer, `ZeroTier.Sockets.dll`: + +```bash +cd examples/csharp +${CSHARP_COMPILER} -target:library -out:ZeroTier.Sockets.dll ../../src/bindings/csharp/*.cs +${CSHARP_COMPILER} -reference:ZeroTier.Sockets.dll example.cs ./example.exe ``` - -## Development notes - -The SWIG interface file `zt.i` is only present for historical reference purposes. SWIG generates a ton of unnecessary boilerplate code which is hard to completely prevent using hints. You can generate a new wrapper for yourself using `swig -c++ -csharp -dllimport "./libzt.so" zt.i` but I would not recommend doing so unless you know what you're in for. \ No newline at end of file +Where `${CSHARP_COMPILER}` may be `csc` or `mono-csc` depending on your platform. diff --git a/include/ZeroTierSockets.h b/include/ZeroTierSockets.h index fd2e996..b8776ca 100644 --- a/include/ZeroTierSockets.h +++ b/include/ZeroTierSockets.h @@ -39,6 +39,11 @@ extern "C" { #endif +#ifdef ZTS_PINVOKE + // Used by P/INVOKE wrappers + typedef void (*CppCallback)(void *msg); +#endif + ////////////////////////////////////////////////////////////////////////////// // Event codes // ////////////////////////////////////////////////////////////////////////////// @@ -1096,8 +1101,6 @@ ZTS_API int ZTCALL zts_disable_local_storage(uint8_t disabled); * @return ZTS_ERR_OK on success. ZTS_ERR_SERVICE or ZTS_ERR_ARG on failure */ #ifdef ZTS_PINVOKE - // Used by P/INVOKE wrappers - typedef void (*CppCallback)(void *msg); ZTS_API int ZTCALL zts_start(const char *path, CppCallback callback, uint16_t port); #else ZTS_API int ZTCALL zts_start(const char *path, void (*callback)(void *), uint16_t port); diff --git a/ports/zt.i b/ports/zt.i deleted file mode 100644 index 69aa836..0000000 --- a/ports/zt.i +++ /dev/null @@ -1,30 +0,0 @@ -/* libzt.i */ - -%begin -%{ -#define SWIG_PYTHON_CAST_MODE -%} - -%include - -#define PYTHON_BUILD 1 - -%module libzt -%{ -#include "../include/ZeroTier.h" -#include "../include/ZeroTierConstants.h" -%} - -%define %cs_callback(TYPE, CSTYPE) - %typemap(ctype) TYPE, TYPE& "void *" - %typemap(in) TYPE %{ $1 = ($1_type)$input; %} - %typemap(in) TYPE& %{ $1 = ($1_type)&$input; %} - %typemap(imtype, out="IntPtr") TYPE, TYPE& "CSTYPE" - %typemap(cstype, out="IntPtr") TYPE, TYPE& "CSTYPE" - %typemap(csin) TYPE, TYPE& "$csinput" -%enddef - -%cs_callback(userCallbackFunc, CSharpCallback) - -%include "../include/ZeroTier.h" -%include "../include/ZeroTierConstants.h" diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..244e119 --- /dev/null +++ b/src/README.md @@ -0,0 +1,4 @@ +# C++ Source + + - C API is exposed via [include/ZeroTierSockets.h](./../include/ZeroTierSocket.h) + - [bindings/](./bindings) Contains the source for wrappers in various languages. See [examples/](./../examples) for their usage. \ No newline at end of file diff --git a/src/bindings/README.md b/src/bindings/README.md new file mode 100644 index 0000000..54895e0 --- /dev/null +++ b/src/bindings/README.md @@ -0,0 +1,4 @@ +# Language Bindings + +These subdirectories contain the abstraction implementations for using libzt in an idiomatic way for languages other than C\C++. Packages which may draw upon these sources are found in [pkg/](../../pkg). Example usage of these bindings can be found in [examples/](../../examples). + diff --git a/examples/csharp/Constants.cs b/src/bindings/csharp/Constants.cs similarity index 100% rename from examples/csharp/Constants.cs rename to src/bindings/csharp/Constants.cs diff --git a/examples/csharp/Event.cs b/src/bindings/csharp/Event.cs similarity index 100% rename from examples/csharp/Event.cs rename to src/bindings/csharp/Event.cs diff --git a/examples/csharp/Node.cs b/src/bindings/csharp/Node.cs similarity index 99% rename from examples/csharp/Node.cs rename to src/bindings/csharp/Node.cs index c12e868..f859ab9 100644 --- a/examples/csharp/Node.cs +++ b/src/bindings/csharp/Node.cs @@ -204,10 +204,10 @@ namespace ZeroTier public Node(string configFilePath, ZeroTierManagedEventCallback managedCallback, UInt16 servicePort) { if (String.IsNullOrEmpty(configFilePath)) { - throw new ArgumentNullException(nameof(configFilePath)); + throw new ArgumentNullException("configFilePath"); } if (managedCallback == null) { - throw new ArgumentNullException(nameof(managedCallback)); + throw new ArgumentNullException("managedCallback"); } _nodeId = 0x0; _configFilePath = configFilePath; diff --git a/src/bindings/csharp/README.md b/src/bindings/csharp/README.md new file mode 100644 index 0000000..3793e1c --- /dev/null +++ b/src/bindings/csharp/README.md @@ -0,0 +1,4 @@ +# C# Language Bindings + + - Install (via [NuGet package](https://www.nuget.org/packages/ZeroTier.Sockets/)): `Install-Package ZeroTier.Sockets` + - Example usage: [examples/csharp](./../../../examples/csharp/) diff --git a/examples/csharp/Socket.cs b/src/bindings/csharp/Socket.cs similarity index 98% rename from examples/csharp/Socket.cs rename to src/bindings/csharp/Socket.cs index 4f240aa..890d16c 100644 --- a/examples/csharp/Socket.cs +++ b/src/bindings/csharp/Socket.cs @@ -135,7 +135,7 @@ namespace ZeroTier throw new ZeroTier.SocketException((int)Constants.ERR_SOCKET); } if (remoteEndPoint == null) { - throw new ArgumentNullException(nameof(remoteEndPoint)); + throw new ArgumentNullException("remoteEndPoint"); } int err = Constants.ERR_OK; int addrlen = 0; @@ -194,7 +194,7 @@ namespace ZeroTier throw new ZeroTier.SocketException((int)Constants.ERR_SOCKET); } if (localEndPoint == null) { - throw new ArgumentNullException(nameof(localEndPoint)); + throw new ArgumentNullException("localEndPoint"); } int err = Constants.ERR_OK; int addrlen = 0; @@ -412,7 +412,7 @@ namespace ZeroTier throw new ZeroTier.SocketException((int)ZeroTier.Constants.ERR_SOCKET); } if (buffer == null) { - throw new ArgumentNullException(nameof(buffer)); + throw new ArgumentNullException("buffer"); } int flags = 0; IntPtr bufferPtr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0); @@ -428,7 +428,7 @@ namespace ZeroTier throw new ZeroTier.SocketException((int)ZeroTier.Constants.ERR_SOCKET); } if (buffer == null) { - throw new ArgumentNullException(nameof(buffer)); + throw new ArgumentNullException("buffer"); } int flags = 0; IntPtr bufferPtr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0); diff --git a/examples/csharp/SocketException.cs b/src/bindings/csharp/SocketException.cs similarity index 92% rename from examples/csharp/SocketException.cs rename to src/bindings/csharp/SocketException.cs index 7945fc0..0b47cd2 100644 --- a/examples/csharp/SocketException.cs +++ b/src/bindings/csharp/SocketException.cs @@ -30,9 +30,9 @@ namespace ZeroTier SocketErrorCode = _socketErrorCode; } /// High-level service error code. See Constants.cs - public int ServiceErrorCode { get; } + public int ServiceErrorCode { get; private set; } /// Low-level socket error code. See Constants.cs - public int SocketErrorCode { get; } + public int SocketErrorCode { get; private set; } } } \ No newline at end of file diff --git a/examples/csharp/zt.i b/src/bindings/csharp/zt.i similarity index 100% rename from examples/csharp/zt.i rename to src/bindings/csharp/zt.i diff --git a/examples/csharp/zt_wrap.cxx b/src/bindings/csharp/zt_wrap.cxx similarity index 100% rename from examples/csharp/zt_wrap.cxx rename to src/bindings/csharp/zt_wrap.cxx