diff --git a/go/go.mod b/go/go.mod index 55de671a6..bad0f7cf4 100644 --- a/go/go.mod +++ b/go/go.mod @@ -1,3 +1,3 @@ -module zerotier-go +module zerotier go 1.13 diff --git a/go/go.sum b/go/go.sum new file mode 100644 index 000000000..e69de29bb diff --git a/go/native/GoGlue.cpp b/go/native/GoGlue.cpp index 6647284bc..f3cc94dd4 100644 --- a/go/native/GoGlue.cpp +++ b/go/native/GoGlue.cpp @@ -20,7 +20,6 @@ #include "../../node/MAC.hpp" #include "../../node/Address.hpp" #include "../../osdep/OSUtils.hpp" -#include "../../osdep/BlockingQueue.hpp" #include "../../osdep/EthernetTap.hpp" #include @@ -89,10 +88,6 @@ 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::string path; std::atomic_bool run; @@ -102,12 +97,19 @@ struct ZT_GoNode_Impl std::map< uint64_t,std::shared_ptr > taps; std::mutex taps_l; - BlockingQueue eq; - std::thread backgroundTaskThread; }; -////////////////////////////////////////////////////////////////////////////// +/****************************************************************************/ + +/* These functions are implemented in Go in pkg/ztnode/node-callbacks.go */ +extern "C" int goPathCheckFunc(ZT_GoNode *,uint64_t,int,const void *,int); +extern "C" int goPathLookupFunc(ZT_GoNode *,uint64_t,int,int *,uint8_t [16],int *); +extern "C" void goStateObjectPutFunc(ZT_GoNode *,int,const uint64_t [2],const void *,int); +extern "C" int goStateObjectGetFunc(ZT_GoNode *,int,const uint64_t [2],void *,unsigned int); +extern "C" void goDNSResolverFunc(ZT_GoNode *,const uint8_t *,int,const char *,uintptr_t); +extern "C" int goVirtualNetworkConfigFunc(ZT_GoNode *,ZT_GoTap *,uint64_t,int,const ZT_VirtualNetworkConfig *); +extern "C" void goZtEvent(ZT_GoNode *,int,const void *); static int ZT_GoNode_VirtualNetworkConfigFunction( ZT_Node *node, @@ -118,13 +120,7 @@ static int ZT_GoNode_VirtualNetworkConfigFunction( 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); - return 0; + return goVirtualNetworkConfigFunc(reinterpret_cast(uptr),reinterpret_cast(*nptr),nwid,op,cfg); } static void ZT_GoNode_VirtualNetworkFrameFunction( @@ -151,10 +147,7 @@ static void ZT_GoNode_EventCallback( 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); + goZtEvent(reinterpret_cast(uptr),et,data); } static void ZT_GoNode_StatePutFunction( @@ -166,18 +159,7 @@ static void ZT_GoNode_StatePutFunction( 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); - } + goStateObjectPutFunc(reinterpret_cast(uptr),objType,id,data,len); } static int ZT_GoNode_StateGetFunction( @@ -189,7 +171,12 @@ static int ZT_GoNode_StateGetFunction( void *buf, unsigned int buflen) { - return reinterpret_cast(uptr)->goStateObjectGetFunc(reinterpret_cast(uptr),reinterpret_cast(uptr)->node,(int)objType,id,buf,buflen); + return goStateObjectGetFunc( + reinterpret_cast(uptr), + (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) @@ -254,7 +241,23 @@ static int ZT_GoNode_PathCheckFunction( int64_t localSocket, const struct sockaddr_storage *sa) { - return reinterpret_cast(uptr)->goPathCheckFunc(reinterpret_cast(uptr),reinterpret_cast(uptr)->node,ztAddress,sa); + switch(sa->ss_family) { + case AF_INET: + return goPathCheckFunc( + reinterpret_cast(uptr), + ztAddress, + AF_INET, + &(reinterpret_cast(sa)->sin_addr.s_addr), + Utils::ntoh((uint16_t)reinterpret_cast(sa)->sin_port)); + case AF_INET6: + return goPathCheckFunc( + reinterpret_cast(uptr), + ztAddress, + AF_INET6, + reinterpret_cast(sa)->sin6_addr.s6_addr, + Utils::ntoh((uint16_t)reinterpret_cast(sa)->sin6_port)); + } + return 0; } static int ZT_GoNode_PathLookupFunction( @@ -265,7 +268,32 @@ static int ZT_GoNode_PathLookupFunction( int desiredAddressFamily, struct sockaddr_storage *sa) { - return reinterpret_cast(uptr)->goPathLookupFunc(reinterpret_cast(uptr),reinterpret_cast(uptr)->node,desiredAddressFamily,sa); + int family = 0; + uint8_t ip[16]; + int port = 0; + const int result = goPathLookupFunc( + reinterpret_cast(uptr), + ztAddress, + desiredAddressFamily, + &family, + ip, + &port + ); + if (result != 0) { + switch(family) { + case AF_INET: + reinterpret_cast(sa)->sin_family = AF_INET; + memcpy(&(reinterpret_cast(sa)->sin_addr.s_addr),ip,4); + reinterpret_cast(sa)->sin_port = Utils::hton((uint16_t)port); + return 1; + case AF_INET6: + reinterpret_cast(sa)->sin6_family = AF_INET6; + memcpy(reinterpret_cast(sa)->sin6_addr.s6_addr,ip,16); + reinterpret_cast(sa)->sin6_port = Utils::hton((uint16_t)port); + return 1; + } + } + return 0; } static void ZT_GoNode_DNSResolver( @@ -277,19 +305,14 @@ static void ZT_GoNode_DNSResolver( 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); + uint8_t t[256]; + for(unsigned int i=0;(i(uptr),t,(int)numTypes,name,requestId); } -////////////////////////////////////////////////////////////////////////////// +/****************************************************************************/ -extern "C" ZT_GoNode *ZT_GoNode_new( - const char *workingPath, - 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)) +extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath) { try { struct ZT_Node_Callbacks cb; @@ -307,9 +330,6 @@ extern "C" ZT_GoNode *ZT_GoNode_new( 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; gn->path = workingPath; gn->run = true; @@ -333,10 +353,6 @@ extern "C" void ZT_GoNode_delete(ZT_GoNode *gn) { gn->run = false; - ZT_GoNodeEvent sd; - sd.type = ZT_GONODE_EVENT_SHUTDOWN; - gn->eq.post(sd); - gn->threads_l.lock(); for(auto t=gn->threads.begin();t!=gn->threads.end();++t) { t->second.run = false; @@ -358,11 +374,6 @@ extern "C" void ZT_GoNode_delete(ZT_GoNode *gn) 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) { @@ -521,11 +532,6 @@ extern "C" int ZT_GoNode_phyStopListen(ZT_GoNode *gn,const char *dev,const char return 0; } -extern "C" void ZT_GoNode_waitForEvent(ZT_GoNode *gn,ZT_GoNodeEvent *ev) -{ - gn->eq.get(*ev); -} - static void tapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { ZT_GoNode *const gn = reinterpret_cast(uptr); @@ -561,3 +567,78 @@ extern "C" void ZT_GoNode_leave(ZT_GoNode *gn,uint64_t nwid) gn->taps.erase(existingTap); } } + +/****************************************************************************/ + +extern "C" void ZT_GoTap_setEnabled(ZT_GoTap *tap,int enabled) +{ + reinterpret_cast(tap)->setEnabled(enabled != 0); +} + +extern "C" int ZT_GoTap_addIp(ZT_GoTap *tap,int af,const void *ip,int port) +{ + switch(af) { + case AF_INET: + return (reinterpret_cast(tap)->addIp(InetAddress(ip,4,(unsigned int)port)) ? 1 : 0); + case AF_INET6: + return (reinterpret_cast(tap)->addIp(InetAddress(ip,16,(unsigned int)port)) ? 1 : 0); + } + return 0; +} + +extern "C" int ZT_GoTap_removeIp(ZT_GoTap *tap,int af,const void *ip,int port) +{ + switch(af) { + case AF_INET: + return (reinterpret_cast(tap)->removeIp(InetAddress(ip,4,(unsigned int)port)) ? 1 : 0); + case AF_INET6: + return (reinterpret_cast(tap)->removeIp(InetAddress(ip,16,(unsigned int)port)) ? 1 : 0); + } + return 0; +} + +extern "C" int ZT_GoTap_ips(ZT_GoTap *tap,void *buf,unsigned int bufSize) +{ + auto ips = reinterpret_cast(tap)->ips(); + unsigned int p = 0; + uint8_t *const b = reinterpret_cast(buf); + for(auto ip=ips.begin();ip!=ips.end();++ip) { + if ((p + 7) > bufSize) + break; + const uint8_t *const ipd = reinterpret_cast(ip->rawIpData()); + const unsigned int port = ip->port(); + if (ip->isV4()) { + b[p++] = AF_INET; + b[p++] = ipd[0]; + b[p++] = ipd[1]; + b[p++] = ipd[2]; + b[p++] = ipd[3]; + b[p++] = (uint8_t)((port >> 8) & 0xff); + b[p++] = (uint8_t)(port & 0xff); + } else if (ip->isV6()) { + if ((p + 19) <= bufSize) { + b[p++] = AF_INET6; + for(int j=0;j<16;++j) + b[p++] = ipd[j]; + b[p++] = (uint8_t)((port >> 8) & 0xff); + b[p++] = (uint8_t)(port & 0xff); + } + } + } + return (int)p; +} + +extern "C" void ZT_GoTap_deviceName(ZT_GoTap *tap,char nbuf[256]) +{ + Utils::scopy(nbuf,256,reinterpret_cast(tap)->deviceName().c_str()); +} + +extern "C" void ZT_GoTap_setFriendlyName(ZT_GoTap *tap,const char *friendlyName) +{ + reinterpret_cast(tap)->setFriendlyName(friendlyName); +} + +extern "C" void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu) +{ + reinterpret_cast(tap)->setMtu(mtu); +} diff --git a/go/native/GoGlue.h b/go/native/GoGlue.h index 929ec6f25..230503d92 100644 --- a/go/native/GoGlue.h +++ b/go/native/GoGlue.h @@ -31,57 +31,6 @@ typedef void ZT_GoTap; 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_Impl &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 { - ZT_GoTap *tap; - int op; /* ZT_VirtualNetworkConfigOperation */ - ZT_VirtualNetworkConfig conf; - } nconf; - } data; -}; - -typedef struct ZT_GoNodeEvent_Impl ZT_GoNodeEvent; - /****************************************************************************/ #ifdef __cplusplus @@ -90,11 +39,10 @@ extern "C" { /****************************************************************************/ -ZT_GoNode *ZT_GoNode_new( - const char *workingPath, - 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)); + +/****************************************************************************/ + +ZT_GoNode *ZT_GoNode_new(const char *workingPath); void ZT_GoNode_delete(ZT_GoNode *gn); @@ -106,8 +54,6 @@ int ZT_GoNode_phyStartListen(ZT_GoNode *gn,const char *dev,const char *ip,const /* Close all listener threads for a given local IP and port */ int ZT_GoNode_phyStopListen(ZT_GoNode *gn,const char *dev,const char *ip,const int port); -void ZT_GoNode_waitForEvent(ZT_GoNode *gn,ZT_GoNodeEvent *ev); - ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid); void ZT_GoNode_leave(ZT_GoNode *gn,uint64_t nwid); @@ -130,7 +76,7 @@ int ZT_GoTap_removeIp(ZT_GoTap *tap,int af,const void *ip,int port); */ int ZT_GoTap_ips(ZT_GoTap *tap,void *buf,unsigned int bufSize); -const char *ZT_GoTap_deviceName(ZT_GoTap *tap); +void ZT_GoTap_deviceName(ZT_GoTap *tap,char nbuf[256]); void ZT_GoTap_setFriendlyName(ZT_GoTap *tap,const char *friendlyName); diff --git a/go/pkg/zerotier/errors.go b/go/pkg/zerotier/errors.go new file mode 100644 index 000000000..765c4ee20 --- /dev/null +++ b/go/pkg/zerotier/errors.go @@ -0,0 +1,25 @@ +/* + * 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 zerotier + +// Err is a basic string error type for ZeroTier +type Err string + +func (e Err) Error() string { return (string)(e) } + +// Simple ZeroTier Errors +const ( + ErrInvalidMACAddress Err = "invalid MAC address" + ErrInvalidParameter Err = "invalid parameter" +) diff --git a/go/pkg/zerotier/mac.go b/go/pkg/zerotier/mac.go new file mode 100644 index 000000000..ac40d240c --- /dev/null +++ b/go/pkg/zerotier/mac.go @@ -0,0 +1,63 @@ +/* + * 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 zerotier + +import ( + "encoding/json" + "fmt" + "strconv" + "strings" +) + +// MAC represents an Ethernet hardware address +type MAC uint64 + +// NewMACFromString decodes a MAC address in canonical colon-separated hex format +func NewMACFromString(s string) (MAC, error) { + ss := strings.Split(s, ":") + if len(ss) != 6 { + return MAC(0), ErrInvalidMACAddress + } + var m uint64 + for i := 0; i < 6; i++ { + m <<= 8 + c, _ := strconv.ParseUint(ss[i], 16, 64) + if c > 0xff { + return MAC(0), ErrInvalidMACAddress + } + m |= (c & 0xff) + } + return MAC(m), nil +} + +// String returns this MAC address in canonical human-readable form +func (m MAC) String() string { + return fmt.Sprintf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", (m>>40)&0xff, (m>>32)&0xff, (m>>24)&0xff, (m>>16)&0xff, (m>>8)&0xff, m&0xff) +} + +// MarshalJSON marshals this MAC as a string +func (m MAC) MarshalJSON() ([]byte, error) { + return []byte(m.String()), nil +} + +// UnmarshalJSON unmarshals this MAC from a string +func (m *MAC) UnmarshalJSON(j []byte) error { + var s string + err := json.Unmarshal(j, &s) + if err != nil { + return err + } + *m, err = NewMACFromString(s) + return err +} diff --git a/go/pkg/ztnode/misc.go b/go/pkg/zerotier/misc.go similarity index 96% rename from go/pkg/ztnode/misc.go rename to go/pkg/zerotier/misc.go index ade75281f..1883cde41 100644 --- a/go/pkg/ztnode/misc.go +++ b/go/pkg/zerotier/misc.go @@ -11,7 +11,7 @@ */ /****/ -package ztnode +package zerotier import "time" diff --git a/go/pkg/zerotier/multicastgroup.go b/go/pkg/zerotier/multicastgroup.go new file mode 100644 index 000000000..f3ffde289 --- /dev/null +++ b/go/pkg/zerotier/multicastgroup.go @@ -0,0 +1,20 @@ +/* + * 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 zerotier + +// MulticastGroup represents a normal Ethernet multicast or broadcast address plus 32 additional ZeroTier-specific bits +type MulticastGroup struct { + MAC MAC + ADI uint32 +} diff --git a/go/pkg/zerotier/network.go b/go/pkg/zerotier/network.go new file mode 100644 index 000000000..28e233c06 --- /dev/null +++ b/go/pkg/zerotier/network.go @@ -0,0 +1,74 @@ +/* + * 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 zerotier + +import ( + "net" + "sync/atomic" + "time" +) + +// NetworkConfig represents the network's current state +type NetworkConfig struct { + // ID is this network's 64-bit globally unique identifier + ID uint64 + + // MAC is the Ethernet MAC address of this device on this network + MAC MAC + + // Name is a short human-readable name set by the controller + Name string + + // Status is a status code indicating this network's authorization status + Status int + + // LastUpdated is the time this network's configuration was last updated from the controller + LastUpdated time.Time + + // Type is this network's type + Type int + + // MTU is the Ethernet MTU for this network + MTU int + + // CanBridge is true if this network is allowed to bridge in other devices with different Ethernet addresses + CanBridge bool + + // AllowsBroadcast is true if the broadcast (ff:ff:ff:ff:ff:ff) address works (excluding IPv4 ARP which is handled via a special path) + AllowsBroadcast bool + + // IPs are static IPs assigned by the network controller to this device + IPs []net.IPNet + + // Routes are static routes assigned by the network controller to this device + Routes []Route + + // MulticastSubscriptions are this device's current multicast subscriptions + MulticastSubscriptions []MulticastGroup + + // PortType is a human-readable description of this port's implementation type or name + PortType string + + // PortDeviceName is the OS-specific device name (e.g. tun0 or feth1856) for this network's virtual port + PortDeviceName string + + // PortErrorCode is an OS-specific error code returned by the virtual NIC driver + PortErrorCode int +} + +// Network is a currently joined network +type Network struct { + config atomic.Value + tap atomic.Value +} diff --git a/go/pkg/zerotier/node-callbacks.go b/go/pkg/zerotier/node-callbacks.go new file mode 100644 index 000000000..31e5024e4 --- /dev/null +++ b/go/pkg/zerotier/node-callbacks.go @@ -0,0 +1,180 @@ +/* + * 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 zerotier + +//#cgo CFLAGS: -O3 +//#define ZT_CGO 1 +//#include +//#include +//#include +//#include "../../native/GoGlue.h" +import "C" + +import ( + "net" + "sync" + "sync/atomic" + "unsafe" +) + +const ( + afInet = C.AF_INET + afInet6 = C.AF_INET6 + + networkStatusRequestingConfiguration = C.ZT_NETWORK_STATUS_REQUESTING_CONFIGURATION + networkStatusOK = C.ZT_NETWORK_STATUS_OK + networkStatusAccessDenied = C.ZT_NETWORK_STATUS_ACCESS_DENIED + networkStatusNotFound = C.ZT_NETWORK_STATUS_NOT_FOUND + networkStatusPortError = C.ZT_NETWORK_STATUS_PORT_ERROR + networkStatusClientTooOld = C.ZT_NETWORK_STATUS_CLIENT_TOO_OLD +) + +var ( + nodesByUserPtr map[uintptr]*Node + nodesByUserPtrLock sync.RWMutex +) + +//export goPathCheckFunc +func goPathCheckFunc(gn unsafe.Pointer, ztAddress C.uint64_t, af C.int, ip unsafe.Pointer, port C.int) C.int { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node != nil && node.pathCheck(uint64(ztAddress), int(af), nil, int(port)) { + return 1 + } + return 0 +} + +//export goPathLookupFunc +func goPathLookupFunc(gn unsafe.Pointer, ztAddress C.uint64_t, desiredAddressFamily int, familyP, ipP, portP unsafe.Pointer) C.int { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return 0 + } + + ip, port := node.pathLookup(uint64(ztAddress)) + ip4 := ip.To4() + if len(ip4) == 4 { + *((*C.int)(familyP)) = afInet + copy((*[4]byte)(ipP)[:], ip4) + *((*C.int)(portP)) = C.int(port) + } else if len(ip) == 16 { + *((*C.int)(familyP)) = afInet6 + copy((*[16]byte)(ipP)[:], ip) + *((*C.int)(portP)) = C.int(port) + } + return 0 +} + +//export goStateObjectPutFunc +func goStateObjectPutFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Pointer, len C.int) { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return + } + if len < 0 { + node.stateObjectDelete(int(objType), *((*[2]uint64)(id))) + } else { + node.stateObjectPut(int(objType), *((*[2]uint64)(id)), C.GoBytes(data, len)) + } +} + +//export goStateObjectGetFunc +func goStateObjectGetFunc(gn unsafe.Pointer, objType C.int, id, data unsafe.Pointer, bufSize C.uint) C.int { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return -1 + } + tmp, found := node.stateObjectGet(int(objType), *((*[2]uint64)(id))) + if found && len(tmp) < int(bufSize) { + if len(tmp) > 0 { + C.memcpy(data, unsafe.Pointer(&(tmp[0])), C.ulong(len(tmp))) + } + return C.int(len(tmp)) + } + return -1 +} + +//export goDNSResolverFunc +func goDNSResolverFunc(gn unsafe.Pointer, dnsRecordTypes unsafe.Pointer, numDNSRecordTypes C.int, name unsafe.Pointer, requestID C.uintptr_t) { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return + } + + recordTypes := C.GoBytes(dnsRecordTypes, numDNSRecordTypes) + recordName := C.GoString((*C.char)(name)) + + go func() { + recordNameCStrCopy := C.CString(recordName) + for _, rt := range recordTypes { + switch rt { + case C.ZT_DNS_RECORD_TXT: + recs, _ := net.LookupTXT(recordName) + for _, rec := range recs { + if len(rec) > 0 { + rnCS := C.CString(rec) + C.ZT_Node_processDNSResult(unsafe.Pointer(node.zn), nil, requestID, recordNameCStrCopy, C.ZT_DNS_RECORD_TXT, unsafe.Pointer(rnCS), C.uint(len(rec)), 0) + C.free(unsafe.Pointer(rnCS)) + } + } + } + } + C.ZT_Node_processDNSResult(unsafe.Pointer(node.zn), nil, requestID, recordNameCStrCopy, C.ZT_DNS_RECORD__END_OF_RESULTS, nil, 0, 0) + C.free(unsafe.Pointer(recordNameCStrCopy)) + }() +} + +//export goVirtualNetworkConfigFunc +func goVirtualNetworkConfigFunc(gn, tapP unsafe.Pointer, nwid C.uint64_t, op C.int, conf unsafe.Pointer) C.int { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return 255 + } + return C.int(node.handleNetworkConfigUpdate(int(op), (*C.ZT_VirtualNetworkConfig)(conf))) +} + +//export goZtEvent +func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) { + nodesByUserPtrLock.RLock() + node := nodesByUserPtr[uintptr(gn)] + nodesByUserPtrLock.RUnlock() + if node == nil { + return + } + switch eventType { + case C.ZT_EVENT_OFFLINE: + atomic.StoreUint32(&node.online, 0) + case C.ZT_EVENT_ONLINE: + atomic.StoreUint32(&node.online, 1) + case C.ZT_EVENT_TRACE: + node.handleTrace(C.GoString((*C.char)(data))) + case C.ZT_EVENT_USER_MESSAGE: + um := (*C.ZT_UserMessage)(data) + node.handleUserMessage(uint64(um.origin), uint64(um.typeId), C.GoBytes(um.data, C.int(um.length))) + case C.ZT_EVENT_REMOTE_TRACE: + rt := (*C.ZT_RemoteTrace)(data) + node.handleRemoteTrace(uint64(rt.origin), C.GoBytes(unsafe.Pointer(rt.data), C.int(rt.len))) + } +} diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go new file mode 100644 index 000000000..298df7b23 --- /dev/null +++ b/go/pkg/zerotier/node.go @@ -0,0 +1,128 @@ +/* + * 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 zerotier + +//#cgo CFLAGS: -O3 +//#cgo LDFLAGS: ${SRCDIR}/../../../build/node/libzt_core.a ${SRCDIR}/../../../build/go/native/libzt_go_native.a -lc++ +//#define ZT_CGO 1 +//#include +//#include "../../native/GoGlue.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" +import ( + "net" + "runtime" + "sync" + "sync/atomic" + "unsafe" +) + +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 +) + +// Tap is an instance of an EthernetTap object +type Tap struct { + tap *C.ZT_GoTap + networkStatus uint32 +} + +// Node is an instance of a ZeroTier node +type Node struct { + gn *C.ZT_GoNode + zn *C.ZT_Node + + taps map[uint64]*Tap + tapsLock sync.RWMutex + + online uint32 + running uint32 +} + +// NewNode creates and initializes a new instance of the ZeroTier node service +func NewNode() *Node { + n := new(Node) + + gnRawAddr := uintptr(unsafe.Pointer(n.gn)) + nodesByUserPtrLock.Lock() + nodesByUserPtr[gnRawAddr] = n + nodesByUserPtrLock.Unlock() + runtime.SetFinalizer(n, func(obj interface{}) { // make sure this always happens + nodesByUserPtrLock.Lock() + delete(nodesByUserPtr, gnRawAddr) + nodesByUserPtrLock.Unlock() + }) + + n.running = 1 + + return n +} + +// Close closes this Node and frees its underlying C++ Node structures +func (n *Node) Close() { + if atomic.SwapUint32(&n.running, 0) != 0 { + C.ZT_GoNode_delete(n.gn) + nodesByUserPtrLock.Lock() + delete(nodesByUserPtr, uintptr(unsafe.Pointer(n.gn))) + nodesByUserPtrLock.Unlock() + } +} + +func (n *Node) pathCheck(ztAddress uint64, af int, ip net.IP, port int) bool { + return true +} + +func (n *Node) pathLookup(ztAddress uint64) (net.IP, int) { + return nil, 0 +} + +func (n *Node) stateObjectPut(objType int, id [2]uint64, data []byte) { +} + +func (n *Node) stateObjectDelete(objType int, id [2]uint64) { +} + +func (n *Node) stateObjectGet(objType int, id [2]uint64) ([]byte, bool) { + return nil, false +} + +func (n *Node) handleTrace(traceMessage string) { +} + +func (n *Node) handleUserMessage(originAddress, messageTypeID uint64, data []byte) { +} + +func (n *Node) handleRemoteTrace(originAddress uint64, dictData []byte) { +} + +func (n *Node) handleNetworkConfigUpdate(op int, config *C.ZT_VirtualNetworkConfig) int { + return 0 +} diff --git a/go/pkg/zerotier/route.go b/go/pkg/zerotier/route.go new file mode 100644 index 000000000..8e68973e1 --- /dev/null +++ b/go/pkg/zerotier/route.go @@ -0,0 +1,28 @@ +/* + * 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 zerotier + +import "net" + +// Route represents a route in a host's routing table +type Route struct { + // Target for this route + Target net.IPNet + + // Via is how to reach this target (null/empty if the target IP range is local to this virtual LAN) + Via net.IP + + // Metric is an interface metric that can affect route priority (behavior can be OS-specific) + Metric int +} diff --git a/go/pkg/ztnode/errors.go b/go/pkg/ztnode/errors.go deleted file mode 100644 index 37956e7c9..000000000 --- a/go/pkg/ztnode/errors.go +++ /dev/null @@ -1,122 +0,0 @@ -/* - * 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/node.go b/go/pkg/ztnode/node.go deleted file mode 100644 index bab6fe58f..000000000 --- a/go/pkg/ztnode/node.go +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 ${SRCDIR}/../../../build/go/native/libzt_go_native.a -lc++ -//#define ZT_CGO 1 -//#include -//#include "../../native/GoGlue.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" -import "sync" - -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 { - gn *C.ZT_GoNode - zn *C.ZT_Node -} - -var ( - nodesByUserPtr map[uintptr]*Node - nodesByUserPtrLock sync.Mutex -) diff --git a/include/ZeroTierCore.h b/include/ZeroTierCore.h index 06774f3c9..37c7ebd23 100644 --- a/include/ZeroTierCore.h +++ b/include/ZeroTierCore.h @@ -983,7 +983,7 @@ typedef struct uint16_t flags; /** - * Route metric (not currently used) + * Route metric */ uint16_t metric; } ZT_VirtualNetworkRoute;