diff --git a/go/cmd/zerotier/cli/status.go b/go/cmd/zerotier/cli/status.go index c7d0ca59d..ec1fa6cbc 100644 --- a/go/cmd/zerotier/cli/status.go +++ b/go/cmd/zerotier/cli/status.go @@ -34,7 +34,11 @@ func Status(basePath, authToken string, args []string, jsonOutput bool) { } fmt.Printf("%.10x: %s %s\n", uint64(status.Address), online, status.Version) fmt.Printf("\tidentity:\t%s\n", status.Identity.String()) - fmt.Printf("\tports:\tprimary: %d secondary: %d\n", status.Config.Settings.PrimaryPort, status.Config.Settings.SecondaryPort) + if status.Config.Settings.SecondaryPort > 0 && status.Config.Settings.SecondaryPort < 65536 { + fmt.Printf("\tports:\t%d %d\n", status.Config.Settings.PrimaryPort, status.Config.Settings.SecondaryPort) + } else { + fmt.Printf("\tports:\t%d\n", status.Config.Settings.PrimaryPort) + } fmt.Printf("\tport search:\t%s\n", enabledDisabled(status.Config.Settings.PortSearch)) fmt.Printf("\tport mapping (uPnP/NAT-PMP):\t%s\n", enabledDisabled(status.Config.Settings.PortMapping)) fmt.Printf("\tblacklisted interface prefixes:\t") diff --git a/go/cmd/zerotier/zerotier.go b/go/cmd/zerotier/zerotier.go index 0c12c8a84..720c2f27d 100644 --- a/go/cmd/zerotier/zerotier.go +++ b/go/cmd/zerotier/zerotier.go @@ -134,7 +134,7 @@ func main() { case "peers", "listpeers": authTokenRequired(authToken) cli.Peers(basePath, authToken, cmdArgs, *jflag) - case "roots", "listroots", "listmoons": + case "roots", "listroots": authTokenRequired(authToken) cli.Roots(basePath, authToken, cmdArgs, *jflag) case "addroot": diff --git a/go/pkg/zerotier/api.go b/go/pkg/zerotier/api.go index 6b57fb0e1..32400317a 100644 --- a/go/pkg/zerotier/api.go +++ b/go/pkg/zerotier/api.go @@ -226,6 +226,12 @@ func apiCheckAuth(out http.ResponseWriter, req *http.Request, token string) bool return false } +type peerMutableFields struct { + Identity *Identity `json:"identity"` + Role *int `json:"role"` + Bootstrap *InetAddress `json:"bootstrap,omitempty"` +} + // createAPIServer creates and starts an HTTP server for a given node func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, error) { // Read authorization token, automatically generating one if it's missing @@ -360,12 +366,35 @@ func createAPIServer(basePath string, node *Node) (*http.Server, *http.Server, e } } + // Right now POST/PUT is only used with peers to add or remove root servers. if req.Method == http.MethodPost || req.Method == http.MethodPut { if queriedID == 0 { _ = apiSendObj(out, req, http.StatusNotFound, &APIErr{"peer not found"}) return } - } else if req.Method == http.MethodGet || req.Method == http.MethodHead { + var peerChanges peerMutableFields + if apiReadObj(out, req, &peerChanges) == nil { + if peerChanges.Role != nil || peerChanges.Bootstrap != nil { + peers := node.Peers() + for _, p := range peers { + if p.Address == queriedID && (peerChanges.Identity == nil || peerChanges.Identity.Equals(p.Identity)) { + if peerChanges.Role != nil && *peerChanges.Role != p.Role { + if *peerChanges.Role == PeerRoleRoot { + _ = node.AddRoot(p.Identity, peerChanges.Bootstrap) + } else { + node.RemoveRoot(p.Identity) + } + } + break + } + } + } + } else { + return + } + } + + if req.Method == http.MethodGet || req.Method == http.MethodHead || req.Method == http.MethodPost || req.Method == http.MethodPut { peers := node.Peers() if queriedID != 0 { for _, p := range peers { diff --git a/go/pkg/zerotier/identity.go b/go/pkg/zerotier/identity.go index 6cca4e162..65ee53bc5 100644 --- a/go/pkg/zerotier/identity.go +++ b/go/pkg/zerotier/identity.go @@ -17,6 +17,7 @@ package zerotier import "C" import ( + "bytes" "encoding/hex" "encoding/json" "fmt" @@ -51,7 +52,7 @@ type Identity struct { func identityFinalizer(obj interface{}) { id, _ := obj.(*Identity) if id != nil && uintptr(id.cid) != 0 { - defer C.ZT_Identity_delete(id.cid) + C.ZT_Identity_delete(id.cid) } } @@ -72,12 +73,25 @@ func newIdentityFromCIdentity(cid unsafe.Pointer) (*Identity, error) { } id.cid = cid - runtime.SetFinalizer(id, identityFinalizer) return id, nil } +// cIdentity returns a pointer to the core ZT_Identity instance or nil/0 on error. +func (id *Identity) cIdentity() unsafe.Pointer { + if uintptr(id.cid) == 0 { + idCStr := C.CString(id.String()) + defer C.free(unsafe.Pointer(idCStr)) + id.cid = C.ZT_Identity_fromString(idCStr) + if uintptr(id.cid) == 0 { + return nil + } + runtime.SetFinalizer(id, identityFinalizer) + } + return id.cid +} + // NewIdentity generates a new identity of the selected type func NewIdentity(identityType int) (*Identity, error) { return newIdentityFromCIdentity(C.ZT_Identity_new(C.enum_ZT_Identity_Type(identityType))) @@ -140,8 +154,6 @@ func NewIdentityFromString(s string) (*Identity, error) { } - runtime.SetFinalizer(id, identityFinalizer) - return id, nil } @@ -184,26 +196,18 @@ func (id *Identity) String() string { // LocallyValidate performs local self-validation of this identity func (id *Identity) LocallyValidate() bool { + id.cIdentity() if uintptr(id.cid) == 0 { - idCStr := C.CString(id.String()) - defer C.free(unsafe.Pointer(idCStr)) - id.cid = C.ZT_Identity_fromString(idCStr) - if uintptr(id.cid) == 0 { - return false - } + return false } return C.ZT_Identity_validate(id.cid) != 0 } // Sign signs a message with this identity func (id *Identity) Sign(msg []byte) ([]byte, error) { + id.cIdentity() if uintptr(id.cid) == 0 { - idCStr := C.CString(id.String()) - defer C.free(unsafe.Pointer(idCStr)) - id.cid = C.ZT_Identity_fromString(idCStr) - if uintptr(id.cid) == 0 { - return nil, ErrInvalidKey - } + return nil, ErrInvalidKey } var dataP unsafe.Pointer @@ -225,13 +229,9 @@ func (id *Identity) Verify(msg, sig []byte) bool { return false } + id.cIdentity() if uintptr(id.cid) == 0 { - idCStr := C.CString(id.String()) - defer C.free(unsafe.Pointer(idCStr)) - id.cid = C.ZT_Identity_fromString(idCStr) - if uintptr(id.cid) == 0 { - return false - } + return false } var dataP unsafe.Pointer @@ -241,6 +241,17 @@ func (id *Identity) Verify(msg, sig []byte) bool { return C.ZT_Identity_verify(id.cid, dataP, C.uint(len(msg)), unsafe.Pointer(&sig[0]), C.uint(len(sig))) != 0 } +// Equals performs a deep equality test between this and another identity +func (id *Identity) Equals(id2 *Identity) bool { + if id2 == nil { + return id == nil + } + if id == nil { + return false + } + return id.address == id2.address && id.idtype == id2.idtype && bytes.Equal(id.publicKey, id2.publicKey) && bytes.Equal(id.privateKey, id2.privateKey) +} + // MarshalJSON marshals this Identity in its string format (private key is never included) func (id *Identity) MarshalJSON() ([]byte, error) { return []byte("\"" + id.String() + "\""), nil diff --git a/go/pkg/zerotier/inetaddress.go b/go/pkg/zerotier/inetaddress.go index bda7bc8ce..88e77fa94 100644 --- a/go/pkg/zerotier/inetaddress.go +++ b/go/pkg/zerotier/inetaddress.go @@ -74,8 +74,12 @@ func sockaddrStorageToUDPAddr2(ss unsafe.Pointer) *net.UDPAddr { return sockaddrStorageToUDPAddr((*C.struct_sockaddr_storage)(ss)) } -func makeSockaddrStorage(ip net.IP, port int, ss *C.struct_sockaddr_storage) bool { +func zeroSockaddrStorage(ss *C.struct_sockaddr_storage) { C.memset(unsafe.Pointer(ss), 0, C.sizeof_struct_sockaddr_storage) +} + +func makeSockaddrStorage(ip net.IP, port int, ss *C.struct_sockaddr_storage) bool { + zeroSockaddrStorage(ss) if len(ip) == 4 { sa4 := (*C.struct_sockaddr_in)(unsafe.Pointer(ss)) sa4.sin_family = syscall.AF_INET diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index 6a10c0031..0e61511d7 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -542,6 +542,32 @@ func (n *Node) Leave(nwid NetworkID) error { return nil } +// AddRoot adds a root server with an optional bootstrap address for establishing first contact. +// If you're already using roots backed by proper global LF data stores the bootstrap address may +// be unnecessary as your node can probably find the new root automatically. +func (n *Node) AddRoot(id *Identity, bootstrap *InetAddress) error { + if id == nil { + return ErrInvalidParameter + } + var cBootstrap C.struct_sockaddr_storage + if bootstrap != nil { + makeSockaddrStorage(bootstrap.IP, bootstrap.Port, &cBootstrap) + } else { + zeroSockaddrStorage(&cBootstrap) + } + C.ZT_Node_addRoot(n.zn, nil, id.cIdentity(), &cBootstrap) + return nil +} + +// RemoveRoot removes a root server for this node. +// This doesn't instantly close paths to the given peer or forget about it. It just +// demotes it to a normal peer. +func (n *Node) RemoveRoot(id *Identity) { + if id != nil { + C.ZT_Node_removeRoot(n.zn, nil, id.cIdentity()) + } +} + // GetNetwork looks up a network by ID or returns nil if not joined func (n *Node) GetNetwork(nwid NetworkID) *Network { n.networksLock.RLock() @@ -883,13 +909,10 @@ func goZtEvent(gn unsafe.Pointer, eventType C.int, data unsafe.Pointer) { 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))) } } diff --git a/node/Buf.cpp b/node/Buf.cpp index 370b56506..f5ef31fc7 100644 --- a/node/Buf.cpp +++ b/node/Buf.cpp @@ -13,104 +13,12 @@ #include "Buf.hpp" -#ifndef __GNUC__ -#include -#endif - namespace ZeroTier { #ifdef __GNUC__ -static uintptr_t s_pool = 0; +uintptr_t Buf_pool = 0; #else -static std::atomic s_pool(0); +std::atomic Buf_pool(0); #endif -void Buf::operator delete(void *ptr,std::size_t sz) -{ - if (ptr) { - uintptr_t bb; - const uintptr_t locked = ~((uintptr_t)0); - for (;;) { -#ifdef __GNUC__ - bb = __sync_fetch_and_or(&s_pool,locked); // get value of s_pool and "lock" by filling with all 1's -#else - bb = s_pool.fetch_or(locked); -#endif - if (bb != locked) - break; - } - - ((Buf *)ptr)->__nextInPool = bb; -#ifdef __GNUC__ - __sync_fetch_and_and(&s_pool,(uintptr_t)ptr); -#else - s_pool.store((uintptr_t)ptr); -#endif - } -} - -SharedPtr Buf::get() -{ - uintptr_t bb; - const uintptr_t locked = ~((uintptr_t)0); - for (;;) { -#ifdef __GNUC__ - bb = __sync_fetch_and_or(&s_pool,locked); // get value of s_pool and "lock" by filling with all 1's -#else - bb = s_pool.fetch_or(locked); -#endif - if (bb != locked) - break; - } - - Buf *b; - if (bb == 0) { -#ifdef __GNUC__ - __sync_fetch_and_and(&s_pool,bb); -#else - s_pool.store(bb); -#endif - b = (Buf *)malloc(sizeof(Buf)); - if (!b) - return SharedPtr(); - } else { - b = (Buf *)bb; -#ifdef __GNUC__ - __sync_fetch_and_and(&s_pool,b->__nextInPool); -#else - s_pool.store(b->__nextInPool); -#endif - } - - b->__refCount.zero(); - return SharedPtr(b); -} - -void Buf::freePool() -{ - uintptr_t bb; - const uintptr_t locked = ~((uintptr_t)0); - for (;;) { -#ifdef __GNUC__ - bb = __sync_fetch_and_or(&s_pool,locked); // get value of s_pool and "lock" by filling with all 1's -#else - bb = s_pool.fetch_or(locked); -#endif - if (bb != locked) - break; - } - -#ifdef __GNUC__ - __sync_fetch_and_and(&s_pool,(uintptr_t)0); -#else - s_pool.store((uintptr_t)0); -#endif - - while (bb != 0) { - uintptr_t next = ((Buf *)bb)->__nextInPool; - free((void *)bb); - bb = next; - } -} - } // namespace ZeroTier diff --git a/node/Buf.hpp b/node/Buf.hpp index e727a7f2c..99408751d 100644 --- a/node/Buf.hpp +++ b/node/Buf.hpp @@ -24,6 +24,10 @@ #include #include +#ifndef __GNUC__ +#include +#endif + // Buffers are 16384 bytes in size because this is the smallest size that can hold any packet // and is a power of two. It needs to be a power of two because masking is significantly faster // than integer division modulus. @@ -32,6 +36,12 @@ namespace ZeroTier { +#ifdef __GNUC__ +extern uintptr_t Buf_pool; +#else +extern std::atomic Buf_pool; +#endif + /** * Buffer and methods for branch-free bounds-checked data assembly and parsing * @@ -62,28 +72,93 @@ namespace ZeroTier { * reads to ensure that overflow did not occur. * * Buf uses a lock-free pool for extremely fast allocation and deallocation. + * + * Buf can optionally take a template parameter that will be placed in the 'data' + * union as 'fields.' This must be a basic plain data type and must be no larger than + * ZT_BUF_MEM_SIZE. It's typically a packed struct. + * + * @tparam U Type to overlap with data bytes in data union (can't be larger than ZT_BUF_MEM_SIZE) */ +template class Buf { - friend class SharedPtr; + friend class SharedPtr< Buf >; private: // Direct construction isn't allowed; use get(). ZT_ALWAYS_INLINE Buf() {} - ZT_ALWAYS_INLINE Buf(const Buf &b) - {} + template + ZT_ALWAYS_INLINE Buf(const Buf &b) + { memcpy(data.bytes,b.data.bytes,ZT_BUF_MEM_SIZE); } public: - static void operator delete(void *ptr,std::size_t sz); + static void operator delete(void *ptr,std::size_t sz) + { + if (ptr) { + uintptr_t bb; + const uintptr_t locked = ~((uintptr_t)0); + for (;;) { +#ifdef __GNUC__ + bb = __sync_fetch_and_or(&Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's +#else + bb = s_pool.fetch_or(locked); +#endif + if (bb != locked) + break; + } + + ((Buf *)ptr)->__nextInPool = bb; +#ifdef __GNUC__ + __sync_fetch_and_and(&Buf_pool,(uintptr_t)ptr); +#else + s_pool.store((uintptr_t)ptr); +#endif + } + } /** * Get obtains a buffer from the pool or allocates a new buffer if the pool is empty * * @return Buffer */ - static SharedPtr get(); + static ZT_ALWAYS_INLINE SharedPtr< Buf > get() + { + uintptr_t bb; + const uintptr_t locked = ~((uintptr_t)0); + for (;;) { +#ifdef __GNUC__ + bb = __sync_fetch_and_or(&Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's +#else + bb = s_pool.fetch_or(locked); +#endif + if (bb != locked) + break; + } + + Buf *b; + if (bb == 0) { +#ifdef __GNUC__ + __sync_fetch_and_and(&Buf_pool,bb); +#else + s_pool.store(bb); +#endif + b = (Buf *)malloc(sizeof(Buf)); + if (!b) + return SharedPtr(); + } else { + b = (Buf *)bb; +#ifdef __GNUC__ + __sync_fetch_and_and(&Buf_pool,b->__nextInPool); +#else + s_pool.store(b->__nextInPool); +#endif + } + + b->__refCount.zero(); + return SharedPtr(b); + } /** * Free buffers in the pool @@ -92,7 +167,32 @@ public: * and outstanding buffers will still be returned to the pool. This just * frees buffers currently held in reserve. */ - static void freePool(); + static inline void freePool() + { + uintptr_t bb; + const uintptr_t locked = ~((uintptr_t)0); + for (;;) { +#ifdef __GNUC__ + bb = __sync_fetch_and_or(&Buf_pool,locked); // get value of s_pool and "lock" by filling with all 1's +#else + bb = s_pool.fetch_or(locked); +#endif + if (bb != locked) + break; + } + +#ifdef __GNUC__ + __sync_fetch_and_and(&Buf_pool,(uintptr_t)0); +#else + s_pool.store((uintptr_t)0); +#endif + + while (bb != 0) { + uintptr_t next = ((Buf *)bb)->__nextInPool; + free((void *)bb); + bb = next; + } + } /** * Check for overflow beyond the size of the buffer @@ -119,9 +219,19 @@ public: static ZT_ALWAYS_INLINE bool readOverflow(const int &ii,const unsigned int size) { return ((ii - (int)size) > 0); } - //////////////////////////////////////////////////////////////////////////// - // Read methods - //////////////////////////////////////////////////////////////////////////// + template + ZT_ALWAYS_INLINE Buf &operator=(const Buf &b) const + { + memcpy(data.bytes,b.data.bytes,ZT_BUF_MEM_SIZE); + return *this; + } + + /** + * Zero memory + * + * For performance reasons Buf does not do this on every get(). + */ + ZT_ALWAYS_INLINE void clear() { memset(data.bytes,0,ZT_BUF_MEM_SIZE); } /** * Read a byte @@ -132,7 +242,7 @@ public: ZT_ALWAYS_INLINE uint8_t rI8(int &ii) const { const unsigned int s = (unsigned int)ii++; - return data[s & ZT_BUF_MEM_MASK]; + return data.bytes[s & ZT_BUF_MEM_MASK]; } /** @@ -147,10 +257,10 @@ public: ii += 2; #ifdef ZT_NO_UNALIGNED_ACCESS return ( - ((uint16_t)data[s] << 8U) | - (uint16_t)data[s + 1]); + ((uint16_t)data.bytes[s] << 8U) | + (uint16_t)data.bytes[s + 1]); #else - return Utils::ntoh(*reinterpret_cast(data + s)); + return Utils::ntoh(*reinterpret_cast(data.bytes + s)); #endif } @@ -166,12 +276,12 @@ public: ii += 4; #ifdef ZT_NO_UNALIGNED_ACCESS return ( - ((uint32_t)data[s] << 24U) | - ((uint32_t)data[s + 1] << 16U) | - ((uint32_t)data[s + 2] << 8U) | - (uint32_t)data[s + 3]); + ((uint32_t)data.bytes[s] << 24U) | + ((uint32_t)data.bytes[s + 1] << 16U) | + ((uint32_t)data.bytes[s + 2] << 8U) | + (uint32_t)data.bytes[s + 3]); #else - return Utils::ntoh(*reinterpret_cast(data + s)); + return Utils::ntoh(*reinterpret_cast(data.bytes + s)); #endif } @@ -187,16 +297,16 @@ public: ii += 8; #ifdef ZT_NO_UNALIGNED_ACCESS return ( - ((uint64_t)data[s] << 56U) | - ((uint64_t)data[s + 1] << 48U) | - ((uint64_t)data[s + 2] << 40U) | - ((uint64_t)data[s + 3] << 32U) | - ((uint64_t)data[s + 4] << 24U) | - ((uint64_t)data[s + 5] << 16U) | - ((uint64_t)data[s + 6] << 8U) | - (uint64_t)data[s + 7]); + ((uint64_t)data.bytes[s] << 56U) | + ((uint64_t)data.bytes[s + 1] << 48U) | + ((uint64_t)data.bytes[s + 2] << 40U) | + ((uint64_t)data.bytes[s + 3] << 32U) | + ((uint64_t)data.bytes[s + 4] << 24U) | + ((uint64_t)data.bytes[s + 5] << 16U) | + ((uint64_t)data.bytes[s + 6] << 8U) | + (uint64_t)data.bytes[s + 7]); #else - return Utils::ntoh(*reinterpret_cast(data + s)); + return Utils::ntoh(*reinterpret_cast(data.bytes + s)); #endif } @@ -219,7 +329,7 @@ public: ZT_ALWAYS_INLINE int rO(int &ii,T &obj) const { if (ii < ZT_BUF_MEM_SIZE) { - int ms = obj.unmarshal(data + ii,ZT_BUF_MEM_SIZE - ii); + int ms = obj.unmarshal(data.bytes + ii,ZT_BUF_MEM_SIZE - ii); if (ms > 0) ii += ms; return ms; @@ -240,10 +350,10 @@ public: */ ZT_ALWAYS_INLINE char *rS(int &ii,char *const buf,const unsigned int bufSize) const { - const char *const s = (const char *)(data + ii); + const char *const s = (const char *)(data.bytes + ii); const int sii = ii; while (ii < ZT_BUF_MEM_SIZE) { - if (data[ii++] == 0) { + if (data.bytes[ii++] == 0) { memcpy(buf,s,ii - sii); return buf; } @@ -266,9 +376,9 @@ public: */ ZT_ALWAYS_INLINE const char *rSnc(int &ii) const { - const char *const s = (const char *)(data + ii); + const char *const s = (const char *)(data.bytes + ii); while (ii < ZT_BUF_MEM_SIZE) { - if (data[ii++] == 0) + if (data.bytes[ii++] == 0) return s; } return nullptr; @@ -287,7 +397,7 @@ public: */ ZT_ALWAYS_INLINE void *rB(int &ii,void *bytes,unsigned int len) const { - const void *const b = (const void *)(data + ii); + const void *const b = (const void *)(data.bytes + ii); if ((ii += (int)len) <= ZT_BUF_MEM_SIZE) { memcpy(bytes,b,len); return bytes; @@ -310,14 +420,10 @@ public: */ ZT_ALWAYS_INLINE const void *rBnc(int &ii,unsigned int len) const { - const void *const b = (const void *)(data + ii); + const void *const b = (const void *)(data.bytes + ii); return ((ii += (int)len) <= ZT_BUF_MEM_SIZE) ? b : nullptr; } - //////////////////////////////////////////////////////////////////////////// - // Write methods - //////////////////////////////////////////////////////////////////////////// - /** * Write a byte * @@ -344,7 +450,7 @@ public: data[s] = (uint8_t)(n >> 8U); data[s + 1] = (uint8_t)n; #else - *reinterpret_cast(data + s) = Utils::hton(n); + *reinterpret_cast(data.bytes + s) = Utils::hton(n); #endif } @@ -364,7 +470,7 @@ public: data[s + 2] = (uint8_t)(n >> 8U); data[s + 3] = (uint8_t)n; #else - *reinterpret_cast(data + s) = Utils::hton(n); + *reinterpret_cast(data.bytes + s) = Utils::hton(n); #endif } @@ -388,7 +494,7 @@ public: data[s + 6] = (uint8_t)(n >> 8U); data[s + 7] = (uint8_t)n; #else - *reinterpret_cast(data + s) = Utils::hton(n); + *reinterpret_cast(data.bytes + s) = Utils::hton(n); #endif } @@ -404,7 +510,7 @@ public: { const unsigned int s = (unsigned int)ii; if ((s + T::marshalSizeMax()) <= ZT_BUF_MEM_SIZE) { - int ms = t.marshal(data + s); + int ms = t.marshal(data.bytes + s); if (ms > 0) ii += ms; } else { @@ -442,28 +548,22 @@ public: { unsigned int s = (unsigned int)ii; if ((ii += (int)len) <= ZT_BUF_MEM_SIZE) - memcpy(data + s,bytes,len); - } - - //////////////////////////////////////////////////////////////////////////// - - ZT_ALWAYS_INLINE Buf &operator=(const Buf &b) - { - if (&b != this) - memcpy(data,b.data,ZT_BUF_MEM_SIZE); - return *this; + memcpy(data.bytes + s,bytes,len); } /** - * Raw buffer + * Raw data and fields (if U template parameter is set) * * The extra eight bytes permit silent overflow of integer types without reading or writing * beyond Buf's memory and without branching or extra masks. They can be ignored otherwise. */ - uint8_t data[ZT_BUF_MEM_SIZE + 8]; + ZT_PACKED_STRUCT(union { + uint8_t bytes[ZT_BUF_MEM_SIZE + 8]; + U fields; + }) data; private: - volatile uintptr_t __nextInPool; + volatile uintptr_t __nextInPool; // next item in free pool if this Buf is in Buf_pool AtomicCounter __refCount; }; diff --git a/node/Constants.hpp b/node/Constants.hpp index 90a8f5382..14e10c78a 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -233,9 +233,6 @@ // Exceptions thrown in core ZT code #define ZT_EXCEPTION_OUT_OF_BOUNDS 100 -#define ZT_EXCEPTION_OUT_OF_MEMORY 101 -#define ZT_EXCEPTION_PRIVATE_KEY_REQUIRED 102 -#define ZT_EXCEPTION_INVALID_ARGUMENT 103 #define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_TYPE 200 #define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW 201 #define ZT_EXCEPTION_INVALID_SERIALIZED_DATA_INVALID_CRYPTOGRAPHIC_TOKEN 202 diff --git a/node/Locator.cpp b/node/Locator.cpp index 01ff74790..35577ccb4 100644 --- a/node/Locator.cpp +++ b/node/Locator.cpp @@ -83,7 +83,7 @@ int Locator::unmarshal(const uint8_t *restrict data,const int len) if (ec > ZT_LOCATOR_MAX_ENDPOINTS) return -1; _endpointCount = ec; - for (int i = 0; i < ec; ++i) { + for (unsigned int i = 0; i < ec; ++i) { int tmp = _at[i].unmarshal(data + p,len - p); if (tmp < 0) return -1; @@ -97,7 +97,7 @@ int Locator::unmarshal(const uint8_t *restrict data,const int len) if (sl > ZT_SIGNATURE_BUFFER_SIZE) return -1; _signatureLength = sl; - if ((p + sl) > len) + if ((p + (int)sl) > len) return -1; memcpy(_signature,data + p,sl); p += (int)sl; diff --git a/node/MAC.hpp b/node/MAC.hpp index 3b36cea9d..287c59925 100644 --- a/node/MAC.hpp +++ b/node/MAC.hpp @@ -35,11 +35,11 @@ public: ZT_ALWAYS_INLINE MAC(const MAC &m) : _m(m._m) {} ZT_ALWAYS_INLINE MAC(const unsigned char a,const unsigned char b,const unsigned char c,const unsigned char d,const unsigned char e,const unsigned char f) : - _m( ((((uint64_t)a) & 0xffULL) << 40) | - ((((uint64_t)b) & 0xffULL) << 32) | - ((((uint64_t)c) & 0xffULL) << 24) | - ((((uint64_t)d) & 0xffULL) << 16) | - ((((uint64_t)e) & 0xffULL) << 8) | + _m( ((((uint64_t)a) & 0xffULL) << 40U) | + ((((uint64_t)b) & 0xffULL) << 32U) | + ((((uint64_t)c) & 0xffULL) << 24U) | + ((((uint64_t)d) & 0xffULL) << 16U) | + ((((uint64_t)e) & 0xffULL) << 8U) | (((uint64_t)f) & 0xffULL) ) {} ZT_ALWAYS_INLINE MAC(const void *bits,unsigned int len) { setTo(bits,len); } ZT_ALWAYS_INLINE MAC(const Address &ztaddr,uint64_t nwid) { fromAddress(ztaddr,nwid); } @@ -70,13 +70,13 @@ public: _m = 0ULL; return; } - const unsigned char *b = (const unsigned char *)bits; - _m = ((((uint64_t)*b) & 0xff) << 40); ++b; - _m |= ((((uint64_t)*b) & 0xff) << 32); ++b; - _m |= ((((uint64_t)*b) & 0xff) << 24); ++b; - _m |= ((((uint64_t)*b) & 0xff) << 16); ++b; - _m |= ((((uint64_t)*b) & 0xff) << 8); ++b; - _m |= (((uint64_t)*b) & 0xff); + const uint8_t *const b = (const uint8_t *)bits; + _m = (uint64_t)b[0] << 40U; + _m |= (uint64_t)b[1] << 32U; + _m |= (uint64_t)b[2] << 24U; + _m |= (uint64_t)b[3] << 16U; + _m |= (uint64_t)b[4] << 8U; + _m |= (uint64_t)b[5]; } /** @@ -87,13 +87,13 @@ public: { if (len < 6) return; - unsigned char *b = (unsigned char *)buf; - *(b++) = (unsigned char)((_m >> 40) & 0xff); - *(b++) = (unsigned char)((_m >> 32) & 0xff); - *(b++) = (unsigned char)((_m >> 24) & 0xff); - *(b++) = (unsigned char)((_m >> 16) & 0xff); - *(b++) = (unsigned char)((_m >> 8) & 0xff); - *b = (unsigned char)(_m & 0xff); + uint8_t *const b = (uint8_t *)buf; + b[0] = (uint8_t)(_m >> 40U); + b[1] = (uint8_t)(_m >> 32U); + b[2] = (uint8_t)(_m >> 24U); + b[3] = (uint8_t)(_m >> 16U); + b[4] = (uint8_t)(_m >> 8U); + b[5] = (uint8_t)_m; } /** @@ -104,7 +104,7 @@ public: template ZT_ALWAYS_INLINE void appendTo(Buffer &b) const { - unsigned char *p = (unsigned char *)b.appendField(6); + uint8_t *p = (uint8_t *)b.appendField(6); *(p++) = (unsigned char)((_m >> 40) & 0xff); *(p++) = (unsigned char)((_m >> 32) & 0xff); *(p++) = (unsigned char)((_m >> 24) & 0xff); @@ -123,11 +123,6 @@ public: */ ZT_ALWAYS_INLINE bool isMulticast() const { return ((_m & 0x010000000000ULL) != 0ULL); } - /** - * @param True if this is a locally-administered MAC - */ - ZT_ALWAYS_INLINE bool isLocallyAdministered() const { return ((_m & 0x020000000000ULL) != 0ULL); } - /** * Set this MAC to a MAC derived from an address and a network ID * @@ -187,25 +182,25 @@ public: ZT_ALWAYS_INLINE unsigned long hashCode() const { return (unsigned long)_m; } - inline char *toString(char buf[18]) const + ZT_ALWAYS_INLINE char *toString(char buf[18]) const { - buf[0] = Utils::HEXCHARS[(_m >> 44) & 0xf]; - buf[1] = Utils::HEXCHARS[(_m >> 40) & 0xf]; + buf[0] = Utils::HEXCHARS[(_m >> 44U) & 0xfU]; + buf[1] = Utils::HEXCHARS[(_m >> 40U) & 0xfU]; buf[2] = ':'; - buf[3] = Utils::HEXCHARS[(_m >> 36) & 0xf]; - buf[4] = Utils::HEXCHARS[(_m >> 32) & 0xf]; + buf[3] = Utils::HEXCHARS[(_m >> 36U) & 0xfU]; + buf[4] = Utils::HEXCHARS[(_m >> 32U) & 0xfU]; buf[5] = ':'; - buf[6] = Utils::HEXCHARS[(_m >> 28) & 0xf]; - buf[7] = Utils::HEXCHARS[(_m >> 24) & 0xf]; + buf[6] = Utils::HEXCHARS[(_m >> 28U) & 0xfU]; + buf[7] = Utils::HEXCHARS[(_m >> 24U) & 0xfU]; buf[8] = ':'; - buf[9] = Utils::HEXCHARS[(_m >> 20) & 0xf]; - buf[10] = Utils::HEXCHARS[(_m >> 16) & 0xf]; + buf[9] = Utils::HEXCHARS[(_m >> 20U) & 0xfU]; + buf[10] = Utils::HEXCHARS[(_m >> 16U) & 0xfU]; buf[11] = ':'; - buf[12] = Utils::HEXCHARS[(_m >> 12) & 0xf]; - buf[13] = Utils::HEXCHARS[(_m >> 8) & 0xf]; + buf[12] = Utils::HEXCHARS[(_m >> 12U) & 0xfU]; + buf[13] = Utils::HEXCHARS[(_m >> 8U) & 0xfU]; buf[14] = ':'; - buf[15] = Utils::HEXCHARS[(_m >> 4) & 0xf]; - buf[16] = Utils::HEXCHARS[_m & 0xf]; + buf[15] = Utils::HEXCHARS[(_m >> 4U) & 0xfU]; + buf[16] = Utils::HEXCHARS[_m & 0xfU]; buf[17] = (char)0; return buf; } diff --git a/node/Packet.hpp b/node/Packet.hpp index 4ba7ab1e1..34f444def 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -254,6 +254,21 @@ */ #define ZT_PROTO_NODE_META_WILL_RELAY_TO "r" +/** + * X coordinate of your node (sent in OK(HELLO)) + */ +#define ZT_PROTO_NODE_META_LOCATION_X "gX" + +/** + * Y coordinate of your node (sent in OK(HELLO)) + */ +#define ZT_PROTO_NODE_META_LOCATION_Y "gY" + +/** + * Z coordinate of your node (sent in OK(HELLO)) + */ +#define ZT_PROTO_NODE_META_LOCATION_Z "gZ" + // --------------------------------------------------------------------------- namespace ZeroTier { diff --git a/node/Peer.hpp b/node/Peer.hpp index b993cd858..375e89a8c 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -59,7 +59,7 @@ public: * * @param renv Runtime environment */ - Peer(const RuntimeEnvironment *renv); + explicit Peer(const RuntimeEnvironment *renv); ZT_ALWAYS_INLINE ~Peer() { Utils::burn(_key,sizeof(_key)); } diff --git a/node/Revocation.hpp b/node/Revocation.hpp index 4c324d439..d269b866a 100644 --- a/node/Revocation.hpp +++ b/node/Revocation.hpp @@ -96,7 +96,7 @@ public: * @param signer Signing identity, must have private key * @return True if signature was successful */ - inline bool sign(const Identity &signer) + ZT_ALWAYS_INLINE bool sign(const Identity &signer) { if (signer.hasPrivate()) { Buffer tmp; diff --git a/node/Utils.cpp b/node/Utils.cpp index 2b3e59ad2..395d0923e 100644 --- a/node/Utils.cpp +++ b/node/Utils.cpp @@ -85,7 +85,7 @@ static unsigned long _Utils_itoa(unsigned long n,char *s) unsigned long pos = _Utils_itoa(n / 10,s); if (pos >= 22) // sanity check,should be impossible pos = 22; - s[pos] = '0' + (char)(n % 10); + s[pos] = (char)('0' + (n % 10)); return pos + 1; } char *decimal(unsigned long n,char s[24])