Browse Source

Bunch more fixes, implement C API for Identity operations.

Adam Ierymenko 5 years ago
parent
commit
2fbeaaf148
12 changed files with 601 additions and 600 deletions
  1. 8 157
      go/native/GoGlue.cpp
  2. 6 0
      go/native/GoGlue.h
  3. 143 130
      include/ZeroTierCore.h
  4. 1 13
      node/CMakeLists.txt
  5. 200 4
      node/Identity.cpp
  6. 7 101
      node/Identity.hpp
  7. 17 17
      node/IncomingPacket.cpp
  8. 3 3
      node/InetAddress.cpp
  9. 69 72
      node/InetAddress.hpp
  10. 106 0
      node/Locator.cpp
  11. 13 90
      node/Locator.hpp
  12. 28 13
      node/Node.cpp

+ 8 - 157
go/native/GoGlue.cpp

@@ -13,29 +13,25 @@
 
 #include "GoGlue.h"
 
+#include <cstring>
+#include <cstdlib>
+#include <cerrno>
+#include <ctime>
+
 #include "../../node/Constants.hpp"
 #include "../../node/InetAddress.hpp"
 #include "../../node/Node.hpp"
 #include "../../node/Utils.hpp"
 #include "../../node/MAC.hpp"
 #include "../../node/Address.hpp"
-#include "../../node/Locator.hpp"
 #include "../../osdep/OSUtils.hpp"
 #include "../../osdep/EthernetTap.hpp"
-#include "../../osdep/ManagedRoute.hpp"
-
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
 
 #ifndef __WINDOWS__
-#include <errno.h>
-#include <signal.h>
+extern "C" {
 #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>
@@ -45,6 +41,7 @@
 #ifdef __BSD__
 #include <net/if.h>
 #endif
+}
 #ifdef __LINUX__
 #ifndef IPV6_DONTFRAG
 #define IPV6_DONTFRAG 62
@@ -56,7 +53,6 @@
 #include <mutex>
 #include <map>
 #include <vector>
-#include <array>
 #include <set>
 #include <memory>
 #include <atomic>
@@ -114,7 +110,6 @@ extern "C" int goPathCheckFunc(void *,uint64_t,int,const void *,int);
 extern "C" int goPathLookupFunc(void *,uint64_t,int,int *,uint8_t [16],int *);
 extern "C" void goStateObjectPutFunc(void *,int,const uint64_t [2],const void *,int);
 extern "C" int goStateObjectGetFunc(void *,int,const uint64_t [2],void *,unsigned int);
-extern "C" void goDNSResolverFunc(void *,const uint8_t *,int,const char *,uintptr_t);
 extern "C" void goVirtualNetworkConfigFunc(void *,ZT_GoTap *,uint64_t,int,const ZT_VirtualNetworkConfig *);
 extern "C" void goZtEvent(void *,int,const void *);
 extern "C" void goHandleTapAddedMulticastGroup(void *,ZT_GoTap *,uint64_t,uint64_t,uint32_t);
@@ -279,6 +274,7 @@ static int ZT_GoNode_PathLookupFunction(
 	void *uptr,
 	void *tptr,
 	uint64_t ztAddress,
+	const ZT_Identity *id,
 	int desiredAddressFamily,
 	struct sockaddr_storage *sa)
 {
@@ -310,20 +306,6 @@ static int ZT_GoNode_PathLookupFunction(
 	return 0;
 }
 
-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)
-{
-	uint8_t t[256];
-	for(unsigned int i=0;(i<numTypes)&&(i<256);++i) t[i] = (uint8_t)types[i];
-	goDNSResolverFunc(reinterpret_cast<ZT_GoNode *>(uptr)->goUserPtr,t,(int)numTypes,name,requestId);
-}
-
 /****************************************************************************/
 
 extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr)
@@ -336,7 +318,6 @@ extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr)
 		cb.virtualNetworkFrameFunction = &ZT_GoNode_VirtualNetworkFrameFunction;
 		cb.virtualNetworkConfigFunction = &ZT_GoNode_VirtualNetworkConfigFunction;
 		cb.eventCallback = &ZT_GoNode_EventCallback;
-		cb.dnsResolver = &ZT_GoNode_DNSResolver;
 		cb.pathCheckFunction = &ZT_GoNode_PathCheckFunction;
 		cb.pathLookupFunction = &ZT_GoNode_PathLookupFunction;
 
@@ -727,133 +708,3 @@ extern "C" int ZT_GoTap_removeRoute(ZT_GoTap *tap,int targetAf,const void *targe
 	}
 	return reinterpret_cast<EthernetTap *>(tap)->removeRoute(target,via,metric);
 }
-
-/****************************************************************************/
-
-extern "C" const char *ZT_GoIdentity_generate(int type)
-{
-	Identity id;
-	id.generate((Identity::Type)type);
-	char *tmp = (char *)malloc(ZT_IDENTITY_STRING_BUFFER_LENGTH);
-	if (tmp)
-		id.toString(true,tmp);
-	return tmp;
-}
-
-extern "C" int ZT_GoIdentity_validate(const char *idStr)
-{
-	Identity id;
-	if (!id.fromString(idStr))
-		return 0;
-	if (!id.locallyValidate())
-		return 0;
-	return 1;
-}
-
-extern "C" int ZT_GoIdentity_sign(const char *idStr,const void *data,unsigned int len,void *sigbuf,unsigned int sigbuflen)
-{
-	Identity id;
-	if (!id.fromString(idStr))
-		return 0;
-	return (int)id.sign(data,len,sigbuf,sigbuflen);
-}
-
-extern "C" int ZT_GoIdentity_verify(const char *idStr,const void *data,unsigned int len,const void *sig,unsigned int siglen)
-{
-	Identity id;
-	if (!id.fromString(idStr))
-		return 0;
-	return id.verify(data,len,sig,siglen) ? 1 : 0;
-}
-
-/****************************************************************************/
-
-extern "C" int ZT_GoLocator_makeSecureDNSName(char *name,unsigned int nameBufSize,uint8_t *privateKey,unsigned int privateKeyBufSize)
-{
-	if ((privateKeyBufSize < ZT_ECC384_PRIVATE_KEY_SIZE)||(nameBufSize < 256))
-		return -1;
-	uint8_t pub[ZT_ECC384_PUBLIC_KEY_SIZE];
-	ECC384GenerateKey(pub,privateKey);
-	const Str n(Locator::makeSecureDnsName(pub));
-	if (n.length() >= nameBufSize)
-		return -1;
-	Utils::scopy(name,nameBufSize,n.c_str());
-	return ZT_ECC384_PRIVATE_KEY_SIZE;
-}
-
-extern "C" int ZT_GoLocator_makeLocator(
-	uint8_t *buf,
-	unsigned int bufSize,
-	int64_t ts,
-	const char *id,
-	const struct sockaddr_storage *physicalAddresses,
-	unsigned int physicalAddressCount,
-	const char **virtualAddresses,
-	unsigned int virtualAddressCount)
-{
-	Locator loc;
-	for(unsigned int i=0;i<physicalAddressCount;++i) {
-		loc.add(*reinterpret_cast<const InetAddress *>(physicalAddresses + i));
-	}
-	for(unsigned int i=0;i<virtualAddressCount;++i) {
-		Identity id;
-		if (!id.fromString(virtualAddresses[i]))
-			return -1;
-		loc.add(id);
-	}
-	Identity signingId;
-	if (!signingId.fromString(id))
-		return -1;
-	if (!signingId.hasPrivate())
-		return -1;
-	if (!loc.finish(signingId,ts))
-		return -1;
-	Buffer<65536> *tmp = new Buffer<65536>();
-	loc.serialize(*tmp);
-	if (tmp->size() > bufSize) {
-		delete tmp;
-		return -1;
-	}
-	memcpy(buf,tmp->data(),tmp->size());
-	int s = (int)tmp->size();
-	delete tmp;
-	return s;
-}
-
-extern "C" int ZT_GoLocator_decodeLocator(const uint8_t *locatorBytes,unsigned int locatorSize,struct ZT_GoLocator_Info *info)
-{
-	Locator loc;
-	if (!loc.deserialize(locatorBytes,locatorSize))
-		return -1;
-	if (!loc.verify())
-		return -2;
-	loc.id().toString(false,info->id);
-	info->phyCount = 0;
-	info->virtCount = 0;
-	for(auto p=loc.phy().begin();p!=loc.phy().end();++p)
-		memcpy(&(info->phy[info->phyCount++]),&(*p),sizeof(struct sockaddr_storage));
-	for(auto v=loc.virt().begin();v!=loc.virt().end();++v)
-		v->toString(false,info->virt[info->virtCount++]);
-	return 1;
-}
-
-int ZT_GoLocator_makeSignedTxtRecords(
-	const uint8_t *locator,
-	unsigned int locatorSize,
-	const char *name,
-	const uint8_t *privateKey,
-	unsigned int privateKeySize,
-	char results[256][256])
-{
-	if (privateKeySize != ZT_ECC384_PRIVATE_KEY_SIZE)
-		return -1;
-	Locator loc;
-	if (!loc.deserialize(locator,locatorSize))
-		return -1;
-	std::vector<Str> r(loc.makeTxtRecords(privateKey));
-	if (r.size() > 256)
-		return -1;
-	for(unsigned long i=0;i<r.size();++i)
-		Utils::scopy(results[i],256,r[i].c_str());
-	return (int)r.size();
-}

+ 6 - 0
go/native/GoGlue.h

@@ -14,9 +14,15 @@
 #ifndef ZT_GONODE_H
 #define ZT_GONODE_H
 
+#ifdef __cplusplus
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#else
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#endif
 
 #include "../../include/ZeroTierCore.h"
 #include "../../node/Constants.hpp"

+ 143 - 130
include/ZeroTierCore.h

@@ -21,6 +21,7 @@
 
 #ifdef __cplusplus
 #include <cstdint>
+extern "C" {
 #else
 #include <stdint.h>
 #endif
@@ -43,10 +44,6 @@
 #define ZT_SDK_API
 #endif
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 /****************************************************************************/
 /* Core constants                                                           */
 /****************************************************************************/
@@ -95,11 +92,6 @@ extern "C" {
  */
 #define ZT_MAX_PHYSMTU (ZT_MAX_PHYSPAYLOAD + ZT_MAX_HEADROOM)
 
-/**
- * Maximum size of a remote trace message's serialized Dictionary
- */
-#define ZT_MAX_REMOTE_TRACE_SIZE 10000
-
 /**
  * Maximum length of network short name
  */
@@ -262,78 +254,6 @@ extern "C" {
  */
 #define ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN 0x0000000000000001ULL
 
-/****************************************************************************/
-
-// Fields in remote trace dictionaries
-#define ZT_REMOTE_TRACE_FIELD__EVENT "event"
-#define ZT_REMOTE_TRACE_FIELD__NODE_ID "nodeId"
-#define ZT_REMOTE_TRACE_FIELD__PACKET_ID "packetId"
-#define ZT_REMOTE_TRACE_FIELD__PACKET_VERB "packetVerb"
-#define ZT_REMOTE_TRACE_FIELD__PACKET_TRUSTED_PATH_ID "packetTrustedPathId"
-#define ZT_REMOTE_TRACE_FIELD__PACKET_TRUSTED_PATH_APPROVED "packetTrustedPathApproved"
-#define ZT_REMOTE_TRACE_FIELD__PACKET_HOPS "packetHops"
-#define ZT_REMOTE_TRACE_FIELD__REMOTE_ZTADDR "remoteZtAddr"
-#define ZT_REMOTE_TRACE_FIELD__REMOTE_PHYADDR "remotePhyAddr"
-#define ZT_REMOTE_TRACE_FIELD__LOCAL_ZTADDR "localZtAddr"
-#define ZT_REMOTE_TRACE_FIELD__LOCAL_PHYADDR "localPhyAddr"
-#define ZT_REMOTE_TRACE_FIELD__LOCAL_SOCKET "localSocket"
-#define ZT_REMOTE_TRACE_FIELD__IP_SCOPE "phyAddrIpScope"
-#define ZT_REMOTE_TRACE_FIELD__NETWORK_ID "networkId"
-#define ZT_REMOTE_TRACE_FIELD__SOURCE_ZTADDR "sourceZtAddr"
-#define ZT_REMOTE_TRACE_FIELD__DEST_ZTADDR "destZtAddr"
-#define ZT_REMOTE_TRACE_FIELD__SOURCE_MAC "sourceMac"
-#define ZT_REMOTE_TRACE_FIELD__DEST_MAC "destMac"
-#define ZT_REMOTE_TRACE_FIELD__ETHERTYPE "etherType"
-#define ZT_REMOTE_TRACE_FIELD__VLAN_ID "vlanId"
-#define ZT_REMOTE_TRACE_FIELD__FRAME_LENGTH "frameLength"
-#define ZT_REMOTE_TRACE_FIELD__FRAME_DATA "frameData"
-#define ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_NOTEE "filterNoTee"
-#define ZT_REMOTE_TRACE_FIELD__FILTER_FLAG_INBOUND "filterInbound"
-#define ZT_REMOTE_TRACE_FIELD__FILTER_RESULT "filterResult"
-#define ZT_REMOTE_TRACE_FIELD__FILTER_BASE_RULE_LOG "filterBaseRuleLog"
-#define ZT_REMOTE_TRACE_FIELD__FILTER_CAP_RULE_LOG "filterCapRuleLog"
-#define ZT_REMOTE_TRACE_FIELD__FILTER_CAP_ID "filterMatchingCapId"
-#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TYPE "credType"
-#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ID "credId"
-#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_TIMESTAMP "credTs"
-#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_INFO "credInfo"
-#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_ISSUED_TO "credIssuedTo"
-#define ZT_REMOTE_TRACE_FIELD__CREDENTIAL_REVOCATION_TARGET "credRevocationTarget"
-#define ZT_REMOTE_TRACE_FIELD__REASON "reason"
-#define ZT_REMOTE_TRACE_FIELD__NETWORK_CONTROLLER_ID "networkControllerId"
-
-// Event types in remote traces
-#define ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE 0x1000
-#define ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH 0x1001
-#define ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH 0x1002
-#define ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED 0x1003
-#define ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE 0x1004
-#define ZT_REMOTE_TRACE_EVENT__PACKET_INVALID 0x1005
-#define ZT_REMOTE_TRACE_EVENT__DROPPED_HELLO 0x1006
-#define ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED 0x2000
-#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED 0x2001
-#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED 0x2002
-#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED 0x2003
-#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_ACCEPTED 0x2004
-#define ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT 0x2005
-#define ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE 0x2006
-
-// Event types in remote traces in hex string form
-#define ZT_REMOTE_TRACE_EVENT__RESETTING_PATHS_IN_SCOPE_S "1000"
-#define ZT_REMOTE_TRACE_EVENT__PEER_CONFIRMING_UNKNOWN_PATH_S "1001"
-#define ZT_REMOTE_TRACE_EVENT__PEER_LEARNED_NEW_PATH_S "1002"
-#define ZT_REMOTE_TRACE_EVENT__PEER_REDIRECTED_S "1003"
-#define ZT_REMOTE_TRACE_EVENT__PACKET_MAC_FAILURE_S "1004"
-#define ZT_REMOTE_TRACE_EVENT__PACKET_INVALID_S "1005"
-#define ZT_REMOTE_TRACE_EVENT__DROPPED_HELLO_S "1006"
-#define ZT_REMOTE_TRACE_EVENT__OUTGOING_NETWORK_FRAME_DROPPED_S "2000"
-#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_ACCESS_DENIED_S "2001"
-#define ZT_REMOTE_TRACE_EVENT__INCOMING_NETWORK_FRAME_DROPPED_S "2002"
-#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_REJECTED_S "2003"
-#define ZT_REMOTE_TRACE_EVENT__CREDENTIAL_ACCEPTED_S "2004"
-#define ZT_REMOTE_TRACE_EVENT__NETWORK_CONFIG_REQUEST_SENT_S "2005"
-#define ZT_REMOTE_TRACE_EVENT__NETWORK_FILTER_TRACE_S "2006"
-
 /****************************************************************************/
 /* Structures and other types                                               */
 /****************************************************************************/
@@ -503,48 +423,23 @@ enum ZT_Event
 	 *
 	 * Meta-data: ZT_UserMessage structure
 	 */
-	ZT_EVENT_USER_MESSAGE = 6,
-
-	/**
-	 * Remote trace received
-	 *
-	 * NOTE: any node can fling a VERB_REMOTE_TRACE at you. It's up to you
-	 * to determine if you want to do anything with it or just silently
-	 * drop it on the floor. It's also up to you to handle these securely!
-	 *
-	 * Meta-data: ZT_RemoteTrace structure
-	 */
-	ZT_EVENT_REMOTE_TRACE = 7
+	ZT_EVENT_USER_MESSAGE = 6
 };
 
 /**
- * Payload of REMOTE_TRACE event
+ * Identity type codes
  */
-typedef struct
+enum ZT_Identity_Type
 {
-	/**
-	 * ZeroTier address of sender (in least significant 40 bits only)
-	 */
-	uint64_t origin;
-
-	/**
-	 * Null-terminated Dictionary containing key/value pairs sent by origin
-	 *
-	 * This *should* be a dictionary, but the implementation only checks
-	 * that it is a valid non-empty C-style null-terminated string. Be very
-	 * careful to use a well-tested parser to parse this as it represents
-	 * data received from a potentially un-trusted peer on the network.
-	 * Invalid payloads should be dropped.
-	 *
-	 * The contents of data[] may be modified.
-	 */
-	const char *data;
+	/* These values must be the same as in Identity.hpp in the core. */
+	ZT_IDENTITY_TYPE_C25519 = 0,
+	ZT_IDENTITY_TYPE_P384 = 1
+};
 
-	/**
-	 * Length of dict[] in bytes, INCLUDING terminating null
-	 */
-	unsigned int len;
-} ZT_RemoteTrace;
+/**
+ * A ZeroTier identity (opaque)
+ */
+typedef void ZT_Identity;
 
 /**
  * User message used with ZT_EVENT_USER_MESSAGE
@@ -589,6 +484,11 @@ typedef struct
 	 */
 	uint64_t address;
 
+	/**
+	 * Actual identity object for this node
+	 */
+	const ZT_Identity *identity;
+
 	/**
 	 * Public identity in string-serialized form (safe to send to others)
 	 *
@@ -965,16 +865,6 @@ enum ZT_VirtualNetworkConfigOperation
 	ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY = 4
 };
 
-/**
- * What trust hierarchy role does this peer have?
- */
-enum ZT_PeerRole
-{
-	ZT_PEER_ROLE_LEAF = 0,       // ordinary node
-	ZT_PEER_ROLE_MOON = 1,       // moon root
-	ZT_PEER_ROLE_PLANET = 2      // planetary root
-};
-
 /**
  * Virtual network configuration
  */
@@ -1183,6 +1073,15 @@ typedef struct
 	int preferred;
 } ZT_PeerPhysicalPath;
 
+/**
+ * What trust hierarchy role does this peer have?
+ */
+enum ZT_PeerRole
+{
+	ZT_PEER_ROLE_LEAF = 0,       // ordinary node
+	ZT_PEER_ROLE_ROOT = 1        // root server
+};
+
 /**
  * Peer status result buffer
  */
@@ -1193,6 +1092,11 @@ typedef struct
 	 */
 	uint64_t address;
 
+	/**
+	 * Peer identity
+	 */
+	const ZT_Identity *identity;
+
 	/**
 	 * Remote major version or -1 if not known
 	 */
@@ -1497,7 +1401,7 @@ typedef int (*ZT_PathLookupFunction)(
 	void *,                           /* User ptr */
 	void *,                           /* Thread ptr */
 	uint64_t,                         /* ZeroTier address (40 bits) */
-	const char *,                     /* Identity in string form */
+	const ZT_Identity *,              /* Full identity of node */
 	int,                              /* Desired ss_family or -1 for any */
 	struct sockaddr_storage *);       /* Result buffer */
 
@@ -1541,12 +1445,12 @@ struct ZT_Node_Callbacks
 	ZT_EventCallback eventCallback;
 
 	/**
-	 * OPTIONAL: Function to check whether a given physical path should be used
+	 * OPTIONAL: Function to check whether a given physical path should be used for ZeroTier traffic
 	 */
 	ZT_PathCheckFunction pathCheckFunction;
 
 	/**
-	 * OPTIONAL: Function to get hints to physical paths to ZeroTier addresses
+	 * RECOMMENDED: Function to look up paths to ZeroTier nodes
 	 */
 	ZT_PathLookupFunction pathLookupFunction;
 };
@@ -1865,6 +1769,115 @@ ZT_SDK_API void ZT_Node_setController(ZT_Node *node,void *networkConfigMasterIns
  */
 ZT_SDK_API enum ZT_ResultCode ZT_Node_setPhysicalPathConfiguration(ZT_Node *node,const struct sockaddr_storage *pathNetwork,const ZT_PhysicalPathConfiguration *pathConfig);
 
+/**
+ * Generate a new identity
+ *
+ * Due to a small amount of proof of work this can be a time consuming and CPU
+ * intensive operation. It takes less than a second on most desktop-class systems
+ * but can take longer on e.g. phones.
+ *
+ * @param type Type of identity to generate
+ * @return New identity or NULL on error
+ */
+ZT_SDK_API ZT_Identity *ZT_Identity_new(enum ZT_Identity_Type type);
+
+/**
+ * Create a new identity object from a string-serialized identity
+ *
+ * @param idStr Identity in string format
+ * @return Identity object or NULL if the supplied identity string was not valid
+ */
+ZT_SDK_API ZT_Identity *ZT_Identity_fromString(const char *idStr);
+
+/**
+ * Validate this identity
+ *
+ * This can be slightly time consuming due to address derivation (work) checking.
+ *
+ * @return Non-zero if identity is valid
+ */
+ZT_SDK_API int ZT_Identity_validate(const ZT_Identity *id);
+
+/**
+ * Sign a data object with this identity
+ *
+ * The identity must have a private key or this will fail.
+ *
+ * @param id Identity to use to sign
+ * @param data Data to sign
+ * @param len Length of data
+ * @param signature Buffer to store signature
+ * @param signatureBufferLength Length of buffer (must be at least 96 bytes)
+ * @return Length of signature in bytes or 0 on failure.
+ */
+ZT_SDK_API unsigned int ZT_Identity_sign(const ZT_Identity *id,const void *data,unsigned int len,void *signature,unsigned int signatureBufferLength);
+
+/**
+ * Verify a signature
+ *
+ * @param id Identity to use to verify
+ * @param data Data to verify
+ * @param len Length of data
+ * @param signature Signature to check
+ * @param sigLen Length of signature in bytes
+ * @return Non-zero if signature is valid
+ */
+ZT_SDK_API int ZT_Identity_verify(const ZT_Identity *id,const void *data,unsigned int len,const void *signature,unsigned int sigLen);
+
+/**
+ * Get identity type
+ *
+ * @param id Identity to query
+ * @return Identity type code
+ */
+ZT_SDK_API enum ZT_Identity_Type ZT_Identity_type(const ZT_Identity *id);
+
+/**
+ * Convert an identity to its string representation
+ *
+ * @param id Identity to convert
+ * @param buf Buffer to store identity (should be at least about 1024 bytes in length)
+ * @param capacity Capacity of buffer
+ * @param includePrivate If true include the private key if present
+ * @return Pointer to buf or NULL on overflow or other error
+ */
+ZT_SDK_API char *ZT_Identity_toString(const ZT_Identity *id,char *buf,int capacity,int includePrivate);
+
+/**
+ * Check whether this identity object also holds a private key
+ *
+ * @param id Identity to query
+ * @return Non-zero if a private key is held
+ */
+ZT_SDK_API int ZT_Identity_hasPrivate(const ZT_Identity *id);
+
+/**
+ * Get the ZeroTier address associated with this identity
+ *
+ * @param id Identity to query
+ * @return ZeroTier address (only least significant 40 bits are meaningful, rest will be 0)
+ */
+ZT_SDK_API uint64_t ZT_Identity_address(const ZT_Identity *id);
+
+/**
+ * Compute a hash of this identity's public keys (or both public and private if includePrivate is true)
+ *
+ * @param id Identity to query
+ * @param h Buffer for 384-bit hash
+ * @param includePrivate If true include private keys if any
+ */
+ZT_SDK_API void ZT_Identity_hash(const ZT_Identity *id,uint8_t h[48],int includePrivate);
+
+/**
+ * Delete an identity and free associated memory
+ *
+ * This should only be used with identities created via Identity_new
+ * and Identity_fromString().
+ *
+ * @param id Identity to delete
+ */
+ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id);
+
 /**
  * Get ZeroTier One version
  *

+ 1 - 13
node/CMakeLists.txt

@@ -56,6 +56,7 @@ set(core_src
 	Identity.cpp
 	IncomingPacket.cpp
 	InetAddress.cpp
+	Locator.cpp
 	Membership.cpp
 	Network.cpp
 	NetworkConfig.cpp
@@ -74,16 +75,3 @@ set(core_src
 add_library(${PROJECT_NAME} STATIC ${core_src} ${core_headers})
 target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_11)
 target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR})
-
-#if(UNIX)
-#	set_source_files_properties(
-#		AES.cpp
-#		AES-aesni.c
-#		ECC384.cpp
-#		Salsa20.cpp
-#		C25519.cpp
-#		Poly1305.cpp
-#		PROPERTIES
-#		COMPILE_FLAGS "-Wall -O3"
-#	)
-#endif(UNIX)

+ 200 - 4
node/Identity.cpp

@@ -11,10 +11,8 @@
  */
 /****/
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdint.h>
+#include <cstring>
+#include <cstdint>
 
 #include "Constants.hpp"
 #include "Identity.hpp"
@@ -143,6 +141,108 @@ bool Identity::locallyValidate() const
 	return false;
 }
 
+bool Identity::hash(uint8_t h[48],const bool includePrivate) const
+{
+	switch(_type) {
+
+	case C25519:
+		if ((_hasPrivate)&&(includePrivate))
+			SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
+		else SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+		return true;
+
+	case P384:
+		if ((_hasPrivate)&&(includePrivate))
+			SHA384(h,&_pub,sizeof(_pub),&_priv,sizeof(_priv));
+		else SHA384(h,&_pub,sizeof(_pub));
+		return true;
+
+	}
+	return false;
+}
+
+unsigned int Identity::sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
+{
+	if (_hasPrivate) {
+		switch(_type) {
+
+			case C25519:
+				if (siglen >= ZT_C25519_SIGNATURE_LEN) {
+					C25519::sign(_priv.c25519,_pub.c25519,data,len,sig);
+					return ZT_C25519_SIGNATURE_LEN;
+				}
+
+			case P384:
+				if (siglen >= ZT_ECC384_SIGNATURE_SIZE) {
+					// When signing with P384 we also hash the C25519 public key as an
+					// extra measure to ensure that only this identity can verify.
+					uint8_t h[48];
+					SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+					ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig);
+					return ZT_ECC384_SIGNATURE_SIZE;
+				}
+
+		}
+	}
+	return 0;
+}
+
+bool Identity::verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const
+{
+	switch(_type) {
+
+		case C25519:
+			return C25519::verify(_pub.c25519,data,len,sig,siglen);
+
+		case P384:
+			if (siglen == ZT_ECC384_SIGNATURE_SIZE) {
+				uint8_t h[48];
+				SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+				return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig);
+			}
+			break;
+
+	}
+	return false;
+}
+
+bool Identity::agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const
+{
+	uint8_t rawkey[128];
+	uint8_t h[64];
+	if (_hasPrivate) {
+		if (_type == C25519) {
+
+			if ((id._type == C25519)||(id._type == P384)) {
+				// If we are a C25519 key we can agree with another C25519 key or with only the
+				// C25519 portion of a type 1 P-384 key.
+				C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
+				SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN);
+				memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
+				return true;
+			}
+
+		} else if (_type == P384) {
+
+			if (id._type == P384) {
+				C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
+				ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN);
+				SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE);
+				memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
+				return true;
+			} else if (id._type == C25519) {
+				// If the other identity is a C25519 identity we can agree using only that type.
+				C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
+				SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN);
+				memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
+				return true;
+			}
+
+		}
+	}
+	return false;
+}
+
 char *Identity::toString(bool includePrivate,char buf[ZT_IDENTITY_STRING_BUFFER_LENGTH]) const
 {
 	switch(_type) {
@@ -286,3 +386,99 @@ bool Identity::fromString(const char *str)
 }
 
 } // namespace ZeroTier
+
+extern "C" {
+
+ZT_Identity *ZT_Identity_new(enum ZT_Identity_Type type)
+{
+	if ((type != ZT_IDENTITY_TYPE_C25519)&&(type != ZT_IDENTITY_TYPE_P384))
+		return nullptr;
+	try {
+		ZeroTier::Identity *id = new ZeroTier::Identity();
+		id->generate((ZeroTier::Identity::Type)type);
+		return reinterpret_cast<ZT_Identity *>(id);
+	} catch ( ... ) {
+		return nullptr;
+	}
+}
+
+ZT_Identity *ZT_Identity_fromString(const char *idStr)
+{
+	if (!idStr)
+		return nullptr;
+	try {
+		ZeroTier::Identity *id = new ZeroTier::Identity();
+		if (!id->fromString(idStr)) {
+			delete id;
+			return nullptr;
+		}
+		return reinterpret_cast<ZT_Identity *>(id);
+	} catch ( ... ) {
+		return nullptr;
+	}
+}
+
+int ZT_Identity_validate(const ZT_Identity *id)
+{
+	if (!id)
+		return 0;
+	return reinterpret_cast<const ZeroTier::Identity *>(id)->locallyValidate() ? 1 : 0;
+}
+
+unsigned int ZT_Identity_sign(const ZT_Identity *id,const void *data,unsigned int len,void *signature,unsigned int signatureBufferLength)
+{
+	if (!id)
+		return 0;
+	if (signatureBufferLength < ZT_SIGNATURE_BUFFER_SIZE)
+		return 0;
+	return reinterpret_cast<const ZeroTier::Identity *>(id)->sign(data,len,signature,signatureBufferLength);
+}
+
+int ZT_Identity_verify(const ZT_Identity *id,const void *data,unsigned int len,const void *signature,unsigned int sigLen)
+{
+	if ((!id)||(!signature)||(!sigLen))
+		return 0;
+	return reinterpret_cast<const ZeroTier::Identity *>(id)->verify(data,len,signature,sigLen) ? 1 : 0;
+}
+
+enum ZT_Identity_Type ZT_Identity_type(const ZT_Identity *id)
+{
+	if (!id)
+		return (ZT_Identity_Type)0;
+	return (enum ZT_Identity_Type)reinterpret_cast<const ZeroTier::Identity *>(id)->type();
+}
+
+char *ZT_Identity_toString(const ZT_Identity *id,char *buf,int capacity,int includePrivate)
+{
+	if ((!id)||(!buf)||(capacity < ZT_IDENTITY_STRING_BUFFER_LENGTH))
+		return nullptr;
+	reinterpret_cast<const ZeroTier::Identity *>(id)->toString(includePrivate != 0,buf);
+	return buf;
+}
+
+int ZT_Identity_hasPrivate(const ZT_Identity *id)
+{
+	if (!id)
+		return 0;
+	return reinterpret_cast<const ZeroTier::Identity *>(id)->hasPrivate() ? 1 : 0;
+}
+
+uint64_t ZT_Identity_address(const ZT_Identity *id)
+{
+	if (!id)
+		return 0;
+	return reinterpret_cast<const ZeroTier::Identity *>(id)->address().toInt();
+}
+
+void ZT_Identity_hash(const ZT_Identity *id,uint8_t h[48],int includePrivate)
+{
+	reinterpret_cast<const ZeroTier::Identity *>(id)->hash(h,includePrivate != 0);
+}
+
+ZT_SDK_API void ZT_Identity_delete(ZT_Identity *id)
+{
+	if (id)
+		delete reinterpret_cast<ZeroTier::Identity *>(id);
+}
+
+}

+ 7 - 101
node/Identity.hpp

@@ -67,7 +67,7 @@ public:
 	 *
 	 * @param str Identity in canonical string format
 	 */
-	ZT_ALWAYS_INLINE Identity(const char *str) { fromString(str); }
+	explicit ZT_ALWAYS_INLINE Identity(const char *str) { fromString(str); }
 
 	/**
 	 * Set identity to NIL value (all zero)
@@ -86,7 +86,7 @@ public:
 	 *
 	 * @param t Type of identity to generate
 	 */
-	void generate(const Type t);
+	void generate(Type t);
 
 	/**
 	 * Check the validity of this identity's pairing of key to address
@@ -106,25 +106,7 @@ public:
 	 * @param h Buffer to receive SHA384 of public key(s)
 	 * @param includePrivate If true, hash private key(s) as well
 	 */
-	inline bool hash(uint8_t h[48],const bool includePrivate = false) const
-	{
-		switch(_type) {
-
-			case C25519:
-				if ((_hasPrivate)&&(includePrivate))
-					SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN,_priv.c25519,ZT_C25519_PRIVATE_KEY_LEN);
-				else SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
-				return true;
-
-			case P384:
-				if ((_hasPrivate)&&(includePrivate))
-					SHA384(h,&_pub,sizeof(_pub),&_priv,sizeof(_priv));
-				else SHA384(h,&_pub,sizeof(_pub));
-				return true;
-
-		}
-		return false;
-	}
+	bool hash(uint8_t h[48],bool includePrivate = false) const;
 
 	/**
 	 * Sign a message with this identity (private key required)
@@ -138,31 +120,7 @@ public:
 	 * @param siglen Length of buffer
 	 * @return Number of bytes actually written to sig or 0 on error
 	 */
-	inline unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const
-	{
-		if (_hasPrivate) {
-			switch(_type) {
-
-				case C25519:
-					if (siglen >= ZT_C25519_SIGNATURE_LEN) {
-						C25519::sign(_priv.c25519,_pub.c25519,data,len,sig);
-						return ZT_C25519_SIGNATURE_LEN;
-					}
-
-				case P384:
-					if (siglen >= ZT_ECC384_SIGNATURE_SIZE) {
-						// When signing with P384 we also hash the C25519 public key as an
-						// extra measure to ensure that only this identity can verify.
-						uint8_t h[48];
-						SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
-						ECC384ECDSASign(_priv.p384,h,(uint8_t *)sig);
-						return ZT_ECC384_SIGNATURE_SIZE;
-					}
-
-			}
-		}
-		return 0;
-	}
+	unsigned int sign(const void *data,unsigned int len,void *sig,unsigned int siglen) const;
 
 	/**
 	 * Verify a message signature against this identity
@@ -173,24 +131,7 @@ public:
 	 * @param siglen Length of signature in bytes
 	 * @return True if signature validates and data integrity checks
 	 */
-	inline bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const
-	{
-		switch(_type) {
-
-			case C25519:
-				return C25519::verify(_pub.c25519,data,len,sig,siglen);
-
-			case P384:
-				if (siglen == ZT_ECC384_SIGNATURE_SIZE) {
-					uint8_t h[48];
-					SHA384(h,data,len,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
-					return ECC384ECDSAVerify(_pub.p384,h,(const uint8_t *)sig);
-				}
-				break;
-
-		}
-		return false;
-	}
+	bool verify(const void *data,unsigned int len,const void *sig,unsigned int siglen) const;
 
 	/**
 	 * Shortcut method to perform key agreement with another identity
@@ -201,42 +142,7 @@ public:
 	 * @param key Result parameter to fill with key bytes
 	 * @return Was agreement successful?
 	 */
-	inline bool agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const
-	{
-		uint8_t rawkey[128];
-		uint8_t h[64];
-		if (_hasPrivate) {
-			if (_type == C25519) {
-
-				if ((id._type == C25519)||(id._type == P384)) {
-					// If we are a C25519 key we can agree with another C25519 key or with only the
-					// C25519 portion of a type 1 P-384 key.
-					C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
-					SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN);
-					memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
-					return true;
-				}
-
-			} else if (_type == P384) {
-
-				if (id._type == P384) {
-					C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
-					ECC384ECDH(id._pub.p384,_priv.p384,rawkey + ZT_C25519_SHARED_KEY_LEN);
-					SHA384(h,rawkey,ZT_C25519_SHARED_KEY_LEN + ZT_ECC384_SHARED_SECRET_SIZE);
-					memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
-					return true;
-				} else if (id._type == C25519) {
-					// If the other identity is a C25519 identity we can agree using only that type.
-					C25519::agree(_priv.c25519,id._pub.c25519,rawkey);
-					SHA512(h,rawkey,ZT_C25519_SHARED_KEY_LEN);
-					memcpy(key,h,ZT_PEER_SECRET_KEY_LENGTH);
-					return true;
-				}
-
-			}
-		}
-		return false;
-	}
+	bool agree(const Identity &id,uint8_t key[ZT_PEER_SECRET_KEY_LENGTH]) const;
 
 	/**
 	 * @return This identity's address
@@ -476,7 +382,7 @@ public:
 					privlen = data[ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE];
 					if (len < (privlen + (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1)))
 						return -1;
-					return (privlen + (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1));
+					return (int)(privlen + (unsigned int)(ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 1 + ZT_IDENTITY_P384_COMPOUND_PRIVATE_KEY_SIZE + 1));
 				} else if (privlen == 0) {
 					_hasPrivate = false;
 					return (ZT_ADDRESS_LENGTH + 1 + ZT_IDENTITY_P384_COMPOUND_PUBLIC_KEY_SIZE + 2);

+ 17 - 17
node/IncomingPacket.cpp

@@ -41,7 +41,7 @@ namespace {
 // Implementation of each protocol verb                                     //
 //////////////////////////////////////////////////////////////////////////////
 
-static void _sendErrorNeedCredentials(IncomingPacket &pkt,const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid,const SharedPtr<Path> &path)
+void _sendErrorNeedCredentials(IncomingPacket &pkt,const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid,const SharedPtr<Path> &path)
 {
 	Packet outp(pkt.source(),RR->identity.address(),Packet::VERB_ERROR);
 	outp.append((uint8_t)pkt.verb());
@@ -52,7 +52,7 @@ static void _sendErrorNeedCredentials(IncomingPacket &pkt,const RuntimeEnvironme
 	path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 }
 
-static inline bool _doHELLO(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const bool alreadyAuthenticated,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doHELLO(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const bool alreadyAuthenticated,const SharedPtr<Path> &path)
 {
 	const int64_t now = RR->node->now();
 
@@ -181,15 +181,15 @@ static inline bool _doHELLO(IncomingPacket &pkt,const RuntimeEnvironment *const
 	return true;
 }
 
-static inline bool _doACK(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doACK(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 }
 
-static inline bool _doQOS_MEASUREMENT(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doQOS_MEASUREMENT(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 }
 
-static inline bool _doERROR(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doERROR(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	const Packet::Verb inReVerb = (Packet::Verb)pkt[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB];
 	const uint64_t inRePacketId = pkt.at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID);
@@ -251,7 +251,7 @@ static inline bool _doERROR(IncomingPacket &pkt,const RuntimeEnvironment *const
 	return true;
 }
 
-static inline bool _doOK(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doOK(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	const Packet::Verb inReVerb = (Packet::Verb)pkt[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
 	const uint64_t inRePacketId = pkt.at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
@@ -318,7 +318,7 @@ static inline bool _doOK(IncomingPacket &pkt,const RuntimeEnvironment *const RR,
 	return true;
 }
 
-static inline bool _doWHOIS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doWHOIS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	if (!peer->rateGateInboundWhoisRequest(RR->node->now()))
 		return true;
@@ -353,7 +353,7 @@ static inline bool _doWHOIS(IncomingPacket &pkt,const RuntimeEnvironment *const
 	return true;
 }
 
-static inline bool _doRENDEZVOUS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doRENDEZVOUS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	if (RR->topology->isRoot(peer->identity())) {
 		const Address with(pkt.field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
@@ -375,7 +375,7 @@ static inline bool _doRENDEZVOUS(IncomingPacket &pkt,const RuntimeEnvironment *c
 	return true;
 }
 
-static inline bool _doFRAME(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doFRAME(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	const uint64_t nwid = pkt.at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID);
 	const SharedPtr<Network> network(RR->node->network(nwid));
@@ -398,7 +398,7 @@ static inline bool _doFRAME(IncomingPacket &pkt,const RuntimeEnvironment *const
 	return true;
 }
 
-static inline bool _doEXT_FRAME(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doEXT_FRAME(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	const uint64_t nwid = pkt.at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID);
 	const SharedPtr<Network> network(RR->node->network(nwid));
@@ -476,7 +476,7 @@ static inline bool _doEXT_FRAME(IncomingPacket &pkt,const RuntimeEnvironment *co
 	return true;
 }
 
-static inline bool _doECHO(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doECHO(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	if (!peer->rateGateEchoRequest(RR->node->now()))
 		return true;
@@ -495,7 +495,7 @@ static inline bool _doECHO(IncomingPacket &pkt,const RuntimeEnvironment *const R
 	return true;
 }
 
-static inline bool _doNETWORK_CREDENTIALS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doNETWORK_CREDENTIALS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	if (!peer->rateGateCredentialsReceived(RR->node->now()))
 		return true;
@@ -577,7 +577,7 @@ static inline bool _doNETWORK_CREDENTIALS(IncomingPacket &pkt,const RuntimeEnvir
 	return true;
 }
 
-static inline bool _doNETWORK_CONFIG_REQUEST(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doNETWORK_CONFIG_REQUEST(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	const uint64_t nwid = pkt.at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
 	const unsigned int hopCount = pkt.hops();
@@ -603,7 +603,7 @@ static inline bool _doNETWORK_CONFIG_REQUEST(IncomingPacket &pkt,const RuntimeEn
 	return true;
 }
 
-static inline bool _doNETWORK_CONFIG(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doNETWORK_CONFIG(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	const SharedPtr<Network> network(RR->node->network(pkt.at<uint64_t>(ZT_PACKET_IDX_PAYLOAD)));
 	if (network) {
@@ -622,7 +622,7 @@ static inline bool _doNETWORK_CONFIG(IncomingPacket &pkt,const RuntimeEnvironmen
 	return true;
 }
 
-static inline bool _doMULTICAST_GATHER(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doMULTICAST_GATHER(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	const uint64_t nwid = pkt.at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID);
 	const unsigned int flags = pkt[ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS];
@@ -670,7 +670,7 @@ static inline bool _doMULTICAST_GATHER(IncomingPacket &pkt,const RuntimeEnvironm
 	return true;
 }
 
-static inline bool _doPUSH_DIRECT_PATHS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doPUSH_DIRECT_PATHS(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	const int64_t now = RR->node->now();
 
@@ -720,7 +720,7 @@ static inline bool _doPUSH_DIRECT_PATHS(IncomingPacket &pkt,const RuntimeEnviron
 	return true;
 }
 
-static inline bool _doUSER_MESSAGE(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
+ZT_ALWAYS_INLINE bool _doUSER_MESSAGE(IncomingPacket &pkt,const RuntimeEnvironment *const RR,void *const tPtr,const SharedPtr<Peer> &peer,const SharedPtr<Path> &path)
 {
 	if (likely(pkt.size() >= (ZT_PACKET_IDX_PAYLOAD + 8))) {
 		ZT_UserMessage um;

+ 3 - 3
node/InetAddress.cpp

@@ -376,7 +376,7 @@ bool InetAddress::operator<(const InetAddress &a) const
 InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
 {
 	InetAddress r;
-	sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
+	sockaddr_in6 *const sin6 = reinterpret_cast<sockaddr_in6 *>(&r);
 	sin6->sin6_family = AF_INET6;
 	sin6->sin6_addr.s6_addr[0] = 0xfe;
 	sin6->sin6_addr.s6_addr[1] = 0x80;
@@ -401,7 +401,7 @@ InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
 InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
 {
 	InetAddress r;
-	sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
+	sockaddr_in6 *const sin6 = reinterpret_cast<sockaddr_in6 *>(&r);
 	sin6->sin6_family = AF_INET6;
 	sin6->sin6_addr.s6_addr[0] = 0xfd;
 	sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 56U);
@@ -427,7 +427,7 @@ InetAddress InetAddress::makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress)
 {
 	nwid ^= (nwid >> 32U);
 	InetAddress r;
-	sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
+	sockaddr_in6 *const sin6 = reinterpret_cast<sockaddr_in6 *>(&r);
 	sin6->sin6_family = AF_INET6;
 	sin6->sin6_addr.s6_addr[0] = 0xfc;
 	sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 24U);

+ 69 - 72
node/InetAddress.hpp

@@ -42,6 +42,16 @@ namespace ZeroTier {
  */
 struct InetAddress : public sockaddr_storage
 {
+private:
+	template<typename SA>
+	ZT_ALWAYS_INLINE void copySockaddrToThis(const SA *sa)
+	{
+		memcpy(reinterpret_cast<void *>(this),sa,sizeof(SA));
+		if (sizeof(SA) < sizeof(InetAddress))
+			memset(reinterpret_cast<char *>(this) + sizeof(SA),0,sizeof(InetAddress) - sizeof(SA));
+	}
+
+public:
 	/**
 	 * Loopback IPv4 address (no port)
 	 */
@@ -79,116 +89,83 @@ struct InetAddress : public sockaddr_storage
 	// Hasher for unordered sets and maps in C++11
 	struct Hasher { ZT_ALWAYS_INLINE std::size_t operator()(const InetAddress &a) const { return (std::size_t)a.hashCode(); } };
 
-	ZT_ALWAYS_INLINE InetAddress() { memset(this,0,sizeof(InetAddress)); }
-	ZT_ALWAYS_INLINE InetAddress(const InetAddress &a) { memcpy(this,&a,sizeof(InetAddress)); }
-	ZT_ALWAYS_INLINE InetAddress(const InetAddress *a) { memcpy(this,a,sizeof(InetAddress)); }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_storage &ss) { *this = ss; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_storage *ss) { *this = ss; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr &sa) { *this = sa; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr *sa) { *this = sa; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in &sa) { *this = sa; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in *sa) { *this = sa; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in6 &sa) { *this = sa; }
-	ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in6 *sa) { *this = sa; }
+	ZT_ALWAYS_INLINE InetAddress() { memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress)); }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_storage &ss) { *this = ss; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_storage *ss) { *this = ss; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr &sa) { *this = sa; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr *sa) { *this = sa; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in &sa) { *this = sa; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in *sa) { *this = sa; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in6 &sa) { *this = sa; }
+	explicit ZT_ALWAYS_INLINE InetAddress(const struct sockaddr_in6 *sa) { *this = sa; }
 	ZT_ALWAYS_INLINE InetAddress(const void *ipBytes,unsigned int ipLen,unsigned int port) { this->set(ipBytes,ipLen,port); }
 	ZT_ALWAYS_INLINE InetAddress(const uint32_t ipv4,unsigned int port) { this->set(&ipv4,4,port); }
-	ZT_ALWAYS_INLINE InetAddress(const char *ipSlashPort) { this->fromString(ipSlashPort); }
-
-	ZT_ALWAYS_INLINE void clear() { memset(this,0,sizeof(InetAddress)); }
-
-	ZT_ALWAYS_INLINE InetAddress &operator=(const InetAddress &a)
-	{
-		if (&a != this)
-			memcpy(this,&a,sizeof(InetAddress));
-		return *this;
-	}
+	explicit ZT_ALWAYS_INLINE InetAddress(const char *ipSlashPort) { this->fromString(ipSlashPort); }
 
-	ZT_ALWAYS_INLINE InetAddress &operator=(const InetAddress *a)
-	{
-		if (a != this)
-			memcpy(this,a,sizeof(InetAddress));
-		return *this;
-	}
+	ZT_ALWAYS_INLINE void clear() { memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress)); }
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_storage &ss)
 	{
-		if (reinterpret_cast<const InetAddress *>(&ss) != this)
-			memcpy(this,&ss,sizeof(InetAddress));
+		memcpy(reinterpret_cast<void *>(this),&ss,sizeof(InetAddress));
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_storage *ss)
 	{
-		if (reinterpret_cast<const InetAddress *>(ss) != this)
-			memcpy(this,ss,sizeof(InetAddress));
+		if (ss)
+			memcpy(reinterpret_cast<void *>(this),ss,sizeof(InetAddress));
+		else memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in &sa)
 	{
-		if (reinterpret_cast<const InetAddress *>(&sa) != this) {
-			memset(this,0,sizeof(InetAddress));
-			memcpy(this,&sa,sizeof(struct sockaddr_in));
-		}
+		copySockaddrToThis(&sa);
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in *sa)
 	{
-		if (reinterpret_cast<const InetAddress *>(sa) != this) {
-			memset(this,0,sizeof(InetAddress));
-			memcpy(this,sa,sizeof(struct sockaddr_in));
-		}
+		if (sa)
+			copySockaddrToThis(sa);
+		else memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in6 &sa)
 	{
-		if (reinterpret_cast<const InetAddress *>(&sa) != this) {
-			memset(this,0,sizeof(InetAddress));
-			memcpy(this,&sa,sizeof(struct sockaddr_in6));
-		}
+		copySockaddrToThis(&sa);
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr_in6 *sa)
 	{
-		if (reinterpret_cast<const InetAddress *>(sa) != this) {
-			memset(this,0,sizeof(InetAddress));
-			memcpy(this,sa,sizeof(struct sockaddr_in6));
-		}
+		if (sa)
+			copySockaddrToThis(sa);
+		else memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr &sa)
 	{
-		if (reinterpret_cast<const InetAddress *>(&sa) != this) {
-			memset(this,0,sizeof(InetAddress));
-			switch(sa.sa_family) {
-				case AF_INET:
-					memcpy(this,&sa,sizeof(struct sockaddr_in));
-					break;
-				case AF_INET6:
-					memcpy(this,&sa,sizeof(struct sockaddr_in6));
-					break;
-			}
-		}
+		if (sa.sa_family == AF_INET)
+			copySockaddrToThis(reinterpret_cast<const sockaddr_in *>(&sa));
+		else if (sa.sa_family == AF_INET6)
+			copySockaddrToThis(reinterpret_cast<const sockaddr_in6 *>(&sa));
+		else memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
 		return *this;
 	}
 
 	ZT_ALWAYS_INLINE InetAddress &operator=(const struct sockaddr *sa)
 	{
-		if (reinterpret_cast<const InetAddress *>(sa) != this) {
-			memset(this,0,sizeof(InetAddress));
-			switch(sa->sa_family) {
-				case AF_INET:
-					memcpy(this,sa,sizeof(struct sockaddr_in));
-					break;
-				case AF_INET6:
-					memcpy(this,sa,sizeof(struct sockaddr_in6));
-					break;
-			}
+		if (sa) {
+			if (sa->sa_family == AF_INET)
+				copySockaddrToThis(reinterpret_cast<const sockaddr_in *>(sa));
+			else if (sa->sa_family == AF_INET6)
+				copySockaddrToThis(reinterpret_cast<const sockaddr_in6 *>(sa));
+			return *this;
 		}
+		memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
 		return *this;
 	}
 
@@ -517,6 +494,9 @@ struct InetAddress : public sockaddr_storage
 	}
 	inline int unmarshal(const uint8_t *restrict data,const int len)
 	{
+#ifdef ZT_NO_TYPE_PUNNING
+		uint16_t tmp;
+#endif
 		if (len <= 0)
 			return -1;
 		switch(data[0]) {
@@ -525,22 +505,32 @@ struct InetAddress : public sockaddr_storage
 			case 4:
 				if (len < 7)
 					return -1;
-				memset(this,0,sizeof(InetAddress));
+				memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
 				reinterpret_cast<sockaddr_in *>(this)->sin_family = AF_INET;
 				reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[0] = data[1];
 				reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[1] = data[2];
 				reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[2] = data[3];
 				reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[3] = data[4];
-				reinterpret_cast<sockaddr_in *>(this)->sin_port = Utils::hton((((uint16_t)data[5]) << 8) | (uint16_t)data[6]);
+#ifdef ZT_NO_TYPE_PUNNING
+				memcpy(&tmp,data + 5,2);
+				reinterpret_cast<sockaddr_in *>(this)->sin_port = tmp;
+#else
+				reinterpret_cast<sockaddr_in *>(this)->sin_port = *((const uint16_t *)(data + 5));
+#endif
 				return 7;
 			case 6:
 				if (len < 19)
 					return -1;
-				memset(this,0,sizeof(InetAddress));
+				memset(reinterpret_cast<void *>(this),0,sizeof(InetAddress));
 				reinterpret_cast<sockaddr_in6 *>(this)->sin6_family = AF_INET6;
 				for(int i=0;i<16;i++)
 					(reinterpret_cast<sockaddr_in6 *>(this)->sin6_addr.s6_addr)[i] = data[i+1];
-				reinterpret_cast<sockaddr_in6 *>(this)->sin6_port = Utils::hton((((uint16_t)data[17]) << 8) | (uint16_t)data[18]);
+#ifdef ZT_NO_TYPE_PUNNING
+				memcpy(&tmp,data + 17,2);
+				reinterpret_cast<sockaddr_in *>(this)->sin_port = tmp;
+#else
+				reinterpret_cast<sockaddr_in *>(this)->sin_port = *((const uint16_t *)(data + 17));
+#endif
 				return 19;
 			default:
 				return -1;
@@ -666,6 +656,13 @@ struct InetAddress : public sockaddr_storage
 	static InetAddress makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress);
 };
 
+InetAddress *asInetAddress(sockaddr_in *p) { return reinterpret_cast<InetAddress *>(p); }
+InetAddress *asInetAddress(sockaddr_in6 *p) { return reinterpret_cast<InetAddress *>(p); }
+InetAddress *asInetAddress(sockaddr *p) { return reinterpret_cast<InetAddress *>(p); }
+const InetAddress *asInetAddress(const sockaddr_in *p) { return reinterpret_cast<const InetAddress *>(p); }
+const InetAddress *asInetAddress(const sockaddr_in6 *p) { return reinterpret_cast<const InetAddress *>(p); }
+const InetAddress *asInetAddress(const sockaddr *p) { return reinterpret_cast<const InetAddress *>(p); }
+
 } // namespace ZeroTier
 
 #endif

+ 106 - 0
node/Locator.cpp

@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+/****/
+
+#include "Locator.hpp"
+
+namespace ZeroTier {
+
+bool Locator::sign(const int64_t ts,const Identity &id)
+{
+	uint8_t signData[ZT_LOCATOR_MARSHAL_SIZE_MAX];
+	if (!id.hasPrivate())
+		return false;
+	_ts = ts;
+	if (_endpointCount > 0)
+		std::sort(_at,_at + _endpointCount);
+	const unsigned int signLen = marshal(signData,true);
+	_signatureLength = id.sign(signData, signLen, _signature, sizeof(_signature));
+	return (_signatureLength > 0);
+}
+
+bool Locator::verify(const Identity &id) const
+{
+	if ((_ts == 0)||(_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE))
+		return false;
+	uint8_t signData[ZT_LOCATOR_MARSHAL_SIZE_MAX];
+	const unsigned int signLen = marshal(signData,true);
+	return id.verify(signData,signLen,_signature,_signatureLength);
+}
+
+int Locator::marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool excludeSignature = false) const
+{
+	if ((_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE))
+		return -1;
+
+	Utils::putUInt64(data,(uint64_t)_ts);
+	int p = 8;
+
+	data[p++] = (uint8_t)(_endpointCount >> 8U);
+	data[p++] = (uint8_t)_endpointCount;
+	for(unsigned int i=0;i<_endpointCount;++i) {
+		int tmp = _at[i].marshal(data + p);
+		if (tmp < 0)
+			return -1;
+		p += tmp;
+	}
+
+	if (!excludeSignature) {
+		data[p++] = (uint8_t)(_signatureLength >> 8U);
+		data[p++] = (uint8_t)_signatureLength;
+		memcpy(data + p,_signature,_signatureLength);
+		p += (int)_signatureLength;
+	}
+
+	return p;
+}
+
+int Locator::unmarshal(const uint8_t *restrict data,const int len)
+{
+	if (len <= (8 + 48))
+		return -1;
+
+	_ts = (int64_t)Utils::readUInt64(data);
+	int p = 8;
+
+	if ((p + 2) > len)
+		return -1;
+	unsigned int ec = (int)data[p++];
+	ec <<= 8U;
+	ec |= data[p++];
+	if (ec > ZT_LOCATOR_MAX_ENDPOINTS)
+		return -1;
+	_endpointCount = ec;
+	for(int i=0;i<ec;++i) {
+		int tmp = _at[i].unmarshal(data + p,len - p);
+		if (tmp < 0)
+			return -1;
+		p += tmp;
+	}
+
+	if ((p + 2) > len)
+		return -1;
+	unsigned int sl = data[p++];
+	sl <<= 8U;
+	sl |= data[p++];
+	if (sl > ZT_SIGNATURE_BUFFER_SIZE)
+		return -1;
+	_signatureLength = sl;
+	if ((p + sl) > len)
+		return -1;
+	memcpy(_signature,data + p,sl);
+	p += (int)sl;
+
+	return p;
+}
+
+} // namespace ZeroTier

+ 13 - 90
node/Locator.hpp

@@ -54,38 +54,32 @@ public:
 	ZT_ALWAYS_INLINE bool isSigned() const { return (_signatureLength > 0); }
 
 	/**
-	 * Add an endpoint to this locator if not already present
+	 * Add an endpoint to this locator
+	 *
+	 * This doesn't check for the presence of the endpoint, so take
+	 * care not to add duplicates.
 	 *
 	 * @param ep Endpoint to add
 	 * @return True if endpoint was added (or already present), false if locator is full
 	 */
-	inline bool add(const Endpoint &ep)
+	ZT_ALWAYS_INLINE bool add(const Endpoint &ep)
 	{
 		if (_endpointCount >= ZT_LOCATOR_MAX_ENDPOINTS)
 			return false;
-		if (!std::binary_search(_at,_at + _endpointCount,ep)) {
-			_at[_endpointCount++] = ep;
-			std::sort(_at,_at + _endpointCount);
-		}
+		_at[_endpointCount++] = ep;
 		return true;
 	}
 
 	/**
 	 * Sign this locator
 	 *
+	 * This sets timestamp, sorts endpoints so that the same set of endpoints
+	 * will always produce the same locator, and signs.
+	 *
 	 * @param id Identity that includes private key
 	 * @return True if signature successful
 	 */
-	inline bool sign(const int64_t ts,const Identity &id)
-	{
-		uint8_t signData[ZT_LOCATOR_MARSHAL_SIZE_MAX];
-		if (!id.hasPrivate())
-			return false;
-		_ts = ts;
-		const unsigned int signLen = marshal(signData,true);
-		_signatureLength = id.sign(signData, signLen, _signature, sizeof(_signature));
-		return (_signatureLength > 0);
-	}
+	bool sign(int64_t ts,const Identity &id);
 
 	/**
 	 * Verify this Locator's validity and signature
@@ -93,84 +87,13 @@ public:
 	 * @param id Identity corresponding to hash
 	 * @return True if valid and signature checks out
 	 */
-	inline bool verify(const Identity &id) const
-	{
-		if ((_ts == 0)||(_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE))
-			return false;
-		uint8_t signData[ZT_LOCATOR_MARSHAL_SIZE_MAX];
-		const unsigned int signLen = marshal(signData,true);
-		return id.verify(signData,signLen,_signature,_signatureLength);
-	}
+	bool verify(const Identity &id) const;
 
 	explicit ZT_ALWAYS_INLINE operator bool() const { return (_ts != 0); }
 
-	// Marshal interface ///////////////////////////////////////////////////////
 	static ZT_ALWAYS_INLINE int marshalSizeMax() { return ZT_LOCATOR_MARSHAL_SIZE_MAX; }
-	inline int marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool excludeSignature = false) const
-	{
-		if ((_endpointCount > ZT_LOCATOR_MAX_ENDPOINTS)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE))
-			return -1;
-
-		Utils::putUInt64(data,(uint64_t)_ts);
-		int p = 8;
-
-		data[p++] = (uint8_t)(_endpointCount >> 8U);
-		data[p++] = (uint8_t)_endpointCount;
-		for(unsigned int i=0;i<_endpointCount;++i) {
-			int tmp = _at[i].marshal(data + p);
-			if (tmp < 0)
-				return -1;
-			p += tmp;
-		}
-
-		if (!excludeSignature) {
-			data[p++] = (uint8_t)(_signatureLength >> 8U);
-			data[p++] = (uint8_t)_signatureLength;
-			memcpy(data + p,_signature,_signatureLength);
-			p += (int)_signatureLength;
-		}
-
-		return p;
-	}
-	inline int unmarshal(const uint8_t *restrict data,const int len)
-	{
-		if (len <= (8 + 48))
-			return -1;
-
-		_ts = (int64_t)Utils::readUInt64(data);
-		int p = 8;
-
-		if ((p + 2) > len)
-			return -1;
-		unsigned int ec = (int)data[p++];
-		ec <<= 8U;
-		ec |= data[p++];
-		if (ec > ZT_LOCATOR_MAX_ENDPOINTS)
-			return -1;
-		_endpointCount = ec;
-		for(int i=0;i<ec;++i) {
-			int tmp = _at[i].unmarshal(data + p,len - p);
-			if (tmp < 0)
-				return -1;
-			p += tmp;
-		}
-
-		if ((p + 2) > len)
-			return -1;
-		unsigned int sl = data[p++];
-		sl <<= 8U;
-		sl |= data[p++];
-		if (sl > ZT_SIGNATURE_BUFFER_SIZE)
-			return -1;
-		_signatureLength = sl;
-		if ((p + sl) > len)
-			return -1;
-		memcpy(_signature,data + p,sl);
-		p += (int)sl;
-
-		return p;
-	}
-	////////////////////////////////////////////////////////////////////////////
+	int marshal(uint8_t data[ZT_LOCATOR_MARSHAL_SIZE_MAX],const bool excludeSignature = false) const;
+	int unmarshal(const uint8_t *restrict data,const int len);
 
 private:
 	int64_t _ts;

+ 28 - 13
node/Node.cpp

@@ -371,6 +371,7 @@ uint64_t Node::address() const
 void Node::status(ZT_NodeStatus *status) const
 {
 	status->address = RR->identity.address().toInt();
+	status->identity = reinterpret_cast<const ZT_Identity *>(&RR->identity);
 	status->publicIdentity = RR->publicIdentityStr;
 	status->secretIdentity = RR->secretIdentityStr;
 	status->online = _online ? 1 : 0;
@@ -384,34 +385,38 @@ ZT_PeerList *Node::peers() const
 	RR->topology->getAllPeers(peers);
 	std::sort(peers.begin(),peers.end(),_sortPeerPtrsByAddress());
 
-	char *buf = (char *)::malloc(sizeof(ZT_PeerList) + (sizeof(ZT_Peer) * peers.size()));
+	char *buf = (char *)::malloc(sizeof(ZT_PeerList) + (sizeof(ZT_Peer) * peers.size()) + (sizeof(Identity) * peers.size()));
 	if (!buf)
 		return (ZT_PeerList *)0;
 	ZT_PeerList *pl = (ZT_PeerList *)buf;
 	pl->peers = (ZT_Peer *)(buf + sizeof(ZT_PeerList));
+	Identity *identities = (Identity *)(buf + sizeof(ZT_PeerList) + (sizeof(ZT_Peer) * peers.size()));
 
+	const int64_t now = _now;
 	pl->peerCount = 0;
 	for(std::vector< SharedPtr<Peer> >::iterator pi(peers.begin());pi!=peers.end();++pi) {
-		ZT_Peer *p = &(pl->peers[pl->peerCount++]);
+		ZT_Peer *p = &(pl->peers[pl->peerCount]);
+
 		p->address = (*pi)->address().toInt();
+		identities[pl->peerCount] = (*pi)->identity(); // need to make a copy in case peer gets deleted
+		p->identity = &identities[pl->peerCount];
 		p->hadAggregateLink = 0;
 		if ((*pi)->remoteVersionKnown()) {
-			p->versionMajor = (*pi)->remoteVersionMajor();
-			p->versionMinor = (*pi)->remoteVersionMinor();
-			p->versionRev = (*pi)->remoteVersionRevision();
+			p->versionMajor = (int)(*pi)->remoteVersionMajor();
+			p->versionMinor = (int)(*pi)->remoteVersionMinor();
+			p->versionRev = (int)(*pi)->remoteVersionRevision();
 		} else {
 			p->versionMajor = -1;
 			p->versionMinor = -1;
 			p->versionRev = -1;
 		}
-		p->latency = (*pi)->latency(_now);
+		p->latency = (int)(*pi)->latency(now);
 		if (p->latency >= 0xffff)
 			p->latency = -1;
-		p->role = RR->topology->isRoot((*pi)->identity()) ? ZT_PEER_ROLE_PLANET : ZT_PEER_ROLE_LEAF;
+		p->role = RR->topology->isRoot((*pi)->identity()) ? ZT_PEER_ROLE_ROOT : ZT_PEER_ROLE_LEAF;
 
-		const int64_t now = _now;
-		std::vector< SharedPtr<Path> > paths((*pi)->paths(_now));
-		SharedPtr<Path> bestp((*pi)->getAppropriatePath(_now,false));
+		std::vector< SharedPtr<Path> > paths((*pi)->paths(now));
+		SharedPtr<Path> bestp((*pi)->getAppropriatePath(now,false));
 		p->hadAggregateLink |= (*pi)->hasAggregateLink();
 		p->pathCount = 0;
 		for(std::vector< SharedPtr<Path> >::iterator path(paths.begin());path!=paths.end();++path) {
@@ -434,6 +439,8 @@ ZT_PeerList *Node::peers() const
 
 			++p->pathCount;
 		}
+
+		++pl->peerCount;
 	}
 
 	return pl;
@@ -553,9 +560,17 @@ bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,cons
 
 bool Node::externalPathLookup(void *tPtr,const Identity &id,int family,InetAddress &addr)
 {
-	char idStr[ZT_IDENTITY_STRING_BUFFER_LENGTH];
-	id.toString(false,idStr);
-	return (_cb.pathLookupFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,id.address().toInt(),idStr,family,reinterpret_cast<sockaddr_storage *>(&addr)) == ZT_RESULT_OK);
+	if (_cb.pathLookupFunction) {
+		return (_cb.pathLookupFunction(
+			reinterpret_cast<ZT_Node *>(this),
+			_uPtr,
+			tPtr,
+			id.address().toInt(),
+			reinterpret_cast<const ZT_Identity *>(&id),
+			family,
+			reinterpret_cast<sockaddr_storage *>(&addr)) == ZT_RESULT_OK);
+	}
+	return false;
 }
 
 ZT_ResultCode Node::setPhysicalPathConfiguration(const struct sockaddr_storage *pathNetwork, const ZT_PhysicalPathConfiguration *pathConfig)