New git repository for release - version 0.2.0 tagged

This commit is contained in:
Adam Ierymenko 2013-07-04 16:56:19 -04:00
commit 150850b800
261 changed files with 75902 additions and 0 deletions

176
node/Address.hpp Normal file
View file

@ -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

110
node/Array.hpp Normal file
View file

@ -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

113
node/AtomicCounter.hpp Normal file
View file

@ -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

94
node/BlobArray.hpp Normal file
View file

@ -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

398
node/Buffer.hpp Normal file
View file

@ -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

107
node/Condition.hpp Normal file
View file

@ -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

311
node/Constants.hpp Normal file
View file

@ -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

77
node/Defaults.cpp Normal file
View file

@ -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

74
node/Defaults.hpp Normal file
View file

@ -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

210
node/Demarc.cpp Normal file
View file

@ -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

168
node/Demarc.hpp Normal file
View file

@ -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

124
node/EllipticCurveKey.hpp Normal file
View file

@ -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

View file

@ -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

View file

@ -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

678
node/EthernetTap.cpp Normal file
View file

@ -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
/* ======================================================================== */

199
node/EthernetTap.hpp Normal file
View file

@ -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

81
node/HMAC.cpp Normal file
View file

@ -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

55
node/HMAC.hpp Normal file
View file

@ -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

323
node/Http.cpp Normal file
View file

@ -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

129
node/Http.hpp Normal file
View file

@ -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

301
node/Identity.cpp Normal file
View file

@ -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

431
node/Identity.hpp Normal file
View file

@ -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

148
node/InetAddress.cpp Normal file
View file

@ -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

334
node/InetAddress.hpp Normal file
View file

@ -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

141
node/Logger.cpp Normal file
View file

@ -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

90
node/Logger.hpp Normal file
View file

@ -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

160
node/MAC.hpp Normal file
View file

@ -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

144
node/MulticastGroup.hpp Normal file
View file

@ -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

197
node/Mutex.hpp Normal file
View file

@ -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

80
node/Network.cpp Normal file
View file

@ -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

162
node/Network.hpp Normal file
View file

@ -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

447
node/Node.cpp Normal file
View file

@ -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

128
node/Node.hpp Normal file
View file

@ -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

206
node/NodeConfig.cpp Normal file
View file

@ -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

136
node/NodeConfig.hpp Normal file
View file

@ -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

47
node/NonCopyable.hpp Normal file
View file

@ -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

159
node/Pack.cpp Normal file
View file

@ -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

141
node/Pack.hpp Normal file
View file

@ -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

64
node/Packet.cpp Normal file
View file

@ -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

812
node/Packet.hpp Normal file
View file

@ -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

141
node/Peer.cpp Normal file
View file

@ -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

435
node/Peer.hpp Normal file
View file

@ -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

View file

@ -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

221
node/Salsa20.cpp Normal file
View file

@ -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

73
node/Salsa20.hpp Normal file
View file

@ -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

133
node/SharedPtr.hpp Normal file
View file

@ -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

1022
node/Switch.cpp Normal file

File diff suppressed because it is too large Load diff

260
node/Switch.hpp Normal file
View file

@ -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

219
node/SysEnv.cpp Normal file
View file

@ -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

58
node/SysEnv.hpp Normal file
View file

@ -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

192
node/Thread.cpp Normal file
View file

@ -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

94
node/Thread.hpp Normal file
View file

@ -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

443
node/Topology.cpp Normal file
View file

@ -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

339
node/Topology.hpp Normal file
View file

@ -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

163
node/UdpSocket.cpp Normal file
View file

@ -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

103
node/UdpSocket.hpp Normal file
View file

@ -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

558
node/Utils.cpp Normal file
View file

@ -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

601
node/Utils.hpp Normal file
View file

@ -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