2015-04-10 03:54:00 +00:00
/*
2019-08-23 16:23:39 +00:00
* Copyright ( c ) 2019 ZeroTier , Inc .
2015-04-10 03:54:00 +00:00
*
2019-08-23 16:23:39 +00:00
* Use of this software is governed by the Business Source License included
* in the LICENSE . TXT file in the project ' s root directory .
2015-04-10 03:54:00 +00:00
*
2019-08-23 16:23:39 +00:00
* Change Date : 2023 - 01 - 01
2015-04-10 03:54:00 +00:00
*
2019-08-23 16:23:39 +00:00
* On the date above , in accordance with the Business Source License , use
* of this software will be governed by version 2.0 of the Apache License .
2015-04-10 03:54:00 +00:00
*/
2019-08-23 16:23:39 +00:00
/****/
2015-04-10 03:54:00 +00:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <stdint.h>
2015-04-16 00:00:26 +00:00
# include <time.h>
# include <errno.h>
2015-04-24 22:44:39 +00:00
# include "node/Constants.hpp"
2015-04-16 00:00:26 +00:00
# ifdef __WINDOWS__
# include <WinSock2.h>
# include <Windows.h>
# include <tchar.h>
# include <wchar.h>
# include <lmcons.h>
# include <newdev.h>
# include <atlbase.h>
2015-07-30 18:31:38 +00:00
# include "osdep/WindowsEthernetTap.hpp"
2015-04-16 00:00:26 +00:00
# include "windows/ZeroTierOne/ServiceInstaller.h"
# include "windows/ZeroTierOne/ServiceBase.h"
# include "windows/ZeroTierOne/ZeroTierOneService.h"
# else
# include <unistd.h>
# include <pwd.h>
# include <fcntl.h>
# include <sys/types.h>
# include <sys/stat.h>
2017-02-16 00:25:49 +00:00
# include <sys/uio.h>
# include <dirent.h>
2015-04-16 00:00:26 +00:00
# include <signal.h>
2017-02-16 00:25:49 +00:00
# ifdef __LINUX__
# include <sys/prctl.h>
# include <sys/syscall.h>
# include <sys/wait.h>
2018-10-26 18:59:55 +00:00
# ifndef ZT_NO_CAPABILITIES
2017-02-16 00:25:49 +00:00
# include <linux/capability.h>
# include <linux/securebits.h>
2016-10-16 11:35:29 +00:00
# endif
2015-04-16 00:00:26 +00:00
# endif
2018-10-26 18:59:55 +00:00
# endif
2015-04-10 03:54:00 +00:00
2015-04-15 21:44:30 +00:00
# include <string>
# include <stdexcept>
2016-08-24 20:37:57 +00:00
# include <iostream>
# include <sstream>
2019-05-31 18:41:24 +00:00
# include <algorithm>
2015-04-15 21:44:30 +00:00
2015-04-16 00:00:26 +00:00
# include "version.h"
# include "include/ZeroTierOne.h"
2015-04-16 22:51:56 +00:00
2015-04-16 00:00:26 +00:00
# include "node/Identity.hpp"
# include "node/CertificateOfMembership.hpp"
# include "node/Utils.hpp"
2015-04-16 01:36:32 +00:00
# include "node/NetworkController.hpp"
2017-01-28 01:34:39 +00:00
# include "node/Buffer.hpp"
2015-04-16 22:51:56 +00:00
2015-04-16 00:00:26 +00:00
# include "osdep/OSUtils.hpp"
2015-04-16 21:55:36 +00:00
# include "osdep/Http.hpp"
2017-03-17 20:55:26 +00:00
# include "osdep/Thread.hpp"
2015-04-16 22:51:56 +00:00
2015-04-15 21:44:30 +00:00
# include "service/OneService.hpp"
2015-04-16 22:51:56 +00:00
2016-08-24 20:37:57 +00:00
# include "ext/json/json.hpp"
2015-09-24 23:21:36 +00:00
# define ZT_PID_PATH "zerotier-one.pid"
2015-04-10 03:54:00 +00:00
using namespace ZeroTier ;
2015-04-16 01:32:25 +00:00
static OneService * volatile zt1Service = ( OneService * ) 0 ;
2015-04-16 00:00:26 +00:00
2016-03-04 05:42:30 +00:00
# define PROGRAM_NAME "ZeroTier One"
2019-08-30 22:30:37 +00:00
# define COPYRIGHT_NOTICE "Copyright (c) 2019 ZeroTier, Inc."
# define LICENSE_GRANT "Licensed under the ZeroTier BSL 1.1 (see LICENSE.txt)"
2016-03-04 05:37:36 +00:00
2015-04-16 21:13:44 +00:00
/****************************************************************************/
/* zerotier-cli personality */
/****************************************************************************/
2016-06-29 19:22:37 +00:00
// This is getting deprecated soon in favor of the stuff in cli/
2015-04-16 21:55:36 +00:00
static void cliPrintHelp ( const char * pn , FILE * out )
{
2016-03-04 05:42:30 +00:00
fprintf ( out ,
2017-03-14 21:40:17 +00:00
" %s version %d.%d.%d build %d (platform %d arch %d) " ZT_EOL_S ,
2016-03-04 05:42:30 +00:00
PROGRAM_NAME ,
2017-03-14 21:40:17 +00:00
ZEROTIER_ONE_VERSION_MAJOR , ZEROTIER_ONE_VERSION_MINOR , ZEROTIER_ONE_VERSION_REVISION , ZEROTIER_ONE_VERSION_BUILD ,
ZT_BUILD_PLATFORM , ZT_BUILD_ARCHITECTURE ) ;
2016-03-04 05:37:36 +00:00
fprintf ( out ,
2016-03-04 05:40:09 +00:00
COPYRIGHT_NOTICE ZT_EOL_S
2016-03-04 05:37:36 +00:00
LICENSE_GRANT ZT_EOL_S ) ;
2016-07-21 20:41:02 +00:00
fprintf ( out , " Usage: %s [-switches] <command/path> [<args>] " ZT_EOL_S " " ZT_EOL_S , pn ) ;
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 ) ;
2019-05-31 18:41:24 +00:00
fprintf ( out , " peers - List all peers (prettier) " ZT_EOL_S ) ;
2016-07-21 20:41:02 +00:00
fprintf ( out , " listnetworks - List all networks " ZT_EOL_S ) ;
fprintf ( out , " join <network> - Join a network " ZT_EOL_S ) ;
fprintf ( out , " leave <network> - Leave a network " ZT_EOL_S ) ;
fprintf ( out , " set <network> <setting> - Set a network setting " ZT_EOL_S ) ;
2018-01-10 22:40:31 +00:00
fprintf ( out , " get <network> <setting> - Get a network setting " ZT_EOL_S ) ;
2019-05-31 18:41:24 +00:00
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 <nwid> ip6plane will return the 6PLANE address " ZT_EOL_S ) ;
fprintf ( out , " assigned to this node. " ZT_EOL_S ) ;
2015-04-16 21:55:36 +00:00
}
static std : : string cliFixJsonCRs ( const std : : string & s )
{
std : : string r ;
for ( std : : string : : const_iterator c ( s . begin ( ) ) ; c ! = s . end ( ) ; + + c ) {
if ( * c = = ' \n ' )
r . append ( ZT_EOL_S ) ;
else r . push_back ( * c ) ;
}
return r ;
}
2015-04-16 21:13:44 +00:00
# ifdef __WINDOWS__
static int cli ( int argc , _TCHAR * argv [ ] )
# else
static int cli ( int argc , char * * argv )
# endif
{
2015-04-16 21:55:36 +00:00
unsigned int port = 0 ;
2016-06-29 19:22:37 +00:00
std : : string homeDir , command , arg1 , arg2 , authToken ;
2015-04-16 21:55:36 +00:00
std : : string ip ( " 127.0.0.1 " ) ;
bool json = false ;
for ( int i = 1 ; i < argc ; + + i ) {
if ( argv [ i ] [ 0 ] = = ' - ' ) {
switch ( argv [ i ] [ 1 ] ) {
case ' q ' : // ignore -q used to invoke this personality
if ( argv [ i ] [ 2 ] ) {
cliPrintHelp ( argv [ 0 ] , stdout ) ;
return 1 ;
}
break ;
case ' j ' :
if ( argv [ i ] [ 2 ] ) {
cliPrintHelp ( argv [ 0 ] , stdout ) ;
return 1 ;
}
json = true ;
break ;
2015-05-18 02:13:22 +00:00
case ' p ' :
2015-04-16 21:55:36 +00:00
port = Utils : : strToUInt ( argv [ i ] + 2 ) ;
if ( ( port > 0xffff ) | | ( port = = 0 ) ) {
cliPrintHelp ( argv [ 0 ] , stdout ) ;
return 1 ;
}
break ;
2015-05-18 02:13:22 +00:00
case ' D ' :
2015-04-16 21:55:36 +00:00
if ( argv [ i ] [ 2 ] ) {
homeDir = argv [ i ] + 2 ;
} else {
cliPrintHelp ( argv [ 0 ] , stdout ) ;
return 1 ;
}
break ;
2015-05-18 02:13:22 +00:00
case ' H ' :
2015-04-16 21:55:36 +00:00
if ( argv [ i ] [ 2 ] ) {
ip = argv [ i ] + 2 ;
} else {
cliPrintHelp ( argv [ 0 ] , stdout ) ;
return 1 ;
}
break ;
2015-05-18 02:13:22 +00:00
case ' T ' :
2015-04-16 21:55:36 +00:00
if ( argv [ i ] [ 2 ] ) {
authToken = argv [ i ] + 2 ;
} else {
cliPrintHelp ( argv [ 0 ] , stdout ) ;
return 1 ;
}
break ;
2015-05-18 02:13:22 +00:00
case ' v ' :
2015-04-16 21:55:36 +00:00
if ( argv [ i ] [ 2 ] ) {
cliPrintHelp ( argv [ 0 ] , stdout ) ;
return 1 ;
}
2016-07-21 20:41:02 +00:00
printf ( " %d.%d.%d " ZT_EOL_S , ZEROTIER_ONE_VERSION_MAJOR , ZEROTIER_ONE_VERSION_MINOR , ZEROTIER_ONE_VERSION_REVISION ) ;
2015-04-16 21:55:36 +00:00
return 0 ;
case ' h ' :
case ' ? ' :
default :
cliPrintHelp ( argv [ 0 ] , stdout ) ;
return 0 ;
}
} else {
2016-06-29 19:22:37 +00:00
if ( arg1 . length ( ) )
arg2 = argv [ i ] ;
else if ( command . length ( ) )
2015-04-16 21:55:36 +00:00
arg1 = argv [ i ] ;
else command = argv [ i ] ;
}
}
if ( ! homeDir . length ( ) )
homeDir = OneService : : platformDefaultHomePath ( ) ;
if ( ( ! port ) | | ( ! authToken . length ( ) ) ) {
if ( ! homeDir . length ( ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: missing port or authentication token and no home directory specified to auto-detect " ZT_EOL_S , argv [ 0 ] ) ;
2015-04-16 21:55:36 +00:00
return 2 ;
}
if ( ! port ) {
std : : string portStr ;
OSUtils : : readFile ( ( homeDir + ZT_PATH_SEPARATOR_S + " zerotier-one.port " ) . c_str ( ) , portStr ) ;
port = Utils : : strToUInt ( portStr . c_str ( ) ) ;
if ( ( port = = 0 ) | | ( port > 0xffff ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: missing port and zerotier-one.port not found in %s " ZT_EOL_S , argv [ 0 ] , homeDir . c_str ( ) ) ;
2015-04-16 21:55:36 +00:00
return 2 ;
}
}
if ( ! authToken . length ( ) ) {
OSUtils : : readFile ( ( homeDir + ZT_PATH_SEPARATOR_S + " authtoken.secret " ) . c_str ( ) , authToken ) ;
2015-05-18 02:13:22 +00:00
# ifdef __UNIX_LIKE__
if ( ! authToken . length ( ) ) {
const char * hd = getenv ( " HOME " ) ;
if ( hd ) {
char p [ 4096 ] ;
# ifdef __APPLE__
2017-07-06 23:11:11 +00:00
OSUtils : : ztsnprintf ( p , sizeof ( p ) , " %s/Library/Application Support/ZeroTier/One/authtoken.secret " , hd ) ;
2015-05-18 02:13:22 +00:00
# else
2017-07-06 23:11:11 +00:00
OSUtils : : ztsnprintf ( p , sizeof ( p ) , " %s/.zeroTierOneAuthToken " , hd ) ;
2015-05-18 02:13:22 +00:00
# endif
OSUtils : : readFile ( p , authToken ) ;
}
}
# endif
2015-04-16 21:55:36 +00:00
if ( ! authToken . length ( ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: missing authentication token and authtoken.secret not found (or readable) in %s " ZT_EOL_S , argv [ 0 ] , homeDir . c_str ( ) ) ;
2015-04-16 21:55:36 +00:00
return 2 ;
}
}
}
InetAddress addr ;
{
char addrtmp [ 256 ] ;
2017-07-06 23:11:11 +00:00
OSUtils : : ztsnprintf ( addrtmp , sizeof ( addrtmp ) , " %s/%u " , ip . c_str ( ) , port ) ;
2015-04-16 21:55:36 +00:00
addr = InetAddress ( addrtmp ) ;
}
std : : map < std : : string , std : : string > requestHeaders ;
std : : map < std : : string , std : : string > responseHeaders ;
std : : string responseBody ;
requestHeaders [ " X-ZT1-Auth " ] = authToken ;
2015-07-29 00:10:56 +00: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 ) ;
if ( scode = = 200 ) {
2018-02-14 00:41:21 +00:00
printf ( " %s " , cliFixJsonCRs ( responseBody ) . c_str ( ) ) ;
2015-07-29 00:10:56 +00:00
return 0 ;
} else {
2016-07-21 20:41:02 +00:00
printf ( " %u %s %s " ZT_EOL_S , scode , command . c_str ( ) , responseBody . c_str ( ) ) ;
2015-07-29 00:10:56 +00:00
return 1 ;
}
} else if ( ( command = = " info " ) | | ( command = = " status " ) ) {
2016-08-24 20:37:57 +00:00
const unsigned int scode = Http : : GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , " /status " , requestHeaders , responseHeaders , responseBody ) ;
2018-02-14 00:41:21 +00: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 20:37:57 +00:00
nlohmann : : json j ;
try {
2017-01-19 23:05:26 +00:00
j = OSUtils : : jsonParse ( responseBody ) ;
2016-08-24 20:37:57 +00:00
} catch ( std : : exception & exc ) {
printf ( " %u %s invalid JSON response (%s) " ZT_EOL_S , scode , command . c_str ( ) , exc . what ( ) ) ;
return 1 ;
} catch ( . . . ) {
printf ( " %u %s invalid JSON response (unknown exception) " ZT_EOL_S , scode , command . c_str ( ) ) ;
return 1 ;
}
2015-04-16 21:55:36 +00:00
if ( scode = = 200 ) {
if ( json ) {
2017-01-19 23:05:26 +00:00
printf ( " %s " ZT_EOL_S , OSUtils : : jsonDump ( j ) . c_str ( ) ) ;
2015-04-16 21:55:36 +00:00
} else {
2017-01-19 23:05:26 +00:00
if ( j . is_object ( ) ) {
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 " ) ) ) ;
}
2015-04-16 21:55:36 +00:00
}
2016-08-24 20:37:57 +00:00
return 0 ;
2015-04-16 21:55:36 +00:00
} else {
2016-07-21 20:41:02 +00:00
printf ( " %u %s %s " ZT_EOL_S , scode , command . c_str ( ) , responseBody . c_str ( ) ) ;
2015-04-16 21:55:36 +00:00
return 1 ;
}
} else if ( command = = " listpeers " ) {
2016-08-24 20:37:57 +00:00
const unsigned int scode = Http : : GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , " /peer " , requestHeaders , responseHeaders , responseBody ) ;
2018-02-14 00:41:21 +00: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 20:37:57 +00:00
nlohmann : : json j ;
try {
2017-01-19 23:05:26 +00:00
j = OSUtils : : jsonParse ( responseBody ) ;
2016-08-24 20:37:57 +00:00
} catch ( std : : exception & exc ) {
printf ( " %u %s invalid JSON response (%s) " ZT_EOL_S , scode , command . c_str ( ) , exc . what ( ) ) ;
return 1 ;
} catch ( . . . ) {
printf ( " %u %s invalid JSON response (unknown exception) " ZT_EOL_S , scode , command . c_str ( ) ) ;
return 1 ;
}
2015-04-16 21:55:36 +00:00
if ( scode = = 200 ) {
if ( json ) {
2017-01-19 23:05:26 +00:00
printf ( " %s " ZT_EOL_S , OSUtils : : jsonDump ( j ) . c_str ( ) ) ;
2015-04-16 21:55:36 +00:00
} else {
2017-01-19 23:05:26 +00:00
printf ( " 200 listpeers <ztaddr> <path> <latency> <version> <role> " ZT_EOL_S ) ;
2016-08-24 20:37:57 +00:00
if ( j . is_array ( ) ) {
for ( unsigned long k = 0 ; k < j . size ( ) ; + + k ) {
2017-01-19 23:05:26 +00:00
nlohmann : : json & p = j [ k ] ;
2016-08-24 20:37:57 +00:00
std : : string bestPath ;
2017-01-19 23:05:26 +00:00
nlohmann : : json & paths = p [ " paths " ] ;
2016-08-24 20:37:57 +00:00
if ( paths . is_array ( ) ) {
for ( unsigned long i = 0 ; i < paths . size ( ) ; + + i ) {
2017-01-19 23:05:26 +00:00
nlohmann : : json & path = paths [ i ] ;
2016-08-24 20:37:57 +00:00
if ( path [ " preferred " ] ) {
char tmp [ 256 ] ;
std : : string addr = path [ " address " ] ;
2017-10-02 22:52:57 +00:00
const int64_t now = OSUtils : : now ( ) ;
2018-01-08 21:06:24 +00:00
OSUtils : : ztsnprintf ( tmp , sizeof ( tmp ) , " %s;%lld;%lld " , addr . c_str ( ) , now - ( int64_t ) path [ " lastSend " ] , now - ( int64_t ) path [ " lastReceive " ] ) ;
2016-08-24 20:37:57 +00:00
bestPath = tmp ;
break ;
2015-04-16 22:51:56 +00:00
}
}
}
2016-08-24 20:37:57 +00:00
if ( bestPath . length ( ) = = 0 ) bestPath = " - " ;
char ver [ 128 ] ;
int64_t vmaj = p [ " versionMajor " ] ;
int64_t vmin = p [ " versionMinor " ] ;
int64_t vrev = p [ " versionRev " ] ;
if ( vmaj > = 0 ) {
2017-07-06 23:11:11 +00:00
OSUtils : : ztsnprintf ( ver , sizeof ( ver ) , " %lld.%lld.%lld " , vmaj , vmin , vrev ) ;
2016-08-24 20:37:57 +00:00
} else {
ver [ 0 ] = ' - ' ;
ver [ 1 ] = ( char ) 0 ;
}
2017-01-19 23:05:26 +00: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 22:51:56 +00:00
}
}
2015-04-16 21:55:36 +00:00
}
2016-08-24 20:37:57 +00:00
return 0 ;
2015-04-16 21:55:36 +00:00
} else {
2016-07-21 20:41:02 +00:00
printf ( " %u %s %s " ZT_EOL_S , scode , command . c_str ( ) , responseBody . c_str ( ) ) ;
2015-04-16 21:55:36 +00:00
return 1 ;
}
2019-05-31 18:41:24 +00:00
} else if ( command = = " peers " ) {
const unsigned int scode = Http : : GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , " /peer " , requestHeaders , responseHeaders , responseBody ) ;
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 ) ;
} catch ( std : : exception & exc ) {
printf ( " %u %s invalid JSON response (%s) " ZT_EOL_S , scode , command . c_str ( ) , exc . what ( ) ) ;
return 1 ;
} catch ( . . . ) {
printf ( " %u %s invalid JSON response (unknown exception) " ZT_EOL_S , scode , command . c_str ( ) ) ;
return 1 ;
}
if ( scode = = 200 ) {
if ( json ) {
printf ( " %s " ZT_EOL_S , OSUtils : : jsonDump ( j ) . c_str ( ) ) ;
} else {
printf ( " 200 peers \n <ztaddr> <ver> <role> <lat> <link> <lastTX> <lastRX> <path> " ZT_EOL_S ) ;
if ( j . is_array ( ) ) {
for ( unsigned long k = 0 ; k < j . size ( ) ; + + k ) {
nlohmann : : json & p = j [ k ] ;
std : : string bestPath ;
nlohmann : : json & paths = p [ " paths " ] ;
if ( paths . is_array ( ) ) {
for ( unsigned long i = 0 ; i < paths . size ( ) ; + + i ) {
nlohmann : : json & path = paths [ i ] ;
if ( path [ " preferred " ] ) {
char tmp [ 256 ] ;
std : : string addr = path [ " address " ] ;
const int64_t now = OSUtils : : now ( ) ;
OSUtils : : ztsnprintf ( tmp , sizeof ( tmp ) , " %-8lld %-8lld %s " , now - ( int64_t ) path [ " lastSend " ] , now - ( int64_t ) path [ " lastReceive " ] , addr . c_str ( ) ) ;
bestPath = std : : string ( " DIRECT " ) + tmp ;
break ;
}
}
}
if ( bestPath . length ( ) = = 0 ) bestPath = " RELAY " ;
char ver [ 128 ] ;
int64_t vmaj = p [ " versionMajor " ] ;
int64_t vmin = p [ " versionMinor " ] ;
int64_t vrev = p [ " versionRev " ] ;
if ( vmaj > = 0 ) {
OSUtils : : ztsnprintf ( ver , sizeof ( ver ) , " %lld.%lld.%lld " , vmaj , vmin , vrev ) ;
} else {
ver [ 0 ] = ' - ' ;
ver [ 1 ] = ( char ) 0 ;
}
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 ( ) ) ;
}
}
}
return 0 ;
} else {
printf ( " %u %s %s " ZT_EOL_S , scode , command . c_str ( ) , responseBody . c_str ( ) ) ;
return 1 ;
}
2015-04-16 21:55:36 +00:00
} else if ( command = = " listnetworks " ) {
2016-08-24 20:37:57 +00:00
const unsigned int scode = Http : : GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , " /network " , requestHeaders , responseHeaders , responseBody ) ;
2018-02-14 00:41:21 +00: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 20:37:57 +00:00
nlohmann : : json j ;
try {
2017-01-19 23:05:26 +00:00
j = OSUtils : : jsonParse ( responseBody ) ;
2016-08-24 20:37:57 +00:00
} catch ( std : : exception & exc ) {
printf ( " %u %s invalid JSON response (%s) " ZT_EOL_S , scode , command . c_str ( ) , exc . what ( ) ) ;
return 1 ;
} catch ( . . . ) {
printf ( " %u %s invalid JSON response (unknown exception) " ZT_EOL_S , scode , command . c_str ( ) ) ;
return 1 ;
}
2015-04-16 21:55:36 +00:00
if ( scode = = 200 ) {
if ( json ) {
2017-01-19 23:05:26 +00:00
printf ( " %s " ZT_EOL_S , OSUtils : : jsonDump ( j ) . c_str ( ) ) ;
2015-04-16 21:55:36 +00:00
} else {
2017-01-19 23:05:26 +00:00
printf ( " 200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips> " ZT_EOL_S ) ;
2016-08-24 20:37:57 +00:00
if ( j . is_array ( ) ) {
for ( unsigned long i = 0 ; i < j . size ( ) ; + + i ) {
2017-01-19 23:05:26 +00:00
nlohmann : : json & n = j [ i ] ;
2016-08-24 20:37:57 +00:00
if ( n . is_object ( ) ) {
std : : string aa ;
2017-01-19 23:05:26 +00:00
nlohmann : : json & assignedAddresses = n [ " assignedAddresses " ] ;
2016-08-24 20:37:57 +00:00
if ( assignedAddresses . is_array ( ) ) {
for ( unsigned long j = 0 ; j < assignedAddresses . size ( ) ; + + j ) {
2017-01-19 23:05:26 +00:00
nlohmann : : json & addr = assignedAddresses [ j ] ;
2016-08-24 20:37:57 +00:00
if ( addr . is_string ( ) ) {
if ( aa . length ( ) > 0 ) aa . push_back ( ' , ' ) ;
2016-11-03 19:10:50 +00:00
aa . append ( addr . get < std : : string > ( ) ) ;
2015-04-16 23:12:10 +00:00
}
}
}
2016-08-24 20:37:57 +00:00
if ( aa . length ( ) = = 0 ) aa = " - " ;
2017-01-19 23:05:26 +00:00
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 ( ) ,
OSUtils : : jsonString ( n [ " status " ] , " - " ) . c_str ( ) ,
OSUtils : : jsonString ( n [ " type " ] , " - " ) . c_str ( ) ,
OSUtils : : jsonString ( n [ " portDeviceName " ] , " - " ) . c_str ( ) ,
aa . c_str ( ) ) ;
2015-04-16 23:12:10 +00:00
}
}
}
2015-04-16 21:55:36 +00:00
}
2016-08-24 20:37:57 +00:00
return 0 ;
2015-04-16 21:55:36 +00:00
} else {
2016-07-21 20:41:02 +00:00
printf ( " %u %s %s " ZT_EOL_S , scode , command . c_str ( ) , responseBody . c_str ( ) ) ;
2015-04-16 21:55:36 +00:00
return 1 ;
}
} else if ( command = = " join " ) {
2015-04-16 22:07:58 +00:00
if ( arg1 . length ( ) ! = 16 ) {
2019-05-31 18:41:24 +00:00
printf ( " invalid network id " ZT_EOL_S ) ;
2015-04-16 22:07:58 +00:00
return 2 ;
}
2015-04-16 23:12:10 +00:00
requestHeaders [ " Content-Type " ] = " application/json " ;
requestHeaders [ " Content-Length " ] = " 2 " ;
2015-04-16 22:07:58 +00:00
unsigned int scode = Http : : POST (
1024 * 1024 * 16 ,
60000 ,
( const struct sockaddr * ) & addr ,
( std : : string ( " /network/ " ) + arg1 ) . c_str ( ) ,
requestHeaders ,
2015-04-16 23:12:10 +00:00
" {} " ,
2 ,
2015-04-16 22:07:58 +00:00
responseHeaders ,
responseBody ) ;
if ( scode = = 200 ) {
if ( json ) {
printf ( " %s " , cliFixJsonCRs ( responseBody ) . c_str ( ) ) ;
} else {
2016-07-21 20:41:02 +00:00
printf ( " 200 join OK " ZT_EOL_S ) ;
2015-04-16 22:07:58 +00:00
}
return 0 ;
} else {
2016-07-21 20:41:02 +00:00
printf ( " %u %s %s " ZT_EOL_S , scode , command . c_str ( ) , responseBody . c_str ( ) ) ;
2015-04-16 22:07:58 +00:00
return 1 ;
}
2015-04-16 21:55:36 +00:00
} else if ( command = = " leave " ) {
2015-04-16 22:07:58 +00:00
if ( arg1 . length ( ) ! = 16 ) {
2019-05-31 18:41:24 +00:00
printf ( " invalid network id " ZT_EOL_S ) ;
2015-04-16 22:07:58 +00:00
return 2 ;
}
2015-04-24 22:44:39 +00:00
unsigned int scode = Http : : DEL (
2015-04-16 22:07:58 +00:00
1024 * 1024 * 16 ,
60000 ,
( const struct sockaddr * ) & addr ,
( std : : string ( " /network/ " ) + arg1 ) . c_str ( ) ,
requestHeaders ,
responseHeaders ,
responseBody ) ;
if ( scode = = 200 ) {
if ( json ) {
printf ( " %s " , cliFixJsonCRs ( responseBody ) . c_str ( ) ) ;
} else {
2016-07-21 20:41:02 +00:00
printf ( " 200 leave OK " ZT_EOL_S ) ;
2015-04-16 22:07:58 +00:00
}
return 0 ;
} else {
2016-07-21 20:41:02 +00:00
printf ( " %u %s %s " ZT_EOL_S , scode , command . c_str ( ) , responseBody . c_str ( ) ) ;
2015-04-16 22:07:58 +00:00
return 1 ;
}
2016-06-29 19:22:37 +00:00
} else if ( command = = " set " ) {
if ( arg1 . length ( ) ! = 16 ) {
2019-05-31 18:41:24 +00:00
fprintf ( stderr , " invalid format: must be a 16-digit (network) ID \n " ) ;
return 2 ;
}
if ( ! arg2 . length ( ) ) {
fprintf ( stderr , " invalid format: include a property name to set \n " ) ;
2016-06-29 19:22:37 +00:00
return 2 ;
}
std : : size_t eqidx = arg2 . find ( ' = ' ) ;
if ( eqidx ! = std : : string : : npos ) {
if ( ( arg2 . substr ( 0 , eqidx ) = = " allowManaged " ) | | ( arg2 . substr ( 0 , eqidx ) = = " allowGlobal " ) | | ( arg2 . substr ( 0 , eqidx ) = = " allowDefault " ) ) {
char jsons [ 1024 ] ;
2017-07-06 23:11:11 +00:00
OSUtils : : ztsnprintf ( jsons , sizeof ( jsons ) , " { \" %s \" :%s} " ,
2016-06-29 19:22:37 +00:00
arg2 . substr ( 0 , eqidx ) . c_str ( ) ,
( ( ( arg2 . substr ( eqidx , 2 ) = = " =t " ) | | ( arg2 . substr ( eqidx , 2 ) = = " =1 " ) ) ? " true " : " false " ) ) ;
char cl [ 128 ] ;
2017-07-06 23:11:11 +00:00
OSUtils : : ztsnprintf ( cl , sizeof ( cl ) , " %u " , ( unsigned int ) strlen ( jsons ) ) ;
2016-06-29 19:22:37 +00:00
requestHeaders [ " Content-Type " ] = " application/json " ;
requestHeaders [ " Content-Length " ] = cl ;
unsigned int scode = Http : : POST (
1024 * 1024 * 16 ,
60000 ,
( const struct sockaddr * ) & addr ,
( std : : string ( " /network/ " ) + arg1 ) . c_str ( ) ,
requestHeaders ,
jsons ,
2017-01-13 22:22:36 +00:00
( unsigned long ) strlen ( jsons ) ,
2016-06-29 19:22:37 +00:00
responseHeaders ,
responseBody ) ;
if ( scode = = 200 ) {
printf ( " %s " , cliFixJsonCRs ( responseBody ) . c_str ( ) ) ;
return 0 ;
} else {
2016-07-21 20:41:02 +00:00
printf ( " %u %s %s " ZT_EOL_S , scode , command . c_str ( ) , responseBody . c_str ( ) ) ;
2016-06-29 19:22:37 +00:00
return 1 ;
}
}
} else {
cliPrintHelp ( argv [ 0 ] , stderr ) ;
return 2 ;
}
2018-01-10 22:05:28 +00:00
} else if ( command = = " get " ) {
if ( arg1 . length ( ) ! = 16 ) {
2019-05-31 18:41:24 +00:00
fprintf ( stderr , " invalid format: must be a 16-digit (network) ID \n " ) ;
return 2 ;
}
if ( ! arg2 . length ( ) ) {
fprintf ( stderr , " invalid format: include a property name to get \n " ) ;
2018-01-10 22:05:28 +00:00
return 2 ;
}
const unsigned int scode = Http : : GET ( 1024 * 1024 * 16 , 60000 , ( const struct sockaddr * ) & addr , " /network " , requestHeaders , responseHeaders , responseBody ) ;
2018-02-14 00:41:21 +00: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 22:05:28 +00:00
nlohmann : : json j ;
try {
j = OSUtils : : jsonParse ( responseBody ) ;
} catch ( std : : exception & exc ) {
printf ( " %u %s invalid JSON response (%s) " ZT_EOL_S , scode , command . c_str ( ) , exc . what ( ) ) ;
return 1 ;
} catch ( . . . ) {
printf ( " %u %s invalid JSON response (unknown exception) " ZT_EOL_S , scode , command . c_str ( ) ) ;
return 1 ;
}
2018-10-03 21:24:21 +00:00
bool bNetworkFound = false ;
2018-01-10 22:05:28 +00:00
if ( j . is_array ( ) ) {
for ( unsigned long i = 0 ; i < j . size ( ) ; + + i ) {
nlohmann : : json & n = j [ i ] ;
if ( n . is_object ( ) ) {
if ( n [ " id " ] = = arg1 ) {
2018-10-03 21:24:21 +00:00
bNetworkFound = true ;
2019-05-31 18:41:24 +00:00
std : : string aa ;
if ( arg2 ! = " ip " & & arg2 ! = " ip4 " & & arg2 ! = " ip6 " & & arg2 ! = " ip6plane " & & arg2 ! = " ip6prefix " ) {
aa . append ( OSUtils : : jsonString ( n [ arg2 ] , " - " ) ) ; // Standard network property field
if ( aa = = " - " ) {
printf ( " error, unknown property name \n " ) ;
break ;
}
printf ( " %s \n " , aa . c_str ( ) ) ;
break ;
}
nlohmann : : json & assignedAddresses = n [ " assignedAddresses " ] ;
if ( assignedAddresses . is_array ( ) ) {
int matchingIdxs [ ZT_MAX_ZT_ASSIGNED_ADDRESSES ] ;
int addressCountOfType = 0 ;
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 " )
) {
matchingIdxs [ addressCountOfType + + ] = k ;
}
}
for ( int k = 0 ; k < addressCountOfType ; k + + ) {
nlohmann : : json & addr = assignedAddresses [ matchingIdxs [ k ] ] ;
if ( ! addr . is_string ( ) ) {
continue ;
}
if ( arg2 . find ( " ip6p " ) = = 0 ) {
if ( arg2 = = " ip6plane " ) {
if ( addr . get < std : : string > ( ) . find ( " fc " ) = = 0 ) {
aa . append ( addr . get < std : : string > ( ) . substr ( 0 , addr . get < std : : string > ( ) . find ( " / " ) ) ) ;
if ( k < addressCountOfType - 1 ) aa . append ( " \n " ) ;
}
}
if ( arg2 = = " ip6prefix " ) {
if ( addr . get < std : : string > ( ) . find ( " fc " ) = = 0 ) {
aa . append ( addr . get < std : : string > ( ) . substr ( 0 , addr . get < std : : string > ( ) . find ( " / " ) ) . substr ( 0 , 24 ) ) ;
if ( k < addressCountOfType - 1 ) aa . append ( " \n " ) ;
}
}
}
else {
aa . append ( addr . get < std : : string > ( ) . substr ( 0 , addr . get < std : : string > ( ) . find ( " / " ) ) ) ;
if ( k < addressCountOfType - 1 ) aa . append ( " \n " ) ;
}
}
}
printf ( " %s \n " , aa . c_str ( ) ) ;
2018-01-10 22:05:28 +00:00
}
}
}
}
2018-10-03 21:24:21 +00:00
if ( ! bNetworkFound ) {
fprintf ( stderr , " unknown network ID, check that you are a member of the network \n " ) ;
}
2018-01-10 22:05:28 +00:00
if ( scode = = 200 ) {
return 0 ;
} else {
printf ( " %u %s %s " ZT_EOL_S , scode , command . c_str ( ) , responseBody . c_str ( ) ) ;
return 1 ;
}
2015-04-16 21:55:36 +00:00
} else {
cliPrintHelp ( argv [ 0 ] , stderr ) ;
return 0 ;
}
2015-04-16 23:43:35 +00:00
return 0 ;
2015-04-16 21:13:44 +00:00
}
2015-04-16 00:00:26 +00:00
/****************************************************************************/
/* zerotier-idtool personality */
/****************************************************************************/
static void idtoolPrintHelp ( FILE * out , const char * pn )
{
2016-03-04 05:42:30 +00: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 ) ;
2016-03-04 05:37:36 +00:00
fprintf ( out ,
2016-03-04 05:40:09 +00:00
COPYRIGHT_NOTICE ZT_EOL_S
2016-03-04 05:37:36 +00:00
LICENSE_GRANT ZT_EOL_S ) ;
2016-07-21 20:41:02 +00:00
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 ) ;
2015-04-16 00:00:26 +00:00
}
static Identity getIdFromArg ( char * arg )
2015-04-10 03:54:00 +00:00
{
2015-04-16 00:00:26 +00:00
Identity id ;
if ( ( strlen ( arg ) > 32 ) & & ( arg [ 10 ] = = ' : ' ) ) { // identity is a literal on the command line
if ( id . fromString ( arg ) )
return id ;
} else { // identity is to be read from a file
std : : string idser ;
2015-04-16 01:36:32 +00:00
if ( OSUtils : : readFile ( arg , idser ) ) {
2017-07-06 23:11:11 +00:00
if ( id . fromString ( idser . c_str ( ) ) )
2015-04-16 00:00:26 +00:00
return id ;
}
}
return Identity ( ) ;
}
# ifdef __WINDOWS__
2015-04-16 21:13:44 +00:00
static int idtool ( int argc , _TCHAR * argv [ ] )
2015-04-16 00:00:26 +00:00
# else
2015-04-16 21:13:44 +00:00
static int idtool ( int argc , char * * argv )
2015-04-16 00:00:26 +00:00
# endif
{
if ( argc < 2 ) {
idtoolPrintHelp ( stdout , argv [ 0 ] ) ;
return 1 ;
}
if ( ! strcmp ( argv [ 1 ] , " generate " ) ) {
2016-05-26 16:21:54 +00:00
uint64_t vanity = 0 ;
int vanityBits = 0 ;
if ( argc > = 5 ) {
vanity = Utils : : hexStrToU64 ( argv [ 4 ] ) & 0xffffffffffULL ;
2017-01-13 22:22:36 +00:00
vanityBits = 4 * ( int ) strlen ( argv [ 4 ] ) ;
2016-05-26 16:21:54 +00:00
if ( vanityBits > 40 )
vanityBits = 40 ;
}
2015-04-16 00:00:26 +00:00
Identity id ;
2016-05-26 16:21:54 +00:00
for ( ; ; ) {
2019-07-17 15:52:08 +00:00
id . generate ( Identity : : C25519 ) ;
2016-05-26 16:21:54 +00:00
if ( ( id . address ( ) . toInt ( ) > > ( 40 - vanityBits ) ) = = vanity ) {
if ( vanityBits > 0 ) {
fprintf ( stderr , " vanity address: found %.10llx ! \n " , ( unsigned long long ) id . address ( ) . toInt ( ) ) ;
}
break ;
} 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 ) ) ) ;
}
}
2017-07-06 23:11:11 +00:00
char idtmp [ 1024 ] ;
std : : string idser = id . toString ( true , idtmp ) ;
2015-04-16 00:00:26 +00:00
if ( argc > = 3 ) {
2015-04-16 01:36:32 +00:00
if ( ! OSUtils : : writeFile ( argv [ 2 ] , idser ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " Error writing to %s " ZT_EOL_S , argv [ 2 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
2016-07-21 20:41:02 +00:00
} else printf ( " %s written " ZT_EOL_S , argv [ 2 ] ) ;
2015-04-16 00:00:26 +00:00
if ( argc > = 4 ) {
2017-07-06 23:11:11 +00:00
idser = id . toString ( false , idtmp ) ;
2015-04-16 01:36:32 +00:00
if ( ! OSUtils : : writeFile ( argv [ 3 ] , idser ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " Error writing to %s " ZT_EOL_S , argv [ 3 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
2016-07-21 20:41:02 +00:00
} else printf ( " %s written " ZT_EOL_S , argv [ 3 ] ) ;
2015-04-16 00:00:26 +00:00
}
} else printf ( " %s " , idser . c_str ( ) ) ;
} else if ( ! strcmp ( argv [ 1 ] , " validate " ) ) {
if ( argc < 3 ) {
idtoolPrintHelp ( stdout , argv [ 0 ] ) ;
return 1 ;
}
Identity id = getIdFromArg ( argv [ 2 ] ) ;
if ( ! id ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " Identity argument invalid or file unreadable: %s " ZT_EOL_S , argv [ 2 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
}
if ( ! id . locallyValidate ( ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s FAILED validation. " ZT_EOL_S , argv [ 2 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
2016-07-21 20:41:02 +00:00
} else printf ( " %s is a valid identity " ZT_EOL_S , argv [ 2 ] ) ;
2015-04-16 00:00:26 +00:00
} else if ( ! strcmp ( argv [ 1 ] , " getpublic " ) ) {
if ( argc < 3 ) {
idtoolPrintHelp ( stdout , argv [ 0 ] ) ;
return 1 ;
}
Identity id = getIdFromArg ( argv [ 2 ] ) ;
if ( ! id ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " Identity argument invalid or file unreadable: %s " ZT_EOL_S , argv [ 2 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
}
2017-07-06 23:11:11 +00:00
char idtmp [ 1024 ] ;
printf ( " %s " , id . toString ( false , idtmp ) ) ;
2015-04-16 00:00:26 +00:00
} else if ( ! strcmp ( argv [ 1 ] , " sign " ) ) {
if ( argc < 4 ) {
idtoolPrintHelp ( stdout , argv [ 0 ] ) ;
return 1 ;
}
Identity id = getIdFromArg ( argv [ 2 ] ) ;
if ( ! id ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " Identity argument invalid or file unreadable: %s " ZT_EOL_S , argv [ 2 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
}
if ( ! id . hasPrivate ( ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s does not contain a private key (must use private to sign) " ZT_EOL_S , argv [ 2 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
}
std : : string inf ;
2015-04-16 01:36:32 +00:00
if ( ! OSUtils : : readFile ( argv [ 3 ] , inf ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s is not readable " ZT_EOL_S , argv [ 3 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
}
2019-07-17 15:52:08 +00:00
uint8_t signature [ ZT_SIGNATURE_BUFFER_SIZE ] ;
const unsigned int siglen = id . sign ( inf . data ( ) , ( unsigned int ) inf . length ( ) , signature , sizeof ( signature ) ) ;
char hexbuf [ 256 ] ;
printf ( " %s " , Utils : : hex ( signature , siglen , hexbuf ) ) ;
2015-04-16 00:00:26 +00:00
} else if ( ! strcmp ( argv [ 1 ] , " verify " ) ) {
2018-04-23 21:11:26 +00:00
if ( argc < 5 ) {
2015-04-16 00:00:26 +00:00
idtoolPrintHelp ( stdout , argv [ 0 ] ) ;
return 1 ;
}
Identity id = getIdFromArg ( argv [ 2 ] ) ;
if ( ! id ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " Identity argument invalid or file unreadable: %s " ZT_EOL_S , argv [ 2 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
}
std : : string inf ;
2015-04-16 01:36:32 +00:00
if ( ! OSUtils : : readFile ( argv [ 3 ] , inf ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s is not readable " ZT_EOL_S , argv [ 3 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
}
2017-07-06 23:11:11 +00:00
char buf [ 4096 ] ;
std : : string signature ( buf , Utils : : unhex ( argv [ 4 ] , buf , ( unsigned int ) sizeof ( buf ) ) ) ;
2015-04-16 00:00:26 +00:00
if ( ( signature . length ( ) > ZT_ADDRESS_LENGTH ) & & ( id . verify ( inf . data ( ) , ( unsigned int ) inf . length ( ) , signature . data ( ) , ( unsigned int ) signature . length ( ) ) ) ) {
2016-07-21 20:41:02 +00:00
printf ( " %s signature valid " ZT_EOL_S , argv [ 3 ] ) ;
2015-04-16 00:00:26 +00:00
} else {
2018-04-23 21:11:26 +00:00
signature . clear ( ) ;
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 ] ) ;
return 1 ;
}
} else {
fprintf ( stderr , " %s signature check FAILED " ZT_EOL_S , argv [ 3 ] ) ;
return 1 ;
}
2015-04-16 00:00:26 +00:00
}
} else {
idtoolPrintHelp ( stdout , argv [ 0 ] ) ;
return 1 ;
}
2015-04-10 03:54:00 +00:00
return 0 ;
}
2015-04-16 00:00:26 +00:00
/****************************************************************************/
/* Unix helper functions and signal handlers */
/****************************************************************************/
# ifdef __UNIX_LIKE__
static void _sighandlerHup ( int sig )
{
}
static void _sighandlerQuit ( int sig )
{
2015-04-16 01:36:32 +00:00
OneService * s = zt1Service ;
if ( s )
s - > terminate ( ) ;
2015-04-16 00:00:26 +00:00
else exit ( 0 ) ;
}
# endif
2017-02-16 00:25:49 +00:00
// Drop privileges on Linux, if supported by libc etc. and "zerotier-one" user exists on system
2018-05-09 20:51:32 +00:00
# if defined(__LINUX__) && !defined(ZT_NO_CAPABILITIES)
2017-02-16 00:57:33 +00:00
# ifndef PR_CAP_AMBIENT
# define PR_CAP_AMBIENT 47
# define PR_CAP_AMBIENT_IS_SET 1
# define PR_CAP_AMBIENT_RAISE 2
# define PR_CAP_AMBIENT_LOWER 3
# define PR_CAP_AMBIENT_CLEAR_ALL 4
# endif
2017-02-16 00:25:49 +00:00
# define ZT_LINUX_USER "zerotier-one"
# 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 ;
} ;
static inline int _zt_capset ( cap_header_struct * hdrp , cap_data_struct * datap ) { return syscall ( SYS_capset , hdrp , datap ) ; }
static void _notDropping ( const char * procName , const std : : string & homeDir )
{
struct stat buf ;
if ( lstat ( homeDir . c_str ( ) , & buf ) < 0 ) {
if ( buf . st_uid ! = 0 | | buf . st_gid ! = 0 ) {
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 ) ;
exit ( 1 ) ;
}
}
fprintf ( stderr , " %s: WARNING: failed to drop privileges (kernel may not support required prctl features), running as root " ZT_EOL_S , procName ) ;
}
static int _setCapabilities ( int flags )
{
cap_header_struct capheader = { _LINUX_CAPABILITY_VERSION_1 , 0 } ;
cap_data_struct capdata ;
capdata . inheritable = capdata . permitted = capdata . effective = flags ;
return _zt_capset ( & capheader , & capdata ) ;
}
static void _recursiveChown ( const char * path , uid_t uid , gid_t gid )
{
struct dirent de ;
struct dirent * dptr ;
lchown ( path , uid , gid ) ;
DIR * d = opendir ( path ) ;
if ( ! d )
return ;
dptr = ( struct dirent * ) 0 ;
for ( ; ; ) {
if ( readdir_r ( d , & de , & dptr ) ! = 0 )
break ;
if ( ! dptr )
break ;
if ( ( strcmp ( dptr - > d_name , " . " ) ! = 0 ) & & ( strcmp ( dptr - > d_name , " .. " ) ! = 0 ) & & ( strlen ( dptr - > d_name ) > 0 ) ) {
std : : string p ( path ) ;
p . push_back ( ZT_PATH_SEPARATOR ) ;
p . append ( dptr - > d_name ) ;
_recursiveChown ( p . c_str ( ) , uid , gid ) ; // will just fail and return on regular files
}
}
closedir ( d ) ;
}
static void dropPrivileges ( const char * procName , const std : : string & homeDir )
{
if ( getuid ( ) ! = 0 )
return ;
// dropPrivileges switches to zerotier-one user while retaining CAP_NET_ADMIN
// and CAP_NET_RAW capabilities.
struct passwd * targetUser = getpwnam ( ZT_LINUX_USER ) ;
if ( ! targetUser )
return ;
if ( prctl ( PR_CAP_AMBIENT , PR_CAP_AMBIENT_IS_SET , CAP_NET_RAW , 0 , 0 ) < 0 ) {
// Kernel has no support for ambient capabilities.
_notDropping ( procName , homeDir ) ;
return ;
}
if ( prctl ( PR_SET_SECUREBITS , SECBIT_KEEP_CAPS | SECBIT_NOROOT ) < 0 ) {
_notDropping ( procName , homeDir ) ;
return ;
}
// Change ownership of our home directory if everything looks good (does nothing if already chown'd)
_recursiveChown ( homeDir . c_str ( ) , targetUser - > pw_uid , targetUser - > pw_gid ) ;
2018-01-30 18:47:31 +00:00
if ( _setCapabilities ( ( 1 < < CAP_NET_ADMIN ) | ( 1 < < CAP_NET_RAW ) | ( 1 < < CAP_SETUID ) | ( 1 < < CAP_SETGID ) | ( 1 < < CAP_NET_BIND_SERVICE ) ) < 0 ) {
2017-02-16 00:25:49 +00:00
_notDropping ( procName , homeDir ) ;
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)
fprintf ( stderr , " %s: FATAL: prctl(PR_SET_DUMPABLE) failed while attempting to relinquish root permissions " ZT_EOL_S , procName ) ;
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 18:47:31 +00:00
if ( _setCapabilities ( ( 1 < < CAP_NET_ADMIN ) | ( 1 < < CAP_NET_RAW ) | ( 1 < < CAP_NET_BIND_SERVICE ) ) < 0 ) {
2017-02-16 00:25:49 +00:00
fprintf ( stderr , " %s: FATAL: unable to drop capabilities after relinquishing root " ZT_EOL_S , procName ) ;
exit ( 1 ) ;
}
if ( prctl ( PR_SET_DUMPABLE , oldDumpable ) < 0 ) {
fprintf ( stderr , " %s: FATAL: prctl(PR_SET_DUMPABLE) failed while attempting to relinquish root permissions " ZT_EOL_S , procName ) ;
exit ( 1 ) ;
}
if ( prctl ( PR_CAP_AMBIENT , PR_CAP_AMBIENT_RAISE , CAP_NET_ADMIN , 0 , 0 ) < 0 ) {
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 ) ;
exit ( 1 ) ;
}
if ( prctl ( PR_CAP_AMBIENT , PR_CAP_AMBIENT_RAISE , CAP_NET_RAW , 0 , 0 ) < 0 ) {
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 ) ;
exit ( 1 ) ;
}
}
} // anonymous namespace
# endif // __LINUX__
2015-04-16 00:00:26 +00: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 )
{
switch ( dwCtrlType ) {
case CTRL_C_EVENT :
case CTRL_BREAK_EVENT :
case CTRL_CLOSE_EVENT :
case CTRL_SHUTDOWN_EVENT :
2015-04-24 22:44:39 +00:00
OneService * s = zt1Service ;
if ( s )
s - > terminate ( ) ;
2015-04-16 00:00:26 +00:00
return TRUE ;
}
return FALSE ;
}
static void _winPokeAHole ( )
{
char myPath [ MAX_PATH ] ;
DWORD ps = GetModuleFileNameA ( NULL , myPath , sizeof ( myPath ) ) ;
if ( ( ps > 0 ) & & ( ps < ( DWORD ) sizeof ( myPath ) ) ) {
STARTUPINFOA startupInfo ;
PROCESS_INFORMATION processInfo ;
startupInfo . cb = sizeof ( startupInfo ) ;
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ) ) ;
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ) ) ;
2015-07-31 00:52:35 +00:00
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 ) ) {
2015-04-16 00:00:26 +00:00
WaitForSingleObject ( processInfo . hProcess , INFINITE ) ;
CloseHandle ( processInfo . hProcess ) ;
CloseHandle ( processInfo . hThread ) ;
}
startupInfo . cb = sizeof ( startupInfo ) ;
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ) ) ;
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ) ) ;
2015-07-31 00:52:35 +00:00
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 ) ) {
2015-04-16 00:00:26 +00:00
WaitForSingleObject ( processInfo . hProcess , INFINITE ) ;
CloseHandle ( processInfo . hProcess ) ;
CloseHandle ( processInfo . hThread ) ;
}
startupInfo . cb = sizeof ( startupInfo ) ;
memset ( & startupInfo , 0 , sizeof ( STARTUPINFOA ) ) ;
memset ( & processInfo , 0 , sizeof ( PROCESS_INFORMATION ) ) ;
2015-07-31 00:52:35 +00:00
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 ) ) {
2015-04-16 00:00:26 +00:00
WaitForSingleObject ( processInfo . hProcess , INFINITE ) ;
CloseHandle ( processInfo . hProcess ) ;
CloseHandle ( processInfo . hThread ) ;
}
}
}
// Returns true if this is running as the local administrator
static BOOL IsCurrentUserLocalAdministrator ( void )
{
BOOL fReturn = FALSE ;
DWORD dwStatus ;
DWORD dwAccessMask ;
DWORD dwAccessDesired ;
DWORD dwACLSize ;
DWORD dwStructureSize = sizeof ( PRIVILEGE_SET ) ;
PACL pACL = NULL ;
PSID psidAdmin = NULL ;
HANDLE hToken = NULL ;
HANDLE hImpersonationToken = NULL ;
PRIVILEGE_SET ps ;
GENERIC_MAPPING GenericMapping ;
PSECURITY_DESCRIPTOR psdAdmin = NULL ;
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY ;
const DWORD ACCESS_READ = 1 ;
const DWORD ACCESS_WRITE = 2 ;
__try
{
if ( ! OpenThreadToken ( GetCurrentThread ( ) , TOKEN_DUPLICATE | TOKEN_QUERY , TRUE , & hToken ) )
{
if ( GetLastError ( ) ! = ERROR_NO_TOKEN )
__leave ;
if ( ! OpenProcessToken ( GetCurrentProcess ( ) , TOKEN_DUPLICATE | TOKEN_QUERY , & hToken ) )
__leave ;
}
if ( ! DuplicateToken ( hToken , SecurityImpersonation , & hImpersonationToken ) )
__leave ;
if ( ! AllocateAndInitializeSid ( & SystemSidAuthority , 2 ,
SECURITY_BUILTIN_DOMAIN_RID ,
DOMAIN_ALIAS_RID_ADMINS ,
0 , 0 , 0 , 0 , 0 , 0 , & psidAdmin ) )
__leave ;
psdAdmin = LocalAlloc ( LPTR , SECURITY_DESCRIPTOR_MIN_LENGTH ) ;
if ( psdAdmin = = NULL )
__leave ;
if ( ! InitializeSecurityDescriptor ( psdAdmin , SECURITY_DESCRIPTOR_REVISION ) )
__leave ;
dwACLSize = sizeof ( ACL ) + sizeof ( ACCESS_ALLOWED_ACE ) + GetLengthSid ( psidAdmin ) - sizeof ( DWORD ) ;
pACL = ( PACL ) LocalAlloc ( LPTR , dwACLSize ) ;
if ( pACL = = NULL )
__leave ;
if ( ! InitializeAcl ( pACL , dwACLSize , ACL_REVISION2 ) )
__leave ;
dwAccessMask = ACCESS_READ | ACCESS_WRITE ;
if ( ! AddAccessAllowedAce ( pACL , ACL_REVISION2 , dwAccessMask , psidAdmin ) )
__leave ;
if ( ! SetSecurityDescriptorDacl ( psdAdmin , TRUE , pACL , FALSE ) )
__leave ;
SetSecurityDescriptorGroup ( psdAdmin , psidAdmin , FALSE ) ;
SetSecurityDescriptorOwner ( psdAdmin , psidAdmin , FALSE ) ;
if ( ! IsValidSecurityDescriptor ( psdAdmin ) )
__leave ;
dwAccessDesired = ACCESS_READ ;
GenericMapping . GenericRead = ACCESS_READ ;
GenericMapping . GenericWrite = ACCESS_WRITE ;
GenericMapping . GenericExecute = 0 ;
GenericMapping . GenericAll = ACCESS_READ | ACCESS_WRITE ;
if ( ! AccessCheck ( psdAdmin , hImpersonationToken , dwAccessDesired ,
& GenericMapping , & ps , & dwStructureSize , & dwStatus ,
& fReturn ) )
{
fReturn = FALSE ;
__leave ;
}
}
__finally
{
// Clean up.
if ( pACL ) LocalFree ( pACL ) ;
if ( psdAdmin ) LocalFree ( psdAdmin ) ;
if ( psidAdmin ) FreeSid ( psidAdmin ) ;
if ( hImpersonationToken ) CloseHandle ( hImpersonationToken ) ;
if ( hToken ) CloseHandle ( hToken ) ;
}
return fReturn ;
}
# endif // __WINDOWS__
/****************************************************************************/
/* main() and friends */
/****************************************************************************/
static void printHelp ( const char * cn , FILE * out )
{
2016-03-04 05:42:30 +00: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 ) ;
2016-03-04 05:37:36 +00:00
fprintf ( out ,
2016-03-04 05:40:09 +00:00
COPYRIGHT_NOTICE ZT_EOL_S
2016-03-04 05:37:36 +00:00
LICENSE_GRANT ZT_EOL_S ) ;
2016-07-21 20:41:02 +00:00
fprintf ( out , " Usage: %s [-switches] [home directory] " ZT_EOL_S " " ZT_EOL_S , cn ) ;
fprintf ( out , " Available switches: " ZT_EOL_S ) ;
fprintf ( out , " -h - Display this help " ZT_EOL_S ) ;
fprintf ( out , " -v - Show version " ZT_EOL_S ) ;
2016-10-16 11:35:29 +00:00
fprintf ( out , " -U - Skip privilege check and do not attempt to drop privileges " ZT_EOL_S ) ;
2016-07-21 20:41:02 +00:00
fprintf ( out , " -p<port> - Port for UDP and TCP/HTTP (default: 9993, 0 for random) " ZT_EOL_S ) ;
2015-07-30 18:31:38 +00:00
2015-04-16 00:00:26 +00:00
# ifdef __UNIX_LIKE__
2016-07-21 20:41:02 +00:00
fprintf ( out , " -d - Fork and run as daemon (Unix-ish OSes) " ZT_EOL_S ) ;
2015-04-16 00:00:26 +00:00
# endif // __UNIX_LIKE__
2015-07-30 18:31:38 +00:00
2015-04-16 00:00:26 +00:00
# ifdef __WINDOWS__
2016-07-21 20:41:02 +00: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 ) ;
2015-04-16 00:00:26 +00:00
# endif // __WINDOWS__
2015-07-30 18:31:38 +00:00
2016-07-21 20:41:02 +00: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-16 00:00:26 +00:00
}
2017-03-17 20:55:26 +00: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 ( )
{
try {
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
case OneService : : ONE_NORMAL_TERMINATION :
break ;
case OneService : : ONE_UNRECOVERABLE_ERROR :
fprintf ( stderr , " %s: fatal error: %s " ZT_EOL_S , progname , zt1Service - > fatalErrorMessage ( ) . c_str ( ) ) ;
returnValue = 1 ;
break ;
case OneService : : ONE_IDENTITY_COLLISION : {
delete zt1Service ;
zt1Service = ( OneService * ) 0 ;
std : : string oldid ;
OSUtils : : readFile ( ( homeDir + ZT_PATH_SEPARATOR_S + " identity.secret " ) . c_str ( ) , oldid ) ;
if ( oldid . length ( ) ) {
OSUtils : : writeFile ( ( homeDir + ZT_PATH_SEPARATOR_S + " identity.secret.saved_after_collision " ) . c_str ( ) , oldid ) ;
OSUtils : : rm ( ( homeDir + ZT_PATH_SEPARATOR_S + " identity.secret " ) . c_str ( ) ) ;
OSUtils : : rm ( ( homeDir + ZT_PATH_SEPARATOR_S + " identity.public " ) . c_str ( ) ) ;
}
} continue ; // restart!
}
break ; // terminate loop -- normally we don't keep restarting
}
delete zt1Service ;
zt1Service = ( OneService * ) 0 ;
} catch ( . . . ) {
fprintf ( stderr , " %s: unexpected exception starting main OneService instance " ZT_EOL_S , progname ) ;
returnValue = 1 ;
}
}
const char * progname ;
unsigned int returnValue ;
unsigned int port ;
const std : : string & homeDir ;
} ;
2015-04-16 00:00:26 +00:00
# ifdef __WINDOWS__
2017-04-18 19:22:44 +00:00
int __cdecl _tmain ( int argc , _TCHAR * argv [ ] )
2015-04-16 00:00:26 +00:00
# else
int main ( int argc , char * * argv )
# endif
{
# ifdef __UNIX_LIKE__
signal ( SIGHUP , & _sighandlerHup ) ;
signal ( SIGPIPE , SIG_IGN ) ;
2018-11-13 20:07:58 +00:00
signal ( SIGIO , SIG_IGN ) ;
2015-04-16 00:00:26 +00:00
signal ( SIGUSR1 , SIG_IGN ) ;
signal ( SIGUSR2 , SIG_IGN ) ;
signal ( SIGALRM , SIG_IGN ) ;
signal ( SIGINT , & _sighandlerQuit ) ;
signal ( SIGTERM , & _sighandlerQuit ) ;
signal ( SIGQUIT , & _sighandlerQuit ) ;
2018-11-13 20:07:58 +00:00
signal ( SIGINT , & _sighandlerQuit ) ;
2015-04-16 00:00:26 +00: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 ;
if ( STDOUT_FILENO > mfd ) mfd = STDOUT_FILENO ;
if ( STDERR_FILENO > mfd ) mfd = STDERR_FILENO ;
for ( int f = mfd + 1 ; f < 1024 ; + + f )
: : close ( f ) ;
}
bool runAsDaemon = false ;
# endif // __UNIX_LIKE__
# ifdef __WINDOWS__
2015-11-13 20:14:28 +00:00
{
WSADATA wsaData ;
WSAStartup ( MAKEWORD ( 2 , 2 ) , & wsaData ) ;
}
2015-04-16 00:00:26 +00:00
# ifdef ZT_WIN_RUN_IN_CONSOLE
bool winRunFromCommandLine = true ;
# else
2015-11-17 17:56:19 +00:00
bool winRunFromCommandLine = false ;
2015-04-16 00:00:26 +00:00
# endif
# endif // __WINDOWS__
if ( ( strstr ( argv [ 0 ] , " zerotier-idtool " ) ) | | ( strstr ( argv [ 0 ] , " ZEROTIER-IDTOOL " ) ) )
return idtool ( argc , argv ) ;
2015-04-16 21:55:36 +00:00
if ( ( strstr ( argv [ 0 ] , " zerotier-cli " ) ) | | ( strstr ( argv [ 0 ] , " ZEROTIER-CLI " ) ) )
return cli ( argc , argv ) ;
2015-04-16 00:00:26 +00:00
std : : string homeDir ;
2015-09-24 23:21:36 +00:00
unsigned int port = ZT_DEFAULT_PORT ;
2015-05-16 21:52:09 +00:00
bool skipRootCheck = false ;
2015-04-16 00:00:26 +00:00
for ( int i = 1 ; i < argc ; + + i ) {
if ( argv [ i ] [ 0 ] = = ' - ' ) {
switch ( argv [ i ] [ 1 ] ) {
case ' p ' : // port -- for both UDP and TCP, packets and control plane
port = Utils : : strToUInt ( argv [ i ] + 2 ) ;
2015-09-23 21:38:16 +00:00
if ( port > 0xffff ) {
2015-04-16 00:00:26 +00:00
printHelp ( argv [ 0 ] , stdout ) ;
return 1 ;
}
break ;
# ifdef __UNIX_LIKE__
case ' d ' : // Run in background as daemon
runAsDaemon = true ;
break ;
# endif // __UNIX_LIKE__
2015-05-16 21:52:09 +00:00
case ' U ' :
skipRootCheck = true ;
break ;
2015-04-16 00:00:26 +00:00
case ' v ' : // Display version
2016-07-21 20:41:02 +00:00
printf ( " %d.%d.%d " ZT_EOL_S , ZEROTIER_ONE_VERSION_MAJOR , ZEROTIER_ONE_VERSION_MINOR , ZEROTIER_ONE_VERSION_REVISION ) ;
2015-04-16 00:00:26 +00:00
return 0 ;
case ' i ' : // Invoke idtool personality
if ( argv [ i ] [ 2 ] ) {
printHelp ( argv [ 0 ] , stdout ) ;
return 0 ;
2017-12-08 19:17:07 +00:00
} else return idtool ( argc - 1 , argv + 1 ) ;
2015-04-16 00:00:26 +00:00
2015-04-16 21:55:36 +00:00
case ' q ' : // Invoke cli personality
if ( argv [ i ] [ 2 ] ) {
printHelp ( argv [ 0 ] , stdout ) ;
return 0 ;
} else return cli ( argc , argv ) ;
2015-04-16 00:00:26 +00:00
# ifdef __WINDOWS__
case ' C ' : // Run from command line instead of as Windows service
winRunFromCommandLine = true ;
break ;
case ' I ' : { // Install this binary as a Windows service
if ( IsCurrentUserLocalAdministrator ( ) ! = TRUE ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: must be run as a local administrator. " ZT_EOL_S , argv [ 0 ] ) ;
2015-04-16 00:00:26 +00:00
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 ( ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: unable to install service: %s " ZT_EOL_S , argv [ 0 ] , ret . c_str ( ) ) ;
2015-04-16 00:00:26 +00:00
return 3 ;
}
return 0 ;
} break ;
case ' R ' : { // Uninstall this binary as Windows service
if ( IsCurrentUserLocalAdministrator ( ) ! = TRUE ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: must be run as a local administrator. " ZT_EOL_S , argv [ 0 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
}
std : : string ret ( UninstallService ( ZT_SERVICE_NAME ) ) ;
if ( ret . length ( ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: unable to uninstall service: %s " ZT_EOL_S , argv [ 0 ] , ret . c_str ( ) ) ;
2015-04-16 00:00:26 +00:00
return 3 ;
}
return 0 ;
} break ;
2015-07-30 18:31:38 +00:00
case ' D ' : {
std : : string err = WindowsEthernetTap : : destroyAllPersistentTapDevices ( ) ;
if ( err . length ( ) > 0 ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: unable to uninstall one or more persistent tap devices: %s " ZT_EOL_S , argv [ 0 ] , err . c_str ( ) ) ;
2015-04-16 00:00:26 +00:00
return 3 ;
}
2015-07-30 18:31:38 +00:00
return 0 ;
2015-04-16 00:00:26 +00:00
} break ;
# endif // __WINDOWS__
case ' h ' :
case ' ? ' :
default :
printHelp ( argv [ 0 ] , stdout ) ;
return 0 ;
}
} else {
if ( homeDir . length ( ) ) {
printHelp ( argv [ 0 ] , stdout ) ;
return 0 ;
} else {
homeDir = argv [ i ] ;
}
}
}
if ( ! homeDir . length ( ) )
homeDir = OneService : : platformDefaultHomePath ( ) ;
2015-04-16 01:32:25 +00:00
if ( ! homeDir . length ( ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: no home path specified and no platform default available " ZT_EOL_S , argv [ 0 ] ) ;
2015-04-16 01:32:25 +00:00
return 1 ;
} else {
2016-11-18 23:49:28 +00:00
std : : vector < std : : string > hpsp ( OSUtils : : split ( homeDir . c_str ( ) , ZT_PATH_SEPARATOR_S , " " , " " ) ) ;
2015-04-16 01:32:25 +00:00
std : : string ptmp ;
if ( homeDir [ 0 ] = = ZT_PATH_SEPARATOR )
ptmp . push_back ( ZT_PATH_SEPARATOR ) ;
for ( std : : vector < std : : string > : : iterator pi ( hpsp . begin ( ) ) ; pi ! = hpsp . end ( ) ; + + pi ) {
if ( ptmp . length ( ) > 0 )
ptmp . push_back ( ZT_PATH_SEPARATOR ) ;
ptmp . append ( * pi ) ;
if ( ( * pi ! = " . " ) & & ( * pi ! = " .. " ) ) {
if ( ! OSUtils : : mkdir ( ptmp ) )
throw std : : runtime_error ( " home path does not exist, and could not create " ) ;
}
}
}
2015-04-16 00:00:26 +00:00
2016-08-26 17:38:43 +00:00
// This can be removed once the new controller code has been around for many versions
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 ( ) ) ;
return 1 ;
}
2015-04-16 00:00:26 +00:00
# ifdef __UNIX_LIKE__
2015-12-22 00:15:39 +00:00
# ifndef ZT_ONE_NO_ROOT_CHECK
2015-05-16 21:52:09 +00:00
if ( ( ! skipRootCheck ) & & ( getuid ( ) ! = 0 ) ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: must be run as root (uid 0) " ZT_EOL_S , argv [ 0 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
}
2015-12-22 00:15:39 +00:00
# endif // !ZT_ONE_NO_ROOT_CHECK
2015-04-16 00:00:26 +00:00
if ( runAsDaemon ) {
long p = ( long ) fork ( ) ;
if ( p < 0 ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: could not fork " ZT_EOL_S , argv [ 0 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
} else if ( p > 0 )
return 0 ; // forked
// else p == 0, so we are daemonized
}
# endif // __UNIX_LIKE__
# ifdef __WINDOWS__
2015-07-30 18:31:38 +00:00
// Uninstall legacy tap devices. New devices will automatically be installed and configured
// when tap instances are created.
WindowsEthernetTap : : destroyAllLegacyPersistentTapDevices ( ) ;
2015-04-16 00:00:26 +00:00
if ( winRunFromCommandLine ) {
// Running in "interactive" mode (mostly for debugging)
if ( IsCurrentUserLocalAdministrator ( ) ! = TRUE ) {
2015-05-16 21:52:09 +00:00
if ( ! skipRootCheck ) {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: must be run as a local administrator. " ZT_EOL_S , argv [ 0 ] ) ;
2015-05-16 21:52:09 +00:00
return 1 ;
}
} else {
2015-07-31 00:52:35 +00:00
_winPokeAHole ( ) ;
2015-04-16 00:00:26 +00:00
}
SetConsoleCtrlHandler ( & _winConsoleCtrlHandler , TRUE ) ;
// continues on to ordinary command line execution code below...
} else {
// Running from service manager
2015-07-31 00:52:35 +00:00
_winPokeAHole ( ) ;
2017-03-17 20:55:26 +00:00
ZeroTierOneService zt1WindowsService ;
if ( CServiceBase : : Run ( zt1WindowsService ) = = TRUE ) {
2015-04-16 00:00:26 +00:00
return 0 ;
} else {
2016-07-21 20:41:02 +00:00
fprintf ( stderr , " %s: unable to start service (try -h for help) " ZT_EOL_S , argv [ 0 ] ) ;
2015-04-16 00:00:26 +00:00
return 1 ;
}
}
# endif // __WINDOWS__
2015-04-16 02:00:26 +00:00
# ifdef __UNIX_LIKE__
2017-02-16 00:25:49 +00:00
# ifdef ZT_HAVE_DROP_PRIVILEGES
2017-05-19 23:16:10 +00:00
if ( ! skipRootCheck )
dropPrivileges ( argv [ 0 ] , homeDir ) ;
2016-10-16 11:35:29 +00:00
# endif
2015-09-24 23:21:36 +00:00
std : : string pidPath ( homeDir + ZT_PATH_SEPARATOR_S + ZT_PID_PATH ) ;
2015-04-16 02:00:26 +00:00
{
// Write .pid file to home folder
FILE * pf = fopen ( pidPath . c_str ( ) , " w " ) ;
if ( pf ) {
fprintf ( pf , " %ld " , ( long ) getpid ( ) ) ;
fclose ( pf ) ;
}
}
# endif // __UNIX_LIKE__
2017-03-17 20:55:26 +00:00
_OneServiceRunner thr ( argv [ 0 ] , homeDir , port ) ;
thr . threadMain ( ) ;
//Thread::join(Thread::start(&thr));
2015-04-16 01:32:25 +00:00
2017-03-18 03:01:58 +00:00
# ifdef __UNIX_LIKE__
2017-03-17 20:55:26 +00:00
OSUtils : : rm ( pidPath . c_str ( ) ) ;
2017-03-18 03:01:58 +00:00
# endif
2015-04-16 01:32:25 +00:00
2017-03-17 20:55:26 +00:00
return thr . returnValue ;
2015-04-16 00:00:26 +00:00
}