New git repository for release - version 0.2.0 tagged
This commit is contained in:
176
node/Address.hpp
Normal file
176
node/Address.hpp
Normal 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
110
node/Array.hpp
Normal 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
113
node/AtomicCounter.hpp
Normal 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
94
node/BlobArray.hpp
Normal 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
398
node/Buffer.hpp
Normal 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
107
node/Condition.hpp
Normal 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
311
node/Constants.hpp
Normal 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
77
node/Defaults.cpp
Normal 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
74
node/Defaults.hpp
Normal 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
210
node/Demarc.cpp
Normal 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
168
node/Demarc.hpp
Normal 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
124
node/EllipticCurveKey.hpp
Normal 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
|
||||
|
||||
374
node/EllipticCurveKeyPair.cpp
Normal file
374
node/EllipticCurveKeyPair.cpp
Normal 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
|
||||
|
||||
128
node/EllipticCurveKeyPair.hpp
Normal file
128
node/EllipticCurveKeyPair.hpp
Normal 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
678
node/EthernetTap.cpp
Normal 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 ðerType,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 ðerType,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
199
node/EthernetTap.hpp
Normal 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 ðerType,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
81
node/HMAC.cpp
Normal 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
55
node/HMAC.hpp
Normal 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
323
node/Http.cpp
Normal 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
129
node/Http.hpp
Normal 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
301
node/Identity.cpp
Normal 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
431
node/Identity.hpp
Normal 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
148
node/InetAddress.cpp
Normal 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
334
node/InetAddress.hpp
Normal 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
141
node/Logger.cpp
Normal 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
90
node/Logger.hpp
Normal 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
160
node/MAC.hpp
Normal 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
144
node/MulticastGroup.hpp
Normal 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
197
node/Mutex.hpp
Normal 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
80
node/Network.cpp
Normal 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
162
node/Network.hpp
Normal 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
447
node/Node.cpp
Normal 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
128
node/Node.hpp
Normal 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
206
node/NodeConfig.cpp
Normal 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
136
node/NodeConfig.hpp
Normal 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
47
node/NonCopyable.hpp
Normal 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
159
node/Pack.cpp
Normal 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
141
node/Pack.hpp
Normal 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
64
node/Packet.cpp
Normal 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
812
node/Packet.hpp
Normal 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
141
node/Peer.cpp
Normal 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
435
node/Peer.hpp
Normal 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
|
||||
87
node/RuntimeEnvironment.hpp
Normal file
87
node/RuntimeEnvironment.hpp
Normal 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
221
node/Salsa20.cpp
Normal 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
73
node/Salsa20.hpp
Normal 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
133
node/SharedPtr.hpp
Normal 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
1022
node/Switch.cpp
Normal file
File diff suppressed because it is too large
Load Diff
260
node/Switch.hpp
Normal file
260
node/Switch.hpp
Normal 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
219
node/SysEnv.cpp
Normal 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
58
node/SysEnv.hpp
Normal 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
192
node/Thread.cpp
Normal 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
94
node/Thread.hpp
Normal 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
443
node/Topology.cpp
Normal 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
339
node/Topology.hpp
Normal 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
163
node/UdpSocket.cpp
Normal 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
103
node/UdpSocket.hpp
Normal 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
558
node/Utils.cpp
Normal 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
601
node/Utils.hpp
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user