Browse Source

New service, work in progress

Adam Ierymenko 5 years ago
parent
commit
ae2120eb96

+ 2 - 2
CMakeLists.txt

@@ -141,7 +141,7 @@ endif()
 add_subdirectory(node)
 add_subdirectory(controller)
 add_subdirectory(osdep)
-add_subdirectory(service)
+#add_subdirectory(service)
 add_subdirectory(root)
 
 if(WIN32)
@@ -151,7 +151,7 @@ if(WIN32)
 endif(WIN32)
 
 set(libs
-	zt_service
+#	zt_service
 	zt_osdep
 	zt_core
 	zt_controller

+ 30 - 0
attic/listaddrinfo.go

@@ -0,0 +1,30 @@
+package main
+
+import (
+	"fmt"
+	"net"
+)
+
+func main() {
+	ifs, err := net.Interfaces()
+	if err != nil {
+		fmt.Printf("Error: %s\n", err.Error())
+		return
+	}
+	for _, i := range ifs {
+		fmt.Printf("name: %s\n", i.Name)
+		fmt.Printf("hwaddr: %s\n", i.HardwareAddr.String())
+		fmt.Printf("index: %d\n", i.Index)
+		fmt.Printf("addrs:\n")
+		addrs, _ := i.Addrs()
+		for _, a := range addrs {
+			fmt.Printf("  %s\n", a.String())
+		}
+		fmt.Printf("multicast:\n")
+		mc, _ := i.MulticastAddrs()
+		for _, m := range mc {
+			fmt.Printf("  %s\n", m.String())
+		}
+		fmt.Printf("\n")
+	}
+}

+ 0 - 0
service/CMakeLists.txt → attic/service/CMakeLists.txt


+ 0 - 0
service/OneService.cpp → attic/service/OneService.cpp


+ 0 - 0
service/OneService.hpp → attic/service/OneService.hpp


+ 0 - 0
service/README.md → attic/service/README.md


+ 3 - 0
go/go.mod

@@ -0,0 +1,3 @@
+module zerotier-go
+
+go 1.13

+ 13 - 0
go/native/CMakeLists.txt

@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 2.8)
+project(zt_go_native)
+
+set(src
+	GoNode.cpp
+)
+
+set(headers
+	GoNode.h
+)
+
+add_library(${PROJECT_NAME} STATIC ${src} ${headers})
+target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11)

+ 493 - 0
go/native/GoNode.cpp

@@ -0,0 +1,493 @@
+/*
+ * Copyright (c)2019 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: 2023-01-01
+ *
+ * On the date above, in accordance with the Business Source License, use
+ * of this software will be governed by vergnn 2.0 of the Apache License.
+ */
+/****/
+
+#include "GoNode.h"
+
+#include "../../node/Constants.hpp"
+#include "../../node/InetAddress.hpp"
+#include "../../node/Node.hpp"
+#include "../../node/Utils.hpp"
+#include "../../osdep/OSUtils.hpp"
+#include "../../osdep/BlockingQueue.hpp"
+#include "../../osdep/EthernetTap.hpp"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifndef __WINDOWS__
+#include <errno.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#ifdef __BSD__
+#include <net/if.h>
+#endif
+#ifdef __LINUX__
+#ifndef IPV6_DONTFRAG
+#define IPV6_DONTFRAG 62
+#endif
+#endif
+#endif // !__WINDOWS__
+
+#include <thread>
+#include <mutex>
+#include <map>
+#include <vector>
+#include <array>
+#include <set>
+
+#ifdef __WINDOWS__
+#define SETSOCKOPT_FLAG_TYPE BOOL
+#define SETSOCKOPT_FLAG_TRUE TRUE
+#define SETSOCKOPT_FLAG_FALSE FALSE
+#else
+#define SETSOCKOPT_FLAG_TYPE int
+#define SETSOCKOPT_FLAG_TRUE 1
+#define SETSOCKOPT_FLAG_FALSE 0
+#endif
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0
+#endif
+
+using namespace ZeroTier;
+
+struct ZT_GoNodeThread
+{
+	std::string ip;
+	int port;
+	int af;
+	std::atomic_bool run;
+	std::thread thr;
+};
+
+struct ZT_GoNode_Impl
+{
+	Node *node;
+	volatile int64_t nextBackgroundTaskDeadline;
+
+	int (*goPathCheckFunc)(ZT_GoNode *,ZT_Node *,uint64_t ztAddress,const void *);
+	int (*goPathLookupFunc)(ZT_GoNode *,ZT_Node *,int desiredAddressFamily,void *);
+	int (*goStateObjectGetFunc)(ZT_GoNode *,ZT_Node *,int objType,const uint64_t id[2],void *buf,unsigned int bufSize);
+
+	std::map< ZT_SOCKET,ZT_GoNodeThread > threads;
+	std::mutex threads_l;
+
+	BlockingQueue<ZT_GoNodeEvent> eq;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static int ZT_GoNode_VirtualNetworkConfigFunction(
+	ZT_Node *node,
+	void *uptr,
+	void *tptr,
+	uint64_t nwid,
+	void **nptr,
+	enum ZT_VirtualNetworkConfigOperation op,
+	const ZT_VirtualNetworkConfig *cfg)
+{
+	ZT_GoNodeEvent ev;
+	ev.type = ZT_GONODE_EVENT_NETWORK_CONFIG_UPDATE;
+	ev.data.nconf.op = op;
+	if (cfg)
+		ev.data.nconf.conf = *cfg;
+	reinterpret_cast<ZT_GoNode *>(uptr)->eq.post(ev);
+}
+
+static void ZT_GoNode_VirtualNetworkFrameFunction(
+	ZT_Node *node,
+	void *uptr,
+	void *tptr,
+	uint64_t nwid,
+	void **nptr,
+	uint64_t srcMac,
+	uint64_t destMac,
+	unsigned int etherType,
+	unsigned int vlanId,
+	const void *data,
+	unsigned int len)
+{
+	if (*nptr)
+		reinterpret_cast<EthernetTap *>(*nptr)->put(MAC(srcMac),MAC(destMac),etherType,data,len);
+}
+
+static void ZT_GoNode_EventCallback(
+	ZT_Node *node,
+	void *uptr,
+	void *tptr,
+	enum ZT_Event et,
+	const void *data)
+{
+	ZT_GoNodeEvent ev;
+	ev.type = ZT_GONODE_EVENT_ZTEVENT;
+	ev.data.zt.type = et;
+	reinterpret_cast<ZT_GoNode *>(uptr)->eq.post(ev);
+}
+
+static void ZT_GoNode_StatePutFunction(
+	ZT_Node *node,
+	void *uptr,
+	void *tptr,
+	enum ZT_StateObjectType objType,
+	const uint64_t id[2],
+	const void *data,
+	int len)
+{
+	if (len < ZT_MAX_STATE_OBJECT_SIZE) { // sanity check
+		ZT_GoNodeEvent ev;
+		ev.type = (len >= 0) ? ZT_GONODE_EVENT_STATE_PUT : ZT_GONODE_EVENT_STATE_DELETE;
+		if (len > 0) {
+			memcpy(ev.data.sobj.data,data,len);
+			ev.data.sobj.len = (unsigned int)len;
+		}
+		ev.data.sobj.objType = objType;
+		ev.data.sobj.id[0] = id[0];
+		ev.data.sobj.id[1] = id[1];
+		reinterpret_cast<ZT_GoNode *>(uptr)->eq.post(ev);
+	}
+}
+
+static int ZT_GoNode_StateGetFunction(
+	ZT_Node *node,
+	void *uptr,
+	void *tptr,
+	enum ZT_StateObjectType objType,
+	const uint64_t id[2],
+	void *buf,
+	unsigned int buflen)
+{
+	return reinterpret_cast<ZT_GoNode *>(uptr)->goStateObjectGetFunc(reinterpret_cast<ZT_GoNode *>(uptr),reinterpret_cast<ZT_GoNode *>(uptr)->node,(int)objType,id,buf,buflen);
+}
+
+static ZT_ALWAYS_INLINE void doUdpSend(ZT_SOCKET sock,const struct sockaddr_storage *addr,const void *data,const unsigned int len,const unsigned int ipTTL)
+{
+	switch(addr->ss_family) {
+		case AF_INET:
+			if ((ipTTL > 0)&&(ipTTL < 255)) {
+#ifdef __WINDOWS__
+				DWORD tmp = (DWORD)ipTTL;
+#else
+				int tmp = (int)ipTTL;
+#endif
+				setsockopt(sock,IPPROTO_IP,IP_TTL,&tmp,sizeof(tmp));
+				sendto(sock,data,len,MSG_DONTWAIT,(const sockaddr *)addr,sizeof(struct sockaddr_in));
+				tmp = 255;
+				setsockopt(sock,IPPROTO_IP,IP_TTL,&tmp,sizeof(tmp));
+			} else {
+				sendto(sock,data,len,MSG_DONTWAIT,(const sockaddr *)addr,sizeof(struct sockaddr_in));
+			}
+			break;
+		case AF_INET6:
+			// The ipTTL option isn't currently used with IPv6. It's only used
+			// with IPv4 "firewall opener" / "NAT buster" preamble packets as part
+			// of IPv4 NAT traversal.
+			sendto(sock,data,len,MSG_DONTWAIT,(const sockaddr *)addr,sizeof(struct sockaddr_in6));
+			break;
+	}
+}
+
+static void ZT_GoNode_WirePacketSendFunction(
+	ZT_Node *node,
+	void *uptr,
+	void *tptr,
+	int64_t localSocket,
+	const struct sockaddr_storage *addr,
+	const void *data,
+	unsigned int len,
+	unsigned int ipTTL)
+{
+	if ((localSocket != -1)&&(localSocket != ZT_INVALID_SOCKET)) {
+		doUdpSend((ZT_SOCKET)localSocket,addr,data,len,ipTTL);
+	} else {
+		ZT_GoNode *const gn = reinterpret_cast<ZT_GoNode *>(uptr);
+		std::set<std::string> ipsSentFrom;
+		std::lock_guard<std::mutex> l(gn->threads_l);
+		for(auto t=gn->threads.begin();t!=gn->threads.end();++t) {
+			if (t->second.af == addr->ss_family) {
+				if (ipsSentFrom.insert(t->second.ip).second) {
+					doUdpSend(t->first,addr,data,len,ipTTL);
+				}
+			}
+		}
+	}
+}
+
+static int ZT_GoNode_PathCheckFunction(
+	ZT_Node *node,
+	void *uptr,
+	void *tptr,
+	uint64_t ztAddress,
+	int64_t localSocket,
+	const struct sockaddr_storage *sa)
+{
+	return reinterpret_cast<ZT_GoNode *>(uptr)->goPathCheckFunc(reinterpret_cast<ZT_GoNode *>(uptr),reinterpret_cast<ZT_GoNode *>(uptr)->node,ztAddress,sa);
+}
+
+static int ZT_GoNode_PathLookupFunction(
+	ZT_Node *node,
+	void *uptr,
+	void *tptr,
+	uint64_t ztAddress,
+	int desiredAddressFamily,
+	struct sockaddr_storage *sa)
+{
+	return reinterpret_cast<ZT_GoNode *>(uptr)->goPathLookupFunc(reinterpret_cast<ZT_GoNode *>(uptr),reinterpret_cast<ZT_GoNode *>(uptr)->node,desiredAddressFamily,sa);
+}
+
+static void ZT_GoNode_DNSResolver(
+	ZT_Node *node,
+	void *uptr,
+	void *tptr,
+	const enum ZT_DNSRecordType *types,
+	unsigned int numTypes,
+	const char *name,
+	uintptr_t requestId)
+{
+	ZT_GoNodeEvent ev;
+	ev.type = ZT_GONODE_EVENT_DNS_GET_TXT;
+	Utils::scopy(ev.data.dns.dnsName,sizeof(ev.data.dns.dnsName),name);
+	reinterpret_cast<ZT_GoNode *>(uptr)->eq.post(ev);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+extern "C" ZT_GoNode *ZT_GoNode_new(
+	int (*goPathCheckFunc)(ZT_GoNode *,ZT_Node *,uint64_t ztAddress,const void *),
+	int (*goPathLookupFunc)(ZT_GoNode *,ZT_Node *,int desiredAddressFamily,void *),
+	int (*goStateObjectGetFunc)(ZT_GoNode *,ZT_Node *,int objType,const uint64_t id[2],void *buf,unsigned int bufSize)
+)
+{
+	try {
+		struct ZT_Node_Callbacks cb;
+		cb.virtualNetworkConfigFunction = &ZT_GoNode_VirtualNetworkConfigFunction;
+		cb.virtualNetworkFrameFunction = &ZT_GoNode_VirtualNetworkFrameFunction;
+		cb.eventCallback = &ZT_GoNode_EventCallback;
+		cb.statePutFunction = &ZT_GoNode_StatePutFunction;
+		cb.stateGetFunction = &ZT_GoNode_StateGetFunction;
+		cb.pathCheckFunction = &ZT_GoNode_PathCheckFunction;
+		cb.pathLookupFunction = &ZT_GoNode_PathLookupFunction;
+		cb.dnsResolver = &ZT_GoNode_DNSResolver;
+
+		ZT_GoNode_Impl *gn = new ZT_GoNode_Impl;
+		const int64_t now = OSUtils::now();
+		gn->node = new Node(reinterpret_cast<void *>(gn),nullptr,&cb,now);
+		gn->nextBackgroundTaskDeadline = now;
+		gn->goPathCheckFunc = goPathCheckFunc;
+		gn->goPathLookupFunc = goPathLookupFunc;
+		gn->goStateObjectGetFunc = goStateObjectGetFunc;
+		return gn;
+	} catch ( ... ) {
+		fprintf(stderr,"FATAL: unable to create new instance of Node (out of memory?)" ZT_EOL_S);
+		exit(1);
+	}
+}
+
+extern "C" void ZT_GoNode_delete(ZT_GoNode *gn)
+{
+	ZT_GoNodeEvent sd;
+	sd.type = ZT_GONODE_EVENT_SHUTDOWN;
+	gn->eq.post(sd);
+
+	std::vector<std::thread> th;
+	gn->threads_l.lock();
+	for(auto t=gn->threads.begin();t!=gn->threads.end();++t) {
+		t->second.run = false;
+		shutdown(t->first,SHUT_RDWR);
+		close(t->first);
+		th.emplace_back(t->second.thr);
+	}
+	gn->threads_l.unlock();
+	for(auto t=th.begin();t!=th.end();++t)
+		t->join();
+
+	delete gn->node;
+	delete gn;
+}
+
+extern "C" ZT_Node *ZT_GoNode_getNode(ZT_GoNode *gn)
+{
+	return gn->node;
+}
+
+// Sets flags and socket options common to both IPv4 and IPv6 UDP sockets
+static void setCommonUdpSocketSettings(ZT_SOCKET udpSock,const char *dev)
+{
+	int bufSize = 1048576;
+	while (bufSize > 131072) {
+		if (setsockopt(udpSock,SOL_SOCKET,SO_RCVBUF,(const char *)&bufSize,sizeof(bufSize)) == 0)
+			break;
+		bufSize -= 131072;
+	}
+	bufSize = 1048576;
+	while (bufSize > 131072) {
+		if (setsockopt(udpSock,SOL_SOCKET,SO_SNDBUF,(const char *)&bufSize,sizeof(bufSize)) == 0)
+			break;
+		bufSize -= 131072;
+	}
+
+	SETSOCKOPT_FLAG_TYPE fl;
+
+#ifdef SO_REUSEPORT
+	fl = SETSOCKOPT_FLAG_TRUE;
+	setsockopt(udpSock,SOL_SOCKET,SO_REUSEPORT,(void *)&fl,sizeof(fl));
+#endif
+#ifndef __LINUX__ // linux wants just SO_REUSEPORT
+	fl = SETSOCKOPT_FLAG_TRUE;
+	setsockopt(udpSock,SOL_SOCKET,SO_REUSEADDR,(void *)&fl,sizeof(fl));
+#endif
+
+	fl = SETSOCKOPT_FLAG_TRUE;
+	setsockopt(udpSock,SOL_SOCKET,SO_BROADCAST,(void *)&fl,sizeof(fl));
+
+#ifdef IP_DONTFRAG
+	fl = SETSOCKOPT_FLAG_FALSE;
+	setsockopt(udpSock,IPPROTO_IP,IP_DONTFRAG,(void *)&fl,sizeof(fl));
+#endif
+#ifdef IP_MTU_DISCOVER
+	fl = SETSOCKOPT_FLAG_FALSE;
+	setsockopt(udpSock,IPPROTO_IP,IP_MTU_DISCOVER,(void *)&fl,sizeof(fl));
+#endif
+
+#ifdef SO_BINDTODEVICE
+	if ((dev)&&(strlen(dev)))
+		setsockopt(udpSock,SOL_SOCKET,SO_BINDTODEVICE,dev,strlen(dev));
+#endif
+#if defined(__BSD__) && defined(IP_BOUND_IF)
+	if ((dev)&&(strlen(dev))) {
+		int idx = if_nametoindex(dev);
+		if (idx != 0)
+			setsockopt(udpSock,IPPROTO_IP,IP_BOUND_IF,(void *)&idx,sizeof(idx));
+	}
+#endif
+}
+
+extern "C" int ZT_GoNode_phyStartListen(ZT_GoNode *gn,const char *dev,const char *ip,const int port)
+{
+	if (strchr(ip,':')) {
+		struct sockaddr_in6 in6;
+		memset(&in6,0,sizeof(in6));
+		in6.sin6_family = AF_INET6;
+		if (inet_pton(AF_INET6,ip,&(in6.sin6_addr)) <= 0)
+			return errno;
+		in6.sin6_port = htons((uint16_t)port);
+
+		ZT_SOCKET udpSock = socket(AF_INET6,SOCK_DGRAM,0);
+		if (udpSock == ZT_INVALID_SOCKET)
+			return errno;
+		setCommonUdpSocketSettings(udpSock,dev);
+		SETSOCKOPT_FLAG_TYPE fl = SETSOCKOPT_FLAG_TRUE;
+		setsockopt(udpSock,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&fl,sizeof(fl));
+#ifdef IPV6_DONTFRAG
+		fl = SETSOCKOPT_FLAG_FALSE;
+		setsockopt(udpSock,IPPROTO_IPV6,IPV6_DONTFRAG,&fl,sizeof(fl));
+#endif
+
+		if (bind(udpSock,reinterpret_cast<const struct sockaddr *>(&in6),sizeof(in6)) != 0)
+			return errno;
+
+		{
+			std::lock_guard<std::mutex> l(gn->threads_l);
+			ZT_GoNodeThread &gnt = gn->threads[udpSock];
+			gnt.ip = ip;
+			gnt.port = port;
+			gnt.af = AF_INET6;
+			gnt.run = true;
+			gnt.thr = std::thread([udpSock,gn,&gnt] {
+				struct sockaddr_in6 in6;
+				socklen_t salen;
+				char buf[16384];
+				while (gnt.run) {
+					salen = sizeof(in6);
+					int s = (int)recvfrom(udpSock,buf,sizeof(buf),0,reinterpret_cast<struct sockaddr *>(&in6),&salen);
+					if (s > 0) {
+						gn->node->processWirePacket(&gnt,OSUtils::now(),(int64_t)udpSock,reinterpret_cast<const struct sockaddr_storage *>(&in6),buf,(unsigned int)s,&(gn->nextBackgroundTaskDeadline));
+					}
+				}
+			});
+		}
+	} else {
+		struct sockaddr_in in;
+		memset(&in,0,sizeof(in));
+		in.sin_family = AF_INET;
+		if (inet_pton(AF_INET,ip,&(in.sin_addr)) <= 0)
+			return errno;
+		in.sin_port = htons((uint16_t)port);
+
+		ZT_SOCKET udpSock = socket(AF_INET,SOCK_DGRAM,0);
+		if (udpSock == ZT_INVALID_SOCKET)
+			return errno;
+		setCommonUdpSocketSettings(udpSock,dev);
+#ifdef SO_NO_CHECK
+		SETSOCKOPT_FLAG_TYPE fl = SETSOCKOPT_FLAG_TRUE;
+		setsockopt(udpSock,SOL_SOCKET,SO_NO_CHECK,&fl,sizeof(fl));
+#endif
+
+		if (bind(udpSock,reinterpret_cast<const struct sockaddr *>(&in),sizeof(in)) != 0)
+			return errno;
+
+		{
+			std::lock_guard<std::mutex> l(gn->threads_l);
+			ZT_GoNodeThread &gnt = gn->threads[udpSock];
+			gnt.ip = ip;
+			gnt.port = port;
+			gnt.af = AF_INET6;
+			gnt.run = true;
+			gnt.thr = std::thread([udpSock,gn,&gnt] {
+				struct sockaddr_in in4;
+				socklen_t salen;
+				char buf[16384];
+				while (gnt.run) {
+					salen = sizeof(in4);
+					int s = (int)recvfrom(udpSock,buf,sizeof(buf),0,reinterpret_cast<struct sockaddr *>(&in4),&salen);
+					if (s > 0) {
+						gn->node->processWirePacket(&gnt,OSUtils::now(),(int64_t)udpSock,reinterpret_cast<const struct sockaddr_storage *>(&in4),buf,(unsigned int)s,&(gn->nextBackgroundTaskDeadline));
+					}
+				}
+			});
+		}
+	}
+
+	return 0;
+}
+
+extern "C" int ZT_GoNode_phyStopListen(ZT_GoNode *gn,const char *dev,const char *ip,const int port)
+{
+	{
+		std::lock_guard<std::mutex> l(gn->threads_l);
+		for(auto t=gn->threads.begin();t!=gn->threads.end();) {
+			if ((t->second.ip == ip)&&(t->second.port == port)) {
+				t->second.run = false;
+				shutdown(t->first,SHUT_RDWR);
+				close(t->first);
+				t->second.thr.join();
+				gn->threads.erase(t++);
+			} else ++t;
+		}
+	}
+}
+
+extern "C" int ZT_GoNode_waitForEvent(ZT_GoNode *gn,ZT_GoNodeEvent *ev)
+{
+	gn->eq.get(*ev);
+}

+ 100 - 0
go/native/GoNode.h

@@ -0,0 +1,100 @@
+/*
+ * Copyright (c)2019 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: 2023-01-01
+ *
+ * On the date above, in accordance with the Business Source License, use
+ * of this software will be governed by vergnn 2.0 of the Apache License.
+ */
+/****/
+
+#ifndef ZT_GONODE_H
+#define ZT_GONODE_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../include/ZeroTierCore.h"
+
+struct ZT_GoNode_Impl;
+typedef struct ZT_GoNode_Impl ZT_GoNode;
+
+#define ZT_GONODE_EVENT_SHUTDOWN 0
+#define ZT_GONODE_EVENT_ZTEVENT 1
+#define ZT_GONODE_EVENT_DNS_GET_TXT 2
+#define ZT_GONODE_EVENT_STATE_PUT 3
+#define ZT_GONODE_EVENT_STATE_DELETE 4
+#define ZT_GONODE_EVENT_NETWORK_CONFIG_UPDATE 5
+
+/**
+ * Variant type for async core generated events pulled via waitForEvent
+ */
+struct ZT_GoNodeEvent_Impl
+{
+#ifdef __cplusplus
+	inline ZT_GoNodeEvent_Impl() { memset(reinterpret_cast<void *>(this),0,sizeof(ZT_GoNodeEvent_Impl)); }
+	inline ZT_GoNodeEvent_Impl(const ZT_GoNodeEvent &ev) { memcpy(reinterpret_cast<void *>(this),reinterpret_cast<const void *>(&ev),sizeof(ZT_GoNodeEvent_Impl)); }
+	inline ZT_GoNodeEvent_Impl &operator=(const ZT_GoNodeEvent_Impl &ev) { memcpy(reinterpret_cast<void *>(this),reinterpret_cast<const void *>(&ev),sizeof(ZT_GoNodeEvent_Impl)); return *this; }
+#endif
+
+	int type;
+
+	union {
+		/* ZeroTier event of ZT_Event type */
+		struct {
+			int type;
+		} zt;
+
+		/* DNS resolution request */
+		struct {
+			uintptr_t requestId;
+			char dnsName[256];
+		} dns;
+
+		/* State object put or delete request */
+		struct {
+			uint8_t data[ZT_MAX_STATE_OBJECT_SIZE];
+			unsigned int len;
+			int objType;
+			uint64_t id[2];
+		} sobj;
+
+		/* Network configuration update event */
+		struct {
+			int op; /* ZT_VirtualNetworkConfigOperation */
+			ZT_VirtualNetworkConfig conf;
+		} nconf;
+	} data;
+};
+
+typedef struct ZT_GoNodeEvent_Impl ZT_GoNodeEvent;
+
+#ifndef __cplusplus
+extern "C" {
+#endif
+
+ZT_GoNode *ZT_GoNode_new(
+	int (*goPathCheckFunc)(ZT_GoNode *,ZT_Node *,uint64_t ztAddress,const void *),
+	int (*goPathLookupFunc)(ZT_GoNode *,ZT_Node *,int desiredAddressFamily,void *),
+	int (*goStateObjectGetFunc)(ZT_GoNode *,ZT_Node *,int objType,const uint64_t id[2],void *buf,unsigned int bufSize)
+);
+
+void ZT_GoNode_delete(ZT_GoNode *gn);
+
+ZT_Node *ZT_GoNode_getNode(ZT_GoNode *gn);
+
+int ZT_GoNode_phyStartListen(ZT_GoNode *gn,const char *dev,const char *ip,const int port);
+
+int ZT_GoNode_phyStopListen(ZT_GoNode *gn,const char *dev,const char *ip,const int port);
+
+int ZT_GoNode_waitForEvent(ZT_GoNode *gn,ZT_GoNodeEvent *ev);
+
+#ifndef __cplusplus
+}
+#endif
+
+#endif

+ 122 - 0
go/pkg/ztnode/errors.go

@@ -0,0 +1,122 @@
+/*
+ * Copyright (c)2019 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: 2023-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.
+ */
+/****/
+
+package ztnode
+
+/*
+
+// errTypeName returns the type name of an error minus any leading * character.
+func errTypeName(err error) string {
+	if err == nil {
+		return ""
+	}
+	et := reflect.TypeOf(err)
+	if et.Kind() == reflect.Ptr {
+		return et.Elem().Name()
+	}
+	return et.Name()
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Err indicates a general LF error such as an invalid parameter or state.
+type Err string
+
+func (e Err) Error() string { return (string)(e) }
+
+// General errors
+const (
+	ErrInvalidPublicKey       Err = "invalid public key"
+	ErrInvalidPrivateKey      Err = "invalid private key"
+	ErrInvalidParameter       Err = "invalid parameter"
+	ErrInvalidObject          Err = "invalid object"
+	ErrUnsupportedType        Err = "unsupported type"
+	ErrUnsupportedCurve       Err = "unsupported ECC curve (for this purpose)"
+	ErrOutOfRange             Err = "parameter out of range"
+	ErrWharrgarblFailed       Err = "Wharrgarbl proof of work algorithm failed (out of memory?)"
+	ErrIO                     Err = "I/O error"
+	ErrIncorrectKey           Err = "incorrect key"
+	ErrAlreadyConnected       Err = "already connected"
+	ErrRecordNotFound         Err = "record not found"
+	ErrRecordIsNewer          Err = "record is newer than timestamp"
+	ErrPulseSpanExeceeded     Err = "pulse is more than one year after record"
+	ErrDuplicateRecord        Err = "duplicate record"
+	ErrPrivateKeyRequired     Err = "private key required"
+	ErrInvalidMessageSize     Err = "message size invalid"
+	ErrQueryRequiresSelectors Err = "query requires at least one selector"
+	ErrQueryInvalidSortOrder  Err = "invalid sort order value"
+	ErrAlreadyMounted         Err = "mount point already mounted"
+)
+
+//////////////////////////////////////////////////////////////////////////////
+
+// ErrRecord indicates an error related to an invalid record or a record failing a check.
+type ErrRecord string
+
+func (e ErrRecord) Error() string { return (string)(e) }
+
+// Errs indicating that a record is invalid
+const (
+	ErrRecordInvalid                   ErrRecord = "record invalid"
+	ErrRecordOwnerSignatureCheckFailed ErrRecord = "owner signature check failed"
+	ErrRecordInsufficientWork          ErrRecord = "insufficient work to pay for this record"
+	ErrRecordNotApproved               ErrRecord = "record not currently approved (via proof of work and/or certificates)"
+	ErrRecordInsufficientLinks         ErrRecord = "insufficient links"
+	ErrRecordTooManyLinks              ErrRecord = "too many links"
+	ErrRecordInvalidLinks              ErrRecord = "links must be sorted and unique"
+	ErrRecordTooManySelectors          ErrRecord = "too many selectors"
+	ErrRecordUnsupportedAlgorithm      ErrRecord = "unsupported algorithm or type"
+	ErrRecordTooLarge                  ErrRecord = "record too large"
+	ErrRecordValueTooLarge             ErrRecord = "record value too large"
+	ErrRecordViolatesSpecialRelativity ErrRecord = "record timestamp too far in the future"
+	ErrRecordTooOld                    ErrRecord = "record older than network timestamp floor"
+	ErrRecordCertificateInvalid        ErrRecord = "certificate invalid"
+	ErrRecordCertificateRequired       ErrRecord = "certificate required"
+	ErrRecordProhibited                ErrRecord = "record administratively prohibited"
+)
+
+//////////////////////////////////////////////////////////////////////////////
+
+// ErrDatabase contains information about a database related problem.
+type ErrDatabase struct {
+	// ErrCode is the error code returned by the C database module.
+	ErrCode int
+
+	// ErrMessage is an error message supplied by the C code or by Go (optional)
+	ErrMessage string
+}
+
+func (e ErrDatabase) Error() string {
+	return fmt.Sprintf("database error: %d (%s)", e.ErrCode, e.ErrMessage)
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// ErrAPI (response) indicates an error and is returned with non-200 responses.
+type ErrAPI struct {
+	Code        int    ``                  // HTTP response code
+	Message     string `json:",omitempty"` // Message indicating the reason for the error
+	ErrTypeName string `json:",omitempty"` // Name of LF native error or empty if HTTP or transport error
+}
+
+// Error implements the error interface, making APIError an 'error' in the Go sense.
+func (e ErrAPI) Error() string {
+	if len(e.ErrTypeName) > 0 {
+		return fmt.Sprintf("%d:%s:%s", e.Code, e.ErrTypeName, e.Message)
+	}
+	return fmt.Sprintf("%d:%s", e.Code, e.Message)
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+*/

+ 19 - 0
go/pkg/ztnode/misc.go

@@ -0,0 +1,19 @@
+/*
+ * Copyright (c)2019 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: 2023-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.
+ */
+/****/
+
+package ztnode
+
+import "time"
+
+// TimeMs returns the time in milliseconds since epoch.
+func TimeMs() int64 { return int64(time.Now().UnixNano()) / int64(1000000) }

+ 48 - 0
go/pkg/ztnode/node.go

@@ -0,0 +1,48 @@
+/*
+ * Copyright (c)2019 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: 2023-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.
+ */
+/****/
+
+package ztnode
+
+//#cgo CFLAGS: -O3
+//#cgo LDFLAGS: ${SRCDIR}/../../../build/node/libzt_core.a -lc++
+//#define ZT_CGO 1
+//#include <stdint.h>
+//#include "../../../include/ZeroTierCore.h"
+//#if __has_include("../../../version.h")
+//#include "../../../version.h"
+//#else
+//#define ZEROTIER_ONE_VERSION_MAJOR 255
+//#define ZEROTIER_ONE_VERSION_MINOR 255
+//#define ZEROTIER_ONE_VERSION_REVISION 255
+//#define ZEROTIER_ONE_VERSION_BUILD 255
+//#endif
+import "C"
+
+const (
+	// CoreVersionMajor is the major version of the ZeroTier core
+	CoreVersionMajor int = C.ZEROTIER_ONE_VERSION_MAJOR
+
+	// CoreVersionMinor is the minor version of the ZeroTier core
+	CoreVersionMinor int = C.ZEROTIER_ONE_VERSION_MINOR
+
+	// CoreVersionRevision is the revision of the ZeroTier core
+	CoreVersionRevision int = C.ZEROTIER_ONE_VERSION_REVISION
+
+	// CoreVersionBuild is the build version of the ZeroTier core
+	CoreVersionBuild int = C.ZEROTIER_ONE_VERSION_BUILD
+)
+
+// Node is an instance of a ZeroTier node
+type Node struct {
+	node *C.ZT_Node
+}

+ 6 - 1
include/ZeroTierCore.h

@@ -166,6 +166,11 @@ extern "C" {
  */
 #define ZT_MAX_MULTICAST_SUBSCRIPTIONS 1024
 
+/**
+ * Maximum size for a state object (via state object put/get callbacks/API)
+ */
+#define ZT_MAX_STATE_OBJECT_SIZE 4096
+
 /**
  * Maximum value for link quality (min is 0)
  */
@@ -1319,7 +1324,7 @@ typedef struct
 	/**
 	 * Whether this peer was ever reachable via an aggregate link
 	 */
-	bool hadAggregateLink;
+	int hadAggregateLink;
 
 	/**
 	 * Known network paths to peer

+ 16 - 15
osdep/BlockingQueue.hpp

@@ -18,8 +18,9 @@
 #include <mutex>
 #include <condition_variable>
 #include <chrono>
+#include <atomic>
 
-#include "Thread.hpp"
+#include "../node/Constants.hpp"
 
 namespace ZeroTier {
 
@@ -32,16 +33,23 @@ template <class T>
 class BlockingQueue
 {
 public:
-	BlockingQueue(void) : r(true) {}
+	enum TimedWaitResult
+	{
+		OK,
+		TIMED_OUT,
+		STOP
+	};
+
+	ZT_ALWAYS_INLINE BlockingQueue(void) : r(true) {}
 
-	inline void post(T t)
+	ZT_ALWAYS_INLINE void post(T t)
 	{
 		std::lock_guard<std::mutex> lock(m);
 		q.push(t);
 		c.notify_one();
 	}
 
-	inline void postLimit(T t,const unsigned long limit)
+	ZT_ALWAYS_INLINE void postLimit(T t,const unsigned long limit)
 	{
 		std::unique_lock<std::mutex> lock(m);
 		for(;;) {
@@ -56,7 +64,7 @@ public:
 		}
 	}
 
-	inline void stop(void)
+	ZT_ALWAYS_INLINE void stop(void)
 	{
 		std::lock_guard<std::mutex> lock(m);
 		r = false;
@@ -64,7 +72,7 @@ public:
 		gc.notify_all();
 	}
 
-	inline bool get(T &value)
+	ZT_ALWAYS_INLINE bool get(T &value)
 	{
 		std::unique_lock<std::mutex> lock(m);
 		if (!r) return false;
@@ -81,14 +89,7 @@ public:
 		return true;
 	}
 
-	enum TimedWaitResult
-	{
-		OK,
-		TIMED_OUT,
-		STOP
-	};
-
-	inline TimedWaitResult get(T &value,const unsigned long ms)
+	ZT_ALWAYS_INLINE TimedWaitResult get(T &value,const unsigned long ms)
 	{
 		const std::chrono::milliseconds ms2{ms};
 		std::unique_lock<std::mutex> lock(m);
@@ -105,7 +106,7 @@ public:
 	}
 
 private:
-	volatile bool r;
+	std::atomic_bool r;
 	std::queue<T> q;
 	mutable std::mutex m;
 	mutable std::condition_variable c,gc;