summaryrefslogtreecommitdiff
path: root/node/EthernetTap.cpp
diff options
context:
space:
mode:
authorAdam Ierymenko <adam.ierymenko@gmail.com>2013-07-09 14:06:55 -0400
committerAdam Ierymenko <adam.ierymenko@gmail.com>2013-07-09 14:06:55 -0400
commitef3e319c64600a921c9d6b33391f026644ec2492 (patch)
treeb2781949894625ebfae9d5c5f2dceb7cd1c4b395 /node/EthernetTap.cpp
parent41cd980bf72b9b22636dff91a1f6067a8474c819 (diff)
downloadinfinitytier-ef3e319c64600a921c9d6b33391f026644ec2492.tar.gz
infinitytier-ef3e319c64600a921c9d6b33391f026644ec2492.zip
Several things:
(1) Probable fix for issue #7 and major cleanup of EthernetTap code with consolidation for all unix-like systems and specialization for different flavors only when needed. (2) Refactor of Buffer<> to make its members private, and Packet to use Buffer's methods exclusively to access them. This improves clarity and means we're no longer lying about Buffer's role in the code's security posture. (3) Add -fstack-protect to Makefile to bounds check stack variables.
Diffstat (limited to 'node/EthernetTap.cpp')
-rw-r--r--node/EthernetTap.cpp562
1 files changed, 230 insertions, 332 deletions
diff --git a/node/EthernetTap.cpp b/node/EthernetTap.cpp
index 62eee69c..9158e937 100644
--- a/node/EthernetTap.cpp
+++ b/node/EthernetTap.cpp
@@ -30,15 +30,17 @@
#include "EthernetTap.hpp"
#include "Logger.hpp"
#include "RuntimeEnvironment.hpp"
+#include "Utils.hpp"
#include "Mutex.hpp"
-#include "MulticastGroup.hpp"
// ff:ff:ff:ff:ff:ff with no ADI
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
-/* ======================================================================== */
-#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
-/* ======================================================================== */
+//
+// TAP implementation for *nix OSes, with some specialization for different flavors
+//
+
+#ifdef __UNIX_LIKE__ /////////////////////////////////////////////////////////
#include <stdint.h>
#include <stdio.h>
@@ -52,10 +54,13 @@ static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
+#include <sys/select.h>
#include <netinet/in.h>
#include <net/if_arp.h>
#include <arpa/inet.h>
+#ifdef __LINUX__
+
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/if_addr.h>
@@ -64,23 +69,49 @@ static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC
#define ZT_ETHERTAP_IP_COMMAND "/sbin/ip"
#define ZT_ETHERTAP_SYSCTL_COMMAND "/sbin/sysctl"
+#endif // __LINUX__
+
+#ifdef __APPLE__
+
+#include <sys/uio.h>
+#include <sys/param.h>
+#include <sys/sysctl.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"
+
+#endif // __APPLE__
+
namespace ZeroTier {
+// Only permit one tap to be opened concurrently across the entire process
static Mutex __tapCreateLock;
-EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned int mtu)
+#ifdef __LINUX__
+EthernetTap::EthernetTap(
+ const RuntimeEnvironment *renv,
+ const MAC &mac,
+ unsigned int mtu,
+ void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &),
+ void *arg)
throw(std::runtime_error) :
_mac(mac),
_mtu(mtu),
_r(renv),
- _putBuf((unsigned char *)0),
- _getBuf((unsigned char *)0),
- _fd(0),
- _isReading(false)
+ _handler(handler),
+ _arg(arg),
+ _fd(0)
{
char procpath[128];
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
+ if (mtu > 4096)
+ throw std::runtime_error("max tap MTU is 4096");
+
_fd = ::open("/dev/net/tun",O_RDWR);
if (_fd <= 0)
throw std::runtime_error("could not open TUN/TAP device");
@@ -152,266 +183,36 @@ EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned
::close(sock);
- _putBuf = new unsigned char[((mtu + 16) * 2)];
- _getBuf = _putBuf + (mtu + 16);
+ ::pipe(_shutdownSignalPipe);
TRACE("tap %s created",_dev);
-}
-
-EthernetTap::~EthernetTap()
-{
- this->close();
- delete [] _putBuf;
-}
-
-void EthernetTap::whack()
-{
- // Linux requires nothing here
-}
-
-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)
- return false;
- if (_ips.count(ip) > 0)
- return true;
-
- // Remove and reconfigure if address is the same but netmask is different
- for(std::set<InetAddress>::iterator i(_ips.begin());i!=_ips.end();++i) {
- if (i->ipsEqual(ip)) {
- ___removeIp(_dev,_ips,*i);
- break;
- }
- }
-
- int cpid;
- if ((cpid = (int)fork()) == 0) {
- execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","add",ip.toString().c_str(),"dev",_dev,(const char *)0);
- exit(-1);
- } else {
- int exitcode = -1;
- waitpid(cpid,&exitcode,0);
- if (exitcode == 0) {
- _ips.insert(ip);
- return true;
- } else return false;
- }
-
- return false;
-}
-
-bool EthernetTap::removeIP(const InetAddress &ip)
-{
- Mutex::Lock _l(_ips_m);
- if (_ips.count(ip) > 0)
- return ___removeIp(_dev,_ips,ip);
- return false;
-}
-
-void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
-{
- if ((_fd > 0)&&(len <= _mtu)) {
- for(int i=0;i<6;++i)
- _putBuf[i] = to.data[i];
- for(int i=0;i<6;++i)
- _putBuf[i+6] = from.data[i];
- *((uint16_t *)(_putBuf + 12)) = htons((uint16_t)etherType);
- memcpy(_putBuf + 14,data,len);
- ::write(_fd,_putBuf,len + 14);
- }
-}
-
-unsigned int EthernetTap::get(MAC &from,MAC &to,unsigned int &etherType,void *buf)
-{
- for(;;) {
- if (_fd > 0) {
- _isReading_m.lock();
- _isReading = true;
- _isReadingThreadId = pthread_self();
- _isReading_m.unlock();
-
- int n = (int)::read(_fd,_getBuf,_mtu + 14);
-
- _isReading_m.lock();
- _isReading = false;
- _isReading_m.unlock();
-
- if (n > 14) {
- for(int i=0;i<6;++i)
- to.data[i] = _getBuf[i];
- for(int i=0;i<6;++i)
- from.data[i] = _getBuf[i + 6];
- etherType = ntohs(((uint16_t *)_getBuf)[6]);
- n -= 14;
- memcpy(buf,_getBuf + 14,n);
- return (unsigned int)n;
- } else if (n < 0) {
- if (_fd <= 0)
- break;
- else if ((errno == EINTR)||(errno == ETIMEDOUT))
- continue;
- else {
- TRACE("unexpected error reading from tap: %s",strerror(errno));
- ::close(_fd);
- _fd = 0;
- break;
- }
- } else {
- TRACE("incomplete read from tap: %d bytes",n);
- continue;
- }
- }
- }
- return 0;
-}
-
-std::string EthernetTap::deviceName()
-{
- return std::string(_dev);
-}
-bool EthernetTap::open() const
-{
- return (_fd > 0);
+ start();
}
-
-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;
-
- newGroups.insert(_blindWildcardMulticastGroup); // always join this
-
- 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)
+#endif // __LINUX__
+
+#ifdef __APPLE__
+EthernetTap::EthernetTap(
+ const RuntimeEnvironment *renv,
+ const MAC &mac,
+ unsigned int mtu,
+ void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &),
+ void *arg)
throw(std::runtime_error) :
_mac(mac),
_mtu(mtu),
_r(renv),
- _putBuf((unsigned char *)0),
- _getBuf((unsigned char *)0),
- _fd(0),
- _isReading(false)
+ _handler(handler),
+ _arg(arg),
+ _fd(0)
{
char devpath[64],ethaddr[64],mtustr[16];
struct stat tmp;
Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
+ if (mtu > 4096)
+ throw std::runtime_error("max tap MTU is 4096");
+
// Check for existence of ZT tap devices, try to load module if not there
if (stat("/dev/zt0",&tmp)) {
int kextpid;
@@ -453,8 +254,8 @@ EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned
sprintf(mtustr,"%u",mtu);
// Configure MAC address and MTU, bring interface up
- int cpid;
- if ((cpid = (int)fork()) == 0) {
+ long cpid;
+ if ((cpid = (long)fork()) == 0) {
execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,"lladdr",ethaddr,"mtu",mtustr,"up",(const char *)0);
exit(-1);
} else {
@@ -468,19 +269,23 @@ EthernetTap::EthernetTap(const RuntimeEnvironment *renv,const MAC &mac,unsigned
whack(); // turns on IPv6 on OSX
- _putBuf = new unsigned char[((mtu + 14) * 2)];
- _getBuf = _putBuf + (mtu + 14);
+ ::pipe(_shutdownSignalPipe);
+
+ start();
}
+#endif // __APPLE__
EthernetTap::~EthernetTap()
{
- this->close();
- delete [] _putBuf;
+ ::write(_shutdownSignalPipe[1],"\0",1); // causes thread to exit
+ join();
+ ::close(_fd);
}
+#ifdef __APPLE__
void EthernetTap::whack()
{
- int cpid = fork();
+ long cpid = (long)fork();
if (cpid == 0) {
execl(ZT_MAC_IPCONFIG,ZT_MAC_IPCONFIG,"set",_dev,"AUTOMATIC-V6",(const char *)0);
exit(-1);
@@ -492,8 +297,63 @@ void EthernetTap::whack()
}
}
}
+#else
+void EthernetTap::whack() {}
+#endif // __APPLE__ / !__APPLE__
-// Helper function to actually remove IP from network device, execs ifconfig
+#ifdef __LINUX__
+static bool ___removeIp(const char *_dev,const InetAddress &ip)
+{
+ long cpid = (long)fork();
+ if (cpid == 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);
+ return (exitcode == 0);
+ }
+}
+
+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)) {
+ if (___removeIp(_dev,*i)) {
+ _ips.erase(i);
+ break;
+ } else {
+ LOG("WARNING: failed to remove old IP/netmask %s to replace with %s",i->toString().c_str(),ip.toString().c_str());
+ }
+ }
+ }
+
+ long cpid;
+ if ((cpid = (long)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;
+}
+#endif // __LINUX__
+
+#ifdef __APPLE__
static bool ___removeIp(const char *_dev,const InetAddress &ip)
{
int cpid;
@@ -544,6 +404,7 @@ bool EthernetTap::addIP(const InetAddress &ip)
return false;
}
+#endif // __APPLE__
bool EthernetTap::removeIP(const InetAddress &ip)
{
@@ -559,94 +420,90 @@ bool EthernetTap::removeIP(const InetAddress &ip)
void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
+ char putBuf[4096 + 14];
if ((_fd > 0)&&(len <= _mtu)) {
for(int i=0;i<6;++i)
- _putBuf[i] = to.data[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);
+ 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);
+ int n = ::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);
+ LOG("ERROR: write underrun: %s tap write() wrote %d of %u bytes of frame",_dev,n,len);
}
}
}
-unsigned int EthernetTap::get(MAC &from,MAC &to,unsigned int &etherType,void *buf)
+std::string EthernetTap::deviceName() const
{
- for(;;) {
- if (_fd > 0) {
- _isReading_m.lock();
- _isReading = true;
- _isReadingThreadId = pthread_self();
- _isReading_m.unlock();
-
- int n = (int)::read(_fd,_getBuf,_mtu + 14);
+ return std::string(_dev);
+}
- _isReading_m.lock();
- _isReading = false;
- _isReading_m.unlock();
+#ifdef __LINUX__
+bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
+{
+ char *ptr,*ptr2;
+ unsigned char mac[6];
+ std::set<MulticastGroup> newGroups;
- 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;
+ 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;
}
- } else {
- TRACE("incomplete read from tap: %d bytes",n);
- continue;
+ if ((devname)&&(!strcmp(devname,_dev))&&(mcastmac)&&(Utils::unhex(mcastmac,mac,6) == 6))
+ newGroups.insert(MulticastGroup(MAC(mac),0));
}
}
+ ::close(fd);
}
- return 0;
-}
-std::string EthernetTap::deviceName()
-{
- return std::string(_dev);
-}
+ {
+ Mutex::Lock _l(_ips_m);
+ for(std::set<InetAddress>::const_iterator i(_ips.begin());i!=_ips.end();++i)
+ newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i));
+ }
-bool EthernetTap::open() const
-{
- return (_fd > 0);
-}
+ bool changed = false;
-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();
+ newGroups.insert(_blindWildcardMulticastGroup); // always join this
+
+ 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;
}
+#endif __LINUX__
+#ifdef __APPLE__
bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
{
std::set<MulticastGroup> newGroups;
@@ -690,13 +547,54 @@ bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
return changed;
}
+#endif // __APPLE__
+
+void EthernetTap::main()
+ throw()
+{
+ fd_set readfds,nullfds;
+ MAC to,from;
+ char getBuf[4096 + 14];
+ Buffer<4096> data;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&nullfds);
+ int nfds = (int)std::max(_shutdownSignalPipe[0],_fd) + 1;
+
+ for(;;) {
+ FD_SET(_shutdownSignalPipe[0],&readfds);
+ FD_SET(_fd,&readfds);
+ select(nfds,&readfds,&nullfds,&nullfds,(struct timeval *)0);
+
+ if (FD_ISSET(_shutdownSignalPipe[0],&readfds)) // writes to shutdown pipe terminate thread
+ break;
+
+ if (FD_ISSET(_fd,&readfds)) {
+ int n = (int)::read(_fd,getBuf,_mtu + 14);
+
+ if (n > 14) {
+ for(int i=0;i<6;++i)
+ to.data[i] = (unsigned char)getBuf[i];
+ for(int i=0;i<6;++i)
+ from.data[i] = (unsigned char)getBuf[i + 6];
+ data.copyFrom(getBuf + 14,(unsigned int)n - 14);
+ _handler(_arg,from,to,ntohs(((const uint16_t *)getBuf)[6]),data);
+ } else if (n < 0) {
+ if ((errno != EINTR)&&(errno != ETIMEDOUT)) {
+ TRACE("unexpected error reading from tap: %s",strerror(errno));
+ break;
+ }
+ }
+ }
+ }
+}
} // namespace ZeroTier
-/* ======================================================================== */
-#elif defined(_WIN32) /* -------------------------------------------------- */
-/* ======================================================================== */
+#endif // __UNIX_LIKE__ //////////////////////////////////////////////////////
+
+#ifdef __WINDOWS__
+
+// TODO
-/* ======================================================================== */
-#endif
-/* ======================================================================== */
+#endif // __WINDOWS__