2025-08-21 11:01:52 -04:00
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
2015-04-09 20:54:00 -07:00
*
2025-08-21 11:01:52 -04:00
* (c) ZeroTier, Inc.
* https://www.zerotier.com/
2015-04-09 20:54:00 -07:00
*/
2020-11-20 21:10:08 -05:00
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
2024-09-15 17:34:01 -04:00
#include "node/ECC.hpp"
2020-11-20 21:10:08 -05:00
#endif
2025-08-11 13:22:39 -04:00
#include "node/Constants.hpp"
#include <errno.h>
#include <stdint.h>
2015-04-09 20:54:00 -07:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2015-04-15 17:00:26 -07:00
#include <time.h>
2015-04-24 15:44:39 -07:00
2015-04-15 17:00:26 -07:00
#ifdef __WINDOWS__
2025-07-08 15:11:57 -04:00
// clang-format off
2021-12-27 14:07:35 -05:00
#include <winsock2.h>
#include <windows.h>
2015-04-15 17:00:26 -07:00
#include <tchar.h>
#include <wchar.h>
#include <lmcons.h>
#include <newdev.h>
#include <atlbase.h>
2020-09-14 15:17:59 -07:00
#include <iphlpapi.h>
#include <iomanip>
2020-09-16 10:03:03 -07:00
#include <shlobj.h>
2015-07-30 11:31:38 -07:00
#include "osdep/WindowsEthernetTap.hpp"
2015-04-15 17:00:26 -07:00
#include "windows/ZeroTierOne/ServiceInstaller.h"
#include "windows/ZeroTierOne/ServiceBase.h"
#include "windows/ZeroTierOne/ZeroTierOneService.h"
2025-07-08 15:11:57 -04:00
// clang-format on
2015-04-15 17:00:26 -07:00
#else
2017-02-15 16:25:49 -08:00
#include <dirent.h>
2025-08-11 13:22:39 -04:00
#include <fcntl.h>
#include <pwd.h>
2015-04-15 17:00:26 -07:00
#include <signal.h>
2025-08-11 13:22:39 -04:00
#include <sys/stat.h>
2020-08-30 14:19:56 -07:00
#include <sys/types.h>
2025-08-11 13:22:39 -04:00
#include <sys/uio.h>
#include <unistd.h>
#ifdef __LINUX__
2025-02-26 10:27:23 -08:00
#include "osdep/ExtOsdep.hpp"
2025-08-11 13:22:39 -04:00
#include <ifaddrs.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
2018-10-26 11:59:55 -07:00
#ifndef ZT_NO_CAPABILITIES
2017-02-15 16:25:49 -08:00
#include <linux/capability.h>
#include <linux/securebits.h>
2016-10-16 13:35:29 +02:00
#endif
2015-04-15 17:00:26 -07:00
#endif
2018-10-26 11:59:55 -07:00
#endif
2015-04-09 20:54:00 -07:00
2015-04-15 17:00:26 -07:00
#include "include/ZeroTierOne.h"
2021-09-01 21:37:49 -07:00
#include "node/Bond.hpp"
2025-08-11 13:22:39 -04:00
#include "node/Buffer.hpp"
#include "node/CertificateOfMembership.hpp"
#include "node/Identity.hpp"
#include "node/NetworkController.hpp"
#include "node/Utils.hpp"
#include "node/World.hpp"
#include "osdep/Http.hpp"
#include "osdep/OSUtils.hpp"
#include "osdep/Thread.hpp"
2015-04-15 14:44:30 -07:00
#include "service/OneService.hpp"
2025-08-11 13:22:39 -04:00
#include "version.h"
2015-04-16 15:51:56 -07:00
2025-08-11 13:22:39 -04:00
#include <algorithm>
#include <iostream>
2022-05-13 09:34:15 -07:00
#include <nlohmann/json.hpp>
2025-08-11 13:22:39 -04:00
#include <sstream>
#include <stdexcept>
#include <string>
2016-08-24 13:37:57 -07:00
2020-09-11 16:43:30 -07:00
#ifdef __APPLE__
2020-09-15 16:49:49 -07:00
#include <CoreServices/CoreServices.h>
2025-08-11 13:22:39 -04:00
#include <SystemConfiguration/SystemConfiguration.h>
2020-09-11 16:43:30 -07:00
#include <ifaddrs.h>
2025-08-11 13:22:39 -04:00
#include <sys/socket.h>
#include <sys/types.h>
2020-09-11 16:43:30 -07:00
#endif
2015-09-24 16:21:36 -07:00
#define ZT_PID_PATH "zerotier-one.pid"
2015-04-09 20:54:00 -07:00
using namespace ZeroTier ;
2025-08-11 13:22:39 -04:00
static OneService * volatile zt1Service = ( OneService * ) 0 ;
2015-04-15 17:00:26 -07:00
2025-08-11 13:22:39 -04:00
#define PROGRAM_NAME "ZeroTier One"
2025-08-21 11:01:52 -04:00
#define COPYRIGHT_NOTICE "Copyright (c) ZeroTier, Inc."
#ifdef ZT_NONFREE_CONTROLLER
2025-09-05 15:39:47 -04:00
#define LICENSE_GRANT ZT_EOL_S "Licensed under a Source-Available License for Non-Commercial" ZT_EOL_S "Use (nonfree/LICENSE.md). Use of this build for Commercial Use" ZT_EOL_S "requires a paid subscription plan or a commercial license" ZT_EOL_S "agreement with ZeroTier, Inc. Visit https: //www.zerotier.com for" ZT_EOL_S "more information."
2025-08-21 11:01:52 -04:00
#else
#define LICENSE_GRANT "Licensed under Mozilla Public License v2.0 (LICENSE-MPL.txt)."
#endif
2016-03-04 16:37:36 +11:00
2015-04-16 14:13:44 -07:00
/****************************************************************************/
/* zerotier-cli personality */
/****************************************************************************/
2025-08-11 13:22:39 -04:00
static void cliPrintHelp ( const char * pn , FILE * out )
2015-04-16 14:55:36 -07:00
{
2025-08-11 13:22:39 -04:00
fprintf (
out ,
2017-03-14 14:40:17 -07:00
"%s version %d.%d.%d build %d (platform %d arch %d)" ZT_EOL_S ,
2016-03-04 16:42:30 +11:00
PROGRAM_NAME ,
2025-08-11 13:22:39 -04:00
ZEROTIER_ONE_VERSION_MAJOR ,
ZEROTIER_ONE_VERSION_MINOR ,
ZEROTIER_ONE_VERSION_REVISION ,
ZEROTIER_ONE_VERSION_BUILD ,
ZT_BUILD_PLATFORM ,
ZT_BUILD_ARCHITECTURE );
fprintf ( out , COPYRIGHT_NOTICE ZT_EOL_S LICENSE_GRANT ZT_EOL_S );
2025-08-21 11:01:52 -04:00
fprintf ( out , ZT_EOL_S "Usage: %s [-switches] <command/path> [<args>]" ZT_EOL_S "" ZT_EOL_S , pn );
2025-08-11 13:22:39 -04:00
fprintf ( out , "Available switches:" ZT_EOL_S );
fprintf ( out , " -h - Display this help" ZT_EOL_S );
fprintf ( out , " -v - Show version" ZT_EOL_S );
fprintf ( out , " -j - Display full raw JSON output" ZT_EOL_S );
fprintf ( out , " -D<path> - ZeroTier home path for parameter auto-detect" ZT_EOL_S );
fprintf ( out , " -p<port> - HTTP port (default: auto)" ZT_EOL_S );
fprintf ( out , " -T<token> - Authentication token (default: auto)" ZT_EOL_S );
fprintf ( out , ZT_EOL_S "Available commands:" ZT_EOL_S );
fprintf ( out , " info - Display status info" ZT_EOL_S );
fprintf ( out , " listpeers - List all peers" ZT_EOL_S );
fprintf ( out , " peers - List all peers (prettier)" ZT_EOL_S );
fprintf ( out , " listnetworks - List all networks" ZT_EOL_S );
fprintf ( out , " join <network ID> - Join a network" ZT_EOL_S );
fprintf ( out , " leave <network ID> - Leave a network" ZT_EOL_S );
fprintf ( out , " set <network ID> <setting> - Set a network setting" ZT_EOL_S );
fprintf ( out , " get <network ID> <setting> - Get a network setting" ZT_EOL_S );
fprintf ( out , " dump - Debug settings dump for support" ZT_EOL_S );
fprintf ( out , ZT_EOL_S "Available settings:" ZT_EOL_S );
fprintf ( out , " Settings to use with [get/set] may include property names from " ZT_EOL_S );
fprintf ( out , " the JSON output of \" zerotier-cli -j listnetworks \" . Additionally, " ZT_EOL_S );
fprintf ( out , " (ip, ip4, ip6, ip6plane, and ip6prefix can be used). For instance:" ZT_EOL_S );
fprintf ( out , " zerotier-cli get <network ID> ip6plane will return the 6PLANE address" ZT_EOL_S );
fprintf ( out , " assigned to this node." ZT_EOL_S );
2015-04-16 14:55:36 -07:00
}
2025-08-11 13:22:39 -04:00
static std :: string cliFixJsonCRs ( const std :: string & s )
2015-04-16 14:55:36 -07:00
{
std :: string r ;
2025-08-11 13:22:39 -04:00
for ( std :: string :: const_iterator c ( s . begin ()); c != s . end (); ++ c ) {
2015-04-16 14:55:36 -07:00
if ( * c == '\n' )
r . append ( ZT_EOL_S );
2025-08-11 13:22:39 -04:00
else
r . push_back ( * c );
2015-04-16 14:55:36 -07:00
}
return r ;
}
2015-04-16 14:13:44 -07:00
#ifdef __WINDOWS__
static int cli ( int argc , _TCHAR * argv [])
#else
2025-08-11 13:22:39 -04:00
static int cli ( int argc , char ** argv )
2015-04-16 14:13:44 -07:00
#endif
{
2015-04-16 14:55:36 -07:00
unsigned int port = 0 ;
2025-08-11 13:22:39 -04:00
std :: string homeDir , command , arg1 , arg2 , arg3 , arg4 , authToken ;
2015-04-16 14:55:36 -07:00
std :: string ip ( "127.0.0.1" );
bool json = false ;
2025-08-11 13:22:39 -04:00
for ( int i = 1 ; i < argc ; ++ i ) {
2015-04-16 14:55:36 -07:00
if ( argv [ i ][ 0 ] == '-' ) {
2025-08-11 13:22:39 -04:00
switch ( argv [ i ][ 1 ]) {
case 'q' : // ignore -q used to invoke this personality
2015-04-16 14:55:36 -07:00
if ( argv [ i ][ 2 ]) {
2025-08-11 13:22:39 -04:00
cliPrintHelp ( argv [ 0 ], stdout );
2015-04-16 14:55:36 -07:00
return 1 ;
}
break ;
case 'j' :
if ( argv [ i ][ 2 ]) {
2025-08-11 13:22:39 -04:00
cliPrintHelp ( argv [ 0 ], stdout );
2015-04-16 14:55:36 -07:00
return 1 ;
}
json = true ;
break ;
2015-05-17 19:13:22 -07:00
case 'p' :
2015-04-16 14:55:36 -07:00
port = Utils :: strToUInt ( argv [ i ] + 2 );
2025-08-11 13:22:39 -04:00
if (( port > 0xffff ) || ( port == 0 )) {
cliPrintHelp ( argv [ 0 ], stdout );
2015-04-16 14:55:36 -07:00
return 1 ;
}
break ;
2015-05-17 19:13:22 -07:00
case 'D' :
2015-04-16 14:55:36 -07:00
if ( argv [ i ][ 2 ]) {
homeDir = argv [ i ] + 2 ;
2025-08-11 13:22:39 -04:00
}
else {
cliPrintHelp ( argv [ 0 ], stdout );
2015-04-16 14:55:36 -07:00
return 1 ;
}
break ;
2015-05-17 19:13:22 -07:00
case 'H' :
2015-04-16 14:55:36 -07:00
if ( argv [ i ][ 2 ]) {
ip = argv [ i ] + 2 ;
2025-08-11 13:22:39 -04:00
}
else {
cliPrintHelp ( argv [ 0 ], stdout );
2015-04-16 14:55:36 -07:00
return 1 ;
}
break ;
2015-05-17 19:13:22 -07:00
case 'T' :
2015-04-16 14:55:36 -07:00
if ( argv [ i ][ 2 ]) {
authToken = argv [ i ] + 2 ;
2025-08-11 13:22:39 -04:00
}
else {
cliPrintHelp ( argv [ 0 ], stdout );
2015-04-16 14:55:36 -07:00
return 1 ;
}
break ;
2015-05-17 19:13:22 -07:00
case 'v' :
2015-04-16 14:55:36 -07:00
if ( argv [ i ][ 2 ]) {
2025-08-11 13:22:39 -04:00
cliPrintHelp ( argv [ 0 ], stdout );
2015-04-16 14:55:36 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
printf ( "%d.%d.%d" ZT_EOL_S , ZEROTIER_ONE_VERSION_MAJOR , ZEROTIER_ONE_VERSION_MINOR , ZEROTIER_ONE_VERSION_REVISION );
2015-04-16 14:55:36 -07:00
return 0 ;
case 'h' :
case '?' :
default :
2025-08-11 13:22:39 -04:00
cliPrintHelp ( argv [ 0 ], stdout );
2015-04-16 14:55:36 -07:00
return 0 ;
}
2025-08-11 13:22:39 -04:00
}
else {
2016-06-29 12:22:37 -07:00
if ( arg1 . length ())
arg2 = argv [ i ];
else if ( command . length ())
2015-04-16 14:55:36 -07:00
arg1 = argv [ i ];
2025-08-11 13:22:39 -04:00
else
command = argv [ i ];
2015-04-16 14:55:36 -07:00
}
}
2025-08-11 13:22:39 -04:00
if ( ! homeDir . length ())
2015-04-16 14:55:36 -07:00
homeDir = OneService :: platformDefaultHomePath ();
2020-09-10 12:42:11 -07:00
// TODO: cleanup this logic
2025-08-11 13:22:39 -04:00
if (( ! port ) || ( ! authToken . length ())) {
if ( ! homeDir . length ()) {
fprintf ( stderr , "%s: missing port or authentication token and no home directory specified to auto-detect" ZT_EOL_S , argv [ 0 ]);
2015-04-16 14:55:36 -07:00
return 2 ;
}
2025-08-11 13:22:39 -04:00
if ( ! port ) {
2015-04-16 14:55:36 -07:00
std :: string portStr ;
2025-08-11 13:22:39 -04:00
OSUtils :: readFile (( homeDir + ZT_PATH_SEPARATOR_S + "zerotier-one.port" ). c_str (), portStr );
2015-04-16 14:55:36 -07:00
port = Utils :: strToUInt ( portStr . c_str ());
2025-08-11 13:22:39 -04:00
if (( port == 0 ) || ( port > 0xffff )) {
fprintf ( stderr , "%s: missing port and zerotier-one.port not found in %s" ZT_EOL_S , argv [ 0 ], homeDir . c_str ());
2015-04-16 14:55:36 -07:00
return 2 ;
}
}
2025-08-11 13:22:39 -04:00
if ( ! authToken . length ()) {
OSUtils :: readFile (( homeDir + ZT_PATH_SEPARATOR_S + "authtoken.secret" ). c_str (), authToken );
2015-05-17 19:13:22 -07:00
#ifdef __UNIX_LIKE__
2025-08-11 13:22:39 -04:00
if ( ! authToken . length ()) {
const char * hd = getenv ( "HOME" );
2015-05-17 19:13:22 -07:00
if ( hd ) {
char p [ 4096 ];
#ifdef __APPLE__
2025-08-11 13:22:39 -04:00
OSUtils :: ztsnprintf ( p , sizeof ( p ), "%s/Library/Application Support/ZeroTier/One/authtoken.secret" , hd );
2015-05-17 19:13:22 -07:00
#else
2025-08-11 13:22:39 -04:00
OSUtils :: ztsnprintf ( p , sizeof ( p ), "%s/.zeroTierOneAuthToken" , hd );
2015-05-17 19:13:22 -07:00
#endif
2025-08-11 13:22:39 -04:00
OSUtils :: readFile ( p , authToken );
2015-05-17 19:13:22 -07:00
}
}
#endif
2025-08-11 13:22:39 -04:00
if ( ! authToken . length ()) {
fprintf ( stderr , "%s: authtoken.secret not found or readable in %s (try again as root)" ZT_EOL_S , argv [ 0 ], homeDir . c_str ());
2015-04-16 14:55:36 -07:00
return 2 ;
}
}
}
InetAddress addr ;
{
char addrtmp [ 256 ];
2025-08-11 13:22:39 -04:00
OSUtils :: ztsnprintf ( addrtmp , sizeof ( addrtmp ), "%s/%u" , ip . c_str (), port );
2015-04-16 14:55:36 -07:00
addr = InetAddress ( addrtmp );
}
2025-08-11 13:22:39 -04:00
std :: map < std :: string , std :: string > requestHeaders ;
std :: map < std :: string , std :: string > responseHeaders ;
2015-04-16 14:55:36 -07:00
std :: string responseBody ;
requestHeaders [ "X-ZT1-Auth" ] = authToken ;
2025-08-11 13:22:39 -04:00
if (( command . length () > 0 ) && ( command [ 0 ] == '/' )) {
unsigned int scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , command . c_str (), requestHeaders , responseHeaders , responseBody );
2015-07-28 17:10:56 -07:00
if ( scode == 200 ) {
2018-02-13 16:41:21 -08:00
printf ( "%s" , cliFixJsonCRs ( responseBody ). c_str ());
2015-07-28 17:10:56 -07:00
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2015-07-28 17:10:56 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else if (( command == "info" ) || ( command == "status" )) {
const unsigned int scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , "/status" , requestHeaders , responseHeaders , responseBody );
2016-08-24 13:37:57 -07:00
2018-02-13 16:41:21 -08:00
if ( scode == 0 ) {
printf ( "Error connecting to the ZeroTier service: %s \n\n Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S , responseBody . c_str ());
return 1 ;
}
2016-08-24 13:37:57 -07:00
nlohmann :: json j ;
try {
2017-01-19 15:05:26 -08:00
j = OSUtils :: jsonParse ( responseBody );
2025-08-11 13:22:39 -04:00
}
catch ( std :: exception & exc ) {
printf ( "%u %s invalid JSON response (%s)" ZT_EOL_S , scode , command . c_str (), exc . what ());
2016-08-24 13:37:57 -07:00
return 1 ;
2025-08-11 13:22:39 -04:00
}
catch (...) {
printf ( "%u %s invalid JSON response (unknown exception)" ZT_EOL_S , scode , command . c_str ());
2016-08-24 13:37:57 -07:00
return 1 ;
}
2015-04-16 14:55:36 -07:00
if ( scode == 200 ) {
if ( json ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" ZT_EOL_S , OSUtils :: jsonDump ( j ). c_str ());
}
else {
2017-01-19 15:05:26 -08:00
if ( j . is_object ()) {
2025-08-11 13:22:39 -04:00
printf ( "200 info %s %s %s" ZT_EOL_S , OSUtils :: jsonString ( j [ "address" ], "-" ). c_str (), OSUtils :: jsonString ( j [ "version" ], "-" ). c_str (), (( j [ "tcpFallbackActive" ]) ? "TUNNELED" : (( j [ "online" ]) ? "ONLINE" : "OFFLINE" )));
2017-01-19 15:05:26 -08:00
}
2015-04-16 14:55:36 -07:00
}
2016-08-24 13:37:57 -07:00
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2015-04-16 14:55:36 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else if ( command == "listpeers" ) {
const unsigned int scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , "/peer" , requestHeaders , responseHeaders , responseBody );
2016-08-24 13:37:57 -07:00
2018-02-13 16:41:21 -08:00
if ( scode == 0 ) {
printf ( "Error connecting to the ZeroTier service: %s \n\n Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S , responseBody . c_str ());
return 1 ;
}
2016-08-24 13:37:57 -07:00
nlohmann :: json j ;
try {
2017-01-19 15:05:26 -08:00
j = OSUtils :: jsonParse ( responseBody );
2025-08-11 13:22:39 -04:00
}
catch ( std :: exception & exc ) {
printf ( "%u %s invalid JSON response (%s)" ZT_EOL_S , scode , command . c_str (), exc . what ());
2016-08-24 13:37:57 -07:00
return 1 ;
2025-08-11 13:22:39 -04:00
}
catch (...) {
printf ( "%u %s invalid JSON response (unknown exception)" ZT_EOL_S , scode , command . c_str ());
2016-08-24 13:37:57 -07:00
return 1 ;
}
2015-04-16 14:55:36 -07:00
if ( scode == 200 ) {
if ( json ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" ZT_EOL_S , OSUtils :: jsonDump ( j ). c_str ());
}
else {
2017-01-19 15:05:26 -08:00
printf ( "200 listpeers <ztaddr> <path> <latency> <version> <role>" ZT_EOL_S );
2016-08-24 13:37:57 -07:00
if ( j . is_array ()) {
2025-08-11 13:22:39 -04:00
for ( unsigned long k = 0 ; k < j . size (); ++ k ) {
nlohmann :: json & p = j [ k ];
2016-08-24 13:37:57 -07:00
std :: string bestPath ;
2025-08-11 13:22:39 -04:00
nlohmann :: json & paths = p [ "paths" ];
2016-08-24 13:37:57 -07:00
if ( paths . is_array ()) {
2025-08-11 13:22:39 -04:00
for ( unsigned long i = 0 ; i < paths . size (); ++ i ) {
nlohmann :: json & path = paths [ i ];
2016-08-24 13:37:57 -07:00
if ( path [ "preferred" ]) {
char tmp [ 256 ];
std :: string addr = path [ "address" ];
2017-10-02 15:52:57 -07:00
const int64_t now = OSUtils :: now ();
2021-08-04 12:16:26 -07:00
int64_t lastSendDiff = ( uint64_t ) path [ "lastSend" ] ? now - ( uint64_t ) path [ "lastSend" ] : - 1 ;
int64_t lastReceiveDiff = ( uint64_t ) path [ "lastReceive" ] ? now - ( uint64_t ) path [ "lastReceive" ] : - 1 ;
2026-05-22 11:39:10 -04:00
OSUtils :: ztsnprintf ( tmp , sizeof ( tmp ), "%s;%lld;%lld" , addr . c_str (), static_cast < long long > ( lastSendDiff ), static_cast < long long > ( lastReceiveDiff ));
2016-08-24 13:37:57 -07:00
bestPath = tmp ;
break ;
2015-04-16 15:51:56 -07:00
}
}
}
2025-08-11 13:22:39 -04:00
if ( bestPath . length () == 0 )
bestPath = "-" ;
2016-08-24 13:37:57 -07:00
char ver [ 128 ];
int64_t vmaj = p [ "versionMajor" ];
int64_t vmin = p [ "versionMinor" ];
int64_t vrev = p [ "versionRev" ];
if ( vmaj >= 0 ) {
2026-05-22 11:39:10 -04:00
OSUtils :: ztsnprintf ( ver , sizeof ( ver ), "%lld.%lld.%lld" , static_cast < long long > ( vmaj ), static_cast < long long > ( vmin ), static_cast < long long > ( vrev ));
2025-08-11 13:22:39 -04:00
}
else {
2016-08-24 13:37:57 -07:00
ver [ 0 ] = '-' ;
ver [ 1 ] = ( char ) 0 ;
}
2025-08-11 13:22:39 -04:00
printf ( "200 listpeers %s %s %d %s %s" ZT_EOL_S , OSUtils :: jsonString ( p [ "address" ], "-" ). c_str (), bestPath . c_str (), ( int ) OSUtils :: jsonInt ( p [ "latency" ], 0 ), ver , OSUtils :: jsonString ( p [ "role" ], "-" ). c_str ());
2015-04-16 15:51:56 -07:00
}
}
2015-04-16 14:55:36 -07:00
}
2016-08-24 13:37:57 -07:00
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2015-04-16 14:55:36 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else if ( command == "peers" ) {
const unsigned int scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , "/peer" , requestHeaders , responseHeaders , responseBody );
2019-05-31 11:41:24 -07:00
if ( scode == 0 ) {
printf ( "Error connecting to the ZeroTier service: %s \n\n Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S , responseBody . c_str ());
return 1 ;
}
nlohmann :: json j ;
try {
j = OSUtils :: jsonParse ( responseBody );
2025-08-11 13:22:39 -04:00
}
catch ( std :: exception & exc ) {
printf ( "%u %s invalid JSON response (%s)" ZT_EOL_S , scode , command . c_str (), exc . what ());
2019-05-31 11:41:24 -07:00
return 1 ;
2025-08-11 13:22:39 -04:00
}
catch (...) {
printf ( "%u %s invalid JSON response (unknown exception)" ZT_EOL_S , scode , command . c_str ());
2019-05-31 11:41:24 -07:00
return 1 ;
}
if ( scode == 200 ) {
if ( json ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" ZT_EOL_S , OSUtils :: jsonDump ( j ). c_str ());
}
else {
2023-01-10 16:01:07 -08:00
bool anyTunneled = false ;
printf ( "200 peers \n <ztaddr> <ver> <role> <lat> <link> <lastTX> <lastRX> <path>" ZT_EOL_S );
2019-05-31 11:41:24 -07:00
if ( j . is_array ()) {
2025-08-11 13:22:39 -04:00
for ( unsigned long k = 0 ; k < j . size (); ++ k ) {
nlohmann :: json & p = j [ k ];
2019-05-31 11:41:24 -07:00
std :: string bestPath ;
2025-08-11 13:22:39 -04:00
nlohmann :: json & paths = p [ "paths" ];
2023-01-10 16:01:07 -08:00
if ( p [ "tunneled" ]) {
anyTunneled = true ;
}
2019-05-31 11:41:24 -07:00
if ( paths . is_array ()) {
2025-08-11 13:22:39 -04:00
for ( unsigned long i = 0 ; i < paths . size (); ++ i ) {
nlohmann :: json & path = paths [ i ];
2019-05-31 11:41:24 -07:00
if ( path [ "preferred" ]) {
char tmp [ 256 ];
std :: string addr = path [ "address" ];
const int64_t now = OSUtils :: now ();
2021-08-04 12:16:26 -07:00
int64_t lastSendDiff = ( uint64_t ) path [ "lastSend" ] ? now - ( uint64_t ) path [ "lastSend" ] : - 1 ;
int64_t lastReceiveDiff = ( uint64_t ) path [ "lastReceive" ] ? now - ( uint64_t ) path [ "lastReceive" ] : - 1 ;
2026-05-22 11:39:10 -04:00
OSUtils :: ztsnprintf ( tmp , sizeof ( tmp ), "%-8lld %-8lld %s" , static_cast < long long > ( lastSendDiff ), static_cast < long long > ( lastReceiveDiff ), addr . c_str ());
2023-01-10 16:01:07 -08:00
if ( p [ "tunneled" ]) {
bestPath = std :: string ( "RELAY " ) + tmp ;
}
else {
bestPath = std :: string ( "DIRECT " ) + tmp ;
}
2019-05-31 11:41:24 -07:00
break ;
}
}
}
2023-01-10 16:01:07 -08:00
if ( bestPath . length () == 0 ) {
bestPath = "RELAY" ;
}
2019-05-31 11:41:24 -07:00
char ver [ 128 ];
int64_t vmaj = p [ "versionMajor" ];
int64_t vmin = p [ "versionMinor" ];
int64_t vrev = p [ "versionRev" ];
if ( vmaj >= 0 ) {
2026-05-22 11:39:10 -04:00
OSUtils :: ztsnprintf ( ver , sizeof ( ver ), "%lld.%lld.%lld" , static_cast < long long > ( vmaj ), static_cast < long long > ( vmin ), static_cast < long long > ( vrev ));
2025-08-11 13:22:39 -04:00
}
else {
2019-05-31 11:41:24 -07:00
ver [ 0 ] = '-' ;
ver [ 1 ] = ( char ) 0 ;
}
2025-08-11 13:22:39 -04:00
printf ( "%s %-6s %-6s %5d %s" ZT_EOL_S , OSUtils :: jsonString ( p [ "address" ], "-" ). c_str (), ver , OSUtils :: jsonString ( p [ "role" ], "-" ). c_str (), ( int ) OSUtils :: jsonInt ( p [ "latency" ], 0 ), bestPath . c_str ());
2019-05-31 11:41:24 -07:00
}
}
2023-01-10 16:01:07 -08:00
if ( anyTunneled ) {
printf ( "NOTE: Currently tunneling through a TCP relay. Ensure that UDP is not blocked. \n " );
}
2019-05-31 11:41:24 -07:00
}
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2019-05-31 11:41:24 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else if ( command == "bond" ) {
2021-09-01 21:37:49 -07:00
/* zerotier-cli bond <cmd> */
2020-11-23 09:59:28 -08:00
if ( arg1 . empty ()) {
2021-04-07 15:15:35 -07:00
printf ( "(bond) command is missing required arguments" ZT_EOL_S );
2020-11-23 09:59:28 -08:00
return 2 ;
}
/* zerotier-cli bond list */
if ( arg1 == "list" ) {
2025-08-11 13:22:39 -04:00
const unsigned int scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , "/peer" , requestHeaders , responseHeaders , responseBody );
2020-11-23 09:59:28 -08:00
if ( scode == 0 ) {
printf ( "Error connecting to the ZeroTier service: %s \n\n Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S , responseBody . c_str ());
return 1 ;
}
nlohmann :: json j ;
try {
j = OSUtils :: jsonParse ( responseBody );
2025-08-11 13:22:39 -04:00
}
catch ( std :: exception & exc ) {
printf ( "%u %s invalid JSON response (%s)" ZT_EOL_S , scode , command . c_str (), exc . what ());
2020-11-23 09:59:28 -08:00
return 1 ;
2025-08-11 13:22:39 -04:00
}
catch (...) {
printf ( "%u %s invalid JSON response (unknown exception)" ZT_EOL_S , scode , command . c_str ());
2020-11-23 09:59:28 -08:00
return 1 ;
}
if ( scode == 200 ) {
if ( json ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" ZT_EOL_S , OSUtils :: jsonDump ( j ). c_str ());
}
else {
2020-11-23 09:59:28 -08:00
bool bFoundBond = false ;
2022-03-02 09:55:23 -08:00
printf ( " <peer> <bondtype> <links>" ZT_EOL_S );
2020-11-23 09:59:28 -08:00
if ( j . is_array ()) {
2025-08-11 13:22:39 -04:00
for ( unsigned long k = 0 ; k < j . size (); ++ k ) {
nlohmann :: json & p = j [ k ];
2020-11-23 09:59:28 -08:00
bool isBonded = p [ "isBonded" ];
if ( isBonded ) {
2023-01-20 13:54:04 -08:00
int8_t bondingPolicyCode = p [ "bondingPolicyCode" ];
2021-05-18 00:05:46 -07:00
int8_t numAliveLinks = p [ "numAliveLinks" ];
int8_t numTotalLinks = p [ "numTotalLinks" ];
2020-11-23 09:59:28 -08:00
bFoundBond = true ;
std :: string policyStr = "none" ;
2023-01-20 13:54:04 -08:00
if ( bondingPolicyCode >= ZT_BOND_POLICY_NONE && bondingPolicyCode <= ZT_BOND_POLICY_BALANCE_AWARE ) {
policyStr = Bond :: getPolicyStrByCode ( bondingPolicyCode );
2020-11-23 09:59:28 -08:00
}
2025-08-11 13:22:39 -04:00
printf ( "%10s %32s %d/%d" ZT_EOL_S , OSUtils :: jsonString ( p [ "address" ], "-" ). c_str (), policyStr . c_str (), numAliveLinks , numTotalLinks );
2020-11-23 09:59:28 -08:00
}
}
}
2025-08-11 13:22:39 -04:00
if ( ! bFoundBond ) {
2020-11-23 09:59:28 -08:00
printf ( " NONE \t\t\t\t NONE \t NONE NONE" ZT_EOL_S );
}
}
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2020-11-23 09:59:28 -08:00
return 1 ;
}
}
2023-08-15 12:59:58 -07:00
else if ( arg1 == "setmtu" ) { /* zerotier-cli bond setmtu <mtu> <iface> <ip> */
requestHeaders [ "Content-Type" ] = "application/json" ;
requestHeaders [ "Content-Length" ] = "2" ;
if ( argc == 8 ) {
arg2 = argv [ 5 ];
arg3 = argv [ 6 ];
arg4 = argv [ 7 ];
}
2025-08-11 13:22:39 -04:00
unsigned int scode = Http :: POST ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , ( std :: string ( "/bond/" ) + arg1 + "/" + arg2 + "/" + arg3 + "/" + arg4 ). c_str (), requestHeaders , "{}" , 2 , responseHeaders , responseBody );
2023-08-15 12:59:58 -07:00
if ( scode == 200 ) {
printf ( "200 setmtu OK" ZT_EOL_S );
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
2024-03-05 00:13:07 -08:00
printf ( "%d Failed to set MTU: %s" ZT_EOL_S , scode , responseBody . c_str ());
2023-08-15 12:59:58 -07:00
return 1 ;
}
return 0 ;
}
2021-09-01 21:37:49 -07:00
else if ( arg1 . length () == 10 ) {
2020-11-23 09:59:28 -08:00
if ( arg2 == "rotate" ) { /* zerotier-cli bond <peerId> rotate */
requestHeaders [ "Content-Type" ] = "application/json" ;
requestHeaders [ "Content-Length" ] = "2" ;
2025-08-11 13:22:39 -04:00
unsigned int scode = Http :: POST ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , ( std :: string ( "/bond/" ) + arg2 + "/" + arg1 ). c_str (), requestHeaders , "{}" , 2 , responseHeaders , responseBody );
2020-11-23 09:59:28 -08:00
if ( scode == 200 ) {
if ( json ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" , cliFixJsonCRs ( responseBody ). c_str ());
}
else {
2023-08-15 12:59:58 -07:00
printf ( "200 rotate OK" ZT_EOL_S );
2020-11-23 09:59:28 -08:00
}
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2020-11-23 09:59:28 -08:00
return 1 ;
}
return 0 ;
}
if ( arg2 == "show" ) {
2025-08-11 13:22:39 -04:00
// fprintf(stderr, "zerotier-cli bond <peerId> show\n");
const unsigned int scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , ( std :: string ( "/bond/" ) + arg2 + "/" + arg1 ). c_str (), requestHeaders , responseHeaders , responseBody );
2020-11-23 09:59:28 -08:00
if ( scode == 0 ) {
printf ( "Error connecting to the ZeroTier service: %s \n\n Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S , responseBody . c_str ());
return 1 ;
}
nlohmann :: json j ;
try {
j = OSUtils :: jsonParse ( responseBody );
2025-08-11 13:22:39 -04:00
}
catch ( std :: exception & exc ) {
printf ( "%u %s invalid JSON response (%s)" ZT_EOL_S , scode , command . c_str (), exc . what ());
2020-11-23 09:59:28 -08:00
return 1 ;
2025-08-11 13:22:39 -04:00
}
catch (...) {
printf ( "%u %s invalid JSON response (unknown exception)" ZT_EOL_S , scode , command . c_str ());
2020-11-23 09:59:28 -08:00
return 1 ;
}
if ( scode == 200 ) {
if ( json ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" ZT_EOL_S , OSUtils :: jsonDump ( j ). c_str ());
}
else {
int numAliveLinks = OSUtils :: jsonInt ( j [ "numAliveLinks" ], 0 );
int numTotalLinks = OSUtils :: jsonInt ( j [ "numTotalLinks" ], 0 );
2022-09-20 14:27:34 -07:00
printf ( "Peer : %s \n " , arg1 . c_str ());
2025-08-11 13:22:39 -04:00
printf ( "Bond : %s \n " , OSUtils :: jsonString ( j [ "bondingPolicyStr" ], "-" ). c_str ());
printf ( "Link Select Method : %d \n " , ( int ) OSUtils :: jsonInt ( j [ "linkSelectMethod" ], 0 ));
2022-09-20 14:27:34 -07:00
printf ( "Links : %d/%d \n " , numAliveLinks , numTotalLinks );
2025-08-11 13:22:39 -04:00
printf ( "Failover Interval (ms) : %d \n " , ( int ) OSUtils :: jsonInt ( j [ "failoverInterval" ], 0 ));
printf ( "Up Delay (ms) : %d \n " , ( int ) OSUtils :: jsonInt ( j [ "upDelay" ], 0 ));
printf ( "Down Delay (ms) : %d \n " , ( int ) OSUtils :: jsonInt ( j [ "downDelay" ], 0 ));
printf ( "Packets Per Link : %d \n " , ( int ) OSUtils :: jsonInt ( j [ "packetsPerLink" ], 0 ));
nlohmann :: json & p = j [ "paths" ];
2020-11-23 09:59:28 -08:00
if ( p . is_array ()) {
2022-09-20 14:27:34 -07:00
printf ( " \n idx"
2025-08-11 13:22:39 -04:00
" interface"
" "
"path socket local port \n " );
for ( int i = 0 ; i < 120 ; i ++ ) {
printf ( "-" );
}
2020-11-23 09:59:28 -08:00
printf ( " \n " );
2026-05-22 11:39:10 -04:00
for ( int i = 0 ; i < ( int ) p . size (); i ++ ) {
2025-08-11 13:22:39 -04:00
printf (
"%2d: %26s %51s %.16llx %12d \n " ,
2020-11-23 09:59:28 -08:00
i ,
2025-08-11 13:22:39 -04:00
OSUtils :: jsonString ( p [ i ][ "ifname" ], "-" ). c_str (),
OSUtils :: jsonString ( p [ i ][ "address" ], "-" ). c_str (),
( unsigned long long ) OSUtils :: jsonInt ( p [ i ][ "localSocket" ], 0 ),
( uint16_t ) OSUtils :: jsonInt ( p [ i ][ "localPort" ], 0 ));
2020-11-23 09:59:28 -08:00
}
2024-03-01 15:43:28 -08:00
printf ( " \n idx lat pdv "
2025-08-11 13:22:39 -04:00
"capacity qual "
"rx_age tx_age eligible bonded flows \n " );
for ( int i = 0 ; i < 120 ; i ++ ) {
printf ( "-" );
}
2020-11-23 09:59:28 -08:00
printf ( " \n " );
2026-05-22 11:39:10 -04:00
for ( int i = 0 ; i < ( int ) p . size (); i ++ ) {
2025-08-11 13:22:39 -04:00
printf (
"%2d: %8.2f %8.2f %10d %7.4f %11d %11d %9d %7d %7d \n " ,
2020-11-23 09:59:28 -08:00
i ,
OSUtils :: jsonDouble ( p [ i ][ "latencyMean" ], 0 ),
OSUtils :: jsonDouble ( p [ i ][ "latencyVariance" ], 0 ),
2021-05-03 19:35:28 -07:00
( int ) OSUtils :: jsonInt ( p [ i ][ "givenLinkSpeed" ], 0 ),
2022-10-09 23:07:16 -07:00
OSUtils :: jsonDouble ( p [ i ][ "relativeQuality" ], 0 ),
2022-09-20 14:27:34 -07:00
( int ) OSUtils :: jsonInt ( p [ i ][ "lastInAge" ], 0 ),
( int ) OSUtils :: jsonInt ( p [ i ][ "lastOutAge" ], 0 ),
2025-08-11 13:22:39 -04:00
( int ) OSUtils :: jsonInt ( p [ i ][ "eligible" ], 0 ),
( int ) OSUtils :: jsonInt ( p [ i ][ "bonded" ], 0 ),
( int ) OSUtils :: jsonInt ( p [ i ][ "assignedFlowCount" ], 0 ));
2020-11-23 09:59:28 -08:00
}
}
}
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2020-11-23 09:59:28 -08:00
return 1 ;
}
return 2 ;
}
}
2024-03-01 15:43:28 -08:00
2020-11-23 09:59:28 -08:00
/* zerotier-cli bond command was malformed in some way */
2021-04-07 15:15:35 -07:00
printf ( "(bond) command is missing required arguments" ZT_EOL_S );
2020-11-23 09:59:28 -08:00
return 2 ;
2025-08-11 13:22:39 -04:00
}
else if ( command == "listbonds" ) {
const unsigned int scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , "/peer" , requestHeaders , responseHeaders , responseBody );
2020-07-27 23:01:12 -07:00
if ( scode == 0 ) {
printf ( "Error connecting to the ZeroTier service: %s \n\n Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S , responseBody . c_str ());
return 1 ;
}
nlohmann :: json j ;
try {
j = OSUtils :: jsonParse ( responseBody );
2025-08-11 13:22:39 -04:00
}
catch ( std :: exception & exc ) {
printf ( "%u %s invalid JSON response (%s)" ZT_EOL_S , scode , command . c_str (), exc . what ());
2020-07-27 23:01:12 -07:00
return 1 ;
2025-08-11 13:22:39 -04:00
}
catch (...) {
printf ( "%u %s invalid JSON response (unknown exception)" ZT_EOL_S , scode , command . c_str ());
2020-07-27 23:01:12 -07:00
return 1 ;
}
if ( scode == 200 ) {
if ( json ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" ZT_EOL_S , OSUtils :: jsonDump ( j ). c_str ());
}
else {
2020-08-06 18:10:40 -07:00
bool bFoundBond = false ;
2022-03-02 09:55:23 -08:00
printf ( " <peer> <bondtype> <links>" ZT_EOL_S );
2020-07-27 23:01:12 -07:00
if ( j . is_array ()) {
2025-08-11 13:22:39 -04:00
for ( unsigned long k = 0 ; k < j . size (); ++ k ) {
nlohmann :: json & p = j [ k ];
2020-07-27 23:01:12 -07:00
bool isBonded = p [ "isBonded" ];
if ( isBonded ) {
2023-01-20 13:54:04 -08:00
int8_t bondingPolicyCode = p [ "bondingPolicyCode" ];
2021-04-07 15:15:35 -07:00
int8_t numAliveLinks = p [ "numAliveLinks" ];
int8_t numTotalLinks = p [ "numTotalLinks" ];
2020-08-06 18:10:40 -07:00
bFoundBond = true ;
2020-07-27 23:01:12 -07:00
std :: string policyStr = "none" ;
2023-01-20 13:54:04 -08:00
if ( bondingPolicyCode >= ZT_BOND_POLICY_NONE && bondingPolicyCode <= ZT_BOND_POLICY_BALANCE_AWARE ) {
policyStr = Bond :: getPolicyStrByCode ( bondingPolicyCode );
2020-07-27 23:01:12 -07:00
}
2025-08-11 13:22:39 -04:00
printf ( "%10s %32s %d/%d" ZT_EOL_S , OSUtils :: jsonString ( p [ "address" ], "-" ). c_str (), policyStr . c_str (), numAliveLinks , numTotalLinks );
2020-07-27 23:01:12 -07:00
}
}
}
2025-08-11 13:22:39 -04:00
if ( ! bFoundBond ) {
2020-08-06 18:10:40 -07:00
printf ( " NONE \t\t\t\t NONE \t NONE NONE" ZT_EOL_S );
}
2020-07-27 23:01:12 -07:00
}
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2020-07-27 23:01:12 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else if ( command == "listnetworks" ) {
const unsigned int scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , "/network" , requestHeaders , responseHeaders , responseBody );
2016-08-24 13:37:57 -07:00
2018-02-13 16:41:21 -08:00
if ( scode == 0 ) {
printf ( "Error connecting to the ZeroTier service: %s \n\n Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S , responseBody . c_str ());
return 1 ;
}
2016-08-24 13:37:57 -07:00
nlohmann :: json j ;
try {
2017-01-19 15:05:26 -08:00
j = OSUtils :: jsonParse ( responseBody );
2025-08-11 13:22:39 -04:00
}
catch ( std :: exception & exc ) {
printf ( "%u %s invalid JSON response (%s)" ZT_EOL_S , scode , command . c_str (), exc . what ());
2016-08-24 13:37:57 -07:00
return 1 ;
2025-08-11 13:22:39 -04:00
}
catch (...) {
printf ( "%u %s invalid JSON response (unknown exception)" ZT_EOL_S , scode , command . c_str ());
2016-08-24 13:37:57 -07:00
return 1 ;
}
2015-04-16 14:55:36 -07:00
if ( scode == 200 ) {
if ( json ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" ZT_EOL_S , OSUtils :: jsonDump ( j ). c_str ());
}
else {
2017-01-19 15:05:26 -08:00
printf ( "200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>" ZT_EOL_S );
2016-08-24 13:37:57 -07:00
if ( j . is_array ()) {
2025-08-11 13:22:39 -04:00
for ( unsigned long i = 0 ; i < j . size (); ++ i ) {
nlohmann :: json & n = j [ i ];
2016-08-24 13:37:57 -07:00
if ( n . is_object ()) {
std :: string aa ;
2025-08-11 13:22:39 -04:00
nlohmann :: json & assignedAddresses = n [ "assignedAddresses" ];
2016-08-24 13:37:57 -07:00
if ( assignedAddresses . is_array ()) {
2025-08-11 13:22:39 -04:00
for ( unsigned long j = 0 ; j < assignedAddresses . size (); ++ j ) {
nlohmann :: json & addr = assignedAddresses [ j ];
2016-08-24 13:37:57 -07:00
if ( addr . is_string ()) {
2025-08-11 13:22:39 -04:00
if ( aa . length () > 0 )
aa . push_back ( ',' );
2016-11-03 12:10:50 -07:00
aa . append ( addr . get < std :: string > ());
2015-04-16 16:12:10 -07:00
}
}
}
2025-08-11 13:22:39 -04:00
if ( aa . length () == 0 )
aa = "-" ;
const std :: string status = OSUtils :: jsonString ( n [ "status" ], "-" );
printf (
"200 listnetworks %s %s %s %s %s %s %s" ZT_EOL_S ,
OSUtils :: jsonString ( n [ "nwid" ], "-" ). c_str (),
OSUtils :: jsonString ( n [ "name" ], "-" ). c_str (),
OSUtils :: jsonString ( n [ "mac" ], "-" ). c_str (),
2021-05-25 14:40:40 -04:00
status . c_str (),
2025-08-11 13:22:39 -04:00
OSUtils :: jsonString ( n [ "type" ], "-" ). c_str (),
OSUtils :: jsonString ( n [ "portDeviceName" ], "-" ). c_str (),
2017-01-19 15:05:26 -08:00
aa . c_str ());
2021-06-05 13:44:45 -07:00
if ( OSUtils :: jsonBool ( n [ "ssoEnabled" ], false )) {
uint64_t authenticationExpiryTime = n [ "authenticationExpiryTime" ];
2021-05-25 14:40:40 -04:00
if ( status == "AUTHENTICATION_REQUIRED" ) {
printf ( " AUTH EXPIRED, URL: %s" ZT_EOL_S , OSUtils :: jsonString ( n [ "authenticationURL" ], "(null)" ). c_str ());
2025-08-11 13:22:39 -04:00
}
else if ( status == "OK" ) {
2022-04-19 21:29:11 -04:00
int64_t expiresIn = (( int64_t ) authenticationExpiryTime - OSUtils :: now ()) / 1000LL ;
if ( expiresIn >= 0 ) {
2026-05-22 11:39:10 -04:00
printf ( " AUTH OK, expires in: %lld seconds" ZT_EOL_S , static_cast < long long > ( expiresIn ));
2022-04-19 21:29:11 -04:00
}
2021-05-25 12:58:33 -04:00
}
}
2015-04-16 16:12:10 -07:00
}
}
}
2015-04-16 14:55:36 -07:00
}
2016-08-24 13:37:57 -07:00
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2015-04-16 14:55:36 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else if ( command == "join" ) {
2015-04-16 15:07:58 -07:00
if ( arg1 . length () != 16 ) {
2019-05-31 11:41:24 -07:00
printf ( "invalid network id" ZT_EOL_S );
2015-04-16 15:07:58 -07:00
return 2 ;
}
2015-04-16 16:12:10 -07:00
requestHeaders [ "Content-Type" ] = "application/json" ;
requestHeaders [ "Content-Length" ] = "2" ;
2025-08-11 13:22:39 -04:00
unsigned int scode = Http :: POST ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , ( std :: string ( "/network/" ) + arg1 ). c_str (), requestHeaders , "{}" , 2 , responseHeaders , responseBody );
2015-04-16 15:07:58 -07:00
if ( scode == 200 ) {
if ( json ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" , cliFixJsonCRs ( responseBody ). c_str ());
}
else {
2016-07-21 22:41:02 +02:00
printf ( "200 join OK" ZT_EOL_S );
2015-04-16 15:07:58 -07:00
}
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2015-04-16 15:07:58 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else if ( command == "leave" ) {
2015-04-16 15:07:58 -07:00
if ( arg1 . length () != 16 ) {
2019-05-31 11:41:24 -07:00
printf ( "invalid network id" ZT_EOL_S );
2015-04-16 15:07:58 -07:00
return 2 ;
}
2025-08-11 13:22:39 -04:00
unsigned int scode = Http :: DEL ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , ( std :: string ( "/network/" ) + arg1 ). c_str (), requestHeaders , responseHeaders , responseBody );
2015-04-16 15:07:58 -07:00
if ( scode == 200 ) {
if ( json ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" , cliFixJsonCRs ( responseBody ). c_str ());
}
else {
2016-07-21 22:41:02 +02:00
printf ( "200 leave OK" ZT_EOL_S );
2015-04-16 15:07:58 -07:00
}
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2015-04-16 15:07:58 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else if ( command == "listmoons" ) {
const unsigned int scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , "/moon" , requestHeaders , responseHeaders , responseBody );
2017-02-13 15:41:53 -08:00
2018-02-13 16:41:21 -08:00
if ( scode == 0 ) {
printf ( "Error connecting to the ZeroTier service: %s \n\n Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S , responseBody . c_str ());
return 1 ;
}
2017-02-13 15:41:53 -08:00
nlohmann :: json j ;
try {
j = OSUtils :: jsonParse ( responseBody );
2025-08-11 13:22:39 -04:00
}
catch ( std :: exception & exc ) {
printf ( "%u %s invalid JSON response (%s)" ZT_EOL_S , scode , command . c_str (), exc . what ());
2017-02-13 15:41:53 -08:00
return 1 ;
2025-08-11 13:22:39 -04:00
}
catch (...) {
printf ( "%u %s invalid JSON response (unknown exception)" ZT_EOL_S , scode , command . c_str ());
2017-02-13 15:41:53 -08:00
return 1 ;
}
if ( scode == 200 ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" ZT_EOL_S , OSUtils :: jsonDump ( j ). c_str ());
2017-02-13 15:41:53 -08:00
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2017-02-13 15:41:53 -08:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else if ( command == "orbit" ) {
2017-02-13 15:41:53 -08:00
const uint64_t worldId = Utils :: hexStrToU64 ( arg1 . c_str ());
const uint64_t seed = Utils :: hexStrToU64 ( arg2 . c_str ());
2025-08-11 13:22:39 -04:00
if (( worldId ) && ( seed )) {
2017-02-13 15:41:53 -08:00
char jsons [ 1024 ];
2025-08-11 13:22:39 -04:00
OSUtils :: ztsnprintf ( jsons , sizeof ( jsons ), "{ \" seed \" : \" %s \" }" , arg2 . c_str ());
2017-02-13 15:41:53 -08:00
char cl [ 128 ];
2025-08-11 13:22:39 -04:00
OSUtils :: ztsnprintf ( cl , sizeof ( cl ), "%u" , ( unsigned int ) strlen ( jsons ));
2017-02-13 15:41:53 -08:00
requestHeaders [ "Content-Type" ] = "application/json" ;
requestHeaders [ "Content-Length" ] = cl ;
2025-08-11 13:22:39 -04:00
unsigned int scode = Http :: POST ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , ( std :: string ( "/moon/" ) + arg1 ). c_str (), requestHeaders , jsons , ( unsigned long ) strlen ( jsons ), responseHeaders , responseBody );
2017-02-13 15:41:53 -08:00
if ( scode == 200 ) {
printf ( "200 orbit OK" ZT_EOL_S );
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2017-02-13 15:41:53 -08:00
return 1 ;
}
}
2025-08-11 13:22:39 -04:00
}
else if ( command == "deorbit" ) {
unsigned int scode = Http :: DEL ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , ( std :: string ( "/moon/" ) + arg1 ). c_str (), requestHeaders , responseHeaders , responseBody );
2017-02-13 15:41:53 -08:00
if ( scode == 200 ) {
if ( json ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" , cliFixJsonCRs ( responseBody ). c_str ());
}
else {
2017-02-13 15:41:53 -08:00
printf ( "200 deorbit OK" ZT_EOL_S );
}
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2017-02-13 15:41:53 -08:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else if ( command == "set" ) {
2016-06-29 12:22:37 -07:00
if ( arg1 . length () != 16 ) {
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "invalid format: must be a 16-digit (network) ID \n " );
2019-05-31 11:41:24 -07:00
return 2 ;
}
2025-08-11 13:22:39 -04:00
if ( ! arg2 . length ()) {
fprintf ( stderr , "invalid format: include a property name to set \n " );
2016-06-29 12:22:37 -07:00
return 2 ;
}
std :: size_t eqidx = arg2 . find ( '=' );
if ( eqidx != std :: string :: npos ) {
2025-08-11 13:22:39 -04:00
if (( arg2 . substr ( 0 , eqidx ) == "allowManaged" ) || ( arg2 . substr ( 0 , eqidx ) == "allowGlobal" ) || ( arg2 . substr ( 0 , eqidx ) == "allowDefault" ) || ( arg2 . substr ( 0 , eqidx ) == "allowDNS" )) {
2016-06-29 12:22:37 -07:00
char jsons [ 1024 ];
2025-08-11 13:22:39 -04:00
OSUtils :: ztsnprintf ( jsons , sizeof ( jsons ), "{ \" %s \" :%s}" , arg2 . substr ( 0 , eqidx ). c_str (), ((( arg2 . substr ( eqidx , 2 ) == "=t" ) || ( arg2 . substr ( eqidx , 2 ) == "=1" )) ? "true" : "false" ));
2016-06-29 12:22:37 -07:00
char cl [ 128 ];
2025-08-11 13:22:39 -04:00
OSUtils :: ztsnprintf ( cl , sizeof ( cl ), "%u" , ( unsigned int ) strlen ( jsons ));
2016-06-29 12:22:37 -07:00
requestHeaders [ "Content-Type" ] = "application/json" ;
requestHeaders [ "Content-Length" ] = cl ;
2025-08-11 13:22:39 -04:00
unsigned int scode = Http :: POST ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , ( std :: string ( "/network/" ) + arg1 ). c_str (), requestHeaders , jsons , ( unsigned long ) strlen ( jsons ), responseHeaders , responseBody );
2016-06-29 12:22:37 -07:00
if ( scode == 200 ) {
2025-08-11 13:22:39 -04:00
printf ( "%s" , cliFixJsonCRs ( responseBody ). c_str ());
2016-06-29 12:22:37 -07:00
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2016-06-29 12:22:37 -07:00
return 1 ;
}
}
2025-08-11 13:22:39 -04:00
}
else {
cliPrintHelp ( argv [ 0 ], stderr );
2016-06-29 12:22:37 -07:00
return 2 ;
}
2025-08-11 13:22:39 -04:00
}
else if ( command == "get" ) {
2018-01-10 14:05:28 -08:00
if ( arg1 . length () != 16 ) {
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "invalid format: must be a 16-digit (network) ID \n " );
2019-05-31 11:41:24 -07:00
return 2 ;
}
2025-08-11 13:22:39 -04:00
if ( ! arg2 . length ()) {
fprintf ( stderr , "invalid format: include a property name to get \n " );
2018-01-10 14:05:28 -08:00
return 2 ;
}
2025-08-11 13:22:39 -04:00
const unsigned int scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , "/network" , requestHeaders , responseHeaders , responseBody );
2018-02-13 16:41:21 -08:00
if ( scode == 0 ) {
printf ( "Error connecting to the ZeroTier service: %s \n\n Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S , responseBody . c_str ());
return 1 ;
}
2018-01-10 14:05:28 -08:00
nlohmann :: json j ;
try {
j = OSUtils :: jsonParse ( responseBody );
2025-08-11 13:22:39 -04:00
}
catch ( std :: exception & exc ) {
printf ( "%u %s invalid JSON response (%s)" ZT_EOL_S , scode , command . c_str (), exc . what ());
2018-01-10 14:05:28 -08:00
return 1 ;
2025-08-11 13:22:39 -04:00
}
catch (...) {
printf ( "%u %s invalid JSON response (unknown exception)" ZT_EOL_S , scode , command . c_str ());
2018-01-10 14:05:28 -08:00
return 1 ;
}
2018-10-03 14:24:21 -07:00
bool bNetworkFound = false ;
2018-01-10 14:05:28 -08:00
if ( j . is_array ()) {
2025-08-11 13:22:39 -04:00
for ( unsigned long i = 0 ; i < j . size (); ++ i ) {
nlohmann :: json & n = j [ i ];
2018-01-10 14:05:28 -08:00
if ( n . is_object ()) {
if ( n [ "id" ] == arg1 ) {
2018-10-03 14:24:21 -07:00
bNetworkFound = true ;
2019-05-31 11:41:24 -07:00
std :: string aa ;
if ( arg2 != "ip" && arg2 != "ip4" && arg2 != "ip6" && arg2 != "ip6plane" && arg2 != "ip6prefix" ) {
2025-08-11 13:22:39 -04:00
aa . append ( OSUtils :: jsonString ( n [ arg2 ], "-" )); // Standard network property field
2019-05-31 11:41:24 -07:00
if ( aa == "-" ) {
printf ( "error, unknown property name \n " );
break ;
}
2025-08-11 13:22:39 -04:00
printf ( "%s \n " , aa . c_str ());
2019-05-31 11:41:24 -07:00
break ;
}
2025-08-11 13:22:39 -04:00
nlohmann :: json & assignedAddresses = n [ "assignedAddresses" ];
2019-05-31 11:41:24 -07:00
if ( assignedAddresses . is_array ()) {
int matchingIdxs [ ZT_MAX_ZT_ASSIGNED_ADDRESSES ];
int addressCountOfType = 0 ;
2025-08-11 13:22:39 -04:00
for ( int k = 0 ; k < std :: min ( ZT_MAX_ZT_ASSIGNED_ADDRESSES , ( int ) assignedAddresses . size ()); ++ k ) {
nlohmann :: json & addr = assignedAddresses [ k ];
if (( arg2 == "ip4" && addr . get < std :: string > (). find ( '.' ) != std :: string :: npos ) || (( arg2 . find ( "ip6" ) == 0 ) && addr . get < std :: string > (). find ( ":" ) != std :: string :: npos ) || ( arg2 == "ip" )) {
2019-05-31 11:41:24 -07:00
matchingIdxs [ addressCountOfType ++ ] = k ;
}
}
2025-08-11 13:22:39 -04:00
for ( int k = 0 ; k < addressCountOfType ; k ++ ) {
nlohmann :: json & addr = assignedAddresses [ matchingIdxs [ k ]];
if ( ! addr . is_string ()) {
2019-05-31 11:41:24 -07:00
continue ;
}
if ( arg2 . find ( "ip6p" ) == 0 ) {
if ( arg2 == "ip6plane" ) {
if ( addr . get < std :: string > (). find ( "fc" ) == 0 ) {
2025-08-11 13:22:39 -04:00
aa . append ( addr . get < std :: string > (). substr ( 0 , addr . get < std :: string > (). find ( '/' )));
if ( k < addressCountOfType - 1 )
aa . append ( " \n " );
2019-05-31 11:41:24 -07:00
}
}
if ( arg2 == "ip6prefix" ) {
if ( addr . get < std :: string > (). find ( "fc" ) == 0 ) {
2025-08-11 13:22:39 -04:00
aa . append ( addr . get < std :: string > (). substr ( 0 , addr . get < std :: string > (). find ( '/' )). substr ( 0 , 24 ));
if ( k < addressCountOfType - 1 )
aa . append ( " \n " );
2019-05-31 11:41:24 -07:00
}
}
}
else {
2025-08-11 13:22:39 -04:00
aa . append ( addr . get < std :: string > (). substr ( 0 , addr . get < std :: string > (). find ( '/' )));
if ( k < addressCountOfType - 1 )
aa . append ( " \n " );
2019-05-31 11:41:24 -07:00
}
}
}
2025-08-11 13:22:39 -04:00
printf ( "%s \n " , aa . c_str ());
2018-01-10 14:05:28 -08:00
}
}
}
}
2025-08-11 13:22:39 -04:00
if ( ! bNetworkFound ) {
fprintf ( stderr , "unknown network ID, check that you are a member of the network \n " );
2018-10-03 14:24:21 -07:00
}
2018-01-10 14:05:28 -08:00
if ( scode == 200 ) {
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
printf ( "%u %s %s" ZT_EOL_S , scode , command . c_str (), responseBody . c_str ());
2018-01-10 14:05:28 -08:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else if ( command == "dump" ) {
2020-09-11 15:31:56 -07:00
std :: stringstream dump ;
2020-09-11 16:43:30 -07:00
dump << "platform: " ;
2021-03-08 21:45:38 -08:00
#ifdef __APPLE__
2020-09-11 16:43:30 -07:00
dump << "macOS" << ZT_EOL_S ;
#elif defined(_WIN32)
dump << "Windows" << ZT_EOL_S ;
2020-09-15 16:49:49 -07:00
#elif defined(__LINUX__)
dump << "Linux" << ZT_EOL_S ;
2020-09-11 16:43:30 -07:00
#else
dump << "other unix based OS" << ZT_EOL_S ;
#endif
2025-08-11 13:22:39 -04:00
dump << "zerotier version: " << ZEROTIER_ONE_VERSION_MAJOR << "." << ZEROTIER_ONE_VERSION_MINOR << "." << ZEROTIER_ONE_VERSION_REVISION << ZT_EOL_S << ZT_EOL_S ;
2020-09-11 16:43:30 -07:00
2020-09-11 15:31:56 -07:00
// grab status
dump << "status" << ZT_EOL_S << "------" << ZT_EOL_S ;
2025-08-11 13:22:39 -04:00
unsigned int scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , "/status" , requestHeaders , responseHeaders , responseBody );
2020-09-11 15:31:56 -07:00
if ( scode != 200 ) {
printf ( "Error connecting to the ZeroTier service: %s \n\n Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S , responseBody . c_str ());
return 1 ;
}
dump << responseBody << ZT_EOL_S ;
responseHeaders . clear ();
responseBody = "" ;
// grab network list
dump << ZT_EOL_S << "networks" << ZT_EOL_S << "--------" << ZT_EOL_S ;
2025-08-11 13:22:39 -04:00
scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , "/network" , requestHeaders , responseHeaders , responseBody );
2020-09-11 15:31:56 -07:00
if ( scode != 200 ) {
printf ( "Error connecting to the ZeroTier service: %s \n\n Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S , responseBody . c_str ());
return 1 ;
}
dump << responseBody << ZT_EOL_S ;
responseHeaders . clear ();
responseBody = "" ;
// list peers
dump << ZT_EOL_S << "peers" << ZT_EOL_S << "-----" << ZT_EOL_S ;
2025-08-11 13:22:39 -04:00
scode = Http :: GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , "/peer" , requestHeaders , responseHeaders , responseBody );
2020-09-11 15:31:56 -07:00
if ( scode != 200 ) {
printf ( "Error connecting to the ZeroTier service: %s \n\n Please check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S , responseBody . c_str ());
return 1 ;
}
dump << responseBody << ZT_EOL_S ;
2023-01-20 13:54:04 -08:00
// Bonds don't need to be queried separately since their data originates from "/peer" responses anyway
2020-09-11 15:31:56 -07:00
responseHeaders . clear ();
responseBody = "" ;
2020-09-11 16:43:30 -07:00
dump << ZT_EOL_S << "local.conf" << ZT_EOL_S << "----------" << ZT_EOL_S ;
2020-09-14 15:17:59 -07:00
std :: string localConf ;
OSUtils :: readFile (( homeDir + ZT_PATH_SEPARATOR_S + "local.conf" ). c_str (), localConf );
if ( localConf . empty ()) {
dump << "None Present" << ZT_EOL_S ;
}
else {
dump << localConf << ZT_EOL_S ;
}
2020-09-11 16:43:30 -07:00
dump << ZT_EOL_S << "Network Interfaces" << ZT_EOL_S << "------------------" << ZT_EOL_S << ZT_EOL_S ;
#ifdef __APPLE__
CFArrayRef interfaces = SCNetworkInterfaceCopyAll ();
CFIndex size = CFArrayGetCount ( interfaces );
2025-08-11 13:22:39 -04:00
for ( CFIndex i = 0 ; i < size ; ++ i ) {
2020-09-11 16:43:30 -07:00
SCNetworkInterfaceRef iface = ( SCNetworkInterfaceRef ) CFArrayGetValueAtIndex ( interfaces , i );
dump << "Interface " << i << ZT_EOL_S << "-----------" << ZT_EOL_S ;
CFStringRef tmp = SCNetworkInterfaceGetBSDName ( iface );
char stringBuffer [ 512 ] = {};
2025-08-11 13:22:39 -04:00
CFStringGetCString ( tmp , stringBuffer , sizeof ( stringBuffer ), kCFStringEncodingUTF8 );
2020-09-11 16:43:30 -07:00
dump << "Name: " << stringBuffer << ZT_EOL_S ;
std :: string ifName ( stringBuffer );
int mtuCur , mtuMin , mtuMax ;
SCNetworkInterfaceCopyMTU ( iface , & mtuCur , & mtuMin , & mtuMax );
dump << "MTU: " << mtuCur << ZT_EOL_S ;
tmp = SCNetworkInterfaceGetHardwareAddressString ( iface );
CFStringGetCString ( tmp , stringBuffer , sizeof ( stringBuffer ), kCFStringEncodingUTF8 );
dump << "MAC: " << stringBuffer << ZT_EOL_S ;
tmp = SCNetworkInterfaceGetInterfaceType ( iface );
CFStringGetCString ( tmp , stringBuffer , sizeof ( stringBuffer ), kCFStringEncodingUTF8 );
dump << "Type: " << stringBuffer << ZT_EOL_S ;
dump << "Addresses:" << ZT_EOL_S ;
struct ifaddrs * ifap , * ifa ;
2025-08-11 13:22:39 -04:00
void * addr ;
2020-09-11 16:43:30 -07:00
getifaddrs ( & ifap );
for ( ifa = ifap ; ifa ; ifa = ifa -> ifa_next ) {
if ( strcmp ( ifName . c_str (), ifa -> ifa_name ) == 0 ) {
if ( ifa -> ifa_addr -> sa_family == AF_INET ) {
2025-08-11 13:22:39 -04:00
struct sockaddr_in * ipv4 = ( struct sockaddr_in * ) ifa -> ifa_addr ;
2020-09-11 16:43:30 -07:00
addr = & ipv4 -> sin_addr ;
2025-08-11 13:22:39 -04:00
}
else if ( ifa -> ifa_addr -> sa_family == AF_INET6 ) {
struct sockaddr_in6 * ipv6 = ( struct sockaddr_in6 * ) ifa -> ifa_addr ;
2020-09-11 16:43:30 -07:00
addr = & ipv6 -> sin6_addr ;
2025-08-11 13:22:39 -04:00
}
else {
2020-09-11 16:43:30 -07:00
continue ;
}
inet_ntop ( ifa -> ifa_addr -> sa_family , addr , stringBuffer , sizeof ( stringBuffer ));
dump << stringBuffer << ZT_EOL_S ;
}
}
dump << ZT_EOL_S ;
}
2020-09-15 16:49:49 -07:00
FSRef fsref ;
UInt8 path [ PATH_MAX ];
2025-08-11 13:22:39 -04:00
if ( FSFindFolder ( kUserDomain , kDesktopFolderType , kDontCreateFolder , & fsref ) == noErr && FSRefMakePath ( & fsref , path , sizeof ( path )) == noErr ) {}
else if ( getenv ( "SUDO_USER" )) {
2025-08-21 10:06:56 -04:00
snprintf (( char * ) path , sizeof ( path ), "/Users/%s/Desktop" , getenv ( "SUDO_USER" ));
2025-08-11 13:22:39 -04:00
}
else {
2020-09-15 16:49:49 -07:00
fprintf ( stdout , "%s" , dump . str (). c_str ());
return 0 ;
}
2025-11-05 06:58:29 -08:00
char dumpfile [ PATH_MAX ];
snprintf ( dumpfile , sizeof ( dumpfile ), "%s%szerotier_dump.txt" , ( char * ) path , ZT_PATH_SEPARATOR_S );
2020-09-15 16:49:49 -07:00
2025-11-05 06:58:29 -08:00
fprintf ( stdout , "Writing dump to: %s \n " , dumpfile );
int fd = open ( dumpfile , O_CREAT | O_WRONLY | O_TRUNC , 0664 );
2020-09-15 16:49:49 -07:00
if ( fd == - 1 ) {
2025-11-05 06:58:29 -08:00
perror ( "Error creating file" );
2020-09-15 16:49:49 -07:00
return 1 ;
}
2021-03-08 21:45:38 -08:00
write ( fd , dump . str (). c_str (), dump . str (). size ());
2020-09-15 16:49:49 -07:00
close ( fd );
2020-09-14 15:17:59 -07:00
#elif defined(_WIN32)
ULONG buffLen = 16384 ;
PIP_ADAPTER_ADDRESSES addresses ;
2021-03-08 21:45:38 -08:00
2020-09-14 15:17:59 -07:00
ULONG ret = 0 ;
do {
addresses = ( PIP_ADAPTER_ADDRESSES ) malloc ( buffLen );
ret = GetAdaptersAddresses ( AF_UNSPEC , 0 , NULL , addresses , & buffLen );
if ( ret == ERROR_BUFFER_OVERFLOW ) {
free ( addresses );
addresses = NULL ;
}
else {
break ;
}
} while ( ret == ERROR_BUFFER_OVERFLOW );
2021-03-08 21:45:38 -08:00
2020-09-14 15:17:59 -07:00
int i = 0 ;
if ( ret == NO_ERROR ) {
PIP_ADAPTER_ADDRESSES curAddr = addresses ;
while ( curAddr ) {
dump << "Interface " << i << ZT_EOL_S << "-----------" << ZT_EOL_S ;
dump << "Name: " << curAddr -> AdapterName << ZT_EOL_S ;
dump << "MTU: " << curAddr -> Mtu << ZT_EOL_S ;
dump << "MAC: " ;
char macBuffer [ 64 ] = {};
2025-08-21 10:06:56 -04:00
snprintf (
2025-08-11 13:22:39 -04:00
macBuffer ,
2025-08-21 10:06:56 -04:00
sizeof ( macBuffer ),
2025-08-11 13:22:39 -04:00
"%02x:%02x:%02x:%02x:%02x:%02x" ,
2020-09-14 15:17:59 -07:00
curAddr -> PhysicalAddress [ 0 ],
curAddr -> PhysicalAddress [ 1 ],
curAddr -> PhysicalAddress [ 2 ],
curAddr -> PhysicalAddress [ 3 ],
curAddr -> PhysicalAddress [ 4 ],
curAddr -> PhysicalAddress [ 5 ]);
dump << macBuffer << ZT_EOL_S ;
dump << "Type: " << curAddr -> IfType << ZT_EOL_S ;
dump << "Addresses:" << ZT_EOL_S ;
PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL ;
pUnicast = curAddr -> FirstUnicastAddress ;
if ( pUnicast ) {
for ( int j = 0 ; pUnicast != NULL ; ++ j ) {
char buf [ 128 ] = {};
DWORD bufLen = 128 ;
LPSOCKADDR a = pUnicast -> Address . lpSockaddr ;
2025-08-11 13:22:39 -04:00
WSAAddressToStringA ( pUnicast -> Address . lpSockaddr , pUnicast -> Address . iSockaddrLength , NULL , buf , & bufLen );
2020-09-14 15:17:59 -07:00
dump << buf << ZT_EOL_S ;
pUnicast = pUnicast -> Next ;
}
}
curAddr = curAddr -> Next ;
++ i ;
}
}
if ( addresses ) {
free ( addresses );
addresses = NULL ;
}
2020-09-16 10:03:03 -07:00
char path [ MAX_PATH + 1 ] = {};
if ( SHGetFolderPathA ( NULL , CSIDL_DESKTOP , NULL , 0 , path ) == S_OK ) {
2025-08-21 10:06:56 -04:00
snprintf ( path , sizeof ( path ), "%s%szerotier_dump.txt" , path , ZT_PATH_SEPARATOR_S );
2020-09-16 10:03:03 -07:00
fprintf ( stdout , "Writing dump to: %s \n " , path );
2025-08-11 13:22:39 -04:00
HANDLE file = CreateFileA ( path , GENERIC_WRITE , 0 , NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL );
2020-09-16 10:03:03 -07:00
if ( file == INVALID_HANDLE_VALUE ) {
fprintf ( stdout , "%s" , dump . str (). c_str ());
return 0 ;
}
2025-11-05 06:12:41 -08:00
BOOL ok = WriteFile ( file , dump . str (). c_str (), dump . str (). size (), NULL , NULL );
if ( ok == FALSE ) {
fprintf ( stderr , "Error writing file \n " );
2020-09-16 10:03:03 -07:00
return 1 ;
}
CloseHandle ( file );
}
else {
fprintf ( stdout , "%s" , dump . str (). c_str ());
}
2020-08-30 14:19:56 -07:00
#elif defined(__LINUX__)
struct ifreq ifr ;
struct ifconf ifc ;
char buf [ 1024 ];
char stringBuffer [ 128 ];
2021-03-08 21:45:38 -08:00
2020-08-30 14:19:56 -07:00
int sock = socket ( AF_INET , SOCK_DGRAM , IPPROTO_IP );
2021-03-08 21:45:38 -08:00
2020-08-30 14:19:56 -07:00
ifc . ifc_len = sizeof ( buf );
ifc . ifc_buf = buf ;
ioctl ( sock , SIOCGIFCONF , & ifc );
2025-08-11 13:22:39 -04:00
struct ifreq * it = ifc . ifc_req ;
const struct ifreq * const end = it + ( ifc . ifc_len / sizeof ( struct ifreq ));
2020-08-30 14:19:56 -07:00
int count = 0 ;
2025-08-11 13:22:39 -04:00
for (; it != end ; ++ it ) {
2020-08-30 14:19:56 -07:00
strcpy ( ifr . ifr_name , it -> ifr_name );
2025-08-11 13:22:39 -04:00
if ( ioctl ( sock , SIOCGIFFLAGS , & ifr ) == 0 ) {
if ( ! ( ifr . ifr_flags & IFF_LOOPBACK )) { // skip loopback
2020-08-30 14:19:56 -07:00
dump << "Interface " << count ++ << ZT_EOL_S << "-----------" << ZT_EOL_S ;
dump << "Name: " << ifr . ifr_name << ZT_EOL_S ;
if ( ioctl ( sock , SIOCGIFMTU , & ifr ) == 0 ) {
dump << "MTU: " << ifr . ifr_mtu << ZT_EOL_S ;
}
if ( ioctl ( sock , SIOCGIFHWADDR , & ifr ) == 0 ) {
unsigned char mac_addr [ 6 ];
memcpy ( mac_addr , ifr . ifr_hwaddr . sa_data , 6 );
2021-02-19 23:42:38 +08:00
char macStr [ 18 ];
2025-08-21 10:06:56 -04:00
snprintf ( macStr , sizeof ( macStr ), "%02x:%02x:%02x:%02x:%02x:%02x" , mac_addr [ 0 ], mac_addr [ 1 ], mac_addr [ 2 ], mac_addr [ 3 ], mac_addr [ 4 ], mac_addr [ 5 ]);
2020-08-30 14:19:56 -07:00
dump << "MAC: " << macStr << ZT_EOL_S ;
}
dump << "Addresses: " << ZT_EOL_S ;
struct ifaddrs * ifap , * ifa ;
2025-08-11 13:22:39 -04:00
void * addr ;
2020-08-30 14:19:56 -07:00
getifaddrs ( & ifap );
2025-08-11 13:22:39 -04:00
for ( ifa = ifap ; ifa ; ifa = ifa -> ifa_next ) {
if ( strcmp ( ifr . ifr_name , ifa -> ifa_name ) == 0 && ifa -> ifa_addr != NULL ) {
if ( ifa -> ifa_addr -> sa_family == AF_INET ) {
struct sockaddr_in * ipv4 = ( struct sockaddr_in * ) ifa -> ifa_addr ;
2020-08-30 14:19:56 -07:00
addr = & ipv4 -> sin_addr ;
2025-08-11 13:22:39 -04:00
}
else if ( ifa -> ifa_addr -> sa_family == AF_INET6 ) {
struct sockaddr_in6 * ipv6 = ( struct sockaddr_in6 * ) ifa -> ifa_addr ;
2020-08-30 14:19:56 -07:00
addr = & ipv6 -> sin6_addr ;
2025-08-11 13:22:39 -04:00
}
else {
2020-08-30 14:19:56 -07:00
continue ;
}
inet_ntop ( ifa -> ifa_addr -> sa_family , addr , stringBuffer , sizeof ( stringBuffer ));
dump << stringBuffer << ZT_EOL_S ;
}
}
}
}
}
close ( sock );
2020-09-23 22:06:22 -04:00
char cwd [ 16384 ];
2025-11-05 06:58:29 -08:00
if ( getcwd ( cwd , sizeof ( cwd )) == nullptr ) {
strcpy ( cwd , "." );
}
char dumpfile [ sizeof ( cwd ) + 32 ];
snprintf ( dumpfile , sizeof ( dumpfile ), "%s%szerotier_dump.txt" , cwd , ZT_PATH_SEPARATOR_S );
fprintf ( stdout , "Writing dump to: %s \n " , dumpfile );
int fd = open ( dumpfile , O_CREAT | O_WRONLY | O_TRUNC , 0664 );
2020-08-30 14:19:56 -07:00
if ( fd == - 1 ) {
2025-11-05 06:58:29 -08:00
perror ( "Error creating file" );
2020-09-15 16:49:49 -07:00
return 1 ;
2020-08-30 14:19:56 -07:00
}
2021-03-08 21:45:38 -08:00
write ( fd , dump . str (). c_str (), dump . str (). size ());
2020-08-30 14:19:56 -07:00
close ( fd );
2020-09-15 16:50:55 -07:00
#else
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "%s" , dump . str (). c_str ());
2020-09-11 16:43:30 -07:00
#endif
2020-08-30 14:19:56 -07:00
// fprintf(stderr, "%s\n", dump.str().c_str());
2025-08-11 13:22:39 -04:00
}
else {
cliPrintHelp ( argv [ 0 ], stderr );
2015-04-16 14:55:36 -07:00
return 0 ;
}
2015-04-16 16:43:35 -07:00
return 0 ;
2015-04-16 14:13:44 -07:00
}
2015-04-15 17:00:26 -07:00
/****************************************************************************/
/* zerotier-idtool personality */
/****************************************************************************/
2025-08-11 13:22:39 -04:00
static void idtoolPrintHelp ( FILE * out , const char * pn )
2015-04-15 17:00:26 -07:00
{
2025-08-11 13:22:39 -04:00
fprintf ( out , "%s version %d.%d.%d" ZT_EOL_S , PROGRAM_NAME , ZEROTIER_ONE_VERSION_MAJOR , ZEROTIER_ONE_VERSION_MINOR , ZEROTIER_ONE_VERSION_REVISION );
fprintf ( out , COPYRIGHT_NOTICE ZT_EOL_S LICENSE_GRANT ZT_EOL_S );
fprintf ( out , "Usage: %s <command> [<args>]" ZT_EOL_S "" ZT_EOL_S "Commands:" ZT_EOL_S , pn );
fprintf ( out , " generate [<identity.secret>] [<identity.public>] [<vanity>]" ZT_EOL_S );
fprintf ( out , " validate <identity.secret/public>" ZT_EOL_S );
fprintf ( out , " getpublic <identity.secret>" ZT_EOL_S );
fprintf ( out , " sign <identity.secret> <file>" ZT_EOL_S );
fprintf ( out , " verify <identity.secret/public> <file> <signature>" ZT_EOL_S );
fprintf ( out , " initmoon <identity.public of first seed>" ZT_EOL_S );
fprintf ( out , " genmoon <moon json>" ZT_EOL_S );
2015-04-15 17:00:26 -07:00
}
2025-08-11 13:22:39 -04:00
static Identity getIdFromArg ( char * arg )
2015-04-15 17:00:26 -07:00
{
Identity id ;
2025-08-11 13:22:39 -04:00
if (( strlen ( arg ) > 32 ) && ( arg [ 10 ] == ':' )) { // identity is a literal on the command line
2015-04-15 17:00:26 -07:00
if ( id . fromString ( arg ))
return id ;
2025-08-11 13:22:39 -04:00
}
else { // identity is to be read from a file
2015-04-15 17:00:26 -07:00
std :: string idser ;
2025-08-11 13:22:39 -04:00
if ( OSUtils :: readFile ( arg , idser )) {
2017-07-06 16:11:11 -07:00
if ( id . fromString ( idser . c_str ()))
2015-04-15 17:00:26 -07:00
return id ;
}
}
return Identity ();
}
#ifdef __WINDOWS__
2015-04-16 14:13:44 -07:00
static int idtool ( int argc , _TCHAR * argv [])
2015-04-15 17:00:26 -07:00
#else
2025-08-11 13:22:39 -04:00
static int idtool ( int argc , char ** argv )
2015-04-15 17:00:26 -07:00
#endif
{
if ( argc < 2 ) {
2025-08-11 13:22:39 -04:00
idtoolPrintHelp ( stdout , argv [ 0 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
if ( ! strcmp ( argv [ 1 ], "generate" )) {
2016-05-26 09:21:54 -07:00
uint64_t vanity = 0 ;
int vanityBits = 0 ;
if ( argc >= 5 ) {
vanity = Utils :: hexStrToU64 ( argv [ 4 ]) & 0xffffffffffULL ;
2017-01-13 14:22:36 -08:00
vanityBits = 4 * ( int ) strlen ( argv [ 4 ]);
2016-05-26 09:21:54 -07:00
if ( vanityBits > 40 )
vanityBits = 40 ;
}
2015-04-15 17:00:26 -07:00
Identity id ;
2025-08-11 13:22:39 -04:00
for (;;) {
2016-05-26 09:21:54 -07:00
id . generate ();
if (( id . address (). toInt () >> ( 40 - vanityBits )) == vanity ) {
if ( vanityBits > 0 ) {
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "vanity address: found %.10llx ! \n " , ( unsigned long long ) id . address (). toInt ());
2016-05-26 09:21:54 -07:00
}
break ;
2025-08-11 13:22:39 -04:00
}
else {
fprintf ( stderr , "vanity address: tried %.10llx looking for first %d bits of %.10llx \n " , ( unsigned long long ) id . address (). toInt (), vanityBits , ( unsigned long long )( vanity << ( 40 - vanityBits )));
2016-05-26 09:21:54 -07:00
}
}
2017-07-06 16:11:11 -07:00
char idtmp [ 1024 ];
2025-08-11 13:22:39 -04:00
std :: string idser = id . toString ( true , idtmp );
2015-04-15 17:00:26 -07:00
if ( argc >= 3 ) {
2025-08-11 13:22:39 -04:00
if ( ! OSUtils :: writeFile ( argv [ 2 ], idser )) {
fprintf ( stderr , "Error writing to %s" ZT_EOL_S , argv [ 2 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
else
printf ( "%s written" ZT_EOL_S , argv [ 2 ]);
if ( argc >= 4 ) {
idser = id . toString ( false , idtmp );
if ( ! OSUtils :: writeFile ( argv [ 3 ], idser )) {
fprintf ( stderr , "Error writing to %s" ZT_EOL_S , argv [ 3 ]);
return 1 ;
}
else
printf ( "%s written" ZT_EOL_S , argv [ 3 ]);
}
}
else
printf ( "%s" , idser . c_str ());
}
else if ( ! strcmp ( argv [ 1 ], "validate" )) {
2015-04-15 17:00:26 -07:00
if ( argc < 3 ) {
2025-08-11 13:22:39 -04:00
idtoolPrintHelp ( stdout , argv [ 0 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
Identity id = getIdFromArg ( argv [ 2 ]);
2025-08-11 13:22:39 -04:00
if ( ! id ) {
fprintf ( stderr , "Identity argument invalid or file unreadable: %s" ZT_EOL_S , argv [ 2 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
if ( ! id . locallyValidate ()) {
fprintf ( stderr , "%s FAILED validation." ZT_EOL_S , argv [ 2 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
2025-08-11 13:22:39 -04:00
}
else
printf ( "%s is a valid identity" ZT_EOL_S , argv [ 2 ]);
}
else if ( ! strcmp ( argv [ 1 ], "getpublic" )) {
2015-04-15 17:00:26 -07:00
if ( argc < 3 ) {
2025-08-11 13:22:39 -04:00
idtoolPrintHelp ( stdout , argv [ 0 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
Identity id = getIdFromArg ( argv [ 2 ]);
2025-08-11 13:22:39 -04:00
if ( ! id ) {
fprintf ( stderr , "Identity argument invalid or file unreadable: %s" ZT_EOL_S , argv [ 2 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
2017-07-06 16:11:11 -07:00
char idtmp [ 1024 ];
2025-08-11 13:22:39 -04:00
printf ( "%s" , id . toString ( false , idtmp ));
}
else if ( ! strcmp ( argv [ 1 ], "sign" )) {
2015-04-15 17:00:26 -07:00
if ( argc < 4 ) {
2025-08-11 13:22:39 -04:00
idtoolPrintHelp ( stdout , argv [ 0 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
Identity id = getIdFromArg ( argv [ 2 ]);
2025-08-11 13:22:39 -04:00
if ( ! id ) {
fprintf ( stderr , "Identity argument invalid or file unreadable: %s" ZT_EOL_S , argv [ 2 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
if ( ! id . hasPrivate ()) {
fprintf ( stderr , "%s does not contain a private key (must use private to sign)" ZT_EOL_S , argv [ 2 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
std :: string inf ;
2025-08-11 13:22:39 -04:00
if ( ! OSUtils :: readFile ( argv [ 3 ], inf )) {
fprintf ( stderr , "%s is not readable" ZT_EOL_S , argv [ 3 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
ECC :: Signature signature = id . sign ( inf . data (), ( unsigned int ) inf . length ());
2017-07-06 16:11:11 -07:00
char hexbuf [ 1024 ];
2025-08-11 13:22:39 -04:00
printf ( "%s" , Utils :: hex ( signature . data , ZT_ECC_SIGNATURE_LEN , hexbuf ));
}
else if ( ! strcmp ( argv [ 1 ], "verify" )) {
2018-04-23 14:11:26 -07:00
if ( argc < 5 ) {
2025-08-11 13:22:39 -04:00
idtoolPrintHelp ( stdout , argv [ 0 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
Identity id = getIdFromArg ( argv [ 2 ]);
2025-08-11 13:22:39 -04:00
if ( ! id ) {
fprintf ( stderr , "Identity argument invalid or file unreadable: %s" ZT_EOL_S , argv [ 2 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
std :: string inf ;
2025-08-11 13:22:39 -04:00
if ( ! OSUtils :: readFile ( argv [ 3 ], inf )) {
fprintf ( stderr , "%s is not readable" ZT_EOL_S , argv [ 3 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
2017-07-06 16:11:11 -07:00
char buf [ 4096 ];
2025-08-11 13:22:39 -04:00
std :: string signature ( buf , Utils :: unhex ( argv [ 4 ], buf , ( unsigned int ) sizeof ( buf )));
if (( signature . length () > ZT_ADDRESS_LENGTH ) && ( id . verify ( inf . data (), ( unsigned int ) inf . length (), signature . data (), ( unsigned int ) signature . length ()))) {
printf ( "%s signature valid" ZT_EOL_S , argv [ 3 ]);
}
else {
2018-04-23 14:11:26 -07:00
signature . clear ();
2025-08-11 13:22:39 -04:00
if ( OSUtils :: readFile ( argv [ 4 ], signature )) {
signature . assign ( buf , Utils :: unhex ( signature . c_str (), buf , ( unsigned int ) sizeof ( buf )));
if (( signature . length () > ZT_ADDRESS_LENGTH ) && ( id . verify ( inf . data (), ( unsigned int ) inf . length (), signature . data (), ( unsigned int ) signature . length ()))) {
printf ( "%s signature valid" ZT_EOL_S , argv [ 3 ]);
}
else {
fprintf ( stderr , "%s signature check FAILED" ZT_EOL_S , argv [ 3 ]);
2018-04-23 14:11:26 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else {
fprintf ( stderr , "%s signature check FAILED" ZT_EOL_S , argv [ 3 ]);
2018-04-23 14:11:26 -07:00
return 1 ;
}
2015-04-15 17:00:26 -07:00
}
2025-08-11 13:22:39 -04:00
}
else if ( ! strcmp ( argv [ 1 ], "initmoon" )) {
2017-01-27 17:34:39 -08:00
if ( argc < 3 ) {
2025-08-11 13:22:39 -04:00
idtoolPrintHelp ( stdout , argv [ 0 ]);
}
else {
2017-01-27 17:34:39 -08:00
const Identity id = getIdFromArg ( argv [ 2 ]);
2025-08-11 13:22:39 -04:00
if ( ! id ) {
fprintf ( stderr , "%s is not a valid identity" ZT_EOL_S , argv [ 2 ]);
2017-01-27 17:34:39 -08:00
return 1 ;
}
2024-09-15 17:34:01 -04:00
ECC :: Pair kp ( ECC :: generate ());
2017-01-27 17:34:39 -08:00
2017-07-06 16:11:11 -07:00
char idtmp [ 4096 ];
2017-01-27 17:34:39 -08:00
nlohmann :: json mj ;
mj [ "objtype" ] = "world" ;
mj [ "worldType" ] = "moon" ;
2025-08-11 13:22:39 -04:00
mj [ "updatesMustBeSignedBy" ] = mj [ "signingKey" ] = Utils :: hex ( kp . pub . data , ZT_ECC_PUBLIC_KEY_SET_LEN , idtmp );
mj [ "signingKey_SECRET" ] = Utils :: hex ( kp . priv . data , ZT_ECC_PRIVATE_KEY_SET_LEN , idtmp );
2017-07-06 16:11:11 -07:00
mj [ "id" ] = id . address (). toString ( idtmp );
2017-01-27 17:34:39 -08:00
nlohmann :: json seedj ;
2025-08-11 13:22:39 -04:00
seedj [ "identity" ] = id . toString ( false , idtmp );
2017-01-27 17:34:39 -08:00
seedj [ "stableEndpoints" ] = nlohmann :: json :: array ();
( mj [ "roots" ] = nlohmann :: json :: array ()). push_back ( seedj );
std :: string mjd ( OSUtils :: jsonDump ( mj ));
2025-08-11 13:22:39 -04:00
printf ( "%s" ZT_EOL_S , mjd . c_str ());
2017-01-27 17:34:39 -08:00
}
2025-08-11 13:22:39 -04:00
}
else if ( ! strcmp ( argv [ 1 ], "genmoon" )) {
2017-01-27 17:34:39 -08:00
if ( argc < 3 ) {
2025-08-11 13:22:39 -04:00
idtoolPrintHelp ( stdout , argv [ 0 ]);
}
else {
2017-01-27 17:34:39 -08:00
std :: string buf ;
2025-08-11 13:22:39 -04:00
if ( ! OSUtils :: readFile ( argv [ 2 ], buf )) {
fprintf ( stderr , "cannot read %s" ZT_EOL_S , argv [ 2 ]);
2017-01-27 17:34:39 -08:00
return 1 ;
}
nlohmann :: json mj ( OSUtils :: jsonParse ( buf ));
2025-08-11 13:22:39 -04:00
const uint64_t id = Utils :: hexStrToU64 ( OSUtils :: jsonString ( mj [ "id" ], "0" ). c_str ());
if ( ! id ) {
fprintf ( stderr , "ID in %s is invalid" ZT_EOL_S , argv [ 2 ]);
2017-02-13 10:05:00 -08:00
return 1 ;
}
2017-01-27 17:34:39 -08:00
World :: Type t ;
if ( mj [ "worldType" ] == "moon" ) {
t = World :: TYPE_MOON ;
2025-08-11 13:22:39 -04:00
}
else if ( mj [ "worldType" ] == "planet" ) {
2017-01-27 17:34:39 -08:00
t = World :: TYPE_PLANET ;
2025-08-11 13:22:39 -04:00
}
else {
fprintf ( stderr , "invalid worldType" ZT_EOL_S );
2017-01-27 17:34:39 -08:00
return 1 ;
}
2024-09-15 17:34:01 -04:00
ECC :: Pair signingKey ;
ECC :: Public updatesMustBeSignedBy ;
2025-08-11 13:22:39 -04:00
Utils :: unhex ( OSUtils :: jsonString ( mj [ "signingKey" ], "" ). c_str (), signingKey . pub . data , ZT_ECC_PUBLIC_KEY_SET_LEN );
Utils :: unhex ( OSUtils :: jsonString ( mj [ "signingKey_SECRET" ], "" ). c_str (), signingKey . priv . data , ZT_ECC_PRIVATE_KEY_SET_LEN );
Utils :: unhex ( OSUtils :: jsonString ( mj [ "updatesMustBeSignedBy" ], "" ). c_str (), updatesMustBeSignedBy . data , ZT_ECC_PUBLIC_KEY_SET_LEN );
2017-01-27 17:34:39 -08:00
std :: vector < World :: Root > roots ;
2025-08-11 13:22:39 -04:00
nlohmann :: json & rootsj = mj [ "roots" ];
2017-01-27 17:34:39 -08:00
if ( rootsj . is_array ()) {
2025-08-11 13:22:39 -04:00
for ( unsigned long i = 0 ; i < ( unsigned long ) rootsj . size (); ++ i ) {
nlohmann :: json & r = rootsj [ i ];
2017-01-27 17:34:39 -08:00
if ( r . is_object ()) {
roots . push_back ( World :: Root ());
2025-08-11 13:22:39 -04:00
roots . back (). identity = Identity ( OSUtils :: jsonString ( r [ "identity" ], "" ). c_str ());
nlohmann :: json & stableEndpointsj = r [ "stableEndpoints" ];
2017-01-27 17:34:39 -08:00
if ( stableEndpointsj . is_array ()) {
2025-08-11 13:22:39 -04:00
for ( unsigned long k = 0 ; k < ( unsigned long ) stableEndpointsj . size (); ++ k )
roots . back (). stableEndpoints . push_back ( InetAddress ( OSUtils :: jsonString ( stableEndpointsj [ k ], "" ). c_str ()));
std :: sort ( roots . back (). stableEndpoints . begin (), roots . back (). stableEndpoints . end ());
2017-01-27 17:34:39 -08:00
}
}
}
}
2025-08-11 13:22:39 -04:00
std :: sort ( roots . begin (), roots . end ());
2017-01-27 17:34:39 -08:00
2017-10-02 15:52:57 -07:00
const int64_t now = OSUtils :: now ();
2025-08-11 13:22:39 -04:00
World w ( World :: make ( t , id , now , updatesMustBeSignedBy , roots , signingKey ));
2017-01-27 17:34:39 -08:00
Buffer < ZT_WORLD_MAX_SERIALIZED_LENGTH > wbuf ;
w . serialize ( wbuf );
char fn [ 128 ];
2026-05-22 11:39:10 -04:00
OSUtils :: ztsnprintf ( fn , sizeof ( fn ), "%.16llx.moon" , static_cast < unsigned long long > ( w . id ()));
2025-08-11 13:22:39 -04:00
OSUtils :: writeFile ( fn , wbuf . data (), wbuf . size ());
printf ( "wrote %s (signed world with timestamp %llu)" ZT_EOL_S , fn , ( unsigned long long ) now );
2017-01-27 17:34:39 -08:00
}
2025-08-11 13:22:39 -04:00
}
else {
idtoolPrintHelp ( stdout , argv [ 0 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
2015-04-09 20:54:00 -07:00
return 0 ;
}
2015-04-15 17:00:26 -07:00
/****************************************************************************/
/* Unix helper functions and signal handlers */
/****************************************************************************/
2026-06-26 13:33:07 -07:00
#ifdef ZT1_CENTRAL_CONTROLLER
#include <execinfo.h> // backtrace()/backtrace_symbols_fd() for the controller crash handler
#endif
2015-04-15 17:00:26 -07:00
#ifdef __UNIX_LIKE__
static void _sighandlerHup ( int sig )
{
}
2020-11-10 16:30:55 -05:00
static void _sighandlerReallyQuit ( int sig )
{
exit ( 0 );
}
2015-04-15 17:00:26 -07:00
static void _sighandlerQuit ( int sig )
{
2025-08-11 13:22:39 -04:00
alarm ( 5 ); // force exit after 5s
OneService * s = zt1Service ;
2015-04-15 18:36:32 -07:00
if ( s )
s -> terminate ();
2025-08-11 13:22:39 -04:00
else
exit ( 0 );
2015-04-15 17:00:26 -07:00
}
2026-06-26 13:33:07 -07:00
#ifdef ZT1_CENTRAL_CONTROLLER
// Controller-only fatal-signal handler. The controller otherwise dies silently on a
// SIGSEGV/SIGABRT/SIGBUS (exit 139/134/135) with nothing in the logs, which makes an
// intermittent crash nearly impossible to locate. Dump a backtrace to stderr (captured
// by `kubectl logs`), then re-raise the default handler so the process still terminates
// with the original signal (preserving the exit code and any core dump).
//
// backtrace_symbols_fd() is async-signal-safe (it does not call malloc, unlike
// backtrace_symbols()). The handler runs on a dedicated alternate stack (SA_ONSTACK) so
// it can still produce output even on a stack-overflow crash.
static char _fatalSigAltStack [ 65536 ];
static void _sighandlerFatal ( int sig , siginfo_t * info , void * )
{
void * frames [ 64 ];
const int n = backtrace ( frames , 64 );
char hdr [ 160 ];
const int hlen = snprintf (
hdr , sizeof ( hdr ), " \n FATAL: caught signal %d (si_addr=%p), backtrace (%d frames): \n " , sig ,
( info ? info -> si_addr : ( void * ) 0 ), n );
if ( hlen > 0 )
( void ) ! write ( STDERR_FILENO , hdr , ( size_t ) hlen );
backtrace_symbols_fd ( frames , n , STDERR_FILENO );
// Re-raise with the default disposition so we exit with the original signal.
signal ( sig , SIG_DFL );
raise ( sig );
}
#endif // ZT1_CENTRAL_CONTROLLER
2015-04-15 17:00:26 -07:00
#endif
2017-02-15 16:25:49 -08:00
// Drop privileges on Linux, if supported by libc etc. and "zerotier-one" user exists on system
2025-08-11 13:22:39 -04:00
#if defined(__LINUX__) && ! defined(ZT_NO_CAPABILITIES)
2017-02-16 00:57:33 +00:00
#ifndef PR_CAP_AMBIENT
2025-08-11 13:22:39 -04:00
#define PR_CAP_AMBIENT 47
#define PR_CAP_AMBIENT_IS_SET 1
#define PR_CAP_AMBIENT_RAISE 2
#define PR_CAP_AMBIENT_LOWER 3
2017-02-16 00:57:33 +00:00
#define PR_CAP_AMBIENT_CLEAR_ALL 4
#endif
2025-08-11 13:22:39 -04:00
#define ZT_LINUX_USER "zerotier-one"
2017-02-15 16:25:49 -08:00
#define ZT_HAVE_DROP_PRIVILEGES 1
namespace {
// libc doesn't export capset, it is instead located in libcap
// We ignore libcap and call it manually.
struct cap_header_struct {
__u32 version ;
int pid ;
};
struct cap_data_struct {
__u32 effective ;
__u32 permitted ;
__u32 inheritable ;
};
2025-08-11 13:22:39 -04:00
static inline int _zt_capset ( cap_header_struct * hdrp , cap_data_struct * datap )
{
return syscall ( SYS_capset , hdrp , datap );
}
2017-02-15 16:25:49 -08:00
2025-08-11 13:22:39 -04:00
static void _notDropping ( const char * procName , const std :: string & homeDir )
2017-02-15 16:25:49 -08:00
{
struct stat buf ;
2025-08-11 13:22:39 -04:00
if ( lstat ( homeDir . c_str (), & buf ) < 0 ) {
2017-02-15 16:25:49 -08:00
if ( buf . st_uid != 0 || buf . st_gid != 0 ) {
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "%s: FATAL: failed to drop privileges and can't run as root since privileges were previously dropped (home directory not owned by root)" ZT_EOL_S , procName );
2017-02-15 16:25:49 -08:00
exit ( 1 );
}
}
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "%s: WARNING: failed to drop privileges (kernel may not support required prctl features), running as root" ZT_EOL_S , procName );
2017-02-15 16:25:49 -08:00
}
static int _setCapabilities ( int flags )
{
2025-08-11 13:22:39 -04:00
cap_header_struct capheader = { _LINUX_CAPABILITY_VERSION_1 , 0 };
2017-02-15 16:25:49 -08:00
cap_data_struct capdata ;
capdata . inheritable = capdata . permitted = capdata . effective = flags ;
return _zt_capset ( & capheader , & capdata );
}
2025-08-11 13:22:39 -04:00
static void _recursiveChown ( const char * path , uid_t uid , gid_t gid )
2017-02-15 16:25:49 -08:00
{
2025-08-11 13:22:39 -04:00
struct dirent * dptr ;
lchown ( path , uid , gid );
DIR * d = opendir ( path );
if ( ! d )
2017-02-15 16:25:49 -08:00
return ;
2026-05-22 11:39:10 -04:00
while (( dptr = readdir ( d )) != nullptr ) {
2025-08-11 13:22:39 -04:00
if (( strcmp ( dptr -> d_name , "." ) != 0 ) && ( strcmp ( dptr -> d_name , ".." ) != 0 ) && ( strlen ( dptr -> d_name ) > 0 )) {
2017-02-15 16:25:49 -08:00
std :: string p ( path );
p . push_back ( ZT_PATH_SEPARATOR );
p . append ( dptr -> d_name );
2025-08-11 13:22:39 -04:00
_recursiveChown ( p . c_str (), uid , gid ); // will just fail and return on regular files
2017-02-15 16:25:49 -08:00
}
}
closedir ( d );
}
2025-08-11 13:22:39 -04:00
static void dropPrivileges ( const char * procName , const std :: string & homeDir )
2017-02-15 16:25:49 -08:00
{
if ( getuid () != 0 )
return ;
// dropPrivileges switches to zerotier-one user while retaining CAP_NET_ADMIN
// and CAP_NET_RAW capabilities.
2025-08-11 13:22:39 -04:00
struct passwd * targetUser = getpwnam ( ZT_LINUX_USER );
if ( ! targetUser )
2017-02-15 16:25:49 -08:00
return ;
if ( prctl ( PR_CAP_AMBIENT , PR_CAP_AMBIENT_IS_SET , CAP_NET_RAW , 0 , 0 ) < 0 ) {
// Kernel has no support for ambient capabilities.
2025-08-11 13:22:39 -04:00
_notDropping ( procName , homeDir );
2017-02-15 16:25:49 -08:00
return ;
}
if ( prctl ( PR_SET_SECUREBITS , SECBIT_KEEP_CAPS | SECBIT_NOROOT ) < 0 ) {
2025-08-11 13:22:39 -04:00
_notDropping ( procName , homeDir );
2017-02-15 16:25:49 -08:00
return ;
}
// Change ownership of our home directory if everything looks good (does nothing if already chown'd)
2025-08-11 13:22:39 -04:00
_recursiveChown ( homeDir . c_str (), targetUser -> pw_uid , targetUser -> pw_gid );
2017-02-15 16:25:49 -08:00
2018-01-30 10:47:31 -08:00
if ( _setCapabilities (( 1 << CAP_NET_ADMIN ) | ( 1 << CAP_NET_RAW ) | ( 1 << CAP_SETUID ) | ( 1 << CAP_SETGID ) | ( 1 << CAP_NET_BIND_SERVICE )) < 0 ) {
2025-08-11 13:22:39 -04:00
_notDropping ( procName , homeDir );
2017-02-15 16:25:49 -08:00
return ;
}
int oldDumpable = prctl ( PR_GET_DUMPABLE );
if ( prctl ( PR_SET_DUMPABLE , 0 ) < 0 ) {
// Disable ptracing. Otherwise there is a small window when previous
// compromised ZeroTier process could ptrace us, when we still have CAP_SETUID.
// (this is mitigated anyway on most distros by ptrace_scope=1)
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "%s: FATAL: prctl(PR_SET_DUMPABLE) failed while attempting to relinquish root permissions" ZT_EOL_S , procName );
2017-02-15 16:25:49 -08:00
exit ( 1 );
}
// Relinquish root
if ( setgid ( targetUser -> pw_gid ) < 0 ) {
perror ( "setgid" );
exit ( 1 );
}
if ( setuid ( targetUser -> pw_uid ) < 0 ) {
perror ( "setuid" );
exit ( 1 );
}
2018-01-30 10:47:31 -08:00
if ( _setCapabilities (( 1 << CAP_NET_ADMIN ) | ( 1 << CAP_NET_RAW ) | ( 1 << CAP_NET_BIND_SERVICE )) < 0 ) {
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "%s: FATAL: unable to drop capabilities after relinquishing root" ZT_EOL_S , procName );
2017-02-15 16:25:49 -08:00
exit ( 1 );
}
if ( prctl ( PR_SET_DUMPABLE , oldDumpable ) < 0 ) {
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "%s: FATAL: prctl(PR_SET_DUMPABLE) failed while attempting to relinquish root permissions" ZT_EOL_S , procName );
2017-02-15 16:25:49 -08:00
exit ( 1 );
}
if ( prctl ( PR_CAP_AMBIENT , PR_CAP_AMBIENT_RAISE , CAP_NET_ADMIN , 0 , 0 ) < 0 ) {
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "%s: FATAL: prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_RAISE,CAP_NET_ADMIN) failed while attempting to relinquish root permissions" ZT_EOL_S , procName );
2017-02-15 16:25:49 -08:00
exit ( 1 );
}
if ( prctl ( PR_CAP_AMBIENT , PR_CAP_AMBIENT_RAISE , CAP_NET_RAW , 0 , 0 ) < 0 ) {
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "%s: FATAL: prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_RAISE,CAP_NET_RAW) failed while attempting to relinquish root permissions" ZT_EOL_S , procName );
2017-02-15 16:25:49 -08:00
exit ( 1 );
}
}
2025-08-11 13:22:39 -04:00
} // anonymous namespace
#endif // __LINUX__
2017-02-15 16:25:49 -08:00
2015-04-15 17:00:26 -07:00
/****************************************************************************/
/* Windows helper functions and signal handlers */
/****************************************************************************/
#ifdef __WINDOWS__
// Console signal handler routine to allow CTRL+C to work, mostly for testing
static BOOL WINAPI _winConsoleCtrlHandler ( DWORD dwCtrlType )
{
2025-08-11 13:22:39 -04:00
switch ( dwCtrlType ) {
2015-04-15 17:00:26 -07:00
case CTRL_C_EVENT :
case CTRL_BREAK_EVENT :
case CTRL_CLOSE_EVENT :
case CTRL_SHUTDOWN_EVENT :
2025-08-11 13:22:39 -04:00
OneService * s = zt1Service ;
2015-04-24 15:44:39 -07:00
if ( s )
s -> terminate ();
2015-04-15 17:00:26 -07:00
return TRUE ;
}
return FALSE ;
}
2020-09-10 12:42:11 -07:00
// TODO: revisit this with https://support.microsoft.com/en-us/help/947709/how-to-use-the-netsh-advfirewall-firewall-context-instead-of-the-netsh
2015-04-15 17:00:26 -07:00
static void _winPokeAHole ()
{
char myPath [ MAX_PATH ];
2025-08-11 13:22:39 -04:00
DWORD ps = GetModuleFileNameA ( NULL , myPath , sizeof ( myPath ));
if (( ps > 0 ) && ( ps < ( DWORD ) sizeof ( myPath ))) {
2015-04-15 17:00:26 -07:00
STARTUPINFOA startupInfo ;
PROCESS_INFORMATION processInfo ;
startupInfo . cb = sizeof ( startupInfo );
2025-08-11 13:22:39 -04:00
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ));
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ));
if ( CreateProcessA (
NULL ,
( LPSTR )( std :: string ( "C: \\ Windows \\ System32 \\ netsh.exe advfirewall firewall delete rule name= \" ZeroTier One \" program= \" " ) + myPath + " \" " ). c_str (),
NULL ,
NULL ,
FALSE ,
CREATE_NO_WINDOW ,
NULL ,
NULL ,
& startupInfo ,
& processInfo )) {
WaitForSingleObject ( processInfo . hProcess , INFINITE );
2015-04-15 17:00:26 -07:00
CloseHandle ( processInfo . hProcess );
CloseHandle ( processInfo . hThread );
}
startupInfo . cb = sizeof ( startupInfo );
2025-08-11 13:22:39 -04:00
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ));
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ));
if ( CreateProcessA (
NULL ,
( LPSTR )( std :: string ( "C: \\ Windows \\ System32 \\ netsh.exe advfirewall firewall add rule name= \" ZeroTier One \" dir=in action=allow program= \" " ) + myPath + " \" enable=yes" ). c_str (),
NULL ,
NULL ,
FALSE ,
CREATE_NO_WINDOW ,
NULL ,
NULL ,
& startupInfo ,
& processInfo )) {
WaitForSingleObject ( processInfo . hProcess , INFINITE );
2015-04-15 17:00:26 -07:00
CloseHandle ( processInfo . hProcess );
CloseHandle ( processInfo . hThread );
}
startupInfo . cb = sizeof ( startupInfo );
2025-08-11 13:22:39 -04:00
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ));
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ));
if ( CreateProcessA (
NULL ,
( LPSTR )( std :: string ( "C: \\ Windows \\ System32 \\ netsh.exe advfirewall firewall add rule name= \" ZeroTier One \" dir=out action=allow program= \" " ) + myPath + " \" enable=yes" ). c_str (),
NULL ,
NULL ,
FALSE ,
CREATE_NO_WINDOW ,
NULL ,
NULL ,
& startupInfo ,
& processInfo )) {
WaitForSingleObject ( processInfo . hProcess , INFINITE );
2015-04-15 17:00:26 -07:00
CloseHandle ( processInfo . hProcess );
CloseHandle ( processInfo . hThread );
}
}
}
// Returns true if this is running as the local administrator
static BOOL IsCurrentUserLocalAdministrator ( void )
{
2025-08-11 13:22:39 -04:00
BOOL fReturn = FALSE ;
DWORD dwStatus ;
DWORD dwAccessMask ;
DWORD dwAccessDesired ;
DWORD dwACLSize ;
DWORD dwStructureSize = sizeof ( PRIVILEGE_SET );
PACL pACL = NULL ;
PSID psidAdmin = NULL ;
2015-04-15 17:00:26 -07:00
2025-08-11 13:22:39 -04:00
HANDLE hToken = NULL ;
2015-04-15 17:00:26 -07:00
HANDLE hImpersonationToken = NULL ;
2025-08-11 13:22:39 -04:00
PRIVILEGE_SET ps ;
2015-04-15 17:00:26 -07:00
GENERIC_MAPPING GenericMapping ;
2025-08-11 13:22:39 -04:00
PSECURITY_DESCRIPTOR psdAdmin = NULL ;
2015-04-15 17:00:26 -07:00
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY ;
2025-08-11 13:22:39 -04:00
const DWORD ACCESS_READ = 1 ;
2015-04-15 17:00:26 -07:00
const DWORD ACCESS_WRITE = 2 ;
2025-08-11 13:22:39 -04:00
__try {
if ( ! OpenThreadToken ( GetCurrentThread (), TOKEN_DUPLICATE | TOKEN_QUERY , TRUE , & hToken )) {
2015-04-15 17:00:26 -07:00
if ( GetLastError () != ERROR_NO_TOKEN )
__leave ;
2025-08-11 13:22:39 -04:00
if ( ! OpenProcessToken ( GetCurrentProcess (), TOKEN_DUPLICATE | TOKEN_QUERY , & hToken ))
2015-04-15 17:00:26 -07:00
__leave ;
}
2025-08-11 13:22:39 -04:00
if ( ! DuplicateToken ( hToken , SecurityImpersonation , & hImpersonationToken ))
2015-04-15 17:00:26 -07:00
__leave ;
2025-08-11 13:22:39 -04:00
if ( ! AllocateAndInitializeSid ( & SystemSidAuthority , 2 , SECURITY_BUILTIN_DOMAIN_RID , DOMAIN_ALIAS_RID_ADMINS , 0 , 0 , 0 , 0 , 0 , 0 , & psidAdmin ))
2015-04-15 17:00:26 -07:00
__leave ;
psdAdmin = LocalAlloc ( LPTR , SECURITY_DESCRIPTOR_MIN_LENGTH );
if ( psdAdmin == NULL )
__leave ;
2025-08-11 13:22:39 -04:00
if ( ! InitializeSecurityDescriptor ( psdAdmin , SECURITY_DESCRIPTOR_REVISION ))
2015-04-15 17:00:26 -07:00
__leave ;
dwACLSize = sizeof ( ACL ) + sizeof ( ACCESS_ALLOWED_ACE ) + GetLengthSid ( psidAdmin ) - sizeof ( DWORD );
pACL = ( PACL ) LocalAlloc ( LPTR , dwACLSize );
if ( pACL == NULL )
__leave ;
2025-08-11 13:22:39 -04:00
if ( ! InitializeAcl ( pACL , dwACLSize , ACL_REVISION2 ))
2015-04-15 17:00:26 -07:00
__leave ;
2025-08-11 13:22:39 -04:00
dwAccessMask = ACCESS_READ | ACCESS_WRITE ;
if ( ! AddAccessAllowedAce ( pACL , ACL_REVISION2 , dwAccessMask , psidAdmin ))
2015-04-15 17:00:26 -07:00
__leave ;
2025-08-11 13:22:39 -04:00
if ( ! SetSecurityDescriptorDacl ( psdAdmin , TRUE , pACL , FALSE ))
2015-04-15 17:00:26 -07:00
__leave ;
SetSecurityDescriptorGroup ( psdAdmin , psidAdmin , FALSE );
SetSecurityDescriptorOwner ( psdAdmin , psidAdmin , FALSE );
2025-08-11 13:22:39 -04:00
if ( ! IsValidSecurityDescriptor ( psdAdmin ))
2015-04-15 17:00:26 -07:00
__leave ;
dwAccessDesired = ACCESS_READ ;
2025-08-11 13:22:39 -04:00
GenericMapping . GenericRead = ACCESS_READ ;
GenericMapping . GenericWrite = ACCESS_WRITE ;
2015-04-15 17:00:26 -07:00
GenericMapping . GenericExecute = 0 ;
2025-08-11 13:22:39 -04:00
GenericMapping . GenericAll = ACCESS_READ | ACCESS_WRITE ;
2015-04-15 17:00:26 -07:00
2025-08-11 13:22:39 -04:00
if ( ! AccessCheck ( psdAdmin , hImpersonationToken , dwAccessDesired , & GenericMapping , & ps , & dwStructureSize , & dwStatus , & fReturn )) {
2015-04-15 17:00:26 -07:00
fReturn = FALSE ;
__leave ;
}
}
2025-08-11 13:22:39 -04:00
__finally {
2015-04-15 17:00:26 -07:00
// Clean up.
2025-08-11 13:22:39 -04:00
if ( pACL )
LocalFree ( pACL );
if ( psdAdmin )
LocalFree ( psdAdmin );
if ( psidAdmin )
FreeSid ( psidAdmin );
if ( hImpersonationToken )
CloseHandle ( hImpersonationToken );
if ( hToken )
CloseHandle ( hToken );
2015-04-15 17:00:26 -07:00
}
return fReturn ;
}
2025-08-11 13:22:39 -04:00
#endif // __WINDOWS__
2015-04-15 17:00:26 -07:00
/****************************************************************************/
/* main() and friends */
/****************************************************************************/
2025-08-11 13:22:39 -04:00
static void printHelp ( const char * cn , FILE * out )
2015-04-15 17:00:26 -07:00
{
2025-08-11 13:22:39 -04:00
fprintf ( out , "%s version %d.%d.%d" ZT_EOL_S , PROGRAM_NAME , ZEROTIER_ONE_VERSION_MAJOR , ZEROTIER_ONE_VERSION_MINOR , ZEROTIER_ONE_VERSION_REVISION );
fprintf ( out , COPYRIGHT_NOTICE ZT_EOL_S LICENSE_GRANT ZT_EOL_S );
2025-08-21 11:01:52 -04:00
fprintf ( out , ZT_EOL_S "Usage: %s [-switches] [home directory]" ZT_EOL_S "" ZT_EOL_S , cn );
2025-08-11 13:22:39 -04:00
fprintf ( out , "Available switches:" ZT_EOL_S );
fprintf ( out , " -h - Display this help" ZT_EOL_S );
fprintf ( out , " -v - Show version" ZT_EOL_S );
fprintf ( out , " -U - Skip privilege check and do not attempt to drop privileges" ZT_EOL_S );
fprintf ( out , " -p<port> - Port for UDP and TCP/HTTP (default: 9993, 0 for random)" ZT_EOL_S );
2015-07-30 11:31:38 -07:00
2015-04-15 17:00:26 -07:00
#ifdef __UNIX_LIKE__
2025-08-11 13:22:39 -04:00
fprintf ( out , " -d - Fork and run as daemon (Unix-ish OSes)" ZT_EOL_S );
#endif // __UNIX_LIKE__
2015-07-30 11:31:38 -07:00
2015-04-15 17:00:26 -07:00
#ifdef __WINDOWS__
2025-08-11 13:22:39 -04:00
fprintf ( out , " -C - Run from command line instead of as service (Windows)" ZT_EOL_S );
fprintf ( out , " -I - Install Windows service (Windows)" ZT_EOL_S );
fprintf ( out , " -R - Uninstall Windows service (Windows)" ZT_EOL_S );
fprintf ( out , " -D - Remove all instances of Windows tap device (Windows)" ZT_EOL_S );
#endif // __WINDOWS__
2015-07-30 11:31:38 -07:00
2025-08-11 13:22:39 -04:00
fprintf ( out , " -i - Generate and manage identities (zerotier-idtool)" ZT_EOL_S );
fprintf ( out , " -q - Query API (zerotier-cli)" ZT_EOL_S );
2015-04-15 17:00:26 -07:00
}
2025-08-11 13:22:39 -04:00
class _OneServiceRunner {
public :
_OneServiceRunner ( const char * pn , const std :: string & hd , unsigned int p ) : progname ( pn ), returnValue ( 0 ), port ( p ), homeDir ( hd )
{
}
void threadMain () throw ()
2017-03-17 13:55:26 -07:00
{
try {
2025-08-11 13:22:39 -04:00
for (;;) {
zt1Service = OneService :: newInstance ( homeDir . c_str (), port );
switch ( zt1Service -> run ()) {
case OneService :: ONE_STILL_RUNNING : // shouldn't happen, run() won't return until done
2017-03-17 13:55:26 -07:00
case OneService :: ONE_NORMAL_TERMINATION :
break ;
case OneService :: ONE_UNRECOVERABLE_ERROR :
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "%s: fatal error: %s" ZT_EOL_S , progname , zt1Service -> fatalErrorMessage (). c_str ());
2017-03-17 13:55:26 -07:00
returnValue = 1 ;
break ;
case OneService :: ONE_IDENTITY_COLLISION : {
delete zt1Service ;
2025-08-11 13:22:39 -04:00
zt1Service = ( OneService * ) 0 ;
2017-03-17 13:55:26 -07:00
std :: string oldid ;
2025-08-11 13:22:39 -04:00
OSUtils :: readFile (( homeDir + ZT_PATH_SEPARATOR_S + "identity.secret" ). c_str (), oldid );
2017-03-17 13:55:26 -07:00
if ( oldid . length ()) {
2025-08-11 13:22:39 -04:00
OSUtils :: writeFile (( homeDir + ZT_PATH_SEPARATOR_S + "identity.secret.saved_after_collision" ). c_str (), oldid );
2017-03-17 13:55:26 -07:00
OSUtils :: rm (( homeDir + ZT_PATH_SEPARATOR_S + "identity.secret" ). c_str ());
OSUtils :: rm (( homeDir + ZT_PATH_SEPARATOR_S + "identity.public" ). c_str ());
}
2025-08-11 13:22:39 -04:00
}
continue ; // restart!
2017-03-17 13:55:26 -07:00
}
2025-08-11 13:22:39 -04:00
break ; // terminate loop -- normally we don't keep restarting
2017-03-17 13:55:26 -07:00
}
delete zt1Service ;
2025-08-11 13:22:39 -04:00
zt1Service = ( OneService * ) 0 ;
}
catch (...) {
fprintf ( stderr , "%s: unexpected exception starting main OneService instance" ZT_EOL_S , progname );
2017-03-17 13:55:26 -07:00
returnValue = 1 ;
}
}
2025-08-11 13:22:39 -04:00
const char * progname ;
2017-03-17 13:55:26 -07:00
unsigned int returnValue ;
unsigned int port ;
2025-08-11 13:22:39 -04:00
const std :: string & homeDir ;
2017-03-17 13:55:26 -07:00
};
2015-04-15 17:00:26 -07:00
#ifdef __WINDOWS__
2017-04-18 12:22:44 -07:00
int __cdecl _tmain ( int argc , _TCHAR * argv [])
2015-04-15 17:00:26 -07:00
#else
2025-08-11 13:22:39 -04:00
int main ( int argc , char ** argv )
2015-04-15 17:00:26 -07:00
#endif
{
2025-08-11 13:22:39 -04:00
#if defined(__LINUX__) && ((! defined(__GLIBC__)) || ((__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 18)))
2020-11-20 21:10:08 -05:00
// This corrects for systems with abnormally small defaults (musl) and also
// shrinks the stack on systems with large defaults to save a bit of memory.
pthread_attr_t tattr ;
pthread_attr_init ( & tattr );
2025-08-11 13:22:39 -04:00
pthread_attr_setstacksize ( & tattr , 1048576 );
2020-11-20 21:10:08 -05:00
pthread_setattr_default_np ( & tattr );
pthread_attr_destroy ( & tattr );
#endif
2015-04-15 17:00:26 -07:00
#ifdef __UNIX_LIKE__
2025-08-11 13:22:39 -04:00
signal ( SIGHUP , & _sighandlerHup );
signal ( SIGPIPE , SIG_IGN );
signal ( SIGIO , SIG_IGN );
signal ( SIGUSR1 , SIG_IGN );
signal ( SIGUSR2 , SIG_IGN );
signal ( SIGALRM , & _sighandlerReallyQuit );
signal ( SIGINT , & _sighandlerQuit );
signal ( SIGTERM , & _sighandlerQuit );
signal ( SIGQUIT , & _sighandlerQuit );
signal ( SIGINT , & _sighandlerQuit );
2015-04-15 17:00:26 -07:00
2026-06-26 13:33:07 -07:00
#ifdef ZT1_CENTRAL_CONTROLLER
// Controller-only: install a backtrace-dumping handler for fatal signals so an
// otherwise-silent crash leaves a stack trace in the logs. See _sighandlerFatal.
{
stack_t ss ;
ss . ss_sp = _fatalSigAltStack ;
ss . ss_size = sizeof ( _fatalSigAltStack );
ss . ss_flags = 0 ;
sigaltstack ( & ss , ( stack_t * ) 0 );
struct sigaction fsa = {};
fsa . sa_sigaction = & _sighandlerFatal ;
fsa . sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESETHAND ;
sigemptyset ( & fsa . sa_mask );
sigaction ( SIGSEGV , & fsa , ( struct sigaction * ) 0 );
sigaction ( SIGABRT , & fsa , ( struct sigaction * ) 0 );
sigaction ( SIGBUS , & fsa , ( struct sigaction * ) 0 );
sigaction ( SIGFPE , & fsa , ( struct sigaction * ) 0 );
sigaction ( SIGILL , & fsa , ( struct sigaction * ) 0 );
}
#endif
2025-02-26 10:27:23 -08:00
#ifdef ZT_EXTOSDEP
int extosdepFd1 = - 1 ;
int extosdepFd2 = - 1 ;
2025-08-11 13:22:39 -04:00
for ( int i = 1 ; i < argc ; ++ i ) {
if ( argv [ i ][ 0 ] != '-' || argv [ i ][ 1 ] != 'x' )
continue ;
if ( sscanf ( argv [ i ] + 2 , "%d,%d" , & extosdepFd1 , & extosdepFd2 ) == 2 )
break ;
2025-02-26 10:27:23 -08:00
fprintf ( stderr , "bad extosdepFd \n " );
return 1 ;
}
2025-08-11 13:22:39 -04:00
#endif // ZT_EXTOSDEP
2025-02-26 10:27:23 -08:00
2015-04-15 17:00:26 -07:00
/* Ensure that there are no inherited file descriptors open from a previous
* incarnation. This is a hack to ensure that GitHub issue #61 or variants
* of it do not return, and should not do anything otherwise bad. */
{
int mfd = STDIN_FILENO ;
2025-08-11 13:22:39 -04:00
if ( STDOUT_FILENO > mfd )
mfd = STDOUT_FILENO ;
if ( STDERR_FILENO > mfd )
mfd = STDERR_FILENO ;
for ( int f = mfd + 1 ; f < 1024 ; ++ f ) {
2025-02-26 10:27:23 -08:00
#ifdef ZT_EXTOSDEP
2025-08-11 13:22:39 -04:00
if ( f == extosdepFd1 || f == extosdepFd2 )
continue ;
#endif // ZT_EXTOSDEP
2015-04-15 17:00:26 -07:00
:: close ( f );
2025-02-26 10:27:23 -08:00
}
2015-04-15 17:00:26 -07:00
}
bool runAsDaemon = false ;
2025-08-11 13:22:39 -04:00
#endif // __UNIX_LIKE__
2015-04-15 17:00:26 -07:00
#ifdef __WINDOWS__
2015-11-13 12:14:28 -08:00
{
WSADATA wsaData ;
2025-08-11 13:22:39 -04:00
WSAStartup ( MAKEWORD ( 2 , 2 ), & wsaData );
2015-11-13 12:14:28 -08:00
}
2015-04-15 17:00:26 -07:00
#ifdef ZT_WIN_RUN_IN_CONSOLE
bool winRunFromCommandLine = true ;
#else
2015-11-17 09:56:19 -08:00
bool winRunFromCommandLine = false ;
2015-04-15 17:00:26 -07:00
#endif
2025-08-11 13:22:39 -04:00
#endif // __WINDOWS__
2015-04-15 17:00:26 -07:00
2025-08-11 13:22:39 -04:00
if (( strstr ( argv [ 0 ], "zerotier-idtool" )) || ( strstr ( argv [ 0 ], "ZEROTIER-IDTOOL" )))
return idtool ( argc , argv );
if (( strstr ( argv [ 0 ], "zerotier-cli" )) || ( strstr ( argv [ 0 ], "ZEROTIER-CLI" )))
return cli ( argc , argv );
2015-04-15 17:00:26 -07:00
std :: string homeDir ;
2015-09-24 16:21:36 -07:00
unsigned int port = ZT_DEFAULT_PORT ;
2015-05-16 14:52:09 -07:00
bool skipRootCheck = false ;
2015-04-15 17:00:26 -07:00
2025-08-11 13:22:39 -04:00
for ( int i = 1 ; i < argc ; ++ i ) {
2015-04-15 17:00:26 -07:00
if ( argv [ i ][ 0 ] == '-' ) {
2025-08-11 13:22:39 -04:00
switch ( argv [ i ][ 1 ]) {
case 'p' : // port -- for both UDP and TCP, packets and control plane
2015-04-15 17:00:26 -07:00
port = Utils :: strToUInt ( argv [ i ] + 2 );
2015-09-23 14:38:16 -07:00
if ( port > 0xffff ) {
2025-08-11 13:22:39 -04:00
printHelp ( argv [ 0 ], stdout );
2015-04-15 17:00:26 -07:00
return 1 ;
}
break ;
#ifdef __UNIX_LIKE__
2025-08-11 13:22:39 -04:00
case 'd' : // Run in background as daemon
2015-04-15 17:00:26 -07:00
runAsDaemon = true ;
break ;
2025-08-11 13:22:39 -04:00
#endif // __UNIX_LIKE__
2015-04-15 17:00:26 -07:00
2015-05-16 14:52:09 -07:00
case 'U' :
skipRootCheck = true ;
break ;
2025-08-11 13:22:39 -04:00
case 'v' : // Display version
printf ( "%d.%d.%d" ZT_EOL_S , ZEROTIER_ONE_VERSION_MAJOR , ZEROTIER_ONE_VERSION_MINOR , ZEROTIER_ONE_VERSION_REVISION );
2015-04-15 17:00:26 -07:00
return 0 ;
2025-08-11 13:22:39 -04:00
case 'i' : // Invoke idtool personality
2015-04-15 17:00:26 -07:00
if ( argv [ i ][ 2 ]) {
2025-08-11 13:22:39 -04:00
printHelp ( argv [ 0 ], stdout );
2015-04-15 17:00:26 -07:00
return 0 ;
2025-08-11 13:22:39 -04:00
}
else
return idtool ( argc - 1 , argv + 1 );
2015-04-15 17:00:26 -07:00
2025-08-11 13:22:39 -04:00
case 'q' : // Invoke cli personality
2015-04-16 14:55:36 -07:00
if ( argv [ i ][ 2 ]) {
2025-08-11 13:22:39 -04:00
printHelp ( argv [ 0 ], stdout );
2015-04-16 14:55:36 -07:00
return 0 ;
2025-08-11 13:22:39 -04:00
}
else
return cli ( argc , argv );
2015-04-16 14:55:36 -07:00
2015-04-15 17:00:26 -07:00
#ifdef __WINDOWS__
2025-08-11 13:22:39 -04:00
case 'C' : // Run from command line instead of as Windows service
2015-04-15 17:00:26 -07:00
winRunFromCommandLine = true ;
break ;
2025-08-11 13:22:39 -04:00
case 'I' : { // Install this binary as a Windows service
if ( IsCurrentUserLocalAdministrator () != TRUE ) {
fprintf ( stderr , "%s: must be run as a local administrator." ZT_EOL_S , argv [ 0 ]);
return 1 ;
}
std :: string ret ( InstallService ( ZT_SERVICE_NAME , ZT_SERVICE_DISPLAY_NAME , ZT_SERVICE_START_TYPE , ZT_SERVICE_DEPENDENCIES , ZT_SERVICE_ACCOUNT , ZT_SERVICE_PASSWORD ));
if ( ret . length ()) {
fprintf ( stderr , "%s: unable to install service: %s" ZT_EOL_S , argv [ 0 ], ret . c_str ());
return 3 ;
}
return 0 ;
} break ;
2015-04-15 17:00:26 -07:00
2025-08-11 13:22:39 -04:00
case 'R' : { // Uninstall this binary as Windows service
if ( IsCurrentUserLocalAdministrator () != TRUE ) {
fprintf ( stderr , "%s: must be run as a local administrator." ZT_EOL_S , argv [ 0 ]);
return 1 ;
}
std :: string ret ( UninstallService ( ZT_SERVICE_NAME ));
if ( ret . length ()) {
fprintf ( stderr , "%s: unable to uninstall service: %s" ZT_EOL_S , argv [ 0 ], ret . c_str ());
return 3 ;
}
return 0 ;
} break ;
2015-04-15 17:00:26 -07:00
2015-07-30 11:31:38 -07:00
case 'D' : {
2025-08-11 13:22:39 -04:00
std :: string err = WindowsEthernetTap :: destroyAllPersistentTapDevices ();
if ( err . length () > 0 ) {
fprintf ( stderr , "%s: unable to uninstall one or more persistent tap devices: %s" ZT_EOL_S , argv [ 0 ], err . c_str ());
return 3 ;
}
return 0 ;
} break ;
#endif // __WINDOWS__
2025-02-26 10:27:23 -08:00
#ifdef ZT_EXTOSDEP
2025-08-11 13:22:39 -04:00
case 'x' :
break ;
2025-02-26 10:27:23 -08:00
#endif
2015-04-15 17:00:26 -07:00
case 'h' :
case '?' :
default :
2025-08-11 13:22:39 -04:00
printHelp ( argv [ 0 ], stdout );
2015-04-15 17:00:26 -07:00
return 0 ;
}
2025-08-11 13:22:39 -04:00
}
else {
2015-04-15 17:00:26 -07:00
if ( homeDir . length ()) {
2025-08-11 13:22:39 -04:00
printHelp ( argv [ 0 ], stdout );
2015-04-15 17:00:26 -07:00
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
2015-04-15 17:00:26 -07:00
homeDir = argv [ i ];
}
}
}
2025-08-11 13:22:39 -04:00
if ( ! homeDir . length ())
2015-04-15 17:00:26 -07:00
homeDir = OneService :: platformDefaultHomePath ();
2025-08-11 13:22:39 -04:00
if ( ! homeDir . length ()) {
fprintf ( stderr , "%s: no home path specified and no platform default available" ZT_EOL_S , argv [ 0 ]);
2015-04-15 18:32:25 -07:00
return 1 ;
2025-08-11 13:22:39 -04:00
}
else {
std :: vector < std :: string > hpsp ( OSUtils :: split ( homeDir . c_str (), ZT_PATH_SEPARATOR_S , "" , "" ));
2015-04-15 18:32:25 -07:00
std :: string ptmp ;
if ( homeDir [ 0 ] == ZT_PATH_SEPARATOR )
ptmp . push_back ( ZT_PATH_SEPARATOR );
2025-08-11 13:22:39 -04:00
for ( std :: vector < std :: string >:: iterator pi ( hpsp . begin ()); pi != hpsp . end (); ++ pi ) {
2015-04-15 18:32:25 -07:00
if ( ptmp . length () > 0 )
ptmp . push_back ( ZT_PATH_SEPARATOR );
ptmp . append ( * pi );
2025-08-11 13:22:39 -04:00
if (( * pi != "." ) && ( * pi != ".." )) {
if ( ! OSUtils :: mkdir ( ptmp ))
2020-09-10 12:42:11 -07:00
throw std :: runtime_error ( "home path does not exist, and could not create. Please verify local system permissions." );
2015-04-15 18:32:25 -07:00
}
}
}
2015-04-15 17:00:26 -07:00
2023-03-07 16:50:34 -05:00
// Check and fix permissions on critical files at startup
try {
char p [ 4096 ];
OSUtils :: ztsnprintf ( p , sizeof ( p ), "%s" ZT_PATH_SEPARATOR_S "identity.secret" , homeDir . c_str ());
if ( OSUtils :: fileExists ( p )) {
OSUtils :: lockDownFile ( p , false );
}
}
catch (...) {
}
try {
char p [ 4096 ];
OSUtils :: ztsnprintf ( p , sizeof ( p ), "%s" ZT_PATH_SEPARATOR_S "authtoken.secret" , homeDir . c_str ());
if ( OSUtils :: fileExists ( p )) {
OSUtils :: lockDownFile ( p , false );
}
}
catch (...) {
}
2016-08-26 10:38:43 -07:00
// This can be removed once the new controller code has been around for many versions
2025-08-11 13:22:39 -04:00
if ( OSUtils :: fileExists (( homeDir + ZT_PATH_SEPARATOR_S + "controller.db" ). c_str (), true )) {
fprintf ( stderr , "%s: FATAL: an old controller.db exists in %s -- see instructions in controller/README.md for how to migrate!" ZT_EOL_S , argv [ 0 ], homeDir . c_str ());
2016-08-26 10:38:43 -07:00
return 1 ;
}
2015-04-15 17:00:26 -07:00
#ifdef __UNIX_LIKE__
2015-12-21 16:15:39 -08:00
#ifndef ZT_ONE_NO_ROOT_CHECK
2025-08-11 13:22:39 -04:00
if (( ! skipRootCheck ) && ( getuid () != 0 )) {
fprintf ( stderr , "%s: must be run as root (uid 0)" ZT_EOL_S , argv [ 0 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
#endif // !ZT_ONE_NO_ROOT_CHECK
2015-04-15 17:00:26 -07:00
if ( runAsDaemon ) {
2025-11-03 17:48:04 -08:00
prometheus :: simpleapi :: saver . stop ();
2015-04-15 17:00:26 -07:00
long p = ( long ) fork ();
if ( p < 0 ) {
2025-08-11 13:22:39 -04:00
fprintf ( stderr , "%s: could not fork" ZT_EOL_S , argv [ 0 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
2025-08-11 13:22:39 -04:00
}
else if ( p > 0 )
2025-11-03 17:48:04 -08:00
_Exit ( 0 ); // forked
2025-08-11 13:22:39 -04:00
// else p == 0, so we are daemonized
2025-11-03 17:48:04 -08:00
prometheus :: simpleapi :: saver . restart ();
2015-04-15 17:00:26 -07:00
}
2025-08-11 13:22:39 -04:00
#endif // __UNIX_LIKE__
2015-04-15 17:00:26 -07:00
#ifdef __WINDOWS__
2015-07-30 11:31:38 -07:00
// Uninstall legacy tap devices. New devices will automatically be installed and configured
// when tap instances are created.
WindowsEthernetTap :: destroyAllLegacyPersistentTapDevices ();
2015-04-15 17:00:26 -07:00
if ( winRunFromCommandLine ) {
// Running in "interactive" mode (mostly for debugging)
if ( IsCurrentUserLocalAdministrator () != TRUE ) {
2025-08-11 13:22:39 -04:00
if ( ! skipRootCheck ) {
fprintf ( stderr , "%s: must be run as a local administrator." ZT_EOL_S , argv [ 0 ]);
2015-05-16 14:52:09 -07:00
return 1 ;
}
2025-08-11 13:22:39 -04:00
}
else {
2015-07-30 17:52:35 -07:00
_winPokeAHole ();
2015-04-15 17:00:26 -07:00
}
2025-08-11 13:22:39 -04:00
SetConsoleCtrlHandler ( & _winConsoleCtrlHandler , TRUE );
2015-04-15 17:00:26 -07:00
// continues on to ordinary command line execution code below...
2025-08-11 13:22:39 -04:00
}
else {
2015-04-15 17:00:26 -07:00
// Running from service manager
2015-07-30 17:52:35 -07:00
_winPokeAHole ();
2017-03-17 13:55:26 -07:00
ZeroTierOneService zt1WindowsService ;
if ( CServiceBase :: Run ( zt1WindowsService ) == TRUE ) {
2015-04-15 17:00:26 -07:00
return 0 ;
2025-08-11 13:22:39 -04:00
}
else {
fprintf ( stderr , "%s: unable to start service (try -h for help)" ZT_EOL_S , argv [ 0 ]);
2015-04-15 17:00:26 -07:00
return 1 ;
}
}
2025-08-11 13:22:39 -04:00
#endif // __WINDOWS__
2015-04-15 17:00:26 -07:00
2015-04-15 19:00:26 -07:00
#ifdef __UNIX_LIKE__
2017-02-15 16:25:49 -08:00
#ifdef ZT_HAVE_DROP_PRIVILEGES
2025-08-11 13:22:39 -04:00
if ( ! skipRootCheck )
dropPrivileges ( argv [ 0 ], homeDir );
2016-10-16 13:35:29 +02:00
#endif
2015-09-24 16:21:36 -07:00
std :: string pidPath ( homeDir + ZT_PATH_SEPARATOR_S + ZT_PID_PATH );
2015-04-15 19:00:26 -07:00
{
// Write .pid file to home folder
2025-08-11 13:22:39 -04:00
FILE * pf = fopen ( pidPath . c_str (), "w" );
2015-04-15 19:00:26 -07:00
if ( pf ) {
2025-08-11 13:22:39 -04:00
fprintf ( pf , "%ld" , ( long ) getpid ());
2015-04-15 19:00:26 -07:00
fclose ( pf );
}
}
2025-08-11 13:22:39 -04:00
#endif // __UNIX_LIKE__
2015-04-15 19:00:26 -07:00
2025-02-26 10:27:23 -08:00
#ifdef ZT_EXTOSDEP
if ( extosdepFd1 < 0 ) {
fprintf ( stderr , "no extosdepFd specified \n " );
OSUtils :: rm ( pidPath . c_str ());
return 1 ;
}
ExtOsdep :: init ( extosdepFd1 , extosdepFd2 );
#endif
2025-08-11 13:22:39 -04:00
_OneServiceRunner thr ( argv [ 0 ], homeDir , port );
2017-03-17 13:55:26 -07:00
thr . threadMain ();
2025-08-11 13:22:39 -04:00
// Thread::join(Thread::start(&thr));
2015-04-15 18:32:25 -07:00
2017-03-17 20:01:58 -07:00
#ifdef __UNIX_LIKE__
2017-03-17 13:55:26 -07:00
OSUtils :: rm ( pidPath . c_str ());
2017-03-17 20:01:58 -07:00
#endif
2015-04-15 18:32:25 -07:00
2017-03-17 13:55:26 -07:00
return thr . returnValue ;
2015-04-15 17:00:26 -07:00
}