mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2026-09-22 15:29:03 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d37c3ad30f | ||
|
|
4708231046 | ||
|
|
924f030994 | ||
|
|
6b76cac6cf | ||
|
|
2762db8408 | ||
|
|
edff13dcae | ||
|
|
e22fae2397 | ||
|
|
73dd9d3b74 | ||
|
|
8d2e20ede6 | ||
|
|
5c1c70a604 | ||
|
|
fb22ef053b | ||
|
|
4ba4269344 | ||
|
|
2dcf584834 | ||
|
|
13aba7640b | ||
|
|
2e46bb4264 | ||
|
|
1d37204a37 | ||
|
|
6b4346d1ac | ||
|
|
4e9280fc7a | ||
|
|
d02ecfb288 | ||
|
|
3afc629ac5 | ||
|
|
6e1339fedf | ||
|
|
7d4b6767eb | ||
|
|
93f9b4392d | ||
|
|
6df9546742 | ||
|
|
4dbeb97eb9 | ||
|
|
34349d1a61 | ||
|
|
6497a4a0f6 | ||
|
|
b8d29cb6ba | ||
|
|
e77e224171 | ||
|
|
dcea212e40 | ||
|
|
644db7a04a | ||
|
|
00b16f6aa6 | ||
|
|
1952db5069 | ||
|
|
6f8364ba59 | ||
|
|
eadff71d37 | ||
|
|
af62a6cade | ||
|
|
de4e29288d | ||
|
|
44d58ee871 | ||
|
|
9d5819ff44 | ||
|
|
08d7fe0432 | ||
|
|
9f2571cc01 | ||
|
|
b4b2e2ed9f | ||
|
|
17ebd0afc7 | ||
|
|
351b7f3a09 | ||
|
|
721625d512 | ||
|
|
282114e96c | ||
|
|
95d123cfc2 | ||
|
|
f5cbb45ab9 | ||
|
|
b7b9b9c0b7 | ||
|
|
715afc0314 | ||
|
|
f281886bfd | ||
|
|
4f0fcc582e | ||
|
|
aa59cfd545 | ||
|
|
f696299cfc | ||
|
|
b0719eacf9 | ||
|
|
c2187c8759 | ||
|
|
56296f96db | ||
|
|
be6b61b1bc | ||
|
|
f8d4611d15 | ||
|
|
f25bd41a03 | ||
|
|
fe3ad5e2cc | ||
|
|
502ea66f15 | ||
|
|
673aab5ba2 | ||
|
|
77457cbff1 | ||
|
|
db0d17cebb | ||
|
|
dd58006d65 | ||
|
|
f0ebb0b0f1 | ||
|
|
8c9d3146df | ||
|
|
117f977ce3 | ||
|
|
d8e5d9d6e8 | ||
|
|
1f5feb75fb | ||
|
|
05031ff938 | ||
|
|
adfb71fa6b | ||
|
|
37341048a9 | ||
|
|
b3491f9de1 | ||
|
|
80fc584923 | ||
|
|
b7389995f4 | ||
|
|
92d9ad4a7f | ||
|
|
8a804b5257 | ||
|
|
e3c5ada3a7 | ||
|
|
7adf0eac7e | ||
|
|
df0a8e8450 | ||
|
|
526435859f | ||
|
|
98d426e1d5 | ||
|
|
b80c229d87 | ||
|
|
9b93141dd0 | ||
|
|
59ebc9d41e | ||
|
|
f0199d6770 | ||
|
|
f2d372545a | ||
|
|
9809511c4c | ||
|
|
538e8a86c8 | ||
|
|
27aa6ea44e | ||
|
|
7475c4047e | ||
|
|
5e58a7d54a | ||
|
|
6a174483d8 | ||
|
|
a76d9d4408 | ||
|
|
1189f4a568 | ||
|
|
51766e6549 | ||
|
|
d315156733 | ||
|
|
4c4675e3ac | ||
|
|
49ef980ecf | ||
|
|
12692c551e | ||
|
|
00f9305ad8 | ||
|
|
0097949ba9 | ||
|
|
1b33a9e807 | ||
|
|
4a0b33561c |
No files matched your search
@@ -31,3 +31,4 @@
|
||||
.qmake.stash
|
||||
*.autosave
|
||||
/ZeroTier One.dmg
|
||||
/root-topology/*.secret
|
||||
@@ -36,19 +36,19 @@ SOURCES += main.cpp \
|
||||
../node/PacketDecoder.cpp \
|
||||
../node/Peer.cpp \
|
||||
../node/Poly1305.cpp \
|
||||
../node/RoutingTable.cpp \
|
||||
../node/Salsa20.cpp \
|
||||
../node/Service.cpp \
|
||||
../node/SHA512.cpp \
|
||||
../node/SocketManager.cpp \
|
||||
../node/SoftwareUpdater.cpp \
|
||||
../node/Switch.cpp \
|
||||
../node/SysEnv.cpp \
|
||||
../node/TcpSocket.cpp \
|
||||
../node/Topology.cpp \
|
||||
../node/UdpSocket.cpp \
|
||||
../node/Utils.cpp \
|
||||
../ext/lz4/lz4.c \
|
||||
../ext/lz4/lz4hc.c
|
||||
../node/Dictionary.cpp
|
||||
|
||||
HEADERS += mainwindow.h \
|
||||
aboutwindow.h \
|
||||
@@ -98,17 +98,15 @@ HEADERS += mainwindow.h \
|
||||
../node/SocketManager.hpp \
|
||||
../node/SoftwareUpdater.hpp \
|
||||
../node/Switch.hpp \
|
||||
../node/SysEnv.hpp \
|
||||
../node/RoutingTable.hpp \
|
||||
../node/TcpSocket.hpp \
|
||||
../node/Thread.hpp \
|
||||
../node/Topology.hpp \
|
||||
../node/UdpSocket.hpp \
|
||||
../node/Utils.hpp \
|
||||
../ext/lz4/lz4.h \
|
||||
../ext/lz4/lz4hc.h
|
||||
|
||||
win32:SOURCES += ../node/WindowsEthernetTap.cpp
|
||||
mac:SOURCES += ../node/UnixEthernetTap.cpp
|
||||
../node/EthernetTap.hpp \
|
||||
../node/EthernetTapFactory.hpp
|
||||
|
||||
FORMS += mainwindow.ui \
|
||||
aboutwindow.ui \
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@
|
||||
#include "../node/Defaults.hpp"
|
||||
|
||||
// Uncomment for testing to disable making sure Windows service is running
|
||||
//#define DISABLE_WINDOWS_SERVICE_MANAGEMENT
|
||||
#define DISABLE_WINDOWS_SERVICE_MANAGEMENT
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <WinSock2.h>
|
||||
|
||||
+14
-11
@@ -216,8 +216,9 @@ void MainWindow::timerEvent(QTimerEvent *event) // event can be null since code
|
||||
}
|
||||
|
||||
if (++this->cyclesSinceResponseFromService >= 3) {
|
||||
if (this->cyclesSinceResponseFromService == 3)
|
||||
if (this->cyclesSinceResponseFromService == 3) {
|
||||
QMessageBox::warning(this,"Service Not Running","Can't connect to the ZeroTier One service. Is it running?",QMessageBox::Ok);
|
||||
}
|
||||
ui->noNetworksLabel->setVisible(true);
|
||||
ui->noNetworksLabel->setText("Connecting to Service...");
|
||||
ui->bottomContainerWidget->setVisible(false);
|
||||
@@ -255,8 +256,8 @@ void MainWindow::customEvent(QEvent *event)
|
||||
std::map< std::string,std::vector<std::string> > newNetworks;
|
||||
for(unsigned long i=1;i<m->ztMessage.size();++i) {
|
||||
std::vector<std::string> l(ZeroTier::Node::NodeControlClient::splitLine(m->ztMessage[i]));
|
||||
// 200 listnetworks <nwid> <name> <status> <config age> <type> <dev> <ips>
|
||||
if ((l.size() == 9)&&(l[2].length() == 16))
|
||||
// 200 listnetworks <nwid> <name> <mac> <status> <config age> <type> <dev> <ips>
|
||||
if ((l.size() == 10)&&(l[2].length() == 16))
|
||||
newNetworks[l[2]] = l;
|
||||
}
|
||||
|
||||
@@ -283,10 +284,11 @@ void MainWindow::customEvent(QEvent *event)
|
||||
alreadyDisplayed.insert(nw->networkId());
|
||||
std::vector<std::string> &l = networks[nw->networkId()];
|
||||
nw->setNetworkName(l[3]);
|
||||
nw->setStatus(l[4],l[5]);
|
||||
nw->setNetworkType(l[6]);
|
||||
nw->setNetworkDeviceName(l[7]);
|
||||
nw->setIps(l[8]);
|
||||
nw->setMAC(l[4]);
|
||||
nw->setStatus(l[5],l[6]);
|
||||
nw->setNetworkType(l[7]);
|
||||
nw->setNetworkDeviceName(l[8]);
|
||||
nw->setIps(l[9]);
|
||||
}
|
||||
}
|
||||
for(std::map< std::string,std::vector<std::string> >::iterator nwdata(networks.begin());nwdata!=networks.end();++nwdata) {
|
||||
@@ -294,10 +296,11 @@ void MainWindow::customEvent(QEvent *event)
|
||||
std::vector<std::string> &l = nwdata->second;
|
||||
NetworkWidget *nw = new NetworkWidget((QWidget *)0,nwdata->first);
|
||||
nw->setNetworkName(l[3]);
|
||||
nw->setStatus(l[4],l[5]);
|
||||
nw->setNetworkType(l[6]);
|
||||
nw->setNetworkDeviceName(l[7]);
|
||||
nw->setIps(l[8]);
|
||||
nw->setMAC(l[4]);
|
||||
nw->setStatus(l[5],l[6]);
|
||||
nw->setNetworkType(l[7]);
|
||||
nw->setNetworkDeviceName(l[8]);
|
||||
nw->setIps(l[9]);
|
||||
QListWidgetItem *item = new QListWidgetItem();
|
||||
item->setSizeHint(nw->sizeHint());
|
||||
ui->networkListWidget->addItem(item);
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>720</width>
|
||||
<height>300</height>
|
||||
<width>850</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -229,8 +229,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>720</width>
|
||||
<height>22</height>
|
||||
<width>850</width>
|
||||
<height>34</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
|
||||
@@ -53,8 +53,8 @@ NetworkWidget::NetworkWidget(QWidget *parent,const std::string &nwid) :
|
||||
|
||||
QFontMetrics fm(ui->ipListWidget->font());
|
||||
int lineHeight = ui->ipListWidget->spacing() + fm.height();
|
||||
ui->ipListWidget->setMinimumHeight(lineHeight * 4);
|
||||
ui->ipListWidget->setMaximumHeight(lineHeight * 4);
|
||||
ui->ipListWidget->setMinimumHeight(lineHeight * 6);
|
||||
ui->ipListWidget->setMaximumHeight(lineHeight * 6);
|
||||
|
||||
#ifdef __APPLE__
|
||||
QWidgetList widgets = this->findChildren<QWidget*>();
|
||||
@@ -145,6 +145,11 @@ void NetworkWidget::setIps(const std::string &commaSeparatedList)
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkWidget::setMAC(const std::string &mac)
|
||||
{
|
||||
ui->macLabel->setText(QString(mac.c_str()));
|
||||
}
|
||||
|
||||
const std::string &NetworkWidget::networkId()
|
||||
{
|
||||
return networkIdStr;
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
void setNetworkType(const std::string &type);
|
||||
void setNetworkDeviceName(const std::string &dev);
|
||||
void setIps(const std::string &commaSeparatedList);
|
||||
void setMAC(const std::string &mac);
|
||||
|
||||
const std::string &networkId();
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>580</width>
|
||||
<width>689</width>
|
||||
<height>253</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@@ -40,6 +40,12 @@
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="leftWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
@@ -57,7 +63,13 @@
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<widget class="QWidget" name="networkInfoWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
@@ -178,6 +190,12 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="networkStatsWidget" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||
@@ -291,7 +309,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="font">
|
||||
<font>
|
||||
@@ -306,7 +324,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="deviceLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
@@ -332,6 +350,47 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>MAC:</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="macLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Your system's Ethernet MAC address.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>?</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -340,6 +399,9 @@
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Expanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
This directory is for old code that isn't used but we don't want to lose track of, and for anything else random like debug scripts.
|
||||
|
||||
#### Some stuff other than shelved code:
|
||||
|
||||
multicast-trace-receiver.rb: receives multicast trace UDP packets from ZT_TRACE_MULTICAST
|
||||
|
||||
encrypt, decrypt: useful shell scripts for AES-encrypting and decrypting files
|
||||
@@ -1,2 +0,0 @@
|
||||
This directory is for old code that isn't used but we don't want to lose
|
||||
track of, and for anything else random like debug scripts.
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
export PATH=/bin:/usr/bin
|
||||
|
||||
if [ ! -e /usr/bin/openssl ]; then
|
||||
echo $0: requires /usr/bin/openssl, please install openssl tools
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo $0: Usage: $0 '<input>' '[output]'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -r "$1" ]; then
|
||||
echo $0: $1 does not exist or is not readable.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
outpath=`echo "$1" | sed 's/[.]aes$//'`
|
||||
if [ "$#" -ge 2 ]; then
|
||||
outpath="$2"
|
||||
fi
|
||||
|
||||
if [ -f "$outpath" ]; then
|
||||
echo $0: $outpath already exists, delete or rename first.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
openssl aes-256-cbc -d -salt -in "$1" -out "$outpath"
|
||||
|
||||
echo $0: wrote "$outpath"
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
export PATH=/bin:/usr/bin
|
||||
|
||||
if [ ! -e /usr/bin/openssl ]; then
|
||||
echo $0: requires /usr/bin/openssl, please install openssl tools
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo $0: Usage: $0 '<input>' '[output]'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -r "$1" ]; then
|
||||
echo $0: $1 does not exist or is not readable.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
outpath="$1.aes"
|
||||
if [ "$#" -ge 2 ]; then
|
||||
outpath="$2"
|
||||
fi
|
||||
|
||||
if [ -f "$outpath" ]; then
|
||||
echo $0: $outpath already exists, delete or rename first.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
openssl aes-256-cbc -salt -in "$1" -out "$outpath"
|
||||
|
||||
echo $0: wrote "$outpath"
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
#
|
||||
# This can be used with the debug build option ZT_TRACE_MULTICAST to trace
|
||||
# a multicast cascade.
|
||||
#
|
||||
# Define ZT_TRACE_MULTICAST to the IP/port where this script will be listening.
|
||||
# The default port here is 6060, so an example would be to add:
|
||||
#
|
||||
# -DZT_TRACE_MULTICAST=\"10.0.0.1/6060\"
|
||||
#
|
||||
# ... to DEFS in the Makefile. Then build and run ZeroTier One on a testnet and
|
||||
# the box defined as the trace endpoint will get spammed with UDP packets
|
||||
# containing trace information for multicast propagation. This script then dumps
|
||||
# these trace packets to stdout. Look at the code in PacketDecoder.cpp to see
|
||||
# what this information entails.
|
||||
#
|
||||
|
||||
require 'socket'
|
||||
|
||||
s = UDPSocket.new
|
||||
s.bind('0.0.0.0',6060)
|
||||
|
||||
loop {
|
||||
m = s.recvfrom(4096)[0].chomp
|
||||
puts m if m.length > 0
|
||||
}
|
||||
@@ -38,9 +38,6 @@ case "$system" in
|
||||
x86_64|amd64|x64)
|
||||
machine="x64"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported machine type: $machine"
|
||||
exit 2
|
||||
esac
|
||||
|
||||
echo "Assembling Linux installer for $machine and version $vmajor.$vminor.$revision"
|
||||
|
||||
@@ -36,16 +36,20 @@
|
||||
#include "IpcConnection.hpp"
|
||||
|
||||
#ifndef __WINDOWS__
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
IpcConnection::IpcConnection(const char *endpoint,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
|
||||
IpcConnection::IpcConnection(const char *endpoint,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
|
||||
_handler(commandHandler),
|
||||
_arg(arg),
|
||||
_timeout(timeout),
|
||||
#ifdef __WINDOWS__
|
||||
_sock(INVALID_HANDLE_VALUE),
|
||||
_incoming(false),
|
||||
@@ -81,12 +85,13 @@ IpcConnection::IpcConnection(const char *endpoint,void (*commandHandler)(void *,
|
||||
}
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
IpcConnection::IpcConnection(HANDLE s,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
|
||||
IpcConnection::IpcConnection(HANDLE s,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
|
||||
#else
|
||||
IpcConnection::IpcConnection(int s,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
|
||||
IpcConnection::IpcConnection(int s,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
|
||||
#endif
|
||||
_handler(commandHandler),
|
||||
_arg(arg),
|
||||
_timeout(timeout),
|
||||
_sock(s),
|
||||
#ifdef __WINDOWS__
|
||||
_incoming(true),
|
||||
@@ -104,18 +109,23 @@ IpcConnection::~IpcConnection()
|
||||
_writeLock.unlock();
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
while (_running) {
|
||||
Thread::cancelIO(_thread);
|
||||
Thread::cancelIO(_thread); // cause Windows to break from blocking read and detect shutdown
|
||||
Sleep(100);
|
||||
}
|
||||
#else
|
||||
|
||||
#else // !__WINDOWS__
|
||||
|
||||
int s = _sock;
|
||||
_sock = 0;
|
||||
if (s > 0) {
|
||||
::shutdown(s,SHUT_RDWR);
|
||||
::close(s);
|
||||
}
|
||||
#endif
|
||||
Thread::join(_thread);
|
||||
|
||||
#endif // __WINDOWS__ / !__WINDOWS__
|
||||
}
|
||||
|
||||
void IpcConnection::printf(const char *format,...)
|
||||
@@ -134,7 +144,7 @@ void IpcConnection::printf(const char *format,...)
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
_writeBuf.append(tmp,n);
|
||||
Thread::cancelIO(_thread);
|
||||
Thread::cancelIO(_thread); // cause Windows to break from blocking read and service write buffer
|
||||
#else
|
||||
if (_sock > 0)
|
||||
::write(_sock,tmp,n);
|
||||
@@ -144,20 +154,39 @@ void IpcConnection::printf(const char *format,...)
|
||||
void IpcConnection::threadMain()
|
||||
throw()
|
||||
{
|
||||
char tmp[65536];
|
||||
char linebuf[65536];
|
||||
char tmp[16384];
|
||||
char linebuf[16384];
|
||||
unsigned int lineptr = 0;
|
||||
char c;
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
DWORD n,i;
|
||||
std::string wbuf;
|
||||
#else
|
||||
|
||||
#else // !__WINDOWS__
|
||||
|
||||
int s,n,i;
|
||||
#endif
|
||||
fd_set readfds,writefds,errorfds;
|
||||
struct timeval tout;
|
||||
|
||||
#ifdef SO_NOSIGPIPE
|
||||
if (_sock > 0) {
|
||||
i = 1;
|
||||
::setsockopt(_sock,SOL_SOCKET,SO_NOSIGPIPE,(char *)&i,sizeof(i));
|
||||
}
|
||||
#endif // SO_NOSIGPIPE
|
||||
|
||||
#endif // __WINDOWS__ / !__WINDOWS__
|
||||
|
||||
while (_run) {
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
/* Note that we do not use fucking timeouts in Windows, since it does seem
|
||||
* to properly detect named pipe endpoint close. But we do use a write buffer
|
||||
* because Windows won't let you divorce reading and writing threads without
|
||||
* all that OVERLAPPED cruft. */
|
||||
{
|
||||
Mutex::Lock _l(_writeLock);
|
||||
if (!_run)
|
||||
@@ -187,16 +216,42 @@ void IpcConnection::threadMain()
|
||||
}
|
||||
if (!_run)
|
||||
break;
|
||||
#else
|
||||
|
||||
#else // !__WINDOWS__
|
||||
|
||||
/* So today I learned that there is no reliable way to detect a half-closed
|
||||
* Unix domain socket. So to make sure we don't leave orphaned sockets around
|
||||
* we just use fucking timeouts. If a socket fucking times out, we break from
|
||||
* the I/O loop and terminate the thread. But this IpcConnection code is ugly
|
||||
* so maybe the OS is simply offended by it and refuses to reveal its mysteries
|
||||
* to me. Oh well... this IPC code will probably get canned when we go to
|
||||
* local HTTP RESTful interfaces or soemthing like that. */
|
||||
if ((s = _sock) <= 0)
|
||||
break;
|
||||
n = (int)::read(s,tmp,sizeof(tmp));
|
||||
if ((n <= 0)||(_sock <= 0))
|
||||
break;
|
||||
#endif
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&writefds);
|
||||
FD_ZERO(&errorfds);
|
||||
FD_SET(s,&readfds);
|
||||
FD_SET(s,&errorfds);
|
||||
tout.tv_sec = _timeout; // use a fucking timeout
|
||||
tout.tv_usec = 0;
|
||||
if (select(s+1,&readfds,&writefds,&errorfds,&tout) <= 0) {
|
||||
break; // socket has fucking timed out
|
||||
} else {
|
||||
if (FD_ISSET(s,&errorfds))
|
||||
break; // socket has an exception... sometimes works
|
||||
else {
|
||||
n = (int)::read(s,tmp,sizeof(tmp));
|
||||
if ((n <= 0)||(_sock <= 0))
|
||||
break; // read returned error... sometimes works
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __WINDOWS__ / !__WINDOWS__
|
||||
|
||||
for(i=0;i<n;++i) {
|
||||
c = (linebuf[lineptr] = tmp[i]);
|
||||
if ((c == '\r')||(c == '\n')||(lineptr == (sizeof(linebuf) - 1))) {
|
||||
if ((c == '\r')||(c == '\n')||(c == (char)0)||(lineptr == (sizeof(linebuf) - 1))) {
|
||||
if (lineptr) {
|
||||
linebuf[lineptr] = (char)0;
|
||||
_handler(_arg,this,IPC_EVENT_COMMAND,linebuf);
|
||||
@@ -211,11 +266,13 @@ void IpcConnection::threadMain()
|
||||
_writeLock.unlock();
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
if (_incoming)
|
||||
DisconnectNamedPipe(_sock);
|
||||
CloseHandle(_sock);
|
||||
_running = false;
|
||||
#endif
|
||||
|
||||
#endif // __WINDOWS__
|
||||
|
||||
if (r)
|
||||
_handler(_arg,this,IPC_EVENT_CONNECTION_CLOSED,(const char *)0);
|
||||
@@ -28,10 +28,10 @@
|
||||
#ifndef ZT_IPCCONNECTION_HPP
|
||||
#define ZT_IPCCONNECTION_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Thread.hpp"
|
||||
#include "NonCopyable.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/Thread.hpp"
|
||||
#include "../node/NonCopyable.hpp"
|
||||
#include "../node/Mutex.hpp"
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <WinSock2.h>
|
||||
@@ -61,11 +61,12 @@ public:
|
||||
* Connect to an IPC endpoint
|
||||
*
|
||||
* @param endpoint Endpoint path
|
||||
* @param timeout Inactivity timeout in seconds
|
||||
* @param commandHandler Command handler function
|
||||
* @param arg First argument to command handler
|
||||
* @throws std::runtime_error Unable to connect
|
||||
*/
|
||||
IpcConnection(const char *endpoint,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
|
||||
IpcConnection(const char *endpoint,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
|
||||
~IpcConnection();
|
||||
|
||||
/**
|
||||
@@ -80,13 +81,14 @@ public:
|
||||
private:
|
||||
// Used by IpcListener to construct incoming connections
|
||||
#ifdef __WINDOWS__
|
||||
IpcConnection(HANDLE s,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
|
||||
IpcConnection(HANDLE s,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
|
||||
#else
|
||||
IpcConnection(int s,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
|
||||
IpcConnection(int s,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
|
||||
#endif
|
||||
|
||||
void (*_handler)(void *,IpcConnection *,IpcConnection::EventType,const char *);
|
||||
void *_arg;
|
||||
unsigned int _timeout;
|
||||
#ifdef __WINDOWS__
|
||||
HANDLE _sock;
|
||||
std::string _writeBuf;
|
||||
@@ -42,10 +42,11 @@
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
IpcListener::IpcListener(const char *ep,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
|
||||
IpcListener::IpcListener(const char *ep,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg) :
|
||||
_endpoint(ep),
|
||||
_handler(commandHandler),
|
||||
_arg(arg),
|
||||
_timeout(timeout),
|
||||
#ifdef __WINDOWS__
|
||||
_run(true),
|
||||
_running(true)
|
||||
@@ -130,7 +131,7 @@ void IpcListener::threadMain()
|
||||
break;
|
||||
}
|
||||
try {
|
||||
_handler(_arg,new IpcConnection(s,_handler,_arg),IpcConnection::IPC_EVENT_NEW_CONNECTION,(const char *)0);
|
||||
_handler(_arg,new IpcConnection(s,_timeout,_handler,_arg),IpcConnection::IPC_EVENT_NEW_CONNECTION,(const char *)0);
|
||||
} catch ( ... ) {} // handlers should not throw
|
||||
} else {
|
||||
CloseHandle(s);
|
||||
@@ -155,7 +156,7 @@ void IpcListener::threadMain()
|
||||
break;
|
||||
}
|
||||
try {
|
||||
_handler(_arg,new IpcConnection(s,_handler,_arg),IpcConnection::IPC_EVENT_NEW_CONNECTION,(const char *)0);
|
||||
_handler(_arg,new IpcConnection(s,_timeout,_handler,_arg),IpcConnection::IPC_EVENT_NEW_CONNECTION,(const char *)0);
|
||||
} catch ( ... ) {} // handlers should not throw
|
||||
}
|
||||
#endif
|
||||
@@ -28,22 +28,14 @@
|
||||
#ifndef ZT_IPCLISTENER_HPP
|
||||
#define ZT_IPCLISTENER_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Thread.hpp"
|
||||
#include "NonCopyable.hpp"
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/Thread.hpp"
|
||||
#include "../node/NonCopyable.hpp"
|
||||
#include "IpcConnection.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#include <WinSock2.h>
|
||||
#include <Windows.h>
|
||||
#define ZT_IPC_ENDPOINT_BASE "\\\\.\\pipe\\ZeroTierOne-"
|
||||
#else
|
||||
#define ZT_IPC_ENDPOINT_BASE "/tmp/.ZeroTierOne-"
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
@@ -58,7 +50,7 @@ public:
|
||||
* The supplied handler is passed on to incoming instances of IpcConnection. When
|
||||
* a connection is first opened, it is called with IPC_EVENT_NEW_CONNECTION. The
|
||||
* receiver must take ownership of the connection object. When a connection is
|
||||
* closed, IPC_EVENT_CONNECTION_CLOSING is generated. At this point (or after) the
|
||||
* closed, IPC_EVENT_CONNECTION_CLOSED is generated. At this point (or after) the
|
||||
* receiver must delete the object. IPC_EVENT_COMMAND is generated when lines of
|
||||
* text are read, and in this cases the last argument is not NULL. No closed event
|
||||
* is generated in the event of manual delete if the connection is still open.
|
||||
@@ -67,11 +59,13 @@ public:
|
||||
* some kind of actor model or something if it gets too unweildy. But for now the
|
||||
* use cases are simple enough that it's not too bad.
|
||||
*
|
||||
* @param IPC endpoint name (OS-specific)
|
||||
* @param timeout Endpoint inactivity timeout in seconds
|
||||
* @param commandHandler Function to call for each command
|
||||
* @param arg First argument to pass to handler
|
||||
* @throws std::runtime_error Unable to bind to endpoint
|
||||
*/
|
||||
IpcListener(const char *ep,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
|
||||
IpcListener(const char *ep,unsigned int timeout,void (*commandHandler)(void *,IpcConnection *,IpcConnection::EventType,const char *),void *arg);
|
||||
|
||||
~IpcListener();
|
||||
|
||||
@@ -82,6 +76,7 @@ private:
|
||||
std::string _endpoint;
|
||||
void (*_handler)(void *,IpcConnection *,IpcConnection::EventType,const char *);
|
||||
void *_arg;
|
||||
unsigned int _timeout;
|
||||
#ifdef __WINDOWS__
|
||||
volatile bool _run;
|
||||
volatile bool _running;
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* ZeroTier One - Global Peer to Peer Ethernet
|
||||
* Copyright (C) 2011-2014 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 "NodeControlClient.hpp"
|
||||
#include "../node/Constants.hpp"
|
||||
#include "../node/Utils.hpp"
|
||||
#include "../node/Defaults.hpp"
|
||||
#include "IpcConnection.hpp"
|
||||
#include "IpcListener.hpp"
|
||||
#include "NodeControlService.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
struct _NodeControlClientImpl
|
||||
{
|
||||
void (*resultHandler)(void *,const char *);
|
||||
void *arg;
|
||||
IpcConnection *ipcc;
|
||||
std::string err;
|
||||
};
|
||||
|
||||
static void _CBipcResultHandler(void *arg,IpcConnection *ipcc,IpcConnection::EventType event,const char *result)
|
||||
{
|
||||
if ((event == IpcConnection::IPC_EVENT_COMMAND)&&(result)) {
|
||||
if (strcmp(result,"200 auth OK"))
|
||||
((_NodeControlClientImpl *)arg)->resultHandler(((_NodeControlClientImpl *)arg)->arg,result);
|
||||
}
|
||||
}
|
||||
|
||||
NodeControlClient::NodeControlClient(const char *ep,const char *authToken,void (*resultHandler)(void *,const char *),void *arg)
|
||||
throw() :
|
||||
_impl((void *)new _NodeControlClientImpl)
|
||||
{
|
||||
_NodeControlClientImpl *impl = (_NodeControlClientImpl *)_impl;
|
||||
impl->resultHandler = resultHandler;
|
||||
impl->arg = arg;
|
||||
try {
|
||||
impl->ipcc = new IpcConnection(ep,ZT_IPC_TIMEOUT,&_CBipcResultHandler,_impl);
|
||||
impl->ipcc->printf("auth %s"ZT_EOL_S,authToken);
|
||||
} catch ( ... ) {
|
||||
impl->ipcc = (IpcConnection *)0;
|
||||
impl->err = "failure connecting to running ZeroTier One service";
|
||||
}
|
||||
}
|
||||
|
||||
NodeControlClient::~NodeControlClient()
|
||||
{
|
||||
if (_impl) {
|
||||
delete ((_NodeControlClientImpl *)_impl)->ipcc;
|
||||
delete (_NodeControlClientImpl *)_impl;
|
||||
}
|
||||
}
|
||||
|
||||
const char *NodeControlClient::error() const
|
||||
throw()
|
||||
{
|
||||
if (((_NodeControlClientImpl *)_impl)->err.length())
|
||||
return ((_NodeControlClientImpl *)_impl)->err.c_str();
|
||||
return (const char *)0;
|
||||
}
|
||||
|
||||
void NodeControlClient::send(const char *command)
|
||||
throw()
|
||||
{
|
||||
try {
|
||||
if (((_NodeControlClientImpl *)_impl)->ipcc)
|
||||
((_NodeControlClientImpl *)_impl)->ipcc->printf("%s"ZT_EOL_S,command);
|
||||
} catch ( ... ) {}
|
||||
}
|
||||
|
||||
std::vector<std::string> NodeControlClient::splitLine(const char *line)
|
||||
{
|
||||
return Utils::split(line," ","\\","\"");
|
||||
}
|
||||
|
||||
const char *NodeControlClient::authTokenDefaultUserPath()
|
||||
{
|
||||
static std::string dlp;
|
||||
static Mutex dlp_m;
|
||||
|
||||
Mutex::Lock _l(dlp_m);
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
|
||||
if (!dlp.length()) {
|
||||
char buf[16384];
|
||||
if (SUCCEEDED(SHGetFolderPathA(NULL,CSIDL_APPDATA,NULL,0,buf)))
|
||||
dlp = (std::string(buf) + "\\ZeroTier\\One\\authtoken.secret");
|
||||
}
|
||||
|
||||
#else // not __WINDOWS__
|
||||
|
||||
if (!dlp.length()) {
|
||||
const char *home = getenv("HOME");
|
||||
if (home) {
|
||||
#ifdef __APPLE__
|
||||
dlp = (std::string(home) + "/Library/Application Support/ZeroTier/One/authtoken.secret");
|
||||
#else
|
||||
dlp = (std::string(home) + "/.zeroTierOneAuthToken");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __WINDOWS__ or not __WINDOWS__
|
||||
|
||||
return dlp.c_str();
|
||||
}
|
||||
|
||||
std::string NodeControlClient::getAuthToken(const char *path,bool generateIfNotFound)
|
||||
{
|
||||
unsigned char randbuf[24];
|
||||
std::string token;
|
||||
|
||||
if (Utils::readFile(path,token))
|
||||
return Utils::trim(token);
|
||||
else token = "";
|
||||
|
||||
if (generateIfNotFound) {
|
||||
Utils::getSecureRandom(randbuf,sizeof(randbuf));
|
||||
for(unsigned int i=0;i<sizeof(randbuf);++i)
|
||||
token.push_back(("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")[(unsigned int)randbuf[i] % 62]);
|
||||
if (!Utils::writeFile(path,token))
|
||||
return std::string();
|
||||
Utils::lockDownFile(path,false);
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* ZeroTier One - Global Peer to Peer Ethernet
|
||||
* Copyright (C) 2011-2014 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_NODECONTROLCLIENT_HPP
|
||||
#define ZT_NODECONTROLCLIENT_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __WINDOWS__
|
||||
#define ZT_IPC_ENDPOINT_BASE "\\\\.\\pipe\\ZeroTierOne-"
|
||||
#else
|
||||
#define ZT_IPC_ENDPOINT_BASE "/tmp/.ZeroTierOne-"
|
||||
#endif
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Client for controlling a local ZeroTier One node
|
||||
*/
|
||||
class NodeControlClient
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Create a new node config client
|
||||
*
|
||||
* Initialization may fail. Call error() to check.
|
||||
*
|
||||
* @param ep Endpoint to connect to (OS-dependent)
|
||||
* @param resultHandler Function to call when commands provide results
|
||||
* @param arg First argument to result handler
|
||||
*/
|
||||
NodeControlClient(const char *ep,const char *authToken,void (*resultHandler)(void *,const char *),void *arg)
|
||||
throw();
|
||||
|
||||
~NodeControlClient();
|
||||
|
||||
/**
|
||||
* @return Initialization error or NULL if none
|
||||
*/
|
||||
const char *error() const
|
||||
throw();
|
||||
|
||||
/**
|
||||
* Send a command to the local node
|
||||
*
|
||||
* Note that the returned conversation ID will never be 0. A return value
|
||||
* of 0 indicates a fatal error such as failure to bind to any local UDP
|
||||
* port.
|
||||
*
|
||||
* @param command
|
||||
* @return Conversation ID that will be provided to result handler when/if results are sent back
|
||||
*/
|
||||
void send(const char *command)
|
||||
throw();
|
||||
inline void send(const std::string &command)
|
||||
throw() { return send(command.c_str()); }
|
||||
|
||||
/**
|
||||
* Split a line of results
|
||||
*
|
||||
* @param line Line to split
|
||||
* @return Vector of fields
|
||||
*/
|
||||
static std::vector<std::string> splitLine(const char *line);
|
||||
static inline std::vector<std::string> splitLine(const std::string &line) { return splitLine(line.c_str()); }
|
||||
|
||||
/**
|
||||
* @return Default path for current user's authtoken.secret or ~/.zeroTierOneAuthToken (location is platform-dependent)
|
||||
*/
|
||||
static const char *authTokenDefaultUserPath();
|
||||
|
||||
/**
|
||||
* Load (or generate) the authentication token
|
||||
*
|
||||
* @param path Full path to authtoken.secret
|
||||
* @param generateIfNotFound If true, generate and save if not found or readable (requires appropriate privileges, returns empty on failure)
|
||||
* @return Authentication token or empty string on failure
|
||||
*/
|
||||
static std::string getAuthToken(const char *path,bool generateIfNotFound);
|
||||
|
||||
private:
|
||||
// NodeControlClient is not copyable
|
||||
NodeControlClient(const NodeControlClient&);
|
||||
const NodeControlClient& operator=(const NodeControlClient&);
|
||||
|
||||
void *_impl;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
Loaded 20 of 121 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user