Add C# P/INVOKE wrapper and client/server example

This commit is contained in:
Joseph Henry
2021-01-04 21:03:57 -08:00
parent a8c757eaff
commit 4122110cea
12 changed files with 3013 additions and 1 deletions

View File

@@ -104,6 +104,13 @@ host_jar_debug:
host_jar_release:
$(DIST_BUILD_SCRIPT) host_jar "release"
host_jar: host_jar_debug host_jar_release
host_pinvoke_release:
$(DIST_BUILD_SCRIPT) host_pinvoke "release"
host_pinvoke_debug:
$(DIST_BUILD_SCRIPT) host_pinvoke "debug"
host_pinvoke: host_pinvoke_release host_pinvoke_debug
host: host_debug host_release
# Build every target available on this host

31
dist.sh
View File

@@ -263,6 +263,37 @@ host_jar()
# java -cp "lib/debug/macos-x86_64/zt.jar:examples/java/src/main/java" ExampleApp
}
# Build all ordinary library types for current host
host_pinvoke()
{
echo "Executing task: " ${FUNCNAME[ 0 ]} "(" $1 ")"
NORMALIZED_OSNAME=$OSNAME
if [[ $OSNAME = *"darwin"* ]]; then
DYNAMIC_LIB_NAME="libzt.dylib"
NORMALIZED_OSNAME="macos"
fi
if [[ $OSNAME = *"linux"* ]]; then
DYNAMIC_LIB_NAME="libzt.so"
fi
# CMake build files
BUILD_DIR=$(pwd)/tmp/${NORMALIZED_OSNAME}-$(uname -m)-$1
mkdir -p $BUILD_DIR
# Where to place results
BIN_OUTPUT_DIR=$(pwd)/bin/$1/${NORMALIZED_OSNAME}-$(uname -m)
mkdir -p $BIN_OUTPUT_DIR
rm -rf $BIN_OUTPUT_DIR/*
LIB_OUTPUT_DIR=$(pwd)/lib/$1/${NORMALIZED_OSNAME}-$(uname -m)
mkdir -p $LIB_OUTPUT_DIR
rm -rf $LIB_OUTPUT_DIR/libzt.a $LIB_OUTPUT_DIR/$DYNAMIC_LIB_NAME $LIB_OUTPUT_DIR/libztcore.a
# Build
cmake -DZTS_PINVOKE=True -H. -B$BUILD_DIR -DCMAKE_BUILD_TYPE=$1
$CMAKE --build $BUILD_DIR $BUILD_CONCURRENCY
# Move and clean up
mv $BUILD_DIR/bin/* $BIN_OUTPUT_DIR
mv $BUILD_DIR/lib/* $LIB_OUTPUT_DIR
clean_post_build
}
# Build all ordinary library types for current host
host()
{

View File

@@ -0,0 +1,353 @@
/*
* 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 ZeroTier;
namespace ZeroTier
{
public class Constants
{
// General error codes
public static readonly int ERR_OK = 0;
public static readonly int ERR_SOCKET = -1;
public static readonly int ERR_SERVICE = -2;
public static readonly int ERR_ARG = -3;
public static readonly int ERR_NO_RESULT = -4;
public static readonly int ERR_GENERAL = -5;
// Node events
public static readonly short EVENT_NODE_UP = 200;
public static readonly short EVENT_NODE_ONLINE = 201;
public static readonly short EVENT_NODE_OFFLINE = 202;
public static readonly short EVENT_NODE_DOWN = 203;
public static readonly short EVENT_NODE_IDENTITY_COLLISION = 204;
public static readonly short EVENT_NODE_UNRECOVERABLE_ERROR = 205;
public static readonly short EVENT_NODE_NORMAL_TERMINATION = 206;
// Network events
public static readonly short EVENT_NETWORK_NOT_FOUND = 210;
public static readonly short EVENT_NETWORK_CLIENT_TOO_OLD = 211;
public static readonly short EVENT_NETWORK_REQ_CONFIG = 212;
public static readonly short EVENT_NETWORK_OK = 213;
public static readonly short EVENT_NETWORK_ACCESS_DENIED = 214;
public static readonly short EVENT_NETWORK_READY_IP4 = 215;
public static readonly short EVENT_NETWORK_READY_IP6 = 216;
public static readonly short EVENT_NETWORK_READY_IP4_IP6 = 217;
public static readonly short EVENT_NETWORK_DOWN = 218;
public static readonly short EVENT_NETWORK_UPDATE = 219;
// Network Stack events
public static readonly short EVENT_STACK_UP = 220;
public static readonly short EVENT_STACK_DOWN = 221;
// lwIP netif events
public static readonly short EVENT_NETIF_UP = 230;
public static readonly short EVENT_NETIF_DOWN = 231;
public static readonly short EVENT_NETIF_REMOVED = 232;
public static readonly short EVENT_NETIF_LINK_UP = 233;
public static readonly short EVENT_NETIF_LINK_DOWN = 234;
// Peer events
public static readonly short EVENT_PEER_DIRECT = 240;
public static readonly short EVENT_PEER_RELAY = 241;
public static readonly short EVENT_PEER_UNREACHABLE = 242;
public static readonly short EVENT_PEER_PATH_DISCOVERED = 243;
public static readonly short EVENT_PEER_PATH_DEAD = 244;
// Route events
public static readonly short EVENT_ROUTE_ADDED = 250;
public static readonly short EVENT_ROUTE_REMOVED = 251;
// Address events
public static readonly short EVENT_ADDR_ADDED_IP4 = 260;
public static readonly short EVENT_ADDR_REMOVED_IP4 = 261;
public static readonly short EVENT_ADDR_ADDED_IP6 = 262;
public static readonly short EVENT_ADDR_REMOVED_IP6 = 263;
// Socket error codes
public static readonly short EPERM = 1; /* Operation not permitted */
public static readonly short ENOENT = 2; /* No such file or directory */
public static readonly short ESRCH = 3; /* No such process */
public static readonly short EINTR = 4; /* Interrupted system call */
public static readonly short EIO = 5; /* I/O error */
public static readonly short ENXIO = 6; /* No such device or address */
public static readonly short E2BIG = 7; /* Arg list too long */
public static readonly short ENOEXEC = 8; /* Exec format error */
public static readonly short EBADF = 9; /* Bad file number */
public static readonly short ECHILD = 10; /* No child processes */
public static readonly short EAGAIN = 11; /* Try again */
public static readonly short ENOMEM = 12; /* Out of memory */
public static readonly short EACCES = 13; /* Permission denied */
public static readonly short EFAULT = 14; /* Bad address */
public static readonly short ENOTBLK = 15; /* Block device required */
public static readonly short EBUSY = 16; /* Device or resource busy */
public static readonly short EEXIST = 17; /* File exists */
public static readonly short EXDEV = 18; /* Cross-device link */
public static readonly short ENODEV = 19; /* No such device */
public static readonly short ENOTDIR = 20; /* Not a directory */
public static readonly short EISDIR = 21; /* Is a directory */
public static readonly short EINVAL = 22; /* Invalid argument */
public static readonly short ENFILE = 23; /* File table overflow */
public static readonly short EMFILE = 24; /* Too many open files */
public static readonly short ENOTTY = 25; /* Not a typewriter */
public static readonly short ETXTBSY = 26; /* Text file busy */
public static readonly short EFBIG = 27; /* File too large */
public static readonly short ENOSPC = 28; /* No space left on device */
public static readonly short ESPIPE = 29; /* Illegal seek */
public static readonly short EROFS = 30; /* Read-only file system */
public static readonly short EMLINK = 31; /* Too many links */
public static readonly short EPIPE = 32; /* Broken pipe */
public static readonly short EDOM = 33; /* Math argument out of domain of func */
public static readonly short ERANGE = 34; /* Math result not representable */
public static readonly short EDEADLK = 35; /* Resource deadlock would occur */
public static readonly short ENAMETOOLONG = 36; /* File name too long */
public static readonly short ENOLCK = 37; /* No record locks available */
public static readonly short ENOSYS = 38; /* Function not implemented */
public static readonly short ENOTEMPTY = 39; /* Directory not empty */
public static readonly short ELOOP = 40; /* Too many symbolic links encountered */
public static readonly short EWOULDBLOCK = EAGAIN; /* Operation would block */
public static readonly short ENOMSG = 42; /* No message of desired type */
public static readonly short EIDRM = 43; /* Identifier removed */
public static readonly short ECHRNG = 44; /* Channel number out of range */
public static readonly short EL2NSYNC = 45; /* Level 2 not synchronized */
public static readonly short EL3HLT = 46; /* Level 3 halted */
public static readonly short EL3RST = 47; /* Level 3 reset */
public static readonly short ELNRNG = 48; /* Link number out of range */
public static readonly short EUNATCH = 49; /* Protocol driver not attached */
public static readonly short ENOCSI = 50; /* No CSI structure available */
public static readonly short EL2HLT = 51; /* Level 2 halted */
public static readonly short EBADE = 52; /* Invalid exchange */
public static readonly short EBADR = 53; /* Invalid request descriptor */
public static readonly short EXFULL = 54; /* Exchange full */
public static readonly short ENOANO = 55; /* No anode */
public static readonly short EBADRQC = 56; /* Invalid request code */
public static readonly short EBADSLT = 57; /* Invalid slot */
public static readonly short EDEADLOCK = EDEADLK;
public static readonly short EBFONT = 59; /* Bad font file format */
public static readonly short ENOSTR = 60; /* Device not a stream */
public static readonly short ENODATA = 61; /* No data available */
public static readonly short ETIME = 62; /* Timer expired */
public static readonly short ENOSR = 63; /* Out of streams resources */
public static readonly short ENONET = 64; /* Machine is not on the network */
public static readonly short ENOPKG = 65; /* Package not installed */
public static readonly short EREMOTE = 66; /* Object is remote */
public static readonly short ENOLINK = 67; /* Link has been severed */
public static readonly short EADV = 68; /* Advertise error */
public static readonly short ESRMNT = 69; /* Srmount error */
public static readonly short ECOMM = 70; /* Communication error on send */
public static readonly short EPROTO = 71; /* Protocol error */
public static readonly short EMULTIHOP = 72; /* Multihop attempted */
public static readonly short EDOTDOT = 73; /* RFS specific error */
public static readonly short EBADMSG = 74; /* Not a data message */
public static readonly short EOVERFLOW = 75; /* Value too large for defined data type */
public static readonly short ENOTUNIQ = 76; /* Name not unique on network */
public static readonly short EBADFD = 77; /* File descriptor in bad state */
public static readonly short EREMCHG = 78; /* Remote address changed */
public static readonly short ELIBACC = 79; /* Can not access a needed shared library */
public static readonly short ELIBBAD = 80; /* Accessing a corrupted shared library */
public static readonly short ELIBSCN = 81; /* .lib section in a.out corrupted */
public static readonly short ELIBMAX = 82; /* Attempting to link in too many shared libraries */
public static readonly short ELIBEXEC = 83; /* Cannot exec a shared library directly */
public static readonly short EILSEQ = 84; /* Illegal byte sequence */
public static readonly short ERESTART = 85; /* Interrupted system call should be restarted */
public static readonly short ESTRPIPE = 86; /* Streams pipe error */
public static readonly short EUSERS = 87; /* Too many users */
public static readonly short ENOTSOCK = 88; /* Socket operation on non-socket */
public static readonly short EDESTADDRREQ = 89; /* Destination address required */
public static readonly short EMSGSIZE = 90; /* Message too long */
public static readonly short EPROTOTYPE = 91; /* Protocol wrong type for socket */
public static readonly short ENOPROTOOPT = 92; /* Protocol not available */
public static readonly short EPROTONOSUPPORT = 93; /* Protocol not supported */
public static readonly short ESOCKTNOSUPPORT = 94; /* Socket type not supported */
public static readonly short EOPNOTSUPP = 95; /* Operation not supported on transport endpoint */
public static readonly short EPFNOSUPPORT = 96; /* Protocol family not supported */
public static readonly short EAFNOSUPPORT = 97; /* Address family not supported by protocol */
public static readonly short EADDRINUSE = 98; /* Address already in use */
public static readonly short EADDRNOTAVAIL = 99; /* Cannot assign requested address */
public static readonly short ENETDOWN = 100; /* Network is down */
public static readonly short ENETUNREACH = 101; /* Network is unreachable */
public static readonly short ENETRESET = 102; /* Network dropped connection because of reset */
public static readonly short ECONNABORTED = 103; /* Software caused connection abort */
public static readonly short ECONNRESET = 104; /* Connection reset by peer */
public static readonly short ENOBUFS = 105; /* No buffer space available */
public static readonly short EISCONN = 106; /* Transport endpoint is already connected */
public static readonly short ENOTCONN = 107; /* Transport endpoint is not connected */
public static readonly short ESHUTDOWN = 108; /* Cannot send after transport endpoint shutdown */
public static readonly short ETOOMANYREFS = 109; /* Too many references: cannot splice */
public static readonly short ETIMEDOUT = 110; /* Connection timed out */
public static readonly short ECONNREFUSED = 111; /* Connection refused */
public static readonly short EHOSTDOWN = 112; /* Host is down */
public static readonly short EHOSTUNREACH = 113; /* No route to host */
public static readonly short EALREADY = 114; /* Operation already in progress */
public static readonly short EINPROGRESS = 115; /* Operation now in progress */
public static readonly short ESTALE = 116; /* Stale NFS file handle */
public static readonly short EUCLEAN = 117; /* Structure needs cleaning */
public static readonly short ENOTNAM = 118; /* Not a XENIX named type file */
public static readonly short ENAVAIL = 119; /* No XENIX semaphores available */
public static readonly short EISNAM = 120; /* Is a named type file */
public static readonly short EREMOTEIO = 121; /* Remote I/O error */
public static readonly short EDQUOT = 122; /* Quota exceeded */
public static readonly short ENOMEDIUM = 123; /* No medium found */
public static readonly short EMEDIUMTYPE = 124; /* Wrong medium type */
public static readonly short INET_ADDRSTRLEN = 16;
public static readonly short INET6_ADDRSTRLEN = 46;
/** 255.255.255.255 */
//public static readonly uint IPADDR_NONE =((uint32_t)0xffffffffUL);
/** 127.0.0.1 */
//public static readonly uint IPADDR_LOOPBACK =((uint32_t)0x7f000001UL);
/** 0.0.0.0 */
//public static readonly uint IPADDR_ANY =((uint32_t)0x00000000UL);
/** 255.255.255.255 */
//public static readonly uint IPADDR_BROADCAST =((uint32_t)0xffffffffUL);
/** 255.255.255.255 */
//public static readonly uint INADDR_NONE =IPADDR_NONE;
/** 127.0.0.1 */
//public static readonly uint INADDR_LOOPBACK =IPADDR_LOOPBACK;
/** 0.0.0.0 */
//public static readonly uint INADDR_ANY =IPADDR_ANY;
/** 255.255.255.255 */
//public static readonly uint INADDR_BROADCAST =IPADDR_BROADCAST;
// Socket protocol types
public static readonly short SOCK_STREAM = 0x0001;
public static readonly short SOCK_DGRAM = 0x0002;
public static readonly short SOCK_RAW = 0x0003;
// Socket family types
public static readonly short AF_UNSPEC = 0x0000;
public static readonly short AF_INET = 0x0002;
public static readonly short AF_INET6 = 0x000a;
public static readonly short PF_INET = AF_INET;
public static readonly short PF_INET6 = AF_INET6;
public static readonly short PF_UNSPEC = AF_UNSPEC;
// Protocol command types
public static readonly short IPPROTO_IP = 0x0000;
public static readonly short IPPROTO_ICMP = 0x0001;
public static readonly short IPPROTO_TCP = 0x0006;
public static readonly short IPPROTO_UDP = 0x0011;
public static readonly short IPPROTO_IPV6 = 0x0029;
public static readonly short IPPROTO_ICMPV6 = 0x003a;
public static readonly short IPPROTO_UDPLITE = 0x0088;
public static readonly short IPPROTO_RAW = 0x00ff;
// send() and recv() flags
public static readonly short MSG_PEEK = 0x0001;
public static readonly short MSG_WAITALL = 0x0002; // NOT YET SUPPORTED
public static readonly short MSG_OOB = 0x0004; // NOT YET SUPPORTED
public static readonly short MSG_DONTWAIT = 0x0008;
public static readonly short MSG_MORE = 0x0010;
// Macro's for defining ioctl() command values
/*
public static readonly ulong IOCPARM_MASK = 0x7fU;
public static readonly ulong IOC_VOID = 0x20000000UL;
public static readonly ulong IOC_OUT = 0x40000000UL;
public static readonly ulong IOC_IN = 0x80000000UL;
public static readonly ulong IOC_INOUT = (IOC_IN | IOC_OUT);
public static readonly ulong IO(x,y) = (IOC_VOID | ((x)<<8)|(y));
public static readonly ulong IOR(x,y,t) = (IOC_OUT | (((long)sizeof(t) & IOCPARM_MASK)<<16) | ((x)<<8) | (y));
public static readonly ulong IOW(x,y,t) = (IOC_IN | (((long)sizeof(t) & IOCPARM_MASK)<<16) | ((x)<<8) | (y));
// ioctl() commands
public static readonly ulong FIONREAD = IOR('f', 127, unsigned long);
public static readonly ulong FIONBIO = IOW('f', 126, unsigned long);
*/
// Socket level option number
public static readonly short SOL_SOCKET = 0x0fff;
// Socket options
public static readonly short SO_DEBUG = 0x0001; // NOT YET SUPPORTED
public static readonly short SO_ACCEPTCONN = 0x0002;
public static readonly short SO_REUSEADDR = 0x0004;
public static readonly short SO_KEEPALIVE = 0x0008;
public static readonly short SO_DONTROUTE = 0x0010; // NOT YET SUPPORTED
public static readonly short SO_BROADCAST = 0x0020;
public static readonly short SO_USELOOPBACK = 0x0040; // NOT YET SUPPORTED
public static readonly short SO_LINGER = 0x0080;
public static readonly short SO_DONTLINGER = ((short)(~SO_LINGER));
public static readonly short SO_OOBINLINE = 0x0100; // NOT YET SUPPORTED
public static readonly short SO_REUSEPORT = 0x0200; // NOT YET SUPPORTED
public static readonly short SO_SNDBUF = 0x1001; // NOT YET SUPPORTED
public static readonly short SO_RCVBUF = 0x1002;
public static readonly short SO_SNDLOWAT = 0x1003; // NOT YET SUPPORTED
public static readonly short SO_RCVLOWAT = 0x1004; // NOT YET SUPPORTED
public static readonly short SO_SNDTIMEO = 0x1005;
public static readonly short SO_RCVTIMEO = 0x1006;
public static readonly short SO_ERROR = 0x1007;
public static readonly short SO_TYPE = 0x1008;
public static readonly short SO_CONTIMEO = 0x1009;
public static readonly short SO_NO_CHECK = 0x100a;
public static readonly short SO_BINDTODEVICE = 0x100b;
// IPPROTO_IP options
public static readonly short IP_TOS = 0x0001;
public static readonly short IP_TTL = 0x0002;
public static readonly short IP_PKTINFO = 0x0008;
// IPPROTO_TCP options
public static readonly short TCP_NODELAY = 0x0001;
public static readonly short TCP_KEEPALIVE = 0x0002;
public static readonly short TCP_KEEPIDLE = 0x0003;
public static readonly short TCP_KEEPINTVL = 0x0004;
public static readonly short TCP_KEEPCNT = 0x0005;
// IPPROTO_IPV6 options
public static readonly short IPV6_CHECKSUM = 0x0007; // RFC3542: calculate and insert the ICMPv6 checksum for raw sockets.
public static readonly short IPV6_V6ONLY = 0x001b; // RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only.
// UDPLITE options
public static readonly short UDPLITE_SEND_CSCOV = 0x01; // sender checksum coverage
public static readonly short UDPLITE_RECV_CSCOV = 0x02; // minimal receiver checksum coverage
// UDPLITE options
public static readonly short IP_MULTICAST_TTL = 5;
public static readonly short IP_MULTICAST_IF = 6;
public static readonly short IP_MULTICAST_LOOP = 7;
// Multicast options
public static readonly short IP_ADD_MEMBERSHIP = 3;
public static readonly short IP_DROP_MEMBERSHIP = 4;
public static readonly short IPV6_JOIN_GROUP = 12;
public static readonly short IPV6_ADD_MEMBERSHIP = IPV6_JOIN_GROUP;
public static readonly short IPV6_LEAVE_GROUP = 13;
public static readonly short IPV6_DROP_MEMBERSHIP = IPV6_LEAVE_GROUP;
// Polling options
public static readonly short POLLIN = 0x001;
public static readonly short POLLOUT = 0x002;
public static readonly short POLLERR = 0x004;
public static readonly short POLLNVAL = 0x008;
// Below values are unimplemented
public static readonly short POLLRDNORM = 0x010;
public static readonly short POLLRDBAND = 0x020;
public static readonly short POLLPRI = 0x040;
public static readonly short POLLWRNORM = 0x080;
public static readonly short POLLWRBAND = 0x100;
public static readonly short POLLHUP = 0x200;
public static readonly short F_GETFL = 0x0003;
public static readonly short F_SETFL = 0x0004;
// File status flags and file access modes for fnctl, these are bits in an int.
public static readonly short O_NONBLOCK = 1;
public static readonly short O_NDELAY = O_NONBLOCK;
public static readonly short O_RDONLY = 2;
public static readonly short O_WRONLY = 4;
public static readonly short O_RDWR = (short)(O_RDONLY|O_WRONLY);
public static readonly short MSG_TRUNC = 0x04;
public static readonly short MSG_CTRUNC = 0x08;
public static readonly short SHUT_RD = 0x0;
public static readonly short SHUT_WR = 0x1;
public static readonly short SHUT_RDWR = 0x2;
}
}

156
examples/csharp/Event.cs Normal file
View File

@@ -0,0 +1,156 @@
/*
* 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.Net;
using ZeroTier;
namespace ZeroTier
{
/* Convenience structures for exposing lower level operational details to the user.
These structures do not have the same memory layout or content as those found in
include/ZeroTierSockets.h */
/// <summary>
/// Structure containing information about the local Node.
/// </summary>
public class NodeDetails
{
// Node ID
public ulong nodeId;
/**
* The port used by the service to send and receive
* all encapsulated traffic
*/
public ushort primaryPort;
public ushort secondaryPort;
public ushort tertiaryPort;
/**
* ZT version
*/
public byte versionMajor;
public byte versionMinor;
public byte versionRev;
}
public class MulticastSubscription
{
ulong macAddress;
uint AdditionalDistinguishingInformation;
}
/// <summary>
/// Structure containing information about virtual networks.
/// </summary>
public class NetworkDetails
{
public ulong networkId;
public ulong macAddress;
public string networkName;
//public byte status;
public byte type;
public ushort mtu;
public bool bridgingAllowed;
public bool broadcastEnabled;
//public int portError;
public IPAddress[] assignedAddresses;
public IPAddress[] routes;
public MulticastSubscription[] multicastSubscroptions;
}
/// <summary>
/// Structure containing state information about the low-level ethernet driver.
/// </summary>
public class NetifDetails
{
public ulong networkId;
public ulong macAddress;
public int mtu;
}
/// <summary>
/// Structure containing routing information for networks.
/// </summary>
public class RouteDetails
{
public EndPoint target;
public EndPoint via;
public ushort flags;
public ushort metric;
}
/// <summary>
/// Structure containing information about remote peer reachability.
/// </summary>
public class PeerDetails
{
ulong nodeId;
byte versionMajor;
byte versionMinor;
byte versionRev;
int latency;
byte role;
IPAddress[] paths;
}
/// <summary>
/// Structure containing information about assigned addresses.
/// </summary>
public class AddrDetails
{
ulong networkId;
IPAddress address;
}
/// <summary>
/// Class used to convey details of a low-level network event to the user.
/// </summary>
public class Event
{
int _eventCode;
string _eventName;
public NodeDetails nodeDetails;
public NetworkDetails networkDetails;
public NetifDetails netifDetails;
public RouteDetails routeDetails;
public PeerDetails peerDetails;
public AddrDetails addrDetails;
public Event(int eventCode, string eventName)
{
_eventCode = eventCode;
_eventName = eventName;
nodeDetails = null;
networkDetails = null;
netifDetails = null;
routeDetails = null;
peerDetails = null;
addrDetails = null;
}
public int EventCode {
get {
return _eventCode;
}
}
public string EventName {
get {
return _eventName;
}
}
}
}

12
examples/csharp/Makefile Normal file
View File

@@ -0,0 +1,12 @@
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

492
examples/csharp/Node.cs Normal file
View File

@@ -0,0 +1,492 @@
/*
* 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
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 myZeroTierEventCallback(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,myZeroTierEventCallback,_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();
}
}
}
}

45
examples/csharp/README.md Normal file
View File

@@ -0,0 +1,45 @@
ZeroTier Sockets for C# .NET (Work In Progress)
=====
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.
tl;dr:
```
using System.Net.Sockets;
using ZeroTier;
void myCallback(ZeroTier.Event e)
{
Console.WriteLine("{0} ({1})", e.EventCode, e.EventName);
}
...
ZeroTier.Node node = new ZeroTier.Node("path", myCallback, 9991);
node.Start();
node.Join(0xc287ac0b42a6fb4c);
...
ZeroTier.Socket sock = new ZeroTier.Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
sock.Connect(remoteEndPoint);
...
node.Stop();
```
See [example.cs](./example.cs) for a complete client/server implementation.
## Building and running the example
```
make debug|release
./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.

513
examples/csharp/Socket.cs Normal file
View File

@@ -0,0 +1,513 @@
/*
* 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; // For ObjectDisposedException
using System.Net; // For IPEndPoint
using System.Net.Sockets; // For SocketException
using System.Runtime.InteropServices;
using ZeroTier;
/// <summary>
/// ZeroTier SDK
/// </summary>
namespace ZeroTier
{
/// <summary>
/// ZeroTier Socket - An lwIP socket mediated over a ZeroTier virtual link
/// </summary>
public class Socket
{
/// <summary>No error.</summary>
public static readonly int ZTS_ERR_OK = 0;
/// <summary>Socket error, see Socket.ErrNo() for additional context.</summary>
public static readonly int ZTS_ERR_SOCKET = -1;
/// <summary>You probably did something at the wrong time.</summary>
public static readonly int ZTS_ERR_SERVICE = -2;
/// <summary>Invalid argument.</summary>
public static readonly int ZTS_ERR_ARG = -3;
/// <summary>No result. (not necessarily an error.)</summary>
public static readonly int ZTS_ERR_NO_RESULT = -4;
/// <summary>Consider filing a bug report.</summary>
public static readonly int ZTS_ERR_GENERAL = -5;
int _fd;
bool _isClosed;
bool _isListening;
AddressFamily _socketFamily;
SocketType _socketType;
ProtocolType _socketProtocol;
internal EndPoint _localEndPoint;
internal EndPoint _remoteEndPoint;
public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
{
int family = -1;
int type = -1;
int protocol = -1;
// Map .NET socket parameters to ZeroTier equivalents
switch (addressFamily)
{
case AddressFamily.InterNetwork:
family = Constants.AF_INET;
break;
case AddressFamily.InterNetworkV6:
family = Constants.AF_INET6;
break;
case AddressFamily.Unknown:
family = Constants.AF_UNSPEC;
break;
}
switch (socketType)
{
case SocketType.Stream:
type = Constants.SOCK_STREAM;
break;
case SocketType.Dgram:
type = Constants.SOCK_DGRAM;
break;
}
switch (protocolType)
{
case ProtocolType.Udp:
protocol = Constants.IPPROTO_TCP;
break;
case ProtocolType.Tcp:
protocol = Constants.IPPROTO_UDP;
break;
case ProtocolType.Unspecified:
protocol = 0; // ?
break;
}
if ((_fd = zts_socket(family, type, protocol)) < 0)
{
throw new SocketException((int)_fd);
}
_socketFamily = addressFamily;
_socketType = socketType;
_socketProtocol = protocolType;
_isClosed = false;
}
private Socket(int fileDescriptor,
AddressFamily addressFamily,
SocketType socketType,
ProtocolType protocolType,
EndPoint localEndPoint,
EndPoint remoteEndPoint)
{
_socketFamily = addressFamily;
_socketType = socketType;
_socketProtocol = protocolType;
_localEndPoint = localEndPoint;
_remoteEndPoint = remoteEndPoint;
_isClosed = false;
_isListening = false;
_fd = fileDescriptor;
}
public void Connect(IPEndPoint remoteEndPoint)
{
if (_isClosed) {
throw new ObjectDisposedException("Socket has been closed");
}
if (_fd < 0) {
// Invalid file descriptor
throw new SocketException((int)Constants.ERR_SOCKET);
}
if (remoteEndPoint == null) {
throw new ArgumentNullException(nameof(remoteEndPoint));
}
int err = Constants.ERR_OK;
int addrlen = 0;
if (remoteEndPoint.AddressFamily == AddressFamily.InterNetwork)
{
zts_sockaddr_in sa = new zts_sockaddr_in();
addrlen = Marshal.SizeOf(typeof(zts_sockaddr_in));
switch (remoteEndPoint.AddressFamily)
{
case AddressFamily.InterNetwork:
sa.sin_family = (byte)Constants.AF_INET;
break;
case AddressFamily.InterNetworkV6:
sa.sin_family = (byte)Constants.AF_INET6;
break;
case AddressFamily.Unknown:
sa.sin_family = (byte)Constants.AF_UNSPEC;
break;
}
sa.sin_port = (short)zts_htons((ushort)remoteEndPoint.Port);
sa.sin_addr = remoteEndPoint.Address.GetAddressBytes();
sa.sin_len = (byte)addrlen; // lwIP-specific
IntPtr ptr1 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(zts_sockaddr)));
Marshal.StructureToPtr(sa, ptr1, false);
//zts_sockaddr sAddr = (zts_sockaddr)Marshal.PtrToStructure(ptr1, typeof(zts_sockaddr));
err = zts_connect(_fd, ptr1, (byte)addrlen);
}
if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
{
/*
socketAddress.iSockaddrLength = Marshal.SizeOf(typeof(sockaddr_in6));
socketAddress.lpSockAddr = CriticalAllocHandle.FromSize(socketAddress.iSockaddrLength);
sockaddr_in6 sa = new sockaddr_in6();
sa.sin6_family = (short)AddressFamily.InterNetworkV6;
sa.sin6_port = (ushort)endpoint.Port;
sa.sin6_addr = endpoint.Address.GetAddressBytes();
sa.sin6_scope_id = (uint)endpoint.Address.ScopeId;
Marshal.StructureToPtr(sa, (IntPtr)socketAddress.lpSockAddr, false);
*/
}
if (err < 0) {
throw new SocketException((int)err);
}
_remoteEndPoint = remoteEndPoint;
}
public void Bind(IPEndPoint localEndPoint)
{
if (_isClosed) {
throw new ObjectDisposedException("Socket has been closed");
}
if (_fd < 0) {
// Invalid file descriptor
throw new SocketException((int)Constants.ERR_SOCKET);
}
if (localEndPoint == null) {
throw new ArgumentNullException(nameof(localEndPoint));
}
int err = Constants.ERR_OK;
int addrlen = 0;
if (localEndPoint.AddressFamily == AddressFamily.InterNetwork)
{
zts_sockaddr_in sa = new zts_sockaddr_in();
addrlen = Marshal.SizeOf(typeof(zts_sockaddr_in));
switch (localEndPoint.AddressFamily)
{
case AddressFamily.InterNetwork:
sa.sin_family = (byte)Constants.AF_INET;
break;
case AddressFamily.InterNetworkV6:
sa.sin_family = (byte)Constants.AF_INET6;
break;
case AddressFamily.Unknown:
sa.sin_family = (byte)Constants.AF_UNSPEC;
break;
}
sa.sin_port = (short)zts_htons((ushort)localEndPoint.Port);
sa.sin_addr = localEndPoint.Address.GetAddressBytes();
sa.sin_len = (byte)addrlen; // lwIP-specific
IntPtr ptr1 = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(zts_sockaddr)));
Marshal.StructureToPtr(sa, ptr1, false);
err = zts_bind(_fd, ptr1, (byte)addrlen);
}
if (localEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
{
/*
socketAddress.iSockaddrLength = Marshal.SizeOf(typeof(sockaddr_in6));
socketAddress.lpSockAddr = CriticalAllocHandle.FromSize(socketAddress.iSockaddrLength);
sockaddr_in6 sa = new sockaddr_in6();
sa.sin6_family = (short)AddressFamily.InterNetworkV6;
sa.sin6_port = (ushort)endpoint.Port;
sa.sin6_addr = endpoint.Address.GetAddressBytes();
sa.sin6_scope_id = (uint)endpoint.Address.ScopeId;
Marshal.StructureToPtr(sa, (IntPtr)socketAddress.lpSockAddr, false);
*/
}
if (err < 0) {
throw new SocketException((int)err);
}
_localEndPoint = localEndPoint;
}
public void Listen(int backlog)
{
if (_isClosed) {
throw new ObjectDisposedException("Socket has been closed");
}
if (_fd < 0) {
// Invalid file descriptor
throw new SocketException((int)Constants.ERR_SOCKET);
}
int err = Constants.ERR_OK;
if ((err = zts_listen(_fd, backlog)) < 0) {
// Invalid backlog value perhaps?
throw new SocketException((int)Constants.ERR_SOCKET);
}
_isListening = true;
}
public Socket Accept()
{
if (_isClosed) {
throw new ObjectDisposedException("Socket has been closed");
}
if (_fd < 0) {
// Invalid file descriptor
throw new SocketException((int)Constants.ERR_SOCKET);
}
if (_isListening == false) {
throw new InvalidOperationException("Socket is not in a listening state. Call Listen() first");
}
// TODO: Rewrite -- Check for memory leaks
// Inform zts_accept of the size of the available address buffer
int addrlen = Marshal.SizeOf(typeof(zts_sockaddr_in));
IntPtr addrlenPtr = GCHandle.Alloc(addrlen, GCHandleType.Pinned).AddrOfPinnedObject();
// Allocate space for address buffer and provide pointer to zts_accept
zts_sockaddr_in in4 = new zts_sockaddr_in();
IntPtr remoteAddrPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(zts_sockaddr_in)));
Marshal.StructureToPtr(in4, remoteAddrPtr, false);
int err = zts_accept(_fd, remoteAddrPtr, addrlenPtr);
if (err < 0) {
throw new SocketException((int)err);
}
in4 = (zts_sockaddr_in)Marshal.PtrToStructure(remoteAddrPtr, typeof(zts_sockaddr_in));
// Convert sockaddr contents to IPEndPoint
IPAddress ipAddress = new IPAddress(in4.sin_addr);
IPEndPoint clientEndPoint = new IPEndPoint(ipAddress, zts_ntohs((ushort)in4.sin_port));
// Create new socket by providing file descriptor returned from zts_accept call.
Socket clientSocket = new Socket(
err, _socketFamily, _socketType, _socketProtocol, _localEndPoint, clientEndPoint);
return clientSocket;
}
public void Shutdown(SocketShutdown how)
{
if (_isClosed) {
throw new ObjectDisposedException("Socket has been closed");
}
int ztHow = 0;
switch (how)
{
case SocketShutdown.Receive:
ztHow = Constants.O_RDONLY;
break;
case SocketShutdown.Send:
ztHow = Constants.O_WRONLY;
break;
case SocketShutdown.Both:
ztHow = Constants.O_RDWR;
break;
}
zts_shutdown(_fd, ztHow);
}
public void Close()
{
if (_isClosed) {
throw new ObjectDisposedException("Socket has already been closed");
}
zts_close(_fd);
_isClosed = true;
}
public EndPoint RemoteEndPoint
{
get {
return _remoteEndPoint;
}
}
public EndPoint LocalEndPoint
{
get {
return _localEndPoint;
}
}
public Int32 Send(Byte[] buffer)
{
if (_isClosed) {
throw new ObjectDisposedException("Socket has been closed");
}
if (_fd < 0) {
throw new SocketException((int)ZeroTier.Constants.ERR_SOCKET);
}
if (buffer == null) {
throw new ArgumentNullException(nameof(buffer));
}
int flags = 0;
IntPtr bufferPtr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
return zts_send(_fd, bufferPtr, (uint)Buffer.ByteLength(buffer), (int)flags);
}
public Int32 Receive(Byte[] buffer)
{
if (_isClosed) {
throw new ObjectDisposedException("Socket has been closed");
}
if (_fd < 0) {
throw new SocketException((int)ZeroTier.Constants.ERR_SOCKET);
}
if (buffer == null) {
throw new ArgumentNullException(nameof(buffer));
}
int flags = 0;
IntPtr bufferPtr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
return zts_recv(_fd, bufferPtr, (uint)Buffer.ByteLength(buffer), (int)flags);
}
/* Structures and functions used internally to communicate with
lower-level C API defined in include/ZeroTierSockets.h */
[DllImport("libzt", EntryPoint="CSharp_zts_get_all_stats")]
static extern int zts_get_all_stats(IntPtr arg1);
[DllImport("libzt", EntryPoint="CSharp_zts_get_protocol_stats")]
static extern int zts_get_protocol_stats(int arg1, IntPtr arg2);
[DllImport("libzt", EntryPoint="CSharp_zts_socket")]
static extern int zts_socket(int arg1, int arg2, int arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_connect")]
static extern int zts_connect(int arg1, IntPtr arg2, ushort arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_bind")]
static extern int zts_bind(int arg1, IntPtr arg2, ushort arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_listen")]
static extern int zts_listen(int arg1, int arg2);
[DllImport("libzt", EntryPoint="CSharp_zts_accept")]
static extern int zts_accept(int arg1, IntPtr arg2, IntPtr arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_setsockopt")]
static extern int zts_setsockopt(int arg1, int arg2, int arg3, IntPtr arg4, ushort arg5);
[DllImport("libzt", EntryPoint="CSharp_zts_getsockopt")]
static extern int zts_getsockopt(int arg1, int arg2, int arg3, IntPtr arg4, IntPtr arg5);
[DllImport("libzt", EntryPoint="CSharp_zts_getsockname")]
static extern int zts_getsockname(int arg1, IntPtr arg2, IntPtr arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_getpeername")]
static extern int zts_getpeername(int arg1, IntPtr arg2, IntPtr arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_close")]
static extern int zts_close(int arg1);
[DllImport("libzt", EntryPoint="CSharp_zts_fcntl")]
static extern int zts_fcntl(int arg1, int arg2, int arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_poll")]
static extern int zts_poll(IntPtr arg1, uint arg2, int arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_ioctl")]
static extern int zts_ioctl(int arg1, uint arg2, IntPtr arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_send")]
static extern int zts_send(int arg1, IntPtr arg2, uint arg3, int arg4);
[DllImport("libzt", EntryPoint="CSharp_zts_sendto")]
static extern int zts_sendto(int arg1, IntPtr arg2, uint arg3, int arg4, IntPtr arg5, ushort arg6);
[DllImport("libzt", EntryPoint="CSharp_zts_sendmsg")]
static extern int zts_sendmsg(int arg1, IntPtr arg2, int arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_recv")]
static extern int zts_recv(int arg1, IntPtr arg2, uint arg3, int arg4);
[DllImport("libzt", EntryPoint="CSharp_zts_recvfrom")]
static extern int zts_recvfrom(int arg1, IntPtr arg2, uint arg3, int arg4, IntPtr arg5, IntPtr arg6);
[DllImport("libzt", EntryPoint="CSharp_zts_recvmsg")]
static extern int zts_recvmsg(int arg1, IntPtr arg2, int arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_read")]
static extern int zts_read(int arg1, IntPtr arg2, uint arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_readv")]
static extern int zts_readv(int arg1, IntPtr arg2, int arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_write")]
static extern int zts_write(int arg1, IntPtr arg2, uint arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_writev")]
static extern int zts_writev(int arg1, IntPtr arg2, int arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_shutdown")]
static extern int zts_shutdown(int arg1, int arg2);
[DllImport("libzt", EntryPoint="CSharp_zts_add_dns_nameserver")]
static extern int zts_add_dns_nameserver(IntPtr arg1);
[DllImport("libzt", EntryPoint="CSharp_zts_del_dns_nameserver")]
static extern int zts_del_dns_nameserver(IntPtr arg1);
[DllImport("libzt", EntryPoint="CSharp_zts_htons")]
static extern ushort zts_htons(ushort arg1);
[DllImport("libzt", EntryPoint="CSharp_zts_htonl")]
static extern ushort zts_htonl(ushort arg1);
[DllImport("libzt", EntryPoint="CSharp_zts_ntohs")]
static extern ushort zts_ntohs(ushort arg1);
[DllImport("libzt", EntryPoint="CSharp_zts_ntohl")]
static extern ushort zts_ntohl(ushort arg1);
[DllImport("libzt", EntryPoint="CSharp_zts_inet_ntop")]
static extern string zts_inet_ntop(int arg1, IntPtr arg2, string arg3, ushort arg4);
[DllImport("libzt", EntryPoint="CSharp_zts_inet_pton")]
static extern int zts_inet_pton(int arg1, string arg2, IntPtr arg3);
[DllImport("libzt", EntryPoint="CSharp_zts_inet_addr")]
static extern ushort zts_inet_addr(string arg1);
[DllImport("libzt", EntryPoint="CSharp_zts_errno_get")]
static extern int zts_errno_get();
/// <summary>
/// Gets the value of errno from the unmanaged region
/// </summary>
/// <value></value>
public static int ErrNo {
get {
return zts_errno_get();
}
}
[StructLayout(LayoutKind.Sequential)]
struct zts_sockaddr
{
public byte sa_len;
public byte sa_family;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)]
public byte[] sa_data;
}
[StructLayout(LayoutKind.Sequential)]
struct zts_in_addr
{
public uint s_addr;
}
[StructLayout(LayoutKind.Sequential)]
struct zts_sockaddr_in
{
public byte sin_len;
public byte sin_family;
public short sin_port;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] sin_addr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public char[] sin_zero; // SIN_ZERO_LEN
}
}
}

201
examples/csharp/example.cs Normal file
View File

@@ -0,0 +1,201 @@
using System;
using System.Threading;
using System.Net;
using System.Net.Sockets; // For SocketType, etc
using System.Text; // For Encoding
using ZeroTier; // For ZeroTier.Node, ZeroTier.Event, and ZeroTier.Socket
public class ExampleApp {
ZeroTier.Node node;
/**
* Initialize and start ZeroTier
*/
public void StartZeroTier(string configFilePath, ushort servicePort, ulong networkId)
{
node = new ZeroTier.Node(configFilePath, myZeroTierEventCallback, servicePort);
node.Start(); // Network activity only begins after calling Start()
/* How you do this next part is up to you, but essentially we're waiting for the node
to signal to us (via a ZeroTierEvent) that it has access to the internet and is
able to talk to one of our root servers. As a convenience you can just periodically check
IsOnline() instead of looking for the event via the callback. */
while (!node.IsOnline()) { Thread.Sleep(100); }
/* After the node comes online you may now join/leave networks. You will receive
notifications via the callback function regarding the status of your join request as well
as any subsequent network-related events such as the assignment of an IP address, added
or removed routes, etc. */
node.Join(networkId);
/* Note that ZeroTierSocket calls will fail if there are no routes available, for this
reason we should wait to make those calls until the node has indicated to us that at
least one network has been joined successfully. */
while (!node.HasRoutes()) { Thread.Sleep(100); }
}
/**
* Stop ZeroTier
*/
public void StopZeroTier()
{
node.Stop();
}
/**
* Your application should process event messages and return control as soon as possible. Blocking
* or otherwise time-consuming operations are not reccomended here.
*/
public void myZeroTierEventCallback(ZeroTier.Event e)
{
Console.WriteLine("Event.eventCode = {0} ({1})", e.EventCode, e.EventName);
if (e.EventCode == ZeroTier.Constants.EVENT_NODE_ONLINE) {
Console.WriteLine("Node is online");
Console.WriteLine(" - Address (NodeId): " + node.NodeId);
}
if (e.EventCode == ZeroTier.Constants.EVENT_NETWORK_OK) {
Console.WriteLine(" - Network ID: " + e.networkDetails.networkId.ToString("x16"));
}
}
/**
* Example server
*/
public void YourServer() {
string data = null;
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
string serverIP = "0.0.0.0";
int port = 8000;
IPAddress ipAddress = IPAddress.Parse(serverIP);
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
Console.WriteLine(localEndPoint.ToString());
ZeroTier.Socket listener = new ZeroTier.Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp );
// Bind the socket to the local endpoint and
// listen for incoming connections.
try {
listener.Bind(localEndPoint);
listener.Listen(10);
// Start listening for connections.
while (true) {
Console.WriteLine("Waiting for a connection...");
// Program is suspended while waiting for an incoming connection.
Console.WriteLine("accepting...");
ZeroTier.Socket handler = listener.Accept();
data = null;
Console.WriteLine("accepted connection from: " + handler.RemoteEndPoint.ToString());
// An incoming connection needs to be processed.
while (true) {
int bytesRec = handler.Receive(bytes);
data += Encoding.ASCII.GetString(bytes,0,bytesRec);
if (data.IndexOf("<EOF>") > -1) {
break;
}
}
// Show the data on the console.
Console.WriteLine( "Text received : {0}", data);
// Echo the data back to the client.
byte[] msg = Encoding.ASCII.GetBytes(data);
handler.Send(msg);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
/**
* Example client
*/
public void YourClient() {
// Data buffer for incoming data.
byte[] bytes = new byte[1024];
// Connect to a remote device.
try {
string serverIP = "10.244.180.7";
int port = 8000;
IPAddress ipAddress = IPAddress.Parse(serverIP);
IPEndPoint remoteEndPoint = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
ZeroTier.Socket sender = new ZeroTier.Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp );
// Connect the socket to the remote endpoint. Catch any errors.
try {
Console.WriteLine("Socket connecting to {0}...",
remoteEndPoint.ToString());
sender.Connect(remoteEndPoint);
Console.WriteLine("Socket connected to {0}",
sender.RemoteEndPoint.ToString());
// Encode the data string into a byte array.
byte[] msg = Encoding.ASCII.GetBytes("This is a test");
// Send the data through the socket.
int bytesSent = sender.Send(msg);
// Receive the response from the remote device.
int bytesRec = sender.Receive(bytes);
Console.WriteLine("Echoed test = {0}",
Encoding.ASCII.GetString(bytes,0,bytesRec));
// Release the socket.
sender.Shutdown(SocketShutdown.Both);
sender.Close();
} catch (ArgumentNullException ane) {
Console.WriteLine("ArgumentNullException : {0}",ane.ToString());
} catch (SocketException se) {
Console.WriteLine("SocketException : {0}",se.ToString());
} catch (Exception e) {
Console.WriteLine("Unexpected exception : {0}", e.ToString());
}
} catch (Exception e) {
Console.WriteLine( e.ToString());
}
}
}
public class example
{
static void Main()
{
ExampleApp exampleApp = new ExampleApp();
ulong networkId = 0x8216ab0a47c622a1;
ushort servicePort = 9991;
string configFilePath = "path";
exampleApp.StartZeroTier(configFilePath, servicePort, networkId);
exampleApp.YourClient();
exampleApp.StopZeroTier();
}
}

55
examples/csharp/zt.i Normal file
View File

@@ -0,0 +1,55 @@
%module zt
%include <windows.i>
%include <stl.i>
%include <typemaps.i>
// Prevent SWIG from doing anything funny with our types
%apply unsigned char { uint8_t };
%apply char { int8_t };
%apply unsigned short { uint16_t };
%apply int { int16_t };
%apply unsigned short { uint32_t };
%apply int { int32_t };
%apply unsigned long long { uint64_t };
%apply long long { int64_t };
%typemap(ctype) zts_sockaddr* "zts_sockaddr*"
// Ignore all classes/structs (We'll define these manually in C#)
// %rename($ignore, %$isclass) "";
%ignore zts_in6_addr;
%ignore zts_sockaddr;
%ignore zts_in_addr;
%ignore zts_sockaddr_in;
%ignore zts_sockaddr_storage;
%ignore zts_sockaddr_in6;
%ignore zts_callback_msg;
%ignore zts_node_details;
%ignore zts_network_details;
%ignore zts_netif_details;
%ignore zts_virtual_network_route;
%ignore zts_peer_details;
%ignore zts_addr_details;
#define ZTS_CSHARP=1
%{
#include "../../include/ZeroTierSockets.h"
%}
// Typemap for our event callback
%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(CppCallback, CSharpCallbackWithStruct)
%include "../../include/ZeroTierSockets.h"

1143
examples/csharp/zt_wrap.cxx Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -93,7 +93,11 @@ int zts_allow_local_conf(uint8_t allowed = 1)
return ZTS_ERR_SERVICE;
}
#ifdef ZTS_PINVOKE
int zts_start(const char *path, CppCallback callback, uint16_t port)
#else
int zts_start(const char *path, void (*callback)(void *), uint16_t port)
#endif
{
Mutex::Lock _l(serviceLock);
_lwip_driver_init();