2015-01-06 13:45:10 -08:00
/*
2019-08-23 09:23:39 -07:00
* Copyright (c)2019 ZeroTier, Inc.
2015-01-06 13:45:10 -08:00
*
2019-08-23 09:23:39 -07: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-01-06 13:45:10 -08:00
*
2020-08-20 12:51:39 -07:00
* Change Date: 2025-01-01
2015-01-06 13:45:10 -08:00
*
2019-08-23 09:23:39 -07: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-01-06 13:45:10 -08:00
*/
2019-08-23 09:23:39 -07:00
/****/
2015-01-06 13:45:10 -08:00
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
2016-11-03 12:10:50 -07:00
#ifndef _WIN32
2015-01-06 13:45:10 -08:00
#include <sys/time.h>
2016-11-03 12:10:50 -07:00
#endif
2015-01-06 13:45:10 -08:00
#include <sys/types.h>
2015-01-08 14:27:55 -08:00
#include <algorithm>
#include <utility>
2015-02-24 14:17:57 -08:00
#include <stdexcept>
2016-08-12 11:30:27 -07:00
#include <map>
2017-04-24 19:16:36 -07:00
#include <thread>
2017-04-27 00:59:36 -07:00
#include <memory>
2015-01-08 14:27:55 -08:00
2015-04-17 15:21:53 -07:00
#include "../include/ZeroTierOne.h"
2017-08-16 14:14:49 -07:00
#include "../version.h"
2015-04-17 15:21:53 -07:00
2016-08-17 10:42:32 -07:00
#include "EmbeddedNetworkController.hpp"
2019-07-22 13:43:06 -07:00
#include "LFDB.hpp"
#include "FileDB.hpp"
#ifdef ZT_CONTROLLER_USE_LIBPQ
#include "PostgreSQL.hpp"
#endif
2015-10-06 15:56:18 -07:00
#include "../node/Node.hpp"
2015-02-24 14:17:57 -08:00
#include "../node/CertificateOfMembership.hpp"
#include "../node/NetworkConfig.hpp"
2016-06-16 16:05:57 -07:00
#include "../node/Dictionary.hpp"
2015-04-21 16:41:35 -07:00
#include "../node/MAC.hpp"
2015-10-06 15:56:18 -07:00
2016-08-12 11:30:27 -07:00
using json = nlohmann :: json ;
2015-03-17 14:54:13 -07:00
2015-05-15 09:41:45 -07:00
// API version reported via JSON control plane
2018-01-10 14:31:28 -08:00
#define ZT_NETCONF_CONTROLLER_API_VERSION 4
2015-05-15 09:41:45 -07:00
2016-02-04 14:39:43 -08:00
// Min duration between requests for an address/nwid combo to prevent floods
#define ZT_NETCONF_MIN_REQUEST_PERIOD 1000
2015-07-23 10:10:17 -07:00
2018-01-12 10:38:19 -08:00
// Global maximum size of arrays in JSON objects
#define ZT_CONTROLLER_MAX_ARRAY_SIZE 16384
2015-01-06 13:45:10 -08:00
namespace ZeroTier {
2017-11-03 11:39:27 -07:00
namespace {
2016-08-16 14:05:17 -07:00
static json _renderRule ( ZT_VirtualNetworkRule & rule )
2016-08-15 18:49:50 -07:00
{
char tmp [ 128 ];
2016-08-16 14:05:17 -07:00
json r = json :: object ();
2016-10-11 12:00:16 -07:00
const ZT_VirtualNetworkRuleType rt = ( ZT_VirtualNetworkRuleType )( rule . t & 0x3f );
switch ( rt ) {
2016-08-15 18:49:50 -07:00
case ZT_NETWORK_RULE_ACTION_DROP :
r [ "type" ] = "ACTION_DROP" ;
break ;
case ZT_NETWORK_RULE_ACTION_ACCEPT :
r [ "type" ] = "ACTION_ACCEPT" ;
break ;
case ZT_NETWORK_RULE_ACTION_TEE :
r [ "type" ] = "ACTION_TEE" ;
2017-07-06 16:11:11 -07:00
r [ "address" ] = Address ( rule . v . fwd . address ). toString ( tmp );
2016-08-29 17:48:36 -07:00
r [ "flags" ] = ( unsigned int ) rule . v . fwd . flags ;
r [ "length" ] = ( unsigned int ) rule . v . fwd . length ;
2016-08-15 18:49:50 -07:00
break ;
2016-09-23 16:08:38 -07:00
case ZT_NETWORK_RULE_ACTION_WATCH :
r [ "type" ] = "ACTION_WATCH" ;
2017-07-06 16:11:11 -07:00
r [ "address" ] = Address ( rule . v . fwd . address ). toString ( tmp );
2016-09-23 16:08:38 -07:00
r [ "flags" ] = ( unsigned int ) rule . v . fwd . flags ;
r [ "length" ] = ( unsigned int ) rule . v . fwd . length ;
break ;
2016-08-15 18:49:50 -07:00
case ZT_NETWORK_RULE_ACTION_REDIRECT :
r [ "type" ] = "ACTION_REDIRECT" ;
2017-07-06 16:11:11 -07:00
r [ "address" ] = Address ( rule . v . fwd . address ). toString ( tmp );
2016-08-29 17:48:36 -07:00
r [ "flags" ] = ( unsigned int ) rule . v . fwd . flags ;
2016-08-15 18:49:50 -07:00
break ;
2017-02-06 14:07:30 -08:00
case ZT_NETWORK_RULE_ACTION_BREAK :
r [ "type" ] = "ACTION_BREAK" ;
2016-08-25 13:31:23 -07:00
break ;
2016-10-11 12:00:16 -07:00
default :
2016-10-05 16:38:42 -07:00
break ;
2016-08-15 18:49:50 -07:00
}
2016-10-11 12:00:16 -07:00
2020-07-16 18:31:56 +02:00
if ( r . empty ()) {
2016-10-11 12:00:16 -07:00
switch ( rt ) {
case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS :
r [ "type" ] = "MATCH_SOURCE_ZEROTIER_ADDRESS" ;
2017-07-06 16:11:11 -07:00
r [ "zt" ] = Address ( rule . v . zt ). toString ( tmp );
2016-10-11 12:00:16 -07:00
break ;
case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS :
r [ "type" ] = "MATCH_DEST_ZEROTIER_ADDRESS" ;
2017-07-06 16:11:11 -07:00
r [ "zt" ] = Address ( rule . v . zt ). toString ( tmp );
2016-10-11 12:00:16 -07:00
break ;
case ZT_NETWORK_RULE_MATCH_VLAN_ID :
r [ "type" ] = "MATCH_VLAN_ID" ;
r [ "vlanId" ] = ( unsigned int ) rule . v . vlanId ;
break ;
case ZT_NETWORK_RULE_MATCH_VLAN_PCP :
r [ "type" ] = "MATCH_VLAN_PCP" ;
r [ "vlanPcp" ] = ( unsigned int ) rule . v . vlanPcp ;
break ;
case ZT_NETWORK_RULE_MATCH_VLAN_DEI :
r [ "type" ] = "MATCH_VLAN_DEI" ;
r [ "vlanDei" ] = ( unsigned int ) rule . v . vlanDei ;
break ;
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE :
r [ "type" ] = "MATCH_MAC_SOURCE" ;
2017-07-06 16:11:11 -07:00
OSUtils :: ztsnprintf ( tmp , sizeof ( tmp ), "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x" ,( unsigned int ) rule . v . mac [ 0 ],( unsigned int ) rule . v . mac [ 1 ],( unsigned int ) rule . v . mac [ 2 ],( unsigned int ) rule . v . mac [ 3 ],( unsigned int ) rule . v . mac [ 4 ],( unsigned int ) rule . v . mac [ 5 ]);
2016-10-11 12:00:16 -07:00
r [ "mac" ] = tmp ;
break ;
case ZT_NETWORK_RULE_MATCH_MAC_DEST :
r [ "type" ] = "MATCH_MAC_DEST" ;
2017-07-06 16:11:11 -07:00
OSUtils :: ztsnprintf ( tmp , sizeof ( tmp ), "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x" ,( unsigned int ) rule . v . mac [ 0 ],( unsigned int ) rule . v . mac [ 1 ],( unsigned int ) rule . v . mac [ 2 ],( unsigned int ) rule . v . mac [ 3 ],( unsigned int ) rule . v . mac [ 4 ],( unsigned int ) rule . v . mac [ 5 ]);
2016-10-11 12:00:16 -07:00
r [ "mac" ] = tmp ;
break ;
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE :
r [ "type" ] = "MATCH_IPV4_SOURCE" ;
2017-07-06 16:11:11 -07:00
r [ "ip" ] = InetAddress ( & ( rule . v . ipv4 . ip ), 4 ,( unsigned int ) rule . v . ipv4 . mask ). toString ( tmp );
2016-10-11 12:00:16 -07:00
break ;
case ZT_NETWORK_RULE_MATCH_IPV4_DEST :
r [ "type" ] = "MATCH_IPV4_DEST" ;
2017-07-06 16:11:11 -07:00
r [ "ip" ] = InetAddress ( & ( rule . v . ipv4 . ip ), 4 ,( unsigned int ) rule . v . ipv4 . mask ). toString ( tmp );
2016-10-11 12:00:16 -07:00
break ;
case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE :
r [ "type" ] = "MATCH_IPV6_SOURCE" ;
2017-07-06 16:11:11 -07:00
r [ "ip" ] = InetAddress ( rule . v . ipv6 . ip , 16 ,( unsigned int ) rule . v . ipv6 . mask ). toString ( tmp );
2016-10-11 12:00:16 -07:00
break ;
case ZT_NETWORK_RULE_MATCH_IPV6_DEST :
r [ "type" ] = "MATCH_IPV6_DEST" ;
2017-07-06 16:11:11 -07:00
r [ "ip" ] = InetAddress ( rule . v . ipv6 . ip , 16 ,( unsigned int ) rule . v . ipv6 . mask ). toString ( tmp );
2016-10-11 12:00:16 -07:00
break ;
case ZT_NETWORK_RULE_MATCH_IP_TOS :
r [ "type" ] = "MATCH_IP_TOS" ;
2017-02-07 09:33:39 -08:00
r [ "mask" ] = ( unsigned int ) rule . v . ipTos . mask ;
r [ "start" ] = ( unsigned int ) rule . v . ipTos . value [ 0 ];
r [ "end" ] = ( unsigned int ) rule . v . ipTos . value [ 1 ];
2016-10-11 12:00:16 -07:00
break ;
case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL :
r [ "type" ] = "MATCH_IP_PROTOCOL" ;
r [ "ipProtocol" ] = ( unsigned int ) rule . v . ipProtocol ;
break ;
case ZT_NETWORK_RULE_MATCH_ETHERTYPE :
r [ "type" ] = "MATCH_ETHERTYPE" ;
r [ "etherType" ] = ( unsigned int ) rule . v . etherType ;
break ;
case ZT_NETWORK_RULE_MATCH_ICMP :
r [ "type" ] = "MATCH_ICMP" ;
2016-10-13 14:14:46 -07:00
r [ "icmpType" ] = ( unsigned int ) rule . v . icmp . type ;
2016-10-11 12:00:16 -07:00
if (( rule . v . icmp . flags & 0x01 ) != 0 )
2016-10-13 14:14:46 -07:00
r [ "icmpCode" ] = ( unsigned int ) rule . v . icmp . code ;
else r [ "icmpCode" ] = json ();
2016-10-11 12:00:16 -07:00
break ;
case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE :
r [ "type" ] = "MATCH_IP_SOURCE_PORT_RANGE" ;
r [ "start" ] = ( unsigned int ) rule . v . port [ 0 ];
r [ "end" ] = ( unsigned int ) rule . v . port [ 1 ];
break ;
case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE :
r [ "type" ] = "MATCH_IP_DEST_PORT_RANGE" ;
r [ "start" ] = ( unsigned int ) rule . v . port [ 0 ];
r [ "end" ] = ( unsigned int ) rule . v . port [ 1 ];
break ;
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS :
r [ "type" ] = "MATCH_CHARACTERISTICS" ;
2017-07-06 16:11:11 -07:00
OSUtils :: ztsnprintf ( tmp , sizeof ( tmp ), "%.16llx" , rule . v . characteristics );
2016-10-11 12:00:16 -07:00
r [ "mask" ] = tmp ;
break ;
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE :
r [ "type" ] = "MATCH_FRAME_SIZE_RANGE" ;
r [ "start" ] = ( unsigned int ) rule . v . frameSize [ 0 ];
r [ "end" ] = ( unsigned int ) rule . v . frameSize [ 1 ];
break ;
case ZT_NETWORK_RULE_MATCH_RANDOM :
r [ "type" ] = "MATCH_RANDOM" ;
r [ "probability" ] = ( unsigned long ) rule . v . randomProbability ;
break ;
case ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE :
r [ "type" ] = "MATCH_TAGS_DIFFERENCE" ;
r [ "id" ] = rule . v . tag . id ;
r [ "value" ] = rule . v . tag . value ;
break ;
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND :
r [ "type" ] = "MATCH_TAGS_BITWISE_AND" ;
r [ "id" ] = rule . v . tag . id ;
r [ "value" ] = rule . v . tag . value ;
break ;
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR :
r [ "type" ] = "MATCH_TAGS_BITWISE_OR" ;
r [ "id" ] = rule . v . tag . id ;
r [ "value" ] = rule . v . tag . value ;
break ;
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR :
r [ "type" ] = "MATCH_TAGS_BITWISE_XOR" ;
r [ "id" ] = rule . v . tag . id ;
r [ "value" ] = rule . v . tag . value ;
break ;
case ZT_NETWORK_RULE_MATCH_TAGS_EQUAL :
r [ "type" ] = "MATCH_TAGS_EQUAL" ;
r [ "id" ] = rule . v . tag . id ;
r [ "value" ] = rule . v . tag . value ;
break ;
2017-02-28 09:22:10 -08:00
case ZT_NETWORK_RULE_MATCH_TAG_SENDER :
r [ "type" ] = "MATCH_TAG_SENDER" ;
r [ "id" ] = rule . v . tag . id ;
r [ "value" ] = rule . v . tag . value ;
break ;
case ZT_NETWORK_RULE_MATCH_TAG_RECEIVER :
r [ "type" ] = "MATCH_TAG_RECEIVER" ;
r [ "id" ] = rule . v . tag . id ;
r [ "value" ] = rule . v . tag . value ;
break ;
2017-10-24 13:33:53 -07:00
case ZT_NETWORK_RULE_MATCH_INTEGER_RANGE :
r [ "type" ] = "INTEGER_RANGE" ;
OSUtils :: ztsnprintf ( tmp , sizeof ( tmp ), "%.16llx" , rule . v . intRange . start );
r [ "start" ] = tmp ;
OSUtils :: ztsnprintf ( tmp , sizeof ( tmp ), "%.16llx" , rule . v . intRange . start + ( uint64_t ) rule . v . intRange . end );
r [ "end" ] = tmp ;
r [ "idx" ] = rule . v . intRange . idx ;
r [ "little" ] = (( rule . v . intRange . format & 0x80 ) != 0 );
r [ "bits" ] = ( rule . v . intRange . format & 63 ) + 1 ;
break ;
2016-10-11 12:00:16 -07:00
default :
break ;
}
2020-07-16 18:31:56 +02:00
if ( ! r . empty ()) {
2016-10-11 12:00:16 -07:00
r [ "not" ] = (( rule . t & 0x80 ) != 0 );
r [ "or" ] = (( rule . t & 0x40 ) != 0 );
}
}
2016-08-15 18:49:50 -07:00
return r ;
}
2016-08-25 16:08:40 -07:00
static bool _parseRule ( json & r , ZT_VirtualNetworkRule & rule )
2016-08-15 18:49:50 -07:00
{
2016-08-25 16:08:40 -07:00
if ( ! r . is_object ())
2016-08-16 14:05:17 -07:00
return false ;
2016-10-11 12:00:16 -07:00
2017-01-10 13:51:10 -08:00
const std :: string t ( OSUtils :: jsonString ( r [ "type" ], "" ));
2016-08-15 18:49:50 -07:00
memset ( & rule , 0 , sizeof ( ZT_VirtualNetworkRule ));
2016-10-11 12:00:16 -07:00
2017-01-10 13:51:10 -08:00
if ( OSUtils :: jsonBool ( r [ "not" ], false ))
2016-08-15 18:49:50 -07:00
rule . t = 0x80 ;
else rule . t = 0x00 ;
2017-01-10 13:51:10 -08:00
if ( OSUtils :: jsonBool ( r [ "or" ], false ))
2016-10-11 12:00:16 -07:00
rule . t |= 0x40 ;
2017-02-28 09:22:10 -08:00
bool tag = false ;
2016-08-15 18:49:50 -07:00
if ( t == "ACTION_DROP" ) {
rule . t |= ZT_NETWORK_RULE_ACTION_DROP ;
return true ;
} else if ( t == "ACTION_ACCEPT" ) {
rule . t |= ZT_NETWORK_RULE_ACTION_ACCEPT ;
return true ;
} else if ( t == "ACTION_TEE" ) {
rule . t |= ZT_NETWORK_RULE_ACTION_TEE ;
2017-01-10 13:51:10 -08:00
rule . v . fwd . address = Utils :: hexStrToU64 ( OSUtils :: jsonString ( r [ "address" ], "0" ). c_str ()) & 0xffffffffffULL ;
rule . v . fwd . flags = ( uint32_t )( OSUtils :: jsonInt ( r [ "flags" ], 0ULL ) & 0xffffffffULL );
rule . v . fwd . length = ( uint16_t )( OSUtils :: jsonInt ( r [ "length" ], 0ULL ) & 0xffffULL );
2016-08-15 18:49:50 -07:00
return true ;
2016-09-23 16:08:38 -07:00
} else if ( t == "ACTION_WATCH" ) {
rule . t |= ZT_NETWORK_RULE_ACTION_WATCH ;
2017-01-10 13:51:10 -08:00
rule . v . fwd . address = Utils :: hexStrToU64 ( OSUtils :: jsonString ( r [ "address" ], "0" ). c_str ()) & 0xffffffffffULL ;
rule . v . fwd . flags = ( uint32_t )( OSUtils :: jsonInt ( r [ "flags" ], 0ULL ) & 0xffffffffULL );
rule . v . fwd . length = ( uint16_t )( OSUtils :: jsonInt ( r [ "length" ], 0ULL ) & 0xffffULL );
2016-09-23 16:08:38 -07:00
return true ;
2016-08-15 18:49:50 -07:00
} else if ( t == "ACTION_REDIRECT" ) {
rule . t |= ZT_NETWORK_RULE_ACTION_REDIRECT ;
2017-01-10 13:51:10 -08:00
rule . v . fwd . address = Utils :: hexStrToU64 ( OSUtils :: jsonString ( r [ "address" ], "0" ). c_str ()) & 0xffffffffffULL ;
rule . v . fwd . flags = ( uint32_t )( OSUtils :: jsonInt ( r [ "flags" ], 0ULL ) & 0xffffffffULL );
2016-08-15 18:49:50 -07:00
return true ;
2017-02-06 14:07:30 -08:00
} else if ( t == "ACTION_BREAK" ) {
rule . t |= ZT_NETWORK_RULE_ACTION_BREAK ;
2016-08-25 13:31:23 -07:00
return true ;
2016-08-15 18:49:50 -07:00
} else if ( t == "MATCH_SOURCE_ZEROTIER_ADDRESS" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS ;
2017-01-10 13:51:10 -08:00
rule . v . zt = Utils :: hexStrToU64 ( OSUtils :: jsonString ( r [ "zt" ], "0" ). c_str ()) & 0xffffffffffULL ;
2016-08-15 18:49:50 -07:00
return true ;
} else if ( t == "MATCH_DEST_ZEROTIER_ADDRESS" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS ;
2017-01-10 13:51:10 -08:00
rule . v . zt = Utils :: hexStrToU64 ( OSUtils :: jsonString ( r [ "zt" ], "0" ). c_str ()) & 0xffffffffffULL ;
2016-08-15 18:49:50 -07:00
return true ;
} else if ( t == "MATCH_VLAN_ID" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_VLAN_ID ;
2017-01-10 13:51:10 -08:00
rule . v . vlanId = ( uint16_t )( OSUtils :: jsonInt ( r [ "vlanId" ], 0ULL ) & 0xffffULL );
2016-08-15 18:49:50 -07:00
return true ;
} else if ( t == "MATCH_VLAN_PCP" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_VLAN_PCP ;
2017-01-10 13:51:10 -08:00
rule . v . vlanPcp = ( uint8_t )( OSUtils :: jsonInt ( r [ "vlanPcp" ], 0ULL ) & 0xffULL );
2016-08-15 18:49:50 -07:00
return true ;
} else if ( t == "MATCH_VLAN_DEI" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_VLAN_DEI ;
2017-01-10 13:51:10 -08:00
rule . v . vlanDei = ( uint8_t )( OSUtils :: jsonInt ( r [ "vlanDei" ], 0ULL ) & 0xffULL );
2016-08-15 18:49:50 -07:00
return true ;
} else if ( t == "MATCH_MAC_SOURCE" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_MAC_SOURCE ;
2017-01-10 13:51:10 -08:00
const std :: string mac ( OSUtils :: jsonString ( r [ "mac" ], "0" ));
2016-08-15 18:49:50 -07:00
Utils :: unhex ( mac . c_str (),( unsigned int ) mac . length (), rule . v . mac , 6 );
return true ;
} else if ( t == "MATCH_MAC_DEST" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_MAC_DEST ;
2017-01-10 13:51:10 -08:00
const std :: string mac ( OSUtils :: jsonString ( r [ "mac" ], "0" ));
2016-08-15 18:49:50 -07:00
Utils :: unhex ( mac . c_str (),( unsigned int ) mac . length (), rule . v . mac , 6 );
return true ;
} else if ( t == "MATCH_IPV4_SOURCE" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_IPV4_SOURCE ;
2017-07-06 16:11:11 -07:00
InetAddress ip ( OSUtils :: jsonString ( r [ "ip" ], "0.0.0.0" ). c_str ());
2016-08-15 18:49:50 -07:00
rule . v . ipv4 . ip = reinterpret_cast < struct sockaddr_in *> ( & ip ) -> sin_addr . s_addr ;
rule . v . ipv4 . mask = Utils :: ntoh ( reinterpret_cast < struct sockaddr_in *> ( & ip ) -> sin_port ) & 0xff ;
if ( rule . v . ipv4 . mask > 32 ) rule . v . ipv4 . mask = 32 ;
return true ;
} else if ( t == "MATCH_IPV4_DEST" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_IPV4_DEST ;
2017-07-06 16:11:11 -07:00
InetAddress ip ( OSUtils :: jsonString ( r [ "ip" ], "0.0.0.0" ). c_str ());
2016-08-15 18:49:50 -07:00
rule . v . ipv4 . ip = reinterpret_cast < struct sockaddr_in *> ( & ip ) -> sin_addr . s_addr ;
rule . v . ipv4 . mask = Utils :: ntoh ( reinterpret_cast < struct sockaddr_in *> ( & ip ) -> sin_port ) & 0xff ;
if ( rule . v . ipv4 . mask > 32 ) rule . v . ipv4 . mask = 32 ;
return true ;
} else if ( t == "MATCH_IPV6_SOURCE" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_IPV6_SOURCE ;
2017-07-06 16:11:11 -07:00
InetAddress ip ( OSUtils :: jsonString ( r [ "ip" ], "::0" ). c_str ());
2019-03-22 15:50:15 -07:00
memcpy ( rule . v . ipv6 . ip , reinterpret_cast < struct sockaddr_in6 *> ( & ip ) -> sin6_addr . s6_addr , 16 );
2016-08-15 18:49:50 -07:00
rule . v . ipv6 . mask = Utils :: ntoh ( reinterpret_cast < struct sockaddr_in6 *> ( & ip ) -> sin6_port ) & 0xff ;
if ( rule . v . ipv6 . mask > 128 ) rule . v . ipv6 . mask = 128 ;
return true ;
} else if ( t == "MATCH_IPV6_DEST" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_IPV6_DEST ;
2017-07-06 16:11:11 -07:00
InetAddress ip ( OSUtils :: jsonString ( r [ "ip" ], "::0" ). c_str ());
2019-03-22 15:50:15 -07:00
memcpy ( rule . v . ipv6 . ip , reinterpret_cast < struct sockaddr_in6 *> ( & ip ) -> sin6_addr . s6_addr , 16 );
2016-08-15 18:49:50 -07:00
rule . v . ipv6 . mask = Utils :: ntoh ( reinterpret_cast < struct sockaddr_in6 *> ( & ip ) -> sin6_port ) & 0xff ;
if ( rule . v . ipv6 . mask > 128 ) rule . v . ipv6 . mask = 128 ;
return true ;
} else if ( t == "MATCH_IP_TOS" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_IP_TOS ;
2017-02-07 09:33:39 -08:00
rule . v . ipTos . mask = ( uint8_t )( OSUtils :: jsonInt ( r [ "mask" ], 0ULL ) & 0xffULL );
rule . v . ipTos . value [ 0 ] = ( uint8_t )( OSUtils :: jsonInt ( r [ "start" ], 0ULL ) & 0xffULL );
rule . v . ipTos . value [ 1 ] = ( uint8_t )( OSUtils :: jsonInt ( r [ "end" ], 0ULL ) & 0xffULL );
2016-08-15 18:49:50 -07:00
return true ;
} else if ( t == "MATCH_IP_PROTOCOL" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_IP_PROTOCOL ;
2017-01-10 13:51:10 -08:00
rule . v . ipProtocol = ( uint8_t )( OSUtils :: jsonInt ( r [ "ipProtocol" ], 0ULL ) & 0xffULL );
2016-08-15 18:49:50 -07:00
return true ;
2016-10-11 12:00:16 -07:00
} else if ( t == "MATCH_ETHERTYPE" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_ETHERTYPE ;
2017-01-10 13:51:10 -08:00
rule . v . etherType = ( uint16_t )( OSUtils :: jsonInt ( r [ "etherType" ], 0ULL ) & 0xffffULL );
2016-10-11 12:00:16 -07:00
return true ;
2016-08-31 14:01:15 -07:00
} else if ( t == "MATCH_ICMP" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_ICMP ;
2017-01-10 13:51:10 -08:00
rule . v . icmp . type = ( uint8_t )( OSUtils :: jsonInt ( r [ "icmpType" ], 0ULL ) & 0xffULL );
2016-10-13 14:14:46 -07:00
json & code = r [ "icmpCode" ];
2016-08-31 14:01:15 -07:00
if ( code . is_null ()) {
rule . v . icmp . code = 0 ;
rule . v . icmp . flags = 0x00 ;
} else {
2017-01-10 13:51:10 -08:00
rule . v . icmp . code = ( uint8_t )( OSUtils :: jsonInt ( code , 0ULL ) & 0xffULL );
2016-08-31 14:01:15 -07:00
rule . v . icmp . flags = 0x01 ;
}
return true ;
2016-08-15 18:49:50 -07:00
} else if ( t == "MATCH_IP_SOURCE_PORT_RANGE" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE ;
2017-01-10 13:51:10 -08:00
rule . v . port [ 0 ] = ( uint16_t )( OSUtils :: jsonInt ( r [ "start" ], 0ULL ) & 0xffffULL );
rule . v . port [ 1 ] = ( uint16_t )( OSUtils :: jsonInt ( r [ "end" ],( uint64_t ) rule . v . port [ 0 ]) & 0xffffULL );
2016-08-15 18:49:50 -07:00
return true ;
} else if ( t == "MATCH_IP_DEST_PORT_RANGE" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE ;
2017-01-10 13:51:10 -08:00
rule . v . port [ 0 ] = ( uint16_t )( OSUtils :: jsonInt ( r [ "start" ], 0ULL ) & 0xffffULL );
rule . v . port [ 1 ] = ( uint16_t )( OSUtils :: jsonInt ( r [ "end" ],( uint64_t ) rule . v . port [ 0 ]) & 0xffffULL );
2016-08-15 18:49:50 -07:00
return true ;
} else if ( t == "MATCH_CHARACTERISTICS" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_CHARACTERISTICS ;
if ( r . count ( "mask" )) {
2016-10-05 12:54:46 -07:00
json & v = r [ "mask" ];
2016-08-16 14:05:17 -07:00
if ( v . is_number ()) {
2016-10-05 12:54:46 -07:00
rule . v . characteristics = v ;
2016-08-16 14:05:17 -07:00
} else {
std :: string tmp = v ;
2016-10-05 12:54:46 -07:00
rule . v . characteristics = Utils :: hexStrToU64 ( tmp . c_str ());
2016-08-16 14:05:17 -07:00
}
2016-08-15 18:49:50 -07:00
}
return true ;
} else if ( t == "MATCH_FRAME_SIZE_RANGE" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE ;
2017-01-10 13:51:10 -08:00
rule . v . frameSize [ 0 ] = ( uint16_t )( OSUtils :: jsonInt ( r [ "start" ], 0ULL ) & 0xffffULL );
rule . v . frameSize [ 1 ] = ( uint16_t )( OSUtils :: jsonInt ( r [ "end" ],( uint64_t ) rule . v . frameSize [ 0 ]) & 0xffffULL );
2016-08-15 18:49:50 -07:00
return true ;
2016-09-30 14:07:00 -07:00
} else if ( t == "MATCH_RANDOM" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_RANDOM ;
2017-01-10 13:51:10 -08:00
rule . v . randomProbability = ( uint32_t )( OSUtils :: jsonInt ( r [ "probability" ], 0ULL ) & 0xffffffffULL );
2016-12-22 16:57:45 -08:00
return true ;
2016-08-31 14:14:58 -07:00
} else if ( t == "MATCH_TAGS_DIFFERENCE" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE ;
2017-02-28 09:22:10 -08:00
tag = true ;
2016-08-15 18:49:50 -07:00
} else if ( t == "MATCH_TAGS_BITWISE_AND" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND ;
2017-02-28 09:22:10 -08:00
tag = true ;
2016-08-15 18:49:50 -07:00
} else if ( t == "MATCH_TAGS_BITWISE_OR" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR ;
2017-02-28 09:22:10 -08:00
tag = true ;
2016-08-15 18:49:50 -07:00
} else if ( t == "MATCH_TAGS_BITWISE_XOR" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR ;
2017-02-28 09:22:10 -08:00
tag = true ;
2016-10-05 16:38:42 -07:00
} else if ( t == "MATCH_TAGS_EQUAL" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_TAGS_EQUAL ;
2017-02-28 09:22:10 -08:00
tag = true ;
} else if ( t == "MATCH_TAG_SENDER" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_TAG_SENDER ;
tag = true ;
} else if ( t == "MATCH_TAG_RECEIVER" ) {
rule . t |= ZT_NETWORK_RULE_MATCH_TAG_RECEIVER ;
tag = true ;
2017-10-24 13:33:53 -07:00
} else if ( t == "INTEGER_RANGE" ) {
json & s = r [ "start" ];
if ( s . is_string ()) {
std :: string tmp = s ;
rule . v . intRange . start = Utils :: hexStrToU64 ( tmp . c_str ());
} else {
rule . v . intRange . start = OSUtils :: jsonInt ( s , 0ULL );
}
json & e = r [ "end" ];
if ( e . is_string ()) {
std :: string tmp = e ;
rule . v . intRange . end = ( uint32_t )( Utils :: hexStrToU64 ( tmp . c_str ()) - rule . v . intRange . start );
} else {
rule . v . intRange . end = ( uint32_t )( OSUtils :: jsonInt ( e , 0ULL ) - rule . v . intRange . start );
}
rule . v . intRange . idx = ( uint16_t ) OSUtils :: jsonInt ( r [ "idx" ], 0ULL );
rule . v . intRange . format = ( OSUtils :: jsonBool ( r [ "little" ], false )) ? 0x80 : 0x00 ;
rule . v . intRange . format |= ( uint8_t )(( OSUtils :: jsonInt ( r [ "bits" ], 1ULL ) - 1 ) & 63 );
2017-02-28 09:22:10 -08:00
}
2017-10-24 13:33:53 -07:00
2017-02-28 09:22:10 -08:00
if ( tag ) {
2017-01-10 13:51:10 -08:00
rule . v . tag . id = ( uint32_t )( OSUtils :: jsonInt ( r [ "id" ], 0ULL ) & 0xffffffffULL );
rule . v . tag . value = ( uint32_t )( OSUtils :: jsonInt ( r [ "value" ], 0ULL ) & 0xffffffffULL );
2016-10-05 16:38:42 -07:00
return true ;
2016-08-15 18:49:50 -07:00
}
2016-10-11 12:00:16 -07:00
2016-08-15 18:49:50 -07:00
return false ;
}
2017-11-03 11:39:27 -07:00
} // anonymous namespace
2020-05-11 15:03:56 -07:00
EmbeddedNetworkController :: EmbeddedNetworkController ( Node * node , const char * ztPath , const char * dbPath , int listenPort , RedisConfig * rc ) :
2017-03-21 06:31:15 -07:00
_startTime ( OSUtils :: now ()),
2019-01-21 11:18:20 -08:00
_listenPort ( listenPort ),
2017-11-03 11:39:27 -07:00
_node ( node ),
2019-08-07 11:23:48 -05:00
_ztPath ( ztPath ),
2017-11-03 11:39:27 -07:00
_path ( dbPath ),
2019-03-05 15:11:50 -08:00
_sender (( NetworkController :: Sender * ) 0 ),
2020-05-11 15:03:56 -07:00
_db ( this ),
_rc ( rc )
2015-01-06 13:45:10 -08:00
{
}
2016-08-17 10:42:32 -07:00
EmbeddedNetworkController ::~ EmbeddedNetworkController ()
2015-01-06 13:45:10 -08:00
{
2017-11-03 11:39:27 -07:00
std :: lock_guard < std :: mutex > l ( _threads_l );
_queue . stop ();
for ( auto t = _threads . begin (); t != _threads . end (); ++ t )
t -> join ();
2015-01-06 13:45:10 -08:00
}
2016-11-10 11:54:47 -08:00
void EmbeddedNetworkController :: init ( const Identity & signingId , Sender * sender )
2015-01-06 13:45:10 -08:00
{
2017-07-14 14:33:36 -07:00
char tmp [ 64 ];
2017-11-03 11:39:27 -07:00
_signingId = signingId ;
_sender = sender ;
_signingIdAddressString = signingId . address (). toString ( tmp );
2019-07-22 13:43:06 -07:00
2018-08-31 14:58:15 -07:00
#ifdef ZT_CONTROLLER_USE_LIBPQ
2019-07-22 13:43:06 -07:00
if (( _path . length () > 9 ) && ( _path . substr ( 0 , 9 ) == "postgres:" )) {
2020-05-11 15:03:56 -07:00
_db . addDB ( std :: shared_ptr < DB > ( new PostgreSQL ( _signingId , _path . substr ( 9 ). c_str (), _listenPort , _rc )));
2019-07-22 13:43:06 -07:00
} else {
2017-11-09 17:01:16 -05:00
#endif
2019-08-06 11:00:35 -05:00
_db . addDB ( std :: shared_ptr < DB > ( new FileDB ( _path . c_str ())));
#ifdef ZT_CONTROLLER_USE_LIBPQ
}
#endif
2019-07-22 13:43:06 -07:00
std :: string lfJSON ;
2019-08-07 11:23:48 -05:00
OSUtils :: readFile (( _ztPath + ZT_PATH_SEPARATOR_S "local.conf" ). c_str (), lfJSON );
2019-07-22 13:43:06 -07:00
if ( lfJSON . length () > 0 ) {
nlohmann :: json lfConfig ( OSUtils :: jsonParse ( lfJSON ));
nlohmann :: json & settings = lfConfig [ "settings" ];
if ( settings . is_object ()) {
2019-07-23 09:29:08 -07:00
nlohmann :: json & controllerDb = settings [ "controllerDb" ];
2019-07-22 13:43:06 -07:00
if ( controllerDb . is_object ()) {
std :: string type = controllerDb [ "type" ];
if ( type == "lf" ) {
std :: string lfOwner = controllerDb [ "owner" ];
std :: string lfHost = controllerDb [ "host" ];
int lfPort = controllerDb [ "port" ];
bool storeOnlineState = controllerDb [ "storeOnlineState" ];
if (( lfOwner . length ()) && ( lfHost . length ()) && ( lfPort > 0 ) && ( lfPort < 65536 )) {
std :: size_t pubHdrLoc = lfOwner . find ( "Public: " );
if (( pubHdrLoc > 0 ) && (( pubHdrLoc + 8 ) < lfOwner . length ())) {
std :: string lfOwnerPublic = lfOwner . substr ( pubHdrLoc + 8 );
std :: size_t pubHdrEnd = lfOwnerPublic . find_first_of ( " \n\r\t " );
if ( pubHdrEnd != std :: string :: npos ) {
lfOwnerPublic = lfOwnerPublic . substr ( 0 , pubHdrEnd );
2019-08-06 11:00:35 -05:00
_db . addDB ( std :: shared_ptr < DB > ( new LFDB ( _signingId , _path . c_str (), lfOwner . c_str (), lfOwnerPublic . c_str (), lfHost . c_str (), lfPort , storeOnlineState )));
2019-07-22 13:43:06 -07:00
}
}
}
}
}
}
}
2019-07-26 17:39:00 -07:00
2019-08-06 11:00:35 -05:00
_db . waitForReady ();
2016-11-10 11:54:47 -08:00
}
void EmbeddedNetworkController :: request (
uint64_t nwid ,
const InetAddress & fromAddr ,
uint64_t requestPacketId ,
const Identity & identity ,
const Dictionary < ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY > & metaData )
{
if ((( ! _signingId ) || ( ! _signingId . hasPrivate ())) || ( _signingId . address (). toInt () != ( nwid >> 24 )) || ( ! _sender ))
return ;
2017-05-02 16:58:51 -07:00
_startThreads ();
2016-11-10 13:57:01 -08:00
_RQEntry * qe = new _RQEntry ;
qe -> nwid = nwid ;
qe -> requestPacketId = requestPacketId ;
qe -> fromAddr = fromAddr ;
qe -> identity = identity ;
qe -> metaData = metaData ;
2017-05-02 16:58:51 -07:00
qe -> type = _RQEntry :: RQENTRY_TYPE_REQUEST ;
2017-11-03 16:59:36 -04:00
_queue . post ( qe );
2015-01-06 17:16:54 -08:00
}
2016-08-17 10:42:32 -07:00
unsigned int EmbeddedNetworkController :: handleControlPlaneHttpGET (
2015-04-21 16:41:35 -07:00
const std :: vector < std :: string > & path ,
const std :: map < std :: string , std :: string > & urlArgs ,
const std :: map < std :: string , std :: string > & headers ,
const std :: string & body ,
std :: string & responseBody ,
std :: string & responseContentType )
{
2020-07-16 18:31:56 +02:00
if (( ! path . empty ()) && ( path [ 0 ] == "network" )) {
2016-08-16 16:46:08 -07:00
if (( path . size () >= 2 ) && ( path [ 1 ]. length () == 16 )) {
const uint64_t nwid = Utils :: hexStrToU64 ( path [ 1 ]. c_str ());
2017-04-26 06:48:08 -07:00
json network ;
2019-08-06 11:00:35 -05:00
if ( ! _db . get ( nwid , network ))
2016-08-16 16:46:08 -07:00
return 404 ;
if ( path . size () >= 3 ) {
if ( path [ 2 ] == "member" ) {
if ( path . size () >= 4 ) {
2017-05-08 08:35:55 -07:00
// Get member
2016-08-16 16:46:08 -07:00
const uint64_t address = Utils :: hexStrToU64 ( path [ 3 ]. c_str ());
2017-04-26 06:48:08 -07:00
json member ;
2019-08-06 11:00:35 -05:00
if ( ! _db . get ( nwid , network , address , member ))
2016-08-16 16:46:08 -07:00
return 404 ;
2017-01-10 13:51:10 -08:00
responseBody = OSUtils :: jsonDump ( member );
2016-08-16 16:46:08 -07:00
responseContentType = "application/json" ;
2017-05-08 08:35:55 -07:00
2016-08-16 16:46:08 -07:00
} else {
2017-05-08 08:35:55 -07:00
// List members and their revisions
2016-08-16 16:46:08 -07:00
responseBody = "{" ;
2017-11-03 11:39:27 -07:00
std :: vector < json > members ;
2019-08-06 11:00:35 -05:00
if ( _db . get ( nwid , network , members )) {
2017-11-03 11:39:27 -07:00
responseBody . reserve (( members . size () + 2 ) * 32 );
std :: string mid ;
for ( auto member = members . begin (); member != members . end (); ++ member ) {
2020-08-05 16:04:02 -07:00
mid = OSUtils :: jsonString (( * member )[ "id" ], "" );
2017-05-08 08:35:55 -07:00
char tmp [ 128 ];
2018-06-05 12:55:39 -07:00
OSUtils :: ztsnprintf ( tmp , sizeof ( tmp ), "%s \" %s \" :%llu" ,( responseBody . length () > 1 ) ? "," : "" , mid . c_str (),( unsigned long long ) OSUtils :: jsonInt (( * member )[ "revision" ], 0 ));
2017-05-08 08:35:55 -07:00
responseBody . append ( tmp );
2016-08-16 16:46:08 -07:00
}
2017-11-03 11:39:27 -07:00
}
2016-08-16 16:46:08 -07:00
responseBody . push_back ( '}' );
responseContentType = "application/json" ;
2017-05-08 08:35:55 -07:00
2016-08-16 16:46:08 -07:00
}
2017-04-24 19:16:36 -07:00
return 200 ;
2016-08-16 16:46:08 -07:00
} // else 404
} else {
2017-05-08 08:35:55 -07:00
// Get network
2016-08-16 16:46:08 -07:00
2017-01-10 13:51:10 -08:00
responseBody = OSUtils :: jsonDump ( network );
2016-08-16 16:46:08 -07:00
responseContentType = "application/json" ;
return 200 ;
}
} else if ( path . size () == 1 ) {
2017-05-08 08:35:55 -07:00
// List networks
2016-08-16 16:46:08 -07:00
2019-08-06 11:00:35 -05:00
std :: set < uint64_t > networkIds ;
_db . networks ( networkIds );
2017-04-26 06:48:08 -07:00
char tmp [ 64 ];
2017-05-08 08:35:55 -07:00
responseBody = "[" ;
responseBody . reserve (( networkIds . size () + 1 ) * 24 );
2019-08-06 11:00:35 -05:00
for ( std :: set < uint64_t >:: const_iterator i ( networkIds . begin ()); i != networkIds . end (); ++ i ) {
2017-04-26 06:48:08 -07:00
if ( responseBody . length () > 1 )
responseBody . push_back ( ',' );
2017-07-06 16:11:11 -07:00
OSUtils :: ztsnprintf ( tmp , sizeof ( tmp ), " \" %.16llx \" " ,( unsigned long long ) * i );
2017-04-26 06:48:08 -07:00
responseBody . append ( tmp );
2016-08-16 16:46:08 -07:00
}
responseBody . push_back ( ']' );
responseContentType = "application/json" ;
2017-04-26 06:48:08 -07:00
2016-08-16 16:46:08 -07:00
return 200 ;
} // else 404
} else {
2017-05-08 08:35:55 -07:00
// Controller status
2016-08-16 16:46:08 -07:00
char tmp [ 4096 ];
2019-08-06 11:00:35 -05:00
const bool dbOk = _db . isReady ();
2018-07-11 10:42:31 -07:00
OSUtils :: ztsnprintf ( tmp , sizeof ( tmp ), "{ \n\t\" controller \" : true, \n\t\" apiVersion \" : %d, \n\t\" clock \" : %llu, \n\t\" databaseReady \" : %s \n } \n " , ZT_NETCONF_CONTROLLER_API_VERSION ,( unsigned long long ) OSUtils :: now (), dbOk ? "true" : "false" );
2016-08-16 16:46:08 -07:00
responseBody = tmp ;
responseContentType = "application/json" ;
2018-07-11 10:42:31 -07:00
return dbOk ? 200 : 503 ;
2016-08-16 16:46:08 -07:00
}
return 404 ;
2015-04-21 16:41:35 -07:00
}
2016-08-17 10:42:32 -07:00
unsigned int EmbeddedNetworkController :: handleControlPlaneHttpPOST (
2015-04-21 16:41:35 -07:00
const std :: vector < std :: string > & path ,
const std :: map < std :: string , std :: string > & urlArgs ,
const std :: map < std :: string , std :: string > & headers ,
const std :: string & body ,
std :: string & responseBody ,
std :: string & responseContentType )
{
if ( path . empty ())
return 404 ;
2016-08-16 16:46:08 -07:00
json b ;
try {
2017-01-10 13:51:10 -08:00
b = OSUtils :: jsonParse ( body );
2016-08-25 16:08:40 -07:00
if ( ! b . is_object ()) {
responseBody = "{ \" message \" : \" body is not a JSON object \" }" ;
responseContentType = "application/json" ;
2016-08-16 16:46:08 -07:00
return 400 ;
2016-08-25 16:08:40 -07:00
}
2016-08-16 16:46:08 -07:00
} catch ( ... ) {
2016-08-25 16:08:40 -07:00
responseBody = "{ \" message \" : \" body JSON is invalid \" }" ;
responseContentType = "application/json" ;
2016-08-16 16:46:08 -07:00
return 400 ;
}
2017-10-02 15:52:57 -07:00
const int64_t now = OSUtils :: now ();
2016-08-16 16:46:08 -07:00
2015-04-21 16:50:02 -07:00
if ( path [ 0 ] == "network" ) {
if (( path . size () >= 2 ) && ( path [ 1 ]. length () == 16 )) {
uint64_t nwid = Utils :: hexStrToU64 ( path [ 1 ]. c_str ());
char nwids [ 24 ];
2017-07-06 16:11:11 -07:00
OSUtils :: ztsnprintf ( nwids , sizeof ( nwids ), "%.16llx" ,( unsigned long long ) nwid );
2015-04-21 16:50:02 -07:00
if ( path . size () >= 3 ) {
2015-04-24 12:29:31 -07:00
2015-04-21 16:50:02 -07:00
if (( path . size () == 4 ) && ( path [ 2 ] == "member" ) && ( path [ 3 ]. length () == 10 )) {
uint64_t address = Utils :: hexStrToU64 ( path [ 3 ]. c_str ());
2016-08-16 16:46:08 -07:00
char addrs [ 24 ];
2017-07-06 16:11:11 -07:00
OSUtils :: ztsnprintf ( addrs , sizeof ( addrs ), "%.10llx" ,( unsigned long long ) address );
2015-04-21 16:50:02 -07:00
2017-11-03 11:39:27 -07:00
json member , network ;
2019-08-06 11:00:35 -05:00
_db . get ( nwid , network , address , member );
2018-01-09 12:39:25 -08:00
DB :: initMember ( member );
2015-07-23 17:18:20 -07:00
2016-08-16 16:46:08 -07:00
try {
2021-05-25 14:40:40 -04:00
if ( b . count ( "activeBridge" )) member [ "activeBridge" ] = OSUtils :: jsonBool ( b [ "activeBridge" ], false );
if ( b . count ( "noAutoAssignIps" )) member [ "noAutoAssignIps" ] = OSUtils :: jsonBool ( b [ "noAutoAssignIps" ], false );
if ( b . count ( "authenticationExpiryTime" )) member [ "authenticationExpiryTime" ] = ( int64_t ) OSUtils :: jsonInt ( b [ "authenticationExpiryTime" ], - 1LL );
if ( b . count ( "authenticationURL" )) member [ "authenticationURL" ] = OSUtils :: jsonString ( b [ "authenticationURL" ], "" );
2016-08-17 10:25:25 -07:00
2017-07-14 13:03:16 -07:00
if ( b . count ( "remoteTraceTarget" )) {
const std :: string rtt ( OSUtils :: jsonString ( b [ "remoteTraceTarget" ], "" ));
if ( rtt . length () == 10 ) {
member [ "remoteTraceTarget" ] = rtt ;
} else {
member [ "remoteTraceTarget" ] = json ();
}
}
2017-12-05 17:49:20 -05:00
if ( b . count ( "remoteTraceLevel" )) member [ "remoteTraceLevel" ] = OSUtils :: jsonInt ( b [ "remoteTraceLevel" ], 0ULL );
2017-07-14 13:03:16 -07:00
2016-08-23 13:02:59 -07:00
if ( b . count ( "authorized" )) {
2017-01-10 13:51:10 -08:00
const bool newAuth = OSUtils :: jsonBool ( b [ "authorized" ], false );
if ( newAuth != OSUtils :: jsonBool ( member [ "authorized" ], false )) {
2016-09-15 13:17:37 -07:00
member [ "authorized" ] = newAuth ;
2016-11-15 14:06:25 -08:00
member [(( newAuth ) ? "lastAuthorizedTime" : "lastDeauthorizedTime" )] = now ;
2017-08-09 14:37:19 -07:00
if ( newAuth ) {
member [ "lastAuthorizedCredentialType" ] = "api" ;
member [ "lastAuthorizedCredential" ] = json ();
}
2016-08-23 13:02:59 -07:00
}
}
2016-08-16 16:46:08 -07:00
if ( b . count ( "ipAssignments" )) {
2016-11-04 15:18:31 -07:00
json & ipa = b [ "ipAssignments" ];
2016-08-16 16:46:08 -07:00
if ( ipa . is_array ()) {
json mipa ( json :: array ());
for ( unsigned long i = 0 ; i < ipa . size (); ++ i ) {
std :: string ips = ipa [ i ];
2017-07-06 16:11:11 -07:00
InetAddress ip ( ips . c_str ());
2016-08-16 16:46:08 -07:00
if (( ip . ss_family == AF_INET ) || ( ip . ss_family == AF_INET6 )) {
2017-07-06 16:11:11 -07:00
char tmpip [ 64 ];
mipa . push_back ( ip . toIpString ( tmpip ));
2018-01-12 10:38:19 -08:00
if ( mipa . size () >= ZT_CONTROLLER_MAX_ARRAY_SIZE )
break ;
2015-09-08 13:02:42 -07:00
}
2015-05-16 12:50:42 -07:00
}
2016-08-16 16:46:08 -07:00
member [ "ipAssignments" ] = mipa ;
2015-05-16 12:50:42 -07:00
}
}
2016-08-18 14:37:56 -07:00
if ( b . count ( "tags" )) {
2016-11-04 15:18:31 -07:00
json & tags = b [ "tags" ];
2016-08-18 14:37:56 -07:00
if ( tags . is_array ()) {
std :: map < uint64_t , uint64_t > mtags ;
for ( unsigned long i = 0 ; i < tags . size (); ++ i ) {
2016-11-04 16:23:41 -07:00
json & tag = tags [ i ];
2016-08-18 14:37:56 -07:00
if (( tag . is_array ()) && ( tag . size () == 2 ))
2017-01-10 13:51:10 -08:00
mtags [ OSUtils :: jsonInt ( tag [ 0 ], 0ULL ) & 0xffffffffULL ] = OSUtils :: jsonInt ( tag [ 1 ], 0ULL ) & 0xffffffffULL ;
2016-08-18 14:37:56 -07:00
}
json mtagsa = json :: array ();
for ( std :: map < uint64_t , uint64_t >:: iterator t ( mtags . begin ()); t != mtags . end (); ++ t ) {
json ta = json :: array ();
ta . push_back ( t -> first );
ta . push_back ( t -> second );
mtagsa . push_back ( ta );
2018-01-12 10:38:19 -08:00
if ( mtagsa . size () >= ZT_CONTROLLER_MAX_ARRAY_SIZE )
break ;
2016-08-18 14:37:56 -07:00
}
member [ "tags" ] = mtagsa ;
}
}
if ( b . count ( "capabilities" )) {
2016-11-04 15:18:31 -07:00
json & capabilities = b [ "capabilities" ];
2016-08-18 14:37:56 -07:00
if ( capabilities . is_array ()) {
json mcaps = json :: array ();
for ( unsigned long i = 0 ; i < capabilities . size (); ++ i ) {
2017-01-10 13:51:10 -08:00
mcaps . push_back ( OSUtils :: jsonInt ( capabilities [ i ], 0ULL ));
2018-01-12 10:38:19 -08:00
if ( mcaps . size () >= ZT_CONTROLLER_MAX_ARRAY_SIZE )
break ;
2016-08-18 14:37:56 -07:00
}
std :: sort ( mcaps . begin (), mcaps . end ());
mcaps . erase ( std :: unique ( mcaps . begin (), mcaps . end ()), mcaps . end ());
member [ "capabilities" ] = mcaps ;
}
}
2016-08-16 16:46:08 -07:00
} catch ( ... ) {
2016-08-25 16:08:40 -07:00
responseBody = "{ \" message \" : \" exception while processing parameters in JSON body \" }" ;
responseContentType = "application/json" ;
2016-08-16 16:46:08 -07:00
return 400 ;
2015-05-16 12:50:42 -07:00
}
2016-08-16 16:46:08 -07:00
member [ "id" ] = addrs ;
member [ "address" ] = addrs ; // legacy
member [ "nwid" ] = nwids ;
2018-01-09 12:39:25 -08:00
DB :: cleanMember ( member );
2019-08-06 11:00:35 -05:00
_db . save ( member , true );
2017-01-10 13:51:10 -08:00
responseBody = OSUtils :: jsonDump ( member );
2016-08-16 16:46:08 -07:00
responseContentType = "application/json" ;
2017-04-28 19:58:21 -07:00
2016-08-16 16:46:08 -07:00
return 200 ;
2015-04-21 16:50:02 -07:00
} // else 404
2015-04-21 18:08:33 -07:00
2015-04-21 16:50:02 -07:00
} else {
2016-08-15 18:49:50 -07:00
// POST to network ID
2015-04-21 16:50:02 -07:00
2017-04-24 19:16:36 -07:00
// Magic ID ending with ______ picks a random unused network ID
if ( path [ 1 ]. substr ( 10 ) == "______" ) {
nwid = 0 ;
uint64_t nwidPrefix = ( Utils :: hexStrToU64 ( path [ 1 ]. substr ( 0 , 10 ). c_str ()) << 24 ) & 0xffffffffff000000ULL ;
uint64_t nwidPostfix = 0 ;
for ( unsigned long k = 0 ; k < 100000 ; ++ k ) { // sanity limit on trials
Utils :: getSecureRandom ( & nwidPostfix , sizeof ( nwidPostfix ));
uint64_t tryNwid = nwidPrefix | ( nwidPostfix & 0xffffffULL );
if (( tryNwid & 0xffffffULL ) == 0ULL ) tryNwid |= 1ULL ;
2019-08-06 11:00:35 -05:00
if ( ! _db . hasNetwork ( tryNwid )) {
2017-04-24 19:16:36 -07:00
nwid = tryNwid ;
break ;
2016-11-04 15:18:31 -07:00
}
}
2017-04-24 19:16:36 -07:00
if ( ! nwid )
return 503 ;
2016-11-04 15:18:31 -07:00
}
2017-07-06 16:11:11 -07:00
OSUtils :: ztsnprintf ( nwids , sizeof ( nwids ), "%.16llx" ,( unsigned long long ) nwid );
2017-04-24 19:16:36 -07:00
2017-04-26 06:48:08 -07:00
json network ;
2019-08-06 11:00:35 -05:00
_db . get ( nwid , network );
2018-01-09 12:39:25 -08:00
DB :: initNetwork ( network );
2016-08-15 18:49:50 -07:00
try {
2017-01-10 13:51:10 -08:00
if ( b . count ( "name" )) network [ "name" ] = OSUtils :: jsonString ( b [ "name" ], "" );
if ( b . count ( "private" )) network [ "private" ] = OSUtils :: jsonBool ( b [ "private" ], true );
if ( b . count ( "enableBroadcast" )) network [ "enableBroadcast" ] = OSUtils :: jsonBool ( b [ "enableBroadcast" ], false );
if ( b . count ( "multicastLimit" )) network [ "multicastLimit" ] = OSUtils :: jsonInt ( b [ "multicastLimit" ], 32ULL );
2017-05-04 17:22:24 -07:00
if ( b . count ( "mtu" )) network [ "mtu" ] = std :: max ( std :: min (( unsigned int ) OSUtils :: jsonInt ( b [ "mtu" ], ZT_DEFAULT_MTU ),( unsigned int ) ZT_MAX_MTU ),( unsigned int ) ZT_MIN_MTU );
2016-08-15 18:49:50 -07:00
2017-07-14 13:03:16 -07:00
if ( b . count ( "remoteTraceTarget" )) {
const std :: string rtt ( OSUtils :: jsonString ( b [ "remoteTraceTarget" ], "" ));
if ( rtt . length () == 10 ) {
network [ "remoteTraceTarget" ] = rtt ;
} else {
network [ "remoteTraceTarget" ] = json ();
}
}
2017-12-05 17:49:20 -05:00
if ( b . count ( "remoteTraceLevel" )) network [ "remoteTraceLevel" ] = OSUtils :: jsonInt ( b [ "remoteTraceLevel" ], 0ULL );
2017-07-14 13:03:16 -07:00
2016-08-15 18:49:50 -07:00
if ( b . count ( "v4AssignMode" )) {
2016-09-30 13:04:26 -07:00
json nv4m ;
json & v4m = b [ "v4AssignMode" ];
if ( v4m . is_string ()) { // backward compatibility
2017-01-10 13:51:10 -08:00
nv4m [ "zt" ] = ( OSUtils :: jsonString ( v4m , "" ) == "zt" );
2016-09-30 13:04:26 -07:00
} else if ( v4m . is_object ()) {
2017-01-10 13:51:10 -08:00
nv4m [ "zt" ] = OSUtils :: jsonBool ( v4m [ "zt" ], false );
2016-09-30 13:04:26 -07:00
} else nv4m [ "zt" ] = false ;
network [ "v4AssignMode" ] = nv4m ;
2016-08-15 18:49:50 -07:00
}
if ( b . count ( "v6AssignMode" )) {
2016-09-30 13:04:26 -07:00
json nv6m ;
json & v6m = b [ "v6AssignMode" ];
2016-08-15 18:49:50 -07:00
if ( ! nv6m . is_object ()) nv6m = json :: object ();
2016-09-30 13:04:26 -07:00
if ( v6m . is_string ()) { // backward compatibility
2017-01-10 13:51:10 -08:00
std :: vector < std :: string > v6ms ( OSUtils :: split ( OSUtils :: jsonString ( v6m , "" ). c_str (), "," , "" , "" ));
2016-09-30 13:04:26 -07:00
std :: sort ( v6ms . begin (), v6ms . end ());
v6ms . erase ( std :: unique ( v6ms . begin (), v6ms . end ()), v6ms . end ());
2016-08-26 14:16:55 -07:00
nv6m [ "rfc4193" ] = false ;
nv6m [ "zt" ] = false ;
nv6m [ "6plane" ] = false ;
2016-09-30 13:04:26 -07:00
for ( std :: vector < std :: string >:: iterator i ( v6ms . begin ()); i != v6ms . end (); ++ i ) {
2016-08-15 18:49:50 -07:00
if ( * i == "rfc4193" )
nv6m [ "rfc4193" ] = true ;
else if ( * i == "zt" )
nv6m [ "zt" ] = true ;
else if ( * i == "6plane" )
nv6m [ "6plane" ] = true ;
}
2016-09-30 13:04:26 -07:00
} else if ( v6m . is_object ()) {
2017-01-10 13:51:10 -08:00
if ( v6m . count ( "rfc4193" )) nv6m [ "rfc4193" ] = OSUtils :: jsonBool ( v6m [ "rfc4193" ], false );
if ( v6m . count ( "zt" )) nv6m [ "zt" ] = OSUtils :: jsonBool ( v6m [ "zt" ], false );
if ( v6m . count ( "6plane" )) nv6m [ "6plane" ] = OSUtils :: jsonBool ( v6m [ "6plane" ], false );
2016-09-30 13:04:26 -07:00
} else {
nv6m [ "rfc4193" ] = false ;
nv6m [ "zt" ] = false ;
nv6m [ "6plane" ] = false ;
2016-08-15 18:49:50 -07:00
}
2016-09-30 13:04:26 -07:00
network [ "v6AssignMode" ] = nv6m ;
2016-08-15 18:49:50 -07:00
}
if ( b . count ( "routes" )) {
2016-08-25 16:08:40 -07:00
json & rts = b [ "routes" ];
2016-08-15 18:49:50 -07:00
if ( rts . is_array ()) {
2016-08-25 16:08:40 -07:00
json nrts = json :: array ();
2016-08-15 18:49:50 -07:00
for ( unsigned long i = 0 ; i < rts . size (); ++ i ) {
2016-08-25 16:08:40 -07:00
json & rt = rts [ i ];
if ( rt . is_object ()) {
json & target = rt [ "target" ];
json & via = rt [ "via" ];
if ( target . is_string ()) {
2017-07-06 16:11:11 -07:00
InetAddress t ( target . get < std :: string > (). c_str ());
2016-08-25 16:08:40 -07:00
InetAddress v ;
2017-07-06 16:11:11 -07:00
if ( via . is_string ()) v . fromString ( via . get < std :: string > (). c_str ());
2016-08-25 16:08:40 -07:00
if ( (( t . ss_family == AF_INET ) || ( t . ss_family == AF_INET6 )) && ( t . netmaskBitsValid ()) ) {
json tmp ;
2017-07-06 16:11:11 -07:00
char tmp2 [ 64 ];
tmp [ "target" ] = t . toString ( tmp2 );
2016-08-25 16:08:40 -07:00
if ( v . ss_family == t . ss_family )
2017-07-06 16:11:11 -07:00
tmp [ "via" ] = v . toIpString ( tmp2 );
2016-08-25 16:08:40 -07:00
else tmp [ "via" ] = json ();
nrts . push_back ( tmp );
2018-01-12 10:38:19 -08:00
if ( nrts . size () >= ZT_CONTROLLER_MAX_ARRAY_SIZE )
break ;
2016-08-25 16:08:40 -07:00
}
2016-08-15 18:49:50 -07:00
}
}
}
2016-08-25 16:08:40 -07:00
network [ "routes" ] = nrts ;
2016-08-15 18:49:50 -07:00
}
}
if ( b . count ( "ipAssignmentPools" )) {
2016-08-25 16:08:40 -07:00
json & ipp = b [ "ipAssignmentPools" ];
2016-08-15 18:49:50 -07:00
if ( ipp . is_array ()) {
2016-08-25 16:08:40 -07:00
json nipp = json :: array ();
2016-08-15 18:49:50 -07:00
for ( unsigned long i = 0 ; i < ipp . size (); ++ i ) {
2016-11-04 16:14:58 -07:00
json & ip = ipp [ i ];
2016-08-15 18:49:50 -07:00
if (( ip . is_object ()) && ( ip . count ( "ipRangeStart" )) && ( ip . count ( "ipRangeEnd" ))) {
2017-07-06 16:11:11 -07:00
InetAddress f ( OSUtils :: jsonString ( ip [ "ipRangeStart" ], "" ). c_str ());
InetAddress t ( OSUtils :: jsonString ( ip [ "ipRangeEnd" ], "" ). c_str ());
2016-08-15 18:49:50 -07:00
if ( (( f . ss_family == AF_INET ) || ( f . ss_family == AF_INET6 )) && ( f . ss_family == t . ss_family ) ) {
2016-08-25 16:08:40 -07:00
json tmp = json :: object ();
2017-07-06 16:11:11 -07:00
char tmp2 [ 64 ];
tmp [ "ipRangeStart" ] = f . toIpString ( tmp2 );
tmp [ "ipRangeEnd" ] = t . toIpString ( tmp2 );
2016-08-15 18:49:50 -07:00
nipp . push_back ( tmp );
2018-01-12 10:38:19 -08:00
if ( nipp . size () >= ZT_CONTROLLER_MAX_ARRAY_SIZE )
break ;
2016-08-15 18:49:50 -07:00
}
}
}
2016-08-25 16:08:40 -07:00
network [ "ipAssignmentPools" ] = nipp ;
2016-08-15 18:49:50 -07:00
}
}
if ( b . count ( "rules" )) {
2016-08-25 16:08:40 -07:00
json & rules = b [ "rules" ];
2016-08-15 18:49:50 -07:00
if ( rules . is_array ()) {
2016-08-17 10:25:25 -07:00
json nrules = json :: array ();
2016-08-15 18:49:50 -07:00
for ( unsigned long i = 0 ; i < rules . size (); ++ i ) {
2016-08-25 16:08:40 -07:00
json & rule = rules [ i ];
2016-08-15 18:49:50 -07:00
if ( rule . is_object ()) {
2016-08-17 10:25:25 -07:00
ZT_VirtualNetworkRule ztr ;
2018-01-12 10:38:19 -08:00
if ( _parseRule ( rule , ztr )) {
2016-08-25 16:08:40 -07:00
nrules . push_back ( _renderRule ( ztr ));
2018-01-12 10:38:19 -08:00
if ( nrules . size () >= ZT_CONTROLLER_MAX_ARRAY_SIZE )
break ;
}
2016-08-15 18:49:50 -07:00
}
}
2016-08-17 10:25:25 -07:00
network [ "rules" ] = nrules ;
2016-08-15 18:49:50 -07:00
}
}
2016-08-17 13:41:45 -07:00
if ( b . count ( "authTokens" )) {
2016-08-25 16:08:40 -07:00
json & authTokens = b [ "authTokens" ];
2017-08-09 14:37:19 -07:00
if ( authTokens . is_object ()) {
json nat ;
for ( json :: iterator t ( authTokens . begin ()); t != authTokens . end (); ++ t ) {
if (( t . value (). is_number ()) && ( t . value () >= 0 ))
nat [ t . key ()] = t . value ();
2016-08-17 13:41:45 -07:00
}
network [ "authTokens" ] = nat ;
2017-08-09 14:37:19 -07:00
} else {
network [ "authTokens" ] = {{}};
2016-08-17 13:41:45 -07:00
}
}
2016-08-18 14:37:56 -07:00
if ( b . count ( "capabilities" )) {
2016-08-25 16:08:40 -07:00
json & capabilities = b [ "capabilities" ];
2016-08-18 14:37:56 -07:00
if ( capabilities . is_array ()) {
std :: map < uint64_t , json > ncaps ;
for ( unsigned long i = 0 ; i < capabilities . size (); ++ i ) {
2016-08-25 16:08:40 -07:00
json & cap = capabilities [ i ];
2016-08-18 14:37:56 -07:00
if ( cap . is_object ()) {
json ncap = json :: object ();
2017-01-10 13:51:10 -08:00
const uint64_t capId = OSUtils :: jsonInt ( cap [ "id" ], 0ULL );
2016-08-18 14:37:56 -07:00
ncap [ "id" ] = capId ;
2017-02-21 13:27:20 -08:00
ncap [ "default" ] = OSUtils :: jsonBool ( cap [ "default" ], false );
2016-08-18 14:37:56 -07:00
2016-08-25 16:08:40 -07:00
json & rules = cap [ "rules" ];
2016-08-18 14:37:56 -07:00
json nrules = json :: array ();
if ( rules . is_array ()) {
for ( unsigned long i = 0 ; i < rules . size (); ++ i ) {
2016-11-04 16:14:58 -07:00
json & rule = rules [ i ];
2016-08-18 14:37:56 -07:00
if ( rule . is_object ()) {
ZT_VirtualNetworkRule ztr ;
2018-01-12 10:38:19 -08:00
if ( _parseRule ( rule , ztr )) {
2016-08-25 16:08:40 -07:00
nrules . push_back ( _renderRule ( ztr ));
2018-01-12 10:38:19 -08:00
if ( nrules . size () >= ZT_CONTROLLER_MAX_ARRAY_SIZE )
break ;
}
2016-08-18 14:37:56 -07:00
}
}
}
ncap [ "rules" ] = nrules ;
ncaps [ capId ] = ncap ;
}
}
json ncapsa = json :: array ();
2018-01-12 10:38:19 -08:00
for ( std :: map < uint64_t , json >:: iterator c ( ncaps . begin ()); c != ncaps . end (); ++ c ) {
2016-08-18 14:37:56 -07:00
ncapsa . push_back ( c -> second );
2018-01-12 10:38:19 -08:00
if ( ncapsa . size () >= ZT_CONTROLLER_MAX_ARRAY_SIZE )
break ;
}
2016-08-18 14:37:56 -07:00
network [ "capabilities" ] = ncapsa ;
}
}
2017-02-21 13:27:20 -08:00
if ( b . count ( "tags" )) {
json & tags = b [ "tags" ];
if ( tags . is_array ()) {
std :: map < uint64_t , json > ntags ;
for ( unsigned long i = 0 ; i < tags . size (); ++ i ) {
json & tag = tags [ i ];
if ( tag . is_object ()) {
json ntag = json :: object ();
const uint64_t tagId = OSUtils :: jsonInt ( tag [ "id" ], 0ULL );
ntag [ "id" ] = tagId ;
2017-03-29 12:32:59 -07:00
json & dfl = tag [ "default" ];
if ( dfl . is_null ())
ntag [ "default" ] = dfl ;
else ntag [ "default" ] = OSUtils :: jsonInt ( dfl , 0ULL );
2017-02-21 13:27:20 -08:00
ntags [ tagId ] = ntag ;
}
}
json ntagsa = json :: array ();
2018-01-12 10:38:19 -08:00
for ( std :: map < uint64_t , json >:: iterator t ( ntags . begin ()); t != ntags . end (); ++ t ) {
2017-02-21 13:27:20 -08:00
ntagsa . push_back ( t -> second );
2018-01-12 10:38:19 -08:00
if ( ntagsa . size () >= ZT_CONTROLLER_MAX_ARRAY_SIZE )
break ;
}
2017-02-21 13:27:20 -08:00
network [ "tags" ] = ntagsa ;
}
}
2020-07-20 14:34:19 -07:00
if ( b . count ( "dns" )) {
json & dns = b [ "dns" ];
2020-09-23 12:16:10 -07:00
if ( dns . is_object ()) {
json nd ;
nd [ "domain" ] = dns [ "domain" ];
json & srv = dns [ "servers" ];
if ( srv . is_array ()) {
json ns = json :: array ();
for ( unsigned int i = 0 ; i < srv . size (); ++ i ) {
ns . push_back ( srv [ i ]);
2020-07-20 14:34:19 -07:00
}
2020-09-23 12:16:10 -07:00
nd [ "servers" ] = ns ;
2020-07-20 14:34:19 -07:00
}
2020-09-23 12:16:10 -07:00
network [ "dns" ] = nd ;
2020-07-20 14:34:19 -07:00
}
}
2016-08-15 18:49:50 -07:00
} catch ( ... ) {
2016-08-25 16:08:40 -07:00
responseBody = "{ \" message \" : \" exception occurred while parsing body variables \" }" ;
responseContentType = "application/json" ;
2016-08-16 16:46:08 -07:00
return 400 ;
2016-08-15 18:49:50 -07:00
}
2016-08-16 16:46:08 -07:00
network [ "id" ] = nwids ;
network [ "nwid" ] = nwids ; // legacy
2016-08-15 18:49:50 -07:00
2018-01-09 12:39:25 -08:00
DB :: cleanNetwork ( network );
2019-08-06 11:00:35 -05:00
_db . save ( network , true );
2016-08-18 12:59:48 -07:00
2017-01-10 13:51:10 -08:00
responseBody = OSUtils :: jsonDump ( network );
2016-08-16 14:05:17 -07:00
responseContentType = "application/json" ;
return 200 ;
} // else 404
2015-04-21 16:50:02 -07:00
} // else 404
2017-03-21 06:31:15 -07:00
}
2015-04-21 16:50:02 -07:00
2015-04-21 16:41:35 -07:00
return 404 ;
}
2016-08-17 10:42:32 -07:00
unsigned int EmbeddedNetworkController :: handleControlPlaneHttpDELETE (
2015-04-21 16:41:35 -07:00
const std :: vector < std :: string > & path ,
const std :: map < std :: string , std :: string > & urlArgs ,
const std :: map < std :: string , std :: string > & headers ,
const std :: string & body ,
std :: string & responseBody ,
std :: string & responseContentType )
{
if ( path . empty ())
return 404 ;
2015-05-16 13:42:53 -07:00
if ( path [ 0 ] == "network" ) {
if (( path . size () >= 2 ) && ( path [ 1 ]. length () == 16 )) {
2016-08-12 15:32:45 -07:00
const uint64_t nwid = Utils :: hexStrToU64 ( path [ 1 ]. c_str ());
2015-05-16 13:42:53 -07:00
if ( path . size () >= 3 ) {
if (( path . size () == 4 ) && ( path [ 2 ] == "member" ) && ( path [ 3 ]. length () == 10 )) {
2016-08-12 15:32:45 -07:00
const uint64_t address = Utils :: hexStrToU64 ( path [ 3 ]. c_str ());
2015-05-16 13:42:53 -07:00
2017-11-03 11:39:27 -07:00
json network , member ;
2019-08-06 11:00:35 -05:00
_db . get ( nwid , network , address , member );
_db . eraseMember ( nwid , address );
2017-05-01 13:21:26 -07:00
{
2017-11-03 11:39:27 -07:00
std :: lock_guard < std :: mutex > l ( _memberStatus_l );
2017-05-01 13:21:26 -07:00
_memberStatus . erase ( _MemberStatusKey ( nwid , address ));
}
2016-11-08 00:05:18 +00:00
if ( ! member . size ())
return 404 ;
2017-01-10 13:51:10 -08:00
responseBody = OSUtils :: jsonDump ( member );
2016-08-12 15:32:45 -07:00
responseContentType = "application/json" ;
2015-05-16 13:42:53 -07:00
return 200 ;
}
} else {
2017-11-03 11:39:27 -07:00
json network ;
2019-08-06 11:00:35 -05:00
_db . get ( nwid , network );
_db . eraseNetwork ( nwid );
2017-05-01 13:21:26 -07:00
{
2017-11-03 11:39:27 -07:00
std :: lock_guard < std :: mutex > l ( _memberStatus_l );
2017-05-01 13:21:26 -07:00
for ( auto i = _memberStatus . begin (); i != _memberStatus . end ();) {
if ( i -> first . networkId == nwid )
_memberStatus . erase ( i ++ );
else ++ i ;
}
}
2017-04-26 06:48:08 -07:00
if ( ! network . size ())
return 404 ;
2017-01-10 13:51:10 -08:00
responseBody = OSUtils :: jsonDump ( network );
2016-08-12 15:32:45 -07:00
responseContentType = "application/json" ;
return 200 ;
2015-05-16 13:42:53 -07:00
}
} // else 404
} // else 404
2015-04-21 16:41:35 -07:00
return 404 ;
}
2017-07-14 13:03:16 -07:00
void EmbeddedNetworkController :: handleRemoteTrace ( const ZT_RemoteTrace & rt )
{
2017-07-18 14:28:38 -07:00
static volatile unsigned long idCounter = 0 ;
2017-07-19 15:16:15 -07:00
char id [ 128 ], tmp [ 128 ];
2017-07-18 15:36:33 -07:00
std :: string k , v ;
2017-07-14 14:33:36 -07:00
try {
// Convert Dictionary into JSON object
json d ;
char * saveptr = ( char * ) 0 ;
for ( char * l = Utils :: stok ( rt . data , " \n " , & saveptr );( l ); l = Utils :: stok (( char * ) 0 , " \n " , & saveptr )) {
char * eq = strchr ( l , '=' );
if ( eq > l ) {
2017-07-18 15:36:33 -07:00
k . assign ( l ,( unsigned long )( eq - l ));
v . clear ();
2017-07-14 14:33:36 -07:00
++ eq ;
while ( * eq ) {
if ( * eq == '\\' ) {
++ eq ;
if ( * eq ) {
switch ( * eq ) {
2017-07-18 15:36:33 -07:00
case 'r' : v . push_back ( '\r' ); break ;
case 'n' : v . push_back ( '\n' ); break ;
case '0' : v . push_back (( char ) 0 ); break ;
case 'e' : v . push_back ( '=' ); break ;
default : v . push_back ( * eq ); break ;
2017-07-14 14:33:36 -07:00
}
++ eq ;
}
} else {
v . push_back ( * ( eq ++ ));
}
}
2017-07-18 15:36:33 -07:00
if (( k . length () > 0 ) && ( v . length () > 0 ))
2017-07-14 14:33:36 -07:00
d [ k ] = v ;
}
}
2017-10-02 15:52:57 -07:00
const int64_t now = OSUtils :: now ();
2017-12-05 14:50:59 -08:00
OSUtils :: ztsnprintf ( id , sizeof ( id ), "%.10llx-%.16llx-%.10llx-%.4x" , _signingId . address (). toInt (), now , rt . origin ,( unsigned int )( idCounter ++ & 0xffff ));
2017-07-18 14:28:38 -07:00
d [ "id" ] = id ;
d [ "objtype" ] = "trace" ;
2017-07-19 15:16:15 -07:00
d [ "ts" ] = now ;
d [ "nodeId" ] = Utils :: hex10 ( rt . origin , tmp );
2019-08-06 11:00:35 -05:00
_db . save ( d , true );
2017-07-14 14:33:36 -07:00
} catch ( ... ) {
// drop invalid trace messages if an error occurs
}
2017-07-14 13:03:16 -07:00
}
2019-08-06 10:42:54 -05:00
void EmbeddedNetworkController :: onNetworkUpdate ( const void * db , uint64_t networkId , const nlohmann :: json & network )
2017-08-16 14:14:49 -07:00
{
// Send an update to all members of the network that are online
2017-10-02 15:52:57 -07:00
const int64_t now = OSUtils :: now ();
2017-11-03 11:39:27 -07:00
std :: lock_guard < std :: mutex > l ( _memberStatus_l );
2017-08-16 14:14:49 -07:00
for ( auto i = _memberStatus . begin (); i != _memberStatus . end (); ++ i ) {
if (( i -> first . networkId == networkId ) && ( i -> second . online ( now )) && ( i -> second . lastRequestMetaData ))
request ( networkId , InetAddress (), 0 , i -> second . identity , i -> second . lastRequestMetaData );
}
}
2019-08-06 10:42:54 -05:00
void EmbeddedNetworkController :: onNetworkMemberUpdate ( const void * db , uint64_t networkId , uint64_t memberId , const nlohmann :: json & member )
2017-08-16 14:14:49 -07:00
{
// Push update to member if online
try {
2017-11-03 11:39:27 -07:00
std :: lock_guard < std :: mutex > l ( _memberStatus_l );
2017-08-16 14:14:49 -07:00
_MemberStatus & ms = _memberStatus [ _MemberStatusKey ( networkId , memberId )];
if (( ms . online ( OSUtils :: now ())) && ( ms . lastRequestMetaData ))
request ( networkId , InetAddress (), 0 , ms . identity , ms . lastRequestMetaData );
} catch ( ... ) {}
}
2019-08-06 10:42:54 -05:00
void EmbeddedNetworkController :: onNetworkMemberDeauthorize ( const void * db , uint64_t networkId , uint64_t memberId )
2017-08-17 13:10:10 -07:00
{
2017-10-02 15:52:57 -07:00
const int64_t now = OSUtils :: now ();
2017-08-17 13:10:10 -07:00
Revocation rev (( uint32_t ) _node -> prng (), networkId , 0 , now , ZT_REVOCATION_FLAG_FAST_PROPAGATE , Address ( memberId ), Revocation :: CREDENTIAL_TYPE_COM );
rev . sign ( _signingId );
{
2017-11-03 11:39:27 -07:00
std :: lock_guard < std :: mutex > l ( _memberStatus_l );
2017-08-17 13:10:10 -07:00
for ( auto i = _memberStatus . begin (); i != _memberStatus . end (); ++ i ) {
if (( i -> first . networkId == networkId ) && ( i -> second . online ( now )))
_node -> ncSendRevocation ( Address ( i -> first . nodeId ), rev );
}
}
}
2016-11-10 13:57:01 -08:00
void EmbeddedNetworkController :: _request (
uint64_t nwid ,
const InetAddress & fromAddr ,
uint64_t requestPacketId ,
const Identity & identity ,
const Dictionary < ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY > & metaData )
{
2017-04-27 19:36:03 -07:00
char nwids [ 24 ];
2017-11-09 17:01:16 -05:00
DB :: NetworkSummaryInfo ns ;
2019-08-06 08:51:23 -05:00
json network , member ;
2017-04-27 19:36:03 -07:00
2016-11-10 13:57:01 -08:00
if ((( ! _signingId ) || ( ! _signingId . hasPrivate ())) || ( _signingId . address (). toInt () != ( nwid >> 24 )) || ( ! _sender ))
return ;
2017-10-02 15:52:57 -07:00
const int64_t now = OSUtils :: now ();
2016-11-10 13:57:01 -08:00
if ( requestPacketId ) {
2017-11-03 11:39:27 -07:00
std :: lock_guard < std :: mutex > l ( _memberStatus_l );
2017-05-01 13:21:26 -07:00
_MemberStatus & ms = _memberStatus [ _MemberStatusKey ( nwid , identity . address (). toInt ())];
if (( now - ms . lastRequestTime ) <= ZT_NETCONF_MIN_REQUEST_PERIOD )
2016-11-10 13:57:01 -08:00
return ;
2017-05-01 13:21:26 -07:00
ms . lastRequestTime = now ;
2016-11-10 13:57:01 -08:00
}
2019-08-06 11:00:35 -05:00
_db . nodeIsOnline ( nwid , identity . address (). toInt (), fromAddr );
2017-11-08 11:06:14 -08:00
2017-11-03 22:40:26 -04:00
Utils :: hex ( nwid , nwids );
2019-08-06 11:00:35 -05:00
_db . get ( nwid , network , identity . address (). toInt (), member , ns );
2020-07-16 18:31:56 +02:00
if (( ! network . is_object ()) || ( network . empty ())) {
2021-05-24 22:58:17 -04:00
_sender -> ncSendError ( nwid , requestPacketId , identity . address (), NetworkController :: NC_ERROR_OBJECT_NOT_FOUND , nullptr , 0 );
2016-11-10 13:57:01 -08:00
return ;
}
2020-07-16 18:31:56 +02:00
const bool newMember = (( ! member . is_object ()) || ( member . empty ()));
2018-01-09 12:39:25 -08:00
DB :: initMember ( member );
2016-11-10 13:57:01 -08:00
{
2017-08-30 17:22:25 -07:00
const std :: string haveIdStr ( OSUtils :: jsonString ( member [ "identity" ], "" ));
2016-11-10 13:57:01 -08:00
if ( haveIdStr . length () > 0 ) {
// If we already know this member's identity perform a full compare. This prevents
// a "collision" from being able to auth onto our network in place of an already
// known member.
try {
if ( Identity ( haveIdStr . c_str ()) != identity ) {
2021-05-24 22:58:17 -04:00
_sender -> ncSendError ( nwid , requestPacketId , identity . address (), NetworkController :: NC_ERROR_ACCESS_DENIED , nullptr , 0 );
2016-11-10 13:57:01 -08:00
return ;
}
} catch ( ... ) {
2021-05-24 22:58:17 -04:00
_sender -> ncSendError ( nwid , requestPacketId , identity . address (), NetworkController :: NC_ERROR_ACCESS_DENIED , nullptr , 0 );
2016-11-10 13:57:01 -08:00
return ;
}
} else {
// If we do not yet know this member's identity, learn it.
2017-07-06 16:11:11 -07:00
char idtmp [ 1024 ];
member [ "identity" ] = identity . toString ( false , idtmp );
2016-11-10 13:57:01 -08:00
}
}
// These are always the same, but make sure they are set
2017-04-27 19:36:03 -07:00
{
2017-07-06 16:11:11 -07:00
char tmpid [ 128 ];
const std :: string addrs ( identity . address (). toString ( tmpid ));
2017-04-27 19:36:03 -07:00
member [ "id" ] = addrs ;
member [ "address" ] = addrs ;
member [ "nwid" ] = nwids ;
}
2016-11-10 13:57:01 -08:00
// Determine whether and how member is authorized
2017-08-09 14:37:19 -07:00
bool authorized = false ;
2016-11-15 14:06:25 -08:00
bool autoAuthorized = false ;
json autoAuthCredentialType , autoAuthCredential ;
2017-01-10 13:51:10 -08:00
if ( OSUtils :: jsonBool ( member [ "authorized" ], false )) {
2017-08-09 14:37:19 -07:00
authorized = true ;
2017-01-10 13:51:10 -08:00
} else if ( ! OSUtils :: jsonBool ( network [ "private" ], true )) {
2017-08-09 14:37:19 -07:00
authorized = true ;
autoAuthorized = true ;
autoAuthCredentialType = "public" ;
2016-11-10 13:57:01 -08:00
} else {
char presentedAuth [ 512 ];
if ( metaData . get ( ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH , presentedAuth , sizeof ( presentedAuth )) > 0 ) {
presentedAuth [ 511 ] = ( char ) 0 ; // sanity check
if (( strlen ( presentedAuth ) > 6 ) && ( ! strncmp ( presentedAuth , "token:" , 6 ))) {
const char * const presentedToken = presentedAuth + 6 ;
2017-08-09 14:37:19 -07:00
json authTokens ( network [ "authTokens" ]);
json & tokenExpires = authTokens [ presentedToken ];
if ( tokenExpires . is_number ()) {
if (( tokenExpires == 0 ) || ( tokenExpires > now )) {
authorized = true ;
autoAuthorized = true ;
autoAuthCredentialType = "token" ;
autoAuthCredential = presentedToken ;
2016-11-10 13:57:01 -08:00
}
}
}
}
}
2016-11-15 14:06:25 -08:00
// If we auto-authorized, update member record
2017-08-09 14:37:19 -07:00
if (( autoAuthorized ) && ( authorized )) {
2016-11-15 14:06:25 -08:00
member [ "authorized" ] = true ;
member [ "lastAuthorizedTime" ] = now ;
2017-08-09 14:37:19 -07:00
member [ "lastAuthorizedCredentialType" ] = autoAuthCredentialType ;
member [ "lastAuthorizedCredential" ] = autoAuthCredential ;
2016-11-15 14:06:25 -08:00
}
2021-05-25 12:58:33 -04:00
2021-06-03 14:38:26 -07:00
// Should we check SSO Stuff?
// If network is configured with SSO, and the member is not marked exempt: yes
// Otherwise no, we use standard auth logic.
bool networkSSOEnabled = OSUtils :: jsonBool ( network [ "ssoEnabled" ], false );
bool memberSSOExempt = OSUtils :: jsonBool ( member [ "ssoExempt" ], false );
2021-06-04 09:46:31 -07:00
if ( networkSSOEnabled && ! memberSSOExempt ) {
int64_t authenticationExpiryTime = ( int64_t ) OSUtils :: jsonInt ( member [ "authenticationExpiryTime" ], 0 );
if (( authenticationExpiryTime == 0 ) || ( authenticationExpiryTime < now )) {
Dictionary < 1024 > authInfo ;
std :: string authenticationURL = _db . getSSOAuthURL ( member );
if ( ! authenticationURL . empty ()) {
authInfo . add ( "aU" , authenticationURL . c_str ());
}
_sender -> ncSendError ( nwid , requestPacketId , identity . address (), NetworkController :: NC_ERROR_AUTHENTICATION_REQUIRED , authInfo . data (), authInfo . sizeBytes ());
return ;
}
}
2017-08-09 14:37:19 -07:00
if ( authorized ) {
2017-05-01 13:21:26 -07:00
// Update version info and meta-data if authorized and if this is a genuine request
if ( requestPacketId ) {
const uint64_t vMajor = metaData . getUI ( ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION , 0 );
const uint64_t vMinor = metaData . getUI ( ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION , 0 );
const uint64_t vRev = metaData . getUI ( ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION , 0 );
const uint64_t vProto = metaData . getUI ( ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION , 0 );
2016-11-10 13:57:01 -08:00
2017-05-01 13:21:26 -07:00
member [ "vMajor" ] = vMajor ;
member [ "vMinor" ] = vMinor ;
member [ "vRev" ] = vRev ;
member [ "vProto" ] = vProto ;
{
2017-11-03 11:39:27 -07:00
std :: lock_guard < std :: mutex > l ( _memberStatus_l );
2017-05-01 13:21:26 -07:00
_MemberStatus & ms = _memberStatus [ _MemberStatusKey ( nwid , identity . address (). toInt ())];
ms . vMajor = ( int ) vMajor ;
ms . vMinor = ( int ) vMinor ;
ms . vRev = ( int ) vRev ;
ms . vProto = ( int ) vProto ;
ms . lastRequestMetaData = metaData ;
ms . identity = identity ;
2016-11-10 13:57:01 -08:00
}
2021-06-04 09:46:31 -07:00
}
2017-05-01 13:21:26 -07:00
} else {
2021-06-04 09:46:31 -07:00
2017-05-01 13:21:26 -07:00
// If they are not authorized, STOP!
2018-01-09 12:39:25 -08:00
DB :: cleanMember ( member );
2019-08-06 11:00:35 -05:00
_db . save ( member , true );
2021-05-24 22:58:17 -04:00
_sender -> ncSendError ( nwid , requestPacketId , identity . address (), NetworkController :: NC_ERROR_ACCESS_DENIED , nullptr , 0 );
2016-11-10 13:57:01 -08:00
return ;
}
// -------------------------------------------------------------------------
2021-05-24 22:58:17 -04:00
// If we made it this far, they are authorized (and authenticated).
2016-11-10 13:57:01 -08:00
// -------------------------------------------------------------------------
2017-10-02 15:52:57 -07:00
int64_t credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA ;
2017-04-26 06:48:08 -07:00
if ( now > ns . mostRecentDeauthTime ) {
2016-11-15 14:26:05 -08:00
// If we recently de-authorized a member, shrink credential TTL/max delta to
// be below the threshold required to exclude it. Cap this to a min/max to
// prevent jitter or absurdly large values.
2017-04-26 06:48:08 -07:00
const uint64_t deauthWindow = now - ns . mostRecentDeauthTime ;
2016-11-15 14:26:05 -08:00
if ( deauthWindow < ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA ) {
credentialtmd = ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA ;
} else if ( deauthWindow < ( ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA + 5000ULL )) {
credentialtmd = deauthWindow - 5000ULL ;
}
}
2016-11-10 13:57:01 -08:00
2018-01-12 10:38:19 -08:00
std :: unique_ptr < NetworkConfig > nc ( new NetworkConfig ());
2017-04-27 00:59:36 -07:00
nc -> networkId = nwid ;
nc -> type = OSUtils :: jsonBool ( network [ "private" ], true ) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC ;
nc -> timestamp = now ;
nc -> credentialTimeMaxDelta = credentialtmd ;
nc -> revision = OSUtils :: jsonInt ( network [ "revision" ], 0ULL );
nc -> issuedTo = identity . address ();
if ( OSUtils :: jsonBool ( network [ "enableBroadcast" ], true )) nc -> flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST ;
Utils :: scopy ( nc -> name , sizeof ( nc -> name ), OSUtils :: jsonString ( network [ "name" ], "" ). c_str ());
2017-05-04 17:22:24 -07:00
nc -> mtu = std :: max ( std :: min (( unsigned int ) OSUtils :: jsonInt ( network [ "mtu" ], ZT_DEFAULT_MTU ),( unsigned int ) ZT_MAX_MTU ),( unsigned int ) ZT_MIN_MTU );
2017-04-27 00:59:36 -07:00
nc -> multicastLimit = ( unsigned int ) OSUtils :: jsonInt ( network [ "multicastLimit" ], 32ULL );
2021-06-03 14:38:26 -07:00
// TODO: Decide what to do with these, or if to remove them
// they don't make sense here as is.
// Utils::scopy(nc->authenticationURL, sizeof(nc->authenticationURL), authenticationURL.c_str());
// nc->authenticationExpiryTime = authenticationExpiryTime;
2021-05-25 13:05:06 -04:00
2017-07-14 14:33:36 -07:00
std :: string rtt ( OSUtils :: jsonString ( member [ "remoteTraceTarget" ], "" ));
if ( rtt . length () == 10 ) {
nc -> remoteTraceTarget = Address ( Utils :: hexStrToU64 ( rtt . c_str ()));
2017-12-05 17:49:20 -05:00
nc -> remoteTraceLevel = ( Trace :: Level ) OSUtils :: jsonInt ( member [ "remoteTraceLevel" ], 0ULL );
2017-07-14 14:33:36 -07:00
} else {
rtt = OSUtils :: jsonString ( network [ "remoteTraceTarget" ], "" );
if ( rtt . length () == 10 ) {
nc -> remoteTraceTarget = Address ( Utils :: hexStrToU64 ( rtt . c_str ()));
2017-07-17 17:02:50 -07:00
} else {
2017-08-18 13:52:10 -07:00
nc -> remoteTraceTarget . zero ();
2017-07-14 14:33:36 -07:00
}
2017-12-05 17:49:20 -05:00
nc -> remoteTraceLevel = ( Trace :: Level ) OSUtils :: jsonInt ( network [ "remoteTraceLevel" ], 0ULL );
2017-07-14 14:33:36 -07:00
}
2017-04-26 06:48:08 -07:00
for ( std :: vector < Address >:: const_iterator ab ( ns . activeBridges . begin ()); ab != ns . activeBridges . end (); ++ ab )
2017-04-27 00:59:36 -07:00
nc -> addSpecialist ( * ab , ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE );
2016-11-10 13:57:01 -08:00
json & v4AssignMode = network [ "v4AssignMode" ];
json & v6AssignMode = network [ "v6AssignMode" ];
json & ipAssignmentPools = network [ "ipAssignmentPools" ];
json & routes = network [ "routes" ];
json & rules = network [ "rules" ];
json & capabilities = network [ "capabilities" ];
2017-02-21 13:27:20 -08:00
json & tags = network [ "tags" ];
2016-11-10 13:57:01 -08:00
json & memberCapabilities = member [ "capabilities" ];
json & memberTags = member [ "tags" ];
2020-08-04 09:45:45 -07:00
json & dns = network [ "dns" ];
2016-11-10 13:57:01 -08:00
if ( metaData . getUI ( ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV , 0 ) <= 0 ) {
// Old versions with no rules engine support get an allow everything rule.
// Since rules are enforced bidirectionally, newer versions *will* still
// enforce rules on the inbound side.
2017-04-27 00:59:36 -07:00
nc -> ruleCount = 1 ;
nc -> rules [ 0 ]. t = ZT_NETWORK_RULE_ACTION_ACCEPT ;
2016-11-10 13:57:01 -08:00
} else {
if ( rules . is_array ()) {
for ( unsigned long i = 0 ; i < rules . size (); ++ i ) {
2017-04-27 00:59:36 -07:00
if ( nc -> ruleCount >= ZT_MAX_NETWORK_RULES )
2016-11-10 13:57:01 -08:00
break ;
2017-04-27 00:59:36 -07:00
if ( _parseRule ( rules [ i ], nc -> rules [ nc -> ruleCount ]))
++ nc -> ruleCount ;
2016-11-10 13:57:01 -08:00
}
}
2017-02-21 13:27:20 -08:00
std :: map < uint64_t , json * > capsById ;
if ( ! memberCapabilities . is_array ())
memberCapabilities = json :: array ();
if ( capabilities . is_array ()) {
2016-11-10 13:57:01 -08:00
for ( unsigned long i = 0 ; i < capabilities . size (); ++ i ) {
json & cap = capabilities [ i ];
2017-02-21 13:27:20 -08:00
if ( cap . is_object ()) {
const uint64_t id = OSUtils :: jsonInt ( cap [ "id" ], 0ULL ) & 0xffffffffULL ;
capsById [ id ] = & cap ;
if (( newMember ) && ( OSUtils :: jsonBool ( cap [ "default" ], false ))) {
bool have = false ;
for ( unsigned long i = 0 ; i < memberCapabilities . size (); ++ i ) {
if ( id == ( OSUtils :: jsonInt ( memberCapabilities [ i ], 0ULL ) & 0xffffffffULL )) {
have = true ;
2016-11-10 13:57:01 -08:00
break ;
2017-02-21 13:27:20 -08:00
}
2016-11-10 13:57:01 -08:00
}
2017-02-21 13:27:20 -08:00
if ( ! have )
memberCapabilities . push_back ( id );
2016-11-10 13:57:01 -08:00
}
}
}
}
2017-02-21 13:27:20 -08:00
for ( unsigned long i = 0 ; i < memberCapabilities . size (); ++ i ) {
const uint64_t capId = OSUtils :: jsonInt ( memberCapabilities [ i ], 0ULL ) & 0xffffffffULL ;
2017-02-21 13:48:29 -08:00
std :: map < uint64_t , json * >:: const_iterator ctmp = capsById . find ( capId );
if ( ctmp != capsById . end ()) {
json * cap = ctmp -> second ;
2020-07-16 18:31:56 +02:00
if (( cap ) && ( cap -> is_object ()) && ( ! cap -> empty ())) {
2017-02-21 13:48:29 -08:00
ZT_VirtualNetworkRule capr [ ZT_MAX_CAPABILITY_RULES ];
unsigned int caprc = 0 ;
json & caprj = ( * cap )[ "rules" ];
2020-07-16 18:31:56 +02:00
if (( caprj . is_array ()) && ( ! caprj . empty ())) {
2017-02-21 13:48:29 -08:00
for ( unsigned long j = 0 ; j < caprj . size (); ++ j ) {
if ( caprc >= ZT_MAX_CAPABILITY_RULES )
break ;
if ( _parseRule ( caprj [ j ], capr [ caprc ]))
++ caprc ;
}
2017-02-21 13:27:20 -08:00
}
2017-04-27 00:59:36 -07:00
nc -> capabilities [ nc -> capabilityCount ] = Capability (( uint32_t ) capId , nwid , now , 1 , capr , caprc );
if ( nc -> capabilities [ nc -> capabilityCount ]. sign ( _signingId , identity . address ()))
++ nc -> capabilityCount ;
if ( nc -> capabilityCount >= ZT_MAX_NETWORK_CAPABILITIES )
2017-02-21 13:48:29 -08:00
break ;
2017-02-21 13:27:20 -08:00
}
}
}
2016-11-10 13:57:01 -08:00
2017-02-21 13:27:20 -08:00
std :: map < uint32_t , uint32_t > memberTagsById ;
2016-11-10 13:57:01 -08:00
if ( memberTags . is_array ()) {
for ( unsigned long i = 0 ; i < memberTags . size (); ++ i ) {
json & t = memberTags [ i ];
if (( t . is_array ()) && ( t . size () == 2 ))
2017-02-21 13:27:20 -08:00
memberTagsById [( uint32_t )( OSUtils :: jsonInt ( t [ 0 ], 0ULL ) & 0xffffffffULL )] = ( uint32_t )( OSUtils :: jsonInt ( t [ 1 ], 0ULL ) & 0xffffffffULL );
2016-11-10 13:57:01 -08:00
}
2017-02-21 13:27:20 -08:00
}
if ( tags . is_array ()) { // check network tags array for defaults that are not present in member tags
for ( unsigned long i = 0 ; i < tags . size (); ++ i ) {
json & t = tags [ i ];
if ( t . is_object ()) {
const uint32_t id = ( uint32_t )( OSUtils :: jsonInt ( t [ "id" ], 0 ) & 0xffffffffULL );
json & dfl = t [ "default" ];
if (( dfl . is_number ()) && ( memberTagsById . find ( id ) == memberTagsById . end ())) {
memberTagsById [ id ] = ( uint32_t )( OSUtils :: jsonInt ( dfl , 0 ) & 0xffffffffULL );
json mt = json :: array ();
mt . push_back ( id );
mt . push_back ( dfl );
memberTags . push_back ( mt ); // add default to member tags if not present
}
}
2016-11-10 13:57:01 -08:00
}
}
2017-02-21 13:27:20 -08:00
for ( std :: map < uint32_t , uint32_t >:: const_iterator t ( memberTagsById . begin ()); t != memberTagsById . end (); ++ t ) {
2017-04-27 00:59:36 -07:00
if ( nc -> tagCount >= ZT_MAX_NETWORK_TAGS )
2017-02-21 13:27:20 -08:00
break ;
2017-04-27 00:59:36 -07:00
nc -> tags [ nc -> tagCount ] = Tag ( nwid , now , identity . address (), t -> first , t -> second );
if ( nc -> tags [ nc -> tagCount ]. sign ( _signingId ))
++ nc -> tagCount ;
2017-02-21 13:27:20 -08:00
}
2016-11-10 13:57:01 -08:00
}
if ( routes . is_array ()) {
for ( unsigned long i = 0 ; i < routes . size (); ++ i ) {
2017-04-27 00:59:36 -07:00
if ( nc -> routeCount >= ZT_MAX_NETWORK_ROUTES )
2016-11-10 13:57:01 -08:00
break ;
json & route = routes [ i ];
json & target = route [ "target" ];
json & via = route [ "via" ];
if ( target . is_string ()) {
2017-07-06 16:11:11 -07:00
const InetAddress t ( target . get < std :: string > (). c_str ());
2016-11-10 13:57:01 -08:00
InetAddress v ;
2017-07-06 16:11:11 -07:00
if ( via . is_string ()) v . fromString ( via . get < std :: string > (). c_str ());
2016-11-10 13:57:01 -08:00
if (( t . ss_family == AF_INET ) || ( t . ss_family == AF_INET6 )) {
2017-04-27 00:59:36 -07:00
ZT_VirtualNetworkRoute * r = & ( nc -> routes [ nc -> routeCount ]);
2016-11-10 13:57:01 -08:00
* ( reinterpret_cast < InetAddress *> ( & ( r -> target ))) = t ;
if ( v . ss_family == t . ss_family )
* ( reinterpret_cast < InetAddress *> ( & ( r -> via ))) = v ;
2017-04-27 00:59:36 -07:00
++ nc -> routeCount ;
2016-11-10 13:57:01 -08:00
}
}
}
}
2017-01-10 13:51:10 -08:00
const bool noAutoAssignIps = OSUtils :: jsonBool ( member [ "noAutoAssignIps" ], false );
2016-11-10 13:57:01 -08:00
if (( v6AssignMode . is_object ()) && ( ! noAutoAssignIps )) {
2017-04-27 00:59:36 -07:00
if (( OSUtils :: jsonBool ( v6AssignMode [ "rfc4193" ], false )) && ( nc -> staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES )) {
nc -> staticIps [ nc -> staticIpCount ++ ] = InetAddress :: makeIpv6rfc4193 ( nwid , identity . address (). toInt ());
nc -> flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION ;
2016-11-10 13:57:01 -08:00
}
2017-04-27 00:59:36 -07:00
if (( OSUtils :: jsonBool ( v6AssignMode [ "6plane" ], false )) && ( nc -> staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES )) {
nc -> staticIps [ nc -> staticIpCount ++ ] = InetAddress :: makeIpv66plane ( nwid , identity . address (). toInt ());
nc -> flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION ;
2016-11-10 13:57:01 -08:00
}
}
bool haveManagedIpv4AutoAssignment = false ;
bool haveManagedIpv6AutoAssignment = false ; // "special" NDP-emulated address types do not count
json ipAssignments = member [ "ipAssignments" ]; // we want to make a copy
if ( ipAssignments . is_array ()) {
for ( unsigned long i = 0 ; i < ipAssignments . size (); ++ i ) {
2018-01-12 10:38:19 -08:00
if ( ipAssignments [ i ]. is_string ()) {
const std :: string ips = ipAssignments [ i ];
InetAddress ip ( ips . c_str ());
2016-11-10 13:57:01 -08:00
2018-01-12 10:38:19 -08:00
int routedNetmaskBits = - 1 ;
for ( unsigned int rk = 0 ; rk < nc -> routeCount ; ++ rk ) {
2019-02-01 14:05:29 -08:00
if ( reinterpret_cast < const InetAddress *> ( & ( nc -> routes [ rk ]. target )) -> containsAddress ( ip )) {
const int nb = ( int )( reinterpret_cast < const InetAddress *> ( & ( nc -> routes [ rk ]. target )) -> netmaskBits ());
if ( nb > routedNetmaskBits )
routedNetmaskBits = nb ;
}
2018-01-12 10:38:19 -08:00
}
2016-11-10 13:57:01 -08:00
2018-01-12 10:38:19 -08:00
if ( routedNetmaskBits >= 0 ) {
if ( nc -> staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES ) {
ip . setPort ( routedNetmaskBits );
nc -> staticIps [ nc -> staticIpCount ++ ] = ip ;
}
if ( ip . ss_family == AF_INET )
haveManagedIpv4AutoAssignment = true ;
else if ( ip . ss_family == AF_INET6 )
haveManagedIpv6AutoAssignment = true ;
2016-11-10 13:57:01 -08:00
}
}
}
} else {
ipAssignments = json :: array ();
}
2017-01-10 13:51:10 -08:00
if ( ( ipAssignmentPools . is_array ()) && (( v6AssignMode . is_object ()) && ( OSUtils :: jsonBool ( v6AssignMode [ "zt" ], false ))) && ( ! haveManagedIpv6AutoAssignment ) && ( ! noAutoAssignIps ) ) {
2016-11-10 13:57:01 -08:00
for ( unsigned long p = 0 ;(( p < ipAssignmentPools . size ()) && ( ! haveManagedIpv6AutoAssignment )); ++ p ) {
json & pool = ipAssignmentPools [ p ];
if ( pool . is_object ()) {
2017-07-06 16:11:11 -07:00
InetAddress ipRangeStart ( OSUtils :: jsonString ( pool [ "ipRangeStart" ], "" ). c_str ());
InetAddress ipRangeEnd ( OSUtils :: jsonString ( pool [ "ipRangeEnd" ], "" ). c_str ());
2016-11-10 13:57:01 -08:00
if ( ( ipRangeStart . ss_family == AF_INET6 ) && ( ipRangeEnd . ss_family == AF_INET6 ) ) {
uint64_t s [ 2 ], e [ 2 ], x [ 2 ], xx [ 2 ];
2019-03-22 15:50:15 -07:00
memcpy ( s , ipRangeStart . rawIpData (), 16 );
memcpy ( e , ipRangeEnd . rawIpData (), 16 );
2016-11-10 13:57:01 -08:00
s [ 0 ] = Utils :: ntoh ( s [ 0 ]);
s [ 1 ] = Utils :: ntoh ( s [ 1 ]);
e [ 0 ] = Utils :: ntoh ( e [ 0 ]);
e [ 1 ] = Utils :: ntoh ( e [ 1 ]);
x [ 0 ] = s [ 0 ];
x [ 1 ] = s [ 1 ];
for ( unsigned int trialCount = 0 ; trialCount < 1000 ; ++ trialCount ) {
if (( trialCount == 0 ) && ( e [ 1 ] > s [ 1 ]) && (( e [ 1 ] - s [ 1 ]) >= 0xffffffffffULL )) {
// First see if we can just cram a ZeroTier ID into the higher 64 bits. If so do that.
xx [ 0 ] = Utils :: hton ( x [ 0 ]);
xx [ 1 ] = Utils :: hton ( x [ 1 ] + identity . address (). toInt ());
} else {
// Otherwise pick random addresses -- this technically doesn't explore the whole range if the lower 64 bit range is >= 1 but that won't matter since that would be huge anyway
Utils :: getSecureRandom (( void * ) xx , 16 );
if (( e [ 0 ] > s [ 0 ]))
xx [ 0 ] %= ( e [ 0 ] - s [ 0 ]);
else xx [ 0 ] = 0 ;
if (( e [ 1 ] > s [ 1 ]))
xx [ 1 ] %= ( e [ 1 ] - s [ 1 ]);
else xx [ 1 ] = 0 ;
xx [ 0 ] = Utils :: hton ( x [ 0 ] + xx [ 0 ]);
xx [ 1 ] = Utils :: hton ( x [ 1 ] + xx [ 1 ]);
}
InetAddress ip6 (( const void * ) xx , 16 , 0 );
// Check if this IP is within a local-to-Ethernet routed network
int routedNetmaskBits = 0 ;
2017-04-27 00:59:36 -07:00
for ( unsigned int rk = 0 ; rk < nc -> routeCount ; ++ rk ) {
if ( ( ! nc -> routes [ rk ]. via . ss_family ) && ( nc -> routes [ rk ]. target . ss_family == AF_INET6 ) && ( reinterpret_cast < const InetAddress *> ( & ( nc -> routes [ rk ]. target )) -> containsAddress ( ip6 )) )
routedNetmaskBits = reinterpret_cast < const InetAddress *> ( & ( nc -> routes [ rk ]. target )) -> netmaskBits ();
2016-11-10 13:57:01 -08:00
}
// If it's routed, then try to claim and assign it and if successful end loop
2017-04-26 06:48:08 -07:00
if ( ( routedNetmaskBits > 0 ) && ( ! std :: binary_search ( ns . allocatedIps . begin (), ns . allocatedIps . end (), ip6 )) ) {
2017-07-06 16:11:11 -07:00
char tmpip [ 64 ];
2018-01-12 10:38:19 -08:00
const std :: string ipStr ( ip6 . toIpString ( tmpip ));
if ( std :: find ( ipAssignments . begin (), ipAssignments . end (), ipStr ) == ipAssignments . end ()) {
ipAssignments . push_back ( ipStr );
member [ "ipAssignments" ] = ipAssignments ;
ip6 . setPort (( unsigned int ) routedNetmaskBits );
if ( nc -> staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES )
nc -> staticIps [ nc -> staticIpCount ++ ] = ip6 ;
haveManagedIpv6AutoAssignment = true ;
break ;
}
2016-11-10 13:57:01 -08:00
}
}
}
}
}
}
2017-01-10 13:51:10 -08:00
if ( ( ipAssignmentPools . is_array ()) && (( v4AssignMode . is_object ()) && ( OSUtils :: jsonBool ( v4AssignMode [ "zt" ], false ))) && ( ! haveManagedIpv4AutoAssignment ) && ( ! noAutoAssignIps ) ) {
2016-11-10 13:57:01 -08:00
for ( unsigned long p = 0 ;(( p < ipAssignmentPools . size ()) && ( ! haveManagedIpv4AutoAssignment )); ++ p ) {
json & pool = ipAssignmentPools [ p ];
if ( pool . is_object ()) {
2017-07-06 16:11:11 -07:00
InetAddress ipRangeStartIA ( OSUtils :: jsonString ( pool [ "ipRangeStart" ], "" ). c_str ());
InetAddress ipRangeEndIA ( OSUtils :: jsonString ( pool [ "ipRangeEnd" ], "" ). c_str ());
2016-11-10 13:57:01 -08:00
if ( ( ipRangeStartIA . ss_family == AF_INET ) && ( ipRangeEndIA . ss_family == AF_INET ) ) {
uint32_t ipRangeStart = Utils :: ntoh (( uint32_t )( reinterpret_cast < struct sockaddr_in *> ( & ipRangeStartIA ) -> sin_addr . s_addr ));
uint32_t ipRangeEnd = Utils :: ntoh (( uint32_t )( reinterpret_cast < struct sockaddr_in *> ( & ipRangeEndIA ) -> sin_addr . s_addr ));
2019-05-14 13:54:27 -07:00
2016-11-10 13:57:01 -08:00
if (( ipRangeEnd < ipRangeStart ) || ( ipRangeStart == 0 ))
continue ;
uint32_t ipRangeLen = ipRangeEnd - ipRangeStart ;
2019-08-23 09:23:39 -07:00
2016-11-10 13:57:01 -08:00
// Start with the LSB of the member's address
uint32_t ipTrialCounter = ( uint32_t )( identity . address (). toInt () & 0xffffffff );
for ( uint32_t k = ipRangeStart , trialCount = 0 ;(( k <= ipRangeEnd ) && ( trialCount < 1000 )); ++ k , ++ trialCount ) {
uint32_t ip = ( ipRangeLen > 0 ) ? ( ipRangeStart + ( ipTrialCounter % ipRangeLen )) : ipRangeStart ;
++ ipTrialCounter ;
2018-10-24 12:06:17 -07:00
if (( ip & 0x000000ff ) == 0x000000ff ) {
2016-11-10 13:57:01 -08:00
continue ; // don't allow addresses that end in .255
2018-10-24 12:06:17 -07:00
}
2016-11-10 13:57:01 -08:00
// Check if this IP is within a local-to-Ethernet routed network
int routedNetmaskBits = - 1 ;
2017-04-27 00:59:36 -07:00
for ( unsigned int rk = 0 ; rk < nc -> routeCount ; ++ rk ) {
if ( nc -> routes [ rk ]. target . ss_family == AF_INET ) {
uint32_t targetIp = Utils :: ntoh (( uint32_t )( reinterpret_cast < const struct sockaddr_in *> ( & ( nc -> routes [ rk ]. target )) -> sin_addr . s_addr ));
int targetBits = Utils :: ntoh (( uint16_t )( reinterpret_cast < const struct sockaddr_in *> ( & ( nc -> routes [ rk ]. target )) -> sin_port ));
2016-11-10 13:57:01 -08:00
if (( ip & ( 0xffffffff << ( 32 - targetBits ))) == targetIp ) {
routedNetmaskBits = targetBits ;
break ;
}
}
}
// If it's routed, then try to claim and assign it and if successful end loop
const InetAddress ip4 ( Utils :: hton ( ip ), 0 );
2017-04-26 06:48:08 -07:00
if ( ( routedNetmaskBits > 0 ) && ( ! std :: binary_search ( ns . allocatedIps . begin (), ns . allocatedIps . end (), ip4 )) ) {
2017-07-06 16:11:11 -07:00
char tmpip [ 64 ];
2018-01-12 10:38:19 -08:00
const std :: string ipStr ( ip4 . toIpString ( tmpip ));
if ( std :: find ( ipAssignments . begin (), ipAssignments . end (), ipStr ) == ipAssignments . end ()) {
ipAssignments . push_back ( ipStr );
member [ "ipAssignments" ] = ipAssignments ;
if ( nc -> staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES ) {
struct sockaddr_in * const v4ip = reinterpret_cast < struct sockaddr_in *> ( & ( nc -> staticIps [ nc -> staticIpCount ++ ]));
v4ip -> sin_family = AF_INET ;
v4ip -> sin_port = Utils :: hton (( uint16_t ) routedNetmaskBits );
v4ip -> sin_addr . s_addr = Utils :: hton ( ip );
}
haveManagedIpv4AutoAssignment = true ;
break ;
2016-11-10 13:57:01 -08:00
}
}
}
}
}
}
}
2020-07-20 14:34:19 -07:00
2020-08-12 13:08:47 -07:00
if ( dns . is_object ()) {
std :: string domain = OSUtils :: jsonString ( dns [ "domain" ], "" );
memcpy ( nc -> dns . domain , domain . c_str (), domain . size ());
json & addrArray = dns [ "servers" ];
if ( addrArray . is_array ()) {
for ( unsigned int j = 0 ; j < addrArray . size () && j < ZT_MAX_DNS_SERVERS ; ++ j ) {
json & addr = addrArray [ j ];
nc -> dns . server_addr [ j ] = InetAddress ( OSUtils :: jsonString ( addr , "" ). c_str ());
2020-07-20 14:34:19 -07:00
}
}
2020-08-04 09:45:45 -07:00
} else {
2020-08-12 13:08:47 -07:00
dns = json :: object ();
2020-07-20 14:34:19 -07:00
}
2019-05-14 13:54:27 -07:00
2017-02-23 11:47:36 -08:00
// Issue a certificate of ownership for all static IPs
2017-04-27 00:59:36 -07:00
if ( nc -> staticIpCount ) {
nc -> certificatesOfOwnership [ 0 ] = CertificateOfOwnership ( nwid , now , identity . address (), 1 );
for ( unsigned int i = 0 ; i < nc -> staticIpCount ; ++ i )
nc -> certificatesOfOwnership [ 0 ]. addThing ( nc -> staticIps [ i ]);
nc -> certificatesOfOwnership [ 0 ]. sign ( _signingId );
nc -> certificateOfOwnershipCount = 1 ;
2017-02-23 11:47:36 -08:00
}
2016-11-10 13:57:01 -08:00
CertificateOfMembership com ( now , credentialtmd , nwid , identity . address ());
if ( com . sign ( _signingId )) {
2017-04-27 00:59:36 -07:00
nc -> com = com ;
2016-11-10 13:57:01 -08:00
} else {
2021-05-24 22:58:17 -04:00
_sender -> ncSendError ( nwid , requestPacketId , identity . address (), NetworkController :: NC_ERROR_INTERNAL_SERVER_ERROR , nullptr , 0 );
2016-11-10 13:57:01 -08:00
return ;
}
2018-01-09 12:39:25 -08:00
DB :: cleanMember ( member );
2019-08-06 11:00:35 -05:00
_db . save ( member , true );
2017-04-27 00:59:36 -07:00
_sender -> ncSendConfig ( nwid , requestPacketId , identity . address (), * ( nc . get ()), metaData . getUI ( ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION , 0 ) < 6 );
2016-11-10 13:57:01 -08:00
}
2017-08-31 18:01:21 -04:00
void EmbeddedNetworkController :: _startThreads ()
{
2017-11-03 11:39:27 -07:00
std :: lock_guard < std :: mutex > l ( _threads_l );
if ( ! _threads . empty ())
return ;
const long hwc = std :: max (( long ) std :: thread :: hardware_concurrency (),( long ) 1 );
for ( long t = 0 ; t < hwc ; ++ t ) {
_threads . emplace_back ([ this ]() {
for (;;) {
2017-11-03 16:59:36 -04:00
_RQEntry * qe = ( _RQEntry * ) 0 ;
2017-11-03 22:40:26 -04:00
if ( ! _queue . get ( qe ))
2017-11-03 11:39:27 -07:00
break ;
try {
2017-11-03 22:40:26 -04:00
if ( qe ) {
2017-11-03 11:39:27 -07:00
_request ( qe -> nwid , qe -> fromAddr , qe -> requestPacketId , qe -> identity , qe -> metaData );
2017-11-03 22:40:26 -04:00
delete qe ;
}
2017-11-03 11:39:27 -07:00
} catch ( std :: exception & e ) {
fprintf ( stderr , "ERROR: exception in controller request handling thread: %s" ZT_EOL_S , e . what ());
} catch ( ... ) {
fprintf ( stderr , "ERROR: exception in controller request handling thread: unknown exception" ZT_EOL_S );
}
}
});
2017-08-31 18:01:21 -04:00
}
}
2015-01-06 13:45:10 -08:00
} // namespace ZeroTier