summaryrefslogtreecommitdiff
path: root/node
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@gmail.com>2013-07-04 16:56:19 -0400
committerAdam Ierymenko <adam.ierymenko@gmail.com>2013-07-04 16:56:19 -0400
commit150850b80012f852521c9935145cf966946334d5 (patch)
treec082369f2fef2515cfa2e4acf1b83250a3963158 /node
downloadinfinitytier-150850b80012f852521c9935145cf966946334d5.tar.gz
infinitytier-150850b80012f852521c9935145cf966946334d5.zip
New git repository for release - version 0.2.0 tagged
Diffstat (limited to 'node')
-rw-r--r--node/Address.hpp176
-rw-r--r--node/Array.hpp110
-rw-r--r--node/AtomicCounter.hpp113
-rw-r--r--node/BlobArray.hpp94
-rw-r--r--node/Buffer.hpp398
-rw-r--r--node/Condition.hpp107
-rw-r--r--node/Constants.hpp311
-rw-r--r--node/Defaults.cpp77
-rw-r--r--node/Defaults.hpp74
-rw-r--r--node/Demarc.cpp210
-rw-r--r--node/Demarc.hpp168
-rw-r--r--node/EllipticCurveKey.hpp124
-rw-r--r--node/EllipticCurveKeyPair.cpp374
-rw-r--r--node/EllipticCurveKeyPair.hpp128
-rw-r--r--node/EthernetTap.cpp678
-rw-r--r--node/EthernetTap.hpp199
-rw-r--r--node/HMAC.cpp81
-rw-r--r--node/HMAC.hpp55
-rw-r--r--node/Http.cpp323
-rw-r--r--node/Http.hpp129
-rw-r--r--node/Identity.cpp301
-rw-r--r--node/Identity.hpp431
-rw-r--r--node/InetAddress.cpp148
-rw-r--r--node/InetAddress.hpp334
-rw-r--r--node/Logger.cpp141
-rw-r--r--node/Logger.hpp90
-rw-r--r--node/MAC.hpp160
-rw-r--r--node/MulticastGroup.hpp144
-rw-r--r--node/Mutex.hpp197
-rw-r--r--node/Network.cpp80
-rw-r--r--node/Network.hpp162
-rw-r--r--node/Node.cpp447
-rw-r--r--node/Node.hpp128
-rw-r--r--node/NodeConfig.cpp206
-rw-r--r--node/NodeConfig.hpp136
-rw-r--r--node/NonCopyable.hpp47
-rw-r--r--node/Pack.cpp159
-rw-r--r--node/Pack.hpp141
-rw-r--r--node/Packet.cpp64
-rw-r--r--node/Packet.hpp812
-rw-r--r--node/Peer.cpp141
-rw-r--r--node/Peer.hpp435
-rw-r--r--node/RuntimeEnvironment.hpp87
-rw-r--r--node/Salsa20.cpp221
-rw-r--r--node/Salsa20.hpp73
-rw-r--r--node/SharedPtr.hpp133
-rw-r--r--node/Switch.cpp1022
-rw-r--r--node/Switch.hpp260
-rw-r--r--node/SysEnv.cpp219
-rw-r--r--node/SysEnv.hpp58
-rw-r--r--node/Thread.cpp192
-rw-r--r--node/Thread.hpp94
-rw-r--r--node/Topology.cpp443
-rw-r--r--node/Topology.hpp339
-rw-r--r--node/UdpSocket.cpp163
-rw-r--r--node/UdpSocket.hpp103
-rw-r--r--node/Utils.cpp558
-rw-r--r--node/Utils.hpp601
58 files changed, 13399 insertions, 0 deletions
diff --git a/node/Address.hpp b/node/Address.hpp
new file mode 100644
index 00000000..4c912540
--- /dev/null
+++ b/node/Address.hpp
@@ -0,0 +1,176 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_ADDRESS_HPP
+#define _ZT_ADDRESS_HPP
+
+#include <stdint.h>
+#include <string>
+#include "Utils.hpp"
+#include "MAC.hpp"
+#include "Constants.hpp"
+
+namespace ZeroTier {
+
+/**
+ * ZeroTier address, which doubles as the last 5 octets of the MAC on taps
+ *
+ * Natural sort order will differ on big vs. little endian machines, but that
+ * won't matter when it's used as a local map/set key.
+ */
+class Address
+{
+private:
+ union {
+ unsigned char o[ZT_ADDRESS_LENGTH];
+ uint64_t v;
+ } _a;
+
+public:
+ Address()
+ throw()
+ {
+ _a.v = 0;
+ }
+
+ Address(const Address &a)
+ throw()
+ {
+ _a.v = a._a.v;
+ }
+
+ /**
+ * Create from a ZeroTier MAC
+ *
+ * @param m MAC (assumed to be a ZeroTier MAC)
+ */
+ Address(const MAC &m)
+ throw()
+ {
+ _a.v = 0;
+ for(int i=0;i<ZT_ADDRESS_LENGTH;++i)
+ _a.o[i] = m.data[i + 1];
+ }
+
+ /**
+ * @param bits Raw address -- 5 bytes in length
+ */
+ Address(const void *bits)
+ throw()
+ {
+ _a.v = 0;
+ for(int i=0;i<ZT_ADDRESS_LENGTH;++i)
+ _a.o[i] = ((const unsigned char *)bits)[i];
+ }
+
+ inline Address &operator=(const Address &a)
+ throw()
+ {
+ _a.v = a._a.v;
+ return *this;
+ }
+
+ /**
+ * Derive a MAC whose first octet is the ZeroTier LAN standard
+ *
+ * @return Ethernet MAC derived from address
+ */
+ inline MAC toMAC() const
+ throw()
+ {
+ MAC m;
+ m.data[0] = ZT_MAC_FIRST_OCTET;
+ for(int i=1;i<6;++i)
+ m.data[i] = _a.o[i - 1];
+ return m;
+ }
+
+ /**
+ * @return Hexadecimal string
+ */
+ inline std::string toString() const
+ {
+ return Utils::hex(_a.o,ZT_ADDRESS_LENGTH);
+ };
+
+ /**
+ * Set address to zero
+ */
+ inline void zero() throw() { _a.v = 0; }
+
+ /**
+ * @return True if this address is not zero
+ */
+ inline operator bool() const throw() { return (_a.v); }
+
+ /**
+ * @return Sum of all bytes in address
+ */
+ inline unsigned int sum() const
+ throw()
+ {
+ unsigned int s = 0;
+ for(unsigned int i=0;i<ZT_ADDRESS_LENGTH;++i)
+ s += _a.o[i];
+ return s;
+ }
+
+ /**
+ * Check if this address is reserved
+ *
+ * The all-zero null address and any address beginning with 0xff are
+ * reserved. (0xff is reserved for future use to designate possibly
+ * longer addresses, addresses based on IPv6 innards, etc.)
+ *
+ * @return True if address is reserved and may not be used
+ */
+ inline bool isReserved() const
+ throw()
+ {
+ return ((!_a.v)||(_a.o[0] == ZT_ADDRESS_RESERVED_PREFIX));
+ }
+
+ inline unsigned char *data() throw() { return _a.o; }
+ inline const unsigned char *data() const throw() { return _a.o; }
+
+ inline unsigned int size() const throw() { return ZT_ADDRESS_LENGTH; }
+
+ inline unsigned char &operator[](unsigned int i) throw() { return _a.o[i]; }
+ inline unsigned char operator[](unsigned int i) const throw() { return _a.o[i]; }
+
+ inline bool operator==(const Address &a) const throw() { return (_a.v == a._a.v); }
+ inline bool operator!=(const Address &a) const throw() { return (_a.v != a._a.v); }
+ inline bool operator<(const Address &a) const throw() { return (_a.v < a._a.v); }
+ inline bool operator>(const Address &a) const throw() { return (_a.v > a._a.v); }
+ inline bool operator<=(const Address &a) const throw() { return (_a.v <= a._a.v); }
+ inline bool operator>=(const Address &a) const throw() { return (_a.v >= a._a.v); }
+};
+
+} // namespace ZeroTier
+
+#endif
+
diff --git a/node/Array.hpp b/node/Array.hpp
new file mode 100644
index 00000000..d0fe10ec
--- /dev/null
+++ b/node/Array.hpp
@@ -0,0 +1,110 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_ARRAY_HPP
+#define _ZT_ARRAY_HPP
+
+#include <string>
+#include <algorithm>
+
+namespace ZeroTier {
+
+/**
+ * Static array -- a simple thing that's belonged in STL since the time of the dinosaurs
+ */
+template<typename T,std::size_t S>
+class Array
+{
+public:
+ Array() throw() {}
+
+ Array(const Array &a)
+ {
+ for(std::size_t i=0;i<S;++i)
+ data[i] = a.data[i];
+ }
+
+ Array(const T *ptr)
+ {
+ for(std::size_t i=0;i<S;++i)
+ data[i] = ptr[i];
+ }
+
+ inline Array &operator=(const Array &a)
+ {
+ for(std::size_t i=0;i<S;++i)
+ data[i] = a.data[i];
+ return *this;
+ }
+
+ typedef T value_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T* iterator;
+ typedef const T* const_iterator;
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ inline iterator begin() throw() { return data; }
+ inline iterator end() throw() { return &(data[S]); }
+ inline const_iterator begin() const throw() { return data; }
+ inline const_iterator end() const throw() { return &(data[S]); }
+
+ inline reverse_iterator rbegin() throw() { return reverse_iterator(begin()); }
+ inline reverse_iterator rend() throw() { return reverse_iterator(end()); }
+ inline const_reverse_iterator rbegin() const throw() { return const_reverse_iterator(begin()); }
+ inline const_reverse_iterator rend() const throw() { return const_reverse_iterator(end()); }
+
+ inline std::size_t size() const throw() { return S; }
+ inline std::size_t max_size() const throw() { return S; }
+
+ inline reference operator[](const std::size_t n) throw() { return data[n]; }
+ inline const_reference operator[](const std::size_t n) const throw() { return data[n]; }
+
+ inline reference front() throw() { return data[0]; }
+ inline const_reference front() const throw() { return data[0]; }
+ inline reference back() throw() { return data[S-1]; }
+ inline const_reference back() const throw() { return data[S-1]; }
+
+ inline bool operator==(const Array &k) const throw() { return std::equal(begin(),end(),k.begin()); }
+ inline bool operator<(const Array &k) const throw() { return std::lexicographical_compare(begin(),end(),k.begin(),k.end()); }
+ inline bool operator!=(const Array &k) const throw() { return !(*this == k); }
+ inline bool operator>(const Array &k) const throw() { return (k < *this); }
+ inline bool operator<=(const Array &k) const throw() { return !(k < *this); }
+ inline bool operator>=(const Array &k) const throw() { return !(*this < k); }
+
+ T data[S];
+};
+
+} // namespace ZeroTier
+
+#endif
+
diff --git a/node/AtomicCounter.hpp b/node/AtomicCounter.hpp
new file mode 100644
index 00000000..ebc70817
--- /dev/null
+++ b/node/AtomicCounter.hpp
@@ -0,0 +1,113 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_ATOMICCOUNTER_HPP
+#define _ZT_ATOMICCOUNTER_HPP
+
+#include "Mutex.hpp"
+#include "NonCopyable.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Simple atomic counter supporting increment and decrement
+ */
+class AtomicCounter : NonCopyable
+{
+public:
+ /**
+ * Initialize counter at zero
+ */
+ AtomicCounter()
+ throw() :
+ _v(0)
+ {
+ }
+
+ inline int operator*() const
+ throw()
+ {
+#ifdef __GNUC__
+ return __sync_or_and_fetch(const_cast <int *> (&_v),0);
+#else
+ _l.lock();
+ int v = _v;
+ _l.unlock();
+ return v;
+#endif
+ }
+
+ inline int operator++()
+ throw()
+ {
+#ifdef __GNUC__
+ return __sync_add_and_fetch(&_v,1);
+#else
+ _l.lock();
+ int v = ++_v;
+ _l.unlock();
+ return v;
+#endif
+ }
+
+ inline int operator--()
+ throw()
+ {
+#ifdef __GNUC__
+ return __sync_sub_and_fetch(&_v,1);
+#else
+ _l.lock();
+ int v = --_v;
+ _l.unlock();
+ return v;
+#endif
+ }
+
+ inline bool operator==(const AtomicCounter &i) const throw() { return (**this == *i); }
+ inline bool operator!=(const AtomicCounter &i) const throw() { return (**this != *i); }
+ inline bool operator>(const AtomicCounter &i) const throw() { return (**this > *i); }
+ inline bool operator<(const AtomicCounter &i) const throw() { return (**this < *i); }
+ inline bool operator>=(const AtomicCounter &i) const throw() { return (**this >= *i); }
+ inline bool operator<=(const AtomicCounter &i) const throw() { return (**this <= *i); }
+
+ inline bool operator==(const int i) const throw() { return (**this == i); }
+ inline bool operator!=(const int i) const throw() { return (**this != i); }
+ inline bool operator>(const int i) const throw() { return (**this > i); }
+ inline bool operator<(const int i) const throw() { return (**this < i); }
+ inline bool operator>=(const int i) const throw() { return (**this >= i); }
+ inline bool operator<=(const int i) const throw() { return (**this <= i); }
+
+private:
+ int _v;
+#ifndef __GNUC__
+ Mutex _l;
+#endif
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/BlobArray.hpp b/node/BlobArray.hpp
new file mode 100644
index 00000000..d78bcffa
--- /dev/null
+++ b/node/BlobArray.hpp
@@ -0,0 +1,94 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_BLOBARRAY_HPP
+#define _ZT_BLOBARRAY_HPP
+
+#include <vector>
+#include <string>
+#include <algorithm>
+
+namespace ZeroTier {
+
+/**
+ * A vector of binary strings serializable in a packed format
+ *
+ * The format uses variable-length integers to indicate the length of each
+ * field. Each byte of the length has another byte with seven more significant
+ * bits if its 8th bit is set. Fields can be up to 2^28 in length.
+ */
+class BlobArray : public std::vector<std::string>
+{
+public:
+ inline std::string serialize() const
+ {
+ std::string r;
+ for(BlobArray::const_iterator i=begin();i!=end();++i) {
+ unsigned int flen = (unsigned int)i->length();
+ do {
+ unsigned char flenb = (unsigned char)(flen & 0x7f);
+ flen >>= 7;
+ flenb |= (flen) ? 0x80 : 0;
+ r.push_back((char)flenb);
+ } while (flen);
+ r.append(*i);
+ }
+ return r;
+ }
+
+ /**
+ * Deserialize, replacing the current contents of this array
+ *
+ * @param data Serialized binary data
+ * @param len Length of serialized data
+ */
+ inline void deserialize(const void *data,unsigned int len)
+ {
+ clear();
+ for(unsigned int i=0;i<len;) {
+ unsigned int flen = 0;
+ unsigned int chunk = 0;
+ while (i < len) {
+ flen |= ((unsigned int)(((const unsigned char *)data)[i] & 0x7f)) << (7 * chunk++);
+ if (!(((const unsigned char *)data)[i++] & 0x80))
+ break;
+ }
+ flen = std::min(flen,len - i);
+ push_back(std::string(((const char *)data) + i,flen));
+ i += flen;
+ }
+ }
+ inline void deserialize(const std::string &data)
+ {
+ deserialize(data.data(),(unsigned int)data.length());
+ }
+};
+
+} // namespace ZeroTier
+
+#endif
+
diff --git a/node/Buffer.hpp b/node/Buffer.hpp
new file mode 100644
index 00000000..d3603d38
--- /dev/null
+++ b/node/Buffer.hpp
@@ -0,0 +1,398 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_BUFFER_HPP
+#define _ZT_BUFFER_HPP
+
+#include <stdexcept>
+#include <string>
+#include <algorithm>
+#include <utility>
+#include <string.h>
+#include <stdint.h>
+#include "Utils.hpp"
+
+#ifdef __GNUC__
+#define ZT_VAR_MAY_ALIAS __attribute__((__may_alias__))
+#else
+#define ZT_VAR_MAY_ALIAS
+#endif
+
+namespace ZeroTier {
+
+/**
+ * A variable length but statically allocated buffer
+ *
+ * Bounds-checking is done everywhere, since this is used in security
+ * critical code. This supports construction and assignment from buffers
+ * of differing capacities, provided the data actually in them fits.
+ * It throws std::out_of_range on any boundary violation.
+ *
+ * The at(), append(), etc. methods encode integers larger than 8-bit in
+ * big-endian (network) byte order.
+ *
+ * @tparam C Total capacity
+ */
+template<unsigned int C>
+class Buffer
+{
+ // I love me!
+ template <unsigned int C2> friend class Buffer;
+
+public:
+ // STL container idioms
+ typedef unsigned char value_type;
+ typedef unsigned char * pointer;
+ typedef const unsigned char * const_pointer;
+ typedef unsigned char & reference;
+ typedef const unsigned char & const_reference;
+ typedef unsigned char * iterator;
+ typedef const unsigned char * const_iterator;
+ typedef unsigned int size_type;
+ typedef int difference_type;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+ inline iterator begin() { return _b; }
+ inline iterator end() { return (_b + _l); }
+ inline const_iterator begin() const { return _b; }
+ inline const_iterator end() const { return (_b + _l); }
+ inline reverse_iterator rbegin() { return reverse_iterator(begin()); }
+ inline reverse_iterator rend() { return reverse_iterator(end()); }
+ inline const_reverse_iterator rbegin() const { return const_reverse_iterator(begin()); }
+ inline const_reverse_iterator rend() const { return const_reverse_iterator(end()); }
+
+ Buffer()
+ throw() :
+ _l(0)
+ {
+ }
+
+ Buffer(unsigned int l)
+ throw(std::out_of_range)
+ {
+ if (l > C)
+ throw std::out_of_range("Buffer: construct with size larger than capacity");
+ _l = l;
+ }
+
+ template<unsigned int C2>
+ Buffer(const Buffer<C2> &b)
+ throw(std::out_of_range)
+ {
+ *this = b;
+ }
+
+ Buffer(const void *b,unsigned int l)
+ throw(std::out_of_range)
+ {
+ copyFrom(b,l);
+ }
+
+ Buffer(const std::string &s)
+ throw(std::out_of_range)
+ {
+ copyFrom(s.data(),s.length());
+ }
+
+ template<unsigned int C2>
+ inline Buffer &operator=(const Buffer<C2> &b)
+ throw(std::out_of_range)
+ {
+ if (b._l > C)
+ throw std::out_of_range("Buffer: assignment from buffer larger than capacity");
+ memcpy(this,&b,sizeof(_l) + b._l); // one memcpy for all fields
+ return *this;
+ }
+
+ inline Buffer &operator=(const std::string &s)
+ throw(std::out_of_range)
+ {
+ copyFrom(s.data(),s.length());
+ return *this;
+ }
+
+ inline void copyFrom(const void *b,unsigned int l)
+ throw(std::out_of_range)
+ {
+ if (l > C)
+ throw std::out_of_range("Buffer: set from C array larger than capacity");
+ _l = l;
+ memcpy(_b,b,l);
+ }
+
+ unsigned char operator[](const unsigned int i) const
+ throw(std::out_of_range)
+ {
+ if (i >= _l)
+ throw std::out_of_range("Buffer: [] beyond end of data");
+ return (unsigned char)_b[i];
+ }
+
+ unsigned char &operator[](const unsigned int i)
+ throw(std::out_of_range)
+ {
+ if (i >= _l)
+ throw std::out_of_range("Buffer: [] beyond end of data");
+ return ((unsigned char *)_b)[i];
+ }
+
+ unsigned char *data() throw() { return (unsigned char *)_b; }
+ const unsigned char *data() const throw() { return (const unsigned char *)_b; }
+
+ /**
+ * Safe way to get a pointer to a field from data() with bounds checking
+ *
+ * @param i Index of field in buffer
+ * @param l Length of field in bytes
+ * @return Pointer to field data
+ * @throws std::out_of_range Field extends beyond data size
+ */
+ unsigned char *field(unsigned int i,unsigned int l)
+ throw(std::out_of_range)
+ {
+ if ((i + l) > _l)
+ throw std::out_of_range("Buffer: field() beyond end of data");
+ return (unsigned char *)(_b + i);
+ }
+ const unsigned char *field(unsigned int i,unsigned int l) const
+ throw(std::out_of_range)
+ {
+ if ((i + l) > _l)
+ throw std::out_of_range("Buffer: field() beyond end of data");
+ return (const unsigned char *)(_b + i);
+ }
+
+ /**
+ * Place a primitive integer value at a given position
+ *
+ * @param i Index to place value
+ * @param v Value
+ * @tparam T Integer type (e.g. uint16_t, int64_t)
+ */
+ template<typename T>
+ inline void setAt(unsigned int i,const T v)
+ throw(std::out_of_range)
+ {
+ if ((i + sizeof(T)) > _l)
+ throw std::out_of_range("Buffer: set() beyond end of data");
+ T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T *>(_b + i);
+ *p = Utils::hton(v);
+ }
+
+ /**
+ * Get a primitive integer value at a given position
+ *
+ * This behaves like set() in reverse.
+ *
+ * @param i Index to get integer
+ * @tparam T Integer type (e.g. uint16_t, int64_t)
+ * @return Integer value
+ */
+ template<typename T>
+ inline T at(unsigned int i) const
+ throw(std::out_of_range)
+ {
+ if ((i + sizeof(T)) > _l)
+ throw std::out_of_range("Buffer: at() beyond end of data");
+ const T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<const T *>(_b + i);
+ return Utils::ntoh(*p);
+ }
+
+ /**
+ * Append an integer type to this buffer
+ *
+ * @param v Value to append
+ * @tparam T Integer type (e.g. uint16_t, int64_t)
+ * @throws std::out_of_range Attempt to append beyond capacity
+ */
+ template<typename T>
+ inline void append(const T v)
+ throw(std::out_of_range)
+ {
+ if ((_l + sizeof(T)) > C)
+ throw std::out_of_range("Buffer: append beyond capacity");
+ T *const ZT_VAR_MAY_ALIAS p = reinterpret_cast<T *>(_b + _l);
+ *p = Utils::hton(v);
+ _l += sizeof(T);
+ }
+
+ /**
+ * Append a C-array of bytes
+ *
+ * @param b Data
+ * @param l Length
+ * @throws std::out_of_range Attempt to append beyond capacity
+ */
+ inline void append(const void *b,unsigned int l)
+ throw(std::out_of_range)
+ {
+ if ((_l + l) > C)
+ throw std::out_of_range("Buffer: append beyond capacity");
+ memcpy(_b + _l,b,l);
+ _l += l;
+ }
+
+ /**
+ * Append a string
+ *
+ * @param s String to append
+ * @throws std::out_of_range Attempt to append beyond capacity
+ */
+ inline void append(const std::string &s)
+ throw(std::out_of_range)
+ {
+ append(s.data(),s.length());
+ }
+
+ /**
+ * Append a buffer
+ *
+ * @param b Buffer to append
+ * @tparam C2 Capacity of second buffer (typically inferred)
+ * @throws std::out_of_range Attempt to append beyond capacity
+ */
+ template<unsigned int C2>
+ inline void append(const Buffer<C2> &b)
+ throw(std::out_of_range)
+ {
+ append(b._b,b._l);
+ }
+
+ /**
+ * Increment size by a given number of bytes
+ *
+ * The contents of new space are undefined.
+ *
+ * @param i Bytes to increment
+ * @throws std::out_of_range Capacity exceeded
+ */
+ inline void addSize(unsigned int i)
+ throw(std::out_of_range)
+ {
+ if ((i + _l) > C)
+ throw std::out_of_range("Buffer: setSize to larger than capacity");
+ _l += i;
+ }
+
+ /**
+ * Set size of data in buffer
+ *
+ * The contents of new space are undefined.
+ *
+ * @param i New size
+ * @throws std::out_of_range Size larger than capacity
+ */
+ inline void setSize(const unsigned int i)
+ throw(std::out_of_range)
+ {
+ if (i > C)
+ throw std::out_of_range("Buffer: setSize to larger than capacity");
+ _l = i;
+ }
+
+ /**
+ * Set buffer data length to zero
+ */
+ inline void clear()
+ throw()
+ {
+ _l = 0;
+ }
+
+ /**
+ * Zero buffer up to size()
+ */
+ inline void zero()
+ throw()
+ {
+ memset(_b,0,_l);
+ }
+
+ /**
+ * Zero unused capacity area
+ */
+ inline void zeroUnused()
+ throw()
+ {
+ memset(_b + _l,0,C - _l);
+ }
+
+ /**
+ * @return Size of data in buffer
+ */
+ inline unsigned int size() const throw() { return _l; }
+
+ /**
+ * @return Capacity of buffer
+ */
+ inline unsigned int capacity() const throw() { return C; }
+
+ template<unsigned int C2>
+ inline bool operator==(const Buffer<C2> &b) const
+ throw()
+ {
+ return ((_l == b._l)&&(!memcmp(_b,b._b,_l)));
+ }
+ template<unsigned int C2>
+ inline bool operator!=(const Buffer<C2> &b) const
+ throw()
+ {
+ return ((_l != b._l)||(memcmp(_b,b._b,_l)));
+ }
+ template<unsigned int C2>
+ inline bool operator<(const Buffer<C2> &b) const
+ throw()
+ {
+ return (memcmp(_b,b._b,std::min(_l,b._l)) < 0);
+ }
+ template<unsigned int C2>
+ inline bool operator>(const Buffer<C2> &b) const
+ throw()
+ {
+ return (b < *this);
+ }
+ template<unsigned int C2>
+ inline bool operator<=(const Buffer<C2> &b) const
+ throw()
+ {
+ return !(b < *this);
+ }
+ template<unsigned int C2>
+ inline bool operator>=(const Buffer<C2> &b) const
+ throw()
+ {
+ return !(*this < b);
+ }
+
+protected:
+ unsigned int _l;
+ char ZT_VAR_MAY_ALIAS _b[C];
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Condition.hpp b/node/Condition.hpp
new file mode 100644
index 00000000..2ce8c98f
--- /dev/null
+++ b/node/Condition.hpp
@@ -0,0 +1,107 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_CONDITION_HPP
+#define _ZT_CONDITION_HPP
+
+#include "NonCopyable.hpp"
+
+#if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+
+#include <time.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include "Utils.hpp"
+
+namespace ZeroTier {
+
+class Condition : NonCopyable
+{
+public:
+ Condition()
+ throw()
+ {
+ pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
+ pthread_cond_init(&_cond,(const pthread_condattr_t *)0);
+ }
+
+ ~Condition()
+ {
+ pthread_cond_destroy(&_cond);
+ pthread_mutex_destroy(&_mh);
+ }
+
+ inline void wait() const
+ throw()
+ {
+ pthread_mutex_lock(const_cast <pthread_mutex_t *>(&_mh));
+ pthread_cond_wait(const_cast <pthread_cond_t *>(&_cond),const_cast <pthread_mutex_t *>(&_mh));
+ pthread_mutex_unlock(const_cast <pthread_mutex_t *>(&_mh));
+ }
+
+ inline void wait(unsigned long ms) const
+ throw()
+ {
+ uint64_t when = Utils::now() + (uint64_t)ms;
+ struct timespec ts;
+ ts.tv_sec = (unsigned long)(when / 1000);
+ ts.tv_nsec = (unsigned long)(when % 1000) * 1000000;
+ pthread_mutex_lock(const_cast <pthread_mutex_t *>(&_mh));
+ pthread_cond_timedwait(const_cast <pthread_cond_t *>(&_cond),const_cast <pthread_mutex_t *>(&_mh),&ts);
+ pthread_mutex_unlock(const_cast <pthread_mutex_t *>(&_mh));
+ }
+
+ inline void signal() const
+ throw()
+ {
+ pthread_cond_signal(const_cast <pthread_cond_t *>(&_cond));
+ }
+
+private:
+ pthread_cond_t _cond;
+ pthread_mutex_t _mh;
+};
+
+} // namespace ZeroTier
+
+#endif // Apple / Linux
+
+#ifdef _WIN32
+
+#include <stdlib.h>
+#include <Windows.h>
+
+namespace ZeroTier {
+
+error need windoze;
+// On Windows this will probably be implemented via Semaphores
+
+} // namespace ZeroTier
+
+#endif // _WIN32
+
+#endif
diff --git a/node/Constants.hpp b/node/Constants.hpp
new file mode 100644
index 00000000..641b25bb
--- /dev/null
+++ b/node/Constants.hpp
@@ -0,0 +1,311 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_CONSTANTS_HPP
+#define _ZT_CONSTANTS_HPP
+
+// Assume these are little-endian, since we don't support old PPC MACs
+// and all newer Mac or Windows systems are either x86_32, x86_64, or
+// ARM in little-endian mode.
+#if defined(__APPLE__) || defined(_WIN32)
+#undef __BYTE_ORDER
+#undef __LITTLE_ENDIAN
+#undef __BIG_ENDIAN
+#define __BIG_ENDIAN 4321
+#define __LITTLE_ENDIAN 1234
+#define __BYTE_ORDER 1234
+#endif
+
+// Linux has endian.h, which should tell us
+#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+#include <endian.h>
+#endif
+
+#ifndef __BYTE_ORDER
+error_no_byte_order_defined
+#endif
+
+#ifndef ZT_OSNAME
+error_no_ZT_OSNAME
+#endif
+
+#ifndef ZT_ARCH
+error_no_ZT_ARCH
+#endif
+
+#ifdef _WIN32
+#define ZT_PATH_SEPARATOR '\\'
+#define ZT_PATH_SEPARATOR_S "\\"
+#define ZT_EOL_S "\r\n"
+#else
+#define ZT_PATH_SEPARATOR '/'
+#define ZT_PATH_SEPARATOR_S "/"
+#define ZT_EOL_S "\n"
+#endif
+
+/**
+ * Length of a ZeroTier address in bytes
+ */
+#define ZT_ADDRESS_LENGTH 5
+
+/**
+ * Addresses beginning with this byte are reserved for the joy of in-band signaling
+ */
+#define ZT_ADDRESS_RESERVED_PREFIX 0xff
+
+/**
+ * Default local UDP port
+ */
+#define ZT_DEFAULT_UDP_PORT 8993
+
+/**
+ * Default payload MTU for UDP packets
+ *
+ * In the future we might support UDP path MTU discovery, but for now we
+ * set a maximum that is equal to 1500 minus 8 (for PPPoE overhead, common
+ * in some markets) minus 48 (IPv6 UDP overhead).
+ */
+#define ZT_UDP_DEFAULT_PAYLOAD_MTU 1444
+
+/**
+ * MTU used for Ethernet tap device
+ *
+ * This is pretty much an unchangeable global constant. To make it change
+ * across nodes would require logic to send ICMP packet too big messages,
+ * which would complicate things. 1500 has been good enough on most LANs
+ * for ages, so a larger MTU should be fine for the forseeable future. This
+ * typically results in two UDP packets per single large frame. Experimental
+ * results seem to show that this is good. Larger MTUs resulting in more
+ * fragments seemed too brittle on slow/crummy links for no benefit.
+ *
+ * If this does change, also change it in tap.h in the tuntaposx code under
+ * mac-tap.
+ *
+ * Overhead for a normal frame split into two packets:
+ *
+ * 1414 = 1444 (typical UDP MTU) - 28 (packet header) - 2 (ethertype)
+ * 1428 = 1444 (typical UDP MTU) - 16 (fragment header)
+ * SUM: 2842
+ *
+ * We use 2800, which leaves some room for other payload in other types of
+ * messages such as multicast propagation or future support for bridging.
+ */
+#define ZT_IF_MTU 2800
+
+/**
+ * Maximum number of networks we can be a member of
+ *
+ * This is a safe value that's within the tap device limit on all known OSes.
+ */
+#define ZT_MAX_NETWORK_MEMBERSHIPS 16
+
+/**
+ * Maximum number of packet fragments we'll support
+ *
+ * The actual spec allows 16, but this is the most we'll support right
+ * now. Packets with more than this many fragments are dropped.
+ */
+#define ZT_MAX_PACKET_FRAGMENTS 3
+
+/**
+ * Timeout for receipt of fragmented packets in ms
+ *
+ * Since there's no retransmits, this is just a really bad case scenario for
+ * transit time. It's short enough that a DOS attack from exhausing buffers is
+ * very unlikely, as the transfer rate would have to be fast enough to fill
+ * system memory in this time.
+ */
+#define ZT_FRAGMENTED_PACKET_RECEIVE_TIMEOUT 1500
+
+/**
+ * First byte of MAC addresses derived from ZeroTier addresses
+ *
+ * This has the 0x02 bit set, which indicates a locally administrered
+ * MAC address rather than one with a known HW ID.
+ */
+#define ZT_MAC_FIRST_OCTET 0x32
+
+/**
+ * How often Topology::clean() is called in ms
+ */
+#define ZT_TOPOLOGY_CLEAN_PERIOD 300000
+
+/**
+ * Delay between WHOIS retries in ms
+ */
+#define ZT_WHOIS_RETRY_DELAY 500
+
+/**
+ * Maximum identity WHOIS retries
+ */
+#define ZT_MAX_WHOIS_RETRIES 3
+
+/**
+ * Transmit queue entry timeout
+ */
+#define ZT_TRANSMIT_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
+
+/**
+ * Receive queue entry timeout
+ */
+#define ZT_RECEIVE_QUEUE_TIMEOUT (ZT_WHOIS_RETRY_DELAY * (ZT_MAX_WHOIS_RETRIES + 1))
+
+/**
+ * Maximum number of ZT hops allowed
+ *
+ * The protocol allows up to 7, but we limit it to something smaller.
+ */
+#define ZT_RELAY_MAX_HOPS 3
+
+/**
+ * Breadth of tree for rumor mill multicast propagation
+ */
+#define ZT_MULTICAST_PROPAGATION_BREADTH 4
+
+/**
+ * Depth of tree for rumor mill multicast propagation
+ *
+ * The maximum number of peers who can receive a multicast is equal to
+ * the sum of BREADTH^i where I is from 1 to DEPTH. This ignores the effect
+ * of the rate limiting algorithm or bloom filter collisions.
+ *
+ * 7 results in a max of 21844 recipients for a given multicast.
+ */
+#define ZT_MULTICAST_PROPAGATION_DEPTH 7
+
+/**
+ * Length of circular ring buffer history of multicast packets
+ */
+#define ZT_MULTICAST_DEDUP_HISTORY_LENGTH 4096
+
+/**
+ * Expiration time in ms for multicast history items
+ */
+#define ZT_MULTICAST_DEDUP_HISTORY_EXPIRE 8000
+
+/**
+ * Period between announcements of all multicast 'likes' in ms
+ *
+ * Announcement occurs when a multicast group is locally joined, but all
+ * memberships are periodically re-broadcast. If they're not they will
+ * expire.
+ */
+#define ZT_MULTICAST_LIKE_ANNOUNCE_ALL_PERIOD 120000
+
+/**
+ * Expire time for multicast 'likes' in ms
+ */
+#define ZT_MULTICAST_LIKE_EXPIRE ((ZT_MULTICAST_LIKE_ANNOUNCE_ALL_PERIOD * 2) + 1000)
+
+/**
+ * Time between polls of local taps for multicast membership changes
+ */
+#define ZT_MULTICAST_LOCAL_POLL_PERIOD 10000
+
+/**
+ * Delay between scans of the topology active peer DB for peers that need ping
+ */
+#define ZT_PING_CHECK_DELAY 7000
+
+/**
+ * Delay between checks of network configuration fingerprint
+ */
+#define ZT_NETWORK_FINGERPRINT_CHECK_DELAY 5000
+
+/**
+ * Delay between pings (actually HELLOs) to direct links
+ */
+#define ZT_PEER_DIRECT_PING_DELAY 120000
+
+/**
+ * Period between rechecks of autoconfigure URL
+ *
+ * This is in the absence of an external message ordering a recheck.
+ */
+#define ZT_AUTOCONFIGURE_INTERVAL 3600000
+
+/**
+ * Period between autoconfigure attempts if no successful autoconfig
+ */
+#define ZT_AUTOCONFIGURE_CHECK_DELAY 15000
+
+/**
+ * Minimum delay in Node service loop
+ *
+ * This is the shortest of the check delays/periods.
+ */
+#define ZT_MIN_SERVICE_LOOP_INTERVAL ZT_NETWORK_FINGERPRINT_CHECK_DELAY
+
+/**
+ * Activity timeout for links
+ *
+ * A link that hasn't spoken in this long is simply considered inactive.
+ */
+#define ZT_PEER_LINK_ACTIVITY_TIMEOUT ((ZT_PEER_DIRECT_PING_DELAY * 2) + 1000)
+
+/**
+ * Delay in ms between firewall opener packets to direct links
+ *
+ * This should be lower than the UDP conversation entry timeout in most
+ * stateful firewalls.
+ */
+#define ZT_FIREWALL_OPENER_DELAY 50000
+
+/**
+ * IP hops (a.k.a. TTL) to set for firewall opener packets
+ *
+ * 2 should permit traversal of double-NAT configurations, such as from inside
+ * a VM running behind local NAT on a host that is itself behind NAT.
+ */
+#define ZT_FIREWALL_OPENER_HOPS 2
+
+/**
+ * Delay sleep overshoot for detection of a probable sleep/wake event
+ */
+#define ZT_SLEEP_WAKE_DETECTION_THRESHOLD 2000
+
+/**
+ * Time to pause main service loop after sleep/wake detect
+ */
+#define ZT_SLEEP_WAKE_SETTLE_TIME 5000
+
+/**
+ * Minimum interval between attempts by relays to unite peers
+ */
+#define ZT_MIN_UNITE_INTERVAL 30000
+
+/**
+ * Delay in milliseconds between firewall opener and real packet for NAT-t
+ */
+#define ZT_RENDEZVOUS_NAT_T_DELAY 500
+
+/**
+ * Generate a new ownership verify secret on launch if older than this
+ */
+#define ZT_OVS_GENERATE_NEW_IF_OLDER_THAN 86400000
+
+#endif
diff --git a/node/Defaults.cpp b/node/Defaults.cpp
new file mode 100644
index 00000000..f1454796
--- /dev/null
+++ b/node/Defaults.cpp
@@ -0,0 +1,77 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include "Defaults.hpp"
+#include "Constants.hpp"
+
+namespace ZeroTier {
+
+const Defaults ZT_DEFAULTS;
+
+static inline std::map< Identity,std::vector<InetAddress> > _mkSupernodeMap()
+ throw(std::runtime_error)
+{
+ std::map< Identity,std::vector<InetAddress> > sn;
+ Identity id;
+ std::vector<InetAddress> addrs;
+
+ // Nothing special about a supernode... except that they are
+ // designated as such.
+
+ // cthulhu.zerotier.com - New York, New York, USA
+ addrs.clear();
+ if (!id.fromString("271ee006a0:1:AgGXs3I+9CWrEmGMxc50x3E+trwtaa2ZMXDU6ezz92fFJXzlhRKGUY/uAToHDdH9XiLxtcA+kUQAZdC4Dy2xtqXxjw==:QgH5Nlx4oWEGVrwhNocqem+3VNd4qzt7RLrmuvqZvKPRS9R70LJYJQLlKZj0ri55Pzg+Mlwy4a4nAgfnRAWA+TW6R0EjSmq72MG585XGNfWBVk3LxMvxlNWErnVNFr2BQS9yzVp4pRjPLdCW4RB3dwEHBUgJ78rwMxQ6IghVCl8CjkDapg=="))
+ throw std::runtime_error("invalid identity in Defaults");
+ addrs.push_back(InetAddress("198.199.73.93",ZT_DEFAULT_UDP_PORT));
+ sn[id] = addrs;
+
+ // nyarlathotep.zerotier.com - San Francisco, California, USA
+ addrs.clear();
+ if (!id.fromString("fa9be4008b:1:AwCHXEi/PJuhtOPUZxnBSMiuGvj6XeRMWu9R9aLR3JD1qluADLQzUPSP2+81Dqvgi2wkQ2cqEpOlDPeUCvtlZwdXEA==:QgH4usG/wzsoUCtO2LL3qkwugtoXEz1PUJbmUzY8vbwzc5bckmVPjMqb4q2CF71+QVPV1K6shIV2EKkBMRSS/D/44EGEwC6tjFGZqmmogaC0P1uQeukTAF4qta46YgC4YQx54/Vd/Yfl8n1Bwmgm0gBB4W1ZQir3p+wp37MGlEN0rlXxqA=="))
+ throw std::runtime_error("invalid identity in Defaults");
+ addrs.push_back(InetAddress("198.199.97.220",ZT_DEFAULT_UDP_PORT));
+ sn[id] = addrs;
+
+ // shub-niggurath.zerotier.com - Amsterdam, Netherlands
+ addrs.clear();
+ if (!id.fromString("48099ecd05:1:AwHO7o1FdDj1nEArfchTDa6EG7Eh2GLdiH86BhcoNv0BHJN4tmrf0Y7/2SZiQFpTTwJf93iph84Dci5+k52u/qkHTQ==:QgGbir8CNxBFFPPj8Eo3Bnp2UmbnZxu/pOq3Ke0WaLBBhHzVuwM+88g7CaDxbZ0AY2VkFc9hmE3VG+xi7g0H86yfVUIBHZnb7N+DCtf8/mphZIHNgmasakRi4hU11kGyLi1nTVTnrmCfAb7w+8SCp64Q5RNvBC/Pvz7pxSwSdjIHkVqRaeo="))
+ throw std::runtime_error("invalid identity in Defaults");
+ addrs.push_back(InetAddress("198.211.127.172",ZT_DEFAULT_UDP_PORT));
+ sn[id] = addrs;
+
+ return sn;
+}
+
+Defaults::Defaults()
+ throw(std::runtime_error) :
+ supernodes(_mkSupernodeMap()),
+ configUrlPrefix("http://api.zerotier.com/one/nc/"),
+ configAuthority("f9f34184ac:1:AwGgrWjb8dARXzruqxiy1+Qf+gz4iM5IMfQTCWrJXkwERdvbvxTPZvtIyitw4gS90TGIxW+e7uJxweg9Vyq5lZJBrg==:QeEQLm9ymLC3EcnIw2OUqufUwb2wgHSAg6wQOXKyhT779p/8Hz5485PZLJCbr/aVHjwzop8APJk9B45Zm0Mb/LEhQTBMH2jvc7qqoYnMCNCO9jpADeMJwMW5e1VFgIObWl9uNjhRbf5/m8dZcn0pKKGwjSoP1QTeVWOC8GkZhE25bUWj")
+{
+}
+
+} // namespace ZeroTier
diff --git a/node/Defaults.hpp b/node/Defaults.hpp
new file mode 100644
index 00000000..b9c8ecf5
--- /dev/null
+++ b/node/Defaults.hpp
@@ -0,0 +1,74 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_DEFAULTS_HPP
+#define _ZT_DEFAULTS_HPP
+
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <map>
+#include "Identity.hpp"
+#include "InetAddress.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Static configuration defaults
+ *
+ * These are the default values that ship baked into the ZeroTier binary. They
+ * define the basic parameters required for it to connect to the rest of the
+ * network and obtain software updates.
+ */
+class Defaults
+{
+public:
+ Defaults()
+ throw(std::runtime_error);
+ ~Defaults() {}
+
+ /**
+ * Supernodes on the ZeroTier network
+ */
+ const std::map< Identity,std::vector<InetAddress> > supernodes;
+
+ /**
+ * URL prefix for autoconfiguration
+ */
+ const std::string configUrlPrefix;
+
+ /**
+ * Identity used to encrypt and authenticate configuration from URL
+ */
+ const std::string configAuthority;
+};
+
+extern const Defaults ZT_DEFAULTS;
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Demarc.cpp b/node/Demarc.cpp
new file mode 100644
index 00000000..ae52db48
--- /dev/null
+++ b/node/Demarc.cpp
@@ -0,0 +1,210 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <vector>
+#include "Demarc.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Logger.hpp"
+#include "UdpSocket.hpp"
+#include "InetAddress.hpp"
+#include "Switch.hpp"
+#include "Buffer.hpp"
+
+namespace ZeroTier {
+
+const Demarc::Port Demarc::ANY_PORT;
+const Demarc::Port Demarc::NULL_PORT;
+
+Demarc::Demarc(const RuntimeEnvironment *renv) :
+ _r(renv)
+{
+}
+
+Demarc::~Demarc()
+{
+ for(std::map< Port,DemarcPortObj >::iterator pe(_ports.begin());pe!=_ports.end();++pe) {
+ switch (pe->second.type) {
+ case PORT_TYPE_UDP_SOCKET_V4:
+ case PORT_TYPE_UDP_SOCKET_V6:
+ delete ((UdpSocket *)pe->second.obj);
+ break;
+ case PORT_TYPE_LOCAL_ETHERNET:
+ case PORT_TYPE_RELAY_TUNNEL:
+ break;
+ }
+ }
+}
+
+std::string Demarc::describe(Demarc::Port p)
+ throw()
+{
+ char buf[64];
+ switch ((DemarcPortType)(((uint64_t)p) >> 60)) {
+ case PORT_TYPE_UDP_SOCKET_V4:
+ sprintf(buf,"udp/4/%d",(int)((uint64_t)p & 0xffff));
+ return std::string(buf);
+ case PORT_TYPE_UDP_SOCKET_V6:
+ sprintf(buf,"udp/6/%d",(int)((uint64_t)p & 0xffff));
+ return std::string(buf);
+ case PORT_TYPE_LOCAL_ETHERNET:
+ return std::string("ethernet");
+ case PORT_TYPE_RELAY_TUNNEL:
+ return std::string("relay");
+ }
+ return std::string("(null)");
+}
+
+bool Demarc::has(Port p) const
+ throw()
+{
+ Mutex::Lock _l(_ports_m);
+ return (_ports.count(p));
+}
+
+bool Demarc::bindLocalUdp(unsigned int localPort)
+ throw()
+{
+ Mutex::Lock _l(_ports_m);
+
+ uint64_t v4p = ((uint64_t)PORT_TYPE_UDP_SOCKET_V4 << 60) | (uint64_t)localPort;
+ uint64_t v6p = ((uint64_t)PORT_TYPE_UDP_SOCKET_V6 << 60) | (uint64_t)localPort;
+ if ((_ports.count((Port)v4p))||(_ports.count((Port)v6p)))
+ return true;
+
+ UdpSocket *v4;
+ try {
+ DemarcPortObj *v4r = &(_ports[(Port)v4p]);
+ v4r->port = (Port)v4p;
+ v4r->parent = this;
+ v4r->obj = v4 = new UdpSocket(localPort,false,&Demarc::_CBudpSocketPacketHandler,v4r);
+ v4r->type = PORT_TYPE_UDP_SOCKET_V4;
+ } catch ( ... ) {
+ _ports.erase((Port)v4p);
+ return false;
+ }
+
+ UdpSocket *v6;
+ try {
+ DemarcPortObj *v6r = &(_ports[(Port)v6p]);
+ v6r->port = (Port)v6p;
+ v6r->parent = this;
+ v6r->obj = v6 = new UdpSocket(localPort,true,&Demarc::_CBudpSocketPacketHandler,v6r);
+ v6r->type = PORT_TYPE_UDP_SOCKET_V6;
+ } catch ( ... ) {
+ delete v4;
+ _ports.erase((Port)v4p);
+ _ports.erase((Port)v6p);
+ return false;
+ }
+
+ return true;
+}
+
+Demarc::Port Demarc::pick(const InetAddress &to) const
+ throw()
+{
+ Mutex::Lock _l(_ports_m);
+ try {
+ std::vector< std::map< Port,DemarcPortObj >::const_iterator > possibilities;
+ for(std::map< Port,DemarcPortObj >::const_iterator pe(_ports.begin());pe!=_ports.end();++pe) {
+ switch (pe->second.type) {
+ case PORT_TYPE_UDP_SOCKET_V4:
+ if (to.isV4())
+ possibilities.push_back(pe);
+ break;
+ case PORT_TYPE_UDP_SOCKET_V6:
+ if (to.isV6())
+ possibilities.push_back(pe);
+ break;
+ default:
+ break;
+ }
+ }
+ if (possibilities.size())
+ return possibilities[Utils::randomInt<unsigned int>() % possibilities.size()]->first;
+ else return NULL_PORT;
+ } catch ( ... ) {
+ return NULL_PORT;
+ }
+}
+
+Demarc::Port Demarc::send(Demarc::Port fromPort,const InetAddress &to,const void *data,unsigned int len,int hopLimit) const
+ throw()
+{
+ _ports_m.lock();
+
+ std::map< Port,DemarcPortObj >::const_iterator pe(_ports.find(fromPort));
+ if (pe == _ports.end()) {
+ try {
+ std::vector< std::map< Port,DemarcPortObj >::const_iterator > possibilities;
+ for(pe=_ports.begin();pe!=_ports.end();++pe) {
+ switch (pe->second.type) {
+ case PORT_TYPE_UDP_SOCKET_V4:
+ if (to.isV4())
+ possibilities.push_back(pe);
+ break;
+ case PORT_TYPE_UDP_SOCKET_V6:
+ if (to.isV6())
+ possibilities.push_back(pe);
+ break;
+ default:
+ break;
+ }
+ }
+ if (possibilities.size())
+ pe = possibilities[Utils::randomInt<unsigned int>() % possibilities.size()];
+ else {
+ _ports_m.unlock();
+ return NULL_PORT;
+ }
+ } catch ( ... ) {
+ _ports_m.unlock();
+ return NULL_PORT;
+ }
+ }
+
+ switch (pe->second.type) {
+ case PORT_TYPE_UDP_SOCKET_V4:
+ case PORT_TYPE_UDP_SOCKET_V6:
+ _ports_m.unlock();
+ if (((UdpSocket *)pe->second.obj)->send(to,data,len,hopLimit))
+ return pe->first;
+ return NULL_PORT;
+ default:
+ break;
+ }
+
+ _ports_m.unlock();
+ return NULL_PORT;
+}
+
+void Demarc::_CBudpSocketPacketHandler(UdpSocket *sock,void *arg,const InetAddress &from,const void *data,unsigned int len)
+{
+ ((DemarcPortObj *)arg)->parent->_r->sw->onRemotePacket(((DemarcPortObj *)arg)->port,from,Buffer<4096>(data,len));
+}
+
+} // namespace ZeroTier
diff --git a/node/Demarc.hpp b/node/Demarc.hpp
new file mode 100644
index 00000000..e13ed0cb
--- /dev/null
+++ b/node/Demarc.hpp
@@ -0,0 +1,168 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_DEMARC_HPP
+#define _ZT_DEMARC_HPP
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <map>
+#include <string>
+#include "Mutex.hpp"
+#include "InetAddress.hpp"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+class UdpSocket;
+
+/**
+ * Local demarcation point
+ *
+ * This holds and provides unique identifiers for all local communication
+ * endpoints, such as UDP sockets, raw Ethernet sockets, tunnels to a relay
+ * server, etc. It permits other code to refer to these via Port and forget
+ * about what they actually are.
+ *
+ * All ports are closed when this class is destroyed.
+ */
+class Demarc
+{
+public:
+ /**
+ * Local demarcation port
+ */
+ typedef uint64_t Port;
+
+ /**
+ * Port identifier used to refer to any port
+ */
+ static const Port ANY_PORT = (Port)0xffffffffffffffffULL;
+
+ /**
+ * Port identifier used to refer to null port / port not found
+ */
+ static const Port NULL_PORT = (Port)0;
+
+ Demarc(const RuntimeEnvironment *renv);
+ ~Demarc();
+
+ /**
+ * Describe a port
+ *
+ * This can describe even ports that are not bound, e.g. from serialized
+ * data.
+ *
+ * @param p Port
+ * @return Human-readable description of port
+ */
+ static std::string describe(Port p)
+ throw();
+
+ /**
+ * @param p Port to check
+ * @return True if this port is bound/connected/etc.
+ */
+ bool has(Port p) const
+ throw();
+
+ /**
+ * Bind local UDP port for both IPv4 and IPv6 traffic
+ *
+ * @param localPort Local IP port
+ * @return True if successfully bound, or if already bound
+ */
+ bool bindLocalUdp(unsigned int localPort)
+ throw();
+
+ /**
+ * Pick a port to send to an address of a given type
+ *
+ * @param to Destination address
+ * @return Port or NULL_PORT if none
+ */
+ Port pick(const InetAddress &to) const
+ throw();
+
+ /**
+ * Send a packet
+ *
+ * If fromPort is ANY_PORT or if the port is not found, a random port is
+ * chosen from those available matching the characteristics of the address
+ * in 'to'.
+ *
+ * @param fromPort Port to send from
+ * @param to Destination IP/port
+ * @param data Data to send
+ * @param len Length of data in bytes
+ * @param hopLimit IP hop limit for UDP packets or -1 for max/unlimited
+ * @return Port actually sent from or NULL_PORT on failure
+ */
+ Port send(Port fromPort,const InetAddress &to,const void *data,unsigned int len,int hopLimit) const
+ throw();
+
+ /**
+ * @param p Port
+ * @return 64-bit integer suitable for serialization
+ */
+ static inline uint64_t portToInt(const Port p) throw() { return (uint64_t)p; }
+
+ /**
+ * @param p 64-bit integer from serialized representation
+ * @return Port suitable for use in code
+ */
+ static inline Port intToPort(const uint64_t p) throw() { return (Port)p; }
+
+private:
+ const RuntimeEnvironment *_r;
+
+ static void _CBudpSocketPacketHandler(UdpSocket *sock,void *arg,const InetAddress &from,const void *data,unsigned int len);
+
+ enum DemarcPortType
+ {
+ PORT_TYPE_UDP_SOCKET_V4 = 1,
+ PORT_TYPE_UDP_SOCKET_V6 = 2,
+ PORT_TYPE_LOCAL_ETHERNET = 3,
+ PORT_TYPE_RELAY_TUNNEL = 4
+ };
+
+ // Variant holding instances of UdpSocket, etc.
+ struct DemarcPortObj
+ {
+ Demarc::Port port;
+ Demarc *parent;
+ void *obj;
+ DemarcPortType type;
+ };
+
+ std::map< Port,DemarcPortObj > _ports;
+ Mutex _ports_m;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/EllipticCurveKey.hpp b/node/EllipticCurveKey.hpp
new file mode 100644
index 00000000..5a7b895f
--- /dev/null
+++ b/node/EllipticCurveKey.hpp
@@ -0,0 +1,124 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_ELLIPTICCURVEKEY_H
+#define _ZT_ELLIPTICCURVEKEY_H
+
+#include <string>
+#include <algorithm>
+#include <string.h>
+#include "Utils.hpp"
+
+/**
+ * Key type ID for identifying our use of NIST-P-521
+ *
+ * If in the future other types of keys are supported (post-quantum crypto?)
+ * then we'll need a key type 2, etc. When keys are stored in the database
+ * they are prefixed by this key type ID byte.
+ */
+#define ZT_KEY_TYPE 1
+
+#define ZT_EC_OPENSSL_CURVE NID_secp521r1
+#define ZT_EC_CURVE_NAME "NIST-P-521"
+#define ZT_EC_PRIME_BYTES 66
+#define ZT_EC_PUBLIC_KEY_BYTES (ZT_EC_PRIME_BYTES + 1)
+#define ZT_EC_PRIVATE_KEY_BYTES ZT_EC_PRIME_BYTES
+#define ZT_EC_MAX_BYTES ZT_EC_PUBLIC_KEY_BYTES
+
+namespace ZeroTier {
+
+class EllipticCurveKeyPair;
+
+/**
+ * An elliptic curve public or private key
+ */
+class EllipticCurveKey
+{
+ friend class EllipticCurveKeyPair;
+
+public:
+ EllipticCurveKey()
+ throw() :
+ _bytes(0)
+ {
+ }
+
+ EllipticCurveKey(const void *data,unsigned int len)
+ throw()
+ {
+ if (len <= ZT_EC_MAX_BYTES) {
+ _bytes = len;
+ memcpy(_key,data,len);
+ } else _bytes = 0;
+ }
+
+ EllipticCurveKey(const EllipticCurveKey &k)
+ throw()
+ {
+ _bytes = k._bytes;
+ memcpy(_key,k._key,_bytes);
+ }
+
+ inline EllipticCurveKey &operator=(const EllipticCurveKey &k)
+ throw()
+ {
+ _bytes = k._bytes;
+ memcpy(_key,k._key,_bytes);
+ return *this;
+ }
+
+ inline void set(const void *data,unsigned int len)
+ throw()
+ {
+ if (len <= ZT_EC_MAX_BYTES) {
+ _bytes = len;
+ memcpy(_key,data,len);
+ } else _bytes = 0;
+ }
+
+ inline const unsigned char *data() const throw() { return _key; }
+ inline unsigned int size() const throw() { return _bytes; }
+ inline std::string toHex() const throw() { return Utils::hex(_key,_bytes); }
+
+ inline unsigned char operator[](const unsigned int i) const throw() { return _key[i]; }
+
+ inline bool operator==(const EllipticCurveKey &k) const throw() { return ((_bytes == k._bytes)&&(!memcmp(_key,k._key,_bytes))); }
+ inline bool operator<(const EllipticCurveKey &k) const throw() { return std::lexicographical_compare(_key,&_key[_bytes],k._key,&k._key[k._bytes]); }
+ inline bool operator!=(const EllipticCurveKey &k) const throw() { return !(*this == k); }
+ inline bool operator>(const EllipticCurveKey &k) const throw() { return (k < *this); }
+ inline bool operator<=(const EllipticCurveKey &k) const throw() { return !(k < *this); }
+ inline bool operator>=(const EllipticCurveKey &k) const throw() { return !(*this < k); }
+
+private:
+ unsigned int _bytes;
+ unsigned char _key[ZT_EC_MAX_BYTES];
+};
+
+} // namespace ZeroTier
+
+#endif
+
diff --git a/node/EllipticCurveKeyPair.cpp b/node/EllipticCurveKeyPair.cpp
new file mode 100644
index 00000000..bed0725e
--- /dev/null
+++ b/node/EllipticCurveKeyPair.cpp
@@ -0,0 +1,374 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/bn.h>
+#include <openssl/obj_mac.h>
+#include <openssl/rand.h>
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/ecdsa.h>
+#include <openssl/sha.h>
+
+#include "EllipticCurveKey.hpp"
+#include "EllipticCurveKeyPair.hpp"
+
+namespace ZeroTier {
+
+class _EC_Group
+{
+public:
+ _EC_Group()
+ {
+ g = EC_GROUP_new_by_curve_name(ZT_EC_OPENSSL_CURVE);
+ }
+ ~_EC_Group() {}
+ EC_GROUP *g;
+};
+static _EC_Group ZT_EC_GROUP;
+
+/* Key derivation function */
+static void *_zt_EC_KDF(const void *in,size_t inlen,void *out,size_t *outlen)
+{
+ SHA256_CTX sha;
+ unsigned char dig[SHA256_DIGEST_LENGTH];
+
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,(const unsigned char *)in,inlen);
+ SHA256_Final(dig,&sha);
+ for(unsigned long i=0,k=0;i<(unsigned long)*outlen;) {
+ if (k == SHA256_DIGEST_LENGTH) {
+ k = 0;
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,(const unsigned char *)in,inlen);
+ SHA256_Update(&sha,dig,SHA256_DIGEST_LENGTH);
+ SHA256_Final(dig,&sha);
+ }
+ ((unsigned char *)out)[i++] = dig[k++];
+ }
+
+ return out;
+}
+
+EllipticCurveKeyPair::EllipticCurveKeyPair() :
+ _pub(),
+ _priv(),
+ _internal_key((void *)0)
+{
+}
+
+EllipticCurveKeyPair::EllipticCurveKeyPair(const EllipticCurveKeyPair &pair) :
+ _pub(pair._pub),
+ _priv(pair._priv),
+ _internal_key((void *)0)
+{
+}
+
+EllipticCurveKeyPair::EllipticCurveKeyPair(const EllipticCurveKey &pubk,const EllipticCurveKey &privk) :
+ _pub(pubk),
+ _priv(privk),
+ _internal_key((void *)0)
+{
+}
+
+EllipticCurveKeyPair::~EllipticCurveKeyPair()
+{
+ if (_internal_key)
+ EC_KEY_free((EC_KEY *)_internal_key);
+}
+
+const EllipticCurveKeyPair &EllipticCurveKeyPair::operator=(const EllipticCurveKeyPair &pair)
+{
+ if (_internal_key)
+ EC_KEY_free((EC_KEY *)_internal_key);
+ _pub = pair._pub;
+ _priv = pair._priv;
+ _internal_key = (void *)0;
+ return *this;
+}
+
+bool EllipticCurveKeyPair::generate()
+{
+ unsigned char tmp[16384];
+ EC_KEY *key;
+ int len;
+
+ // Make sure OpenSSL libcrypto has sufficient randomness (on most
+ // platforms it auto-seeds, so this is a sanity check).
+ if (!RAND_status()) {
+#if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+ FILE *rf = fopen("/dev/urandom","r");
+ if (rf) {
+ fread(tmp,sizeof(tmp),1,rf);
+ fclose(rf);
+ } else {
+ fprintf(stderr,"WARNING: cannot open /dev/urandom\n");
+ for(unsigned int i=0;i<sizeof(tmp);++i)
+ tmp[i] = (unsigned char)(rand() >> 3);
+ }
+ RAND_seed(tmp,sizeof(tmp));
+#else
+#ifdef _WIN32
+ error need win32;
+#else
+ error;
+#endif
+#endif
+ }
+
+ key = EC_KEY_new();
+ if (!key) return false;
+
+ if (!EC_KEY_set_group(key,ZT_EC_GROUP.g)) {
+ EC_KEY_free(key);
+ return false;
+ }
+
+ if (!EC_KEY_generate_key(key)) {
+ EC_KEY_free(key);
+ return false;
+ }
+
+ memset(_priv._key,0,sizeof(_priv._key));
+ len = BN_num_bytes(EC_KEY_get0_private_key(key));
+ if ((len > ZT_EC_PRIME_BYTES)||(len < 0)) {
+ EC_KEY_free(key);
+ return false;
+ }
+ BN_bn2bin(EC_KEY_get0_private_key(key),&(_priv._key[ZT_EC_PRIME_BYTES - len]));
+ _priv._bytes = ZT_EC_PRIME_BYTES;
+
+ memset(_pub._key,0,sizeof(_pub._key));
+ len = EC_POINT_point2oct(ZT_EC_GROUP.g,EC_KEY_get0_public_key(key),POINT_CONVERSION_COMPRESSED,_pub._key,sizeof(_pub._key),0);
+ if (len != ZT_EC_PUBLIC_KEY_BYTES) {
+ EC_KEY_free(key);
+ return false;
+ }
+ _pub._bytes = ZT_EC_PUBLIC_KEY_BYTES;
+
+ if (_internal_key)
+ EC_KEY_free((EC_KEY *)_internal_key);
+ _internal_key = key;
+
+ return true;
+}
+
+bool EllipticCurveKeyPair::agree(const EllipticCurveKey &theirPublicKey,unsigned char *agreedUponKey,unsigned int agreedUponKeyLength) const
+{
+ if (theirPublicKey._bytes != ZT_EC_PUBLIC_KEY_BYTES)
+ return false;
+
+ if (!_internal_key) {
+ if (!(const_cast <EllipticCurveKeyPair *> (this))->initInternalKey())
+ return false;
+ }
+
+ EC_POINT *pub = EC_POINT_new(ZT_EC_GROUP.g);
+ if (!pub)
+ return false;
+ EC_POINT_oct2point(ZT_EC_GROUP.g,pub,theirPublicKey._key,ZT_EC_PUBLIC_KEY_BYTES,0);
+
+ int i = ECDH_compute_key(agreedUponKey,agreedUponKeyLength,pub,(EC_KEY *)_internal_key,&_zt_EC_KDF);
+ EC_POINT_free(pub);
+
+ return (i == (int)agreedUponKeyLength);
+}
+
+std::string EllipticCurveKeyPair::sign(const void *sha256) const
+{
+ unsigned char buf[256];
+ std::string sigbin;
+
+ if (!_internal_key) {
+ if (!(const_cast <EllipticCurveKeyPair *> (this))->initInternalKey())
+ return std::string();
+ }
+
+ ECDSA_SIG *sig = ECDSA_do_sign((const unsigned char *)sha256,SHA256_DIGEST_LENGTH,(EC_KEY *)_internal_key);
+ if (!sig)
+ return std::string();
+
+ int rlen = BN_num_bytes(sig->r);
+ if ((rlen > 255)||(rlen <= 0)) {
+ ECDSA_SIG_free(sig);
+ return std::string();
+ }
+ sigbin.push_back((char)rlen);
+ BN_bn2bin(sig->r,buf);
+ sigbin.append((const char *)buf,rlen);
+
+ int slen = BN_num_bytes(sig->s);
+ if ((slen > 255)||(slen <= 0)) {
+ ECDSA_SIG_free(sig);
+ return std::string();
+ }
+ sigbin.push_back((char)slen);
+ BN_bn2bin(sig->s,buf);
+ sigbin.append((const char *)buf,slen);
+
+ ECDSA_SIG_free(sig);
+
+ return sigbin;
+}
+
+std::string EllipticCurveKeyPair::sign(const void *data,unsigned int len) const
+{
+ SHA256_CTX sha;
+ unsigned char dig[SHA256_DIGEST_LENGTH];
+
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,(const unsigned char *)data,len);
+ SHA256_Final(dig,&sha);
+
+ return sign(dig);
+}
+
+bool EllipticCurveKeyPair::verify(const void *sha256,const EllipticCurveKey &pk,const void *sigbytes,unsigned int siglen)
+{
+ bool result = false;
+ ECDSA_SIG *sig = (ECDSA_SIG *)0;
+ EC_POINT *pub = (EC_POINT *)0;
+ EC_KEY *key = (EC_KEY *)0;
+ int rlen,slen;
+
+ if (!siglen)
+ goto verify_sig_return;
+ rlen = ((const unsigned char *)sigbytes)[0];
+ if (!rlen)
+ goto verify_sig_return;
+ if (siglen < (unsigned int)(rlen + 2))
+ goto verify_sig_return;
+ slen = ((const unsigned char *)sigbytes)[rlen + 1];
+ if (!slen)
+ goto verify_sig_return;
+ if (siglen < (unsigned int)(rlen + slen + 2))
+ goto verify_sig_return;
+
+ sig = ECDSA_SIG_new();
+ if (!sig)
+ goto verify_sig_return;
+
+ BN_bin2bn((const unsigned char *)sigbytes + 1,rlen,sig->r);
+ BN_bin2bn((const unsigned char *)sigbytes + (1 + rlen + 1),slen,sig->s);
+
+ pub = EC_POINT_new(ZT_EC_GROUP.g);
+ if (!pub)
+ goto verify_sig_return;
+ EC_POINT_oct2point(ZT_EC_GROUP.g,pub,pk._key,ZT_EC_PUBLIC_KEY_BYTES,0);
+
+ key = EC_KEY_new();
+ if (!key)
+ goto verify_sig_return;
+ if (!EC_KEY_set_group(key,ZT_EC_GROUP.g))
+ goto verify_sig_return;
+ EC_KEY_set_public_key(key,pub);
+
+ result = (ECDSA_do_verify((const unsigned char *)sha256,SHA256_DIGEST_LENGTH,sig,key) == 1);
+
+verify_sig_return:
+ if (key)
+ EC_KEY_free(key);
+ if (pub)
+ EC_POINT_free(pub);
+ if (sig)
+ ECDSA_SIG_free(sig);
+
+ return result;
+}
+
+bool EllipticCurveKeyPair::verify(const void *data,unsigned int len,const EllipticCurveKey &pk,const void *sigbytes,unsigned int siglen)
+{
+ SHA256_CTX sha;
+ unsigned char dig[SHA256_DIGEST_LENGTH];
+
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,(const unsigned char *)data,len);
+ SHA256_Final(dig,&sha);
+
+ return verify(dig,pk,sigbytes,siglen);
+}
+
+bool EllipticCurveKeyPair::initInternalKey()
+{
+ EC_KEY *key;
+ EC_POINT *kxy;
+ BIGNUM *pn;
+
+ if (_priv._bytes != ZT_EC_PRIME_BYTES) return false;
+ if (_pub._bytes != ZT_EC_PUBLIC_KEY_BYTES) return false;
+
+ key = EC_KEY_new();
+ if (!key) return false;
+
+ if (!EC_KEY_set_group(key,ZT_EC_GROUP.g)) {
+ EC_KEY_free(key);
+ return false;
+ }
+
+ pn = BN_new();
+ if (!pn) {
+ EC_KEY_free(key);
+ return false;
+ }
+ if (!BN_bin2bn(_priv._key,ZT_EC_PRIME_BYTES,pn)) {
+ BN_free(pn);
+ EC_KEY_free(key);
+ return false;
+ }
+ if (!EC_KEY_set_private_key(key,pn)) {
+ BN_free(pn);
+ EC_KEY_free(key);
+ return false;
+ }
+ BN_free(pn);
+
+ kxy = EC_POINT_new(ZT_EC_GROUP.g);
+ if (!kxy) {
+ EC_KEY_free(key);
+ return false;
+ }
+ EC_POINT_oct2point(ZT_EC_GROUP.g,kxy,_pub._key,ZT_EC_PUBLIC_KEY_BYTES,0);
+ if (!EC_KEY_set_public_key(key,kxy)) {
+ EC_POINT_free(kxy);
+ EC_KEY_free(key);
+ return false;
+ }
+ EC_POINT_free(kxy);
+
+ if (_internal_key)
+ EC_KEY_free((EC_KEY *)_internal_key);
+ _internal_key = key;
+
+ return true;
+}
+
+} // namespace ZeroTier
+
diff --git a/node/EllipticCurveKeyPair.hpp b/node/EllipticCurveKeyPair.hpp
new file mode 100644
index 00000000..2649f4c4
--- /dev/null
+++ b/node/EllipticCurveKeyPair.hpp
@@ -0,0 +1,128 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_ELLIPTICCURVEKEYPAIR_HPP
+#define _ZT_ELLIPTICCURVEKEYPAIR_HPP
+
+#include <string>
+#include "EllipticCurveKey.hpp"
+
+namespace ZeroTier {
+
+/**
+ * An elliptic curve key pair supporting generation and key agreement
+ */
+class EllipticCurveKeyPair
+{
+public:
+ EllipticCurveKeyPair();
+ EllipticCurveKeyPair(const EllipticCurveKeyPair &pair);
+ EllipticCurveKeyPair(const EllipticCurveKey &pubk,const EllipticCurveKey &privk);
+ ~EllipticCurveKeyPair();
+
+ const EllipticCurveKeyPair &operator=(const EllipticCurveKeyPair &pair);
+
+ /**
+ * Fill this structure with a newly generated public/private key pair
+ *
+ * @return True if key generation is successful
+ */
+ bool generate();
+
+ /**
+ * Perform elliptic curve key agreement
+ *
+ * @param theirPublicKey Remote side's public key
+ * @param agreedUponKey Buffer to fill with agreed-upon symmetric key
+ * @param agreedUponKeyLength Number of bytes to generate
+ * @return True if key agreement is successful
+ */
+ bool agree(const EllipticCurveKey &theirPublicKey,unsigned char *agreedUponKey,unsigned int agreedUponKeyLength) const;
+
+ /**
+ * Sign a SHA256 hash
+ *
+ * @param sha256 Pointer to 256-bit / 32-byte SHA hash to sign
+ * @return ECDSA signature (r and s in binary format, each prefixed by an 8-bit size)
+ */
+ std::string sign(const void *sha256) const;
+
+ /**
+ * Sign something with this pair's private key, computing its hash first
+ *
+ * @param data Data to hash and sign
+ * @param len Length of data
+ * @return Signature bytes
+ */
+ std::string sign(const void *data,unsigned int len) const;
+
+ /**
+ * Verify a signature
+ *
+ * @param sha256 Pointer to 256-bit / 32-byte SHA hash to verify
+ * @param pk Public key to verify against
+ * @param sigbytes Signature bytes
+ * @param siglen Length of signature
+ */
+ static bool verify(const void *sha256,const EllipticCurveKey &pk,const void *sigbytes,unsigned int siglen);
+
+ /**
+ * Verify a signature
+ *
+ * @param data Data to verify
+ * @param len Length of data
+ * @param pk Public key to verify against
+ * @param sigbytes Signature bytes
+ * @param siglen Length of signature
+ */
+ static bool verify(const void *data,unsigned int len,const EllipticCurveKey &pk,const void *sigbytes,unsigned int siglen);
+
+ inline bool operator==(const EllipticCurveKeyPair &kp) const
+ throw()
+ {
+ return ((_pub == kp._pub)&&(_priv == kp._priv));
+ }
+ inline bool operator!=(const EllipticCurveKeyPair &kp) const
+ throw()
+ {
+ return ((_pub != kp._pub)||(_priv != kp._priv));
+ }
+
+ inline const EllipticCurveKey &pub() const throw() { return _pub; }
+ inline const EllipticCurveKey &priv() const throw() { return _priv; }
+
+private:
+ bool initInternalKey();
+
+ EllipticCurveKey _pub;
+ EllipticCurveKey _priv;
+ void *_internal_key;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/EthernetTap.cpp b/node/EthernetTap.cpp
new file mode 100644
index 00000000..e50e48d3
--- /dev/null
+++ b/node/EthernetTap.cpp
@@ -0,0 +1,678 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <iostream>
+#include <string>
+#include "EthernetTap.hpp"
+#include "Logger.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Mutex.hpp"
+
+/* ======================================================================== */
+#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+/* ======================================================================== */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <net/if_arp.h>
+#include <arpa/inet.h>
+
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/if_addr.h>
+#include <linux/if_ether.h>
+
+#define ZT_ETHERTAP_IP_COMMAND "/sbin/ip"
+#define ZT_ETHERTAP_SYSCTL_COMMAND "/sbin/sysctl"
+
+namespace ZeroTier {
+
+static Mutex __tapCreateLock;
+
+EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned int mtu)
+ throw(std::runtime_error) :
+ _mac(mac),
+ _mtu(mtu),
+ _r(renv),
+ _putBuf((unsigned char *)0),
+ _getBuf((unsigned char *)0),
+ _fd(0),
+ _isReading(false)
+{
+ char procpath[128];
+ Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
+
+ _fd = ::open("/dev/net/tun",O_RDWR);
+ if (_fd <= 0)
+ throw std::runtime_error("could not open TUN/TAP device");
+
+ struct ifreq ifr;
+ memset(&ifr,0,sizeof(ifr));
+
+ { // pick an unused device name
+ int devno = 0;
+ struct stat sbuf;
+ do {
+ sprintf(ifr.ifr_name,"zt%d",devno++);
+ sprintf(procpath,"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name);
+ } while (stat(procpath,&sbuf) == 0);
+ }
+
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ if (ioctl(_fd,TUNSETIFF,(void *)&ifr) < 0) {
+ ::close(_fd);
+ throw std::runtime_error("unable to configure TUN/TAP device for TAP operation");
+ }
+
+ strcpy(_dev,ifr.ifr_name);
+
+ ioctl(_fd,TUNSETPERSIST,0); // valgrind may generate a false alarm here
+
+ // Open an arbitrary socket to talk to netlink
+ int sock = socket(AF_INET,SOCK_DGRAM,0);
+ if (sock <= 0) {
+ ::close(_fd);
+ throw std::runtime_error("unable to open netlink socket");
+ }
+
+ // Set MAC address
+ ifr.ifr_ifru.ifru_hwaddr.sa_family = ARPHRD_ETHER;
+ memcpy(ifr.ifr_ifru.ifru_hwaddr.sa_data,mac.data,6);
+ if (ioctl(sock,SIOCSIFHWADDR,(void *)&ifr) < 0) {
+ ::close(_fd);
+ ::close(sock);
+ throw std::runtime_error("unable to configure TAP hardware (MAC) address");
+ return;
+ }
+
+ // Set MTU
+ ifr.ifr_ifru.ifru_mtu = (int)mtu;
+ if (ioctl(sock,SIOCSIFMTU,(void *)&ifr) < 0) {
+ ::close(_fd);
+ ::close(sock);
+ throw std::runtime_error("unable to configure TAP MTU");
+ }
+
+ if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) {
+ ::close(_fd);
+ throw std::runtime_error("unable to set flags on file descriptor for TAP device");
+ }
+
+ /* Bring interface up */
+ if (ioctl(sock,SIOCGIFFLAGS,(void *)&ifr) < 0) {
+ ::close(_fd);
+ ::close(sock);
+ throw std::runtime_error("unable to get TAP interface flags");
+ }
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(sock,SIOCSIFFLAGS,(void *)&ifr) < 0) {
+ ::close(_fd);
+ ::close(sock);
+ throw std::runtime_error("unable to set TAP interface flags");
+ }
+
+ ::close(sock);
+
+ _putBuf = new unsigned char[((mtu + 16) * 2)];
+ _getBuf = _putBuf + (mtu + 16);
+
+ TRACE("tap %s created",_dev);
+}
+
+EthernetTap::~EthernetTap()
+{
+ this->close();
+ delete [] _putBuf;
+}
+
+static bool ___removeIp(const char *_dev,std::set<InetAddress> &_ips,const InetAddress &ip)
+{
+ long cpid;
+ if ((cpid = (long)fork()) == 0) {
+ execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","del",ip.toString().c_str(),"dev",_dev,(const char *)0);
+ exit(1); /* not reached unless exec fails */
+ } else {
+ int exitcode = 1;
+ waitpid(cpid,&exitcode,0);
+ if (exitcode == 0) {
+ _ips.erase(ip);
+ return true;
+ } else return false;
+ }
+}
+
+bool EthernetTap::addIP(const InetAddress &ip)
+{
+ Mutex::Lock _l(_ips_m);
+
+ if (!ip.isValid())
+ return false;
+ if (_ips.count(ip) > 0)
+ return true;
+
+ // Remove and reconfigure if address is the same but netmask is different
+ for(std::set<InetAddress>::iterator i(_ips.begin());i!=_ips.end();++i) {
+ if (i->ipsEqual(ip)) {
+ ___removeIp(_dev,_ips,*i);
+ break;
+ }
+ }
+
+ int cpid;
+ if ((cpid = (int)fork()) == 0) {
+ execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","add",ip.toString().c_str(),"dev",_dev,(const char *)0);
+ exit(-1);
+ } else {
+ int exitcode = -1;
+ waitpid(cpid,&exitcode,0);
+ if (exitcode == 0) {
+ _ips.insert(ip);
+ return true;
+ } else return false;
+ }
+
+ return false;
+}
+
+bool EthernetTap::removeIP(const InetAddress &ip)
+{
+ Mutex::Lock _l(_ips_m);
+ if (_ips.count(ip) > 0)
+ return ___removeIp(_dev,_ips,ip);
+ return false;
+}
+
+void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
+{
+ if ((_fd > 0)&&(len <= _mtu)) {
+ for(int i=0;i<6;++i)
+ _putBuf[i] = to.data[i];
+ for(int i=0;i<6;++i)
+ _putBuf[i+6] = from.data[i];
+ *((uint16_t *)(_putBuf + 12)) = htons((uint16_t)etherType);
+ memcpy(_putBuf + 14,data,len);
+ ::write(_fd,_putBuf,len + 14);
+ }
+}
+
+unsigned int EthernetTap::get(MAC &from,MAC &to,unsigned int &etherType,void *buf)
+{
+ for(;;) {
+ if (_fd > 0) {
+ _isReading_m.lock();
+ _isReading = true;
+ _isReadingThreadId = pthread_self();
+ _isReading_m.unlock();
+
+ int n = (int)::read(_fd,_getBuf,_mtu + 14);
+
+ _isReading_m.lock();
+ _isReading = false;
+ _isReading_m.unlock();
+
+ if (n > 14) {
+ for(int i=0;i<6;++i)
+ to.data[i] = _getBuf[i];
+ for(int i=0;i<6;++i)
+ from.data[i] = _getBuf[i + 6];
+ etherType = ntohs(((uint16_t *)_getBuf)[6]);
+ n -= 14;
+ memcpy(buf,_getBuf + 14,n);
+ return (unsigned int)n;
+ } else if (n < 0) {
+ if (_fd <= 0)
+ break;
+ else if ((errno == EINTR)||(errno == ETIMEDOUT))
+ continue;
+ else {
+ TRACE("unexpected error reading from tap: %s",strerror(errno));
+ ::close(_fd);
+ _fd = 0;
+ break;
+ }
+ } else {
+ TRACE("incomplete read from tap: %d bytes",n);
+ continue;
+ }
+ }
+ }
+ return 0;
+}
+
+std::string EthernetTap::deviceName()
+{
+ return std::string(_dev);
+}
+
+bool EthernetTap::open() const
+{
+ return (_fd > 0);
+}
+
+void EthernetTap::close()
+{
+ Mutex::Lock _l(__tapCreateLock); // also prevent create during close()
+ if (_fd > 0) {
+ int f = _fd;
+ _fd = 0;
+ ::close(f);
+
+ _isReading_m.lock();
+ if (_isReading)
+ pthread_kill(_isReadingThreadId,SIGUSR2);
+ _isReading_m.unlock();
+ }
+}
+
+bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
+{
+ char *ptr,*ptr2;
+ unsigned char mac[6];
+ std::set<MulticastGroup> newGroups;
+
+ int fd = ::open("/proc/net/dev_mcast",O_RDONLY);
+ if (fd > 0) {
+ char buf[131072];
+ int n = (int)::read(fd,buf,sizeof(buf));
+ if ((n > 0)&&(n < (int)sizeof(buf))) {
+ buf[n] = (char)0;
+ for(char *l=strtok_r(buf,"\r\n",&ptr);(l);l=strtok_r((char *)0,"\r\n",&ptr)) {
+ int fno = 0;
+ char *devname = (char *)0;
+ char *mcastmac = (char *)0;
+ for(char *f=strtok_r(l," \t",&ptr2);(f);f=strtok_r((char *)0," \t",&ptr2)) {
+ if (fno == 1)
+ devname = f;
+ else if (fno == 4)
+ mcastmac = f;
+ ++fno;
+ }
+ if ((devname)&&(!strcmp(devname,_dev))&&(mcastmac)&&(Utils::unhex(mcastmac,mac,6) == 6))
+ newGroups.insert(MulticastGroup(MAC(mac),0));
+ }
+ }
+ ::close(fd);
+ }
+
+ {
+ Mutex::Lock _l(_ips_m);
+ for(std::set<InetAddress>::const_iterator i(_ips.begin());i!=_ips.end();++i)
+ newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i));
+ }
+
+ bool changed = false;
+
+ for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) {
+ if (!groups.count(*mg)) {
+ groups.insert(*mg);
+ changed = true;
+ }
+ }
+ for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) {
+ if (!newGroups.count(*mg)) {
+ groups.erase(mg++);
+ changed = true;
+ } else ++mg;
+ }
+
+ return changed;
+}
+
+} // namespace ZeroTier
+
+/* ======================================================================== */
+#elif defined(__APPLE__) /* ----------------------------------------------- */
+/* ======================================================================== */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <ifaddrs.h>
+
+#define ZT_ETHERTAP_IFCONFIG "/sbin/ifconfig"
+#define ZT_MAC_KEXTLOAD "/sbin/kextload"
+#define ZT_MAC_IPCONFIG "/usr/sbin/ipconfig"
+
+namespace ZeroTier {
+
+static Mutex __tapCreateLock;
+
+EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned int mtu)
+ throw(std::runtime_error) :
+ _mac(mac),
+ _mtu(mtu),
+ _r(renv),
+ _putBuf((unsigned char *)0),
+ _getBuf((unsigned char *)0),
+ _fd(0),
+ _isReading(false)
+{
+ char devpath[64],ethaddr[64],mtustr[16];
+ struct stat tmp;
+ Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
+
+ // Check for existence of ZT tap devices, try to load module if not there
+ if (stat("/dev/zt0",&tmp)) {
+ int kextpid;
+ char tmp[4096];
+ strcpy(tmp,_r->homePath.c_str());
+ if ((kextpid = (int)fork()) == 0) {
+ chdir(tmp);
+ execl(ZT_MAC_KEXTLOAD,ZT_MAC_KEXTLOAD,"-q","-repository",tmp,"tap.kext",(const char *)0);
+ exit(-1);
+ } else {
+ int exitcode = -1;
+ waitpid(kextpid,&exitcode,0);
+ usleep(500);
+ }
+ }
+ if (stat("/dev/zt0",&tmp))
+ throw std::runtime_error("/dev/zt# tap devices do not exist and unable to load kernel extension");
+
+ // Open the first available device (ones in use will fail with resource busy)
+ for(int i=0;i<256;++i) {
+ sprintf(devpath,"/dev/zt%d",i);
+ if (stat(devpath,&tmp))
+ throw std::runtime_error("no more TAP devices available");
+ _fd = ::open(devpath,O_RDWR);
+ if (_fd > 0) {
+ sprintf(_dev,"zt%d",i);
+ break;
+ }
+ }
+ if (_fd <= 0)
+ throw std::runtime_error("unable to open TAP device or no more devices available");
+
+ if (fcntl(_fd,F_SETFL,fcntl(_fd,F_GETFL) & ~O_NONBLOCK) == -1) {
+ ::close(_fd);
+ throw std::runtime_error("unable to set flags on file descriptor for TAP device");
+ }
+
+ sprintf(ethaddr,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)mac[0],(int)mac[1],(int)mac[2],(int)mac[3],(int)mac[4],(int)mac[5]);
+ sprintf(mtustr,"%u",mtu);
+
+ // Configure MAC address and MTU, bring interface up
+ int cpid;
+ if ((cpid = (int)fork()) == 0) {
+ execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,"lladdr",ethaddr,"mtu",mtustr,"up",(const char *)0);
+ exit(-1);
+ } else {
+ int exitcode = -1;
+ waitpid(cpid,&exitcode,0);
+ if (exitcode) {
+ ::close(_fd);
+ throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface");
+ }
+ }
+
+ // OSX seems to require that IPv6 be turned on on tap devices
+ if ((cpid = (int)fork()) == 0) {
+ execl(ZT_MAC_IPCONFIG,ZT_MAC_IPCONFIG,"set",_dev,"AUTOMATIC-V6",(const char *)0);
+ exit(-1);
+ } else {
+ int exitcode = -1;
+ waitpid(cpid,&exitcode,0);
+ if (exitcode) {
+ ::close(_fd);
+ throw std::runtime_error("ifconfig failure setting link-layer address and activating tap interface");
+ }
+ }
+
+ _putBuf = new unsigned char[((mtu + 14) * 2)];
+ _getBuf = _putBuf + (mtu + 14);
+}
+
+EthernetTap::~EthernetTap()
+{
+ this->close();
+ delete [] _putBuf;
+}
+
+static bool ___removeIp(const char *_dev,std::set<InetAddress> &_ips,const InetAddress &ip)
+{
+ int cpid;
+ if ((cpid = (int)fork()) == 0) {
+ execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
+ exit(-1);
+ } else {
+ int exitcode = -1;
+ waitpid(cpid,&exitcode,0);
+ if (exitcode == 0) {
+ _ips.erase(ip);
+ return true;
+ } else return false;
+ }
+}
+
+bool EthernetTap::addIP(const InetAddress &ip)
+{
+ Mutex::Lock _l(_ips_m);
+
+ if (!ip)
+ return false;
+ if (_ips.count(ip) > 0)
+ return true;
+
+ // Remove and reconfigure if address is the same but netmask is different
+ for(std::set<InetAddress>::iterator i(_ips.begin());i!=_ips.end();++i) {
+ if (i->ipsEqual(ip)) {
+ ___removeIp(_dev,_ips,*i);
+ break;
+ }
+ }
+
+ int cpid;
+ if ((cpid = (int)fork()) == 0) {
+ execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
+ exit(-1);
+ } else {
+ int exitcode = -1;
+ waitpid(cpid,&exitcode,0);
+ if (exitcode == 0) {
+ _ips.insert(ip);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool EthernetTap::removeIP(const InetAddress &ip)
+{
+ Mutex::Lock _l(_ips_m);
+ if (_ips.count(ip) > 0)
+ return ___removeIp(_dev,_ips,ip);
+ return false;
+}
+
+void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
+{
+ if ((_fd > 0)&&(len <= _mtu)) {
+ for(int i=0;i<6;++i)
+ _putBuf[i] = to.data[i];
+ for(int i=0;i<6;++i)
+ _putBuf[i+6] = from.data[i];
+ *((uint16_t *)(_putBuf + 12)) = htons((uint16_t)etherType);
+ memcpy(_putBuf + 14,data,len);
+ len += 14;
+ int n = (int)::write(_fd,_putBuf,len);
+ if (n <= 0) {
+ LOG("error writing packet to Ethernet tap device: %s",strerror(errno));
+ } else if (n != (int)len) {
+ // Saw this gremlin once, so log it if we see it again... OSX tap
+ // or something seems to have goofy issues with certain MTUs.
+ LOG("WARNING: Apple gremlin: tap write() wrote %d of %u bytes of frame",n,len);
+ }
+ }
+}
+
+unsigned int EthernetTap::get(MAC &from,MAC &to,unsigned int &etherType,void *buf)
+{
+ for(;;) {
+ if (_fd > 0) {
+ _isReading_m.lock();
+ _isReading = true;
+ _isReadingThreadId = pthread_self();
+ _isReading_m.unlock();
+
+ int n = (int)::read(_fd,_getBuf,_mtu + 14);
+
+ _isReading_m.lock();
+ _isReading = false;
+ _isReading_m.unlock();
+
+ if (n > 14) {
+ for(int i=0;i<6;++i)
+ to.data[i] = _getBuf[i];
+ for(int i=0;i<6;++i)
+ from.data[i] = _getBuf[i + 6];
+ etherType = ntohs(((uint16_t *)_getBuf)[6]);
+ n -= 14;
+ memcpy(buf,_getBuf + 14,n);
+ return (unsigned int)n;
+ } else if (n < 0) {
+ if (_fd <= 0)
+ break;
+ else if ((errno == EINTR)||(errno == ETIMEDOUT))
+ continue;
+ else {
+ TRACE("unexpected error reading from tap: %s",strerror(errno));
+ ::close(_fd);
+ _fd = 0;
+ break;
+ }
+ } else {
+ TRACE("incomplete read from tap: %d bytes",n);
+ continue;
+ }
+ }
+ }
+ return 0;
+}
+
+std::string EthernetTap::deviceName()
+{
+ return std::string(_dev);
+}
+
+bool EthernetTap::open() const
+{
+ return (_fd > 0);
+}
+
+void EthernetTap::close()
+{
+ Mutex::Lock _l(__tapCreateLock); // also prevent create during close()
+ if (_fd > 0) {
+ int f = _fd;
+ _fd = 0;
+ ::close(f);
+
+ _isReading_m.lock();
+ if (_isReading)
+ pthread_kill(_isReadingThreadId,SIGUSR2);
+ _isReading_m.unlock();
+ }
+}
+
+bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
+{
+ std::set<MulticastGroup> newGroups;
+ struct ifmaddrs *ifmap = (struct ifmaddrs *)0;
+ if (!getifmaddrs(&ifmap)) {
+ struct ifmaddrs *p = ifmap;
+ while (p) {
+ if (p->ifma_addr->sa_family == AF_LINK) {
+ struct sockaddr_dl *in = (struct sockaddr_dl *)p->ifma_name;
+ struct sockaddr_dl *la = (struct sockaddr_dl *)p->ifma_addr;
+ if ((la->sdl_alen == 6)&&(in->sdl_nlen <= sizeof(_dev))&&(!memcmp(_dev,in->sdl_data,in->sdl_nlen)))
+ newGroups.insert(MulticastGroup(MAC(la->sdl_data + la->sdl_nlen),0));
+ }
+ p = p->ifma_next;
+ }
+ freeifmaddrs(ifmap);
+ }
+
+ {
+ Mutex::Lock _l(_ips_m);
+ for(std::set<InetAddress>::const_iterator i(_ips.begin());i!=_ips.end();++i)
+ newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i));
+ }
+
+ bool changed = false;
+
+ for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) {
+ if (!groups.count(*mg)) {
+ groups.insert(*mg);
+ changed = true;
+ }
+ }
+ for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) {
+ if (!newGroups.count(*mg)) {
+ groups.erase(mg++);
+ changed = true;
+ } else ++mg;
+ }
+
+ return changed;
+}
+
+} // namespace ZeroTier
+
+/* ======================================================================== */
+#elif defined(_WIN32) /* -------------------------------------------------- */
+/* ======================================================================== */
+
+/* ======================================================================== */
+#endif
+/* ======================================================================== */
diff --git a/node/EthernetTap.hpp b/node/EthernetTap.hpp
new file mode 100644
index 00000000..bf1d0eef
--- /dev/null
+++ b/node/EthernetTap.hpp
@@ -0,0 +1,199 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_ETHERNETTAP_HPP
+#define _ZT_ETHERNETTAP_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <map>
+#include <list>
+#include <vector>
+#include <set>
+#include <string>
+#include <stdexcept>
+#include "Array.hpp"
+#include "Utils.hpp"
+#include "InetAddress.hpp"
+#include "NonCopyable.hpp"
+#include "MAC.hpp"
+#include "Constants.hpp"
+#include "Mutex.hpp"
+#include "MulticastGroup.hpp"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * System ethernet tap device
+ */
+class EthernetTap : NonCopyable
+{
+public:
+ /**
+ * Construct a new TAP device
+ *
+ * @param renv Runtime environment
+ * @param mac MAC address of device
+ * @param mtu MTU of device
+ * @throws std::runtime_error Unable to allocate device
+ */
+ EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned int mtu)
+ throw(std::runtime_error);
+
+ ~EthernetTap();
+
+ /**
+ * @return MAC address of this interface
+ */
+ inline const MAC &mac() const throw() { return _mac; }
+
+ /**
+ * @return MTU of this interface
+ */
+ inline unsigned int mtu() const throw() { return _mtu; }
+
+ /**
+ * Add an IP to this interface
+ *
+ * @param ip IP and netmask (netmask stored in port field)
+ * @return True if IP added successfully
+ */
+ bool addIP(const InetAddress &ip);
+
+ /**
+ * Remove an IP from this interface
+ *
+ * @param ip IP and netmask (netmask stored in port field)
+ * @return True if IP removed successfully
+ */
+ bool removeIP(const InetAddress &ip);
+
+ /**
+ * @return Set of IP addresses / netmasks
+ */
+ inline std::set<InetAddress> ips() const
+ {
+ Mutex::Lock _l(_ips_m);
+ return _ips;
+ }
+
+ /**
+ * Set this tap's IP addresses to exactly this set of IPs
+ *
+ * New IPs are created, ones not in this list are removed.
+ *
+ * @param ips IP addresses with netmask in port field
+ */
+ inline void setIps(const std::set<InetAddress> &allIps)
+ {
+ for(std::set<InetAddress>::iterator i(allIps.begin());i!=allIps.end();++i)
+ addIP(*i);
+ std::set<InetAddress> myIps(ips());
+ for(std::set<InetAddress>::iterator i(myIps.begin());i!=myIps.end();++i) {
+ if (!allIps.count(*i))
+ removeIP(*i);
+ }
+ }
+
+ /**
+ * Put a frame, making it available to the OS for processing
+ *
+ * @param from MAC address from which frame originated
+ * @param to MAC address of destination (typically MAC of tap itself)
+ * @param etherType Ethernet protocol ID
+ * @param data Frame payload
+ * @param len Length of frame
+ */
+ void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
+
+ /**
+ * Get the next packet from the interface, blocking if none is available.
+ *
+ * @param from Filled with MAC address of source (normally our own)
+ * @param to Filled with MAC address of destination
+ * @param etherType Filled with Ethernet frame type
+ * @param buf Buffer to fill (must have room for MTU bytes)
+ * @return Number of bytes read or 0 if none
+ */
+ unsigned int get(MAC &from,MAC &to,unsigned int &etherType,void *buf);
+
+ /**
+ * @return OS-specific device or connection name
+ */
+ std::string deviceName();
+
+ /**
+ * @return True if tap is open
+ */
+ bool open() const;
+
+ /**
+ * Close this tap, invalidating the object and causing get() to abort
+ */
+ void close();
+
+ /**
+ * Fill or modify a set to contain multicast groups for this device
+ *
+ * This populates a set or, if already populated, modifies it to contain
+ * only multicast groups in which this device is interested.
+ *
+ * @param groups Set to modify in place
+ * @return True if set was changed since last call
+ */
+ bool updateMulticastGroups(std::set<MulticastGroup> &groups);
+
+private:
+ const MAC _mac;
+ const unsigned int _mtu;
+
+ const RuntimeEnvironment *_r;
+
+ std::set<InetAddress> _ips;
+ Mutex _ips_m;
+
+#if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+
+ char _dev[16];
+ unsigned char *_putBuf;
+ unsigned char *_getBuf;
+ int _fd;
+
+ bool _isReading;
+ pthread_t _isReadingThreadId;
+ Mutex _isReading_m;
+
+#elif defined(_WIN32) /* -------------------------------------------------- */
+
+#endif /* ----------------------------------------------------------------- */
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/HMAC.cpp b/node/HMAC.cpp
new file mode 100644
index 00000000..d8a756a9
--- /dev/null
+++ b/node/HMAC.cpp
@@ -0,0 +1,81 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include "HMAC.hpp"
+
+#include <openssl/sha.h>
+
+namespace ZeroTier {
+
+void HMAC::sha256(const void *key,unsigned int klen,const void *message,unsigned int len,void *mac)
+ throw()
+{
+ union {
+ uint64_t q[12];
+ uint8_t b[96];
+ } key2,opad,ipad;
+ SHA256_CTX sha;
+
+ if (klen == 32) { // this is what we use, so handle this quickly
+ key2.q[0] = ((const uint64_t *)key)[0];
+ key2.q[1] = ((const uint64_t *)key)[1];
+ key2.q[2] = ((const uint64_t *)key)[2];
+ key2.q[3] = ((const uint64_t *)key)[3];
+ key2.q[4] = 0ULL;
+ key2.q[5] = 0ULL;
+ key2.q[6] = 0ULL;
+ key2.q[7] = 0ULL;
+ } else { // for correctness and testing against test vectors
+ if (klen > 64) {
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,key,klen);
+ SHA256_Final(key2.b,&sha);
+ klen = 32;
+ } else {
+ for(unsigned int i=0;i<klen;++i)
+ key2.b[i] = ((const uint8_t *)key)[i];
+ }
+ while (klen < 64)
+ key2.b[klen++] = (uint8_t)0;
+ }
+
+ for(unsigned int i=0;i<8;++i)
+ opad.q[i] = 0x5c5c5c5c5c5c5c5cULL ^ key2.q[i];
+ for(unsigned int i=0;i<8;++i)
+ ipad.q[i] = 0x3636363636363636ULL ^ key2.q[i];
+
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,(const unsigned char *)ipad.b,64);
+ SHA256_Update(&sha,(const unsigned char *)message,len);
+ SHA256_Final((unsigned char *)(opad.b + 64),&sha);
+
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,opad.b,96);
+ SHA256_Final((unsigned char *)mac,&sha);
+}
+
+} // namespace ZeroTier
diff --git a/node/HMAC.hpp b/node/HMAC.hpp
new file mode 100644
index 00000000..f48c33c1
--- /dev/null
+++ b/node/HMAC.hpp
@@ -0,0 +1,55 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_HMAC_HPP
+#define _ZT_HMAC_HPP
+
+#include <stdint.h>
+
+namespace ZeroTier {
+
+/**
+ * HMAC authenticator functions
+ */
+class HMAC
+{
+public:
+ /**
+ * Compute HMAC-SHA256
+ *
+ * @param key Key bytes
+ * @param klen Length of key
+ * @param len Length of message
+ * @param mac Buffer to receive 32-byte MAC
+ */
+ static void sha256(const void *key,unsigned int klen,const void *message,unsigned int len,void *mac)
+ throw();
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Http.cpp b/node/Http.cpp
new file mode 100644
index 00000000..6a79a974
--- /dev/null
+++ b/node/Http.cpp
@@ -0,0 +1,323 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <vector>
+#include <set>
+#include <list>
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include "Http.hpp"
+#include "Utils.hpp"
+#include "InetAddress.hpp"
+
+static http_parser_settings _http_parser_settings;
+
+namespace ZeroTier {
+
+static bool _sendAll(int fd,const void *buf,unsigned int len)
+{
+ for(;;) {
+ int n = (int)::send(fd,buf,len,0);
+ if ((n < 0)&&(errno == EINTR))
+ continue;
+ return (n == (int)len);
+ }
+}
+
+const std::map<std::string,std::string> Http::EMPTY_HEADERS;
+
+Http::Request::Request(
+ Http::Method m,
+ const std::string &url,
+ const std::map<std::string,std::string> &rh,
+ const std::string &rb,
+ bool (*handler)(Request *,void *,const std::string &,int,const std::map<std::string,std::string> &,const std::string &),
+ void *arg) :
+ _url(url),
+ _requestHeaders(rh),
+ _handler(handler),
+ _arg(arg),
+ _method(m),
+ _fd(0)
+{
+ _http_parser_settings.on_message_begin = &Http::Request::_http_on_message_begin;
+ _http_parser_settings.on_url = &Http::Request::_http_on_url;
+ _http_parser_settings.on_status_complete = &Http::Request::_http_on_status_complete;
+ _http_parser_settings.on_header_field = &Http::Request::_http_on_header_field;
+ _http_parser_settings.on_header_value = &Http::Request::_http_on_header_value;
+ _http_parser_settings.on_headers_complete = &Http::Request::_http_on_headers_complete;
+ _http_parser_settings.on_body = &Http::Request::_http_on_body;
+ _http_parser_settings.on_message_complete = &Http::Request::_http_on_message_complete;
+
+ start();
+}
+
+Http::Request::~Request()
+{
+ if (_fd > 0)
+ ::close(_fd);
+ join();
+}
+
+void Http::Request::main()
+ throw()
+{
+ char buf[131072];
+
+ try {
+ http_parser_init(&_parser,HTTP_RESPONSE);
+ _parser.data = this;
+
+ http_parser_url urlParsed;
+ if (http_parser_parse_url(_url.c_str(),_url.length(),0,&urlParsed)) {
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"URL parse error");
+ return;
+ }
+ if (!(urlParsed.field_set & (1 << UF_SCHEMA))) {
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"URL specifies no schema");
+ return;
+ }
+
+ std::string schema(_url.substr(urlParsed.field_data[UF_SCHEMA].off,urlParsed.field_data[UF_SCHEMA].len));
+
+ if (schema == "file") {
+ const std::string filePath(_url.substr(urlParsed.field_data[UF_PATH].off,urlParsed.field_data[UF_PATH].len));
+
+ uint64_t lm = Utils::getLastModified(filePath.c_str());
+ if (lm) {
+ const std::map<std::string,std::string>::const_iterator ifModSince(_requestHeaders.find("If-Modified-Since"));
+ if ((ifModSince != _requestHeaders.end())&&(ifModSince->second.length())) {
+ uint64_t t64 = Utils::fromRfc1123(ifModSince->second);
+ if ((t64)&&(lm > t64)) {
+ suicidalThread = !_handler(this,_arg,_url,304,_responseHeaders,"");
+ return;
+ }
+ }
+
+ if (Utils::readFile(filePath.c_str(),_responseBody)) {
+ _responseHeaders["Last-Modified"] = Utils::toRfc1123(lm);
+ suicidalThread = !_handler(this,_arg,_url,200,_responseHeaders,_responseBody);
+ return;
+ }
+ }
+
+ suicidalThread = !_handler(this,_arg,_url,404,_responseHeaders,"file not found or not readable");
+ return;
+ } else if (schema == "http") {
+ if (!(urlParsed.field_set & (1 << UF_HOST))) {
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"URL contains no host");
+ return;
+ }
+ std::string host(_url.substr(urlParsed.field_data[UF_HOST].off,urlParsed.field_data[UF_HOST].len));
+
+ std::list<InetAddress> v4,v6;
+ {
+ struct addrinfo *res = (struct addrinfo *)0;
+ if (!getaddrinfo(host.c_str(),(const char *)0,(const struct addrinfo *)0,&res)) {
+ struct addrinfo *p = res;
+ do {
+ if (p->ai_family == AF_INET)
+ v4.push_back(InetAddress(p->ai_addr));
+ else if (p->ai_family == AF_INET6)
+ v6.push_back(InetAddress(p->ai_addr));
+ } while ((p = p->ai_next));
+ freeaddrinfo(res);
+ }
+ }
+
+ std::list<InetAddress> *addrList;
+ if (v4.empty()&&v6.empty()) {
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"could not find address for host in URL");
+ return;
+ } else if (v4.empty()) {
+ addrList = &v6;
+ } else {
+ addrList = &v4;
+ }
+ InetAddress *addr;
+ {
+ addrList->sort();
+ addrList->unique();
+ unsigned int i = 0,k = 0;
+ k = Utils::randomInt<unsigned int>() % addrList->size();
+ std::list<InetAddress>::iterator a(addrList->begin());
+ while (i++ != k) ++a;
+ addr = &(*a);
+ }
+
+ int remotePort = ((urlParsed.field_set & (1 << UF_PORT))&&(urlParsed.port)) ? (int)urlParsed.port : (int)80;
+ if ((remotePort <= 0)||(remotePort > 0xffff)) {
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"URL port out of range");
+ return;
+ }
+ addr->setPort(remotePort);
+
+ _fd = socket(addr->isV6() ? AF_INET6 : AF_INET,SOCK_STREAM,0);
+ if (_fd <= 0) {
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"could not open socket");
+ return;
+ }
+
+ for(;;) {
+ if (connect(_fd,addr->saddr(),addr->saddrLen())) {
+ if (errno == EINTR)
+ continue;
+ ::close(_fd); _fd = 0;
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"connection failed to remote host");
+ return;
+ } else break;
+ }
+
+ const char *mstr = "GET";
+ switch(_method) {
+ case HTTP_METHOD_HEAD: mstr = "HEAD"; break;
+ default: break;
+ }
+ int mlen = (int)snprintf(buf,sizeof(buf),"%s %s HTTP/1.1\r\nAccept-Encoding: \r\nHost: %s\r\n",mstr,_url.substr(urlParsed.field_data[UF_PATH].off,urlParsed.field_data[UF_PATH].len).c_str(),host.c_str());
+ if (mlen >= (int)sizeof(buf)) {
+ ::close(_fd); _fd = 0;
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"URL too long");
+ return;
+ }
+ if (!_sendAll(_fd,buf,mlen)) {
+ ::close(_fd); _fd = 0;
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"write error");
+ return;
+ }
+
+ for(std::map<std::string,std::string>::const_iterator rh(_requestHeaders.begin());rh!=_requestHeaders.end();++rh) {
+ mlen = (int)snprintf(buf,sizeof(buf),"%s: %s\r\n",rh->first.c_str(),rh->second.c_str());
+ if (mlen >= (int)sizeof(buf)) {
+ ::close(_fd); _fd = 0;
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"header too long");
+ return;
+ }
+ if (!_sendAll(_fd,buf,mlen)) {
+ ::close(_fd); _fd = 0;
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"write error");
+ return;
+ }
+ }
+
+ if (!_sendAll(_fd,"\r\n",2)) {
+ ::close(_fd); _fd = 0;
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"write error");
+ return;
+ }
+
+ _responseStatusCode = 0;
+ _messageComplete = false;
+ for(;;) {
+ mlen = (int)::recv(_fd,buf,sizeof(buf),0);
+ if (mlen < 0) {
+ if (errno != EINTR)
+ break;
+ else continue;
+ }
+ if (((int)http_parser_execute(&_parser,&_http_parser_settings,buf,mlen) != mlen)||(_parser.upgrade)) {
+ ::close(_fd); _fd = 0;
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"invalid HTTP response from server");
+ return;
+ }
+ if (_messageComplete) {
+ ::close(_fd); _fd = 0;
+ suicidalThread = !_handler(this,_arg,_url,_responseStatusCode,_responseHeaders,_responseBody);
+ return;
+ }
+ }
+
+ ::close(_fd); _fd = 0;
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"empty HTTP response from server");
+ return;
+ } else {
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"only 'file' and 'http' methods are supported");
+ return;
+ }
+ } catch ( ... ) {
+ suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"unexpected exception retrieving URL");
+ return;
+ }
+}
+
+int Http::Request::_http_on_message_begin(http_parser *parser)
+{
+ return 0;
+}
+int Http::Request::_http_on_url(http_parser *parser,const char *data,size_t length)
+{
+ return 0;
+}
+int Http::Request::_http_on_status_complete(http_parser *parser)
+{
+ Http::Request *r = (Http::Request *)parser->data;
+ r->_responseStatusCode = parser->status_code;
+ return 0;
+}
+int Http::Request::_http_on_header_field(http_parser *parser,const char *data,size_t length)
+{
+ Http::Request *r = (Http::Request *)parser->data;
+ if ((r->_currentHeaderField.length())&&(r->_responseHeaders.find(r->_currentHeaderField) != r->_responseHeaders.end()))
+ r->_currentHeaderField.assign("");
+ r->_currentHeaderField.append(data,length);
+ return 0;
+}
+int Http::Request::_http_on_header_value(http_parser *parser,const char *data,size_t length)
+{
+ Http::Request *r = (Http::Request *)parser->data;
+ if (r->_currentHeaderField.length())
+ r->_responseHeaders[r->_currentHeaderField].append(data,length);
+ return 0;
+}
+int Http::Request::_http_on_headers_complete(http_parser *parser)
+{
+ Http::Request *r = (Http::Request *)parser->data;
+ return ((r->_method == Http::HTTP_METHOD_HEAD) ? 1 : 0);
+}
+int Http::Request::_http_on_body(http_parser *parser,const char *data,size_t length)
+{
+ Http::Request *r = (Http::Request *)parser->data;
+ r->_responseBody.append(data,length);
+ return 0;
+}
+int Http::Request::_http_on_message_complete(http_parser *parser)
+{
+ Http::Request *r = (Http::Request *)parser->data;
+ r->_messageComplete = true;
+ return 0;
+}
+
+} // namespace ZeroTier
diff --git a/node/Http.hpp b/node/Http.hpp
new file mode 100644
index 00000000..a099b15d
--- /dev/null
+++ b/node/Http.hpp
@@ -0,0 +1,129 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_HTTP_HPP
+#define _ZT_HTTP_HPP
+
+#include <map>
+#include <string>
+#include <stdexcept>
+#include "Thread.hpp"
+
+#include "../ext/http-parser/http_parser.h"
+
+namespace ZeroTier {
+
+class Http
+{
+public:
+ /**
+ * HTTP request methods
+ */
+ enum Method
+ {
+ HTTP_METHOD_GET,
+ HTTP_METHOD_HEAD
+ };
+
+ /**
+ * An empty headers map for convenience
+ */
+ static const std::map<std::string,std::string> EMPTY_HEADERS;
+
+ /**
+ * HTTP request
+ */
+ class Request : protected Thread
+ {
+ public:
+ /**
+ * Create and issue an HTTP request
+ *
+ * The supplied handler is called when the request is
+ * complete or if an error occurs. A code of zero indicates
+ * that the server could not be reached, and a description
+ * of the error will be in 'body'. If the handler returns
+ * false the Request object deletes itself. Otherwise the
+ * object must be deleted by other code.
+ *
+ * @param m Request method
+ * @param url Destination URL
+ * @param rh Request headers
+ * @param rb Request body or empty string for none (currently unused)
+ * @param handler Request handler function
+ * @param arg First argument to request handler
+ */
+ Request(
+ Http::Method m,
+ const std::string &url,
+ const std::map<std::string,std::string> &rh,
+ const std::string &rb,
+ bool (*handler)(Request *,void *,const std::string &,int,const std::map<std::string,std::string> &,const std::string &),
+ void *arg);
+
+ /**
+ * Destruction cancels any in-progress request
+ */
+ virtual ~Request();
+
+ protected:
+ virtual void main()
+ throw();
+
+ private:
+ // HTTP parser handlers
+ static int _http_on_message_begin(http_parser *parser);
+ static int _http_on_url(http_parser *parser,const char *data,size_t length);
+ static int _http_on_status_complete(http_parser *parser);
+ static int _http_on_header_field(http_parser *parser,const char *data,size_t length);
+ static int _http_on_header_value(http_parser *parser,const char *data,size_t length);
+ static int _http_on_headers_complete(http_parser *parser);
+ static int _http_on_body(http_parser *parser,const char *data,size_t length);
+ static int _http_on_message_complete(http_parser *parser);
+
+ http_parser _parser;
+ std::string _url;
+
+ std::map<std::string,std::string> _requestHeaders;
+ std::map<std::string,std::string> _responseHeaders;
+
+ std::string _currentHeaderField;
+ std::string _responseBody;
+
+ bool (*_handler)(Request *,void *,const std::string &,int,const std::map<std::string,std::string> &,const std::string &);
+ void *_arg;
+
+ Http::Method _method;
+ int _responseStatusCode;
+ bool _messageComplete;
+ volatile int _fd;
+ };
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Identity.cpp b/node/Identity.cpp
new file mode 100644
index 00000000..f16947a0
--- /dev/null
+++ b/node/Identity.cpp
@@ -0,0 +1,301 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <openssl/sha.h>
+
+#include "Identity.hpp"
+#include "Salsa20.hpp"
+#include "HMAC.hpp"
+#include "Utils.hpp"
+
+namespace ZeroTier {
+
+void Identity::generate()
+{
+ delete [] _keyPair;
+
+ // Generate key pair and derive address
+ do {
+ _keyPair = new EllipticCurveKeyPair();
+ _keyPair->generate();
+ _address = deriveAddress(_keyPair->pub().data(),_keyPair->pub().size());
+ } while (_address.isReserved());
+ _publicKey = _keyPair->pub();
+
+ // Sign address, key type, and public key with private key (with a zero
+ // byte between each field). Including this extra data means simply editing
+ // the address of an identity will be detected as its signature will be
+ // invalid. Of course, deep verification of address/key relationship is
+ // required to cover the more elaborate address claim jump attempt case.
+ SHA256_CTX sha;
+ unsigned char dig[32];
+ unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0;
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,_address.data(),ZT_ADDRESS_LENGTH);
+ SHA256_Update(&sha,&zero,1);
+ SHA256_Update(&sha,&idtype,1);
+ SHA256_Update(&sha,&zero,1);
+ SHA256_Update(&sha,_publicKey.data(),_publicKey.size());
+ SHA256_Update(&sha,&zero,1);
+ SHA256_Final(dig,&sha);
+ _signature = _keyPair->sign(dig);
+}
+
+bool Identity::locallyValidate(bool doAddressDerivationCheck) const
+{
+ SHA256_CTX sha;
+ unsigned char dig[32];
+ unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0;
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,_address.data(),ZT_ADDRESS_LENGTH);
+ SHA256_Update(&sha,&zero,1);
+ SHA256_Update(&sha,&idtype,1);
+ SHA256_Update(&sha,&zero,1);
+ SHA256_Update(&sha,_publicKey.data(),_publicKey.size());
+ SHA256_Update(&sha,&zero,1);
+ SHA256_Final(dig,&sha);
+
+ return ((EllipticCurveKeyPair::verify(dig,_publicKey,_signature.data(),_signature.length()))&&((!doAddressDerivationCheck)||(deriveAddress(_publicKey.data(),_publicKey.size()) == _address)));
+}
+
+std::string Identity::toString(bool includePrivate) const
+{
+ std::string r;
+ r.append(_address.toString());
+ r.append(":1:"); // 1 == IDENTITY_TYPE_NIST_P_521
+ r.append(Utils::base64Encode(_publicKey.data(),_publicKey.size()));
+ r.push_back(':');
+ r.append(Utils::base64Encode(_signature.data(),_signature.length()));
+ if ((includePrivate)&&(_keyPair)) {
+ r.push_back(':');
+ r.append(Utils::base64Encode(_keyPair->priv().data(),_keyPair->priv().size()));
+ }
+ return r;
+}
+
+bool Identity::fromString(const char *str)
+{
+ delete _keyPair;
+ _keyPair = (EllipticCurveKeyPair *)0;
+
+ std::vector<std::string> fields(Utils::split(Utils::trim(std::string(str)).c_str(),":","",""));
+
+ if (fields.size() < 4)
+ return false;
+
+ if (fields[1] != "1")
+ return false; // version mismatch
+
+ std::string b(Utils::unhex(fields[0]));
+ if (b.length() != ZT_ADDRESS_LENGTH)
+ return false;
+ _address = b.data();
+
+ b = Utils::base64Decode(fields[2]);
+ if ((!b.length())||(b.length() > ZT_EC_MAX_BYTES))
+ return false;
+ _publicKey.set(b.data(),b.length());
+
+ _signature = Utils::base64Decode(fields[3]);
+ if (!_signature.length())
+ return false;
+
+ if (fields.size() >= 5) {
+ b = Utils::base64Decode(fields[4]);
+ if ((!b.length())||(b.length() > ZT_EC_MAX_BYTES))
+ return false;
+ _keyPair = new EllipticCurveKeyPair(_publicKey,EllipticCurveKey(b.data(),b.length()));
+ }
+
+ return true;
+}
+
+// These are core protocol parameters and can't be changed without a new
+// identity type.
+#define ZT_IDENTITY_DERIVEADDRESS_ROUNDS 4
+#define ZT_IDENTITY_DERIVEADDRESS_MEMORY 33554432
+
+Address Identity::deriveAddress(const void *keyBytes,unsigned int keyLen)
+{
+ unsigned char dig[32];
+ Salsa20 s20a,s20b;
+ SHA256_CTX sha;
+
+ /*
+ * Sequential memory-hard algorithm wedding address to public key
+ *
+ * Conventional hashcash with long computations and quick verifications
+ * unfortunately cannot be used here. If that were used, it would be
+ * equivalently costly to simply increment/vary the public key and find
+ * a collision as it would be to find the address. We need something
+ * that creates a costly 1:~1 mapping from key to address, hence this odd
+ * algorithm.
+ *
+ * This is designed not to be parallelizable and to be resistant to
+ * implementation on things like GPUs with tiny-memory nodes and poor
+ * branching capability. Toward that end it throws branching and a large
+ * memory buffer into the mix. It can only be efficiently computed by a
+ * single core with at least ~32MB RAM.
+ *
+ * Search for "sequential memory hard algorithm" for academic references
+ * to similar concepts.
+ *
+ * Right now this takes ~1700ms on a 2.4ghz Intel Core i5. If this could
+ * be reduced to 1ms per derivation, it would take about 34 years to search
+ * the entire 40-bit address space for an average of ~17 years to generate
+ * a key colliding with a known existing address.
+ */
+
+ // Initial starting digest
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,(const unsigned char *)keyBytes,keyLen); // key
+ SHA256_Final(dig,&sha);
+
+ s20a.init(dig,256,"ZeroTier");
+
+ unsigned char *ram = new unsigned char[ZT_IDENTITY_DERIVEADDRESS_MEMORY];
+
+ // Encrypt and digest a large memory buffer for several rounds
+ for(unsigned long i=0;i<ZT_IDENTITY_DERIVEADDRESS_MEMORY;++i)
+ ram[i] = (unsigned char)(i & 0xff) ^ dig[i & 31];
+ for(unsigned long r=0;r<ZT_IDENTITY_DERIVEADDRESS_ROUNDS;++r) {
+ SHA256_Init(&sha);
+
+ SHA256_Update(&sha,(const unsigned char *)keyBytes,keyLen);
+ SHA256_Update(&sha,dig,32);
+
+ for(unsigned long i=0;i<ZT_IDENTITY_DERIVEADDRESS_MEMORY;++i) {
+ if (ram[i] == 17) // Forces a branch to be required
+ ram[i] ^= dig[i & 31];
+ }
+ s20b.init(dig,256,"ZeroTier");
+ s20a.encrypt(ram,ram,ZT_IDENTITY_DERIVEADDRESS_MEMORY);
+ s20b.encrypt(ram,ram,ZT_IDENTITY_DERIVEADDRESS_MEMORY);
+ SHA256_Update(&sha,ram,ZT_IDENTITY_DERIVEADDRESS_MEMORY);
+
+ SHA256_Final(dig,&sha);
+ }
+
+ // Final digest, executed for twice our number of rounds
+ SHA256_Init(&sha);
+ for(unsigned long r=0;r<(ZT_IDENTITY_DERIVEADDRESS_ROUNDS * 2);++r) {
+ SHA256_Update(&sha,(const unsigned char *)keyBytes,keyLen);
+ SHA256_Update(&sha,ram,ZT_IDENTITY_DERIVEADDRESS_ROUNDS);
+ SHA256_Update(&sha,dig,32);
+ SHA256_Update(&sha,(const unsigned char *)keyBytes,keyLen);
+ }
+ SHA256_Final(dig,&sha);
+
+ delete [] ram;
+
+ return Address(dig); // first 5 bytes of dig[]
+}
+
+std::string Identity::encrypt(const Identity &to,const void *data,unsigned int len) const
+{
+ unsigned char key[64];
+ unsigned char mac[32];
+ unsigned char iv[8];
+
+ if (!agree(to,key,sizeof(key)))
+ return std::string();
+ Utils::getSecureRandom(iv,8);
+ for(int i=0;i<8;++i)
+ key[i + 32] ^= iv[i]; // perturb HMAC key with IV so IV is effectively included in HMAC
+ Salsa20 s20(key,256,iv);
+
+ std::string compressed;
+ compressed.reserve(len);
+ Utils::compress((const char *)data,(const char *)data + len,Utils::StringAppendOutput(compressed));
+ if (!compressed.length())
+ return std::string();
+
+ char *encrypted = new char[compressed.length() + 16];
+ try {
+ s20.encrypt(compressed.data(),encrypted + 16,(unsigned int)compressed.length());
+ HMAC::sha256(key + 32,32,encrypted + 16,(unsigned int)compressed.length(),mac);
+ for(int i=0;i<8;++i)
+ encrypted[i] = iv[i];
+ for(int i=0;i<8;++i)
+ encrypted[i + 8] = mac[i];
+
+ std::string s(encrypted,compressed.length() + 16);
+ delete [] encrypted;
+ return s;
+ } catch ( ... ) {
+ delete [] encrypted;
+ return std::string();
+ }
+}
+
+std::string Identity::decrypt(const Identity &from,const void *cdata,unsigned int len) const
+{
+ unsigned char key[64];
+ unsigned char mac[32];
+
+ if (len < 16)
+ return std::string();
+
+ if (!agree(from,key,sizeof(key)))
+ return std::string();
+
+ for(int i=0;i<8;++i)
+ key[i + 32] ^= ((const unsigned char *)cdata)[i]; // apply IV to HMAC key
+ HMAC::sha256(key + 32,32,((const char *)cdata) + 16,(unsigned int)(len - 16),mac);
+ for(int i=0;i<8;++i) {
+ if (((const unsigned char *)cdata)[i + 8] != mac[i])
+ return std::string();
+ }
+
+ char *decbuf = new char[len - 16];
+ try {
+ Salsa20 s20(key,256,cdata); // first 8 bytes are IV
+ len -= 16;
+ s20.decrypt((const char *)cdata + 16,decbuf,len);
+
+ std::string decompressed;
+ if (Utils::decompress((const char *)decbuf,(const char *)decbuf + len,Utils::StringAppendOutput(decompressed))) {
+ delete [] decbuf;
+ return decompressed;
+ } else {
+ delete [] decbuf;
+ return std::string();
+ }
+ } catch ( ... ) {
+ delete [] decbuf;
+ return std::string();
+ }
+}
+
+} // namespace ZeroTier
+
diff --git a/node/Identity.hpp b/node/Identity.hpp
new file mode 100644
index 00000000..5cdfe9f8
--- /dev/null
+++ b/node/Identity.hpp
@@ -0,0 +1,431 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_IDENTITY_HPP
+#define _ZT_IDENTITY_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+#include "EllipticCurveKey.hpp"
+#include "EllipticCurveKeyPair.hpp"
+#include "Array.hpp"
+#include "Utils.hpp"
+#include "Address.hpp"
+#include "Buffer.hpp"
+
+/**
+ * Maximum length for a serialized identity
+ */
+#define IDENTITY_MAX_BINARY_SERIALIZED_LENGTH ((ZT_EC_MAX_BYTES * 2) + 256)
+
+namespace ZeroTier {
+
+/**
+ * A ZeroTier identity
+ *
+ * An identity consists of a public key, a 40-bit ZeroTier address computed
+ * from that key in a collision-resistant fashion, and a self-signature.
+ *
+ * The address derivation algorithm makes it computationally very expensive to
+ * search for a different public key that duplicates an existing address. (See
+ * code for deriveAddress() for this algorithm.)
+ *
+ * After derivation, the address must be checked against isReserved(). If the
+ * address is reserved, generation is repeated until a valid address results.
+ *
+ * Serialization of an identity:
+ *
+ * <[5] address> - 40-bit ZeroTier network address
+ * <[1] type> - Identity type ID (rest is type-dependent)
+ * <[1] key length> - Length of public key
+ * <[n] public key> - Elliptic curve public key
+ * <[1] sig length> - Length of ECDSA self-signature
+ * <[n] signature> - ECDSA signature of first four fields
+ * [<[1] key length>] - [Optional] Length of private key
+ * [<[n] private key>] - [Optional] Private key
+ *
+ * Local storage of an identity also requires storage of its private key.
+ */
+class Identity
+{
+public:
+ /**
+ * Identity types
+ */
+ enum Type
+ {
+ /* Elliptic curve NIST-P-521 and ECDSA signature */
+ IDENTITY_TYPE_NIST_P_521 = 1
+ /* We won't need another identity type until quantum computers with
+ * tens of thousands of qubits are a reality. */
+ };
+
+ Identity() :
+ _keyPair((EllipticCurveKeyPair *)0)
+ {
+ }
+
+ Identity(const Identity &id) :
+ _keyPair((id._keyPair) ? new EllipticCurveKeyPair(*id._keyPair) : (EllipticCurveKeyPair *)0),
+ _publicKey(id._publicKey),
+ _address(id._address),
+ _signature(id._signature)
+ {
+ }
+
+ Identity(const char *str)
+ throw(std::invalid_argument) :
+ _keyPair((EllipticCurveKeyPair *)0)
+ {
+ if (!fromString(str))
+ throw std::invalid_argument("invalid string-serialized identity");
+ }
+
+ Identity(const std::string &str)
+ throw(std::invalid_argument) :
+ _keyPair((EllipticCurveKeyPair *)0)
+ {
+ if (!fromString(str))
+ throw std::invalid_argument("invalid string-serialized identity");
+ }
+
+ template<unsigned int C>
+ Identity(const Buffer<C> &b,unsigned int startAt = 0)
+ throw(std::out_of_range,std::invalid_argument) :
+ _keyPair((EllipticCurveKeyPair *)0)
+ {
+ deserialize(b,startAt);
+ }
+
+ ~Identity()
+ {
+ delete _keyPair;
+ }
+
+ inline Identity &operator=(const Identity &id)
+ {
+ _keyPair = (id._keyPair) ? new EllipticCurveKeyPair(*id._keyPair) : (EllipticCurveKeyPair *)0;
+ _publicKey = id._publicKey;
+ _address = id._address;
+ _signature = id._signature;
+ return *this;
+ }
+
+ /**
+ * Generate a new identity (address, key pair)
+ *
+ * This is a somewhat time consuming operation by design, as the address
+ * is derived from the key using a purposefully expensive many-round
+ * hash/encrypt/hash operation. This took about two seconds on a 2.4ghz
+ * Intel Core i5 in 2013.
+ *
+ * In the very unlikely event that a reserved address is created, generate
+ * will automatically run again.
+ */
+ void generate();
+
+ /**
+ * Performs local validation, with two levels available
+ *
+ * With the parameter false, this performs self-signature verification
+ * which checks the basic integrity of the key and identity. Setting the
+ * parameter to true performs a fairly time consuming computation to
+ * check that the address was properly derived from the key. This is
+ * normally not done unless a conflicting identity is received, in
+ * which case the invalid identity is thrown out.
+ *
+ * @param doAddressDerivationCheck If true, do the time-consuming address check
+ * @return True if validation check passes
+ */
+ bool locallyValidate(bool doAddressDerivationCheck) const;
+
+ /**
+ * @return Private key pair or NULL if not included with this identity
+ */
+ inline const EllipticCurveKeyPair *privateKeyPair() const throw() { return _keyPair; }
+
+ /**
+ * @return True if this identity has its private portion
+ */
+ inline bool hasPrivate() const throw() { return (_keyPair); }
+
+ /**
+ * Encrypt a block of data to send to another identity
+ *
+ * This identity must have a secret key.
+ *
+ * The encrypted data format is:
+ * <[8] Salsa20 initialization vector>
+ * <[8] first 8 bytes of HMAC-SHA-256 of ciphertext>
+ * <[...] encrypted compressed data>
+ *
+ * Keying is accomplished using agree() (KDF function is in the
+ * EllipticCurveKeyPair.cpp source) to generate 64 bytes of key. The first
+ * 32 bytes are used as the Salsa20 key, and the last 32 bytes are used
+ * as the HMAC key.
+ *
+ * @param to Identity of recipient of encrypted message
+ * @param data Data to encrypt
+ * @param len Length of data
+ * @return Encrypted data or empty string on failure
+ */
+ std::string encrypt(const Identity &to,const void *data,unsigned int len) const;
+
+ /**
+ * Decrypt a message encrypted with encrypt()
+ *
+ * This identity must have a secret key.
+ *
+ * @param from Identity of sender of encrypted message
+ * @param cdata Encrypted message
+ * @param len Length of encrypted message
+ * @return Decrypted data or empty string on failure
+ */
+ std::string decrypt(const Identity &from,const void *cdata,unsigned int len) const;
+
+ /**
+ * Shortcut method to perform key agreement with another identity
+ *
+ * This identity must have its private portion.
+ *
+ * @param id Identity to agree with
+ * @param key Result parameter to fill with key bytes
+ * @param klen Length of key in bytes
+ * @return Was agreement successful?
+ */
+ inline bool agree(const Identity &id,void *key,unsigned int klen) const
+ {
+ if ((id)&&(_keyPair))
+ return _keyPair->agree(id._publicKey,(unsigned char *)key,klen);
+ return false;
+ }
+
+ /**
+ * Sign a hash with this identity's private key
+ *
+ * @param sha256 32-byte hash to sign
+ * @return ECDSA signature or empty string on failure or if identity has no private portion
+ */
+ inline std::string sign(const void *sha256) const
+ {
+ if (_keyPair)
+ return _keyPair->sign(sha256);
+ return std::string();
+ }
+
+ /**
+ * Sign a block of data with this identity's private key
+ *
+ * This is a shortcut to SHA-256 hashing then signing.
+ *
+ * @param sha256 32-byte hash to sign
+ * @return ECDSA signature or empty string on failure or if identity has no private portion
+ */
+ inline std::string sign(const void *data,unsigned int len) const
+ {
+ if (_keyPair)
+ return _keyPair->sign(data,len);
+ return std::string();
+ }
+
+ /**
+ * Verify something signed with this identity's public key
+ *
+ * @param sha256 32-byte hash to verify
+ * @param sigbytes Signature bytes
+ * @param siglen Length of signature
+ * @return True if signature is valid
+ */
+ inline bool verifySignature(const void *sha256,const void *sigbytes,unsigned int siglen) const
+ {
+ return EllipticCurveKeyPair::verify(sha256,_publicKey,sigbytes,siglen);
+ }
+
+ /**
+ * Verify something signed with this identity's public key
+ *
+ * @param data Data to verify
+ * @param len Length of data to verify
+ * @param sigbytes Signature bytes
+ * @param siglen Length of signature
+ * @return True if signature is valid
+ */
+ inline bool verifySignature(const void *data,unsigned int len,const void *sigbytes,unsigned int siglen) const
+ {
+ return EllipticCurveKeyPair::verify(data,len,_publicKey,sigbytes,siglen);
+ }
+
+ /**
+ * @return Public key (available in all identities)
+ */
+ inline const EllipticCurveKey &publicKey() const throw() { return _publicKey; }
+
+ /**
+ * @return Identity type
+ */
+ inline Type type() const throw() { return IDENTITY_TYPE_NIST_P_521; }
+
+ /**
+ * @return This identity's address
+ */
+ inline const Address &address() const throw() { return _address; }
+
+ /**
+ * Serialize this identity (binary)
+ *
+ * @param b Destination buffer to append to
+ * @param includePrivate If true, include private key component (if present) (default: false)
+ * @throws std::out_of_range Buffer too small
+ */
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b,bool includePrivate = false) const
+ throw(std::out_of_range)
+ {
+ b.append(_address.data(),ZT_ADDRESS_LENGTH);
+ b.append((unsigned char)IDENTITY_TYPE_NIST_P_521);
+ b.append((unsigned char)(_publicKey.size() & 0xff));
+ b.append(_publicKey.data(),_publicKey.size());
+ b.append((unsigned char)(_signature.length() & 0xff));
+ b.append(_signature);
+ if ((includePrivate)&&(_keyPair)) {
+ b.append((unsigned char)(_keyPair->priv().size() & 0xff));
+ b.append(_keyPair->priv().data(),_keyPair->priv().size());
+ } else b.append((unsigned char)0);
+ }
+
+ /**
+ * Deserialize a binary serialized identity
+ *
+ * If an exception is thrown, the Identity object is left in an undefined
+ * state and should not be used.
+ *
+ * @param b Buffer containing serialized data
+ * @param startAt Index within buffer of serialized data (default: 0)
+ * @return Length of serialized data read from buffer
+ * @throws std::out_of_range Buffer too small
+ * @throws std::invalid_argument Serialized data invalid
+ */
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ throw(std::out_of_range,std::invalid_argument)
+ {
+ delete _keyPair;
+ _keyPair = (EllipticCurveKeyPair *)0;
+
+ unsigned int p = startAt;
+
+ _address = b.field(p,ZT_ADDRESS_LENGTH);
+ p += ZT_ADDRESS_LENGTH;
+
+ if (b[p++] != IDENTITY_TYPE_NIST_P_521)
+ throw std::invalid_argument("Identity: deserialize(): unsupported identity type");
+
+ unsigned int publicKeyLength = b[p++];
+ if (!publicKeyLength)
+ throw std::invalid_argument("Identity: deserialize(): no public key");
+ _publicKey.set(b.field(p,publicKeyLength),publicKeyLength);
+ p += publicKeyLength;
+
+ unsigned int signatureLength = b[p++];
+ if (!signatureLength)
+ throw std::invalid_argument("Identity: deserialize(): no signature");
+ _signature.assign((const char *)b.field(p,signatureLength),signatureLength);
+ p += signatureLength;
+
+ unsigned int privateKeyLength = b[p++];
+ if (privateKeyLength) {
+ _keyPair = new EllipticCurveKeyPair(_publicKey,EllipticCurveKey(b.field(p,privateKeyLength),privateKeyLength));
+ p += privateKeyLength;
+ }
+
+ return (p - startAt);
+ }
+
+ /**
+ * Serialize to a more human-friendly string
+ *
+ * @param includePrivate If true, include private key (if it exists)
+ * @return ASCII string representation of identity
+ */
+ std::string toString(bool includePrivate) const;
+
+ /**
+ * Deserialize a human-friendly string
+ *
+ * Note: validation is for the format only. The locallyValidate() method
+ * must be used to check signature and address/key correspondence.
+ *
+ * @param str String to deserialize
+ * @return True if deserialization appears successful
+ */
+ bool fromString(const char *str);
+ inline bool fromString(const std::string &str) { return fromString(str.c_str()); }
+
+ /**
+ * @return True if this identity contains something
+ */
+ inline operator bool() const throw() { return (_publicKey.size()); }
+
+ inline bool operator==(const Identity &id) const
+ throw()
+ {
+ if (_address == id._address) {
+ if ((_keyPair)&&(id._keyPair))
+ return (*_keyPair == *id._keyPair);
+ return (_publicKey == id._publicKey);
+ }
+ return false;
+ }
+ inline bool operator<(const Identity &id) const
+ throw()
+ {
+ if (_address < id._address)
+ return true;
+ else if (_address == id._address)
+ return (_publicKey < id._publicKey);
+ return false;
+ }
+ inline bool operator!=(const Identity &id) const throw() { return !(*this == id); }
+ inline bool operator>(const Identity &id) const throw() { return (id < *this); }
+ inline bool operator<=(const Identity &id) const throw() { return !(id < *this); }
+ inline bool operator>=(const Identity &id) const throw() { return !(*this < id); }
+
+private:
+ // Compute an address from public key bytes
+ static Address deriveAddress(const void *keyBytes,unsigned int keyLen);
+
+ EllipticCurveKeyPair *_keyPair;
+ EllipticCurveKey _publicKey;
+ Address _address;
+ std::string _signature;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp
new file mode 100644
index 00000000..79efbaf2
--- /dev/null
+++ b/node/InetAddress.cpp
@@ -0,0 +1,148 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string>
+
+#include "InetAddress.hpp"
+
+namespace ZeroTier {
+
+const InetAddress InetAddress::LO4("127.0.0.1",0);
+const InetAddress InetAddress::LO6("::1",0);
+
+void InetAddress::set(const std::string &ip,unsigned int port)
+ throw()
+{
+ memset(&_sa,0,sizeof(_sa));
+ if (ip.find(':') != std::string::npos) {
+ _sa.sin6.sin6_family = AF_INET6;
+ _sa.sin6.sin6_port = htons((uint16_t)port);
+ if (inet_pton(AF_INET6,ip.c_str(),(void *)&(_sa.sin6.sin6_addr.s6_addr)) <= 0)
+ _sa.saddr.sa_family = 0;
+ } else {
+ _sa.sin.sin_family = AF_INET;
+ _sa.sin.sin_port = htons((uint16_t)port);
+ if (inet_pton(AF_INET,ip.c_str(),(void *)&(_sa.sin.sin_addr.s_addr)) <= 0)
+ _sa.saddr.sa_family = 0;
+ }
+}
+
+std::string InetAddress::toString() const
+{
+ char buf[128],buf2[128];
+
+ switch(_sa.saddr.sa_family) {
+ case AF_INET:
+ if (inet_ntop(AF_INET,(const void *)&(_sa.sin.sin_addr.s_addr),buf,sizeof(buf))) {
+ sprintf(buf2,"%s/%u",buf,(unsigned int)ntohs(_sa.sin.sin_port));
+ return std::string(buf2);
+ }
+ break;
+ case AF_INET6:
+ if (inet_ntop(AF_INET6,(const void *)&(_sa.sin6.sin6_addr.s6_addr),buf,sizeof(buf))) {
+ sprintf(buf2,"%s/%u",buf,(unsigned int)ntohs(_sa.sin6.sin6_port));
+ return std::string(buf2);
+ }
+ break;
+ }
+
+ return std::string();
+}
+
+void InetAddress::fromString(const std::string &ipSlashPort)
+{
+ std::size_t slashAt = ipSlashPort.find('/');
+ if ((slashAt == std::string::npos)||(slashAt >= ipSlashPort.length()))
+ set(ipSlashPort,0);
+ else {
+ long p = strtol(ipSlashPort.substr(slashAt+1).c_str(),(char **)0,10);
+ if ((p > 0)&&(p <= 0xffff))
+ set(ipSlashPort.substr(0,slashAt),(unsigned int)p);
+ else set(ipSlashPort.substr(0,slashAt),0);
+ }
+}
+
+std::string InetAddress::toIpString() const
+{
+ char buf[128];
+
+ switch(_sa.saddr.sa_family) {
+ case AF_INET:
+ if (inet_ntop(AF_INET,(const void *)&(_sa.sin.sin_addr.s_addr),buf,sizeof(buf)))
+ return std::string(buf);
+ break;
+ case AF_INET6:
+ if (inet_ntop(AF_INET6,(const void *)&(_sa.sin6.sin6_addr.s6_addr),buf,sizeof(buf)))
+ return std::string(buf);
+ break;
+ }
+
+ return std::string();
+}
+
+bool InetAddress::operator==(const InetAddress &a) const
+ throw()
+{
+ if (_sa.saddr.sa_family == AF_INET) {
+ if (a._sa.saddr.sa_family == AF_INET)
+ return ((_sa.sin.sin_addr.s_addr == a._sa.sin.sin_addr.s_addr)&&(_sa.sin.sin_port == a._sa.sin.sin_port));
+ return false;
+ } else if (_sa.saddr.sa_family == AF_INET6) {
+ if (a._sa.saddr.sa_family == AF_INET6) {
+ if (_sa.sin6.sin6_port == a._sa.sin6.sin6_port)
+ return (!memcmp(_sa.sin6.sin6_addr.s6_addr,a._sa.sin6.sin6_addr.s6_addr,sizeof(_sa.sin6.sin6_addr.s6_addr)));
+ }
+ return false;
+ } else if (!_sa.saddr.sa_family)
+ return (!a._sa.saddr.sa_family);
+ return (!memcmp(&_sa,&a._sa,sizeof(_sa)));
+}
+
+bool InetAddress::operator<(const InetAddress &a) const
+ throw()
+{
+ if (_sa.saddr.sa_family == AF_INET) {
+ if (a._sa.saddr.sa_family == AF_INET)
+ return ((ntohl(_sa.sin.sin_addr.s_addr < ntohl(a._sa.sin.sin_addr.s_addr)))||((_sa.sin.sin_addr.s_addr == a._sa.sin.sin_addr.s_addr)&&(ntohs(_sa.sin.sin_port) < ntohs(a._sa.sin.sin_port))));
+ else if (a._sa.saddr.sa_family == AF_INET6)
+ return true;
+ } else if (_sa.saddr.sa_family == AF_INET6) {
+ if (a._sa.saddr.sa_family == AF_INET6) {
+ int cmp = memcmp(_sa.sin6.sin6_addr.s6_addr,a._sa.sin6.sin6_addr.s6_addr,16);
+ return ((cmp < 0)||((!cmp)&&(ntohs(_sa.sin6.sin6_port) < ntohs(a._sa.sin6.sin6_port))));
+ } else if (a._sa.saddr.sa_family == AF_INET)
+ return false;
+ }
+ return (_sa.saddr.sa_family < a._sa.saddr.sa_family);
+}
+
+} // namespace ZeroTier
diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp
new file mode 100644
index 00000000..42079274
--- /dev/null
+++ b/node/InetAddress.hpp
@@ -0,0 +1,334 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_INETADDRESS_HPP
+#define _ZT_INETADDRESS_HPP
+
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <string>
+
+namespace ZeroTier {
+
+/**
+ * Wrapper for sockaddr structures for IPV4 and IPV6
+ */
+class InetAddress
+{
+public:
+ /**
+ * Address type
+ */
+ enum AddressType
+ {
+ TYPE_NULL = 0,
+ TYPE_IPV4 = AF_INET,
+ TYPE_IPV6 = AF_INET6
+ };
+
+ /**
+ * Loopback IPv4 address (no port)
+ */
+ static const InetAddress LO4;
+
+ /**
+ * Loopback IPV6 address (no port)
+ */
+ static const InetAddress LO6;
+
+ InetAddress()
+ throw()
+ {
+ memset(&_sa,0,sizeof(_sa));
+ }
+
+ InetAddress(const InetAddress &a)
+ throw()
+ {
+ memcpy(&_sa,&a._sa,sizeof(_sa));
+ }
+
+ InetAddress(const struct sockaddr *sa)
+ throw()
+ {
+ this->set(sa);
+ }
+
+ InetAddress(const void *ipBytes,unsigned int ipLen,unsigned int port)
+ throw()
+ {
+ this->set(ipBytes,ipLen,port);
+ }
+
+ InetAddress(const std::string &ip,unsigned int port)
+ throw()
+ {
+ this->set(ip,port);
+ }
+
+ InetAddress(const std::string &ipSlashPort)
+ throw()
+ {
+ this->fromString(ipSlashPort);
+ }
+
+ inline InetAddress &operator=(const InetAddress &a)
+ throw()
+ {
+ memcpy(&_sa,&a._sa,sizeof(_sa));
+ return *this;
+ }
+
+ /**
+ * Set from an OS-level sockaddr structure
+ *
+ * @param sa Socket address (V4 or V6)
+ */
+ inline void set(const struct sockaddr *sa)
+ throw()
+ {
+ switch(sa->sa_family) {
+ case AF_INET:
+ memcpy(&_sa.sin,sa,sizeof(struct sockaddr_in));
+ break;
+ case AF_INET6:
+ memcpy(&_sa.sin6,sa,sizeof(struct sockaddr_in6));
+ break;
+ default:
+ _sa.saddr.sa_family = 0;
+ break;
+ }
+ }
+
+ /**
+ * Set from a string-format IP and a port
+ *
+ * @param ip IP address in V4 or V6 ASCII notation
+ * @param port Port or 0 for none
+ */
+ void set(const std::string &ip,unsigned int port)
+ throw();
+
+ /**
+ * Set from a raw IP and port number
+ *
+ * @param ipBytes Bytes of IP address in network byte order
+ * @param ipLen Length of IP address: 4 or 16
+ * @param port Port number or 0 for none
+ */
+ inline void set(const void *ipBytes,unsigned int ipLen,unsigned int port)
+ throw()
+ {
+ _sa.saddr.sa_family = 0;
+ if (ipLen == 4) {
+ setV4();
+ memcpy(rawIpData(),ipBytes,4);
+ setPort(port);
+ } else if (ipLen == 16) {
+ setV6();
+ memcpy(rawIpData(),ipBytes,16);
+ setPort(port);
+ }
+ }
+
+ /**
+ * Set the port component
+ *
+ * @param port Port, 0 to 65535
+ */
+ inline void setPort(unsigned int port)
+ throw()
+ {
+ if (_sa.saddr.sa_family == AF_INET)
+ _sa.sin.sin_port = htons((uint16_t)port);
+ else if (_sa.saddr.sa_family == AF_INET6)
+ _sa.sin6.sin6_port = htons((uint16_t)port);
+ }
+
+ /**
+ * @return ASCII IP/port format representation
+ */
+ std::string toString() const;
+
+ /**
+ * @param ipSlashPort ASCII IP/port format notation
+ */
+ void fromString(const std::string &ipSlashPort);
+
+ /**
+ * @return IP portion only, in ASCII string format
+ */
+ std::string toIpString() const;
+
+ /**
+ * @return Port or 0 if no port component defined
+ */
+ inline unsigned int port() const
+ throw()
+ {
+ switch(_sa.saddr.sa_family) {
+ case AF_INET:
+ return ntohs(_sa.sin.sin_port);
+ case AF_INET6:
+ return ntohs(_sa.sin6.sin6_port);
+ }
+ return 0;
+ }
+
+ /**
+ * Alias for port()
+ *
+ * This just aliases port() to make code more readable when netmask bits
+ * are stuffed there, as they are in Network, EthernetTap, and a few other
+ * spots.
+ *
+ * @return Netmask bits
+ */
+ inline unsigned int netmaskBits() const
+ throw()
+ {
+ return port();
+ }
+
+ /**
+ * @return True if this is an IPv4 address
+ */
+ inline bool isV4() const throw() { return (_sa.saddr.sa_family == AF_INET); }
+
+ /**
+ * @return True if this is an IPv6 address
+ */
+ inline bool isV6() const throw() { return (_sa.saddr.sa_family == AF_INET6); }
+
+ /**
+ * @return Address type or TYPE_NULL if not defined
+ */
+ inline AddressType type() const throw() { return (AddressType)_sa.saddr.sa_family; }
+
+ /**
+ * Force type to IPv4
+ */
+ inline void setV4() throw() { _sa.saddr.sa_family = AF_INET; }
+
+ /**
+ * Force type to IPv6
+ */
+ inline void setV6() throw() { _sa.saddr.sa_family = AF_INET6; }
+
+ /**
+ * @return Raw sockaddr structure
+ */
+ inline struct sockaddr *saddr() throw() { return &(_sa.saddr); }
+ inline const struct sockaddr *saddr() const throw() { return &(_sa.saddr); }
+
+ /**
+ * @return Length of sockaddr_in if IPv4, sockaddr_in6 if IPv6
+ */
+ inline unsigned int saddrLen() const
+ throw()
+ {
+ return (isV4() ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6));
+ }
+
+ /**
+ * @return Combined length of internal structure, room for either V4 or V6
+ */
+ inline unsigned int saddrSpaceLen() const
+ throw()
+ {
+ return sizeof(_sa);
+ }
+
+ /**
+ * @return Raw sockaddr_in structure (valid if IPv4)
+ */
+ inline const struct sockaddr_in *saddr4() const throw() { return &(_sa.sin); }
+
+ /**
+ * @return Raw sockaddr_in6 structure (valid if IPv6)
+ */
+ inline const struct sockaddr_in6 *saddr6() const throw() { return &(_sa.sin6); }
+
+ /**
+ * @return Raw IP address (4 bytes for IPv4, 16 bytes for IPv6)
+ */
+ inline void *rawIpData() throw() { return ((_sa.saddr.sa_family == AF_INET) ? (void *)(&(_sa.sin.sin_addr.s_addr)) : (void *)_sa.sin6.sin6_addr.s6_addr); }
+ inline const void *rawIpData() const throw() { return ((_sa.saddr.sa_family == AF_INET) ? (void *)(&(_sa.sin.sin_addr.s_addr)) : (void *)_sa.sin6.sin6_addr.s6_addr); }
+
+ /**
+ * Compare only the IP portions of addresses, ignoring port
+ *
+ * @param a Address to compare
+ * @return True if both addresses are of the same (valid) type and their IPs match
+ */
+ inline bool ipsEqual(const InetAddress &a) const
+ throw()
+ {
+ if (_sa.saddr.sa_family == a._sa.saddr.sa_family) {
+ switch(_sa.saddr.sa_family) {
+ case AF_INET:
+ return (_sa.sin.sin_addr.s_addr == a._sa.sin.sin_addr.s_addr);
+ case AF_INET6:
+ return (!memcmp(_sa.sin6.sin6_addr.s6_addr,a._sa.sin6.sin6_addr.s6_addr,16));
+ }
+ }
+ return false;
+ }
+
+ bool operator==(const InetAddress &a) const throw();
+ inline bool operator!=(const InetAddress &a) const throw() { return !(*this == a); }
+ bool operator<(const InetAddress &a) const throw();
+ inline bool operator>(const InetAddress &a) const throw() { return (a < *this); }
+ inline bool operator<=(const InetAddress &a) const throw() { return !(a < *this); }
+ inline bool operator>=(const InetAddress &a) const throw() { return !(*this < a); }
+
+ /**
+ * @return True if address family is non-zero
+ */
+ inline operator bool() const throw() { return ((_sa.saddr.sa_family == AF_INET)||(_sa.saddr.sa_family == AF_INET6)); }
+
+ /**
+ * Set to null/zero
+ */
+ inline void zero()
+ throw()
+ {
+ _sa.saddr.sa_family = 0;
+ }
+
+private:
+ union {
+ struct sockaddr saddr;
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ } _sa;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Logger.cpp b/node/Logger.cpp
new file mode 100644
index 00000000..7bed5990
--- /dev/null
+++ b/node/Logger.cpp
@@ -0,0 +1,141 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include "Logger.hpp"
+
+namespace ZeroTier {
+
+Logger::Logger(const char *p,const char *prefix,unsigned long maxLogSize) :
+ _path((p) ? p : ""),
+ _prefix((prefix) ? (std::string(prefix) + " ") : ""),
+ _maxLogSize(maxLogSize),
+ _log_m(),
+ _log((FILE *)0)
+{
+ if (_path.length())
+ _log = fopen(_path.c_str(),"a");
+ else _log = stdout;
+}
+
+Logger::~Logger()
+{
+ fflush(_log);
+ if ((_log)&&(_log != stdout)&&(_log != stderr))
+ fclose(_log);
+}
+
+void Logger::log(const char *fmt,...)
+{
+ va_list ap;
+ char tmp[128];
+
+ if (_log) {
+ Mutex::Lock _l(_log_m);
+ _rotateIfNeeded();
+
+ if (_log) {
+ time_t now = time(0);
+ char *nowstr = ctime_r(&now,tmp);
+ for(char *c=nowstr;*c;++c) {
+ if (*c == '\n')
+ *c = '\0';
+ }
+
+ if (_prefix.length())
+ fwrite(_prefix.data(),1,_prefix.length(),_log);
+
+ fprintf(_log,"[%s] ",nowstr);
+ va_start(ap,fmt);
+ vfprintf(_log,fmt,ap);
+ va_end(ap);
+#ifdef _WIN32
+ fwrite("\r\n",1,2,_log);
+#else
+ fwrite("\n",1,1,_log);
+#endif
+
+ fflush(_log);
+ }
+ }
+}
+
+#ifdef ZT_TRACE
+void Logger::trace(const char *module,unsigned int line,const char *fmt,...)
+{
+ va_list ap;
+ char tmp[128];
+
+ if (_log) {
+ Mutex::Lock _l(_log_m);
+ _rotateIfNeeded();
+
+ if (_log) {
+ time_t now = time(0);
+ char *nowstr = ctime_r(&now,tmp);
+ for(char *c=nowstr;*c;++c) {
+ if (*c == '\n')
+ *c = '\0';
+ }
+
+ if (_prefix.length())
+ fwrite(_prefix.data(),1,_prefix.length(),_log);
+
+ fprintf(_log,"[%s] TRACE/%s:%u ",nowstr,module,line);
+ va_start(ap,fmt);
+ vfprintf(_log,fmt,ap);
+ va_end(ap);
+#ifdef _WIN32
+ fwrite("\r\n",1,2,_log);
+#else
+ fwrite("\n",1,1,_log);
+#endif
+
+ fflush(_log);
+ }
+ }
+}
+#endif
+
+void Logger::_rotateIfNeeded()
+{
+ if ((_maxLogSize)&&(_log != stdout)&&(_log != stderr)) {
+ long pos = ftell(_log);
+ if (pos > (long)_maxLogSize) {
+ fclose(_log);
+ rename(_path.c_str(),std::string(_path).append(".old").c_str());
+ _log = fopen(_path.c_str(),"w");
+ }
+ }
+}
+
+} // namespace ZeroTier
+
diff --git a/node/Logger.hpp b/node/Logger.hpp
new file mode 100644
index 00000000..8ecfc0d6
--- /dev/null
+++ b/node/Logger.hpp
@@ -0,0 +1,90 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_LOGGER_HPP
+#define _ZT_LOGGER_HPP
+
+#include <stdio.h>
+#include <string>
+#include <stdexcept>
+#include "NonCopyable.hpp"
+#include "Mutex.hpp"
+
+#undef LOG
+#define LOG(f,...) if (_r->log) _r->log->log(f,##__VA_ARGS__)
+
+#undef TRACE
+#ifdef ZT_TRACE
+#define TRACE(f,...) if (_r->log) _r->log->trace(__FILE__,__LINE__,f,##__VA_ARGS__)
+#else
+#define TRACE(f,...) {}
+#endif
+
+namespace ZeroTier {
+
+/**
+ * Utility for outputting logs to a file or stdout/stderr
+ */
+class Logger : NonCopyable
+{
+public:
+ /**
+ * Construct a logger to log to a file or stdout
+ *
+ * If a path is supplied to log to a file, maxLogSize indicates the size
+ * at which this file is closed, renamed to .old, and then a new log is
+ * opened (essentially a log rotation). If stdout is used, this is ignored.
+ *
+ * @param p Path to log to or NULL to use stdout
+ * @param prefix Prefix to prepend to log lines or NULL for none
+ * @param maxLogSize Maximum log size (0 for no limit)
+ */
+ Logger(const char *p,const char *prefix,unsigned long maxLogSize);
+ ~Logger();
+
+ void log(const char *fmt,...);
+
+#ifdef ZT_TRACE
+ void trace(const char *module,unsigned int line,const char *fmt,...);
+#else
+ inline void trace(const char *module,unsigned int line,const char *fmt,...) {}
+#endif
+
+private:
+ void _rotateIfNeeded();
+
+ std::string _path;
+ std::string _prefix;
+ unsigned long _maxLogSize;
+ Mutex _log_m;
+ FILE *_log;
+};
+
+} // namespace ZeroTier
+
+#endif
+
diff --git a/node/MAC.hpp b/node/MAC.hpp
new file mode 100644
index 00000000..f33277c0
--- /dev/null
+++ b/node/MAC.hpp
@@ -0,0 +1,160 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_MAC_HPP
+#define _ZT_MAC_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "Array.hpp"
+#include "Constants.hpp"
+#include "Utils.hpp"
+
+namespace ZeroTier {
+
+/**
+ * An Ethernet MAC address
+ */
+class MAC : public Array<unsigned char,6>
+{
+public:
+ /**
+ * Create a zero/null MAC
+ */
+ MAC()
+ throw()
+ {
+ for(unsigned int i=0;i<6;++i)
+ data[i] = 0;
+ }
+
+ /**
+ * Create a MAC consisting of only this octet
+ *
+ * @param octet Octet to fill MAC with (e.g. 0xff for broadcast-all)
+ */
+ MAC(const unsigned char octet)
+ throw()
+ {
+ for(unsigned int i=0;i<6;++i)
+ data[i] = octet;
+ }
+
+ /**
+ * Create a MAC from raw bits
+ *
+ * @param bits 6 bytes of MAC address data
+ */
+ MAC(const void *bits)
+ throw()
+ {
+ for(unsigned int i=0;i<6;++i)
+ data[i] = ((const unsigned char *)bits)[i];
+ }
+
+ /**
+ * @return True if non-NULL (not all zero)
+ */
+ inline operator bool() const
+ throw()
+ {
+ for(unsigned int i=0;i<6;++i) {
+ if (data[i])
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return True if this is the broadcast-all MAC (0xff:0xff:...)
+ */
+ inline bool isBroadcast() const
+ throw()
+ {
+ for(unsigned int i=0;i<6;++i) {
+ if (data[i] != 0xff)
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return True if this is a multicast/broadcast address
+ */
+ inline bool isMulticast() const
+ throw()
+ {
+ return ((data[0] & 1));
+ }
+
+ /**
+ * @return True if this is a ZeroTier unicast MAC
+ */
+ inline bool isZeroTier() const
+ throw()
+ {
+ return (data[0] == ZT_MAC_FIRST_OCTET);
+ }
+
+ /**
+ * Zero this MAC
+ */
+ inline void zero()
+ throw()
+ {
+ for(unsigned int i=0;i<6;++i)
+ data[i] = 0;
+ }
+
+ /**
+ * @param s String hex representation (with or without :'s)
+ * @return True if string decoded into a full-length MAC
+ */
+ inline bool fromString(const char *s)
+ {
+ std::string b(Utils::unhex(s));
+ if (b.length() == 6) {
+ for(unsigned int i=0;i<6;++i)
+ data[i] = (unsigned char)b[i];
+ return true;
+ }
+ for(unsigned int i=0;i<6;++i)
+ data[i] = 0;
+ return false;
+ }
+
+ inline std::string toString() const
+ {
+ char tmp[32];
+ sprintf(tmp,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",(int)data[0],(int)data[1],(int)data[2],(int)data[3],(int)data[4],(int)data[5]);
+ return std::string(tmp);
+ }
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp
new file mode 100644
index 00000000..ac7de481
--- /dev/null
+++ b/node/MulticastGroup.hpp
@@ -0,0 +1,144 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_MULTICASTGROUP_HPP
+#define _ZT_MULTICASTGROUP_HPP
+
+#include <stdint.h>
+#include <string>
+#include "MAC.hpp"
+#include "InetAddress.hpp"
+
+namespace ZeroTier {
+
+/**
+ * A multicast group composed of a multicast MAC and a 64-bit ADI field
+ *
+ * ADI stands for additional distinguishing information. ADI is primarily for
+ * adding additional information to broadcast (ff:ff:ff:ff:ff:ff) memberships,
+ * since straight-up broadcast won't scale. Right now it's zero except for
+ * IPv4 ARP, where it holds the IPv4 address itself to make ARP into a
+ * selective multicast query that can scale.
+ *
+ * In the future we might add some kind of plugin architecture that can add
+ * ADI for things like mDNS (multicast DNS) to improve the selectivity of
+ * those protocols.
+ *
+ * MulticastGroup behaves as an immutable value object.
+ */
+class MulticastGroup
+{
+public:
+ MulticastGroup()
+ throw() :
+ _mac(),
+ _adi(0)
+ {
+ }
+
+ MulticastGroup(const MAC &m,uint32_t a)
+ throw() :
+ _mac(m),
+ _adi(a)
+ {
+ }
+
+ /**
+ * Derive the multicast group used for address resolution (ARP/NDP) for an IP
+ *
+ * @param ip IP address (port field is ignored)
+ * @return Multicat group for ARP/NDP
+ */
+ static inline MulticastGroup deriveMulticastGroupForAddressResolution(const InetAddress &ip)
+ throw()
+ {
+ if (ip.isV4()) {
+ // IPv4 wants braodcast MACs, so we shove the V4 address itself into
+ // the Multicast Group ADI field. Making V4 ARP work is basically why
+ // ADI was added, as well as handling other things that want mindless
+ // Ethernet broadcast to all.
+ return MulticastGroup(MAC((unsigned char)0xff),Utils::ntoh(*((const uint32_t *)ip.rawIpData())));
+ } else if (ip.isV6()) {
+ // IPv6 is better designed in this respect. We can compute the IPv6
+ // multicast address directly from the IP address, and it gives us
+ // 24 bits of uniqueness. Collisions aren't likely to be common enough
+ // to care about.
+ const unsigned char *a = (const unsigned char *)ip.rawIpData();
+ MAC m;
+ m.data[0] = 0x33;
+ m.data[1] = 0x33;
+ m.data[2] = 0xff;
+ m.data[3] = a[13];
+ m.data[4] = a[14];
+ m.data[5] = a[15];
+ return MulticastGroup(m,0);
+ }
+ return MulticastGroup();
+ }
+
+ /**
+ * @return Human readable string representing this group
+ */
+ inline std::string toString() const
+ {
+ char buf[64];
+ sprintf(buf,"%.2x:%.2x:%.2x:%.2x:%.2x:%.2x/%.8lx",(unsigned int)_mac.data[0],(unsigned int)_mac.data[1],(unsigned int)_mac.data[2],(unsigned int)_mac.data[3],(unsigned int)_mac.data[4],(unsigned int)_mac.data[5],(unsigned long)_adi);
+ return std::string(buf);
+ }
+
+ /**
+ * @return Multicast address
+ */
+ inline const MAC &mac() const throw() { return _mac; }
+
+ /**
+ * @return Additional distinguishing information
+ */
+ inline uint32_t adi() const throw() { return _adi; }
+
+ inline bool operator==(const MulticastGroup &g) const throw() { return ((_mac == g._mac)&&(_adi == g._adi)); }
+ inline bool operator!=(const MulticastGroup &g) const throw() { return ((_mac != g._mac)||(_adi != g._adi)); }
+ inline bool operator<(const MulticastGroup &g) const throw()
+ {
+ if (_mac < g._mac)
+ return true;
+ else if (_mac == g._mac)
+ return (_adi < g._adi);
+ return false;
+ }
+ inline bool operator>(const MulticastGroup &g) const throw() { return (g < *this); }
+ inline bool operator<=(const MulticastGroup &g) const throw() { return !(g < *this); }
+ inline bool operator>=(const MulticastGroup &g) const throw() { return !(*this < g); }
+
+private:
+ MAC _mac;
+ uint32_t _adi;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Mutex.hpp b/node/Mutex.hpp
new file mode 100644
index 00000000..493cc425
--- /dev/null
+++ b/node/Mutex.hpp
@@ -0,0 +1,197 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_MUTEX_HPP
+#define _ZT_MUTEX_HPP
+
+#include "NonCopyable.hpp"
+
+#if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+
+#include <stdlib.h>
+#include <pthread.h>
+
+namespace ZeroTier {
+
+class Mutex : NonCopyable
+{
+public:
+ Mutex()
+ throw()
+ {
+ pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
+ }
+
+ ~Mutex()
+ {
+ pthread_mutex_destroy(&_mh);
+ }
+
+ inline void lock()
+ throw()
+ {
+ pthread_mutex_lock(&_mh);
+ }
+
+ inline void unlock()
+ throw()
+ {
+ pthread_mutex_unlock(&_mh);
+ }
+
+ inline void lock() const
+ throw()
+ {
+ (const_cast <Mutex *> (this))->lock();
+ }
+
+ inline void unlock() const
+ throw()
+ {
+ (const_cast <Mutex *> (this))->unlock();
+ }
+
+ /**
+ * Uses C++ contexts and constructor/destructor to lock/unlock automatically
+ */
+ class Lock : NonCopyable
+ {
+ public:
+ Lock(Mutex &m)
+ throw() :
+ _m(&m)
+ {
+ m.lock();
+ }
+
+ Lock(const Mutex &m)
+ throw() :
+ _m(const_cast<Mutex *>(&m))
+ {
+ _m->lock();
+ }
+
+ ~Lock()
+ {
+ _m->unlock();
+ }
+
+ private:
+ Mutex *const _m;
+ };
+
+private:
+ pthread_mutex_t _mh;
+};
+
+} // namespace ZeroTier
+
+#endif // Apple / Linux
+
+#ifdef _WIN32
+
+#include <stdlib.h>
+#include <Windows.h>
+
+namespace ZeroTier {
+
+class Mutex : NonCopyable
+{
+public:
+ Mutex()
+ throw()
+ {
+ InitializeCriticalSection(&_cs);
+ }
+
+ ~Mutex()
+ {
+ DeleteCriticalSection(&_cs);
+ }
+
+ inline void lock()
+ throw()
+ {
+ EnterCriticalSection(&_cs);
+ }
+
+ inline void unlock()
+ throw()
+ {
+ LeaveCriticalSection(&_cs);
+ }
+
+ inline void lock() const
+ throw()
+ {
+ (const_cast <Mutex *> (this))->lock();
+ }
+
+ inline void unlock() const
+ throw()
+ {
+ (const_cast <Mutex *> (this))->unlock();
+ }
+
+ /**
+ * Uses C++ contexts and constructor/destructor to lock/unlock automatically
+ */
+ class Lock : NonCopyable
+ {
+ public:
+ Lock(Mutex &m)
+ throw() :
+ _m(&m)
+ {
+ m.lock();
+ }
+
+ Lock(const Mutex &m)
+ throw() :
+ _m(const_cast<Mutex *>(&m))
+ {
+ _m->lock();
+ }
+
+ ~Lock()
+ {
+ _m->unlock();
+ }
+
+ private:
+ Mutex *const _m;
+ };
+
+private:
+ CRITICAL_SECTION _cs;
+};
+
+} // namespace ZeroTier
+
+#endif // _WIN32
+
+#endif
diff --git a/node/Network.cpp b/node/Network.cpp
new file mode 100644
index 00000000..41f04e9f
--- /dev/null
+++ b/node/Network.cpp
@@ -0,0 +1,80 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include "Network.hpp"
+#include "Switch.hpp"
+
+namespace ZeroTier {
+
+Network::Network(const RuntimeEnvironment *renv,uint64_t id)
+ throw(std::runtime_error) :
+ Thread(),
+ _r(renv),
+ _id(id),
+ _tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU),
+ _members(),
+ _open(false),
+ _lock()
+{
+ TRACE("new network %llu created, TAP device: %s",id,_tap.deviceName().c_str());
+ start();
+}
+
+Network::~Network()
+{
+ _tap.close();
+ join();
+ TRACE("network %llu (%s) closed",_id,_tap.deviceName().c_str());
+}
+
+void Network::main()
+ throw()
+{
+ Buffer<4096> buf;
+ MAC from,to;
+ unsigned int etherType = 0;
+
+ while (_tap.open()) {
+ unsigned int len = _tap.get(from,to,etherType,buf.data());
+ if (len) {
+ buf.setSize(len);
+ try {
+ if (!*__refCount)
+ break; // sanity check
+ _r->sw->onLocalEthernet(SharedPtr<Network>(this),from,to,etherType,buf);
+ } catch (std::exception &exc) {
+ TRACE("unexpected exception handling local packet: %s",exc.what());
+ } catch ( ... ) {
+ TRACE("unexpected exception handling local packet");
+ }
+ } else break;
+ }
+
+ TRACE("network %llu thread terminating",_id);
+}
+
+} // namespace ZeroTier
diff --git a/node/Network.hpp b/node/Network.hpp
new file mode 100644
index 00000000..1bbf0a9f
--- /dev/null
+++ b/node/Network.hpp
@@ -0,0 +1,162 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_NETWORK_HPP
+#define _ZT_NETWORK_HPP
+
+#include <string>
+#include <set>
+#include <vector>
+#include <stdexcept>
+#include "EthernetTap.hpp"
+#include "Address.hpp"
+#include "Mutex.hpp"
+#include "InetAddress.hpp"
+#include "Constants.hpp"
+#include "SharedPtr.hpp"
+#include "AtomicCounter.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Thread.hpp"
+#include "MulticastGroup.hpp"
+
+namespace ZeroTier {
+
+class NodeConfig;
+
+/**
+ * Local network endpoint
+ */
+class Network : protected Thread
+{
+ friend class SharedPtr<Network>;
+ friend class NodeConfig;
+
+private:
+ virtual ~Network();
+
+ Network(const RuntimeEnvironment *renv,uint64_t id)
+ throw(std::runtime_error);
+
+public:
+ /**
+ * @return Network ID
+ */
+ inline uint64_t id() const throw() { return _id; }
+
+ /**
+ * @return Ethernet tap
+ */
+ inline EthernetTap &tap() throw() { return _tap; }
+
+ /**
+ * Get this network's members
+ *
+ * If this is an open network, membership isn't relevant and this doesn't
+ * mean much. If it's a closed network, frames will only be exchanged to/from
+ * members.
+ *
+ * @return Members of this network
+ */
+ inline std::set<Address> members() const
+ {
+ Mutex::Lock _l(_lock);
+ return _members;
+ }
+
+ /**
+ * @param addr Address to check
+ * @return True if address is a member
+ */
+ inline bool isMember(const Address &addr) const
+ throw()
+ {
+ Mutex::Lock _l(_lock);
+ return (_members.count(addr) > 0);
+ }
+
+ /**
+ * Shortcut to check open() and then isMember()
+ *
+ * @param addr Address to check
+ * @return True if network is open or if address is a member
+ */
+ inline bool isAllowed(const Address &addr) const
+ throw()
+ {
+ Mutex::Lock _l(_lock);
+ return ((_open)||(_members.count(addr) > 0));
+ }
+
+ /**
+ * @return True if network is open (no membership required)
+ */
+ inline bool open() const
+ throw()
+ {
+ Mutex::Lock _l(_lock);
+ return _open;
+ }
+
+ /**
+ * Update internal multicast group set and return true if changed
+ *
+ * @return True if internal multicast group set has changed
+ */
+ inline bool updateMulticastGroups()
+ {
+ Mutex::Lock _l(_lock);
+ return _tap.updateMulticastGroups(_multicastGroups);
+ }
+
+ /**
+ * @return Latest set of multicast groups
+ */
+ inline std::set<MulticastGroup> multicastGroups() const
+ {
+ Mutex::Lock _l(_lock);
+ return _multicastGroups;
+ }
+
+protected:
+ virtual void main()
+ throw();
+
+private:
+ const RuntimeEnvironment *_r;
+ uint64_t _id;
+ EthernetTap _tap;
+ std::set<Address> _members;
+ std::set<MulticastGroup> _multicastGroups;
+ bool _open;
+ Mutex _lock;
+
+ AtomicCounter __refCount;
+};
+
+} // naemspace ZeroTier
+
+#endif
diff --git a/node/Node.cpp b/node/Node.cpp
new file mode 100644
index 00000000..830ee354
--- /dev/null
+++ b/node/Node.cpp
@@ -0,0 +1,447 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <map>
+#include <set>
+#include <utility>
+#include <algorithm>
+#include <list>
+#include <vector>
+#include <string>
+
+#ifndef _WIN32
+#include <fcntl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/file.h>
+#endif
+
+#include <openssl/sha.h>
+
+#include "Condition.hpp"
+#include "Node.hpp"
+#include "Topology.hpp"
+#include "Demarc.hpp"
+#include "Switch.hpp"
+#include "Utils.hpp"
+#include "EthernetTap.hpp"
+#include "Logger.hpp"
+#include "Constants.hpp"
+#include "InetAddress.hpp"
+#include "Pack.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "NodeConfig.hpp"
+#include "Defaults.hpp"
+#include "SysEnv.hpp"
+#include "Network.hpp"
+#include "MulticastGroup.hpp"
+#include "Mutex.hpp"
+
+#include "../version.h"
+
+namespace ZeroTier {
+
+struct _NodeImpl
+{
+ RuntimeEnvironment renv;
+ std::string reasonForTerminationStr;
+ Node::ReasonForTermination reasonForTermination;
+ bool started;
+ bool running;
+ volatile bool terminateNow;
+
+ // Helper used to rapidly terminate from run()
+ inline Node::ReasonForTermination terminateBecause(Node::ReasonForTermination r,const char *rstr)
+ {
+ RuntimeEnvironment *_r = &renv;
+ LOG("terminating: %s",rstr);
+
+ reasonForTerminationStr = rstr;
+ reasonForTermination = r;
+ running = false;
+ return r;
+ }
+};
+
+Node::Node(const char *hp,const char *urlPrefix,const char *configAuthorityIdentity)
+ throw() :
+ _impl(new _NodeImpl)
+{
+ _NodeImpl *impl = (_NodeImpl *)_impl;
+
+ impl->renv.homePath = hp;
+ impl->renv.autoconfUrlPrefix = urlPrefix;
+ impl->renv.configAuthorityIdentityStr = configAuthorityIdentity;
+
+ impl->reasonForTermination = Node::NODE_RUNNING;
+ impl->started = false;
+ impl->running = false;
+ impl->terminateNow = false;
+}
+
+Node::~Node()
+{
+ _NodeImpl *impl = (_NodeImpl *)_impl;
+
+ delete impl->renv.sysEnv;
+ delete impl->renv.topology;
+ delete impl->renv.sw;
+ delete impl->renv.demarc;
+ delete impl->renv.nc;
+ delete impl->renv.log;
+
+ delete impl;
+}
+
+/**
+ * Execute node in current thread
+ *
+ * This does not return until the node shuts down. Shutdown may be caused
+ * by an internally detected condition such as a new upgrade being
+ * available or a fatal error, or it may be signaled externally using
+ * the terminate() method.
+ *
+ * @return Reason for termination
+ */
+Node::ReasonForTermination Node::run()
+ throw()
+{
+ _NodeImpl *impl = (_NodeImpl *)_impl;
+ RuntimeEnvironment *_r = (RuntimeEnvironment *)&(impl->renv);
+
+ impl->started = true;
+ impl->running = true;
+
+ try {
+#ifdef ZT_LOG_STDOUT
+ _r->log = new Logger((const char *)0,(const char *)0,0);
+#else
+ _r->log = new Logger((_r->homePath + ZT_PATH_SEPARATOR_S + "node.log").c_str(),(const char *)0,131072);
+#endif
+
+ TRACE("initializing...");
+
+ if (!_r->configAuthority.fromString(_r->configAuthorityIdentityStr))
+ return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"configuration authority identity is not valid");
+
+ bool gotId = false;
+ std::string identitySecretPath(_r->homePath + ZT_PATH_SEPARATOR_S + "identity.secret");
+ std::string identityPublicPath(_r->homePath + ZT_PATH_SEPARATOR_S + "identity.public");
+ std::string idser;
+ if (Utils::readFile(identitySecretPath.c_str(),idser))
+ gotId = _r->identity.fromString(idser);
+ if (gotId) {
+ // Make sure identity.public matches identity.secret
+ idser = std::string();
+ Utils::readFile(identityPublicPath.c_str(),idser);
+ std::string pubid(_r->identity.toString(false));
+ if (idser != pubid) {
+ if (!Utils::writeFile(identityPublicPath.c_str(),pubid))
+ return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"could not write identity.public (home path not writable?)");
+ }
+ } else {
+ LOG("no identity found, generating one... this might take a few seconds...");
+ _r->identity.generate();
+ LOG("generated new identity: %s",_r->identity.address().toString().c_str());
+ idser = _r->identity.toString(true);
+ if (!Utils::writeFile(identitySecretPath.c_str(),idser))
+ return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"could not write identity.secret (home path not writable?)");
+ idser = _r->identity.toString(false);
+ if (!Utils::writeFile(identityPublicPath.c_str(),idser))
+ return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"could not write identity.public (home path not writable?)");
+ }
+ Utils::lockDownFile(identitySecretPath.c_str(),false);
+
+ // Generate ownership verification secret, which can be presented to
+ // a controlling web site (like ours) to prove ownership of a node and
+ // permit its configuration to be centrally modified. When ZeroTier One
+ // requests its config it sends a hash of this secret, and so the
+ // config server can verify this hash to determine if the secret the
+ // user presents is correct.
+ std::string ovsPath(_r->homePath + ZT_PATH_SEPARATOR_S + "thisdeviceismine");
+ if (((Utils::now() - Utils::getLastModified(ovsPath.c_str())) >= ZT_OVS_GENERATE_NEW_IF_OLDER_THAN)||(!Utils::readFile(ovsPath.c_str(),_r->ownershipVerificationSecret))) {
+ _r->ownershipVerificationSecret = "";
+ for(unsigned int i=0;i<24;++i)
+ _r->ownershipVerificationSecret.push_back("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[Utils::randomInt<unsigned int>() % 62]);
+ _r->ownershipVerificationSecret.append(ZT_EOL_S);
+ if (!Utils::writeFile(ovsPath.c_str(),_r->ownershipVerificationSecret))
+ return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"could not write 'thisdeviceismine' (home path not writable?)");
+ }
+ Utils::lockDownFile(ovsPath.c_str(),false);
+ _r->ownershipVerificationSecret = Utils::trim(_r->ownershipVerificationSecret); // trim off CR file is saved with
+ unsigned char ovsDig[32];
+ SHA256_CTX sha;
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,_r->ownershipVerificationSecret.data(),_r->ownershipVerificationSecret.length());
+ SHA256_Final(ovsDig,&sha);
+ _r->ownershipVerificationSecretHash = Utils::base64Encode(ovsDig,32);
+
+ // Create the core objects in RuntimeEnvironment: node config, demarcation
+ // point, switch, network topology database, and system environment
+ // watcher.
+ _r->nc = new NodeConfig(_r,_r->autoconfUrlPrefix + _r->identity.address().toString());
+ _r->demarc = new Demarc(_r);
+ _r->sw = new Switch(_r);
+ _r->topology = new Topology(_r,(_r->homePath + ZT_PATH_SEPARATOR_S + "peer.db").c_str());
+ _r->sysEnv = new SysEnv(_r);
+
+ // TODO: make configurable
+ bool boundPort = false;
+ for(unsigned int p=ZT_DEFAULT_UDP_PORT;p<(ZT_DEFAULT_UDP_PORT + 128);++p) {
+ if (_r->demarc->bindLocalUdp(p)) {
+ boundPort = true;
+ break;
+ }
+ }
+ if (!boundPort)
+ return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"could not bind any local UDP ports");
+
+ // TODO: bootstrap off network so we don't have to update code for
+ // changes in supernodes.
+ _r->topology->setSupernodes(ZT_DEFAULTS.supernodes);
+ } catch (std::bad_alloc &exc) {
+ return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"memory allocation failure");
+ } catch (std::runtime_error &exc) {
+ return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,exc.what());
+ } catch ( ... ) {
+ return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"unknown exception during initialization");
+ }
+
+ try {
+ uint64_t lastPingCheck = 0;
+ uint64_t lastTopologyClean = Utils::now(); // don't need to do this immediately
+ uint64_t lastNetworkFingerprintCheck = 0;
+ uint64_t lastAutoconfigureCheck = 0;
+ uint64_t networkConfigurationFingerprint = _r->sysEnv->getNetworkConfigurationFingerprint();
+ uint64_t lastMulticastCheck = 0;
+ uint64_t lastMulticastAnnounceAll = 0;
+ long lastDelayDelta = 0;
+
+ LOG("%s starting version %s",_r->identity.address().toString().c_str(),versionString());
+
+ while (!impl->terminateNow) {
+ uint64_t now = Utils::now();
+ bool pingAll = false; // set to true to force a ping of *all* known direct links
+
+ // Detect sleep/wake by looking for delay loop pauses that are longer
+ // than we intended to pause.
+ if (lastDelayDelta >= ZT_SLEEP_WAKE_DETECTION_THRESHOLD) {
+ lastNetworkFingerprintCheck = 0; // force network environment check
+ lastMulticastCheck = 0; // force multicast group check on taps
+ pingAll = true;
+
+ LOG("probable suspend/resume detected, pausing a moment for things to settle...");
+ Thread::sleep(ZT_SLEEP_WAKE_SETTLE_TIME);
+ }
+
+ // Periodically check our network environment, sending pings out to all
+ // our direct links if things look like we got a different address.
+ if ((now - lastNetworkFingerprintCheck) >= ZT_NETWORK_FINGERPRINT_CHECK_DELAY) {
+ lastNetworkFingerprintCheck = now;
+ uint64_t fp = _r->sysEnv->getNetworkConfigurationFingerprint();
+ if (fp != networkConfigurationFingerprint) {
+ LOG("netconf fingerprint change: %.16llx != %.16llx, pinging all peers",networkConfigurationFingerprint,fp);
+ networkConfigurationFingerprint = fp;
+ pingAll = true;
+ lastAutoconfigureCheck = 0; // check autoconf after network config change
+ lastMulticastCheck = 0; // check multicast group membership after network config change
+ }
+ }
+
+ if ((now - lastAutoconfigureCheck) >= ZT_AUTOCONFIGURE_CHECK_DELAY) {
+ // It seems odd to only do this simple check every so often, but the purpose is to
+ // delay between calls to refreshConfiguration() enough that the previous attempt
+ // has time to either succeed or fail. Otherwise we'll block the whole loop, since
+ // config update is guarded by a Mutex.
+ lastAutoconfigureCheck = now;
+ if ((now - _r->nc->lastAutoconfigure()) >= ZT_AUTOCONFIGURE_INTERVAL)
+ _r->nc->refreshConfiguration(); // happens in background
+ }
+
+ // Periodically check for changes in our local multicast subscriptions and broadcast
+ // those changes to peers.
+ if ((now - lastMulticastCheck) >= ZT_MULTICAST_LOCAL_POLL_PERIOD) {
+ lastMulticastCheck = now;
+ bool announceAll = ((now - lastMulticastAnnounceAll) >= ZT_MULTICAST_LIKE_ANNOUNCE_ALL_PERIOD);
+ try {
+ std::map< SharedPtr<Network>,std::set<MulticastGroup> > toAnnounce;
+ {
+ std::vector< SharedPtr<Network> > networks(_r->nc->networks());
+ for(std::vector< SharedPtr<Network> >::const_iterator nw(networks.begin());nw!=networks.end();++nw) {
+ if (((*nw)->updateMulticastGroups())||(announceAll))
+ toAnnounce.insert(std::pair< SharedPtr<Network>,std::set<MulticastGroup> >(*nw,(*nw)->multicastGroups()));
+ }
+ }
+
+ if (toAnnounce.size()) {
+ _r->sw->announceMulticastGroups(toAnnounce);
+
+ // Only update lastMulticastAnnounceAll if we've announced something. This keeps
+ // the announceAll condition true during startup when there are no multicast
+ // groups until there is at least one. Technically this shouldn't be required as
+ // updateMulticastGroups() should return true on any change, but why not?
+ if (announceAll)
+ lastMulticastAnnounceAll = now;
+ }
+ } catch (std::exception &exc) {
+ LOG("unexpected exception announcing multicast groups: %s",exc.what());
+ } catch ( ... ) {
+ LOG("unexpected exception announcing multicast groups: (unknown)");
+ }
+ }
+
+ if ((now - lastPingCheck) >= ZT_PING_CHECK_DELAY) {
+ lastPingCheck = now;
+ try {
+ if (_r->topology->isSupernode(_r->identity.address())) {
+ // The only difference in how supernodes behave is here: they only
+ // actively ping each other and only passively listen for pings
+ // from anyone else. They also don't send firewall openers, since
+ // they're never firewalled.
+ std::vector< SharedPtr<Peer> > sns(_r->topology->supernodePeers());
+ for(std::vector< SharedPtr<Peer> >::const_iterator p(sns.begin());p!=sns.end();++p) {
+ if ((now - (*p)->lastDirectSend()) > ZT_PEER_DIRECT_PING_DELAY)
+ _r->sw->sendHELLO((*p)->address());
+ }
+ } else {
+ std::vector< SharedPtr<Peer> > needPing,needFirewallOpener;
+
+ if (pingAll) {
+ _r->topology->eachPeer(Topology::CollectPeersWithActiveDirectPath(needPing));
+ } else {
+ _r->topology->eachPeer(Topology::CollectPeersThatNeedPing(needPing));
+ _r->topology->eachPeer(Topology::CollectPeersThatNeedFirewallOpener(needFirewallOpener));
+ }
+
+ for(std::vector< SharedPtr<Peer> >::iterator p(needPing.begin());p!=needPing.end();++p) {
+ try {
+ _r->sw->sendHELLO((*p)->address());
+ } catch (std::exception &exc) {
+ LOG("unexpected exception sending HELLO to %s: %s",(*p)->address().toString().c_str());
+ } catch ( ... ) {
+ LOG("unexpected exception sending HELLO to %s: (unknown)",(*p)->address().toString().c_str());
+ }
+ }
+
+ for(std::vector< SharedPtr<Peer> >::iterator p(needFirewallOpener.begin());p!=needFirewallOpener.end();++p) {
+ try {
+ (*p)->sendFirewallOpener(_r,now);
+ } catch (std::exception &exc) {
+ LOG("unexpected exception sending firewall opener to %s: %s",(*p)->address().toString().c_str(),exc.what());
+ } catch ( ... ) {
+ LOG("unexpected exception sending firewall opener to %s: (unknown)",(*p)->address().toString().c_str());
+ }
+ }
+ }
+ } catch (std::exception &exc) {
+ LOG("unexpected exception running ping check cycle: %s",exc.what());
+ } catch ( ... ) {
+ LOG("unexpected exception running ping check cycle: (unkonwn)");
+ }
+ }
+
+ if ((now - lastTopologyClean) >= ZT_TOPOLOGY_CLEAN_PERIOD) {
+ lastTopologyClean = now;
+ _r->topology->clean(); // happens in background
+ }
+
+ try {
+ unsigned long delay = std::min((unsigned long)ZT_MIN_SERVICE_LOOP_INTERVAL,_r->sw->doTimerTasks());
+ uint64_t start = Utils::now();
+ Thread::sleep(delay);
+ lastDelayDelta = (long)(Utils::now() - start) - (long)delay;
+ } catch (std::exception &exc) {
+ LOG("unexpected exception running Switch doTimerTasks: %s",exc.what());
+ } catch ( ... ) {
+ LOG("unexpected exception running Switch doTimerTasks: (unknown)");
+ }
+ }
+ } catch ( ... ) {
+ return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"unexpected exception during outer main I/O loop");
+ }
+
+ return impl->terminateBecause(Node::NODE_NORMAL_TERMINATION,"normal termination");
+}
+
+/**
+ * Obtain a human-readable reason for node termination
+ *
+ * @return Reason for node termination or NULL if run() has not returned
+ */
+const char *Node::reasonForTermination() const
+ throw()
+{
+ if ((!((_NodeImpl *)_impl)->started)||(((_NodeImpl *)_impl)->running))
+ return (const char *)0;
+ return ((_NodeImpl *)_impl)->reasonForTerminationStr.c_str();
+}
+
+/**
+ * Cause run() to return with NODE_NORMAL_TERMINATION
+ *
+ * This can be called from a signal handler or another thread to signal a
+ * running node to shut down. Shutdown may take a few seconds, so run()
+ * may not return instantly. Multiple calls are ignored.
+ */
+void Node::terminate()
+ throw()
+{
+ ((_NodeImpl *)_impl)->terminateNow = true;
+}
+
+class _VersionStringMaker
+{
+public:
+ char vs[32];
+ _VersionStringMaker()
+ {
+ sprintf(vs,"%d.%d.%d",(int)ZEROTIER_ONE_VERSION_MAJOR,(int)ZEROTIER_ONE_VERSION_MINOR,(int)ZEROTIER_ONE_VERSION_REVISION);
+ }
+ ~_VersionStringMaker() {}
+};
+static const _VersionStringMaker __versionString;
+
+const char *Node::versionString() throw() { return __versionString.vs; }
+
+unsigned int Node::versionMajor() throw() { return ZEROTIER_ONE_VERSION_MAJOR; }
+unsigned int Node::versionMinor() throw() { return ZEROTIER_ONE_VERSION_MINOR; }
+unsigned int Node::versionRevision() throw() { return ZEROTIER_ONE_VERSION_REVISION; }
+
+// Scanned for by loader and/or updater to determine a binary's version
+const unsigned char EMBEDDED_VERSION_STAMP[20] = {
+ 0x6d,0xfe,0xff,0x01,0x90,0xfa,0x89,0x57,0x88,0xa1,0xaa,0xdc,0xdd,0xde,0xb0,0x33,
+ ZEROTIER_ONE_VERSION_MAJOR,
+ ZEROTIER_ONE_VERSION_MINOR,
+ (unsigned char)(((unsigned int)ZEROTIER_ONE_VERSION_REVISION) & 0xff), /* little-endian */
+ (unsigned char)((((unsigned int)ZEROTIER_ONE_VERSION_REVISION) >> 8) & 0xff)
+};
+
+} // namespace ZeroTier
diff --git a/node/Node.hpp b/node/Node.hpp
new file mode 100644
index 00000000..1cc9a746
--- /dev/null
+++ b/node/Node.hpp
@@ -0,0 +1,128 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_NODE_HPP
+#define _ZT_NODE_HPP
+
+namespace ZeroTier {
+
+/**
+ * A ZeroTier One node
+ *
+ * This class hides all its implementation details and all other classes in
+ * preparation for ZeroTier One being made available in library form for
+ * embedding in mobile apps.
+ */
+class Node
+{
+public:
+ /**
+ * Returned by node main if/when it terminates
+ */
+ enum ReasonForTermination
+ {
+ NODE_RUNNING = 0,
+ NODE_NORMAL_TERMINATION = 1,
+ NODE_RESTART_FOR_RECONFIGURATION = 2,
+ NODE_UNRECOVERABLE_ERROR = 3,
+ NODE_NEW_VERSION_AVAILABLE = 4
+ };
+
+ /**
+ * Create a new node
+ *
+ * The node is not executed until run() is called.
+ *
+ * @param hp Home directory path
+ * @param url URL prefix for autoconfiguration (http and file permitted)
+ * @param configAuthorityIdentity Public identity used to encrypt/authenticate configuration from this URL (ASCII string format)
+ * @throws std::invalid_argument Invalid argument supplied to constructor
+ */
+ Node(const char *hp,const char *urlPrefix,const char *configAuthorityIdentity)
+ throw();
+
+ ~Node();
+
+ /**
+ * Execute node in current thread
+ *
+ * This does not return until the node shuts down. Shutdown may be caused
+ * by an internally detected condition such as a new upgrade being
+ * available or a fatal error, or it may be signaled externally using
+ * the terminate() method.
+ *
+ * @return Reason for termination
+ */
+ ReasonForTermination run()
+ throw();
+
+ /**
+ * Obtain a human-readable reason for node termination
+ *
+ * @return Reason for node termination or NULL if run() has not returned
+ */
+ const char *reasonForTermination() const
+ throw();
+
+ /**
+ * Cause run() to return with NODE_NORMAL_TERMINATION
+ *
+ * This can be called from a signal handler or another thread to signal a
+ * running node to shut down. Shutdown may take a few seconds, so run()
+ * may not return instantly. Multiple calls are ignored.
+ */
+ void terminate()
+ throw();
+
+ /**
+ * Get the ZeroTier version in major.minor.revision string format
+ *
+ * @return Version in string form
+ */
+ static const char *versionString()
+ throw();
+
+ static unsigned int versionMajor() throw();
+ static unsigned int versionMinor() throw();
+ static unsigned int versionRevision() throw();
+
+private:
+ void *const _impl; // private implementation
+};
+
+/**
+ * An embedded version code that can be searched for in the binary
+ *
+ * This shouldn't be used by users, but is exported to make certain that
+ * the linker actually includes it in the image.
+ */
+extern const unsigned char EMBEDDED_VERSION_STAMP[20];
+
+} // namespace ZeroTier
+
+#endif
+
diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp
new file mode 100644
index 00000000..47310050
--- /dev/null
+++ b/node/NodeConfig.cpp
@@ -0,0 +1,206 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <memory>
+#include <string>
+
+#include <json/json.h>
+
+#include "NodeConfig.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Defaults.hpp"
+#include "Utils.hpp"
+#include "Logger.hpp"
+
+namespace ZeroTier {
+
+NodeConfig::NodeConfig(const RuntimeEnvironment *renv,const std::string &url) :
+ _r(renv),
+ _lastAutoconfigure(0),
+ _lastAutoconfigureLastModified(),
+ _url(url),
+ _autoconfigureLock(),
+ _networks(),
+ _networks_m()
+{
+}
+
+NodeConfig::~NodeConfig()
+{
+ _autoconfigureLock.lock(); // wait for any autoconfs to finish
+ _autoconfigureLock.unlock();
+}
+
+void NodeConfig::refreshConfiguration()
+{
+ _autoconfigureLock.lock(); // unlocked when handler gets called
+
+ TRACE("refreshing autoconfigure URL %s (if modified since: '%s')",_url.c_str(),_lastAutoconfigureLastModified.c_str());
+
+ std::map<std::string,std::string> reqHeaders;
+ reqHeaders["X-ZT-ID"] = _r->identity.toString(false);
+ reqHeaders["X-ZT-OVSH"] = _r->ownershipVerificationSecretHash;
+ if (_lastAutoconfigureLastModified.length())
+ reqHeaders["If-Modified-Since"] = _lastAutoconfigureLastModified;
+
+ new Http::Request(Http::HTTP_METHOD_GET,_url,reqHeaders,std::string(),&NodeConfig::_CBautoconfHandler,this);
+}
+
+void NodeConfig::__CBautoconfHandler(const std::string &lastModified,const std::string &body)
+{
+ try {
+ Json::Value root;
+ Json::Reader reader;
+
+ std::string dec(_r->identity.decrypt(_r->configAuthority,body.data(),body.length()));
+ if (!dec.length()) {
+ LOG("autoconfigure from %s failed: data did not decrypt as from config authority %s",_url.c_str(),_r->configAuthority.address().toString().c_str());
+ return;
+ }
+ TRACE("decrypted autoconf: %s",dec.c_str());
+
+ if (!reader.parse(dec,root,false)) {
+ LOG("autoconfigure from %s failed: JSON parse error: %s",_url.c_str(),reader.getFormattedErrorMessages().c_str());
+ return;
+ }
+
+ if (!root.isObject()) {
+ LOG("autoconfigure from %s failed: not a JSON object",_url.c_str());
+ return;
+ }
+
+ // Configure networks
+ const Json::Value &networks = root["_networks"];
+ if (networks.isArray()) {
+ Mutex::Lock _l(_networks_m);
+ for(unsigned int ni=0;ni<networks.size();++ni) {
+ if (networks[ni].isObject()) {
+ const Json::Value &nwid_ = networks[ni]["id"];
+ uint64_t nwid = nwid_.isNumeric() ? (uint64_t)nwid_.asUInt64() : (uint64_t)strtoull(networks[ni]["id"].asString().c_str(),(char **)0,10);
+
+ if (nwid) {
+ SharedPtr<Network> nw;
+ std::map< uint64_t,SharedPtr<Network> >::iterator nwent(_networks.find(nwid));
+ if (nwent != _networks.end())
+ nw = nwent->second;
+ else {
+ try {
+ nw = SharedPtr<Network>(new Network(_r,nwid));
+ _networks[nwid] = nw;
+ } catch (std::exception &exc) {
+ LOG("unable to create network %llu: %s",nwid,exc.what());
+ } catch ( ... ) {
+ LOG("unable to create network %llu: unknown exception",nwid);
+ }
+ }
+
+ if (nw) {
+ Mutex::Lock _l2(nw->_lock);
+ nw->_open = networks[ni]["isOpen"].asBool();
+
+ // Ensure that TAP device has all the right IP addresses
+ // TODO: IPv6 might work a tad differently
+ std::set<InetAddress> allIps;
+ const Json::Value &addresses = networks[ni]["_addresses"];
+ if (addresses.isArray()) {
+ for(unsigned int ai=0;ai<addresses.size();++ai) {
+ if (addresses[ai].isString()) {
+ InetAddress addr(addresses[ai].asString());
+ if (addr) {
+ TRACE("network %llu IP/netmask: %s",nwid,addr.toString().c_str());
+ allIps.insert(addr);
+ }
+ }
+ }
+ }
+ nw->_tap.setIps(allIps);
+
+ // NOTE: the _members field is optional for open networks,
+ // since members of open nets do not need to check membership
+ // of packet senders and mutlicasters.
+ const Json::Value &members = networks[ni]["_members"];
+ nw->_members.clear();
+ if (members.isArray()) {
+ for(unsigned int mi=0;mi<members.size();++mi) {
+ std::string rawAddr(Utils::unhex(members[mi].asString()));
+ if (rawAddr.length() == ZT_ADDRESS_LENGTH) {
+ Address addr(rawAddr.data());
+ if ((addr)&&(!addr.isReserved())) {
+ TRACE("network %llu member: %s",nwid,addr.toString().c_str());
+ nw->_members.insert(addr);
+ }
+ }
+ }
+ }
+ }
+ } else {
+ TRACE("ignored networks[%u], 'id' field missing");
+ }
+ } else {
+ TRACE("ignored networks[%u], not a JSON object",ni);
+ }
+ }
+ }
+
+ _lastAutoconfigure = Utils::now();
+ _lastAutoconfigureLastModified = lastModified;
+ } catch (std::exception &exc) {
+ TRACE("exception parsing autoconf URL response: %s",exc.what());
+ } catch ( ... ) {
+ TRACE("unexpected exception parsing autoconf URL response");
+ }
+}
+
+bool NodeConfig::_CBautoconfHandler(Http::Request *req,void *arg,const std::string &url,int code,const std::map<std::string,std::string> &headers,const std::string &body)
+{
+#ifdef ZT_TRACE
+ const RuntimeEnvironment *_r = ((NodeConfig *)arg)->_r;
+#endif
+
+ if (code == 200) {
+ TRACE("200 got autoconfigure response from %s: %u bytes",url.c_str(),(unsigned int)body.length());
+
+ std::map<std::string,std::string>::const_iterator lm(headers.find("Last-Modified"));
+ if (lm != headers.end())
+ ((NodeConfig *)arg)->__CBautoconfHandler(lm->second,body);
+ else ((NodeConfig *)arg)->__CBautoconfHandler(std::string(),body);
+ } else if (code == 304) {
+ TRACE("304 autoconfigure deferred, remote URL %s not modified",url.c_str());
+ ((NodeConfig *)arg)->_lastAutoconfigure = Utils::now(); // still considered a success
+ } else if (code == 409) { // conflict, ID address in use by another ID
+ TRACE("%d autoconfigure failed from %s",code,url.c_str());
+ } else {
+ TRACE("%d autoconfigure failed from %s",code,url.c_str());
+ }
+
+ ((NodeConfig *)arg)->_autoconfigureLock.unlock();
+ return false; // causes Request to delete itself
+}
+
+} // namespace ZeroTier
diff --git a/node/NodeConfig.hpp b/node/NodeConfig.hpp
new file mode 100644
index 00000000..a6ea6293
--- /dev/null
+++ b/node/NodeConfig.hpp
@@ -0,0 +1,136 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_NODECONFIG_HPP
+#define _ZT_NODECONFIG_HPP
+
+#include <map>
+#include <set>
+#include <string>
+#include <stdint.h>
+#include "SharedPtr.hpp"
+#include "Network.hpp"
+#include "Utils.hpp"
+#include "Http.hpp"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * Node configuration holder and fetcher
+ */
+class NodeConfig
+{
+public:
+ /**
+ * @param renv Runtime environment
+ * @param url Autoconfiguration URL (http:// or file://)
+ */
+ NodeConfig(const RuntimeEnvironment *renv,const std::string &url);
+
+ ~NodeConfig();
+
+ /**
+ * @param nwid Network ID
+ * @return Network or NULL if no network for that ID
+ */
+ inline SharedPtr<Network> network(uint64_t nwid) const
+ {
+ Mutex::Lock _l(_networks_m);
+ std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.find(nwid));
+ return ((n == _networks.end()) ? SharedPtr<Network>() : n->second);
+ }
+
+ /**
+ * @return Vector containing all networks
+ */
+ inline std::vector< SharedPtr<Network> > networks() const
+ {
+ std::vector< SharedPtr<Network> > nwlist;
+ Mutex::Lock _l(_networks_m);
+ for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
+ nwlist.push_back(n->second);
+ return nwlist;
+ }
+
+ /**
+ * @param nwid Network ID
+ * @return True if this network exists
+ */
+ inline bool hasNetwork(uint64_t nwid)
+ {
+ Mutex::Lock _l(_networks_m);
+ return (_networks.count(nwid) > 0);
+ }
+
+ /**
+ * @return Set of network tap device names
+ */
+ inline std::set<std::string> networkTapDeviceNames() const
+ {
+ std::set<std::string> tapDevs;
+ Mutex::Lock _l(_networks_m);
+ for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
+ tapDevs.insert(n->second->tap().deviceName());
+ return tapDevs;
+ }
+
+ /**
+ * @return Time of last successful autoconfigure or refresh
+ */
+ inline uint64_t lastAutoconfigure() const { return _lastAutoconfigure; }
+
+ /**
+ * @return Autoconfiguration URL
+ */
+ inline const std::string &url() const { return _url; }
+
+ /**
+ * Refresh configuration from autoconf URL
+ */
+ void refreshConfiguration();
+
+private:
+ void __CBautoconfHandler(const std::string &lastModified,const std::string &body);
+ static bool _CBautoconfHandler(Http::Request *req,void *arg,const std::string &url,int code,const std::map<std::string,std::string> &headers,const std::string &body);
+
+ const RuntimeEnvironment *_r;
+
+ volatile uint64_t _lastAutoconfigure;
+
+ std::string _lastAutoconfigureLastModified;
+ std::string _url;
+ Mutex _autoconfigureLock;
+
+ std::map< uint64_t,SharedPtr<Network> > _networks;
+ Mutex _networks_m;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/NonCopyable.hpp b/node/NonCopyable.hpp
new file mode 100644
index 00000000..26536a36
--- /dev/null
+++ b/node/NonCopyable.hpp
@@ -0,0 +1,47 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _NONCOPYABLE_HPP__
+#define _NONCOPYABLE_HPP__
+
+namespace ZeroTier {
+
+/**
+ * A simple concept that belongs in the C++ language spec
+ */
+class NonCopyable
+{
+protected:
+ NonCopyable() throw() {}
+private:
+ NonCopyable(const NonCopyable&);
+ const NonCopyable& operator=(const NonCopyable&);
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Pack.cpp b/node/Pack.cpp
new file mode 100644
index 00000000..8bac1300
--- /dev/null
+++ b/node/Pack.cpp
@@ -0,0 +1,159 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <iostream>
+#include <string.h>
+#include <stdlib.h>
+#include "Pack.hpp"
+#include "BlobArray.hpp"
+#include "Utils.hpp"
+
+#include <openssl/sha.h>
+
+namespace ZeroTier {
+
+std::vector<const Pack::Entry *> Pack::getAll() const
+{
+ std::vector<const Entry *> v;
+ for(std::map<std::string,Entry>::const_iterator e=_entries.begin();e!=_entries.end();++e)
+ v.push_back(&(e->second));
+ return v;
+}
+
+const Pack::Entry *Pack::get(const std::string &name) const
+{
+ std::map<std::string,Entry>::const_iterator e(_entries.find(name));
+ return ((e == _entries.end()) ? (const Entry *)0 : &(e->second));
+}
+
+const Pack::Entry *Pack::put(const std::string &name,const std::string &content)
+{
+ SHA256_CTX sha;
+
+ Pack::Entry &e = _entries[name];
+ e.name = name;
+ e.content = content;
+
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,content.data(),content.length());
+ SHA256_Final(e.sha256,&sha);
+
+ e.signedBy.zero();
+ e.signature.assign((const char *)0,0);
+
+ return &e;
+}
+
+void Pack::clear()
+{
+ _entries.clear();
+}
+
+std::string Pack::serialize() const
+{
+ BlobArray archive;
+ for(std::map<std::string,Entry>::const_iterator e=_entries.begin();e!=_entries.end();++e) {
+ BlobArray entry;
+ entry.push_back(e->second.name);
+ entry.push_back(e->second.content);
+ entry.push_back(std::string((const char *)e->second.sha256,sizeof(e->second.sha256)));
+ entry.push_back(std::string((const char *)e->second.signedBy.data(),e->second.signedBy.size()));
+ entry.push_back(e->second.signature);
+ archive.push_back(entry.serialize());
+ }
+
+ std::string ser(archive.serialize());
+ std::string comp;
+ Utils::compress(ser.begin(),ser.end(),Utils::StringAppendOutput(comp));
+ return comp;
+}
+
+bool Pack::deserialize(const void *sd,unsigned int sdlen)
+{
+ unsigned char dig[32];
+ SHA256_CTX sha;
+
+ std::string decomp;
+ if (!Utils::decompress(((const char *)sd),((const char *)sd) + sdlen,Utils::StringAppendOutput(decomp)))
+ return false;
+
+ BlobArray archive;
+ archive.deserialize(decomp.data(),decomp.length());
+ clear();
+ for(BlobArray::const_iterator i=archive.begin();i!=archive.end();++i) {
+ BlobArray entry;
+ entry.deserialize(i->data(),i->length());
+
+ if (entry.size() != 5) return false;
+ if (entry[2].length() != 32) return false; // SHA-256
+ if (entry[3].length() != ZT_ADDRESS_LENGTH) return false; // Address
+
+ Pack::Entry &e = _entries[entry[0]];
+ e.name = entry[0];
+ e.content = entry[1];
+
+ SHA256_Init(&sha);
+ SHA256_Update(&sha,e.content.data(),e.content.length());
+ SHA256_Final(dig,&sha);
+ if (memcmp(dig,entry[2].data(),32)) return false; // integrity check failed
+ memcpy(e.sha256,dig,32);
+
+ if (entry[3].length() == ZT_ADDRESS_LENGTH)
+ e.signedBy = entry[3].data();
+ else e.signedBy.zero();
+ e.signature = entry[4];
+ }
+ return true;
+}
+
+bool Pack::signAll(const Identity &id)
+{
+ for(std::map<std::string,Entry>::iterator e=_entries.begin();e!=_entries.end();++e) {
+ e->second.signedBy = id.address();
+ e->second.signature = id.sign(e->second.sha256);
+ if (!e->second.signature.length())
+ return false;
+ }
+ return true;
+}
+
+std::vector<const Pack::Entry *> Pack::verifyAll(const Identity &id,bool mandatory) const
+{
+ std::vector<const Entry *> bad;
+ for(std::map<std::string,Entry>::const_iterator e=_entries.begin();e!=_entries.end();++e) {
+ if ((e->second.signedBy)&&(e->second.signature.length())) {
+ if (id.address() != e->second.signedBy)
+ bad.push_back(&(e->second));
+ else if (!id.verifySignature(e->second.sha256,e->second.signature.data(),e->second.signature.length()))
+ bad.push_back(&(e->second));
+ } else if (mandatory)
+ bad.push_back(&(e->second));
+ }
+ return bad;
+}
+
+} // namespace ZeroTier
diff --git a/node/Pack.hpp b/node/Pack.hpp
new file mode 100644
index 00000000..a0aecd6e
--- /dev/null
+++ b/node/Pack.hpp
@@ -0,0 +1,141 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_PACK_HPP
+#define _ZT_PACK_HPP
+
+#include <string>
+#include <map>
+#include <list>
+#include <stdexcept>
+#include "Address.hpp"
+#include "Identity.hpp"
+
+namespace ZeroTier {
+
+/**
+ * A very simple archive format for distributing packs of files or resources
+ *
+ * This is used for things like the auto-updater. It's not suitable for huge
+ * files, since at present it must work in memory. Packs support signing with
+ * identities and signature verification.
+ */
+class Pack
+{
+public:
+ /**
+ * Pack entry structure for looking up deserialized entries
+ */
+ struct Entry
+ {
+ std::string name;
+ std::string content;
+ unsigned char sha256[32];
+ Address signedBy;
+ std::string signature;
+ };
+
+ Pack() {}
+ ~Pack() {}
+
+ /**
+ * @return Vector of all entries
+ */
+ std::vector<const Entry *> getAll() const;
+
+ /**
+ * Look up an entry
+ *
+ * @param name Name to look up
+ * @return Pointer to entry if it exists or NULL if not found
+ */
+ const Entry *get(const std::string &name) const;
+
+ /**
+ * Add an entry to this pack
+ *
+ * @param name Entry to add
+ * @param content Entry's contents
+ * @return The new entry
+ */
+ const Entry *put(const std::string &name,const std::string &content);
+
+ /**
+ * Remove all entries
+ */
+ void clear();
+
+ /**
+ * @return Number of entries in pack
+ */
+ inline unsigned int numEntries() const { return (unsigned int)_entries.size(); }
+
+ /**
+ * Serialize this pack
+ *
+ * @return Serialized form (compressed with LZ4)
+ */
+ std::string serialize() const;
+
+ /**
+ * Deserialize this pack
+ *
+ * Any current contents are lost. This does not verify signatures,
+ * but does check SHA256 hashes for entry integrity. If the return
+ * value is false, the pack's contents are undefined.
+ *
+ * @param sd Serialized data
+ * @param sdlen Length of serialized data
+ * @return True on success, false on deserialization error
+ */
+ bool deserialize(const void *sd,unsigned int sdlen);
+ inline bool deserialize(const std::string &sd) { return deserialize(sd.data(),sd.length()); }
+
+ /**
+ * Sign all entries in this pack with a given identity
+ *
+ * @param id Identity to sign with
+ * @return True on signature success, false if error
+ */
+ bool signAll(const Identity &id);
+
+ /**
+ * Verify all signed entries
+ *
+ * @param id Identity to verify against
+ * @param mandatory If true, require that all entries be signed and fail if no signature
+ * @return Vector of entries that failed verification or empty vector if all passed
+ */
+ std::vector<const Entry *> verifyAll(const Identity &id,bool mandatory) const;
+
+private:
+ std::map<std::string,Entry> _entries;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Packet.cpp b/node/Packet.cpp
new file mode 100644
index 00000000..d12f396d
--- /dev/null
+++ b/node/Packet.cpp
@@ -0,0 +1,64 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include "Packet.hpp"
+
+namespace ZeroTier {
+
+const char *Packet::verbString(Verb v)
+ throw()
+{
+ switch(v) {
+ case VERB_NOP: return "NOP";
+ case VERB_HELLO: return "HELLO";
+ case VERB_ERROR: return "ERROR";
+ case VERB_OK: return "OK";
+ case VERB_WHOIS: return "WHOIS";
+ case VERB_RENDEZVOUS: return "RENDEZVOUS";
+ case VERB_FRAME: return "FRAME";
+ case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
+ case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
+ }
+ return "(unknown)";
+}
+
+const char *Packet::errorString(ErrorCode e)
+ throw()
+{
+ switch(e) {
+ case ERROR_NONE: return "NONE";
+ case ERROR_INVALID_REQUEST: return "INVALID_REQUEST";
+ case ERROR_BAD_PROTOCOL_VERSION: return "BAD_PROTOCOL_VERSION";
+ case ERROR_NOT_FOUND: return "NOT_FOUND";
+ case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION";
+ case ERROR_IDENTITY_INVALID: return "IDENTITY_INVALID";
+ case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION";
+ }
+ return "(unknown)";
+}
+
+} // namespace ZeroTier
diff --git a/node/Packet.hpp b/node/Packet.hpp
new file mode 100644
index 00000000..3fade674
--- /dev/null
+++ b/node/Packet.hpp
@@ -0,0 +1,812 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_N_PACKET_HPP
+#define _ZT_N_PACKET_HPP
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <string>
+#include <iostream>
+
+#include "Address.hpp"
+#include "HMAC.hpp"
+#include "Salsa20.hpp"
+#include "Utils.hpp"
+#include "Constants.hpp"
+#include "Buffer.hpp"
+
+#include "../ext/lz4/lz4.h"
+
+/**
+ * Protocol version
+ */
+#define ZT_PROTO_VERSION 1
+
+/**
+ * Maximum hop count allowed by packet structure (3 bits, 0-7)
+ *
+ * This is not necessarily the maximum hop counter after which
+ * relaying is no longer performed.
+ */
+#define ZT_PROTO_MAX_HOPS 7
+
+/**
+ * Header flag indicating that a packet is encrypted with Salsa20
+ *
+ * If this is not set, then the packet's payload is in the clear and the
+ * HMAC is over this (since there is no ciphertext). Otherwise the HMAC is
+ * of the ciphertext after encryption.
+ */
+#define ZT_PROTO_FLAG_ENCRYPTED 0x80
+
+/**
+ * Header flag indicating that a packet is fragmented
+ *
+ * If this flag is set, the receiver knows to expect more than one fragment.
+ * See Packet::Fragment for details.
+ */
+#define ZT_PROTO_FLAG_FRAGMENTED 0x40
+
+/**
+ * Verb flag indicating payload is compressed with LZ4
+ */
+#define ZT_PROTO_VERB_FLAG_COMPRESSED 0x80
+
+// Indices of fields in normal packet header -- do not change as this
+// might require both code rework and will break compatibility.
+#define ZT_PACKET_IDX_IV 0
+#define ZT_PACKET_IDX_DEST 8
+#define ZT_PACKET_IDX_SOURCE 13
+#define ZT_PACKET_IDX_FLAGS 18
+#define ZT_PACKET_IDX_HMAC 19
+#define ZT_PACKET_IDX_VERB 27
+#define ZT_PACKET_IDX_PAYLOAD 28
+
+/**
+ * ZeroTier packet buffer size
+ *
+ * This can be changed. This provides enough room for MTU-size packet
+ * payloads plus some overhead. The subtraction of sizeof(unsigned int)
+ * makes it an even multiple of 1024 (see Buffer), which might reduce
+ * memory use a little.
+ */
+#define ZT_PROTO_MAX_PACKET_LENGTH (3072 - sizeof(unsigned int))
+
+/**
+ * Minimum viable packet length (also length of header)
+ */
+#define ZT_PROTO_MIN_PACKET_LENGTH ZT_PACKET_IDX_PAYLOAD
+
+// Indexes of fields in fragment header -- also can't be changed without
+// breaking compatibility.
+#define ZT_PACKET_FRAGMENT_IDX_PACKET_ID 0
+#define ZT_PACKET_FRAGMENT_IDX_DEST 8
+#define ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR 13
+#define ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO 14
+#define ZT_PACKET_FRAGMENT_IDX_HOPS 15
+#define ZT_PACKET_FRAGMENT_IDX_PAYLOAD 16
+
+/**
+ * Value found at ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR in fragments
+ */
+#define ZT_PACKET_FRAGMENT_INDICATOR ZT_ADDRESS_RESERVED_PREFIX
+
+/**
+ * Minimum viable fragment length
+ */
+#define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD
+
+#define ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE 32
+
+// Field incides for parsing verbs
+#define ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION (ZT_PACKET_IDX_PAYLOAD)
+#define ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION (ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION + 1)
+#define ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION (ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION + 1)
+#define ZT_PROTO_VERB_HELLO_IDX_REVISION (ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION + 1)
+#define ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP (ZT_PROTO_VERB_HELLO_IDX_REVISION + 2)
+#define ZT_PROTO_VERB_HELLO_IDX_IDENTITY (ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP + 8)
+#define ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB (ZT_PACKET_IDX_PAYLOAD)
+#define ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID (ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB + 1)
+#define ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE (ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID + 8)
+#define ZT_PROTO_VERB_ERROR_IDX_PAYLOAD (ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE + 1)
+#define ZT_PROTO_VERB_OK_IDX_IN_RE_VERB (ZT_PACKET_IDX_PAYLOAD)
+#define ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID (ZT_PROTO_VERB_OK_IDX_IN_RE_VERB + 1)
+#define ZT_PROTO_VERB_OK_IDX_PAYLOAD (ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID + 8)
+#define ZT_PROTO_VERB_WHOIS_IDX_ZTADDRESS (ZT_PACKET_IDX_PAYLOAD)
+#define ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS (ZT_PACKET_IDX_PAYLOAD)
+#define ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT (ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS + 5)
+#define ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN (ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT + 2)
+#define ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS (ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN + 1)
+#define ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
+#define ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE (ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID + 8)
+#define ZT_PROTO_VERB_FRAME_IDX_PAYLOAD (ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE + 2)
+#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
+#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_MULTICAST_MAC (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID + 8)
+#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ADI (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_MULTICAST_MAC + 6)
+#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_BLOOM (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ADI + 4)
+#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_HOPS (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_BLOOM + ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE)
+#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_LOAD_FACTOR (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_HOPS + 1)
+#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FROM_MAC (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_LOAD_FACTOR + 2)
+#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FROM_MAC + 6)
+#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE + 2)
+
+// Field indices for parsing OK and ERROR payloads of replies
+#define ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP (ZT_PROTO_VERB_OK_IDX_PAYLOAD)
+#define ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY (ZT_PROTO_VERB_OK_IDX_PAYLOAD)
+#define ZT_PROTO_VERB_WHOIS__ERROR__IDX_ZTADDRESS (ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)
+
+namespace ZeroTier {
+
+/**
+ * ZeroTier packet
+ *
+ * Packet format:
+ * <[8] random initialization vector (doubles as 64-bit packet ID)>
+ * <[5] destination ZT address>
+ * <[5] source ZT address>
+ * <[1] flags (LS 5 bits) and ZT hop count (MS 3 bits)>
+ * <[8] first 8 bytes of 32-byte HMAC-SHA-256 MAC>
+ * [... -- begin encryption envelope -- ...]
+ * <[1] encrypted flags (MS 3 bits) and verb (LS 5 bits)>
+ * [... verb-specific payload ...]
+ *
+ * Packets smaller than 28 bytes are invalid and silently discarded.
+ *
+ * MAC is computed on ciphertext *after* encryption. See also:
+ *
+ * http://tonyarcieri.com/all-the-crypto-code-youve-ever-written-is-probably-broken
+ *
+ * For unencrypted packets, MAC is computed on plaintext. Only HELLO is ever
+ * sent in the clear, as it's the "here is my public key" message.
+ */
+class Packet : public Buffer<ZT_PROTO_MAX_PACKET_LENGTH>
+{
+public:
+ /**
+ * A packet fragment
+ *
+ * Fragments are sent if a packet is larger than UDP MTU. The first fragment
+ * is sent with its normal header with the fragmented flag set. Remaining
+ * fragments are sent this way.
+ *
+ * The fragmented bit indicates that there is at least one fragment. Fragments
+ * themselves contain the total, so the receiver must "learn" this from the
+ * first fragment it receives.
+ *
+ * Fragments are sent with the following format:
+ * <[8] packet ID of packet whose fragment this belongs to>
+ * <[5] destination ZT address>
+ * <[1] 0xff, a reserved address, signals that this isn't a normal packet>
+ * <[1] total fragments (most significant 4 bits), fragment no (LS 4 bits)>
+ * <[1] ZT hop count>
+ * <[...] fragment data>
+ *
+ * The protocol supports a maximum of 16 fragments. If a fragment is received
+ * before its main packet header, it should be cached for a brief period of
+ * time to see if its parent arrives. Loss of any fragment constitutes packet
+ * loss; there is no retransmission mechanism. The receiver must wait for full
+ * receipt to authenticate and decrypt; there is no per-fragment MAC. (But if
+ * fragments are corrupt, the MAC will fail for the whole assembled packet.)
+ */
+ class Fragment : public Buffer<ZT_PROTO_MAX_PACKET_LENGTH>
+ {
+ public:
+ Fragment() :
+ Buffer<ZT_PROTO_MAX_PACKET_LENGTH>()
+ {
+ }
+
+ template<unsigned int C2>
+ Fragment(const Buffer<C2> &b)
+ throw(std::out_of_range) :
+ Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
+ {
+ }
+
+ /**
+ * Initialize from a packet
+ *
+ * @param p Original assembled packet
+ * @param fragStart Start of fragment (raw index in packet data)
+ * @param fragLen Length of fragment in bytes
+ * @param fragNo Which fragment (>= 1, since 0 is Packet with end chopped off)
+ * @param fragTotal Total number of fragments (including 0)
+ * @throws std::out_of_range Packet size would exceed buffer
+ */
+ Fragment(const Packet &p,unsigned int fragStart,unsigned int fragLen,unsigned int fragNo,unsigned int fragTotal)
+ throw(std::out_of_range)
+ {
+ init(p,fragStart,fragLen,fragNo,fragTotal);
+ }
+
+ /**
+ * Initialize from a packet
+ *
+ * @param p Original assembled packet
+ * @param fragStart Start of fragment (raw index in packet data)
+ * @param fragLen Length of fragment in bytes
+ * @param fragNo Which fragment (>= 1, since 0 is Packet with end chopped off)
+ * @param fragTotal Total number of fragments (including 0)
+ * @throws std::out_of_range Packet size would exceed buffer
+ */
+ inline void init(const Packet &p,unsigned int fragStart,unsigned int fragLen,unsigned int fragNo,unsigned int fragTotal)
+ throw(std::out_of_range)
+ {
+ if ((fragStart + fragLen) > p.size())
+ throw std::out_of_range("Packet::Fragment: tried to construct fragment of packet past its length");
+ setSize(fragLen + ZT_PROTO_MIN_FRAGMENT_LENGTH);
+
+ // NOTE: this copies both the IV/packet ID and the destination address.
+ memcpy(_b + ZT_PACKET_FRAGMENT_IDX_PACKET_ID,p.data() + ZT_PACKET_IDX_IV,13);
+
+ _b[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] = ZT_PACKET_FRAGMENT_INDICATOR;
+ _b[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO] = (char)(((fragTotal & 0xf) << 4) | (fragNo & 0xf));
+ _b[ZT_PACKET_FRAGMENT_IDX_HOPS] = 0;
+
+ memcpy(_b + ZT_PACKET_FRAGMENT_IDX_PAYLOAD,p.data() + fragStart,fragLen);
+ }
+
+ /**
+ * Get this fragment's destination
+ *
+ * @return Destination ZT address
+ */
+ inline Address destination() const { return Address(_b + ZT_PACKET_FRAGMENT_IDX_DEST); }
+
+ /**
+ * @return True if fragment is of a valid length
+ */
+ inline bool lengthValid() const { return (_l >= ZT_PACKET_FRAGMENT_IDX_PAYLOAD); }
+
+ /**
+ * @return ID of packet this is a fragment of
+ */
+ inline uint64_t packetId() const { return at<uint64_t>(ZT_PACKET_FRAGMENT_IDX_PACKET_ID); }
+
+ /**
+ * @return Total number of fragments in packet
+ */
+ inline unsigned int totalFragments() const { return (((unsigned int)_b[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO] >> 4) & 0xf); }
+
+ /**
+ * @return Fragment number of this fragment
+ */
+ inline unsigned int fragmentNumber() const { return ((unsigned int)_b[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_NO] & 0xf); }
+
+ /**
+ * @return Fragment ZT hop count
+ */
+ inline unsigned int hops() const { return (unsigned int)_b[ZT_PACKET_FRAGMENT_IDX_HOPS]; }
+
+ /**
+ * Increment this packet's hop count
+ */
+ inline void incrementHops()
+ {
+ _b[ZT_PACKET_FRAGMENT_IDX_HOPS] = (_b[ZT_PACKET_FRAGMENT_IDX_HOPS] + 1) & ZT_PROTO_MAX_HOPS;
+ }
+
+ /**
+ * @return Fragment payload
+ */
+ inline unsigned char *payload() { return (unsigned char *)(_b + ZT_PACKET_FRAGMENT_IDX_PAYLOAD); }
+ inline const unsigned char *payload() const { return (const unsigned char *)(_b + ZT_PACKET_FRAGMENT_IDX_PAYLOAD); }
+
+ /**
+ * @return Length of payload in bytes
+ */
+ inline unsigned int payloadLength() const { return ((_l > ZT_PACKET_FRAGMENT_IDX_PAYLOAD) ? (_l - ZT_PACKET_FRAGMENT_IDX_PAYLOAD) : 0); }
+ };
+
+ /**
+ * ZeroTier protocol verbs
+ */
+ enum Verb /* Max value: 32 (5 bits) */
+ {
+ /* No operation, payload ignored, no reply */
+ VERB_NOP = 0,
+
+ /* Announcement of a node's existence:
+ * <[1] protocol version>
+ * <[1] software major version>
+ * <[1] software minor version>
+ * <[2] software revision>
+ * <[8] timestamp (ms since epoch)>
+ * <[...] binary serialized identity (see Identity)>
+ *
+ * OK payload:
+ * <[8] timestamp (echoed from original HELLO)>
+ *
+ * ERROR has no payload.
+ */
+ VERB_HELLO = 1,
+
+ /* Error response:
+ * <[1] in-re verb>
+ * <[8] in-re packet ID>
+ * <[1] error code>
+ * <[...] error-dependent payload>
+ */
+ VERB_ERROR = 2,
+
+ /* Success response:
+ * <[1] in-re verb>
+ * <[8] in-re packet ID>
+ * <[...] request-specific payload>
+ */
+ VERB_OK = 3,
+
+ /* Query an identity by address:
+ * <[5] address to look up>
+ *
+ * OK response payload:
+ * <[...] binary serialized identity>
+ *
+ * Error payload will be address queried.
+ */
+ VERB_WHOIS = 4,
+
+ /* Meet another node at a given protocol address:
+ * <[5] ZeroTier address of peer that might be found at this address>
+ * <[2] 16-bit protocol address port>
+ * <[1] protocol address length (4 for IPv4, 16 for IPv6)>
+ * <[...] protocol address (network byte order)>
+ *
+ * This is sent by a relaying node to initiate NAT traversal between two
+ * peers that are communicating by way of indirect relay. The relay will
+ * send this to both peers at the same time on a periodic basis, telling
+ * each where it might find the other on the network.
+ *
+ * Upon receipt, a peer sends a message such as NOP or HELLO to the other
+ * peer. Peers only "learn" one anothers' direct addresses when they
+ * successfully *receive* a message and authenticate it. Optionally, peers
+ * will usually preface these messages with one or more firewall openers
+ * to clear the path.
+ *
+ * Nodes should implement rate control, limiting the rate at which they
+ * respond to these packets to prevent their use in DDOS attacks. Nodes
+ * may also ignore these messages if a peer is not known or is not being
+ * actively communicated with.
+ *
+ * No OK or ERROR is generated.
+ */
+ VERB_RENDEZVOUS = 5,
+
+ /* A ZT-to-ZT unicast ethernet frame:
+ * <[8] 64-bit network ID>
+ * <[2] 16-bit ethertype>
+ * <[...] ethernet payload>
+ *
+ * MAC addresses are derived from the packet's source and destination
+ * ZeroTier addresses. ZeroTier does not support VLANs or other extensions
+ * beyond core Ethernet.
+ *
+ * No OK or ERROR is generated.
+ */
+ VERB_FRAME = 6,
+
+ /* A multicast frame:
+ * <[8] 64-bit network ID>
+ * <[6] destination multicast Ethernet address>
+ * <[4] multicast additional distinguishing information (ADI)>
+ * <[32] multicast propagation bloom filter>
+ * <[1] 8-bit strict propagation hop count>
+ * <[2] 16-bit average peer multicast bandwidth load>
+ * <[6] source Ethernet address>
+ * <[2] 16-bit ethertype>
+ * <[...] ethernet payload>
+ *
+ * No OK or ERROR is generated.
+ */
+ VERB_MULTICAST_FRAME = 7,
+
+ /* Announce interest in multicast group(s):
+ * <[8] 64-bit network ID>
+ * <[6] multicast Ethernet address>
+ * <[4] multicast additional distinguishing information (ADI)>
+ * [... additional tuples of network/address/adi ...]
+ *
+ * OK is generated on successful receipt.
+ */
+ VERB_MULTICAST_LIKE = 8
+ };
+
+ /**
+ * Error codes for VERB_ERROR
+ */
+ enum ErrorCode
+ {
+ /* No error, not actually used in transit */
+ ERROR_NONE = 0,
+
+ /* Invalid request */
+ ERROR_INVALID_REQUEST = 1,
+
+ /* Bad/unsupported protocol version */
+ ERROR_BAD_PROTOCOL_VERSION = 2,
+
+ /* Unknown object queried (e.g. with WHOIS) */
+ ERROR_NOT_FOUND = 3,
+
+ /* HELLO pushed an identity whose address is already claimed */
+ ERROR_IDENTITY_COLLISION = 4,
+
+ /* Identity was not valid */
+ ERROR_IDENTITY_INVALID = 5,
+
+ /* Verb or use case not supported/enabled by this node */
+ ERROR_UNSUPPORTED_OPERATION = 6
+ };
+
+ /**
+ * @param v Verb
+ * @return String representation (e.g. HELLO, OK)
+ */
+ static const char *verbString(Verb v)
+ throw();
+
+ /**
+ * @param e Error code
+ * @return String error name
+ */
+ static const char *errorString(ErrorCode e)
+ throw();
+
+ template<unsigned int C2>
+ Packet(const Buffer<C2> &b)
+ throw(std::out_of_range) :
+ Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
+ {
+ }
+
+ /**
+ * Construct a new empty packet with a unique random packet ID
+ *
+ * Flags and hops will be zero. Other fields and data region are undefined.
+ * Use the header access methods (setDestination() and friends) to fill out
+ * the header. Payload should be appended; initial size is header size.
+ */
+ Packet() :
+ Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(ZT_PROTO_MIN_PACKET_LENGTH)
+ {
+ Utils::getSecureRandom(_b + ZT_PACKET_IDX_IV,8);
+ _b[ZT_PACKET_IDX_FLAGS] = 0; // zero flags and hops
+ }
+
+ /**
+ * Construct a new empty packet with a unique random packet ID
+ *
+ * @param dest Destination ZT address
+ * @param source Source ZT address
+ * @param v Verb
+ */
+ Packet(const Address &dest,const Address &source,const Verb v) :
+ Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(ZT_PROTO_MIN_PACKET_LENGTH)
+ {
+ Utils::getSecureRandom(_b + ZT_PACKET_IDX_IV,8);
+ setDestination(dest);
+ setSource(source);
+ _b[ZT_PACKET_IDX_FLAGS] = 0; // zero flags and hops
+ setVerb(v);
+ }
+
+ /**
+ * Reset this packet structure for reuse in place
+ *
+ * @param dest Destination ZT address
+ * @param source Source ZT address
+ * @param v Verb
+ */
+ inline void reset(const Address &dest,const Address &source,const Verb v)
+ {
+ setSize(ZT_PROTO_MIN_PACKET_LENGTH);
+ Utils::getSecureRandom(_b + ZT_PACKET_IDX_IV,8);
+ setDestination(dest);
+ setSource(source);
+ _b[ZT_PACKET_IDX_FLAGS] = 0; // zero flags and hops
+ setVerb(v);
+ }
+
+ /**
+ * Set this packet's destination
+ *
+ * @param dest ZeroTier address of destination
+ */
+ inline void setDestination(const Address &dest)
+ {
+ for(unsigned int i=0;i<ZT_ADDRESS_LENGTH;++i)
+ _b[i + ZT_PACKET_IDX_DEST] = dest[i];
+ }
+
+ /**
+ * Set this packet's source
+ *
+ * @param source ZeroTier address of source
+ */
+ inline void setSource(const Address &source)
+ {
+ for(unsigned int i=0;i<ZT_ADDRESS_LENGTH;++i)
+ _b[i + ZT_PACKET_IDX_SOURCE] = source[i];
+ }
+
+ /**
+ * Get this packet's destination
+ *
+ * @return Destination ZT address
+ */
+ inline Address destination() const { return Address(_b + ZT_PACKET_IDX_DEST); }
+
+ /**
+ * Get this packet's source
+ *
+ * @return Source ZT address
+ */
+ inline Address source() const { return Address(_b + ZT_PACKET_IDX_SOURCE); }
+
+ /**
+ * @return True if packet is of valid length
+ */
+ inline bool lengthValid() const { return (_l >= ZT_PROTO_MIN_PACKET_LENGTH); }
+
+ /**
+ * @return True if packet is encrypted
+ */
+ inline bool encrypted() const { return (((unsigned char)_b[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_ENCRYPTED)); }
+
+ /**
+ * @return True if packet is fragmented (expect fragments)
+ */
+ inline bool fragmented() const { return (((unsigned char)_b[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED)); }
+
+ /**
+ * Set this packet's fragmented flag
+ *
+ * @param f Fragmented flag value
+ */
+ inline void setFragmented(bool f)
+ {
+ if (f)
+ _b[ZT_PACKET_IDX_FLAGS] |= (char)ZT_PROTO_FLAG_FRAGMENTED;
+ else _b[ZT_PACKET_IDX_FLAGS] &= (char)(~ZT_PROTO_FLAG_FRAGMENTED);
+ }
+
+ /**
+ * @return True if compressed (result only valid if unencrypted)
+ */
+ inline bool compressed() const { return (((unsigned char)_b[ZT_PACKET_IDX_VERB] & ZT_PROTO_VERB_FLAG_COMPRESSED)); }
+
+ /**
+ * @return ZeroTier forwarding hops (0 to 7)
+ */
+ inline unsigned int hops() const { return ((unsigned int)_b[ZT_PACKET_IDX_FLAGS] & 0x07); }
+
+ /**
+ * Increment this packet's hop count
+ */
+ inline void incrementHops()
+ {
+ _b[ZT_PACKET_IDX_FLAGS] = (char)((unsigned char)_b[ZT_PACKET_IDX_FLAGS] & 0xf8) | (((unsigned char)_b[ZT_PACKET_IDX_FLAGS] + 1) & 0x07);
+ }
+
+ /**
+ * Get this packet's unique ID (the IV field interpreted as uint64_t)
+ *
+ * @return Packet ID
+ */
+ inline uint64_t packetId() const { return at<uint64_t>(ZT_PACKET_IDX_IV); }
+
+ /**
+ * Set packet verb
+ *
+ * This also has the side-effect of clearing any verb flags, such as
+ * compressed, and so must only be done during packet composition.
+ *
+ * @param v New packet verb
+ */
+ inline void setVerb(Verb v) { _b[ZT_PACKET_IDX_VERB] = (char)v; }
+
+ /**
+ * @return Packet verb (not including flag bits)
+ */
+ inline Verb verb() const { return (Verb)(_b[ZT_PACKET_IDX_VERB] & 0x1f); }
+
+ /**
+ * @return Length of packet payload
+ */
+ inline unsigned int payloadLength() const throw() { return ((_l < ZT_PROTO_MIN_PACKET_LENGTH) ? 0 : (_l - ZT_PROTO_MIN_PACKET_LENGTH)); }
+
+ /**
+ * @return Packet payload
+ */
+ inline unsigned char *payload() throw() { return (unsigned char *)(_b + ZT_PACKET_IDX_PAYLOAD); }
+ inline const unsigned char *payload() const throw() { return (const unsigned char *)(_b + ZT_PACKET_IDX_PAYLOAD); }
+
+ /**
+ * Compute the HMAC of this packet's payload and set HMAC field
+ *
+ * For encrypted packets, this must be called after encryption.
+ *
+ * @param key 256-bit (32 byte) key
+ */
+ inline void hmacSet(const void *key)
+ throw()
+ {
+ unsigned char mac[32];
+ unsigned char key2[32];
+ _mangleKey((const unsigned char *)key,key2);
+ HMAC::sha256(key2,sizeof(key2),_b + ZT_PACKET_IDX_VERB,(_l >= ZT_PACKET_IDX_VERB) ? (_l - ZT_PACKET_IDX_VERB) : 0,mac);
+ memcpy(_b + ZT_PACKET_IDX_HMAC,mac,8);
+ }
+
+ /**
+ * Check the HMAC of this packet's payload
+ *
+ * For encrypted packets, this must be checked before decryption.
+ *
+ * @param key 256-bit (32 byte) key
+ */
+ inline bool hmacVerify(const void *key) const
+ throw()
+ {
+ unsigned char mac[32];
+ unsigned char key2[32];
+ if (_l < ZT_PACKET_IDX_VERB)
+ return false; // incomplete packets fail
+ _mangleKey((const unsigned char *)key,key2);
+ HMAC::sha256(key2,sizeof(key2),_b + ZT_PACKET_IDX_VERB,_l - ZT_PACKET_IDX_VERB,mac);
+ return (!memcmp(_b + ZT_PACKET_IDX_HMAC,mac,8));
+ }
+
+ /**
+ * Encrypt this packet
+ *
+ * @param key 256-bit (32 byte) key
+ */
+ inline void encrypt(const void *key)
+ throw()
+ {
+ _b[ZT_PACKET_IDX_FLAGS] |= ZT_PROTO_FLAG_ENCRYPTED;
+ unsigned char key2[32];
+ _mangleKey((const unsigned char *)key,key2);
+ Salsa20 s20(key2,256,_b + ZT_PACKET_IDX_IV);
+ s20.encrypt(_b + ZT_PACKET_IDX_VERB,_b + ZT_PACKET_IDX_VERB,(_l >= ZT_PACKET_IDX_VERB) ? (_l - ZT_PACKET_IDX_VERB) : 0);
+ }
+
+ /**
+ * Decrypt this packet
+ *
+ * @param key 256-bit (32 byte) key
+ */
+ inline void decrypt(const void *key)
+ throw()
+ {
+ unsigned char key2[32];
+ _mangleKey((const unsigned char *)key,key2);
+ Salsa20 s20(key2,256,_b + ZT_PACKET_IDX_IV);
+ s20.decrypt(_b + ZT_PACKET_IDX_VERB,_b + ZT_PACKET_IDX_VERB,(_l >= ZT_PACKET_IDX_VERB) ? (_l - ZT_PACKET_IDX_VERB) : 0);
+ _b[ZT_PACKET_IDX_FLAGS] &= (char)(~ZT_PROTO_FLAG_ENCRYPTED);
+ }
+
+ /**
+ * Attempt to compress payload if not already (must be unencrypted)
+ *
+ * This requires that the payload at least contain the verb byte already
+ * set. The compressed flag in the verb is set if compression successfully
+ * results in a size reduction. If no size reduction occurs, compression
+ * is not done and the flag is left cleared.
+ *
+ * @return True if compression occurred
+ */
+ inline bool compress()
+ throw()
+ {
+ unsigned char buf[ZT_PROTO_MAX_PACKET_LENGTH * 2];
+ if ((!compressed())&&(_l > (ZT_PACKET_IDX_PAYLOAD + 32))) {
+ int pl = (int)(_l - ZT_PACKET_IDX_PAYLOAD);
+ int cl = LZ4_compress((const char *)(_b + ZT_PACKET_IDX_PAYLOAD),(char *)buf,pl);
+ if ((cl > 0)&&(cl < pl)) {
+ _b[ZT_PACKET_IDX_VERB] |= (char)ZT_PROTO_VERB_FLAG_COMPRESSED;
+ memcpy(_b + ZT_PACKET_IDX_PAYLOAD,buf,cl);
+ _l = (unsigned int)cl + ZT_PACKET_IDX_PAYLOAD;
+ return true;
+ }
+ }
+ _b[ZT_PACKET_IDX_VERB] &= (char)(~ZT_PROTO_VERB_FLAG_COMPRESSED);
+ return false;
+ }
+
+ /**
+ * Attempt to decompress payload if it is compressed (must be unencrypted)
+ *
+ * If payload is compressed, it is decompressed and the compressed verb
+ * flag is cleared. Otherwise nothing is done and true is returned.
+ *
+ * @return True if data is now decompressed and valid, false on error
+ */
+ inline bool uncompress()
+ throw()
+ {
+ unsigned char buf[ZT_PROTO_MAX_PACKET_LENGTH];
+ if ((compressed())&&(_l >= ZT_PROTO_MIN_PACKET_LENGTH)) {
+ if (_l > ZT_PACKET_IDX_PAYLOAD) {
+ int ucl = LZ4_uncompress_unknownOutputSize((const char *)(_b + ZT_PACKET_IDX_PAYLOAD),(char *)buf,_l - ZT_PACKET_IDX_PAYLOAD,sizeof(buf));
+ if ((ucl > 0)&&(ucl <= (int)(capacity() - ZT_PACKET_IDX_PAYLOAD))) {
+ memcpy(_b + ZT_PACKET_IDX_PAYLOAD,buf,ucl);
+ _l = (unsigned int)ucl + ZT_PACKET_IDX_PAYLOAD;
+ } else return false;
+ }
+ _b[ZT_PACKET_IDX_VERB] &= ~ZT_PROTO_VERB_FLAG_COMPRESSED;
+ }
+ return true;
+ }
+
+private:
+ /**
+ * Deterministically mangle a 256-bit crypto key based on packet characteristics
+ *
+ * This takes the static agreed-upon input key and mangles it using
+ * info from the packet. This serves two purposes:
+ *
+ * (1) It reduces the (already minute) probability of a duplicate key /
+ * IV combo, which is good since keys are extremely long-lived. Another
+ * way of saying this is that it increases the effective IV size by
+ * using other parts of the packet as IV material.
+ * (2) It causes HMAC to fail should any of the following change: ordering
+ * of source and dest addresses, flags, IV, or packet size. HMAC has
+ * no explicit scheme for AAD (additional authenticated data).
+ *
+ * NOTE: this function will have to be changed if the order of any packet
+ * fields or their sizes/padding changes in the spec.
+ *
+ * @param in Input key (32 bytes)
+ * @param out Output buffer (32 bytes)
+ */
+ inline void _mangleKey(const unsigned char *in,unsigned char *out) const
+ throw()
+ {
+ // Random IV (Salsa20 also uses the IV natively, but HMAC doesn't), and
+ // destination and source addresses. Using dest and source addresses
+ // gives us a (likely) different key space for a->b vs b->a.
+ for(unsigned int i=0;i<18;++i) // 8 + (ZT_ADDRESS_LENGTH * 2) == 18
+ out[i] = in[i] ^ (unsigned char)_b[i];
+ // Flags, but masking off hop count which is altered by forwarding nodes
+ out[18] = in[18] ^ ((unsigned char)_b[ZT_PACKET_IDX_FLAGS] & 0xf8);
+ // Raw packet size in bytes -- each raw packet size defines a possibly
+ // different space of keys.
+ out[19] = in[19] ^ (unsigned char)(_l & 0xff);
+ out[20] = in[20] ^ (unsigned char)((_l >> 8) & 0xff); // little endian
+ // Rest of raw key is used unchanged
+ for(unsigned int i=21;i<32;++i)
+ out[i] = in[i];
+ }
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Peer.cpp b/node/Peer.cpp
new file mode 100644
index 00000000..09732947
--- /dev/null
+++ b/node/Peer.cpp
@@ -0,0 +1,141 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include "Peer.hpp"
+
+namespace ZeroTier {
+
+Peer::Peer() :
+ _dirty(false)
+{
+}
+
+Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
+ throw(std::runtime_error) :
+ _id(peerIdentity),
+ _dirty(true)
+{
+ if (!myIdentity.agree(peerIdentity,_keys,sizeof(_keys)))
+ throw std::runtime_error("new peer identity key agreement failed");
+}
+
+void Peer::onReceive(const RuntimeEnvironment *_r,Demarc::Port localPort,const InetAddress &fromAddr,unsigned int latency,unsigned int hops,Packet::Verb verb,uint64_t now)
+{
+ if (!hops) { // direct packet
+ WanPath *wp = (fromAddr.isV4() ? &_ipv4p : &_ipv6p);
+
+ wp->lastReceive = now;
+ if (verb == Packet::VERB_FRAME)
+ wp->lastUnicastFrame = now;
+ if (latency)
+ wp->latency = latency;
+ wp->localPort = localPort;
+ if (!wp->fixed)
+ wp->addr = fromAddr;
+
+ _dirty = true;
+ }
+}
+
+bool Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,bool relay,Packet::Verb verb,uint64_t now)
+{
+ if ((_ipv6p.isActive(now))||((!(_ipv4p.addr))&&(_ipv6p.addr))) {
+ if (_r->demarc->send(_ipv6p.localPort,_ipv6p.addr,data,len,-1)) {
+ _ipv6p.lastSend = now;
+ if (verb == Packet::VERB_FRAME)
+ _ipv6p.lastUnicastFrame = now;
+ _dirty = true;
+ return true;
+ }
+ }
+
+ if (_ipv4p.addr) {
+ if (_r->demarc->send(_ipv4p.localPort,_ipv4p.addr,data,len,-1)) {
+ _ipv4p.lastSend = now;
+ if (verb == Packet::VERB_FRAME)
+ _ipv4p.lastUnicastFrame = now;
+ _dirty = true;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Peer::sendFirewallOpener(const RuntimeEnvironment *_r,uint64_t now)
+{
+ bool sent = false;
+ if (_ipv4p.addr) {
+ if (_r->demarc->send(_ipv4p.localPort,_ipv4p.addr,"\0",1,ZT_FIREWALL_OPENER_HOPS)) {
+ _ipv4p.lastFirewallOpener = now;
+ _dirty = true;
+ sent = true;
+ }
+ }
+ if (_ipv6p.addr) {
+ if (_r->demarc->send(_ipv6p.localPort,_ipv6p.addr,"\0",1,ZT_FIREWALL_OPENER_HOPS)) {
+ _ipv6p.lastFirewallOpener = now;
+ _dirty = true;
+ sent = true;
+ }
+ }
+ return sent;
+}
+
+void Peer::setPathAddress(const InetAddress &addr,bool fixed)
+{
+ if (addr.isV4()) {
+ _ipv4p.addr = addr;
+ _ipv4p.fixed = fixed;
+ _dirty = true;
+ } else if (addr.isV6()) {
+ _ipv6p.addr = addr;
+ _ipv6p.fixed = fixed;
+ _dirty = true;
+ }
+}
+
+void Peer::clearFixedFlag(InetAddress::AddressType t)
+{
+ switch(t) {
+ case InetAddress::TYPE_NULL:
+ _ipv4p.fixed = false;
+ _ipv6p.fixed = false;
+ _dirty = true;
+ break;
+ case InetAddress::TYPE_IPV4:
+ _ipv4p.fixed = false;
+ _dirty = true;
+ break;
+ case InetAddress::TYPE_IPV6:
+ _ipv6p.fixed = false;
+ _dirty = true;
+ break;
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/Peer.hpp b/node/Peer.hpp
new file mode 100644
index 00000000..5da19468
--- /dev/null
+++ b/node/Peer.hpp
@@ -0,0 +1,435 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_PEER_HPP
+#define _ZT_PEER_HPP
+
+#include <algorithm>
+#include <utility>
+#include <stdexcept>
+#include <stdint.h>
+#include "Address.hpp"
+#include "Utils.hpp"
+#include "Identity.hpp"
+#include "Constants.hpp"
+#include "Logger.hpp"
+#include "Demarc.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "InetAddress.hpp"
+#include "EllipticCurveKey.hpp"
+#include "Packet.hpp"
+#include "SharedPtr.hpp"
+#include "AtomicCounter.hpp"
+#include "NonCopyable.hpp"
+#include "Mutex.hpp"
+
+/**
+ * Max length of serialized peer record
+ */
+#define ZT_PEER_MAX_SERIALIZED_LENGTH ( \
+ 64 + \
+ IDENTITY_MAX_BINARY_SERIALIZED_LENGTH + \
+ (( \
+ (sizeof(uint64_t) * 5) + \
+ sizeof(uint16_t) + \
+ 1 + \
+ sizeof(uint16_t) + \
+ 16 + \
+ 1 \
+ ) * 2) + \
+ 64 \
+)
+
+namespace ZeroTier {
+
+/**
+ * A peer on the network
+ *
+ * Threading note:
+ *
+ * This structure contains no locks at the moment, but also performs no
+ * memory allocation or pointer manipulation. As a result is is technically
+ * "safe" for threads, as in won't crash. Right now it's only changed from
+ * the core I/O thread so this isn't an issue. If multiple I/O threads are
+ * introduced it ought to have a lock of some kind.
+ */
+class Peer : NonCopyable
+{
+ friend class SharedPtr<Peer>;
+
+private:
+ ~Peer() {}
+
+public:
+ Peer();
+
+ /**
+ * Construct a new peer
+ *
+ * @param myIdentity Identity of THIS node (for key agreement)
+ * @param peerIdentity Identity of peer
+ * @throws std::runtime_error Key agreement with peer's identity failed
+ */
+ Peer(const Identity &myIdentity,const Identity &peerIdentity)
+ throw(std::runtime_error);
+
+ /**
+ * @return This peer's ZT address (short for identity().address())
+ */
+ inline const Address &address() const throw() { return _id.address(); }
+
+ /**
+ * @return This peer's identity
+ */
+ inline const Identity &identity() const throw() { return _id; }
+
+ /**
+ * Must be called on authenticated packet receive from this peer
+ *
+ * @param _r Runtime environment
+ * @param localPort Local port on which packet was received
+ * @param fromAddr Internet address of sender
+ * @param latency Latency or 0 if unknown
+ * @param hops ZeroTier (not IP) hops
+ * @param verb Packet verb
+ * @param now Current time
+ */
+ void onReceive(const RuntimeEnvironment *_r,Demarc::Port localPort,const InetAddress &fromAddr,unsigned int latency,unsigned int hops,Packet::Verb verb,uint64_t now);
+
+ /**
+ * Send a UDP packet to this peer
+ *
+ * If the active link is timed out (no receives for ping timeout ms), then
+ * the active link number is incremented after send. This causes sends to
+ * cycle through links if there is no clear active link. This also happens
+ * if the send fails for some reason.
+ *
+ * @param _r Runtime environment
+ * @param data Data to send
+ * @param len Length of packet
+ * @param relay This is a relay on behalf of another peer (verb is ignored)
+ * @param verb Packet verb (if not relay)
+ * @param now Current time
+ * @return True if packet appears to have been sent, false on local failure
+ */
+ bool send(const RuntimeEnvironment *_r,const void *data,unsigned int len,bool relay,Packet::Verb verb,uint64_t now);
+
+ /**
+ * Send firewall opener to active link
+ *
+ * @param _r Runtime environment
+ * @param now Current time
+ * @return True if send appears successful for at least one address type
+ */
+ bool sendFirewallOpener(const RuntimeEnvironment *_r,uint64_t now);
+
+ /**
+ * Set an address to reach this peer
+ *
+ * @param addr Address to set
+ * @param fixed If true, address is fixed (won't be changed on packet receipt)
+ */
+ void setPathAddress(const InetAddress &addr,bool fixed);
+
+ /**
+ * Clear the fixed flag for an address type
+ *
+ * @param t Type to clear, or TYPE_NULL to clear flag on all types
+ */
+ void clearFixedFlag(InetAddress::AddressType t);
+
+ /**
+ * @return Last successfully sent firewall opener
+ */
+ uint64_t lastFirewallOpener() const
+ throw()
+ {
+ return std::max(_ipv4p.lastFirewallOpener,_ipv6p.lastFirewallOpener);
+ }
+
+ /**
+ * @return Time of last direct packet receive
+ */
+ uint64_t lastDirectReceive() const
+ throw()
+ {
+ return std::max(_ipv4p.lastReceive,_ipv6p.lastReceive);
+ }
+
+ /**
+ * @return Time of last direct packet send
+ */
+ uint64_t lastDirectSend() const
+ throw()
+ {
+ return std::max(_ipv4p.lastSend,_ipv6p.lastSend);
+ }
+
+ /**
+ * @return Time of most recent unicast frame (actual data transferred)
+ */
+ uint64_t lastUnicastFrame() const
+ throw()
+ {
+ return std::max(_ipv4p.lastUnicastFrame,_ipv6p.lastUnicastFrame);
+ }
+
+ /**
+ * @return Lowest of measured latencies of all paths or 0 if unknown
+ */
+ unsigned int latency() const
+ throw()
+ {
+ if (_ipv4p.latency) {
+ if (_ipv6p.latency)
+ return std::min(_ipv4p.latency,_ipv6p.latency);
+ else return _ipv4p.latency;
+ } else if (_ipv6p.latency)
+ return _ipv6p.latency;
+ return 0;
+ }
+
+ /**
+ * @return True if this peer has at least one direct IP address path
+ */
+ inline bool hasDirectPath() const
+ throw()
+ {
+ return ((_ipv4p.addr)||(_ipv6p.addr));
+ }
+
+ /**
+ * @param now Current time
+ * @return True if hasDirectPath() is true and at least one path is active
+ */
+ inline bool hasActiveDirectPath(uint64_t now) const
+ throw()
+ {
+ return ((_ipv4p.isActive(now))||(_ipv6p.isActive(now)));
+ }
+
+ /**
+ * @return 256-bit encryption key
+ */
+ inline const unsigned char *cryptKey() const
+ throw()
+ {
+ return _keys; // crypt key is first 32-byte key
+ }
+
+ /**
+ * @return 256-bit MAC (message authentication code) key
+ */
+ inline const unsigned char *macKey() const
+ throw()
+ {
+ return (_keys + 32); // mac key is second 32-byte key
+ }
+
+ /**
+ * Get and reset dirty flag
+ *
+ * @return Previous value of dirty flag before reset
+ */
+ inline bool getAndResetDirty()
+ throw()
+ {
+ bool d = _dirty;
+ _dirty = false;
+ return d;
+ }
+
+ /**
+ * @return Current value of dirty flag
+ */
+ inline bool dirty() const throw() { return _dirty; }
+
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b)
+ throw(std::out_of_range)
+ {
+ b.append((unsigned char)1); // version
+ b.append(_keys,sizeof(_keys));
+ _id.serialize(b,false);
+ _ipv4p.serialize(b);
+ _ipv6p.serialize(b);
+ }
+
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ throw(std::out_of_range,std::invalid_argument)
+ {
+ unsigned int p = startAt;
+
+ if (b[p++] != 1)
+ throw std::invalid_argument("Peer: deserialize(): version mismatch");
+
+ memcpy(_keys,b.field(p,sizeof(_keys)),sizeof(_keys)); p += sizeof(_keys);
+ p += _id.deserialize(b,p);
+ p += _ipv4p.deserialize(b,p);
+ p += _ipv6p.deserialize(b,p);
+
+ _dirty = false;
+
+ return (p - startAt);
+ }
+
+ /**
+ * @return True if this Peer is initialized with something
+ */
+ inline operator bool() const throw() { return (_id); }
+
+ /**
+ * Find a common set of addresses by which two peers can link, if any
+ *
+ * @param a Peer A
+ * @param b Peer B
+ * @param now Current time
+ * @return Pair: B's address to send to A, A's address to send to B
+ */
+ static inline std::pair<InetAddress,InetAddress> findCommonGround(const Peer &a,const Peer &b,uint64_t now)
+ throw()
+ {
+ if ((a._ipv6p.isActive(now))&&(b._ipv6p.isActive(now)))
+ return std::pair<InetAddress,InetAddress>(b._ipv6p.addr,a._ipv6p.addr);
+ else if ((a._ipv4p.isActive(now))&&(b._ipv4p.isActive(now)))
+ return std::pair<InetAddress,InetAddress>(b._ipv4p.addr,a._ipv4p.addr);
+ else if ((a._ipv6p.addr)&&(b._ipv6p.addr))
+ return std::pair<InetAddress,InetAddress>(b._ipv6p.addr,a._ipv6p.addr);
+ else if ((a._ipv4p.addr)&&(b._ipv4p.addr))
+ return std::pair<InetAddress,InetAddress>(b._ipv4p.addr,a._ipv4p.addr);
+ return std::pair<InetAddress,InetAddress>();
+ }
+
+private:
+ class WanPath
+ {
+ public:
+ WanPath() :
+ lastSend(0),
+ lastReceive(0),
+ lastUnicastFrame(0),
+ lastFirewallOpener(0),
+ localPort(Demarc::ANY_PORT),
+ latency(0),
+ addr(),
+ fixed(false)
+ {
+ }
+
+ inline bool isActive(const uint64_t now) const
+ throw()
+ {
+ return ((addr)&&((now - lastReceive) < ZT_PEER_LINK_ACTIVITY_TIMEOUT));
+ }
+
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b)
+ throw(std::out_of_range)
+ {
+ b.append(lastSend);
+ b.append(lastReceive);
+ b.append(lastUnicastFrame);
+ b.append(lastFirewallOpener);
+ b.append(Demarc::portToInt(localPort));
+ b.append((uint16_t)latency);
+
+ b.append((unsigned char)addr.type());
+ switch(addr.type()) {
+ case InetAddress::TYPE_NULL:
+ break;
+ case InetAddress::TYPE_IPV4:
+ b.append(addr.rawIpData(),4);
+ b.append((uint16_t)addr.port());
+ break;
+ case InetAddress::TYPE_IPV6:
+ b.append(addr.rawIpData(),16);
+ b.append((uint16_t)addr.port());
+ break;
+ }
+
+ b.append(fixed ? (unsigned char)1 : (unsigned char)0);
+ }
+
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ throw(std::out_of_range,std::invalid_argument)
+ {
+ unsigned int p = startAt;
+
+ lastSend = b.template at<uint64_t>(p); p += sizeof(uint64_t);
+ lastReceive = b.template at<uint64_t>(p); p += sizeof(uint64_t);
+ lastUnicastFrame = b.template at<uint64_t>(p); p += sizeof(uint64_t);
+ lastFirewallOpener = b.template at<uint64_t>(p); p += sizeof(uint64_t);
+ localPort = Demarc::intToPort(b.template at<uint64_t>(p)); p += sizeof(uint64_t);
+ latency = b.template at<uint16_t>(p); p += sizeof(uint16_t);
+
+ switch ((InetAddress::AddressType)b[p++]) {
+ case InetAddress::TYPE_NULL:
+ addr.zero();
+ break;
+ case InetAddress::TYPE_IPV4:
+ addr.set(b.field(p,4),4,b.template at<uint16_t>(p + 4));
+ p += 4 + sizeof(uint16_t);
+ break;
+ case InetAddress::TYPE_IPV6:
+ addr.set(b.field(p,16),16,b.template at<uint16_t>(p + 16));
+ p += 16 + sizeof(uint16_t);
+ break;
+ }
+
+ fixed = (b[p++] != 0);
+
+ return (p - startAt);
+ }
+
+ uint64_t lastSend;
+ uint64_t lastReceive;
+ uint64_t lastUnicastFrame;
+ uint64_t lastFirewallOpener;
+ Demarc::Port localPort; // ANY_PORT if not defined
+ unsigned int latency; // 0 if never determined
+ InetAddress addr; // null InetAddress if path is undefined
+ bool fixed; // do not learn address from received packets
+ };
+
+ unsigned char _keys[32 * 2]; // crypt key[32], mac key[32]
+ Identity _id;
+
+ WanPath _ipv4p;
+ WanPath _ipv6p;
+
+ // Fields below this line are not persisted with serialize()
+
+ bool _dirty;
+
+ AtomicCounter __refCount;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp
new file mode 100644
index 00000000..a253af6e
--- /dev/null
+++ b/node/RuntimeEnvironment.hpp
@@ -0,0 +1,87 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_RUNTIMEENVIRONMENT_HPP
+#define _ZT_RUNTIMEENVIRONMENT_HPP
+
+#include <string>
+#include "Identity.hpp"
+
+namespace ZeroTier {
+
+class NodeConfig;
+class Logger;
+class Demarc;
+class Switch;
+class Topology;
+class SysEnv;
+
+/**
+ * Holds global state for an instance of ZeroTier::Node
+ *
+ * I do not believe in mutable static variables, period, or in global static
+ * instances of objects that don't basically represent constants. It makes
+ * unit testing, embedding, threading, and other things hard and is poor
+ * practice.
+ *
+ * So we put everything that we would want to be global, like Logger, here
+ * and we give everybody this as _r. The Node creates and initializes this
+ * on startup and deletes things on shutdown.
+ */
+class RuntimeEnvironment
+{
+public:
+ RuntimeEnvironment() :
+ identity(),
+ log((Logger *)0),
+ nc((NodeConfig *)0),
+ demarc((Demarc *)0),
+ sw((Switch *)0),
+ topology((Topology *)0)
+ {
+ }
+
+ std::string homePath;
+ std::string autoconfUrlPrefix;
+ std::string configAuthorityIdentityStr;
+ std::string ownershipVerificationSecret;
+ std::string ownershipVerificationSecretHash; // base64 of SHA-256 X16 rounds
+
+ Identity configAuthority;
+ Identity identity;
+
+ Logger *log; // may be null
+ NodeConfig *nc;
+ Demarc *demarc;
+ Switch *sw;
+ Topology *topology;
+ SysEnv *sysEnv;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Salsa20.cpp b/node/Salsa20.cpp
new file mode 100644
index 00000000..802f82e9
--- /dev/null
+++ b/node/Salsa20.cpp
@@ -0,0 +1,221 @@
+/*
+ * Based on public domain code available at: http://cr.yp.to/snuffle.html
+ *
+ * This therefore is public domain.
+ */
+
+#include "Salsa20.hpp"
+
+#define ROTATE(v,c) (((v) << (c)) | ((v) >> (32 - (c))))
+#define XOR(v,w) ((v) ^ (w))
+#define PLUS(v,w) ((uint32_t)((v) + (w)))
+#define PLUSONE(v) ((uint32_t)((v) + 1))
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define U8TO32_LITTLE(p) (*((const uint32_t *)((const void *)(p))))
+#define U32TO8_LITTLE(c,v) *((uint32_t *)((void *)(c))) = (v)
+#else
+#ifdef __GNUC__
+#define U8TO32_LITTLE(p) __builtin_bswap32(*((const uint32_t *)((const void *)(p))))
+#define U32TO8_LITTLE(c,v) *((uint32_t *)((void *)(c))) = __builtin_bswap32((v))
+#else
+error need be;
+#endif
+#endif
+
+namespace ZeroTier {
+
+static const char *sigma = "expand 32-byte k";
+static const char *tau = "expand 16-byte k";
+
+void Salsa20::init(const void *key,unsigned int kbits,const void *iv)
+ throw()
+{
+ const char *constants;
+ const uint8_t *k = (const uint8_t *)key;
+
+ _state[1] = U8TO32_LITTLE(k + 0);
+ _state[2] = U8TO32_LITTLE(k + 4);
+ _state[3] = U8TO32_LITTLE(k + 8);
+ _state[4] = U8TO32_LITTLE(k + 12);
+ if (kbits == 256) { /* recommended */
+ k += 16;
+ constants = sigma;
+ } else { /* kbits == 128 */
+ constants = tau;
+ }
+ _state[11] = U8TO32_LITTLE(k + 0);
+ _state[12] = U8TO32_LITTLE(k + 4);
+ _state[13] = U8TO32_LITTLE(k + 8);
+ _state[14] = U8TO32_LITTLE(k + 12);
+
+ _state[6] = U8TO32_LITTLE(((const uint8_t *)iv) + 0);
+ _state[7] = U8TO32_LITTLE(((const uint8_t *)iv) + 4);
+ _state[8] = 0;
+ _state[9] = 0;
+
+ _state[0] = U8TO32_LITTLE(constants + 0);
+ _state[5] = U8TO32_LITTLE(constants + 4);
+ _state[10] = U8TO32_LITTLE(constants + 8);
+ _state[15] = U8TO32_LITTLE(constants + 12);
+}
+
+void Salsa20::encrypt(const void *in,void *out,unsigned int bytes)
+ throw()
+{
+ uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15;
+ uint32_t j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15;
+ uint8_t tmp[64];
+ const uint8_t *m = (const uint8_t *)in;
+ uint8_t *c = (uint8_t *)out;
+ uint8_t *ctarget = c;
+ unsigned int i;
+
+ if (!bytes) return;
+
+ j0 = _state[0];
+ j1 = _state[1];
+ j2 = _state[2];
+ j3 = _state[3];
+ j4 = _state[4];
+ j5 = _state[5];
+ j6 = _state[6];
+ j7 = _state[7];
+ j8 = _state[8];
+ j9 = _state[9];
+ j10 = _state[10];
+ j11 = _state[11];
+ j12 = _state[12];
+ j13 = _state[13];
+ j14 = _state[14];
+ j15 = _state[15];
+
+ for (;;) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) tmp[i] = m[i];
+ m = tmp;
+ ctarget = c;
+ c = tmp;
+ }
+ x0 = j0;
+ x1 = j1;
+ x2 = j2;
+ x3 = j3;
+ x4 = j4;
+ x5 = j5;
+ x6 = j6;
+ x7 = j7;
+ x8 = j8;
+ x9 = j9;
+ x10 = j10;
+ x11 = j11;
+ x12 = j12;
+ x13 = j13;
+ x14 = j14;
+ x15 = j15;
+ for (i = 20;i > 0;i -= 2) {
+ x4 = XOR( x4,ROTATE(PLUS( x0,x12), 7));
+ x8 = XOR( x8,ROTATE(PLUS( x4, x0), 9));
+ x12 = XOR(x12,ROTATE(PLUS( x8, x4),13));
+ x0 = XOR( x0,ROTATE(PLUS(x12, x8),18));
+ x9 = XOR( x9,ROTATE(PLUS( x5, x1), 7));
+ x13 = XOR(x13,ROTATE(PLUS( x9, x5), 9));
+ x1 = XOR( x1,ROTATE(PLUS(x13, x9),13));
+ x5 = XOR( x5,ROTATE(PLUS( x1,x13),18));
+ x14 = XOR(x14,ROTATE(PLUS(x10, x6), 7));
+ x2 = XOR( x2,ROTATE(PLUS(x14,x10), 9));
+ x6 = XOR( x6,ROTATE(PLUS( x2,x14),13));
+ x10 = XOR(x10,ROTATE(PLUS( x6, x2),18));
+ x3 = XOR( x3,ROTATE(PLUS(x15,x11), 7));
+ x7 = XOR( x7,ROTATE(PLUS( x3,x15), 9));
+ x11 = XOR(x11,ROTATE(PLUS( x7, x3),13));
+ x15 = XOR(x15,ROTATE(PLUS(x11, x7),18));
+ x1 = XOR( x1,ROTATE(PLUS( x0, x3), 7));
+ x2 = XOR( x2,ROTATE(PLUS( x1, x0), 9));
+ x3 = XOR( x3,ROTATE(PLUS( x2, x1),13));
+ x0 = XOR( x0,ROTATE(PLUS( x3, x2),18));
+ x6 = XOR( x6,ROTATE(PLUS( x5, x4), 7));
+ x7 = XOR( x7,ROTATE(PLUS( x6, x5), 9));
+ x4 = XOR( x4,ROTATE(PLUS( x7, x6),13));
+ x5 = XOR( x5,ROTATE(PLUS( x4, x7),18));
+ x11 = XOR(x11,ROTATE(PLUS(x10, x9), 7));
+ x8 = XOR( x8,ROTATE(PLUS(x11,x10), 9));
+ x9 = XOR( x9,ROTATE(PLUS( x8,x11),13));
+ x10 = XOR(x10,ROTATE(PLUS( x9, x8),18));
+ x12 = XOR(x12,ROTATE(PLUS(x15,x14), 7));
+ x13 = XOR(x13,ROTATE(PLUS(x12,x15), 9));
+ x14 = XOR(x14,ROTATE(PLUS(x13,x12),13));
+ x15 = XOR(x15,ROTATE(PLUS(x14,x13),18));
+ }
+ x0 = PLUS(x0,j0);
+ x1 = PLUS(x1,j1);
+ x2 = PLUS(x2,j2);
+ x3 = PLUS(x3,j3);
+ x4 = PLUS(x4,j4);
+ x5 = PLUS(x5,j5);
+ x6 = PLUS(x6,j6);
+ x7 = PLUS(x7,j7);
+ x8 = PLUS(x8,j8);
+ x9 = PLUS(x9,j9);
+ x10 = PLUS(x10,j10);
+ x11 = PLUS(x11,j11);
+ x12 = PLUS(x12,j12);
+ x13 = PLUS(x13,j13);
+ x14 = PLUS(x14,j14);
+ x15 = PLUS(x15,j15);
+
+ x0 = XOR(x0,U8TO32_LITTLE(m + 0));
+ x1 = XOR(x1,U8TO32_LITTLE(m + 4));
+ x2 = XOR(x2,U8TO32_LITTLE(m + 8));
+ x3 = XOR(x3,U8TO32_LITTLE(m + 12));
+ x4 = XOR(x4,U8TO32_LITTLE(m + 16));
+ x5 = XOR(x5,U8TO32_LITTLE(m + 20));
+ x6 = XOR(x6,U8TO32_LITTLE(m + 24));
+ x7 = XOR(x7,U8TO32_LITTLE(m + 28));
+ x8 = XOR(x8,U8TO32_LITTLE(m + 32));
+ x9 = XOR(x9,U8TO32_LITTLE(m + 36));
+ x10 = XOR(x10,U8TO32_LITTLE(m + 40));
+ x11 = XOR(x11,U8TO32_LITTLE(m + 44));
+ x12 = XOR(x12,U8TO32_LITTLE(m + 48));
+ x13 = XOR(x13,U8TO32_LITTLE(m + 52));
+ x14 = XOR(x14,U8TO32_LITTLE(m + 56));
+ x15 = XOR(x15,U8TO32_LITTLE(m + 60));
+
+ j8 = PLUSONE(j8);
+ if (!j8) {
+ j9 = PLUSONE(j9);
+ /* stopping at 2^70 bytes per nonce is user's responsibility */
+ }
+
+ U32TO8_LITTLE(c + 0,x0);
+ U32TO8_LITTLE(c + 4,x1);
+ U32TO8_LITTLE(c + 8,x2);
+ U32TO8_LITTLE(c + 12,x3);
+ U32TO8_LITTLE(c + 16,x4);
+ U32TO8_LITTLE(c + 20,x5);
+ U32TO8_LITTLE(c + 24,x6);
+ U32TO8_LITTLE(c + 28,x7);
+ U32TO8_LITTLE(c + 32,x8);
+ U32TO8_LITTLE(c + 36,x9);
+ U32TO8_LITTLE(c + 40,x10);
+ U32TO8_LITTLE(c + 44,x11);
+ U32TO8_LITTLE(c + 48,x12);
+ U32TO8_LITTLE(c + 52,x13);
+ U32TO8_LITTLE(c + 56,x14);
+ U32TO8_LITTLE(c + 60,x15);
+
+ if (bytes <= 64) {
+ if (bytes < 64) {
+ for (i = 0;i < bytes;++i) ctarget[i] = c[i];
+ }
+ _state[8] = j8;
+ _state[9] = j9;
+ return;
+ }
+ bytes -= 64;
+ c += 64;
+ m += 64;
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/Salsa20.hpp b/node/Salsa20.hpp
new file mode 100644
index 00000000..488d8754
--- /dev/null
+++ b/node/Salsa20.hpp
@@ -0,0 +1,73 @@
+/*
+ * Based on public domain code available at: http://cr.yp.to/snuffle.html
+ *
+ * This therefore is public domain.
+ */
+
+#ifndef _ZT_SALSA20_HPP
+#define _ZT_SALSA20_HPP
+
+#include <stdint.h>
+#include "Constants.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Salsa20/20 stream cipher
+ */
+class Salsa20
+{
+public:
+ Salsa20() throw() {}
+
+ /**
+ * @param key Key bits
+ * @param kbits Number of key bits: 128 or 256 (recommended)
+ * @param iv 64-bit initialization vector
+ */
+ Salsa20(const void *key,unsigned int kbits,const void *iv)
+ throw()
+ {
+ init(key,kbits,iv);
+ }
+
+ /**
+ * Initialize cipher
+ *
+ * @param key Key bits
+ * @param kbits Number of key bits: 128 or 256 (recommended)
+ * @param iv 64-bit initialization vector
+ */
+ void init(const void *key,unsigned int kbits,const void *iv)
+ throw();
+
+ /**
+ * Encrypt data
+ *
+ * @param in Input data
+ * @param out Output buffer
+ * @param bytes Length of data
+ */
+ void encrypt(const void *in,void *out,unsigned int bytes)
+ throw();
+
+ /**
+ * Decrypt data
+ *
+ * @param in Input data
+ * @param out Output buffer
+ * @param bytes Length of data
+ */
+ inline void decrypt(const void *in,void *out,unsigned int bytes)
+ throw()
+ {
+ encrypt(in,out,bytes);
+ }
+
+private:
+ uint32_t _state[16];
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/SharedPtr.hpp b/node/SharedPtr.hpp
new file mode 100644
index 00000000..014e34fa
--- /dev/null
+++ b/node/SharedPtr.hpp
@@ -0,0 +1,133 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_SHAREDPTR_HPP
+#define _ZT_SHAREDPTR_HPP
+
+#include "Mutex.hpp"
+#include "AtomicCounter.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Simple reference counted pointer
+ *
+ * This is an introspective shared pointer. Classes that need to be reference
+ * counted must list this as a 'friend' and must have a private instance of
+ * AtomicCounter called __refCount. They should also have private destructors,
+ * since only this class should delete them.
+ *
+ * Because this is introspective, it is safe to apply to a naked pointer
+ * multiple times provided there is always at least one holding SharedPtr.
+ */
+template<typename T>
+class SharedPtr
+{
+public:
+ SharedPtr()
+ throw() :
+ _ptr((T *)0)
+ {
+ }
+
+ SharedPtr(T *obj)
+ throw() :
+ _ptr(obj)
+ {
+ ++obj->__refCount;
+ }
+
+ SharedPtr(const SharedPtr &sp)
+ throw() :
+ _ptr(sp._getAndInc())
+ {
+ }
+
+ ~SharedPtr()
+ {
+ if (_ptr) {
+ if (--_ptr->__refCount <= 0)
+ delete _ptr;
+ }
+ }
+
+ inline SharedPtr &operator=(const SharedPtr &sp)
+ {
+ if (_ptr != sp._ptr) {
+ T *p = sp._getAndInc();
+ if (_ptr) {
+ if (--_ptr->__refCount <= 0)
+ delete _ptr;
+ }
+ _ptr = p;
+ }
+ return *this;
+ }
+
+ inline operator bool() const throw() { return (_ptr); }
+ inline T &operator*() const throw() { return *_ptr; }
+ inline T *operator->() const throw() { return _ptr; }
+
+ /**
+ * @return Raw pointer to held object
+ */
+ inline T *ptr() const throw() { return _ptr; }
+
+ /**
+ * Set this pointer to null
+ */
+ inline void zero()
+ {
+ if (_ptr) {
+ if (--_ptr->__refCount <= 0)
+ delete _ptr;
+ }
+ _ptr = (T *)0;
+ }
+
+ inline bool operator==(const SharedPtr &sp) const throw() { return (_ptr == sp._ptr); }
+ inline bool operator!=(const SharedPtr &sp) const throw() { return (_ptr != sp._ptr); }
+ inline bool operator>(const SharedPtr &sp) const throw() { return (_ptr > sp._ptr); }
+ inline bool operator<(const SharedPtr &sp) const throw() { return (_ptr < sp._ptr); }
+ inline bool operator>=(const SharedPtr &sp) const throw() { return (_ptr >= sp._ptr); }
+ inline bool operator<=(const SharedPtr &sp) const throw() { return (_ptr <= sp._ptr); }
+
+private:
+ inline T *_getAndInc() const
+ throw()
+ {
+ if (_ptr)
+ ++_ptr->__refCount;
+ return _ptr;
+ }
+
+ T *_ptr;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Switch.cpp b/node/Switch.cpp
new file mode 100644
index 00000000..87218b0d
--- /dev/null
+++ b/node/Switch.cpp
@@ -0,0 +1,1022 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <algorithm>
+#include <utility>
+#include <stdexcept>
+
+#include "Switch.hpp"
+#include "Node.hpp"
+#include "EthernetTap.hpp"
+#include "InetAddress.hpp"
+#include "Topology.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Defaults.hpp"
+#include "Peer.hpp"
+#include "NodeConfig.hpp"
+#include "Demarc.hpp"
+
+#include "../version.h"
+
+namespace ZeroTier {
+
+Switch::Switch(const RuntimeEnvironment *renv) :
+ _r(renv)
+{
+ memset(_multicastHistory,0,sizeof(_multicastHistory));
+}
+
+Switch::~Switch()
+{
+}
+
+void Switch::onRemotePacket(Demarc::Port localPort,const InetAddress &fromAddr,const Buffer<4096> &data)
+{
+ Packet packet;
+
+ try {
+ if (data.size() > ZT_PROTO_MIN_FRAGMENT_LENGTH) {
+ // Message is long enough to be a Packet or Packet::Fragment
+
+ if (data[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) {
+ // Looks like a Packet::Fragment
+ Packet::Fragment fragment(data);
+
+ Address destination(fragment.destination());
+ if (destination != _r->identity.address()) {
+ // Fragment is not for us, so try to relay it
+
+ if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
+ fragment.incrementHops();
+
+ SharedPtr<Peer> relayTo = _r->topology->getPeer(destination);
+ if ((!relayTo)||(!relayTo->send(_r,fragment.data(),fragment.size(),true,Packet::VERB_NOP,Utils::now()))) {
+ relayTo = _r->topology->getBestSupernode();
+ if (relayTo)
+ relayTo->send(_r,fragment.data(),fragment.size(),true,Packet::VERB_NOP,Utils::now());
+ }
+ } else {
+ TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
+ }
+ } else {
+ // Fragment looks like ours
+
+ uint64_t pid = fragment.packetId();
+ unsigned int fno = fragment.fragmentNumber();
+ unsigned int tf = fragment.totalFragments();
+
+ if ((tf <= ZT_MAX_PACKET_FRAGMENTS)&&(fno < ZT_MAX_PACKET_FRAGMENTS)&&(fno > 0)&&(tf > 1)) {
+ // Fragment appears basically sane. Its fragment number must be
+ // 1 or more, since a Packet with fragmented bit set is fragment 0.
+ // Total fragments must be more than 1, otherwise why are we
+ // seeing a Packet::Fragment?
+
+ Mutex::Lock _l(_defragQueue_m);
+ std::map< uint64_t,DefragQueueEntry >::iterator dqe(_defragQueue.find(pid));
+
+ if (dqe == _defragQueue.end()) {
+ // We received a Packet::Fragment without its head, so queue it and wait
+
+ DefragQueueEntry &dq = _defragQueue[pid];
+ dq.creationTime = Utils::now();
+ dq.frags[fno - 1] = fragment;
+ dq.totalFragments = tf; // total fragment count is known
+ dq.haveFragments = 1 << fno; // we have only this fragment
+ //TRACE("fragment (%u/%u) of %.16llx from %s",fno + 1,tf,pid,fromAddr.toString().c_str());
+ } else if (!(dqe->second.haveFragments & (1 << fno))) {
+ // We have other fragments and maybe the head, so add this one and check
+
+ dqe->second.frags[fno - 1] = fragment;
+ dqe->second.totalFragments = tf;
+ //TRACE("fragment (%u/%u) of %.16llx from %s",fno + 1,tf,pid,fromAddr.toString().c_str());
+
+ if (Utils::countBits(dqe->second.haveFragments |= (1 << fno)) == tf) {
+ // We have all fragments -- assemble and process full Packet
+
+ //TRACE("packet %.16llx is complete, assembling and processing...",pid);
+ packet = dqe->second.frag0;
+ for(unsigned int f=1;f<tf;++f)
+ packet.append(dqe->second.frags[f - 1].payload(),dqe->second.frags[f - 1].payloadLength());
+ _defragQueue.erase(dqe);
+
+ goto Switch_onRemotePacket_complete_packet_handler;
+ }
+ } // else this is a duplicate fragment, ignore
+ }
+ }
+
+ } else if (data.size() > ZT_PROTO_MIN_PACKET_LENGTH) {
+ // Looks like a Packet -- either unfragmented or a fragmented packet head
+ packet = data;
+
+ Address destination(packet.destination());
+ if (destination != _r->identity.address()) {
+ // Packet is not for us, so try to relay it
+
+ if (packet.hops() < ZT_RELAY_MAX_HOPS) {
+ packet.incrementHops();
+
+ SharedPtr<Peer> relayTo = _r->topology->getPeer(destination);
+ if ((relayTo)&&(relayTo->send(_r,packet.data(),packet.size(),true,Packet::VERB_NOP,Utils::now()))) {
+ // TODO: don't unite immediately, wait until the peers have exchanged a packet or two
+ unite(packet.source(),destination,false); // periodically try to get them to talk directly
+ } else {
+ relayTo = _r->topology->getBestSupernode();
+ if (relayTo)
+ relayTo->send(_r,packet.data(),packet.size(),true,Packet::VERB_NOP,Utils::now());
+ }
+ } else {
+ TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet.source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
+ }
+ } else if (packet.fragmented()) {
+ // Packet is the head of a fragmented packet series
+
+ uint64_t pid = packet.packetId();
+ Mutex::Lock _l(_defragQueue_m);
+ std::map< uint64_t,DefragQueueEntry >::iterator dqe(_defragQueue.find(pid));
+
+ if (dqe == _defragQueue.end()) {
+ // If we have no other fragments yet, create an entry and save the head
+
+ DefragQueueEntry &dq = _defragQueue[pid];
+ dq.creationTime = Utils::now();
+ dq.frag0 = packet;
+ dq.totalFragments = 0; // 0 == unknown, waiting for Packet::Fragment
+ dq.haveFragments = 1; // head is first bit (left to right)
+ //TRACE("fragment (0/?) of %.16llx from %s",pid,fromAddr.toString().c_str());
+ } else if (!(dqe->second.haveFragments & 1)) {
+ // If we have other fragments but no head, see if we are complete with the head
+
+ if ((dqe->second.totalFragments)&&(Utils::countBits(dqe->second.haveFragments |= 1) == dqe->second.totalFragments)) {
+ // We have all fragments -- assemble and process full Packet
+
+ //TRACE("packet %.16llx is complete, assembling and processing...",pid);
+ // packet already contains head, so append fragments
+ for(unsigned int f=1;f<dqe->second.totalFragments;++f)
+ packet.append(dqe->second.frags[f - 1].payload(),dqe->second.frags[f - 1].payloadLength());
+ _defragQueue.erase(dqe);
+
+ goto Switch_onRemotePacket_complete_packet_handler;
+ } else {
+ // Still waiting on more fragments, so queue the head
+
+ dqe->second.frag0 = packet;
+ }
+ } // else this is a duplicate head, ignore
+ } else {
+ // Packet is unfragmented, so just process it
+ goto Switch_onRemotePacket_complete_packet_handler;
+ }
+
+ }
+ }
+
+ // If we made it here and didn't jump over, we either queued a fragment
+ // or dropped an invalid or duplicate one. (The goto looks easier to
+ // understand than having a million returns up there.)
+ return;
+
+Switch_onRemotePacket_complete_packet_handler:
+ // Packets that get here are ours and are fully assembled. Don't worry -- if
+ // they are corrupt HMAC authentication will reject them later.
+
+ {
+ //TRACE("%s : %s -> %s",fromAddr.toString().c_str(),packet.source().toString().c_str(),packet.destination().toString().c_str());
+ PacketServiceAttemptResult r = _tryHandleRemotePacket(localPort,fromAddr,packet);
+ if (r != PACKET_SERVICE_ATTEMPT_OK) {
+ Address source(packet.source());
+ {
+ Mutex::Lock _l(_rxQueue_m);
+ std::multimap< Address,RXQueueEntry >::iterator qe(_rxQueue.insert(std::pair< Address,RXQueueEntry >(source,RXQueueEntry())));
+ qe->second.creationTime = Utils::now();
+ qe->second.packet = packet;
+ qe->second.localPort = localPort;
+ qe->second.fromAddr = fromAddr;
+ }
+ if (r == PACKET_SERVICE_ATTEMPT_PEER_UNKNOWN)
+ _requestWhois(source);
+ }
+ }
+ } catch (std::exception &ex) {
+ TRACE("dropped packet from %s: %s",fromAddr.toString().c_str(),ex.what());
+ } catch ( ... ) {
+ TRACE("dropped packet from %s: unexpected exception",fromAddr.toString().c_str());
+ }
+}
+
+void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data)
+{
+ if (from != network->tap().mac()) {
+ LOG("ignored tap: %s -> %s %s (bridging is not supported)",from.toString().c_str(),to.toString().c_str(),Utils::etherTypeName(etherType));
+ return;
+ }
+
+ if (to == network->tap().mac()) {
+ // Right thing to do? Will this ever happen?
+ TRACE("weird OS behavior: ethernet frame received from self, reflecting");
+ network->tap().put(from,to,etherType,data.data(),data.size());
+ return;
+ }
+
+ if ((etherType != ZT_ETHERTYPE_ARP)&&(etherType != ZT_ETHERTYPE_IPV4)&&(etherType != ZT_ETHERTYPE_IPV6)) {
+ LOG("ignored tap: %s -> %s %s (not a supported etherType)",from.toString().c_str(),to.toString().c_str(),Utils::etherTypeName(etherType));
+ return;
+ }
+
+ if (to.isMulticast()) {
+ MulticastGroup mg(to,0);
+
+ // Handle special cases: IPv4 ARP
+ if ((etherType == ZT_ETHERTYPE_ARP)&&(data.size() == 28)&&(data[2] == 0x08)&&(data[3] == 0x00)&&(data[4] == 6)&&(data[5] == 4)&&(data[7] == 0x01))
+ mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(data.field(24,4),4,0));
+
+ // Remember this message's CRC, but don't drop if we've already seen it
+ // since it's our own.
+ _checkAndUpdateMulticastHistory(from,mg.mac(),data.data(),data.size(),network->id(),Utils::now());
+
+ // Start multicast propagation with empty bloom filter
+ unsigned char bloom[ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE];
+ memset(bloom,0,sizeof(bloom));
+ _propagateMulticast(network,bloom,mg,0,0,from,etherType,data.data(),data.size());
+ } else if (to.isZeroTier()) {
+ // Simple unicast frame from us to another node
+ Address toZT(to.data + 1);
+ if (network->isAllowed(toZT)) {
+ Packet outp(toZT,_r->identity.address(),Packet::VERB_FRAME);
+ outp.append(network->id());
+ outp.append((uint16_t)etherType);
+ outp.append(data);
+ outp.compress();
+ send(outp,true);
+ } else {
+ TRACE("UNICAST: %s -> %s %s (dropped, destination not a member of closed network %llu)",from.toString().c_str(),to.toString().c_str(),Utils::etherTypeName(etherType),network->id());
+ }
+ } else {
+ TRACE("UNICAST: %s -> %s %s (dropped, destination MAC not ZeroTier)",from.toString().c_str(),to.toString().c_str(),Utils::etherTypeName(etherType));
+ }
+}
+
+void Switch::send(const Packet &packet,bool encrypt)
+{
+ //TRACE("%.16llx %s -> %s (size: %u) (enc: %s)",packet.packetId(),Packet::verbString(packet.verb()),packet.destination().toString().c_str(),packet.size(),(encrypt ? "yes" : "no"));
+
+ PacketServiceAttemptResult r = _trySend(packet,encrypt);
+ if (r != PACKET_SERVICE_ATTEMPT_OK) {
+ {
+ Mutex::Lock _l(_txQueue_m);
+ std::multimap< Address,TXQueueEntry >::iterator qe(_txQueue.insert(std::pair< Address,TXQueueEntry >(packet.destination(),TXQueueEntry())));
+ qe->second.creationTime = Utils::now();
+ qe->second.packet = packet;
+ qe->second.encrypt = encrypt;
+ }
+ if (r == PACKET_SERVICE_ATTEMPT_PEER_UNKNOWN)
+ _requestWhois(packet.destination());
+ }
+}
+
+void Switch::sendHELLO(const Address &dest)
+{
+ Packet outp(dest,_r->identity.address(),Packet::VERB_HELLO);
+ outp.append((unsigned char)ZT_PROTO_VERSION);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
+ outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
+ outp.append(Utils::now());
+ _r->identity.serialize(outp,false);
+ send(outp,false);
+}
+
+bool Switch::unite(const Address &p1,const Address &p2,bool force)
+{
+ SharedPtr<Peer> p1p = _r->topology->getPeer(p1);
+ if (!p1p)
+ return false;
+ SharedPtr<Peer> p2p = _r->topology->getPeer(p2);
+ if (!p2p)
+ return false;
+
+ uint64_t now = Utils::now();
+
+ std::pair<InetAddress,InetAddress> cg(Peer::findCommonGround(*p1p,*p2p,now));
+ if (!(cg.first))
+ return false;
+
+ // Addresses are sorted in key for last unite attempt map for order
+ // invariant lookup: (p1,p2) == (p2,p1)
+ Array<Address,2> uniteKey;
+ if (p1 >= p2) {
+ uniteKey[0] = p2;
+ uniteKey[1] = p1;
+ } else {
+ uniteKey[0] = p1;
+ uniteKey[1] = p2;
+ }
+ {
+ Mutex::Lock _l(_lastUniteAttempt_m);
+ std::map< Array< Address,2 >,uint64_t >::const_iterator e(_lastUniteAttempt.find(uniteKey));
+ if ((!force)&&(e != _lastUniteAttempt.end())&&((now - e->second) < ZT_MIN_UNITE_INTERVAL))
+ return false;
+ else _lastUniteAttempt[uniteKey] = now;
+ }
+
+ TRACE("unite: %s(%s) <> %s(%s)",p1.toString().c_str(),cg.second.toString().c_str(),p2.toString().c_str(),cg.first.toString().c_str());
+
+ { // tell p1 where to find p2
+ Packet outp(p1,_r->identity.address(),Packet::VERB_RENDEZVOUS);
+ outp.append(p2.data(),ZT_ADDRESS_LENGTH);
+ outp.append((uint16_t)cg.first.port());
+ if (cg.first.isV6()) {
+ outp.append((unsigned char)16);
+ outp.append(cg.first.rawIpData(),16);
+ } else {
+ outp.append((unsigned char)4);
+ outp.append(cg.first.rawIpData(),4);
+ }
+ outp.encrypt(p1p->cryptKey());
+ outp.hmacSet(p1p->macKey());
+ p1p->send(_r,outp.data(),outp.size(),false,Packet::VERB_RENDEZVOUS,now);
+ }
+ { // tell p2 where to find p1
+ Packet outp(p2,_r->identity.address(),Packet::VERB_RENDEZVOUS);
+ outp.append(p1.data(),ZT_ADDRESS_LENGTH);
+ outp.append((uint16_t)cg.second.port());
+ if (cg.second.isV6()) {
+ outp.append((unsigned char)16);
+ outp.append(cg.second.rawIpData(),16);
+ } else {
+ outp.append((unsigned char)4);
+ outp.append(cg.second.rawIpData(),4);
+ }
+ outp.encrypt(p2p->cryptKey());
+ outp.hmacSet(p2p->macKey());
+ p2p->send(_r,outp.data(),outp.size(),false,Packet::VERB_RENDEZVOUS,now);
+ }
+
+ return true;
+}
+
+unsigned long Switch::doTimerTasks()
+{
+ unsigned long nextDelay = ~((unsigned long)0); // big number, caller will cap return value
+ uint64_t now = Utils::now();
+
+ {
+ Mutex::Lock _l(_rendezvousQueue_m);
+ for(std::map< Address,RendezvousQueueEntry >::iterator i(_rendezvousQueue.begin());i!=_rendezvousQueue.end();) {
+ if (now >= i->second.fireAtTime) {
+ SharedPtr<Peer> withPeer = _r->topology->getPeer(i->first);
+ if (withPeer) {
+ TRACE("sending NAT-T NOP to %s(%s)",i->first.toString().c_str(),i->second.inaddr.toString().c_str());
+ Packet outp(i->first,_r->identity.address(),Packet::VERB_NOP);
+ outp.append("ZT",2); // arbitrary payload
+ outp.hmacSet(withPeer->macKey());
+ _r->demarc->send(i->second.localPort,i->second.inaddr,outp.data(),outp.size(),-1);
+ }
+ _rendezvousQueue.erase(i++);
+ } else {
+ nextDelay = std::min(nextDelay,(unsigned long)(i->second.fireAtTime - now));
+ ++i;
+ }
+ }
+ }
+
+ {
+ Mutex::Lock _l(_outstandingWhoisRequests_m);
+ for(std::map< Address,WhoisRequest >::iterator i(_outstandingWhoisRequests.begin());i!=_outstandingWhoisRequests.end();) {
+ unsigned long since = (unsigned long)(now - i->second.lastSent);
+ if (since >= ZT_WHOIS_RETRY_DELAY) {
+ if (i->second.retries >= ZT_MAX_WHOIS_RETRIES) {
+ TRACE("WHOIS %s timed out",i->first.toString().c_str());
+ _outstandingWhoisRequests.erase(i++);
+ continue;
+ } else {
+ i->second.lastSent = now;
+ i->second.peersConsulted[i->second.retries] = _sendWhoisRequest(i->first,i->second.peersConsulted,i->second.retries);
+ ++i->second.retries;
+ TRACE("WHOIS %s (retry %u)",i->first.toString().c_str(),i->second.retries);
+ nextDelay = std::min(nextDelay,(unsigned long)ZT_WHOIS_RETRY_DELAY);
+ }
+ } else nextDelay = std::min(nextDelay,ZT_WHOIS_RETRY_DELAY - since);
+ ++i;
+ }
+ }
+
+ {
+ Mutex::Lock _l(_txQueue_m);
+ for(std::multimap< Address,TXQueueEntry >::iterator i(_txQueue.begin());i!=_txQueue.end();) {
+ if (_trySend(i->second.packet,i->second.encrypt) == PACKET_SERVICE_ATTEMPT_OK)
+ _txQueue.erase(i++);
+ else if ((now - i->second.creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) {
+ TRACE("TX %s -> %s timed out",i->second.packet.source().toString().c_str(),i->second.packet.destination().toString().c_str());
+ _txQueue.erase(i++);
+ } else ++i;
+ }
+ }
+ {
+ Mutex::Lock _l(_rxQueue_m);
+ for(std::multimap< Address,RXQueueEntry >::iterator i(_rxQueue.begin());i!=_rxQueue.end();) {
+ if ((now - i->second.creationTime) > ZT_RECEIVE_QUEUE_TIMEOUT) {
+ TRACE("RX from %s timed out waiting for WHOIS",i->second.packet.source().toString().c_str());
+ _rxQueue.erase(i++);
+ } else ++i;
+ }
+ }
+
+ {
+ Mutex::Lock _l(_defragQueue_m);
+ for(std::map< uint64_t,DefragQueueEntry >::iterator i(_defragQueue.begin());i!=_defragQueue.end();) {
+ if ((now - i->second.creationTime) > ZT_FRAGMENTED_PACKET_RECEIVE_TIMEOUT) {
+ TRACE("incomplete fragmented packet %.16llx timed out, fragments discarded",i->first);
+ _defragQueue.erase(i++);
+ } else ++i;
+ }
+ }
+
+ return std::max(nextDelay,(unsigned long)50); // minimum delay
+}
+
+void Switch::announceMulticastGroups(const std::map< SharedPtr<Network>,std::set<MulticastGroup> > &allMemberships)
+{
+ std::vector< SharedPtr<Peer> > directPeers;
+ _r->topology->eachPeer(Topology::CollectPeersWithActiveDirectPath(directPeers));
+
+#ifdef ZT_TRACE
+ unsigned int totalMulticastGroups = 0;
+ for(std::map< SharedPtr<Network>,std::set<MulticastGroup> >::const_iterator i(allMemberships.begin());i!=allMemberships.end();++i)
+ totalMulticastGroups += (unsigned int)i->second.size();
+ TRACE("announcing %u multicast groups for %u networks to %u peers",totalMulticastGroups,(unsigned int)allMemberships.size(),(unsigned int)directPeers.size());
+#endif
+
+ for(std::vector< SharedPtr<Peer> >::iterator p(directPeers.begin());p!=directPeers.end();++p) {
+ Packet outp((*p)->address(),_r->identity.address(),Packet::VERB_MULTICAST_LIKE);
+
+ for(std::map< SharedPtr<Network>,std::set<MulticastGroup> >::const_iterator nwmgs(allMemberships.begin());nwmgs!=allMemberships.end();++nwmgs) {
+ if ((nwmgs->first->open())||(_r->topology->isSupernode((*p)->address()))||(nwmgs->first->isMember((*p)->address()))) {
+ for(std::set<MulticastGroup>::iterator mg(nwmgs->second.begin());mg!=nwmgs->second.end();++mg) {
+ if ((outp.size() + 18) > ZT_UDP_DEFAULT_PAYLOAD_MTU) {
+ send(outp,true);
+ outp.reset((*p)->address(),_r->identity.address(),Packet::VERB_MULTICAST_LIKE);
+ }
+
+ outp.append((uint64_t)nwmgs->first->id());
+ outp.append(mg->mac().data,6);
+ outp.append((uint32_t)mg->adi());
+ }
+ }
+ }
+
+ if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH)
+ send(outp,true);
+ }
+}
+
+void Switch::_CBaddPeerFromHello(void *arg,const SharedPtr<Peer> &p,Topology::PeerVerifyResult result)
+{
+ _CBaddPeerFromHello_Data *req = (_CBaddPeerFromHello_Data *)arg;
+ const RuntimeEnvironment *_r = req->parent->_r;
+
+ switch(result) {
+ case Topology::PEER_VERIFY_ACCEPTED_NEW:
+ case Topology::PEER_VERIFY_ACCEPTED_ALREADY_HAVE:
+ case Topology::PEER_VERIFY_ACCEPTED_DISPLACED_INVALID_ADDRESS: {
+ Packet outp(req->source,_r->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_HELLO);
+ outp.append(req->helloPacketId);
+ outp.append(req->helloTimestamp);
+ outp.encrypt(p->cryptKey());
+ outp.hmacSet(p->macKey());
+ req->parent->_r->demarc->send(req->localPort,req->fromAddr,outp.data(),outp.size(),-1);
+ } break;
+ case Topology::PEER_VERIFY_REJECTED_INVALID_IDENTITY: {
+ Packet outp(req->source,_r->identity.address(),Packet::VERB_ERROR);
+ outp.append((unsigned char)Packet::VERB_HELLO);
+ outp.append(req->helloPacketId);
+ outp.append((unsigned char)Packet::ERROR_IDENTITY_INVALID);
+ outp.encrypt(p->cryptKey());
+ outp.hmacSet(p->macKey());
+ req->parent->_r->demarc->send(req->localPort,req->fromAddr,outp.data(),outp.size(),-1);
+ } break;
+ case Topology::PEER_VERIFY_REJECTED_DUPLICATE:
+ case Topology::PEER_VERIFY_REJECTED_DUPLICATE_TRIAGED: {
+ Packet outp(req->source,_r->identity.address(),Packet::VERB_ERROR);
+ outp.append((unsigned char)Packet::VERB_HELLO);
+ outp.append(req->helloPacketId);
+ outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION);
+ outp.encrypt(p->cryptKey());
+ outp.hmacSet(p->macKey());
+ req->parent->_r->demarc->send(req->localPort,req->fromAddr,outp.data(),outp.size(),-1);
+ } break;
+ }
+
+ delete req;
+}
+
+void Switch::_CBaddPeerFromWhois(void *arg,const SharedPtr<Peer> &p,Topology::PeerVerifyResult result)
+{
+ Switch *d = (Switch *)arg;
+
+ switch(result) {
+ case Topology::PEER_VERIFY_ACCEPTED_NEW:
+ case Topology::PEER_VERIFY_ACCEPTED_ALREADY_HAVE:
+ case Topology::PEER_VERIFY_ACCEPTED_DISPLACED_INVALID_ADDRESS:
+ d->_outstandingWhoisRequests_m.lock();
+ d->_outstandingWhoisRequests.erase(p->identity().address());
+ d->_outstandingWhoisRequests_m.unlock();
+ d->_retryPendingFor(p->identity().address());
+ break;
+ default:
+ break;
+ }
+}
+
+void Switch::_propagateMulticast(const SharedPtr<Network> &network,unsigned char *bloom,const MulticastGroup &mg,unsigned int mcHops,unsigned int mcLoadFactor,const MAC &from,unsigned int etherType,const void *data,unsigned int len)
+{
+ SharedPtr<Peer> propPeers[ZT_MULTICAST_PROPAGATION_BREADTH];
+ unsigned int np = _r->topology->pickMulticastPropagationPeers(network->id(),Address(),bloom,ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE * 8,ZT_MULTICAST_PROPAGATION_BREADTH,mg,propPeers);
+
+ for(unsigned int i=0;i<np;++i)
+ Utils::bloomAdd(bloom,ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE,propPeers[i]->address().sum());
+
+ for(unsigned int i=0;i<np;++i) {
+ Packet outp(propPeers[i]->address(),_r->identity.address(),Packet::VERB_MULTICAST_FRAME);
+ outp.append(network->id());
+ outp.append(mg.mac().data,6);
+ outp.append((uint32_t)mg.adi());
+ outp.append(bloom,ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE);
+ outp.append((uint8_t)mcHops);
+ outp.append((uint16_t)mcLoadFactor);
+ outp.append(from.data,6);
+ outp.append((uint16_t)etherType);
+ outp.append(data,len);
+ outp.compress();
+ send(outp,true);
+ }
+}
+
+Switch::PacketServiceAttemptResult Switch::_tryHandleRemotePacket(Demarc::Port localPort,const InetAddress &fromAddr,Packet &packet)
+{
+ // NOTE: We assume any packet that's made it here is for us. If it's not it
+ // will fail HMAC validation and be discarded anyway, amounting to a second
+ // layer of sanity checking.
+
+ Address source(packet.source());
+
+ if ((!packet.encrypted())&&(packet.verb() == Packet::VERB_HELLO)) {
+ // Unencrypted HELLOs are handled here since they are used to
+ // populate our identity cache in the first place. Thus we might get
+ // a HELLO for someone for whom we don't have a Peer record.
+ TRACE("HELLO from %s(%s)",source.toString().c_str(),fromAddr.toString().c_str());
+ _doHELLO(localPort,fromAddr,packet);
+ return PACKET_SERVICE_ATTEMPT_OK;
+ }
+
+ SharedPtr<Peer> peer = _r->topology->getPeer(source);
+ if (peer) {
+ uint64_t now = Utils::now();
+ unsigned int latency = 0;
+
+ if (!packet.hmacVerify(peer->macKey())) {
+ TRACE("dropped packet from %s(%s), HMAC authentication failed (size: %u)",source.toString().c_str(),fromAddr.toString().c_str(),packet.size());
+ return PACKET_SERVICE_ATTEMPT_OK;
+ }
+ if (packet.encrypted()) {
+ packet.decrypt(peer->cryptKey());
+ } else if (packet.verb() != Packet::VERB_NOP) {
+ TRACE("ODD: %s from %s wasn't encrypted",Packet::verbString(packet.verb()),source.toString().c_str());
+ }
+ if (!packet.uncompress()) {
+ TRACE("dropped packet from %s(%s), compressed data invalid",source.toString().c_str(),fromAddr.toString().c_str());
+ return PACKET_SERVICE_ATTEMPT_OK;
+ }
+
+ switch(packet.verb()) {
+ case Packet::VERB_NOP: // these are sent for NAT-t
+ TRACE("NOP from %s(%s) (probably NAT-t)",source.toString().c_str(),fromAddr.toString().c_str());
+ break;
+ case Packet::VERB_HELLO: // usually they're handled up top, but technically an encrypted HELLO is legal
+ _doHELLO(localPort,fromAddr,packet);
+ break;
+ case Packet::VERB_ERROR:
+ try {
+#ifdef ZT_TRACE
+ Packet::Verb inReVerb = (Packet::Verb)packet[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB];
+ Packet::ErrorCode errorCode = (Packet::ErrorCode)packet[ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE];
+ TRACE("ERROR %s from %s in-re %s",Packet::errorString(errorCode),source.toString().c_str(),Packet::verbString(inReVerb));
+#endif
+ // TODO: handle key errors, such as duplicate identity
+ } catch (std::exception &ex) {
+ TRACE("dropped ERROR from %s: unexpected exception: %s",source.toString().c_str(),ex.what());
+ } catch ( ... ) {
+ TRACE("dropped ERROR from %s: unexpected exception: (unknown)",source.toString().c_str());
+ }
+ break;
+ case Packet::VERB_OK:
+ try {
+ Packet::Verb inReVerb = (Packet::Verb)packet[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
+ switch(inReVerb) {
+ case Packet::VERB_HELLO:
+ latency = std::min((unsigned int)(now - packet.at<uint64_t>(ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP)),(unsigned int)0xffff);
+ TRACE("OK(HELLO), latency to %s: %u",source.toString().c_str(),latency);
+ break;
+ case Packet::VERB_WHOIS:
+ // Right now we only query supernodes for WHOIS and only accept
+ // OK back from them. If we query other nodes, we'll have to
+ // do something to prevent WHOIS cache poisoning such as
+ // using the packet ID field in the OK packet to match with the
+ // original query. Technically we should be doing this anyway.
+ if (_r->topology->isSupernode(source))
+ _r->topology->addPeer(SharedPtr<Peer>(new Peer(_r->identity,Identity(packet,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY))),&Switch::_CBaddPeerFromWhois,this);
+ break;
+ default:
+ break;
+ }
+ } catch (std::exception &ex) {
+ TRACE("dropped OK from %s: unexpected exception: %s",source.toString().c_str(),ex.what());
+ } catch ( ... ) {
+ TRACE("dropped OK from %s: unexpected exception: (unknown)",source.toString().c_str());
+ }
+ break;
+ case Packet::VERB_WHOIS: {
+ if (packet.payloadLength() == ZT_ADDRESS_LENGTH) {
+ SharedPtr<Peer> p(_r->topology->getPeer(Address(packet.payload())));
+ if (p) {
+ Packet outp(source,_r->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_WHOIS);
+ outp.append(packet.packetId());
+ p->identity().serialize(outp,false);
+ outp.encrypt(peer->cryptKey());
+ outp.hmacSet(peer->macKey());
+ _r->demarc->send(localPort,fromAddr,outp.data(),outp.size(),-1);
+ TRACE("sent WHOIS response to %s for %s",source.toString().c_str(),Address(packet.payload()).toString().c_str());
+ } else {
+ Packet outp(source,_r->identity.address(),Packet::VERB_ERROR);
+ outp.append((unsigned char)Packet::VERB_WHOIS);
+ outp.append(packet.packetId());
+ outp.append((unsigned char)Packet::ERROR_NOT_FOUND);
+ outp.append(packet.payload(),ZT_ADDRESS_LENGTH);
+ outp.encrypt(peer->cryptKey());
+ outp.hmacSet(peer->macKey());
+ _r->demarc->send(localPort,fromAddr,outp.data(),outp.size(),-1);
+ TRACE("sent WHOIS ERROR to %s for %s (not found)",source.toString().c_str(),Address(packet.payload()).toString().c_str());
+ }
+ } else {
+ TRACE("dropped WHOIS from %s: missing or invalid address",source.toString().c_str());
+ }
+ } break;
+ case Packet::VERB_RENDEZVOUS:
+ try {
+ Address with(packet.field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH));
+ RendezvousQueueEntry qe;
+ if (_r->topology->getPeer(with)) {
+ unsigned int port = packet.at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
+ unsigned int addrlen = packet[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
+ if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
+ qe.inaddr.set(packet.field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
+ qe.fireAtTime = now + ZT_RENDEZVOUS_NAT_T_DELAY; // then send real packet in a few ms
+ qe.localPort = _r->demarc->pick(qe.inaddr);
+ TRACE("RENDEZVOUS from %s says %s might be at %s, starting NAT-t",source.toString().c_str(),with.toString().c_str(),qe.inaddr.toString().c_str());
+ _r->demarc->send(qe.localPort,qe.inaddr,"\0",1,ZT_FIREWALL_OPENER_HOPS); // start with firewall opener
+ {
+ Mutex::Lock _l(_rendezvousQueue_m);
+ _rendezvousQueue[with] = qe;
+ }
+ } else {
+ TRACE("dropped corrupt RENDEZVOUS from %s (bad address or port)",source.toString().c_str());
+ }
+ } else {
+ TRACE("ignored RENDEZVOUS from %s for unknown peer %s",source.toString().c_str(),with.toString().c_str());
+ }
+ } catch (std::exception &ex) {
+ TRACE("dropped RENDEZVOUS from %s: %s",source.toString().c_str(),ex.what());
+ } catch ( ... ) {
+ TRACE("dropped RENDEZVOUS from %s: unexpected exception",source.toString().c_str());
+ }
+ break;
+ case Packet::VERB_FRAME:
+ try {
+ SharedPtr<Network> network(_r->nc->network(packet.at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID)));
+ if (network) {
+ if (network->isAllowed(source)) {
+ unsigned int etherType = packet.at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
+ if ((etherType != ZT_ETHERTYPE_ARP)&&(etherType != ZT_ETHERTYPE_IPV4)&&(etherType != ZT_ETHERTYPE_IPV6)) {
+ TRACE("dropped FRAME from %s: unsupported ethertype",source.toString().c_str());
+ } else if (packet.size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
+ network->tap().put(source.toMAC(),network->tap().mac(),etherType,packet.data() + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,packet.size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD);
+ }
+ } else {
+ TRACE("dropped FRAME from %s: not a member of closed network %llu",source.toString().c_str(),network->id());
+ }
+ } else {
+ TRACE("dropped FRAME from %s: network %llu unknown",source.toString().c_str(),packet.at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
+ }
+ } catch (std::exception &ex) {
+ TRACE("dropped FRAME from %s: unexpected exception: %s",source.toString().c_str(),ex.what());
+ } catch ( ... ) {
+ TRACE("dropped FRAME from %s: unexpected exception: (unknown)",source.toString().c_str());
+ }
+ break;
+ case Packet::VERB_MULTICAST_FRAME:
+ try {
+ SharedPtr<Network> network(_r->nc->network(packet.at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID)));
+ if (network) {
+ if (network->isAllowed(source)) {
+ if (packet.size() > ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD) {
+ MulticastGroup mg(MAC(packet.field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_MULTICAST_MAC,6)),packet.at<uint32_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ADI));
+ unsigned char bloom[ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE];
+ memcpy(bloom,packet.field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_BLOOM,ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE),ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE);
+ unsigned int hops = packet[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_HOPS];
+ unsigned int loadFactor = packet.at<uint16_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_LOAD_FACTOR);
+ MAC fromMac(packet.field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FROM_MAC,6));
+ unsigned int etherType = packet.at<uint16_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE);
+
+ if ((fromMac.isZeroTier())&&(network->isAllowed(Address(fromMac)))) {
+ if (_checkAndUpdateMulticastHistory(fromMac,mg.mac(),packet.data() + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD,packet.size() - ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD,network->id(),now)) {
+ TRACE("dropped MULTICAST_FRAME from %s: duplicate multicast",source.toString().c_str());
+ } else {
+ //TRACE("MULTICAST_FRAME: %s -> %s (adi: %.8lx), %u bytes, net: %llu",fromMac.toString().c_str(),mg.mac().toString().c_str(),(unsigned long)mg.adi(),packet.size() - ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD,network->id());
+ network->tap().put(fromMac,mg.mac(),etherType,packet.data() + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD,packet.size() - ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD);
+
+ // TODO: implement load factor based propagation rate limitation
+ // How it will work: each node will adjust loadFactor based on
+ // its current load of multicast traffic. Then it will probabilistically
+ // fail to propagate, with the probability being based on load factor.
+ // This will need some in-the-field testing and tuning to get right.
+ _propagateMulticast(network,bloom,mg,hops+1,loadFactor,fromMac,etherType,packet.data() + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD,packet.size() - ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD);
+ }
+ } else {
+ TRACE("dropped MULTICAST_FRAME from %s: ultimate sender %s not a member of closed network %llu",source.toString().c_str(),fromMac.toString().c_str(),network->id());
+ }
+ }
+ } else {
+ TRACE("dropped MULTICAST_FRAME from %s: not a member of closed network %llu",source.toString().c_str(),network->id());
+ }
+ } else {
+ TRACE("dropped MULTICAST_FRAME from %s: network %llu unknown",source.toString().c_str(),packet.at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID));
+ }
+ } catch (std::exception &ex) {
+ TRACE("dropped MULTICAST_FRAME from %s: unexpected exception: %s",source.toString().c_str(),ex.what());
+ } catch ( ... ) {
+ TRACE("dropped MULTICAST_FRAME from %s: unexpected exception: (unknown)",source.toString().c_str());
+ }
+ break;
+ case Packet::VERB_MULTICAST_LIKE:
+ try {
+ unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
+ unsigned int numAccepted = 0;
+ while ((ptr + 18) <= packet.size()) {
+ uint64_t nwid = packet.at<uint64_t>(ptr); ptr += 8;
+ SharedPtr<Network> network(_r->nc->network(nwid));
+ if (network) {
+ if (network->isAllowed(source)) {
+ MAC mac(packet.field(ptr,6)); ptr += 6;
+ uint32_t adi = packet.at<uint32_t>(ptr); ptr += 4;
+ TRACE("peer %s likes multicast group %s:%.8lx on network %llu",source.toString().c_str(),mac.toString().c_str(),(unsigned long)adi,nwid);
+ _r->topology->likesMulticastGroup(nwid,MulticastGroup(mac,adi),source,now);
+ ++numAccepted;
+ } else {
+ TRACE("ignored MULTICAST_LIKE from %s: not a member of closed network %llu",source.toString().c_str(),nwid);
+ }
+ } else {
+ TRACE("ignored MULTICAST_LIKE from %s: network %llu unknown",source.toString().c_str(),nwid);
+ }
+ }
+
+ Packet outp(source,_r->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_MULTICAST_LIKE);
+ outp.append(packet.packetId());
+ outp.append((uint16_t)numAccepted);
+ outp.encrypt(peer->cryptKey());
+ outp.hmacSet(peer->macKey());
+ _r->demarc->send(localPort,fromAddr,outp.data(),outp.size(),-1);
+ } catch (std::exception &ex) {
+ TRACE("dropped MULTICAST_LIKE from %s: unexpected exception: %s",source.toString().c_str(),ex.what());
+ } catch ( ... ) {
+ TRACE("dropped MULTICAST_LIKE from %s: unexpected exception: (unknown)",source.toString().c_str());
+ }
+ break;
+ default:
+ TRACE("ignored unrecognized verb %.2x from %s",(unsigned int)packet.verb(),source.toString().c_str());
+ break;
+ }
+
+ // Update peer timestamps and learn new links
+ peer->onReceive(_r,localPort,fromAddr,latency,packet.hops(),packet.verb(),now);
+ } else return PACKET_SERVICE_ATTEMPT_PEER_UNKNOWN;
+
+ return PACKET_SERVICE_ATTEMPT_OK;
+}
+
+void Switch::_doHELLO(Demarc::Port localPort,const InetAddress &fromAddr,Packet &packet)
+{
+ Address source(packet.source());
+ try {
+ unsigned int protoVersion = packet[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
+ unsigned int vMajor = packet[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION];
+ unsigned int vMinor = packet[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION];
+ unsigned int vRevision = packet.at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
+ uint64_t timestamp = packet.at<uint64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
+ Identity id(packet,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
+
+ SharedPtr<Peer> candidate(new Peer(_r->identity,id));
+ candidate->setPathAddress(fromAddr,false);
+
+ // Initial sniff test
+ if (protoVersion != ZT_PROTO_VERSION) {
+ TRACE("rejected HELLO from %s(%s): invalid protocol version",source.toString().c_str(),fromAddr.toString().c_str());
+ Packet outp(source,_r->identity.address(),Packet::VERB_ERROR);
+ outp.append((unsigned char)Packet::VERB_HELLO);
+ outp.append(packet.packetId());
+ outp.append((unsigned char)Packet::ERROR_BAD_PROTOCOL_VERSION);
+ outp.encrypt(candidate->cryptKey());
+ outp.hmacSet(candidate->macKey());
+ _r->demarc->send(localPort,fromAddr,outp.data(),outp.size(),-1);
+ return;
+ }
+ if (id.address().isReserved()) {
+ TRACE("rejected HELLO from %s(%s): identity has reserved address",source.toString().c_str(),fromAddr.toString().c_str());
+ Packet outp(source,_r->identity.address(),Packet::VERB_ERROR);
+ outp.append((unsigned char)Packet::VERB_HELLO);
+ outp.append(packet.packetId());
+ outp.append((unsigned char)Packet::ERROR_IDENTITY_INVALID);
+ outp.encrypt(candidate->cryptKey());
+ outp.hmacSet(candidate->macKey());
+ _r->demarc->send(localPort,fromAddr,outp.data(),outp.size(),-1);
+ return;
+ }
+ if (id.address() != source) {
+ TRACE("rejected HELLO from %s(%s): identity is not for sender of packet (HELLO is a self-announcement)",source.toString().c_str(),fromAddr.toString().c_str());
+ Packet outp(source,_r->identity.address(),Packet::VERB_ERROR);
+ outp.append((unsigned char)Packet::VERB_HELLO);
+ outp.append(packet.packetId());
+ outp.append((unsigned char)Packet::ERROR_INVALID_REQUEST);
+ outp.encrypt(candidate->cryptKey());
+ outp.hmacSet(candidate->macKey());
+ _r->demarc->send(localPort,fromAddr,outp.data(),outp.size(),-1);
+ return;
+ }
+
+ // Is this a HELLO for a peer we already know? If so just update its
+ // packet receive stats and send an OK.
+ SharedPtr<Peer> existingPeer(_r->topology->getPeer(id.address()));
+ if ((existingPeer)&&(existingPeer->identity() == id)) {
+ existingPeer->onReceive(_r,localPort,fromAddr,0,packet.hops(),Packet::VERB_HELLO,Utils::now());
+
+ Packet outp(source,_r->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_HELLO);
+ outp.append(packet.packetId());
+ outp.append(timestamp);
+ outp.encrypt(existingPeer->cryptKey());
+ outp.hmacSet(existingPeer->macKey());
+ _r->demarc->send(localPort,fromAddr,outp.data(),outp.size(),-1);
+ return;
+ }
+
+ // Otherwise we call addPeer() and set up a callback to handle the verdict
+ _CBaddPeerFromHello_Data *arg = new _CBaddPeerFromHello_Data;
+ arg->parent = this;
+ arg->source = source;
+ arg->fromAddr = fromAddr;
+ arg->localPort = localPort;
+ arg->vMajor = vMajor;
+ arg->vMinor = vMinor;
+ arg->vRevision = vRevision;
+ arg->helloPacketId = packet.packetId();
+ arg->helloTimestamp = timestamp;
+ _r->topology->addPeer(candidate,&Switch::_CBaddPeerFromHello,arg);
+ } catch (std::exception &ex) {
+ TRACE("dropped HELLO from %s(%s): %s",source.toString().c_str(),fromAddr.toString().c_str(),ex.what());
+ } catch ( ... ) {
+ TRACE("dropped HELLO from %s(%s): unexpected exception",source.toString().c_str(),fromAddr.toString().c_str());
+ }
+}
+
+void Switch::_requestWhois(const Address &addr)
+{
+ TRACE("requesting WHOIS for %s",addr.toString().c_str());
+ _sendWhoisRequest(addr,(const Address *)0,0);
+ Mutex::Lock _l(_outstandingWhoisRequests_m);
+ std::pair< std::map< Address,WhoisRequest >::iterator,bool > entry(_outstandingWhoisRequests.insert(std::pair<Address,WhoisRequest>(addr,WhoisRequest())));
+ entry.first->second.lastSent = Utils::now();
+ entry.first->second.retries = 0; // reset retry count if entry already existed
+}
+
+Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
+{
+ SharedPtr<Peer> supernode(_r->topology->getBestSupernode(peersAlreadyConsulted,numPeersAlreadyConsulted));
+ if (supernode) {
+ Packet outp(supernode->address(),_r->identity.address(),Packet::VERB_WHOIS);
+ outp.append(addr.data(),ZT_ADDRESS_LENGTH);
+ outp.encrypt(supernode->cryptKey());
+ outp.hmacSet(supernode->macKey());
+ supernode->send(_r,outp.data(),outp.size(),false,Packet::VERB_WHOIS,Utils::now());
+ return supernode->address();
+ }
+ return Address();
+}
+
+Switch::PacketServiceAttemptResult Switch::_trySend(const Packet &packet,bool encrypt)
+{
+ SharedPtr<Peer> peer(_r->topology->getPeer(packet.destination()));
+ if (peer) {
+ uint64_t now = Utils::now();
+
+ bool isRelay;
+ SharedPtr<Peer> via;
+ if ((_r->topology->isSupernode(peer->address()))||(peer->hasActiveDirectPath(now))) {
+ isRelay = false;
+ via = peer;
+ } else {
+ isRelay = true;
+ via = _r->topology->getBestSupernode();
+ if (!via)
+ return PACKET_SERVICE_ATTEMPT_SEND_FAILED;
+ }
+
+ Packet tmp(packet);
+
+ unsigned int chunkSize = std::min(tmp.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU);
+ tmp.setFragmented(chunkSize < tmp.size());
+
+ if (encrypt)
+ tmp.encrypt(peer->cryptKey());
+ tmp.hmacSet(peer->macKey());
+
+ Packet::Verb verb = packet.verb();
+ if (via->send(_r,tmp.data(),chunkSize,isRelay,verb,now)) {
+ if (chunkSize < tmp.size()) {
+ // Too big for one bite, fragment the rest
+ unsigned int fragStart = chunkSize;
+ unsigned int remaining = tmp.size() - chunkSize;
+ unsigned int fragsRemaining = (remaining / (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
+ if ((fragsRemaining * (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH)) < remaining)
+ ++fragsRemaining;
+ unsigned int totalFragments = fragsRemaining + 1;
+
+ for(unsigned int f=0;f<fragsRemaining;++f) {
+ chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
+ Packet::Fragment frag(tmp,fragStart,chunkSize,f + 1,totalFragments);
+ if (!via->send(_r,frag.data(),frag.size(),isRelay,verb,now)) {
+ TRACE("WARNING: packet send to %s failed on later fragment #%u (check IP layer buffer sizes?)",via->address().toString().c_str(),f + 1);
+ return PACKET_SERVICE_ATTEMPT_SEND_FAILED;
+ }
+ fragStart += chunkSize;
+ remaining -= chunkSize;
+ }
+ }
+
+ return PACKET_SERVICE_ATTEMPT_OK;
+ }
+ return PACKET_SERVICE_ATTEMPT_SEND_FAILED;
+ }
+ return PACKET_SERVICE_ATTEMPT_PEER_UNKNOWN;
+}
+
+void Switch::_retryPendingFor(const Address &addr)
+{
+ {
+ Mutex::Lock _l(_txQueue_m);
+ std::pair< std::multimap< Address,TXQueueEntry >::iterator,std::multimap< Address,TXQueueEntry >::iterator > eqrange = _txQueue.equal_range(addr);
+ for(std::multimap< Address,TXQueueEntry >::iterator i(eqrange.first);i!=eqrange.second;) {
+ if (_trySend(i->second.packet,i->second.encrypt) == PACKET_SERVICE_ATTEMPT_OK)
+ _txQueue.erase(i++);
+ else ++i;
+ }
+ }
+ {
+ Mutex::Lock _l(_rxQueue_m);
+ std::pair< std::multimap< Address,RXQueueEntry >::iterator,std::multimap< Address,RXQueueEntry >::iterator > eqrange = _rxQueue.equal_range(addr);
+ for(std::multimap< Address,RXQueueEntry >::iterator i(eqrange.first);i!=eqrange.second;) {
+ if (_tryHandleRemotePacket(i->second.localPort,i->second.fromAddr,i->second.packet) == PACKET_SERVICE_ATTEMPT_OK)
+ _rxQueue.erase(i++);
+ else ++i;
+ }
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/Switch.hpp b/node/Switch.hpp
new file mode 100644
index 00000000..f9244cba
--- /dev/null
+++ b/node/Switch.hpp
@@ -0,0 +1,260 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_N_SWITCH_HPP
+#define _ZT_N_SWITCH_HPP
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "Mutex.hpp"
+#include "MAC.hpp"
+#include "NonCopyable.hpp"
+#include "Constants.hpp"
+#include "Packet.hpp"
+#include "Utils.hpp"
+#include "InetAddress.hpp"
+#include "Topology.hpp"
+#include "Array.hpp"
+#include "Network.hpp"
+#include "SharedPtr.hpp"
+#include "Demarc.hpp"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+class EthernetTap;
+class Logger;
+class Node;
+class Peer;
+
+/**
+ * Core of the distributed Ethernet switch and protocol implementation
+ */
+class Switch : NonCopyable
+{
+public:
+ Switch(const RuntimeEnvironment *renv);
+ ~Switch();
+
+ /**
+ * Called when a packet is received from the real network
+ *
+ * @param localPort Local port on which packet was received
+ * @param fromAddr Internet IP address of origin
+ * @param data Packet data
+ */
+ void onRemotePacket(Demarc::Port localPort,const InetAddress &fromAddr,const Buffer<4096> &data);
+
+ /**
+ * Called when a packet comes from a local Ethernet tap
+ *
+ * @param network Which network's TAP did this packet come from?
+ * @param from Originating MAC address
+ * @param to Destination MAC address
+ * @param etherType Ethernet packet type
+ * @param data Ethernet payload
+ */
+ void onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data);
+
+ /**
+ * Send a packet to a ZeroTier address (destination in packet)
+ *
+ * The packet must be fully composed with source and destination but not
+ * yet encrypted. If the destination peer is known the packet
+ * is sent immediately. Otherwise it is queued and a WHOIS is dispatched.
+ *
+ * The packet may be compressed. Compression isn't done here.
+ *
+ * Needless to say, the packet's source must be this node. Otherwise it
+ * won't be encrypted right. (This is not used for relaying.)
+ *
+ * @param packet Packet to send
+ * @param encrypt Encrypt packet payload? (always true except for HELLO)
+ */
+ void send(const Packet &packet,bool encrypt);
+
+ /**
+ * Send a HELLO announcement
+ *
+ * @param dest Address of destination
+ */
+ void sendHELLO(const Address &dest);
+
+ /**
+ * Send RENDEZVOUS to two peers to permit them to directly connect
+ *
+ * This only works if both peers are known, with known working direct
+ * links to this peer. The best link for each peer is sent to the other.
+ *
+ * A rate limiter is in effect via the _lastUniteAttempt map. If force
+ * is true, a unite attempt is made even if one has been made less than
+ * ZT_MIN_UNITE_INTERVAL milliseconds ago.
+ *
+ * @param p1 One of two peers (order doesn't matter)
+ * @param p2 Second of pair
+ * @param force If true, send now regardless of interval
+ */
+ bool unite(const Address &p1,const Address &p2,bool force);
+
+ /**
+ * Perform retries and other periodic timer tasks
+ *
+ * @return Number of milliseconds until doTimerTasks() should be run again
+ */
+ unsigned long doTimerTasks();
+
+ /**
+ * Announce multicast group memberships
+ *
+ * This efficiently announces memberships, sending single packets with
+ * many LIKEs.
+ *
+ * @param allMemberships Memberships for a number of networks
+ */
+ void announceMulticastGroups(const std::map< SharedPtr<Network>,std::set<MulticastGroup> > &allMemberships);
+
+private:
+ // Returned by _send() and _processRemotePacket() to indicate what happened
+ enum PacketServiceAttemptResult
+ {
+ PACKET_SERVICE_ATTEMPT_OK,
+ PACKET_SERVICE_ATTEMPT_PEER_UNKNOWN,
+ PACKET_SERVICE_ATTEMPT_SEND_FAILED
+ };
+
+ struct _CBaddPeerFromHello_Data
+ {
+ Switch *parent;
+ Address source;
+ InetAddress fromAddr;
+ int localPort;
+ unsigned int vMajor,vMinor,vRevision;
+ uint64_t helloPacketId;
+ uint64_t helloTimestamp;
+ };
+ static void _CBaddPeerFromHello(void *arg,const SharedPtr<Peer> &p,Topology::PeerVerifyResult result);
+ static void _CBaddPeerFromWhois(void *arg,const SharedPtr<Peer> &p,Topology::PeerVerifyResult result); // arg == this
+
+ void _propagateMulticast(const SharedPtr<Network> &network,unsigned char *bloom,const MulticastGroup &mg,unsigned int mcHops,unsigned int mcLoadFactor,const MAC &from,unsigned int etherType,const void *data,unsigned int len);
+ PacketServiceAttemptResult _tryHandleRemotePacket(Demarc::Port localPort,const InetAddress &fromAddr,Packet &packet);
+ void _doHELLO(Demarc::Port localPort,const InetAddress &fromAddr,Packet &packet);
+ void _requestWhois(const Address &addr);
+ Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
+ PacketServiceAttemptResult _trySend(const Packet &packet,bool encrypt);
+ void _retryPendingFor(const Address &addr);
+
+ // Updates entry for crc in multicast history, returns true if already
+ // present in history and not expired.
+ inline bool _checkAndUpdateMulticastHistory(const MAC &fromMac,const MAC &toMulticastMac,const void *payload,unsigned int len,const uint64_t nwid,const uint64_t now)
+ {
+ uint64_t crc = Utils::crc64(0,fromMac.data,6);
+ crc = Utils::crc64(crc,toMulticastMac.data,6);
+ crc = Utils::crc64(crc,payload,len);
+ crc += nwid; // also include network ID
+
+ uint64_t earliest = 0xffffffffffffffffULL;
+ unsigned long earliestIdx = 0;
+ for(unsigned int i=0;i<ZT_MULTICAST_DEDUP_HISTORY_LENGTH;++i) {
+ if (_multicastHistory[i][0] == crc) {
+ uint64_t then = _multicastHistory[i][1];
+ _multicastHistory[i][1] = now;
+ return ((now - then) < ZT_MULTICAST_DEDUP_HISTORY_EXPIRE);
+ } else if (_multicastHistory[i][1] < earliest) {
+ earliest = _multicastHistory[i][1];
+ earliestIdx = i;
+ }
+ }
+
+ _multicastHistory[earliestIdx][0] = crc; // replace oldest entry
+ _multicastHistory[earliestIdx][1] = now;
+
+ return false;
+ }
+
+ const RuntimeEnvironment *const _r;
+
+ // Multicast packet CRC64's for packets we've received recently, to reject
+ // duplicates during propagation. [0] is CRC64, [1] is time.
+ uint64_t _multicastHistory[ZT_MULTICAST_DEDUP_HISTORY_LENGTH][2];
+
+ struct WhoisRequest
+ {
+ uint64_t lastSent;
+ Address peersConsulted[ZT_MAX_WHOIS_RETRIES]; // by retry
+ unsigned int retries; // 0..ZT_MAX_WHOIS_RETRIES
+ };
+ std::map< Address,WhoisRequest > _outstandingWhoisRequests;
+ Mutex _outstandingWhoisRequests_m;
+
+ struct TXQueueEntry
+ {
+ uint64_t creationTime;
+ Packet packet; // unencrypted/untagged for TX queue
+ bool encrypt;
+ };
+ std::multimap< Address,TXQueueEntry > _txQueue; // by destination address
+ Mutex _txQueue_m;
+
+ struct RXQueueEntry
+ {
+ uint64_t creationTime;
+ Demarc::Port localPort;
+ Packet packet; // encrypted/tagged
+ InetAddress fromAddr;
+ };
+ std::multimap< Address,RXQueueEntry > _rxQueue; // by source address
+ Mutex _rxQueue_m;
+
+ struct DefragQueueEntry
+ {
+ uint64_t creationTime;
+ Packet frag0;
+ Packet::Fragment frags[ZT_MAX_PACKET_FRAGMENTS - 1];
+ unsigned int totalFragments; // 0 if only frag0 received, waiting for frags
+ uint32_t haveFragments; // bit mask, LSB to MSB
+ };
+ std::map< uint64_t,DefragQueueEntry > _defragQueue;
+ Mutex _defragQueue_m;
+
+ std::map< Array< Address,2 >,uint64_t > _lastUniteAttempt; // key is always sorted in ascending order, for set-like behavior
+ Mutex _lastUniteAttempt_m;
+
+ struct RendezvousQueueEntry
+ {
+ InetAddress inaddr;
+ uint64_t fireAtTime;
+ Demarc::Port localPort;
+ };
+ std::map< Address,RendezvousQueueEntry > _rendezvousQueue;
+ Mutex _rendezvousQueue_m;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/SysEnv.cpp b/node/SysEnv.cpp
new file mode 100644
index 00000000..016e9caa
--- /dev/null
+++ b/node/SysEnv.cpp
@@ -0,0 +1,219 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <set>
+#include <string>
+
+#include "SysEnv.hpp"
+#include "Utils.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "NodeConfig.hpp"
+
+#ifdef __APPLE__
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <net/route.h>
+#endif
+
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <unistd.h>
+#include <signal.h>
+#endif
+
+namespace ZeroTier {
+
+SysEnv::SysEnv(const RuntimeEnvironment *renv) :
+ _r(renv)
+{
+}
+
+SysEnv::~SysEnv()
+{
+}
+
+#ifdef __APPLE__
+
+uint64_t SysEnv::getNetworkConfigurationFingerprint()
+ throw()
+{
+ int mib[6];
+ size_t needed;
+ uint64_t fingerprint = 5381; // djb2 hash algorithm is used below
+
+ // Right now this just scans for changes in default routes. This is not
+ // totally robust -- it will miss cases where we switch from one 10.0.0.0/24
+ // network with gateway .1 to another -- but most of the time it'll pick
+ // up shifts in connectivity.
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_UNSPEC;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ if (!sysctl(mib,6,NULL,&needed,NULL,0)) {
+ char *buf = (char *)malloc(needed);
+ if (buf) {
+ if (!sysctl(mib,6,buf,&needed,NULL,0)) {
+ struct rt_msghdr *rtm;
+ for(char *next=buf,*end=buf+needed;next<end;) {
+ rtm = (struct rt_msghdr *)next;
+ char *saptr = (char *)(rtm + 1);
+ char *saend = next + rtm->rtm_msglen;
+ if (((rtm->rtm_addrs & RTA_DST))&&((rtm->rtm_addrs & RTA_GATEWAY))) {
+ int sano = 0;
+ struct sockaddr *dst = (struct sockaddr *)0;
+ struct sockaddr *gateway = (struct sockaddr *)0;
+ while (saptr < saend) {
+ struct sockaddr *sa = (struct sockaddr *)saptr;
+ if (!sa->sa_len)
+ break;
+ if (sano == 0)
+ dst = sa;
+ else if (sano == 1)
+ gateway = sa;
+ else if (sano > 1)
+ break;
+ ++sano;
+ saptr += sa->sa_len;
+ }
+ if ((dst)&&(gateway)) {
+ if ((dst->sa_family == AF_INET)&&(gateway->sa_family == AF_INET)&&(!((struct sockaddr_in *)dst)->sin_addr.s_addr)) {
+ fingerprint = ((fingerprint << 5) + fingerprint) + (uint64_t)((struct sockaddr_in *)gateway)->sin_addr.s_addr;
+ } else if ((dst->sa_family == AF_INET6)&&(gateway->sa_family == AF_INET6)&&(Utils::isZero(((struct sockaddr_in6 *)dst)->sin6_addr.s6_addr,16))) {
+ for(unsigned int i=0;i<16;++i)
+ fingerprint = ((fingerprint << 5) + fingerprint) + (uint64_t)((struct sockaddr_in6 *)gateway)->sin6_addr.s6_addr[i];
+ }
+ }
+ }
+ next = saend;
+ }
+ }
+ free(buf);
+ }
+ }
+
+ return fingerprint;
+}
+
+#endif // __APPLE__
+
+#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+
+uint64_t SysEnv::getNetworkConfigurationFingerprint()
+ throw()
+{
+ char buf[16384];
+ uint64_t fingerprint = 5381; // djb2 hash algorithm is used below
+ char *t1,*t2;
+
+ try {
+ std::set<std::string> tapDevs(_r->nc->networkTapDeviceNames());
+
+ // Include default IPv4 route if available
+ int fd = open("/proc/net/route",O_RDONLY);
+ if (fd > 0) {
+ long n = read(fd,buf,sizeof(buf) - 1);
+ ::close(fd);
+ if (n > 0) {
+ buf[n] = 0;
+ for(char *line=strtok_r(buf,"\r\n",&t1);(line);line=strtok_r((char *)0,"\r\n",&t1)) {
+ int fno = 0;
+ for(char *field=strtok_r(line," \t",&t2);(field);field=strtok_r((char *)0," \t",&t2)) {
+ if (fno == 0) { // device name
+ if ((tapDevs.count(std::string(field)))||(!strcmp(field,"lo")))
+ break;
+ } else if ((fno == 1)||(fno == 2)) { // destination, gateway
+ if (strlen(field) == 8) { // ignore header junk, use only hex route info
+ while (*field)
+ fingerprint = ((fingerprint << 5) + fingerprint) + (uint64_t)*(field++);
+ }
+ } else if (fno > 2)
+ break;
+ ++fno;
+ }
+ }
+ }
+ }
+
+ // Include IPs of IPv6 enabled interfaces if available
+ fd = open("/proc/net/if_inet6",O_RDONLY);
+ if (fd > 0) {
+ long n = read(fd,buf,sizeof(buf) - 1);
+ ::close(fd);
+ if (n > 0) {
+ buf[n] = 0;
+ for(char *line=strtok_r(buf,"\r\n",&t1);(line);line=strtok_r((char *)0,"\r\n",&t1)) {
+ int fno = 0;
+ const char *v6ip = (const char *)0;
+ const char *devname = (const char *)0;
+ for(char *field=strtok_r(line," \t",&t2);(field);field=strtok_r((char *)0," \t",&t2)) {
+ switch(fno) {
+ case 0:
+ v6ip = field;
+ break;
+ case 5:
+ devname = field;
+ break;
+ }
+ ++fno;
+ }
+
+ if ((v6ip)&&(devname)) {
+ if ((!(tapDevs.count(std::string(devname))))&&(strcmp(devname,"lo"))) {
+ while (*v6ip)
+ fingerprint = ((fingerprint << 5) + fingerprint) + (uint64_t)*(v6ip++);
+ }
+ }
+ }
+ }
+ }
+ } catch ( ... ) {}
+
+ return fingerprint;
+}
+
+#endif // __linux__
+
+#ifdef _WIN32
+
+not implemented yet;
+
+#endif // _WIN32
+
+} // namespace ZeroTier
diff --git a/node/SysEnv.hpp b/node/SysEnv.hpp
new file mode 100644
index 00000000..af3efa5b
--- /dev/null
+++ b/node/SysEnv.hpp
@@ -0,0 +1,58 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_SYSENV_HPP
+#define _ZT_SYSENV_HPP
+
+#include <stdint.h>
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * Local system environment monitoring utilities
+ */
+class SysEnv
+{
+public:
+ SysEnv(const RuntimeEnvironment *renv);
+ ~SysEnv();
+
+ /**
+ * @return Fingerprint of currently running network environment
+ */
+ uint64_t getNetworkConfigurationFingerprint()
+ throw();
+
+private:
+ const RuntimeEnvironment *_r;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Thread.cpp b/node/Thread.cpp
new file mode 100644
index 00000000..37a1a5a5
--- /dev/null
+++ b/node/Thread.cpp
@@ -0,0 +1,192 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include "Thread.hpp"
+
+#if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <stdexcept>
+
+extern "C" {
+static void *__m_thread_main(void *ptr)
+{
+ ((ZeroTier::Thread *)ptr)->__intl_run();
+ return (void *)0;
+}
+}
+
+namespace ZeroTier {
+
+Thread::Thread() :
+ suicidalThread(false),
+ _impl(malloc(sizeof(pthread_t))),
+ _running()
+{
+ memset(_impl,0,sizeof(pthread_t));
+}
+
+Thread::~Thread()
+{
+ free(_impl);
+}
+
+void Thread::start()
+{
+ if (!*_running) {
+ ++_running;
+ pthread_create((pthread_t *)_impl,(const pthread_attr_t *)0,&__m_thread_main,(void *)this);
+ }
+}
+
+void Thread::join()
+{
+ void *tmp;
+ if (*_running)
+ pthread_join(*((pthread_t *)_impl),&tmp);
+}
+
+void Thread::sleep(unsigned long ms)
+{
+ usleep(ms);
+}
+
+void Thread::__intl_run()
+{
+ for(;;) {
+ _notInit = false;
+ this->main();
+ if (suicidalThread) {
+ delete this;
+ return;
+ }
+ if (_notInit) // UGLY ASS HACK: see main()
+ usleep(50);
+ else break;
+ }
+ --_running;
+}
+
+void Thread::main()
+ throw()
+{
+ _notInit = true; // UGLY ASS HACK: retry if subclass has not defined virtual function pointer yet
+}
+
+} // namespace ZeroTier
+
+#endif
+
+#ifdef _WIN32
+
+#include <Windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+DWORD WINAPI __m_thread_main(LPVOID lpParam)
+{
+ ((ZeroTier::Thread *)lpParam)->__intl_run();
+ return 0;
+}
+
+struct __m_thread_info
+{
+ HANDLE threadHandle;
+ DWORD threadId;
+ bool started;
+};
+
+namespace ZeroTier {
+
+Thread::Thread() :
+ suicidalThread(false),
+ _impl(malloc(sizeof(__m_thread_info))),
+ _running()
+{
+ memset(_impl,0,sizeof(__m_thread_info));
+}
+
+Thread::~Thread()
+{
+ if (((__m_thread_info *)_impl)->started)
+ CloseHandle(((__m_thread_info *)_impl)->threadHandle);
+ free(_impl);
+}
+
+void Thread::start()
+{
+ if (!*_running) {
+ ++_running;
+ if ((((__m_thread_info *)_impl)->threadHandle = CreateThread(NULL,0,__m_thread_main,this,0,&(((__m_thread_info *)_impl)->threadId))) != NULL) {
+ ((__m_thread_info *)_impl)->started = true;
+ }
+ }
+}
+
+void Thread::join()
+{
+ if (*_running)
+ WaitForSingleObject(((__m_thread_info *)_impl)->threadHandle,INFINITE);
+}
+
+void Thread::__intl_run()
+{
+ for(;;) {
+ _notInit = false;
+ this->main();
+ if (suicidalThread) {
+ delete this;
+ return;
+ }
+ if (_notInit)
+ Thread::sleep(50);
+ else break;
+ }
+ --_running;
+}
+
+void Thread::main()
+ throw()
+{
+ _notInit = true; // HACK: retry if subclass has not defined virtual function pointer yet
+}
+
+struct _Thread_RunInBackgroundData
+{
+ void (*func)(void *);
+ void *ptr;
+ HANDLE threadHandle;
+ DWORD threadId;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Thread.hpp b/node/Thread.hpp
new file mode 100644
index 00000000..b023fbae
--- /dev/null
+++ b/node/Thread.hpp
@@ -0,0 +1,94 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_THREAD_HPP
+#define _ZT_THREAD_HPP
+
+#include "NonCopyable.hpp"
+#include "AtomicCounter.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Wrapper for OS-dependent thread functions like pthread_create, etc.
+ */
+class Thread : NonCopyable
+{
+public:
+ Thread();
+ virtual ~Thread();
+
+ /**
+ * Start thread -- can only be called once
+ */
+ void start();
+
+ /**
+ * Wait for thread to terminate
+ *
+ * More than one thread should not simultaneously use join().
+ */
+ void join();
+
+ /**
+ * @return True if thread is running
+ */
+ inline bool running() const { return (*_running > 0); }
+
+ /**
+ * Internal bounce method; do not call or override
+ */
+ void __intl_run();
+
+ /**
+ * Sleep the current thread
+ *
+ * @param ms Milliseconds to sleep
+ */
+ static void sleep(unsigned long ms);
+
+protected:
+ /**
+ * Override to set a thread main function
+ */
+ virtual void main()
+ throw();
+
+ /**
+ * Subclasses can set to true to cause Thread to delete itself on exit
+ */
+ volatile bool suicidalThread;
+
+private:
+ void *_impl;
+ AtomicCounter _running;
+ volatile bool _notInit;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Topology.cpp b/node/Topology.cpp
new file mode 100644
index 00000000..cbe42e8f
--- /dev/null
+++ b/node/Topology.cpp
@@ -0,0 +1,443 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include "Topology.hpp"
+#include "NodeConfig.hpp"
+
+namespace ZeroTier {
+
+#define ZT_KISSDB_HASH_TABLE_SIZE 131072
+#define ZT_KISSDB_KEY_SIZE ZT_ADDRESS_LENGTH
+#define ZT_KISSDB_VALUE_SIZE ZT_PEER_MAX_SERIALIZED_LENGTH
+
+Topology::Topology(const RuntimeEnvironment *renv,const char *dbpath)
+ throw(std::runtime_error) :
+ Thread(),
+ _r(renv)
+{
+ if (KISSDB_open(&_dbm,dbpath,KISSDB_OPEN_MODE_RWCREAT,ZT_KISSDB_HASH_TABLE_SIZE,ZT_KISSDB_KEY_SIZE,ZT_KISSDB_VALUE_SIZE)) {
+ if (KISSDB_open(&_dbm,dbpath,KISSDB_OPEN_MODE_RWREPLACE,ZT_KISSDB_HASH_TABLE_SIZE,ZT_KISSDB_KEY_SIZE,ZT_KISSDB_VALUE_SIZE))
+ throw std::runtime_error("unable to open peer database (rw/create)");
+ }
+
+ if ((_dbm.key_size != ZT_KISSDB_KEY_SIZE)||(_dbm.value_size != ZT_KISSDB_VALUE_SIZE)||(_dbm.hash_table_size != ZT_KISSDB_HASH_TABLE_SIZE)) {
+ KISSDB_close(&_dbm);
+ if (KISSDB_open(&_dbm,dbpath,KISSDB_OPEN_MODE_RWREPLACE,ZT_KISSDB_HASH_TABLE_SIZE,ZT_KISSDB_KEY_SIZE,ZT_KISSDB_VALUE_SIZE))
+ throw std::runtime_error("unable to open peer database (recreate)");
+ }
+
+ Utils::lockDownFile(dbpath,false); // node.db caches secrets
+
+ start();
+}
+
+Topology::~Topology()
+{
+ {
+ Mutex::Lock _l(_peerDeepVerifyJobs_m);
+ _peerDeepVerifyJobs.push_back(_PeerDeepVerifyJob());
+ _peerDeepVerifyJobs.back().type = _PeerDeepVerifyJob::CLEAN_CACHE;
+ _peerDeepVerifyJobs.push_back(_PeerDeepVerifyJob());
+ _peerDeepVerifyJobs.back().type = _PeerDeepVerifyJob::EXIT_THREAD;
+ }
+ _peerDeepVerifyJobs_c.signal();
+
+ while (running())
+ Thread::sleep(10); // wait for thread to terminate without join()
+
+ KISSDB_close(&_dbm);
+}
+
+void Topology::setSupernodes(const std::map< Identity,std::vector<InetAddress> > &sn)
+{
+ Mutex::Lock _l(_supernodes_m);
+ _supernodes = sn;
+ _supernodeAddresses.clear();
+ _supernodePeers.clear();
+ for(std::map< Identity,std::vector<InetAddress> >::const_iterator i(sn.begin());i!=sn.end();++i) {
+ if (i->first != _r->identity) {
+ SharedPtr<Peer> p(getPeer(i->first.address()));
+ if ((!p)||(p->identity() != i->first)) {
+ p = SharedPtr<Peer>(new Peer(_r->identity,i->first));
+ _reallyAddPeer(p);
+ }
+ for(std::vector<InetAddress>::const_iterator j(i->second.begin());j!=i->second.end();++j)
+ p->setPathAddress(*j,true);
+ _supernodePeers.push_back(p);
+ }
+ _supernodeAddresses.insert(i->first.address());
+ }
+}
+
+void Topology::addPeer(const SharedPtr<Peer> &candidate,void (*callback)(void *,const SharedPtr<Peer> &,Topology::PeerVerifyResult),void *arg)
+{
+ if (candidate->address() != _r->identity.address()) {
+ Mutex::Lock _l(_peerDeepVerifyJobs_m);
+ _peerDeepVerifyJobs.push_back(_PeerDeepVerifyJob());
+ _PeerDeepVerifyJob &job = _peerDeepVerifyJobs.back();
+ job.callback = callback;
+ job.arg = arg;
+ job.candidate = candidate;
+ job.type = _PeerDeepVerifyJob::VERIFY_PEER;
+ _peerDeepVerifyJobs_c.signal();
+ } else {
+ TRACE("BUG: addPeer() caught and ignored attempt to add peer for self");
+ if (callback)
+ callback(arg,candidate,PEER_VERIFY_REJECTED_DUPLICATE_TRIAGED);
+ }
+}
+
+SharedPtr<Peer> Topology::getPeer(const Address &zta)
+{
+ if (zta == _r->identity.address()) {
+ TRACE("BUG: ignored attempt to getPeer() for self, returned NULL");
+ return SharedPtr<Peer>();
+ }
+
+ {
+ Mutex::Lock _l(_activePeers_m);
+ std::map< Address,SharedPtr<Peer> >::const_iterator ap(_activePeers.find(zta));
+ if ((ap != _activePeers.end())&&(ap->second))
+ return ap->second;
+ }
+
+ Buffer<ZT_KISSDB_VALUE_SIZE> b(ZT_KISSDB_VALUE_SIZE);
+ _dbm_m.lock();
+ if (!KISSDB_get(&_dbm,zta.data(),b.data())) {
+ _dbm_m.unlock();
+
+ SharedPtr<Peer> p(new Peer());
+ try {
+ p->deserialize(b,0);
+ Mutex::Lock _l(_activePeers_m);
+ _activePeers[zta] = p;
+ return p;
+ } catch ( ... ) {
+ TRACE("unexpected exception deserializing peer %s from peerdb",zta.toString().c_str());
+ return SharedPtr<Peer>();
+ }
+ } else _dbm_m.unlock();
+
+ return SharedPtr<Peer>();
+}
+
+SharedPtr<Peer> Topology::getBestSupernode(const Address *avoid,unsigned int avoidCount) const
+{
+ SharedPtr<Peer> bestSupernode;
+ unsigned long bestSupernodeLatency = 0xffff;
+ uint64_t now = Utils::now();
+
+ Mutex::Lock _l(_supernodes_m);
+
+ for(std::vector< SharedPtr<Peer> >::const_iterator sn=_supernodePeers.begin();sn!=_supernodePeers.end();) {
+ for(unsigned int i=0;i<avoidCount;++i) {
+ if (avoid[i] == (*sn)->address())
+ goto skip_and_try_next_supernode;
+ }
+ if ((*sn)->hasActiveDirectPath(now)) { // only consider those that responded to pings
+ unsigned int l = (*sn)->latency();
+ if ((l)&&(l <= bestSupernodeLatency)) {
+ bestSupernodeLatency = l;
+ bestSupernode = *sn;
+ }
+ }
+skip_and_try_next_supernode:
+ ++sn;
+ }
+
+ if (bestSupernode)
+ return bestSupernode;
+
+ for(std::vector< SharedPtr<Peer> >::const_iterator sn=_supernodePeers.begin();sn!=_supernodePeers.end();++sn) {
+ if ((*sn)->hasActiveDirectPath(now)) { // only consider those that responded to pings
+ unsigned int l = (*sn)->latency();
+ if ((l)&&(l <= bestSupernodeLatency)) {
+ bestSupernodeLatency = l;
+ bestSupernode = *sn;
+ }
+ }
+ }
+
+ if (bestSupernode)
+ return bestSupernode;
+
+ uint64_t bestSupernodeLastDirectReceive = 0;
+ for(std::vector< SharedPtr<Peer> >::const_iterator sn=_supernodePeers.begin();sn!=_supernodePeers.end();++sn) {
+ uint64_t l = (*sn)->lastDirectReceive();
+ if (l > bestSupernodeLastDirectReceive) {
+ bestSupernodeLastDirectReceive = l;
+ bestSupernode = *sn;
+ }
+ }
+
+ return bestSupernode;
+}
+
+void Topology::clean()
+{
+ {
+ Mutex::Lock _l(_peerDeepVerifyJobs_m);
+ _peerDeepVerifyJobs.push_back(_PeerDeepVerifyJob());
+ _peerDeepVerifyJobs.back().type = _PeerDeepVerifyJob::CLEAN_CACHE;
+ }
+ _peerDeepVerifyJobs_c.signal();
+}
+
+void Topology::likesMulticastGroup(uint64_t nwid,const MulticastGroup &mg,const Address &addr,uint64_t now)
+{
+ Mutex::Lock _l(_multicastGroupMembers_m);
+ _multicastGroupMembers[nwid][mg][addr] = now;
+}
+
+struct _PickMulticastPropagationPeersPeerPrioritySortOrder
+{
+ inline bool operator()(const SharedPtr<Peer> &p1,const SharedPtr<Peer> &p2) const
+ {
+ return (p1->lastUnicastFrame() >= p2->lastUnicastFrame());
+ }
+};
+#define _MAX_PEERS_TO_CONSIDER 256
+unsigned int Topology::pickMulticastPropagationPeers(uint64_t nwid,const Address &exclude,const void *propagationBloom,unsigned int propagationBloomSize,unsigned int count,const MulticastGroup &mg,SharedPtr<Peer> *peers)
+{
+ SharedPtr<Peer> possiblePeers[_MAX_PEERS_TO_CONSIDER];
+ unsigned int numPossiblePeers = 0;
+
+ if (count > _MAX_PEERS_TO_CONSIDER)
+ count = _MAX_PEERS_TO_CONSIDER;
+
+ Mutex::Lock _l1(_activePeers_m);
+ Mutex::Lock _l2(_supernodes_m);
+
+ // Grab known non-supernode peers in multicast group, excluding 'exclude'
+ // Also lazily clean up the _multicastGroupMembers structure
+ {
+ Mutex::Lock _l3(_multicastGroupMembers_m);
+ std::map< uint64_t,std::map< MulticastGroup,std::map< Address,uint64_t > > >::iterator mgm(_multicastGroupMembers.find(nwid));
+ if (mgm != _multicastGroupMembers.end()) {
+ std::map< MulticastGroup,std::map< Address,uint64_t > >::iterator g(mgm->second.find(mg));
+ if (g != mgm->second.end()) {
+ uint64_t now = Utils::now();
+ for(std::map< Address,uint64_t >::iterator m(g->second.begin());m!=g->second.end();) {
+ if ((now - m->second) < ZT_MULTICAST_LIKE_EXPIRE) {
+ std::map< Address,SharedPtr<Peer> >::const_iterator p(_activePeers.find(m->first));
+ if (p != _activePeers.end()) {
+ possiblePeers[numPossiblePeers++] = p->second;
+ if (numPossiblePeers > _MAX_PEERS_TO_CONSIDER)
+ break;
+ }
+ ++m;
+ } else g->second.erase(m++);
+ }
+ if (!g->second.size())
+ mgm->second.erase(g);
+ }
+ }
+ }
+
+ // Sort non-supernode peers in descending order of most recent data
+ // exchange timestamp. This sorts by implicit social relationships -- who
+ // you are talking to are the people who get multicasts first.
+ std::sort(&(possiblePeers[0]),&(possiblePeers[numPossiblePeers]),_PickMulticastPropagationPeersPeerPrioritySortOrder());
+
+ // Tack on a supernode peer to the end if we don't have enough regular
+ // peers, using supernodes to bridge gaps in sparse multicast groups.
+ if (numPossiblePeers < count) {
+ SharedPtr<Peer> bestSupernode;
+ unsigned int bestSupernodeLatency = 0xffff;
+ for(std::vector< SharedPtr<Peer> >::const_iterator sn(_supernodePeers.begin());sn!=_supernodePeers.end();++sn) {
+ if (((*sn)->latency())&&((*sn)->latency() < bestSupernodeLatency)) {
+ bestSupernodeLatency = (*sn)->latency();
+ bestSupernode = *sn;
+ }
+ }
+ if (bestSupernode)
+ possiblePeers[numPossiblePeers++] = bestSupernode;
+ }
+
+ unsigned int num = 0;
+
+ // First, try to pick peers not in the propgation bloom filter
+ for(unsigned int i=0;i<numPossiblePeers;++i) {
+ if (!Utils::bloomContains(propagationBloom,propagationBloomSize,possiblePeers[i]->address().sum())) {
+ peers[num++] = possiblePeers[i];
+ if (num >= count)
+ return num;
+ }
+ }
+
+ // Next, pick other peers until full (without duplicates)
+ for(unsigned int i=0;i<numPossiblePeers;++i) {
+ for(unsigned int j=0;j<num;++j) {
+ if (peers[j] == possiblePeers[i])
+ goto check_next_peer;
+ }
+ peers[num++] = possiblePeers[i];
+ if (num >= count)
+ return num;
+check_next_peer:
+ continue;
+ }
+
+ return num;
+}
+
+void Topology::main()
+ throw()
+{
+ for(;;) {
+ _peerDeepVerifyJobs_m.lock();
+ if (_peerDeepVerifyJobs.empty()) {
+ _peerDeepVerifyJobs_m.unlock();
+ _peerDeepVerifyJobs_c.wait();
+ continue;
+ }
+ _PeerDeepVerifyJob job(_peerDeepVerifyJobs.front());
+ _peerDeepVerifyJobs.pop_front();
+ unsigned long queueRemaining = _peerDeepVerifyJobs.size();
+ _peerDeepVerifyJobs_m.unlock();
+
+ switch(job.type) {
+ case _PeerDeepVerifyJob::VERIFY_PEER:
+ /* TODO: We should really verify peers every time completely if this
+ * is a supernode, perhaps deferring the expensive part for new
+ * addresses. An attempt at claim jumping should also trigger a
+ * short duration ban of the originating IP address in most cases,
+ * since this means either malicious intent or broken software. */
+ TRACE("verifying peer: %s",job.candidate->identity().address().toString().c_str());
+
+ if ((job.candidate->identity())&&(!job.candidate->identity().address().isReserved())&&(job.candidate->identity().locallyValidate(false))) {
+ // Peer passes sniff test, so check to see if we've already got
+ // one with the same address.
+
+ SharedPtr<Peer> existingPeer(getPeer(job.candidate->identity().address()));
+
+ if (existingPeer) {
+ if (existingPeer->identity() == job.candidate->identity()) {
+ // It's an *exact* duplicate, so return the existing peer
+ if (job.callback)
+ job.callback(job.arg,existingPeer,PEER_VERIFY_ACCEPTED_ALREADY_HAVE);
+ } else if (queueRemaining > 3) {
+ /* Prevents a CPU hog DOS attack, while allowing a very unlikely kind of
+ * DOS attack where someone knows someone else's address prior to their
+ * registering it and claim-jumps them and then floods with bad identities
+ * to hold their claim. Of the two, the latter would be infeasable
+ * without already having cracked the target's machine in which case
+ * the attacker has their private key anyway and can really steal their
+ * identity. So why bother.*/
+ TRACE("%s is duplicate, load too high, old won",job.candidate->identity().address().toString().c_str());
+ if (job.callback)
+ job.callback(job.arg,job.candidate,PEER_VERIFY_REJECTED_DUPLICATE_TRIAGED);
+ } else {
+ // It's different so deeply validate it first, then the
+ // existing claimant, and toss the imposter. If both verify, the
+ // one we already have wins.
+
+ if (!job.candidate->identity().locallyValidate(true)) {
+ LOG("Topology: IMPOSTER %s rejected",job.candidate->identity().address().toString().c_str());
+ if (job.callback)
+ job.callback(job.arg,job.candidate,PEER_VERIFY_REJECTED_INVALID_IDENTITY);
+ } else if (!existingPeer->identity().locallyValidate(true)) {
+ LOG("Topology: previous IMPOSTER %s displaced by valid identity!",job.candidate->identity().address().toString().c_str());
+ _reallyAddPeer(job.candidate);
+ if (job.callback)
+ job.callback(job.arg,job.candidate,PEER_VERIFY_ACCEPTED_DISPLACED_INVALID_ADDRESS);
+ } else {
+ LOG("Topology: tie between apparently valid claims on %s, oldest won",job.candidate->identity().address().toString().c_str());
+ if (job.callback)
+ job.callback(job.arg,job.candidate,PEER_VERIFY_REJECTED_DUPLICATE);
+ }
+ }
+ } else {
+ TRACE("%s accepted as new",job.candidate->identity().address().toString().c_str());
+ _reallyAddPeer(job.candidate);
+ if (job.callback)
+ job.callback(job.arg,job.candidate,PEER_VERIFY_ACCEPTED_NEW);
+ }
+ } else {
+ TRACE("%s rejected, identity failed initial checks",job.candidate->identity().address().toString().c_str());
+ if (job.callback)
+ job.callback(job.arg,job.candidate,PEER_VERIFY_REJECTED_INVALID_IDENTITY);
+ }
+ break;
+ case _PeerDeepVerifyJob::CLEAN_CACHE:
+ TRACE("cleaning caches and flushing modified peers to disk...");
+ {
+ Mutex::Lock _l(_activePeers_m);
+ for(std::map< Address,SharedPtr<Peer> >::iterator p(_activePeers.begin());p!=_activePeers.end();++p) {
+ if (p->second->getAndResetDirty()) {
+ try {
+ Buffer<ZT_PEER_MAX_SERIALIZED_LENGTH> b;
+ p->second->serialize(b);
+ b.zeroUnused();
+ _dbm_m.lock();
+ if (KISSDB_put(&_dbm,p->second->identity().address().data(),b.data())) {
+ TRACE("error writing %s to peer.db",p->second->identity().address().toString().c_str());
+ }
+ _dbm_m.unlock();
+ } catch ( ... ) {
+ TRACE("unexpected exception flushing %s to peer.db",p->second->identity().address().toString().c_str());
+ }
+ }
+ }
+ }
+ {
+ Mutex::Lock _l(_multicastGroupMembers_m);
+ for(std::map< uint64_t,std::map< MulticastGroup,std::map< Address,uint64_t > > >::iterator mgm(_multicastGroupMembers.begin());mgm!=_multicastGroupMembers.end();) {
+ if (_r->nc->hasNetwork(mgm->first))
+ ++mgm;
+ else _multicastGroupMembers.erase(mgm++);
+ }
+ }
+ break;
+ case _PeerDeepVerifyJob::EXIT_THREAD:
+ TRACE("thread terminating...");
+ return;
+ }
+ }
+}
+
+void Topology::_reallyAddPeer(const SharedPtr<Peer> &p)
+{
+ {
+ Mutex::Lock _l(_activePeers_m);
+ _activePeers[p->identity().address()] = p;
+ }
+ try {
+ Buffer<ZT_PEER_MAX_SERIALIZED_LENGTH> b;
+ p->serialize(b);
+ b.zeroUnused();
+ _dbm_m.lock();
+ if (KISSDB_put(&_dbm,p->identity().address().data(),b.data())) {
+ TRACE("error writing %s to peerdb",p->address().toString().c_str());
+ } else p->getAndResetDirty();
+ _dbm_m.unlock();
+ } catch ( ... ) {
+ TRACE("unexpected exception flushing to peerdb");
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/Topology.hpp b/node/Topology.hpp
new file mode 100644
index 00000000..95124497
--- /dev/null
+++ b/node/Topology.hpp
@@ -0,0 +1,339 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_TOPOLOGY_HPP
+#define _ZT_TOPOLOGY_HPP
+
+#include <map>
+#include <set>
+#include <list>
+#include <vector>
+#include <stdexcept>
+
+#include "Address.hpp"
+#include "Peer.hpp"
+#include "Mutex.hpp"
+#include "Condition.hpp"
+#include "InetAddress.hpp"
+#include "Constants.hpp"
+#include "Thread.hpp"
+#include "MulticastGroup.hpp"
+#include "Utils.hpp"
+
+#include "../ext/kissdb/kissdb.h"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * Database of network topology
+ */
+class Topology : protected Thread
+{
+public:
+ /**
+ * Result of peer add/verify
+ */
+ enum PeerVerifyResult
+ {
+ PEER_VERIFY_ACCEPTED_NEW, /* new peer */
+ PEER_VERIFY_ACCEPTED_ALREADY_HAVE, /* we already knew ye */
+ PEER_VERIFY_ACCEPTED_DISPLACED_INVALID_ADDRESS, /* you booted out an impostor */
+ PEER_VERIFY_REJECTED_INVALID_IDENTITY, /* identity is invalid or validation failed */
+ PEER_VERIFY_REJECTED_DUPLICATE, /* someone equally valid already has your address */
+ PEER_VERIFY_REJECTED_DUPLICATE_TRIAGED /* you look duplicate and I'm too busy to deep verify */
+ };
+
+ Topology(const RuntimeEnvironment *renv,const char *dbpath)
+ throw(std::runtime_error);
+
+ virtual ~Topology();
+
+ /**
+ * Set up supernodes for this network
+ *
+ * @param sn Supernodes for this network
+ */
+ void setSupernodes(const std::map< Identity,std::vector<InetAddress> > &sn);
+
+ /**
+ * Add a peer to this network
+ *
+ * Verification and adding actually occurs in the background, since in
+ * rare cases it can be somewhat CPU-intensive. The callback will be
+ * called (from the background thread) when add is complete.
+ *
+ * The peer given to the callback may not be the same object provided
+ * as a candidate if the candidate was an exact duplicate of a peer we
+ * already have.
+ *
+ * @param candidate New candidate peer to be added
+ * @param callback Callback to call when peer verification is complete
+ * @param arg First argument to callback
+ * @return Verification result or PEER_VERIFY__IN_PROGRESS if occurring in background
+ */
+ void addPeer(const SharedPtr<Peer> &candidate,void (*callback)(void *,const SharedPtr<Peer> &,PeerVerifyResult),void *arg);
+
+ /**
+ * Get a peer from its address
+ *
+ * @param zta ZeroTier address of peer
+ * @return Peer or NULL if not found
+ */
+ SharedPtr<Peer> getPeer(const Address &zta);
+
+ /**
+ * @return Current network supernodes
+ */
+ inline std::map< Identity,std::vector<InetAddress> > supernodes() const
+ {
+ Mutex::Lock _l(_supernodes_m);
+ return _supernodes;
+ }
+
+ /**
+ * @return Vector of peers that are supernodes
+ */
+ inline std::vector< SharedPtr<Peer> > supernodePeers() const
+ {
+ Mutex::Lock _l(_supernodes_m);
+ return _supernodePeers;
+ }
+
+ /**
+ * Get the current favorite supernode
+ *
+ * @return Supernode with lowest latency or NULL if none
+ */
+ inline SharedPtr<Peer> getBestSupernode() const
+ {
+ return getBestSupernode((const Address *)0,0);
+ }
+
+ /**
+ * Get the best supernode, avoiding supernodes listed in an array
+ *
+ * This will get the best supernode (lowest latency, etc.) but will
+ * try to avoid the listed supernodes, only using them if no others
+ * are available.
+ *
+ * @param avoid Nodes to avoid
+ * @param avoidCount Number of nodes to avoid
+ * @return Supernode or NULL if none
+ */
+ SharedPtr<Peer> getBestSupernode(const Address *avoid,unsigned int avoidCount) const;
+
+ /**
+ * @param zta ZeroTier address
+ * @return True if this is a designated supernode
+ */
+ inline bool isSupernode(const Address &zta) const
+ throw()
+ {
+ Mutex::Lock _l(_supernodes_m);
+ return (_supernodeAddresses.count(zta) > 0);
+ }
+
+ /**
+ * Clean and flush database now (runs in the background)
+ */
+ void clean();
+
+ /**
+ * Pick peers for multicast propagation
+ *
+ * @param nwid Network ID
+ * @param exclude Peer to exclude or zero address for none
+ * @param propagationBloom Propgation bloom filter
+ * @param propagationBloomSize Size of propagation bloom filter in BITS
+ * @param count Number of peers desired (propagation breadth)
+ * @param mg Multicast group
+ * @param peers Array to receive peers (must be at least [count])
+ * @return Number of peers actually picked
+ */
+ unsigned int pickMulticastPropagationPeers(uint64_t nwid,const Address &exclude,const void *propagationBloom,unsigned int propagationBloomSize,unsigned int count,const MulticastGroup &mg,SharedPtr<Peer> *peers);
+
+ /**
+ * Add or update last 'like' time for an address's membership in a multicast group
+ *
+ * @param nwid Network ID
+ * @param mg Multicast group
+ * @param addr ZeroTier address
+ * @param now Current time
+ */
+ void likesMulticastGroup(uint64_t nwid,const MulticastGroup &mg,const Address &addr,uint64_t now);
+
+ /**
+ * Apply a function or function object to all peers
+ *
+ * @param f Function to apply
+ * @tparam F Function or function object type
+ */
+ template<typename F>
+ inline void eachPeer(F f)
+ {
+ Mutex::Lock _l(_activePeers_m);
+ for(std::map< Address,SharedPtr<Peer> >::const_iterator p(_activePeers.begin());p!=_activePeers.end();++p)
+ f(*this,p->second);
+ }
+
+ /**
+ * Function object to collect peers that need a firewall opener sent
+ */
+ class CollectPeersThatNeedFirewallOpener
+ {
+ public:
+ CollectPeersThatNeedFirewallOpener(std::vector< SharedPtr<Peer> > &v) :
+ _now(Utils::now()),
+ _v(v)
+ {
+ }
+
+ inline void operator()(Topology &t,const SharedPtr<Peer> &p)
+ {
+ if ((p->hasDirectPath())&&((_now - p->lastFirewallOpener()) >= ZT_FIREWALL_OPENER_DELAY))
+ _v.push_back(p);
+ }
+
+ private:
+ uint64_t _now;
+ std::vector< SharedPtr<Peer> > &_v;
+ };
+
+ /**
+ * Function object to collect peers that need a ping sent
+ */
+ class CollectPeersThatNeedPing
+ {
+ public:
+ CollectPeersThatNeedPing(std::vector< SharedPtr<Peer> > &v) :
+ _now(Utils::now()),
+ _v(v)
+ {
+ }
+
+ inline void operator()(Topology &t,const SharedPtr<Peer> &p)
+ {
+ if (((p->hasActiveDirectPath(_now))||(t.isSupernode(p->address())))&&((_now - p->lastDirectSend()) >= ZT_PEER_DIRECT_PING_DELAY))
+ _v.push_back(p);
+ }
+
+ private:
+ uint64_t _now;
+ std::vector< SharedPtr<Peer> > &_v;
+ };
+
+ /**
+ * Function object to collect peers with active links (and supernodes)
+ */
+ class CollectPeersWithActiveDirectPath
+ {
+ public:
+ CollectPeersWithActiveDirectPath(std::vector< SharedPtr<Peer> > &v) :
+ _now(Utils::now()),
+ _v(v)
+ {
+ }
+
+ inline void operator()(Topology &t,const SharedPtr<Peer> &p)
+ {
+ if ((p->hasActiveDirectPath(_now))||(t.isSupernode(p->address())))
+ _v.push_back(p);
+ }
+
+ private:
+ uint64_t _now;
+ std::vector< SharedPtr<Peer> > &_v;
+ };
+
+ /**
+ * Function object to collect peers with any known direct path
+ */
+ class CollectPeersWithDirectPath
+ {
+ public:
+ CollectPeersWithDirectPath(std::vector< SharedPtr<Peer> > &v) :
+ _v(v)
+ {
+ }
+
+ inline void operator()(Topology &t,const SharedPtr<Peer> &p)
+ {
+ if (p->hasDirectPath())
+ _v.push_back(p);
+ }
+
+ private:
+ std::vector< SharedPtr<Peer> > &_v;
+ };
+
+protected:
+ virtual void main()
+ throw();
+
+private:
+ void _reallyAddPeer(const SharedPtr<Peer> &p);
+
+ // A job for the background deep verify thread (also does cache cleaning, flushing, etc.)
+ struct _PeerDeepVerifyJob
+ {
+ void (*callback)(void *,const SharedPtr<Peer> &,Topology::PeerVerifyResult);
+ void *arg;
+ SharedPtr<Peer> candidate;
+ enum {
+ VERIFY_PEER,
+ CLEAN_CACHE,
+ EXIT_THREAD
+ } type;
+ };
+
+ const RuntimeEnvironment *const _r;
+
+ std::map< Address,SharedPtr<Peer> > _activePeers;
+ Mutex _activePeers_m;
+
+ std::list< _PeerDeepVerifyJob > _peerDeepVerifyJobs;
+ Mutex _peerDeepVerifyJobs_m;
+ Condition _peerDeepVerifyJobs_c;
+
+ std::map< Identity,std::vector<InetAddress> > _supernodes;
+ std::set< Address > _supernodeAddresses;
+ std::vector< SharedPtr<Peer> > _supernodePeers;
+ Mutex _supernodes_m;
+
+ KISSDB _dbm;
+ Mutex _dbm_m;
+
+ // Multicast group members by network ID, then multicast group
+ std::map< uint64_t,std::map< MulticastGroup,std::map< Address,uint64_t > > > _multicastGroupMembers;
+ Mutex _multicastGroupMembers_m;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/UdpSocket.cpp b/node/UdpSocket.cpp
new file mode 100644
index 00000000..95156fcc
--- /dev/null
+++ b/node/UdpSocket.cpp
@@ -0,0 +1,163 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#ifdef _WIN32
+#include <Windows.h>
+#else
+#include <unistd.h>
+#include <signal.h>
+#endif
+
+#include "UdpSocket.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Logger.hpp"
+#include "Switch.hpp"
+
+namespace ZeroTier {
+
+UdpSocket::UdpSocket(
+ int localPort,
+ bool ipv6,
+ void (*packetHandler)(UdpSocket *,void *,const InetAddress &,const void *,unsigned int),
+ void *arg)
+ throw(std::runtime_error) :
+ Thread(),
+ _packetHandler(packetHandler),
+ _arg(arg),
+ _localPort(localPort),
+ _sock(0),
+ _v6(ipv6)
+{
+ int yes,no;
+
+ if ((localPort <= 0)||(localPort > 0xffff))
+ throw std::runtime_error("port is out of range");
+
+ if (ipv6) {
+ _sock = socket(AF_INET6,SOCK_DGRAM,0);
+ if (_sock <= 0)
+ throw std::runtime_error("unable to create IPv6 SOCK_DGRAM socket");
+
+ yes = 1; setsockopt(_sock,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&yes,sizeof(yes));
+ no = 0; setsockopt(_sock,SOL_SOCKET,SO_REUSEADDR,(void *)&no,sizeof(no));
+#ifdef IP_DONTFRAG
+ no = 0; setsockopt(_sock,IPPROTO_IP,IP_DONTFRAG,&no,sizeof(no));
+#endif
+#ifdef IP_MTU_DISCOVER
+ no = 0; setsockopt(_sock,IPPROTO_IP,IP_MTU_DISCOVER,&no,sizeof(no));
+#endif
+#ifdef IPV6_MTU_DISCOVER
+ no = 0; setsockopt(_sock,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&no,sizeof(no));
+#endif
+
+ struct sockaddr_in6 sin6;
+ memset(&sin6,0,sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(localPort);
+ memcpy(&(sin6.sin6_addr),&in6addr_any,sizeof(struct in6_addr));
+ if (::bind(_sock,(const struct sockaddr *)&sin6,sizeof(sin6))) {
+ ::close(_sock);
+ throw std::runtime_error("unable to bind to port");
+ }
+ } else {
+ _sock = socket(AF_INET,SOCK_DGRAM,0);
+ if (_sock <= 0)
+ throw std::runtime_error("unable to create IPv4 SOCK_DGRAM socket");
+
+ no = 0; setsockopt(_sock,SOL_SOCKET,SO_REUSEADDR,(void *)&no,sizeof(no));
+#ifdef IP_DONTFRAG
+ no = 0; setsockopt(_sock,IPPROTO_IP,IP_DONTFRAG,&no,sizeof(no));
+#endif
+#ifdef IP_MTU_DISCOVER
+ no = 0; setsockopt(_sock,IPPROTO_IP,IP_MTU_DISCOVER,&no,sizeof(no));
+#endif
+
+ struct sockaddr_in sin;
+ memset(&sin,0,sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(localPort);
+ sin.sin_addr.s_addr = INADDR_ANY;
+ if (::bind(_sock,(const struct sockaddr *)&sin,sizeof(sin))) {
+ ::close(_sock);
+ throw std::runtime_error("unable to bind to port");
+ }
+ }
+
+ start();
+}
+
+UdpSocket::~UdpSocket()
+{
+ close(_sock);
+}
+
+bool UdpSocket::send(const InetAddress &to,const void *data,unsigned int len,int hopLimit)
+ throw()
+{
+ Mutex::Lock _l(_sendLock);
+ if (to.isV6()) {
+ if (!_v6)
+ return false;
+ setsockopt(_sock,IPPROTO_IPV6,IPV6_UNICAST_HOPS,&hopLimit,sizeof(hopLimit));
+ return ((int)sendto(_sock,data,len,0,to.saddr(),to.saddrLen()) == (int)len);
+ } else {
+ if (_v6)
+ return false;
+ setsockopt(_sock,IPPROTO_IP,IP_TTL,&hopLimit,sizeof(hopLimit));
+ return ((int)sendto(_sock,data,len,0,to.saddr(),to.saddrLen()) == (int)len);
+ }
+}
+
+void UdpSocket::main()
+ throw()
+{
+ char buf[32768];
+ InetAddress from;
+ socklen_t salen;
+ int n;
+
+ for(;;) {
+ salen = from.saddrSpaceLen();
+ n = (int)recvfrom(_sock,buf,sizeof(buf),0,from.saddr(),&salen);
+ if (n < 0) {
+ if ((errno != EINTR)&&(errno != ETIMEDOUT))
+ break;
+ } else if (n > 0)
+ _packetHandler(this,_arg,from,buf,(unsigned int)n);
+ }
+}
+
+} // namespace ZeroTier
diff --git a/node/UdpSocket.hpp b/node/UdpSocket.hpp
new file mode 100644
index 00000000..be407d56
--- /dev/null
+++ b/node/UdpSocket.hpp
@@ -0,0 +1,103 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_UDPSOCKET_HPP
+#define _ZT_UDPSOCKET_HPP
+
+#include <stdexcept>
+#include "Thread.hpp"
+#include "InetAddress.hpp"
+#include "Mutex.hpp"
+
+namespace ZeroTier {
+
+/**
+ * A local UDP socket
+ *
+ * The socket listens in a background thread and sends packets to Switch.
+ */
+class UdpSocket : protected Thread
+{
+public:
+ /**
+ * Create and bind a local UDP socket
+ *
+ * @param localPort Local port to listen to
+ * @param ipv6 If true, bind this as an IPv6 socket, otherwise IPv4
+ * @param packetHandler Function to call when packets are read
+ * @param arg First argument (after self) to function
+ * @throws std::runtime_error Unable to bind
+ */
+ UdpSocket(
+ int localPort,
+ bool ipv6,
+ void (*packetHandler)(UdpSocket *,void *,const InetAddress &,const void *,unsigned int),
+ void *arg)
+ throw(std::runtime_error);
+
+ virtual ~UdpSocket();
+
+ /**
+ * @return Locally bound port
+ */
+ inline int localPort() const throw() { return _localPort; }
+
+ /**
+ * @return True if this is an IPv6 socket
+ */
+ inline bool v6() const throw() { return _v6; }
+
+ /**
+ * Send a packet
+ *
+ * Attempt to send V6 on a V4 or V4 on a V6 socket will return false.
+ *
+ * @param to Destination IP/port
+ * @param data Data to send
+ * @param len Length of data in bytes
+ * @param hopLimit IP hop limit for UDP packet or -1 for max (max: 255)
+ * @return True if packet successfully sent to link layer
+ */
+ bool send(const InetAddress &to,const void *data,unsigned int len,int hopLimit)
+ throw();
+
+protected:
+ virtual void main()
+ throw();
+
+private:
+ void (*_packetHandler)(UdpSocket *,void *,const InetAddress &,const void *,unsigned int);
+ void *_arg;
+ int _localPort;
+ int _sock;
+ bool _v6;
+ Mutex _sendLock;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/node/Utils.cpp b/node/Utils.cpp
new file mode 100644
index 00000000..7e55d26b
--- /dev/null
+++ b/node/Utils.cpp
@@ -0,0 +1,558 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "Utils.hpp"
+#include "Mutex.hpp"
+
+#if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#ifdef _WIN32
+#include <Windows.h>
+#endif
+
+#include <sys/stat.h>
+#include <openssl/rand.h>
+
+namespace ZeroTier {
+
+const char Utils::HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
+
+const uint64_t Utils::crc64Table[256] = {
+ 0x0000000000000000ULL,0x7ad870c830358979ULL,
+ 0xf5b0e190606b12f2ULL,0x8f689158505e9b8bULL,
+ 0xc038e5739841b68fULL,0xbae095bba8743ff6ULL,
+ 0x358804e3f82aa47dULL,0x4f50742bc81f2d04ULL,
+ 0xab28ecb46814fe75ULL,0xd1f09c7c5821770cULL,
+ 0x5e980d24087fec87ULL,0x24407dec384a65feULL,
+ 0x6b1009c7f05548faULL,0x11c8790fc060c183ULL,
+ 0x9ea0e857903e5a08ULL,0xe478989fa00bd371ULL,
+ 0x7d08ff3b88be6f81ULL,0x07d08ff3b88be6f8ULL,
+ 0x88b81eabe8d57d73ULL,0xf2606e63d8e0f40aULL,
+ 0xbd301a4810ffd90eULL,0xc7e86a8020ca5077ULL,
+ 0x4880fbd87094cbfcULL,0x32588b1040a14285ULL,
+ 0xd620138fe0aa91f4ULL,0xacf86347d09f188dULL,
+ 0x2390f21f80c18306ULL,0x594882d7b0f40a7fULL,
+ 0x1618f6fc78eb277bULL,0x6cc0863448deae02ULL,
+ 0xe3a8176c18803589ULL,0x997067a428b5bcf0ULL,
+ 0xfa11fe77117cdf02ULL,0x80c98ebf2149567bULL,
+ 0x0fa11fe77117cdf0ULL,0x75796f2f41224489ULL,
+ 0x3a291b04893d698dULL,0x40f16bccb908e0f4ULL,
+ 0xcf99fa94e9567b7fULL,0xb5418a5cd963f206ULL,
+ 0x513912c379682177ULL,0x2be1620b495da80eULL,
+ 0xa489f35319033385ULL,0xde51839b2936bafcULL,
+ 0x9101f7b0e12997f8ULL,0xebd98778d11c1e81ULL,
+ 0x64b116208142850aULL,0x1e6966e8b1770c73ULL,
+ 0x8719014c99c2b083ULL,0xfdc17184a9f739faULL,
+ 0x72a9e0dcf9a9a271ULL,0x08719014c99c2b08ULL,
+ 0x4721e43f0183060cULL,0x3df994f731b68f75ULL,
+ 0xb29105af61e814feULL,0xc849756751dd9d87ULL,
+ 0x2c31edf8f1d64ef6ULL,0x56e99d30c1e3c78fULL,
+ 0xd9810c6891bd5c04ULL,0xa3597ca0a188d57dULL,
+ 0xec09088b6997f879ULL,0x96d1784359a27100ULL,
+ 0x19b9e91b09fcea8bULL,0x636199d339c963f2ULL,
+ 0xdf7adabd7a6e2d6fULL,0xa5a2aa754a5ba416ULL,
+ 0x2aca3b2d1a053f9dULL,0x50124be52a30b6e4ULL,
+ 0x1f423fcee22f9be0ULL,0x659a4f06d21a1299ULL,
+ 0xeaf2de5e82448912ULL,0x902aae96b271006bULL,
+ 0x74523609127ad31aULL,0x0e8a46c1224f5a63ULL,
+ 0x81e2d7997211c1e8ULL,0xfb3aa75142244891ULL,
+ 0xb46ad37a8a3b6595ULL,0xceb2a3b2ba0eececULL,
+ 0x41da32eaea507767ULL,0x3b024222da65fe1eULL,
+ 0xa2722586f2d042eeULL,0xd8aa554ec2e5cb97ULL,
+ 0x57c2c41692bb501cULL,0x2d1ab4dea28ed965ULL,
+ 0x624ac0f56a91f461ULL,0x1892b03d5aa47d18ULL,
+ 0x97fa21650afae693ULL,0xed2251ad3acf6feaULL,
+ 0x095ac9329ac4bc9bULL,0x7382b9faaaf135e2ULL,
+ 0xfcea28a2faafae69ULL,0x8632586aca9a2710ULL,
+ 0xc9622c4102850a14ULL,0xb3ba5c8932b0836dULL,
+ 0x3cd2cdd162ee18e6ULL,0x460abd1952db919fULL,
+ 0x256b24ca6b12f26dULL,0x5fb354025b277b14ULL,
+ 0xd0dbc55a0b79e09fULL,0xaa03b5923b4c69e6ULL,
+ 0xe553c1b9f35344e2ULL,0x9f8bb171c366cd9bULL,
+ 0x10e3202993385610ULL,0x6a3b50e1a30ddf69ULL,
+ 0x8e43c87e03060c18ULL,0xf49bb8b633338561ULL,
+ 0x7bf329ee636d1eeaULL,0x012b592653589793ULL,
+ 0x4e7b2d0d9b47ba97ULL,0x34a35dc5ab7233eeULL,
+ 0xbbcbcc9dfb2ca865ULL,0xc113bc55cb19211cULL,
+ 0x5863dbf1e3ac9decULL,0x22bbab39d3991495ULL,
+ 0xadd33a6183c78f1eULL,0xd70b4aa9b3f20667ULL,
+ 0x985b3e827bed2b63ULL,0xe2834e4a4bd8a21aULL,
+ 0x6debdf121b863991ULL,0x1733afda2bb3b0e8ULL,
+ 0xf34b37458bb86399ULL,0x8993478dbb8deae0ULL,
+ 0x06fbd6d5ebd3716bULL,0x7c23a61ddbe6f812ULL,
+ 0x3373d23613f9d516ULL,0x49aba2fe23cc5c6fULL,
+ 0xc6c333a67392c7e4ULL,0xbc1b436e43a74e9dULL,
+ 0x95ac9329ac4bc9b5ULL,0xef74e3e19c7e40ccULL,
+ 0x601c72b9cc20db47ULL,0x1ac40271fc15523eULL,
+ 0x5594765a340a7f3aULL,0x2f4c0692043ff643ULL,
+ 0xa02497ca54616dc8ULL,0xdafce7026454e4b1ULL,
+ 0x3e847f9dc45f37c0ULL,0x445c0f55f46abeb9ULL,
+ 0xcb349e0da4342532ULL,0xb1eceec59401ac4bULL,
+ 0xfebc9aee5c1e814fULL,0x8464ea266c2b0836ULL,
+ 0x0b0c7b7e3c7593bdULL,0x71d40bb60c401ac4ULL,
+ 0xe8a46c1224f5a634ULL,0x927c1cda14c02f4dULL,
+ 0x1d148d82449eb4c6ULL,0x67ccfd4a74ab3dbfULL,
+ 0x289c8961bcb410bbULL,0x5244f9a98c8199c2ULL,
+ 0xdd2c68f1dcdf0249ULL,0xa7f41839ecea8b30ULL,
+ 0x438c80a64ce15841ULL,0x3954f06e7cd4d138ULL,
+ 0xb63c61362c8a4ab3ULL,0xcce411fe1cbfc3caULL,
+ 0x83b465d5d4a0eeceULL,0xf96c151de49567b7ULL,
+ 0x76048445b4cbfc3cULL,0x0cdcf48d84fe7545ULL,
+ 0x6fbd6d5ebd3716b7ULL,0x15651d968d029fceULL,
+ 0x9a0d8ccedd5c0445ULL,0xe0d5fc06ed698d3cULL,
+ 0xaf85882d2576a038ULL,0xd55df8e515432941ULL,
+ 0x5a3569bd451db2caULL,0x20ed197575283bb3ULL,
+ 0xc49581ead523e8c2ULL,0xbe4df122e51661bbULL,
+ 0x3125607ab548fa30ULL,0x4bfd10b2857d7349ULL,
+ 0x04ad64994d625e4dULL,0x7e7514517d57d734ULL,
+ 0xf11d85092d094cbfULL,0x8bc5f5c11d3cc5c6ULL,
+ 0x12b5926535897936ULL,0x686de2ad05bcf04fULL,
+ 0xe70573f555e26bc4ULL,0x9ddd033d65d7e2bdULL,
+ 0xd28d7716adc8cfb9ULL,0xa85507de9dfd46c0ULL,
+ 0x273d9686cda3dd4bULL,0x5de5e64efd965432ULL,
+ 0xb99d7ed15d9d8743ULL,0xc3450e196da80e3aULL,
+ 0x4c2d9f413df695b1ULL,0x36f5ef890dc31cc8ULL,
+ 0x79a59ba2c5dc31ccULL,0x037deb6af5e9b8b5ULL,
+ 0x8c157a32a5b7233eULL,0xf6cd0afa9582aa47ULL,
+ 0x4ad64994d625e4daULL,0x300e395ce6106da3ULL,
+ 0xbf66a804b64ef628ULL,0xc5bed8cc867b7f51ULL,
+ 0x8aeeace74e645255ULL,0xf036dc2f7e51db2cULL,
+ 0x7f5e4d772e0f40a7ULL,0x05863dbf1e3ac9deULL,
+ 0xe1fea520be311aafULL,0x9b26d5e88e0493d6ULL,
+ 0x144e44b0de5a085dULL,0x6e963478ee6f8124ULL,
+ 0x21c640532670ac20ULL,0x5b1e309b16452559ULL,
+ 0xd476a1c3461bbed2ULL,0xaeaed10b762e37abULL,
+ 0x37deb6af5e9b8b5bULL,0x4d06c6676eae0222ULL,
+ 0xc26e573f3ef099a9ULL,0xb8b627f70ec510d0ULL,
+ 0xf7e653dcc6da3dd4ULL,0x8d3e2314f6efb4adULL,
+ 0x0256b24ca6b12f26ULL,0x788ec2849684a65fULL,
+ 0x9cf65a1b368f752eULL,0xe62e2ad306bafc57ULL,
+ 0x6946bb8b56e467dcULL,0x139ecb4366d1eea5ULL,
+ 0x5ccebf68aecec3a1ULL,0x2616cfa09efb4ad8ULL,
+ 0xa97e5ef8cea5d153ULL,0xd3a62e30fe90582aULL,
+ 0xb0c7b7e3c7593bd8ULL,0xca1fc72bf76cb2a1ULL,
+ 0x45775673a732292aULL,0x3faf26bb9707a053ULL,
+ 0x70ff52905f188d57ULL,0x0a2722586f2d042eULL,
+ 0x854fb3003f739fa5ULL,0xff97c3c80f4616dcULL,
+ 0x1bef5b57af4dc5adULL,0x61372b9f9f784cd4ULL,
+ 0xee5fbac7cf26d75fULL,0x9487ca0fff135e26ULL,
+ 0xdbd7be24370c7322ULL,0xa10fceec0739fa5bULL,
+ 0x2e675fb4576761d0ULL,0x54bf2f7c6752e8a9ULL,
+ 0xcdcf48d84fe75459ULL,0xb71738107fd2dd20ULL,
+ 0x387fa9482f8c46abULL,0x42a7d9801fb9cfd2ULL,
+ 0x0df7adabd7a6e2d6ULL,0x772fdd63e7936bafULL,
+ 0xf8474c3bb7cdf024ULL,0x829f3cf387f8795dULL,
+ 0x66e7a46c27f3aa2cULL,0x1c3fd4a417c62355ULL,
+ 0x935745fc4798b8deULL,0xe98f353477ad31a7ULL,
+ 0xa6df411fbfb21ca3ULL,0xdc0731d78f8795daULL,
+ 0x536fa08fdfd90e51ULL,0x29b7d047efec8728ULL
+};
+
+const char Utils::base64EncMap[64] = {
+ 0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,
+ 0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,
+ 0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,
+ 0x59,0x5A,0x61,0x62,0x63,0x64,0x65,0x66,
+ 0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,
+ 0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,
+ 0x77,0x78,0x79,0x7A,0x30,0x31,0x32,0x33,
+ 0x34,0x35,0x36,0x37,0x38,0x39,0x2B,0x2F
+};
+
+const char Utils::base64DecMap[128] = {
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x3F,
+ 0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,
+ 0x3C,0x3D,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,
+ 0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,
+ 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
+ 0x17,0x18,0x19,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
+ 0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,
+ 0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,
+ 0x31,0x32,0x33,0x00,0x00,0x00,0x00,0x00
+};
+
+static const char *DAY_NAMES[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
+static const char *MONTH_NAMES[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
+
+std::string Utils::base64Encode(const void *data,unsigned int len)
+{
+ if (!len)
+ return std::string();
+
+ std::string out;
+ unsigned int sidx = 0;
+
+ if (len > 1) {
+ while (sidx < (len - 2)) {
+ out.push_back(base64EncMap[(((const unsigned char *)data)[sidx] >> 2) & 077]);
+ out.push_back(base64EncMap[((((const unsigned char *)data)[sidx + 1] >> 4) & 017) | ((((const unsigned char *)data)[sidx] << 4) & 077)]);
+ out.push_back(base64EncMap[((((const unsigned char *)data)[sidx + 2] >> 6) & 003) | ((((const unsigned char *)data)[sidx + 1] << 2) & 077)]);
+ out.push_back(base64EncMap[((const unsigned char *)data)[sidx + 2] & 077]);
+ sidx += 3;
+ }
+ }
+ if (sidx < len) {
+ out.push_back(base64EncMap[(((const unsigned char *)data)[sidx] >> 2) & 077]);
+ if (sidx < len - 1) {
+ out.push_back(base64EncMap[((((const unsigned char *)data)[sidx + 1] >> 4) & 017) | ((((const unsigned char *)data)[sidx] << 4) & 077)]);
+ out.push_back(base64EncMap[(((const unsigned char *)data)[sidx + 1] << 2) & 077]);
+ } else out.push_back(base64EncMap[(((const unsigned char *)data)[sidx] << 4) & 077]);
+ }
+ while (out.length() < (((len + 2) / 3) * 4))
+ out.push_back('=');
+
+ return out;
+}
+
+std::string Utils::base64Decode(const char *data,unsigned int len)
+{
+ if (!len)
+ return std::string();
+ std::string out;
+
+ while ((len)&&(((const unsigned char *)data)[len-1] == '='))
+ --len;
+
+ for (unsigned idx=0;idx<len;idx++) {
+ unsigned char ch = ((const unsigned char *)data)[idx];
+ if ((ch > 47 && ch < 58) || (ch > 64 && ch < 91) || (ch > 96 && ch < 123) || ch == '+' || ch == '/' || ch == '=')
+ out.push_back(base64DecMap[ch]);
+ else return std::string();
+ }
+
+ unsigned outLen = len - ((len + 3) / 4);
+ if ((!outLen)||((((outLen + 2) / 3) * 4) < len))
+ return std::string();
+
+ unsigned sidx = 0;
+ unsigned didx = 0;
+ if (outLen > 1) {
+ while (didx < outLen - 2) {
+ out[didx] = (((out[sidx] << 2) & 255) | ((out[sidx + 1] >> 4) & 003));
+ out[didx + 1] = (((out[sidx + 1] << 4) & 255) | ((out[sidx + 2] >> 2) & 017));
+ out[didx + 2] = (((out[sidx + 2] << 6) & 255) | (out[sidx + 3] & 077));
+ sidx += 4;
+ didx += 3;
+ }
+ }
+
+ if (didx < outLen)
+ out[didx] = (((out[sidx] << 2) & 255) | ((out[sidx + 1] >> 4) & 003));
+ if (++didx < outLen)
+ out[didx] = (((out[sidx + 1] << 4) & 255) | ((out[sidx + 2] >> 2) & 017));
+
+ return out.substr(0,outLen);
+}
+
+const char *Utils::etherTypeName(const unsigned int etherType)
+{
+ static char tmp[6];
+ switch(etherType) {
+ case ZT_ETHERTYPE_IPV4:
+ return "IPV4";
+ case ZT_ETHERTYPE_ARP:
+ return "ARP";
+ case ZT_ETHERTYPE_RARP:
+ return "RARP";
+ case ZT_ETHERTYPE_ATALK:
+ return "ATALK";
+ case ZT_ETHERTYPE_AARP:
+ return "AARP";
+ case ZT_ETHERTYPE_IPX_A:
+ return "IPX_A";
+ case ZT_ETHERTYPE_IPX_B:
+ return "IPX_B";
+ case ZT_ETHERTYPE_IPV6:
+ return "IPV6";
+ }
+ sprintf(tmp,"%.4x",etherType);
+ return tmp; // technically not thread safe, but we're only going to see this in debugging if ever
+}
+
+std::string Utils::hex(const void *data,unsigned int len)
+{
+ std::string r;
+ r.reserve(len * 2);
+ for(unsigned int i=0;i<len;++i) {
+ r.push_back(HEXCHARS[(((const unsigned char *)data)[i] & 0xf0) >> 4]);
+ r.push_back(HEXCHARS[((const unsigned char *)data)[i] & 0x0f]);
+ }
+ return r;
+}
+
+std::string Utils::unhex(const char *hex)
+{
+ int n = 1;
+ unsigned char c,b = 0;
+ std::string r;
+
+ while ((c = (unsigned char)*(hex++))) {
+ if ((c >= 48)&&(c <= 57)) { // 0..9
+ if ((n ^= 1))
+ r.push_back((char)(b | (c - 48)));
+ else b = (c - 48) << 4;
+ } else if ((c >= 65)&&(c <= 70)) { // A..F
+ if ((n ^= 1))
+ r.push_back((char)(b | (c - (65 - 10))));
+ else b = (c - (65 - 10)) << 4;
+ } else if ((c >= 97)&&(c <= 102)) { // a..f
+ if ((n ^= 1))
+ r.push_back((char)(b | (c - (97 - 10))));
+ else b = (c - (97 - 10)) << 4;
+ }
+ }
+
+ return r;
+}
+
+unsigned int Utils::unhex(const char *hex,void *buf,unsigned int len)
+{
+ int n = 1;
+ unsigned char c,b = 0;
+ unsigned int l = 0;
+
+ while ((c = (unsigned char)*(hex++))) {
+ if ((c >= 48)&&(c <= 57)) { // 0..9
+ if ((n ^= 1)) {
+ if (l >= len) break;
+ ((unsigned char *)buf)[l++] = (b | (c - 48));
+ } else b = (c - 48) << 4;
+ } else if ((c >= 65)&&(c <= 70)) { // A..F
+ if ((n ^= 1)) {
+ if (l >= len) break;
+ ((unsigned char *)buf)[l++] = (b | (c - (65 - 10)));
+ } else b = (c - (65 - 10)) << 4;
+ } else if ((c >= 97)&&(c <= 102)) { // a..f
+ if ((n ^= 1)) {
+ if (l >= len) break;
+ ((unsigned char *)buf)[l++] = (b | (c - (97 - 10)));
+ } else b = (c - (97 - 10)) << 4;
+ }
+ }
+
+ return l;
+}
+
+void Utils::getSecureRandom(void *buf,unsigned int bytes)
+{
+ unsigned char tmp[16384];
+ while (!RAND_bytes((unsigned char *)buf,bytes)) {
+#if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+ FILE *rf = fopen("/dev/urandom","r");
+ if (rf) {
+ fread(tmp,sizeof(tmp),1,rf);
+ fclose(rf);
+ RAND_seed(tmp,sizeof(tmp));
+ } else {
+ fprintf(stderr,"FATAL: could not open /dev/urandom\n");
+ exit(-1);
+ }
+#else
+#ifdef _WIN32
+ error need win32;
+#else
+ error;
+#endif
+#endif
+ }
+}
+
+void Utils::lockDownFile(const char *path,bool isDir)
+{
+#if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
+ chmod(path,isDir ? 0700 : 0600);
+#else
+#ifdef _WIN32
+ error need win32;
+#endif
+#endif
+}
+
+uint64_t Utils::getLastModified(const char *path)
+{
+ struct stat s;
+ if (stat(path,&s))
+ return 0;
+ return (((uint64_t)s.st_mtime) * 1000ULL);
+}
+
+std::string Utils::toRfc1123(uint64_t t64)
+{
+ struct tm t;
+ char buf[128];
+ time_t utc = (time_t)(t64 / 1000ULL);
+ gmtime_r(&utc,&t);
+ sprintf(buf,"%3s, %02d %3s %4d %02d:%02d:%02d GMT",DAY_NAMES[t.tm_wday],t.tm_mday,MONTH_NAMES[t.tm_mon],t.tm_year + 1900,t.tm_hour,t.tm_min,t.tm_sec);
+ return std::string(buf);
+}
+
+uint64_t Utils::fromRfc1123(const char *tstr)
+{
+ struct tm t;
+ char wdays[128],mons[128];
+
+ int l = strlen(tstr);
+ if ((l < 29)||(l > 64))
+ return 0;
+ int assigned = sscanf(tstr,"%3s, %02d %3s %4d %02d:%02d:%02d GMT",wdays,&t.tm_mday,mons,&t.tm_year,&t.tm_hour,&t.tm_min,&t.tm_sec);
+ if (assigned != 7)
+ return 0;
+
+ wdays[3] = '\0';
+ for(t.tm_wday=0;t.tm_wday<7;++t.tm_wday) {
+ if (!strcasecmp(DAY_NAMES[t.tm_wday],wdays))
+ break;
+ }
+ if (t.tm_wday == 7)
+ return 0;
+ mons[3] = '\0';
+ for(t.tm_mon=0;t.tm_mon<12;++t.tm_mon) {
+ if (!strcasecmp(MONTH_NAMES[t.tm_mday],mons))
+ break;
+ }
+ if (t.tm_mon == 12)
+ return 0;
+
+ t.tm_wday = 0; // ignored by timegm
+ t.tm_yday = 0; // ignored by timegm
+ t.tm_isdst = 0; // ignored by timegm
+
+ time_t utc = timegm(&t);
+ return ((utc > 0) ? (1000ULL * (uint64_t)utc) : 0ULL);
+}
+
+bool Utils::readFile(const char *path,std::string &buf)
+{
+ char tmp[4096];
+ FILE *f = fopen(path,"rb");
+ if (f) {
+ for(;;) {
+ long n = (long)fread(tmp,1,sizeof(tmp),f);
+ if (n > 0)
+ buf.append(tmp,n);
+ else break;
+ }
+ fclose(f);
+ return true;
+ }
+ return false;
+}
+
+bool Utils::writeFile(const char *path,const void *buf,unsigned int len)
+{
+ FILE *f = fopen(path,"wb");
+ if (f) {
+ if ((long)fwrite(buf,1,len,f) != (long)len) {
+ fclose(f);
+ return false;
+ } else {
+ fclose(f);
+ return true;
+ }
+ }
+ return false;
+}
+
+std::vector<std::string> Utils::split(const char *s,const char *const sep,const char *esc,const char *quot)
+{
+ std::vector<std::string> fields;
+ std::string buf;
+
+ if (!esc)
+ esc = "";
+ if (!quot)
+ quot = "";
+
+ bool escapeState = false;
+ char quoteState = 0;
+ while (*s) {
+ if (escapeState) {
+ escapeState = false;
+ buf.push_back(*s);
+ } else if (quoteState) {
+ if (*s == quoteState) {
+ quoteState = 0;
+ fields.push_back(buf);
+ buf.clear();
+ } else buf.push_back(*s);
+ } else {
+ const char *quotTmp;
+ if (strchr(esc,*s))
+ escapeState = true;
+ else if ((buf.size() <= 0)&&((quotTmp = strchr(quot,*s))))
+ quoteState = *quotTmp;
+ else if (strchr(sep,*s)) {
+ if (buf.size() > 0) {
+ fields.push_back(buf);
+ buf.clear();
+ } // else skip runs of seperators
+ } else buf.push_back(*s);
+ }
+ ++s;
+ }
+
+ if (buf.size())
+ fields.push_back(buf);
+
+ return fields;
+}
+
+std::string Utils::trim(const std::string &s)
+{
+ unsigned long end = s.length();
+ while (end) {
+ char c = s[end - 1];
+ if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t'))
+ --end;
+ else break;
+ }
+ unsigned long start = 0;
+ while (start < end) {
+ char c = s[start];
+ if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t'))
+ ++start;
+ else break;
+ }
+ return s.substr(start,end - start);
+}
+
+} // namespace ZeroTier
diff --git a/node/Utils.hpp b/node/Utils.hpp
new file mode 100644
index 00000000..b8aced63
--- /dev/null
+++ b/node/Utils.hpp
@@ -0,0 +1,601 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013 ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_UTILS_HPP
+#define _ZT_UTILS_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <string>
+#include <stdexcept>
+#include <vector>
+
+#include "../ext/lz4/lz4.h"
+#include "../ext/lz4/lz4hc.h"
+#include "../ext/huffandpuff/huffman.h"
+
+#include "Constants.hpp"
+
+/* Ethernet frame types that might be relevant to us */
+#define ZT_ETHERTYPE_IPV4 0x0800
+#define ZT_ETHERTYPE_ARP 0x0806
+#define ZT_ETHERTYPE_RARP 0x8035
+#define ZT_ETHERTYPE_ATALK 0x809b
+#define ZT_ETHERTYPE_AARP 0x80f3
+#define ZT_ETHERTYPE_IPX_A 0x8137
+#define ZT_ETHERTYPE_IPX_B 0x8138
+#define ZT_ETHERTYPE_IPV6 0x86dd
+
+/**
+ * Maximum compression/decompression block size (do not change)
+ */
+#define ZT_COMPRESSION_BLOCK_SIZE 16777216
+
+namespace ZeroTier {
+
+/**
+ * Miscellaneous utility functions and global constants
+ */
+class Utils
+{
+public:
+ /**
+ * @param etherType Ethernet type ID
+ * @return Name of Ethernet protocol (e.g. ARP, IPV4)
+ */
+ static const char *etherTypeName(const unsigned int etherType);
+
+ /**
+ * @param data Data to convert to hex
+ * @param len Length of data
+ * @return Hexadecimal string
+ */
+ static std::string hex(const void *data,unsigned int len);
+ static inline std::string hex(const std::string &data) { return hex(data.data(),data.length()); }
+
+ /**
+ * @param hex Hexadecimal ASCII code (non-hex chars are ignored)
+ * @return Binary data
+ */
+ static std::string unhex(const char *hex);
+ static inline std::string unhex(const std::string &hex) { return unhex(hex.c_str()); }
+
+ /**
+ * @param hex Hexadecimal ASCII
+ * @param buf Buffer to fill
+ * @param len Length of buffer
+ * @return Number of characters actually written
+ */
+ static unsigned int unhex(const char *hex,void *buf,unsigned int len);
+
+ /**
+ * @param buf Buffer to fill
+ * @param bytes Number of random bytes to generate
+ */
+ static void getSecureRandom(void *buf,unsigned int bytes);
+
+ /**
+ * @tparam T Integer type to fill and return
+ * @return Random int using secure random source
+ */
+ template<typename T>
+ static inline T randomInt()
+ {
+ T foo = 0; // prevents valgrind warnings
+ getSecureRandom(&foo,sizeof(foo));
+ return foo;
+ }
+
+ /**
+ * Set modes on a file to something secure
+ *
+ * This locks a file so that only the owner can access it. What it actually
+ * does varies by platform.
+ *
+ * @param path Path to lock
+ * @param isDir True if this is a directory
+ */
+ static void lockDownFile(const char *path,bool isDir);
+
+ /**
+ * Get file last modification time
+ *
+ * Resolution is often only second, not millisecond, but the return is
+ * always in ms for comparison against now().
+ *
+ * @param path Path to file to get time
+ * @return Last modification time in ms since epoch or 0 if not found
+ */
+ static uint64_t getLastModified(const char *path);
+
+ /**
+ * @param t64 Time in ms since epoch
+ * @return RFC1123 date string
+ */
+ static std::string toRfc1123(uint64_t t64);
+
+ /**
+ * @param tstr Time in RFC1123 string format
+ * @return Time in ms since epoch
+ */
+ static uint64_t fromRfc1123(const char *tstr);
+ static inline uint64_t fromRfc1123(const std::string &tstr) { return fromRfc1123(tstr.c_str()); }
+
+ /**
+ * String append output function object for use with compress/decompress
+ */
+ class StringAppendOutput
+ {
+ public:
+ StringAppendOutput(std::string &s) : _s(s) {}
+ inline void operator()(const void *data,unsigned int len) { _s.append((const char *)data,len); }
+ private:
+ std::string &_s;
+ };
+
+ /**
+ * STDIO FILE append output function object for compress/decompress
+ *
+ * Throws std::runtime_error on write error.
+ */
+ class FILEAppendOutput
+ {
+ public:
+ FILEAppendOutput(FILE *f) : _f(f) {}
+ inline void operator()(const void *data,unsigned int len)
+ throw(std::runtime_error)
+ {
+ if ((int)fwrite(data,1,len,_f) != (int)len)
+ throw std::runtime_error("write failed");
+ }
+ private:
+ FILE *_f;
+ };
+
+ /**
+ * Compress data
+ *
+ * O must be a function or function object that takes the following
+ * arguments: (const void *data,unsigned int len)
+ *
+ * @param in Input iterator that reads bytes (char, uint8_t, etc.)
+ * @param out Output iterator that writes bytes
+ */
+ template<typename I,typename O>
+ static inline void compress(I begin,I end,O out)
+ {
+ char huffheap[HUFFHEAP_SIZE];
+ unsigned int bufLen = LZ4_compressBound(ZT_COMPRESSION_BLOCK_SIZE);
+ char *buf = new char[bufLen * 2];
+ char *buf2 = buf + bufLen;
+
+ try {
+ I inp(begin);
+ for(;;) {
+ unsigned int readLen = 0;
+ while ((readLen < ZT_COMPRESSION_BLOCK_SIZE)&&(inp != end)) {
+ buf[readLen++] = (char)*inp;
+ ++inp;
+ }
+ if (!readLen)
+ break;
+
+ uint32_t l = hton((uint32_t)readLen);
+ out((const void *)&l,4); // original size
+
+ if (readLen < 32) { // don't bother compressing itty bitty blocks
+ l = 0; // stored
+ out((const void *)&l,4);
+ out((const void *)buf,readLen);
+ continue;
+ }
+
+ int lz4CompressedLen = LZ4_compressHC(buf,buf2,(int)readLen);
+ if ((lz4CompressedLen <= 0)||(lz4CompressedLen >= (int)readLen)) {
+ l = 0; // stored
+ out((const void *)&l,4);
+ out((const void *)buf,readLen);
+ continue;
+ }
+
+ unsigned long huffCompressedLen = huffman_compress((const unsigned char *)buf2,lz4CompressedLen,(unsigned char *)buf,bufLen,huffheap);
+ if ((!huffCompressedLen)||((int)huffCompressedLen >= lz4CompressedLen)) {
+ l = hton((uint32_t)lz4CompressedLen); // lz4 only
+ out((const void *)&l,4);
+ out((const void *)buf2,(unsigned int)lz4CompressedLen);
+ } else {
+ l = hton((uint32_t)0x80000000 | (uint32_t)huffCompressedLen); // lz4 with huffman
+ out((const void *)&l,4);
+ out((const void *)buf,(unsigned int)huffCompressedLen);
+ }
+ }
+
+ delete [] buf;
+ } catch ( ... ) {
+ delete [] buf;
+ throw;
+ }
+ }
+
+ /**
+ * Decompress data
+ *
+ * O must be a function or function object that takes the following
+ * arguments: (const void *data,unsigned int len)
+ *
+ * @param in Input iterator that reads bytes (char, uint8_t, etc.)
+ * @param out Output iterator that writes bytes
+ * @return False on decompression error
+ */
+ template<typename I,typename O>
+ static inline bool decompress(I begin,I end,O out)
+ {
+ char huffheap[HUFFHEAP_SIZE];
+ volatile char i32c[4];
+ void *const i32cp = (void *)i32c;
+ unsigned int bufLen = LZ4_compressBound(ZT_COMPRESSION_BLOCK_SIZE);
+ char *buf = new char[bufLen * 2];
+ char *buf2 = buf + bufLen;
+
+ try {
+ I inp(begin);
+ while (inp != end) {
+ i32c[0] = (char)*inp; if (++inp == end) { delete [] buf; return false; }
+ i32c[1] = (char)*inp; if (++inp == end) { delete [] buf; return false; }
+ i32c[2] = (char)*inp; if (++inp == end) { delete [] buf; return false; }
+ i32c[3] = (char)*inp; if (++inp == end) { delete [] buf; return false; }
+ unsigned int originalSize = ntoh(*((const uint32_t *)i32cp));
+ i32c[0] = (char)*inp; if (++inp == end) { delete [] buf; return false; }
+ i32c[1] = (char)*inp; if (++inp == end) { delete [] buf; return false; }
+ i32c[2] = (char)*inp; if (++inp == end) { delete [] buf; return false; }
+ i32c[3] = (char)*inp; if (++inp == end) { delete [] buf; return false; }
+ uint32_t _compressedSize = ntoh(*((const uint32_t *)i32cp));
+ unsigned int compressedSize = _compressedSize & 0x7fffffff;
+
+ if (compressedSize) {
+ if (compressedSize > bufLen) {
+ delete [] buf;
+ return false;
+ }
+ unsigned int readLen = 0;
+ while ((readLen < compressedSize)&&(inp != end)) {
+ buf[readLen++] = (char)*inp;
+ ++inp;
+ }
+ if (readLen != compressedSize) {
+ delete [] buf;
+ return false;
+ }
+
+ if ((_compressedSize & 0x80000000)) { // lz4 and huffman
+ unsigned long lz4CompressedSize = huffman_decompress((const unsigned char *)buf,compressedSize,(unsigned char *)buf2,bufLen,huffheap);
+ if (lz4CompressedSize) {
+ if (LZ4_uncompress_unknownOutputSize(buf2,buf,lz4CompressedSize,bufLen) != (int)originalSize) {
+ delete [] buf;
+ return false;
+ } else out((const void *)buf,(unsigned int)originalSize);
+ } else {
+ delete [] buf;
+ return false;
+ }
+ } else { // lz4 only
+ if (LZ4_uncompress_unknownOutputSize(buf,buf2,compressedSize,bufLen) != (int)originalSize) {
+ delete [] buf;
+ return false;
+ } else out((const void *)buf2,(unsigned int)originalSize);
+ }
+ } else { // stored
+ if (originalSize > bufLen) {
+ delete [] buf;
+ return false;
+ }
+ unsigned int readLen = 0;
+ while ((readLen < originalSize)&&(inp != end)) {
+ buf[readLen++] = (char)*inp;
+ ++inp;
+ }
+ if (readLen != originalSize) {
+ delete [] buf;
+ return false;
+ }
+
+ out((const void *)buf,(unsigned int)originalSize);
+ }
+ }
+
+ delete [] buf;
+ return true;
+ } catch ( ... ) {
+ delete [] buf;
+ throw;
+ }
+ }
+
+ /**
+ * @return Current time in milliseconds since epoch
+ */
+ static inline uint64_t now()
+ throw()
+ {
+ struct timeval tv;
+ gettimeofday(&tv,(struct timezone *)0);
+ return ( (1000ULL * (uint64_t)tv.tv_sec) + (uint64_t)(tv.tv_usec / 1000) );
+ };
+
+ /**
+ * Read the full contents of a file into a string buffer
+ *
+ * The buffer isn't cleared, so if it already contains data the file's data will
+ * be appended.
+ *
+ * @param path Path of file to read
+ * @param buf Buffer to fill
+ * @return True if open and read successful
+ */
+ static bool readFile(const char *path,std::string &buf);
+
+ /**
+ * Write a block of data to disk, replacing any current file contents
+ *
+ * @param path Path to write
+ * @param buf Buffer containing data
+ * @param len Length of buffer
+ * @return True if entire file was successfully written
+ */
+ static bool writeFile(const char *path,const void *buf,unsigned int len);
+
+ /**
+ * Write a block of data to disk, replacing any current file contents
+ *
+ * @param path Path to write
+ * @param s Data to write
+ * @return True if entire file was successfully written
+ */
+ static inline bool writeFile(const char *path,const std::string &s)
+ {
+ return writeFile(path,s.data(),s.length());
+ }
+
+ /**
+ * @param data Binary data to encode
+ * @param len Length of data
+ * @return Base64-encoded string
+ */
+ static std::string base64Encode(const void *data,unsigned int len);
+ inline static std::string base64Encode(const std::string &data) { return base64Encode(data.data(),data.length()); }
+
+ /**
+ * @param data Base64-encoded string
+ * @param len Length of encoded string
+ * @return Decoded binary date
+ */
+ static std::string base64Decode(const char *data,unsigned int len);
+ inline static std::string base64Decode(const std::string &data) { return base64Decode(data.data(),data.length()); }
+
+ /**
+ * Split a string by delimiter, with optional escape and quote characters
+ *
+ * @param s String to split
+ * @param sep One or more separators
+ * @param esc Zero or more escape characters
+ * @param quot Zero or more quote characters
+ * @return Vector of tokens
+ */
+ static std::vector<std::string> split(const char *s,const char *const sep,const char *esc,const char *quot);
+
+ /**
+ * Trim whitespace from the start and end of a string
+ *
+ * @param s String to trim
+ * @return Trimmed string
+ */
+ static std::string trim(const std::string &s);
+
+ /**
+ * Count the number of bits set in an integer
+ *
+ * @param v 32-bit integer
+ * @return Number of bits set in this integer (0-32)
+ */
+ static inline uint32_t countBits(uint32_t v)
+ throw()
+ {
+ v = v - ((v >> 1) & (uint32_t)0x55555555);
+ v = (v & (uint32_t)0x33333333) + ((v >> 2) & (uint32_t)0x33333333);
+ return ((((v + (v >> 4)) & (uint32_t)0xF0F0F0F) * (uint32_t)0x1010101) >> 24);
+ }
+
+ /**
+ * Check if a memory buffer is all-zero
+ *
+ * @param p Memory to scan
+ * @param len Length of memory
+ * @return True if memory is all zero
+ */
+ static inline bool isZero(const void *p,unsigned int len)
+ throw()
+ {
+ for(unsigned int i=0;i<len;++i) {
+ if (((const unsigned char *)p)[i])
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Match two strings with bits masked netmask-style
+ *
+ * @param a First string
+ * @param abits Number of bits in first string
+ * @param b Second string
+ * @param bbits Number of bits in second string
+ * @return True if min(abits,bbits) match between a and b
+ */
+ static inline bool matchNetmask(const void *a,unsigned int abits,const void *b,unsigned int bbits)
+ throw()
+ {
+ const unsigned char *aptr = (const unsigned char *)a;
+ const unsigned char *bptr = (const unsigned char *)b;
+
+ while ((abits >= 8)&&(bbits >= 8)) {
+ if (*aptr++ != *bptr++)
+ return false;
+ abits -= 8;
+ bbits -= 8;
+ }
+
+ unsigned char mask = 0xff << (8 - ((abits > bbits) ? bbits : abits));
+ return ((*aptr & mask) == (*aptr & mask));
+ }
+
+ /**
+ * Add a value to a bloom filter
+ *
+ * Note that bloom filter methods depend on n being evenly distributed, so
+ * it's the job of the caller to implement any hashing.
+ *
+ * @param bits Bloom filter data (must be filterSize / 8 bytes in length)
+ * @param filterSize Size of bloom filter in BITS
+ * @param n Number to add
+ */
+ static inline void bloomAdd(void *bits,unsigned int filterSize,unsigned int n)
+ throw()
+ {
+ n %= filterSize;
+ ((unsigned char *)bits)[n / 8] |= (0x80 >> (n % 8));
+ }
+
+ /**
+ * Test for a value in a bloom filter
+ *
+ * @param bits Bloom filter data (must be filterSize / 8 bytes in length)
+ * @param filterSize Size of bloom filter in BITS
+ * @param n Number to test
+ * @return True if number might be in filter
+ */
+ static inline bool bloomContains(const void *bits,unsigned int filterSize,unsigned int n)
+ throw()
+ {
+ n %= filterSize;
+ return ((((const unsigned char *)bits)[n / 8] & (0x80 >> (n % 8))));
+ }
+
+ /**
+ * Compute CRC64
+ *
+ * @param crc Previous CRC (0 to start)
+ * @param s String to add to crc
+ * @param l Length of string in bytes
+ * @return New CRC
+ */
+ static inline uint64_t crc64(uint64_t crc,const void *s,unsigned int l)
+ throw()
+ {
+ for(unsigned int i=0;i<l;++i)
+ crc = crc64Table[(uint8_t)crc ^ ((const uint8_t *)s)[i]] ^ (crc >> 8);
+ return crc;
+ }
+
+ static inline uint8_t hton(uint8_t n) throw() { return n; }
+ static inline int8_t hton(int8_t n) throw() { return n; }
+ static inline uint16_t hton(uint16_t n) throw() { return htons(n); }
+ static inline int16_t hton(int16_t n) throw() { return (int16_t)htons((uint16_t)n); }
+ static inline uint32_t hton(uint32_t n) throw() { return htonl(n); }
+ static inline int32_t hton(int32_t n) throw() { return (int32_t)htonl((uint32_t)n); }
+ static inline uint64_t hton(uint64_t n)
+ throw()
+ {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#ifdef __GNUC__
+ return __builtin_bswap64(n);
+#else
+ return (
+ ((n & 0x00000000000000FFULL) << 56) |
+ ((n & 0x000000000000FF00ULL) << 40) |
+ ((n & 0x0000000000FF0000ULL) << 24) |
+ ((n & 0x00000000FF000000ULL) << 8) |
+ ((n & 0x000000FF00000000ULL) >> 8) |
+ ((n & 0x0000FF0000000000ULL) >> 24) |
+ ((n & 0x00FF000000000000ULL) >> 40) |
+ ((n & 0xFF00000000000000ULL) >> 56)
+ );
+#endif
+#else
+ return n;
+#endif
+ }
+ static inline int64_t hton(int64_t n) throw() { return (int64_t)hton((uint64_t)n); }
+
+ static inline uint8_t ntoh(uint8_t n) throw() { return n; }
+ static inline int8_t ntoh(int8_t n) throw() { return n; }
+ static inline uint16_t ntoh(uint16_t n) throw() { return ntohs(n); }
+ static inline int16_t ntoh(int16_t n) throw() { return (int16_t)ntohs((uint16_t)n); }
+ static inline uint32_t ntoh(uint32_t n) throw() { return ntohl(n); }
+ static inline int32_t ntoh(int32_t n) throw() { return (int32_t)ntohl((uint32_t)n); }
+ static inline uint64_t ntoh(uint64_t n)
+ throw()
+ {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#ifdef __GNUC__
+ return __builtin_bswap64(n);
+#else
+ return (
+ ((n & 0x00000000000000FFULL) << 56) |
+ ((n & 0x000000000000FF00ULL) << 40) |
+ ((n & 0x0000000000FF0000ULL) << 24) |
+ ((n & 0x00000000FF000000ULL) << 8) |
+ ((n & 0x000000FF00000000ULL) >> 8) |
+ ((n & 0x0000FF0000000000ULL) >> 24) |
+ ((n & 0x00FF000000000000ULL) >> 40) |
+ ((n & 0xFF00000000000000ULL) >> 56)
+ );
+#endif
+#else
+ return n;
+#endif
+ }
+ static inline int64_t ntoh(int64_t n) throw() { return (int64_t)ntoh((uint64_t)n); }
+
+ /**
+ * Hexadecimal characters 0-f
+ */
+ static const char HEXCHARS[16];
+
+private:
+ static const uint64_t crc64Table[256];
+ static const char base64EncMap[64];
+ static const char base64DecMap[128];
+};
+
+} // namespace ZeroTier
+
+#endif
+