|
|
/* |
|
|
* Copyright (c)2013-2020 ZeroTier, Inc. |
|
|
* |
|
|
* Use of this software is governed by the Business Source License included |
|
|
* in the LICENSE.TXT file in the project's root directory. |
|
|
* |
|
|
* Change Date: 2024-01-01 |
|
|
* |
|
|
* On the date above, in accordance with the Business Source License, use |
|
|
* of this software will be governed by version 2.0 of the Apache License. |
|
|
*/ |
|
|
/****/ |
|
|
|
|
|
using System.Runtime.InteropServices; |
|
|
using System; |
|
|
|
|
|
// Prototype of callback used by ZeroTier to signal events to C# application |
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|
|
public delegate void CSharpCallbackWithStruct(IntPtr msgPtr); |
|
|
|
|
|
/// <summary> |
|
|
/// ZeroTier SDK |
|
|
/// </summary> |
|
|
namespace ZeroTier |
|
|
{ |
|
|
public delegate void ZeroTierManagedEventCallback(ZeroTier.Event nodeEvent); |
|
|
|
|
|
/// <summary> |
|
|
/// ZeroTier Node - Virtual network subsystem |
|
|
/// </summary> |
|
|
public class Node |
|
|
{ |
|
|
static ulong _nodeId = 0x0; |
|
|
static bool _isOnline = false; |
|
|
static bool _joinedAtLeastOneNetwork = false; |
|
|
static bool _hasBeenFreed = false; |
|
|
string _configFilePath; |
|
|
ushort _servicePort; |
|
|
static ZeroTierManagedEventCallback _managedCallback; |
|
|
|
|
|
// Callback used internally to ferry events from the C++ layer |
|
|
static void OnZeroTierEvent(IntPtr msgPtr) |
|
|
{ |
|
|
// Marshal the callback message pointer to a structure that we can inspect |
|
|
zts_callback_msg msg = |
|
|
(zts_callback_msg)Marshal.PtrToStructure(msgPtr, typeof(zts_callback_msg)); |
|
|
|
|
|
ZeroTier.Event newEvent = null; |
|
|
|
|
|
// Node events |
|
|
if (msg.eventCode == Constants.EVENT_NODE_UP) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NODE_UP"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NODE_ONLINE) { |
|
|
_isOnline = true; |
|
|
// Marshal the node details pointer to a structure |
|
|
zts_node_details details = |
|
|
(zts_node_details)Marshal.PtrToStructure(msg.node, typeof(zts_node_details)); |
|
|
_nodeId = details.address; |
|
|
newEvent = new ZeroTier.Event(msg.eventCode, "EVENT_NODE_ONLINE"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NODE_OFFLINE) { |
|
|
_isOnline = false; |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NODE_OFFLINE"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NODE_NORMAL_TERMINATION) { |
|
|
_isOnline = false; |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NODE_NORMAL_TERMINATION"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NODE_DOWN) { |
|
|
_isOnline = false; |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NODE_DOWN"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NODE_IDENTITY_COLLISION) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NODE_IDENTITY_COLLISION"); |
|
|
_isOnline = false; |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NODE_UNRECOVERABLE_ERROR) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NODE_UNRECOVERABLE_ERROR"); |
|
|
_isOnline = false; |
|
|
} |
|
|
|
|
|
// Network events |
|
|
if (msg.eventCode == Constants.EVENT_NETWORK_NOT_FOUND) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETWORK_NOT_FOUND"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETWORK_REQ_CONFIG) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETWORK_REQ_CONFIG"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETWORK_ACCESS_DENIED) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETWORK_ACCESS_DENIED"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETWORK_READY_IP4) { |
|
|
_joinedAtLeastOneNetwork = true; |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETWORK_READY_IP4"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETWORK_READY_IP6) { |
|
|
_joinedAtLeastOneNetwork = true; |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETWORK_READY_IP6"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETWORK_DOWN) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETWORK_DOWN"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETWORK_CLIENT_TOO_OLD) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETWORK_CLIENT_TOO_OLD"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETWORK_REQ_CONFIG) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETWORK_REQ_CONFIG"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETWORK_OK) { |
|
|
zts_network_details unmanagedDetails = |
|
|
(zts_network_details)Marshal.PtrToStructure(msg.network, typeof(zts_network_details)); |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETWORK_OK"); |
|
|
newEvent.networkDetails = new NetworkDetails(); |
|
|
newEvent.networkDetails.networkId = unmanagedDetails.nwid; |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETWORK_ACCESS_DENIED) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETWORK_ACCESS_DENIED"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETWORK_READY_IP4_IP6) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETWORK_READY_IP4_IP6"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETWORK_UPDATE) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETWORK_UPDATE"); |
|
|
} |
|
|
|
|
|
// Stack events |
|
|
if (msg.eventCode == Constants.EVENT_STACK_UP) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_STACK_UP"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_STACK_DOWN) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_STACK_DOWN"); |
|
|
} |
|
|
|
|
|
// Address events |
|
|
if (msg.eventCode == Constants.EVENT_ADDR_ADDED_IP4) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_ADDR_ADDED_IP4"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_ADDR_ADDED_IP6) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_ADDR_ADDED_IP6"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_ADDR_REMOVED_IP4) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_ADDR_REMOVED_IP4"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_ADDR_REMOVED_IP6) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_ADDR_REMOVED_IP6"); |
|
|
} |
|
|
// peer events |
|
|
if (msg.eventCode == Constants.EVENT_PEER_DIRECT) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_PEER_DIRECT"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_PEER_RELAY) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_PEER_RELAY"); |
|
|
} |
|
|
// newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_PEER_UNREACHABLE"); |
|
|
if (msg.eventCode == Constants.EVENT_PEER_PATH_DISCOVERED) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_PEER_PATH_DISCOVERED"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_PEER_PATH_DEAD) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_PEER_PATH_DEAD"); |
|
|
} |
|
|
|
|
|
// Route events |
|
|
if (msg.eventCode == Constants.EVENT_ROUTE_ADDED) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_ROUTE_ADDED"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_ROUTE_REMOVED) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_ROUTE_REMOVED"); |
|
|
} |
|
|
|
|
|
// Netif events |
|
|
if (msg.eventCode == Constants.EVENT_NETIF_UP) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETIF_UP"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETIF_DOWN) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETIF_DOWN"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETIF_REMOVED) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETIF_REMOVED"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETIF_LINK_UP) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETIF_LINK_UP"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_NETIF_LINK_DOWN) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_NETIF_LINK_DOWN"); |
|
|
} |
|
|
if (msg.eventCode == Constants.EVENT_ADDR_REMOVED_IP6) { |
|
|
newEvent = new ZeroTier.Event(msg.eventCode,"EVENT_ADDR_REMOVED_IP6"); |
|
|
} |
|
|
|
|
|
// Pass the converted Event to the managed callback (visible to user) |
|
|
if (newEvent != null) { |
|
|
_managedCallback(newEvent); |
|
|
} |
|
|
} |
|
|
|
|
|
/// <summary> |
|
|
/// Creates a new Node. Start this object by calling Start(). |
|
|
/// </summary> |
|
|
/// <param name="configFilePath">Where keys and config files will be stored on the filesystem</param> |
|
|
/// <param name="managedCallback">Where you would like to receive ZeroTier event notifications</param> |
|
|
/// <param name="servicePort">The port ZeroTier will use to send its encrypted </param> |
|
|
/// <returns></returns> |
|
|
public Node(string configFilePath, ZeroTierManagedEventCallback managedCallback, UInt16 servicePort) |
|
|
{ |
|
|
if (String.IsNullOrEmpty(configFilePath)) { |
|
|
throw new ArgumentNullException(nameof(configFilePath)); |
|
|
} |
|
|
if (managedCallback == null) { |
|
|
throw new ArgumentNullException(nameof(managedCallback)); |
|
|
} |
|
|
_nodeId = 0x0; |
|
|
_configFilePath = configFilePath; |
|
|
_servicePort = servicePort; |
|
|
_managedCallback = new ZeroTierManagedEventCallback(managedCallback); |
|
|
} |
|
|
|
|
|
/// <summary> |
|
|
/// Starts the ZeroTier node/service |
|
|
/// </summary> |
|
|
/// <returns></returns> |
|
|
public int Start() |
|
|
{ |
|
|
if (_hasBeenFreed == true) { |
|
|
throw new ObjectDisposedException("ZeroTier Node has previously been freed. Restart application to create new instance."); |
|
|
} |
|
|
return zts_start(_configFilePath,OnZeroTierEvent,_servicePort); |
|
|
} |
|
|
|
|
|
/// <summary> |
|
|
/// Frees (most) resources used by ZeroTier. ZeroTier may not be started again after this call. |
|
|
/// </summary> |
|
|
/// <returns></returns> |
|
|
public int Free() |
|
|
{ |
|
|
_nodeId = 0x0; |
|
|
_hasBeenFreed = true; |
|
|
return zts_free(); |
|
|
} |
|
|
|
|
|
/// <summary> |
|
|
/// Stop all ZeroTier service activity. The service may be started again with Start(). |
|
|
/// </summary> |
|
|
/// <returns></returns> |
|
|
public int Stop() |
|
|
{ |
|
|
_nodeId = 0x0; |
|
|
return zts_stop(); |
|
|
} |
|
|
|
|
|
/// <summary> |
|
|
/// Restarts the ZeroTier service, internal stack and driver. (Mostly used for debugging.) |
|
|
/// </summary> |
|
|
/// <returns></returns> |
|
|
public int Restart() |
|
|
{ |
|
|
_nodeId = 0x0; |
|
|
return zts_restart(); |
|
|
} |
|
|
|
|
|
/// <summary> |
|
|
/// Requests to join a ZeroTier network. Remember to authorize your node/device. |
|
|
/// </summary> |
|
|
/// <param name="nwid">Network ID</param> |
|
|
/// <returns></returns> |
|
|
public int Join(ulong nwid) |
|
|
{ |
|
|
return zts_join(nwid); |
|
|
} |
|
|
|
|
|
/// <summary> |
|
|
/// Leaves a ZeroTier network. |
|
|
/// </summary> |
|
|
/// <param name="nwid"></param> |
|
|
/// <returns></returns> |
|
|
public int Leave(ulong nwid) |
|
|
{ |
|
|
return zts_leave(nwid); |
|
|
} |
|
|
|
|
|
/// <summary> |
|
|
/// Returns whether the Node is online (able to reach the internet.) |
|
|
/// </summary> |
|
|
/// <returns></returns> |
|
|
public bool IsOnline() |
|
|
{ |
|
|
return _isOnline; |
|
|
} |
|
|
|
|
|
/// <summary> |
|
|
/// Returns whether routing information is available from at least one ZeroTier network. |
|
|
/// </summary> |
|
|
/// <returns></returns> |
|
|
public bool HasRoutes() |
|
|
{ |
|
|
return _joinedAtLeastOneNetwork; |
|
|
} |
|
|
|
|
|
public ulong NodeId |
|
|
{ |
|
|
get { |
|
|
return _nodeId; |
|
|
} |
|
|
} |
|
|
|
|
|
/* Structures and functions used internally to communicate with |
|
|
lower-level C API defined in include/ZeroTierSockets.h */ |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_start")] |
|
|
static extern int zts_start(string arg1, CSharpCallbackWithStruct arg2, ushort arg3); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_stop")] |
|
|
static extern int zts_stop(); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_restart")] |
|
|
static extern int zts_restart(); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_free")] |
|
|
static extern int zts_free(); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_join")] |
|
|
static extern int zts_join(ulong arg1); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_leave")] |
|
|
static extern int zts_leave(ulong arg1); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_allow_network_caching")] |
|
|
static extern int zts_allow_network_caching(byte arg1); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_allow_peer_caching")] |
|
|
static extern int zts_allow_peer_caching(byte arg1); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_allow_local_conf")] |
|
|
static extern int zts_allow_local_conf(byte arg1); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_orbit")] |
|
|
static extern int zts_orbit(ulong arg1, ulong arg2); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_deorbit")] |
|
|
static extern int zts_deorbit(ulong arg1); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_get_6plane_addr")] |
|
|
static extern int zts_get_6plane_addr(IntPtr arg1, ulong arg2, ulong arg3); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_get_rfc4193_addr")] |
|
|
static extern int zts_get_rfc4193_addr(IntPtr arg1, ulong arg2, ulong arg3); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_generate_adhoc_nwid_from_range")] |
|
|
static extern ulong zts_generate_adhoc_nwid_from_range(ushort arg1, ushort arg2); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_delay_ms")] |
|
|
static extern void zts_delay_ms(int arg1); |
|
|
|
|
|
[DllImport("libzt", EntryPoint="CSharp_zts_errno_get")] |
|
|
static extern int zts_errno_get(); |
|
|
|
|
|
[StructLayout(LayoutKind.Sequential)] |
|
|
struct zts_node_details |
|
|
{ |
|
|
public ulong address; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Virtual network configuration |
|
|
*/ |
|
|
[StructLayout(LayoutKind.Sequential)] |
|
|
struct zts_network_details |
|
|
{ |
|
|
/** |
|
|
* 64-bit ZeroTier network ID |
|
|
*/ |
|
|
public ulong nwid; |
|
|
|
|
|
/** |
|
|
* Ethernet MAC (48 bits) that should be assigned to port |
|
|
*/ |
|
|
public ulong mac; |
|
|
|
|
|
/** |
|
|
* Network name (from network configuration master) |
|
|
*/ |
|
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] |
|
|
public byte[] name; |
|
|
|
|
|
/** |
|
|
* Network configuration request status |
|
|
*/ |
|
|
public byte status; // ? |
|
|
|
|
|
/** |
|
|
* Network type |
|
|
*/ |
|
|
public byte type; // ? |
|
|
|
|
|
/** |
|
|
* Maximum interface MTU |
|
|
*/ |
|
|
public uint mtu; |
|
|
|
|
|
/** |
|
|
* If nonzero, the network this port belongs to indicates DHCP availability |
|
|
* |
|
|
* This is a suggestion. The underlying implementation is free to ignore it |
|
|
* for security or other reasons. This is simply a netconf parameter that |
|
|
* means 'DHCP is available on this network.' |
|
|
*/ |
|
|
public int dhcp; |
|
|
|
|
|
/** |
|
|
* If nonzero, this port is allowed to bridge to other networks |
|
|
* |
|
|
* This is informational. If this is false (0), bridged packets will simply |
|
|
* be dropped and bridging won't work. |
|
|
*/ |
|
|
public int bridge; |
|
|
|
|
|
/** |
|
|
* If nonzero, this network supports and allows broadcast (ff:ff:ff:ff:ff:ff) traffic |
|
|
*/ |
|
|
public int broadcastEnabled; |
|
|
|
|
|
/** |
|
|
* If the network is in PORT_ERROR state, this is the (negative) error code most recently reported |
|
|
*/ |
|
|
public int portError; |
|
|
|
|
|
/** |
|
|
* Revision number as reported by controller or 0 if still waiting for config |
|
|
*/ |
|
|
public ulong netconfRevision; |
|
|
|
|
|
/** |
|
|
* Number of assigned addresses |
|
|
*/ |
|
|
public uint assignedAddressCount; |
|
|
|
|
|
/** |
|
|
* ZeroTier-assigned addresses (in sockaddr_storage structures) |
|
|
* |
|
|
* For IP, the port number of the sockaddr_XX structure contains the number |
|
|
* of bits in the address netmask. Only the IP address and port are used. |
|
|
* Other fields like interface number can be ignored. |
|
|
* |
|
|
* This is only used for ZeroTier-managed address assignments sent by the |
|
|
* virtual network's configuration master. |
|
|
*/ |
|
|
//struct zts_sockaddr_storage assignedAddresses[ZTS_MAX_ZT_ASSIGNED_ADDRESSES]; |
|
|
|
|
|
/** |
|
|
* Number of ZT-pushed routes |
|
|
*/ |
|
|
public uint routeCount; |
|
|
|
|
|
/** |
|
|
* Routes (excluding those implied by assigned addresses and their masks) |
|
|
*/ |
|
|
//ZTS_VirtualNetworkRoute routes[ZTS_MAX_NETWORK_ROUTES]; |
|
|
|
|
|
/** |
|
|
* Number of multicast groups subscribed |
|
|
*/ |
|
|
public uint multicastSubscriptionCount; |
|
|
|
|
|
/** |
|
|
* Multicast groups to which this network's device is subscribed |
|
|
*/ |
|
|
//struct { |
|
|
// uint64_t mac; /* MAC in lower 48 bits */ |
|
|
// uint32_t adi; /* Additional distinguishing information, usually zero except for IPv4 ARP groups */ |
|
|
//} multicastSubscriptions[ZTS_MAX_MULTICAST_SUBSCRIPTIONS]; |
|
|
} |
|
|
|
|
|
[StructLayout(LayoutKind.Sequential)] |
|
|
struct zts_callback_msg |
|
|
{ |
|
|
public short eventCode; |
|
|
//[MarshalAs(UnmanagedType.LPStruct, SizeConst = 4)] |
|
|
public IntPtr node; |
|
|
public IntPtr network; |
|
|
} |
|
|
|
|
|
|
|
|
/// <summary> |
|
|
/// Gets the value of errno from the unmanaged region |
|
|
/// </summary> |
|
|
/// <value></value> |
|
|
public static int ErrNo { |
|
|
get { |
|
|
return zts_errno_get(); |
|
|
} |
|
|
} |
|
|
} |
|
|
}
|
|
|
|