From ae2120eb961841eaef28b6db673e05379c214fa0 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 20 Sep 2019 09:09:05 -0700 Subject: [PATCH] New service, work in progress --- CMakeLists.txt | 4 +- attic/listaddrinfo.go | 30 ++ {service => attic/service}/CMakeLists.txt | 0 {service => attic/service}/OneService.cpp | 0 {service => attic/service}/OneService.hpp | 0 {service => attic/service}/README.md | 0 go/go.mod | 3 + go/native/CMakeLists.txt | 13 + go/native/GoNode.cpp | 493 ++++++++++++++++++++++ go/native/GoNode.h | 100 +++++ go/pkg/ztnode/errors.go | 122 ++++++ go/pkg/ztnode/misc.go | 19 + go/pkg/ztnode/node.go | 48 +++ include/ZeroTierCore.h | 7 +- osdep/BlockingQueue.hpp | 31 +- 15 files changed, 852 insertions(+), 18 deletions(-) create mode 100644 attic/listaddrinfo.go rename {service => attic/service}/CMakeLists.txt (100%) rename {service => attic/service}/OneService.cpp (100%) rename {service => attic/service}/OneService.hpp (100%) rename {service => attic/service}/README.md (100%) create mode 100644 go/go.mod create mode 100644 go/native/CMakeLists.txt create mode 100644 go/native/GoNode.cpp create mode 100644 go/native/GoNode.h create mode 100644 go/pkg/ztnode/errors.go create mode 100644 go/pkg/ztnode/misc.go create mode 100644 go/pkg/ztnode/node.go diff --git a/CMakeLists.txt b/CMakeLists.txt index 307a4845..0e3792c8 100644 --- a/CMakeLists.txt +++ b/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 diff --git a/attic/listaddrinfo.go b/attic/listaddrinfo.go new file mode 100644 index 00000000..3db54bf0 --- /dev/null +++ b/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") + } +} diff --git a/service/CMakeLists.txt b/attic/service/CMakeLists.txt similarity index 100% rename from service/CMakeLists.txt rename to attic/service/CMakeLists.txt diff --git a/service/OneService.cpp b/attic/service/OneService.cpp similarity index 100% rename from service/OneService.cpp rename to attic/service/OneService.cpp diff --git a/service/OneService.hpp b/attic/service/OneService.hpp similarity index 100% rename from service/OneService.hpp rename to attic/service/OneService.hpp diff --git a/service/README.md b/attic/service/README.md similarity index 100% rename from service/README.md rename to attic/service/README.md diff --git a/go/go.mod b/go/go.mod new file mode 100644 index 00000000..55de671a --- /dev/null +++ b/go/go.mod @@ -0,0 +1,3 @@ +module zerotier-go + +go 1.13 diff --git a/go/native/CMakeLists.txt b/go/native/CMakeLists.txt new file mode 100644 index 00000000..4db0d57d --- /dev/null +++ b/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) diff --git a/go/native/GoNode.cpp b/go/native/GoNode.cpp new file mode 100644 index 00000000..c7872a39 --- /dev/null +++ b/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 +#include +#include + +#ifndef __WINDOWS__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __BSD__ +#include +#endif +#ifdef __LINUX__ +#ifndef IPV6_DONTFRAG +#define IPV6_DONTFRAG 62 +#endif +#endif +#endif // !__WINDOWS__ + +#include +#include +#include +#include +#include +#include + +#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 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(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(*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(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(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(uptr)->goStateObjectGetFunc(reinterpret_cast(uptr),reinterpret_cast(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(uptr); + std::set ipsSentFrom; + std::lock_guard 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(uptr)->goPathCheckFunc(reinterpret_cast(uptr),reinterpret_cast(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(uptr)->goPathLookupFunc(reinterpret_cast(uptr),reinterpret_cast(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(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(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 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(&in6),sizeof(in6)) != 0) + return errno; + + { + std::lock_guard 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(&in6),&salen); + if (s > 0) { + gn->node->processWirePacket(&gnt,OSUtils::now(),(int64_t)udpSock,reinterpret_cast(&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(&in),sizeof(in)) != 0) + return errno; + + { + std::lock_guard 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(&in4),&salen); + if (s > 0) { + gn->node->processWirePacket(&gnt,OSUtils::now(),(int64_t)udpSock,reinterpret_cast(&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 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); +} diff --git a/go/native/GoNode.h b/go/native/GoNode.h new file mode 100644 index 00000000..285ea222 --- /dev/null +++ b/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 +#include +#include + +#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(this),0,sizeof(ZT_GoNodeEvent_Impl)); } + inline ZT_GoNodeEvent_Impl(const ZT_GoNodeEvent &ev) { memcpy(reinterpret_cast(this),reinterpret_cast(&ev),sizeof(ZT_GoNodeEvent_Impl)); } + inline ZT_GoNodeEvent_Impl &operator=(const ZT_GoNodeEvent_Impl &ev) { memcpy(reinterpret_cast(this),reinterpret_cast(&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 diff --git a/go/pkg/ztnode/errors.go b/go/pkg/ztnode/errors.go new file mode 100644 index 00000000..37956e7c --- /dev/null +++ b/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) +} + +////////////////////////////////////////////////////////////////////////////// + +*/ diff --git a/go/pkg/ztnode/misc.go b/go/pkg/ztnode/misc.go new file mode 100644 index 00000000..ade75281 --- /dev/null +++ b/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) } diff --git a/go/pkg/ztnode/node.go b/go/pkg/ztnode/node.go new file mode 100644 index 00000000..763fdea4 --- /dev/null +++ b/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 +//#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 +} diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index ab7ce51f..37520194 100644 --- a/include/ZeroTierCore.h +++ b/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 diff --git a/osdep/BlockingQueue.hpp b/osdep/BlockingQueue.hpp index 0a317288..f5a6ba49 100644 --- a/osdep/BlockingQueue.hpp +++ b/osdep/BlockingQueue.hpp @@ -18,8 +18,9 @@ #include #include #include +#include -#include "Thread.hpp" +#include "../node/Constants.hpp" namespace ZeroTier { @@ -32,16 +33,23 @@ template class BlockingQueue { public: - BlockingQueue(void) : r(true) {} + enum TimedWaitResult + { + OK, + TIMED_OUT, + STOP + }; - inline void post(T t) + ZT_ALWAYS_INLINE BlockingQueue(void) : r(true) {} + + ZT_ALWAYS_INLINE void post(T t) { std::lock_guard 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 lock(m); for(;;) { @@ -56,7 +64,7 @@ public: } } - inline void stop(void) + ZT_ALWAYS_INLINE void stop(void) { std::lock_guard 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 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 lock(m); @@ -105,7 +106,7 @@ public: } private: - volatile bool r; + std::atomic_bool r; std::queue q; mutable std::mutex m; mutable std::condition_variable c,gc;