Переглянути джерело

Add length limit to TEE and REDIRECT, and completely factor out old C json-parser to eliminate a dependency.

Adam Ierymenko 9 роки тому
батько
коміт
8e3463d47a

+ 4 - 4
AUTHORS.md

@@ -39,11 +39,11 @@ These are included in ext/ for platforms that do not have them available in comm
    * Home page: https://github.com/joyent/http-parser/
    * License grant: MIT/Expat
 
- * json-parser by James McLaughlin
+ * C++11 json (nlohmann/json) by Niels Lohmann
 
-   * Files: ext/json-parser/*
-   * Home page: https://github.com/udp/json-parser/
-   * License grant: BSD attribution
+   * Files: ext/json/*
+   * Home page: https://github.com/nlohmann/json
+   * License grant: MIT
 
  * TunTapOSX by Mattias Nissler
 

+ 13 - 5
controller/EmbeddedNetworkController.cpp

@@ -121,11 +121,15 @@ static json _renderRule(ZT_VirtualNetworkRule &rule)
 			break;
 		case ZT_NETWORK_RULE_ACTION_TEE:
 			r["type"] = "ACTION_TEE";
-			r["zt"] = Address(rule.v.zt).toString();
+			r["address"] = Address(rule.v.fwd.address).toString();
+			r["flags"] = (uint64_t)rule.v.fwd.flags;
+			r["length"] = (uint64_t)rule.v.fwd.length;
 			break;
 		case ZT_NETWORK_RULE_ACTION_REDIRECT:
 			r["type"] = "ACTION_REDIRECT";
-			r["zt"] = Address(rule.v.zt).toString();
+			r["address"] = Address(rule.v.fwd.address).toString();
+			r["flags"] = (uint64_t)rule.v.fwd.flags;
+			r["length"] = (uint64_t)rule.v.fwd.length;
 			break;
 		case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
 			r["type"] = "MATCH_SOURCE_ZEROTIER_ADDRESS";
@@ -235,7 +239,7 @@ static bool _parseRule(const json &r,ZT_VirtualNetworkRule &rule)
 {
 	if (r.is_object())
 		return false;
-	std::string t = r["type"];
+	const std::string t(_jS(r["type"],""));
 	memset(&rule,0,sizeof(ZT_VirtualNetworkRule));
 	if (_jB(r["not"],false))
 		rule.t = 0x80;
@@ -248,11 +252,15 @@ static bool _parseRule(const json &r,ZT_VirtualNetworkRule &rule)
 		return true;
 	} else if (t == "ACTION_TEE") {
 		rule.t |= ZT_NETWORK_RULE_ACTION_TEE;
-		rule.v.zt = Utils::hexStrToU64(_jS(r["zt"],"0").c_str()) & 0xffffffffffULL;
+		rule.v.fwd.address = Utils::hexStrToU64(_jS(r["address"],"0").c_str()) & 0xffffffffffULL;
+		rule.v.fwd.flags = (uint32_t)(_jI(r["flags"],0ULL) & 0xffffffffULL);
+		rule.v.fwd.length = (uint16_t)(_jI(r["length"],0ULL) & 0xffffULL);
 		return true;
 	} else if (t == "ACTION_REDIRECT") {
 		rule.t |= ZT_NETWORK_RULE_ACTION_REDIRECT;
-		rule.v.zt = Utils::hexStrToU64(_jS(r["zt"],"0").c_str()) & 0xffffffffffULL;
+		rule.v.fwd.address = Utils::hexStrToU64(_jS(r["zt"],"0").c_str()) & 0xffffffffffULL;
+		rule.v.fwd.flags = (uint32_t)(_jI(r["flags"],0ULL) & 0xffffffffULL);
+		rule.v.fwd.length = (uint16_t)(_jI(r["length"],0ULL) & 0xffffULL);
 		return true;
 	} else if (t == "MATCH_SOURCE_ZEROTIER_ADDRESS") {
 		rule.t |= ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS;

+ 0 - 20
ext/json-parser/AUTHORS

@@ -1,20 +0,0 @@
-All contributors arranged by first commit:
-
-James McLaughlin
-Alex Gartrell
-Peter Scott
-Mathias Kaerlev
-Emiel Mols
-Czarek Tomczak
-Nicholas Braden
-Ivan Kozub
-Árpád Goretity
-Igor Gnatenko
-Haïkel Guémar
-Tobias Waldekranz
-Patrick Donnelly 
-Wilmer van der Gaast
-Jin Wei
-François Cartegnie
-Matthijs Boelstra
-

+ 0 - 26
ext/json-parser/LICENSE

@@ -1,26 +0,0 @@
-
-  Copyright (C) 2012, 2013 James McLaughlin et al.  All rights reserved.
- 
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions
-  are met:
- 
-  1. Redistributions of source code must retain the above copyright
-     notice, this list of conditions and the following disclaimer.
- 
-  2. Redistributions in binary form must reproduce the above copyright
-     notice, this list of conditions and the following disclaimer in the
-     documentation and/or other materials provided with the distribution.
- 
-  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-  SUCH DAMAGE.
-

+ 0 - 97
ext/json-parser/README.md

@@ -1,97 +0,0 @@
-Very low footprint JSON parser written in portable ANSI C.
-
-* BSD licensed with no dependencies (i.e. just drop the C file into your project)
-* Never recurses or allocates more memory than it needs
-* Very simple API with operator sugar for C++
-
-[![Build Status](https://secure.travis-ci.org/udp/json-parser.png)](http://travis-ci.org/udp/json-parser)
-
-_Want to serialize?  Check out [json-builder](https://github.com/udp/json-builder)!_
-
-Installing
-----------
-
-There is now a makefile which will produce a libjsonparser static and dynamic library.  However, this
-is _not_ required to build json-parser, and the source files (`json.c` and `json.h`) should be happy
-in any build system you already have in place.
-
-
-API
----
-
-    json_value * json_parse (const json_char * json,
-                             size_t length);
-
-    json_value * json_parse_ex (json_settings * settings,
-                                const json_char * json,
-                                size_t length,
-                                char * error);
-
-    void json_value_free (json_value *);
-
-The `type` field of `json_value` is one of:
-
-* `json_object` (see `u.object.length`, `u.object.values[x].name`, `u.object.values[x].value`)
-* `json_array` (see `u.array.length`, `u.array.values`)
-* `json_integer` (see `u.integer`)
-* `json_double` (see `u.dbl`)
-* `json_string` (see `u.string.ptr`, `u.string.length`)
-* `json_boolean` (see `u.boolean`)
-* `json_null`
-
-
-Compile-Time Options
---------------------
-
-    -DJSON_TRACK_SOURCE
-
-Stores the source location (line and column number) inside each `json_value`.
-
-This is useful for application-level error reporting.
-
-
-Runtime Options
----------------
-
-    settings |= json_enable_comments;
-
-Enables C-style `// line` and `/* block */` comments.
-
-    size_t value_extra
-
-The amount of space (if any) to allocate at the end of each `json_value`, in
-order to give the application space to add metadata.
-
-    void * (* mem_alloc) (size_t, int zero, void * user_data);
-    void (* mem_free) (void *, void * user_data);
-
-Custom allocator routines.  If NULL, the default `malloc` and `free` will be used.
-
-The `user_data` pointer will be forwarded from `json_settings` to allow application
-context to be passed.
-
-
-Changes in version 1.1.0
-------------------------
-
-* UTF-8 byte order marks are now skipped if present
-
-* Allows cross-compilation by honoring --host if given (@wkz)
-
-* Maximum size for error buffer is now exposed in header (@LB--)
-
-* GCC warning for `static` after `const` fixed (@batrick)
-
-* Optional support for C-style line and block comments added (@Jin-W-FS)
-
-* `name_length` field added to object values 
-
-* It is now possible to retrieve the source line/column number of a parsed `json_value` when `JSON_TRACK_SOURCE` is enabled
-
-* The application may now extend `json_value` using the `value_extra` setting
-
-* Un-ambiguate pow call in the case of C++ overloaded pow (@fcartegnie)
-
-* Fix null pointer de-reference when a non-existing array is closed and no root value is present
-
-

+ 0 - 1012
ext/json-parser/json.c

@@ -1,1012 +0,0 @@
-/* vim: set et ts=3 sw=3 sts=3 ft=c:
- *
- * Copyright (C) 2012, 2013, 2014 James McLaughlin et al.  All rights reserved.
- * https://github.com/udp/json-parser
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include "json.h"
-
-#ifdef _MSC_VER
-   #ifndef _CRT_SECURE_NO_WARNINGS
-      #define _CRT_SECURE_NO_WARNINGS
-   #endif
-   #pragma warning(disable:4996)
-#endif
-
-const struct _json_value json_value_none;
-
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-
-typedef unsigned int json_uchar;
-
-static unsigned char hex_value (json_char c)
-{
-   if (isdigit(c))
-      return c - '0';
-
-   switch (c) {
-      case 'a': case 'A': return 0x0A;
-      case 'b': case 'B': return 0x0B;
-      case 'c': case 'C': return 0x0C;
-      case 'd': case 'D': return 0x0D;
-      case 'e': case 'E': return 0x0E;
-      case 'f': case 'F': return 0x0F;
-      default: return 0xFF;
-   }
-}
-
-typedef struct
-{
-   unsigned long used_memory;
-
-   unsigned int uint_max;
-   unsigned long ulong_max;
-
-   json_settings settings;
-   int first_pass;
-
-   const json_char * ptr;
-   unsigned int cur_line, cur_col;
-
-} json_state;
-
-static void * default_alloc (size_t size, int zero, void * user_data)
-{
-   return zero ? calloc (1, size) : malloc (size);
-}
-
-static void default_free (void * ptr, void * user_data)
-{
-   free (ptr);
-}
-
-static void * json_alloc (json_state * state, unsigned long size, int zero)
-{
-   if ((state->ulong_max - state->used_memory) < size)
-      return 0;
-
-   if (state->settings.max_memory
-         && (state->used_memory += size) > state->settings.max_memory)
-   {
-      return 0;
-   }
-
-   return state->settings.mem_alloc (size, zero, state->settings.user_data);
-}
-
-static int new_value (json_state * state,
-                      json_value ** top, json_value ** root, json_value ** alloc,
-                      json_type type)
-{
-   json_value * value;
-   int values_size;
-
-   if (!state->first_pass)
-   {
-      value = *top = *alloc;
-      *alloc = (*alloc)->_reserved.next_alloc;
-
-      if (!*root)
-         *root = value;
-
-      switch (value->type)
-      {
-         case json_array:
-
-            if (value->u.array.length == 0)
-               break;
-
-            if (! (value->u.array.values = (json_value **) json_alloc
-               (state, value->u.array.length * sizeof (json_value *), 0)) )
-            {
-               return 0;
-            }
-
-            value->u.array.length = 0;
-            break;
-
-         case json_object:
-
-            if (value->u.object.length == 0)
-               break;
-
-            values_size = sizeof (*value->u.object.values) * value->u.object.length;
-
-            if (! (value->u.object.values = (json_object_entry *) json_alloc
-                  (state, values_size + ((unsigned long) value->u.object.values), 0)) )
-            {
-               return 0;
-            }
-
-            value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size;
-
-            value->u.object.length = 0;
-            break;
-
-         case json_string:
-
-            if (! (value->u.string.ptr = (json_char *) json_alloc
-               (state, (value->u.string.length + 1) * sizeof (json_char), 0)) )
-            {
-               return 0;
-            }
-
-            value->u.string.length = 0;
-            break;
-
-         default:
-            break;
-      };
-
-      return 1;
-   }
-
-   if (! (value = (json_value *) json_alloc
-         (state, sizeof (json_value) + state->settings.value_extra, 1)))
-   {
-      return 0;
-   }
-
-   if (!*root)
-      *root = value;
-
-   value->type = type;
-   value->parent = *top;
-
-   #ifdef JSON_TRACK_SOURCE
-      value->line = state->cur_line;
-      value->col = state->cur_col;
-   #endif
-
-   if (*alloc)
-      (*alloc)->_reserved.next_alloc = value;
-
-   *alloc = *top = value;
-
-   return 1;
-}
-
-#define whitespace \
-   case '\n': ++ state.cur_line;  state.cur_col = 0; \
-   case ' ': case '\t': case '\r'
-
-#define string_add(b)  \
-   do { if (!state.first_pass) string [string_length] = b;  ++ string_length; } while (0);
-
-#define line_and_col \
-   state.cur_line, state.cur_col
-
-static const long
-   flag_next             = 1 << 0,
-   flag_reproc           = 1 << 1,
-   flag_need_comma       = 1 << 2,
-   flag_seek_value       = 1 << 3, 
-   flag_escaped          = 1 << 4,
-   flag_string           = 1 << 5,
-   flag_need_colon       = 1 << 6,
-   flag_done             = 1 << 7,
-   flag_num_negative     = 1 << 8,
-   flag_num_zero         = 1 << 9,
-   flag_num_e            = 1 << 10,
-   flag_num_e_got_sign   = 1 << 11,
-   flag_num_e_negative   = 1 << 12,
-   flag_line_comment     = 1 << 13,
-   flag_block_comment    = 1 << 14;
-
-json_value * json_parse_ex (json_settings * settings,
-                            const json_char * json,
-                            size_t length,
-                            char * error_buf)
-{
-   json_char error [json_error_max];
-   const json_char * end;
-   json_value * top, * root, * alloc = 0;
-   json_state state = { 0 };
-   long flags;
-   long num_digits = 0, num_e = 0;
-   json_int_t num_fraction = 0;
-
-   /* Skip UTF-8 BOM
-    */
-   if (length >= 3 && ((unsigned char) json [0]) == 0xEF
-                   && ((unsigned char) json [1]) == 0xBB
-                   && ((unsigned char) json [2]) == 0xBF)
-   {
-      json += 3;
-      length -= 3;
-   }
-
-   error[0] = '\0';
-   end = (json + length);
-
-   memcpy (&state.settings, settings, sizeof (json_settings));
-
-   if (!state.settings.mem_alloc)
-      state.settings.mem_alloc = default_alloc;
-
-   if (!state.settings.mem_free)
-      state.settings.mem_free = default_free;
-
-   memset (&state.uint_max, 0xFF, sizeof (state.uint_max));
-   memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max));
-
-   state.uint_max -= 8; /* limit of how much can be added before next check */
-   state.ulong_max -= 8;
-
-   for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass)
-   {
-      json_uchar uchar;
-      unsigned char uc_b1, uc_b2, uc_b3, uc_b4;
-      json_char * string = 0;
-      unsigned int string_length = 0;
-
-      top = root = 0;
-      flags = flag_seek_value;
-
-      state.cur_line = 1;
-
-      for (state.ptr = json ;; ++ state.ptr)
-      {
-         json_char b = (state.ptr == end ? 0 : *state.ptr);
-         
-         if (flags & flag_string)
-         {
-            if (!b)
-            {  sprintf (error, "Unexpected EOF in string (at %d:%d)", line_and_col);
-               goto e_failed;
-            }
-
-            if (string_length > state.uint_max)
-               goto e_overflow;
-
-            if (flags & flag_escaped)
-            {
-               flags &= ~ flag_escaped;
-
-               switch (b)
-               {
-                  case 'b':  string_add ('\b');  break;
-                  case 'f':  string_add ('\f');  break;
-                  case 'n':  string_add ('\n');  break;
-                  case 'r':  string_add ('\r');  break;
-                  case 't':  string_add ('\t');  break;
-                  case 'u':
-
-                    if (end - state.ptr < 4 || 
-                        (uc_b1 = hex_value (*++ state.ptr)) == 0xFF ||
-                        (uc_b2 = hex_value (*++ state.ptr)) == 0xFF ||
-                        (uc_b3 = hex_value (*++ state.ptr)) == 0xFF ||
-                        (uc_b4 = hex_value (*++ state.ptr)) == 0xFF)
-                    {
-                        sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col);
-                        goto e_failed;
-                    }
-
-                    uc_b1 = (uc_b1 << 4) | uc_b2;
-                    uc_b2 = (uc_b3 << 4) | uc_b4;
-                    uchar = (uc_b1 << 8) | uc_b2;
-
-                    if ((uchar & 0xF800) == 0xD800) {
-                        json_uchar uchar2;
-                        
-                        if (end - state.ptr < 6 || (*++ state.ptr) != '\\' || (*++ state.ptr) != 'u' ||
-                            (uc_b1 = hex_value (*++ state.ptr)) == 0xFF ||
-                            (uc_b2 = hex_value (*++ state.ptr)) == 0xFF ||
-                            (uc_b3 = hex_value (*++ state.ptr)) == 0xFF ||
-                            (uc_b4 = hex_value (*++ state.ptr)) == 0xFF)
-                        {
-                            sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col);
-                            goto e_failed;
-                        }
-
-                        uc_b1 = (uc_b1 << 4) | uc_b2;
-                        uc_b2 = (uc_b3 << 4) | uc_b4;
-                        uchar2 = (uc_b1 << 8) | uc_b2;
-                        
-                        uchar = 0x010000 | ((uchar & 0x3FF) << 10) | (uchar2 & 0x3FF);
-                    }
-
-                    if (sizeof (json_char) >= sizeof (json_uchar) || (uchar <= 0x7F))
-                    {
-                       string_add ((json_char) uchar);
-                       break;
-                    }
-
-                    if (uchar <= 0x7FF)
-                    {
-                        if (state.first_pass)
-                           string_length += 2;
-                        else
-                        {  string [string_length ++] = 0xC0 | (uchar >> 6);
-                           string [string_length ++] = 0x80 | (uchar & 0x3F);
-                        }
-
-                        break;
-                    }
-
-                    if (uchar <= 0xFFFF) {
-                        if (state.first_pass)
-                           string_length += 3;
-                        else
-                        {  string [string_length ++] = 0xE0 | (uchar >> 12);
-                           string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F);
-                           string [string_length ++] = 0x80 | (uchar & 0x3F);
-                        }
-                        
-                        break;
-                    }
-
-                    if (state.first_pass)
-                       string_length += 4;
-                    else
-                    {  string [string_length ++] = 0xF0 | (uchar >> 18);
-                       string [string_length ++] = 0x80 | ((uchar >> 12) & 0x3F);
-                       string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F);
-                       string [string_length ++] = 0x80 | (uchar & 0x3F);
-                    }
-
-                    break;
-
-                  default:
-                     string_add (b);
-               };
-
-               continue;
-            }
-
-            if (b == '\\')
-            {
-               flags |= flag_escaped;
-               continue;
-            }
-
-            if (b == '"')
-            {
-               if (!state.first_pass)
-                  string [string_length] = 0;
-
-               flags &= ~ flag_string;
-               string = 0;
-
-               switch (top->type)
-               {
-                  case json_string:
-
-                     top->u.string.length = string_length;
-                     flags |= flag_next;
-
-                     break;
-
-                  case json_object:
-
-                     if (state.first_pass)
-                        (*(json_char **) &top->u.object.values) += string_length + 1;
-                     else
-                     {  
-                        top->u.object.values [top->u.object.length].name
-                           = (json_char *) top->_reserved.object_mem;
-
-                        top->u.object.values [top->u.object.length].name_length
-                           = string_length;
-
-                        (*(json_char **) &top->_reserved.object_mem) += string_length + 1;
-                     }
-
-                     flags |= flag_seek_value | flag_need_colon;
-                     continue;
-
-                  default:
-                     break;
-               };
-            }
-            else
-            {
-               string_add (b);
-               continue;
-            }
-         }
-
-         if (state.settings.settings & json_enable_comments)
-         {
-            if (flags & (flag_line_comment | flag_block_comment))
-            {
-               if (flags & flag_line_comment)
-               {
-                  if (b == '\r' || b == '\n' || !b)
-                  {
-                     flags &= ~ flag_line_comment;
-                     -- state.ptr;  /* so null can be reproc'd */
-                  }
-
-                  continue;
-               }
-
-               if (flags & flag_block_comment)
-               {
-                  if (!b)
-                  {  sprintf (error, "%d:%d: Unexpected EOF in block comment", line_and_col);
-                     goto e_failed;
-                  }
-
-                  if (b == '*' && state.ptr < (end - 1) && state.ptr [1] == '/')
-                  {
-                     flags &= ~ flag_block_comment;
-                     ++ state.ptr;  /* skip closing sequence */
-                  }
-
-                  continue;
-               }
-            }
-            else if (b == '/')
-            {
-               if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object)
-               {  sprintf (error, "%d:%d: Comment not allowed here", line_and_col);
-                  goto e_failed;
-               }
-
-               if (++ state.ptr == end)
-               {  sprintf (error, "%d:%d: EOF unexpected", line_and_col);
-                  goto e_failed;
-               }
-
-               switch (b = *state.ptr)
-               {
-                  case '/':
-                     flags |= flag_line_comment;
-                     continue;
-
-                  case '*':
-                     flags |= flag_block_comment;
-                     continue;
-
-                  default:
-                     sprintf (error, "%d:%d: Unexpected `%c` in comment opening sequence", line_and_col, b);
-                     goto e_failed;
-               };
-            }
-         }
-
-         if (flags & flag_done)
-         {
-            if (!b)
-               break;
-
-            switch (b)
-            {
-               whitespace:
-                  continue;
-
-               default:
-
-                  sprintf (error, "%d:%d: Trailing garbage: `%c`",
-                           state.cur_line, state.cur_col, b);
-
-                  goto e_failed;
-            };
-         }
-
-         if (flags & flag_seek_value)
-         {
-            switch (b)
-            {
-               whitespace:
-                  continue;
-
-               case ']':
-
-                  if (top && top->type == json_array)
-                     flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next;
-                  else
-                  {  sprintf (error, "%d:%d: Unexpected ]", line_and_col);
-                     goto e_failed;
-                  }
-
-                  break;
-
-               default:
-
-                  if (flags & flag_need_comma)
-                  {
-                     if (b == ',')
-                     {  flags &= ~ flag_need_comma;
-                        continue;
-                     }
-                     else
-                     {
-                        sprintf (error, "%d:%d: Expected , before %c",
-                                 state.cur_line, state.cur_col, b);
-
-                        goto e_failed;
-                     }
-                  }
-
-                  if (flags & flag_need_colon)
-                  {
-                     if (b == ':')
-                     {  flags &= ~ flag_need_colon;
-                        continue;
-                     }
-                     else
-                     { 
-                        sprintf (error, "%d:%d: Expected : before %c",
-                                 state.cur_line, state.cur_col, b);
-
-                        goto e_failed;
-                     }
-                  }
-
-                  flags &= ~ flag_seek_value;
-
-                  switch (b)
-                  {
-                     case '{':
-
-                        if (!new_value (&state, &top, &root, &alloc, json_object))
-                           goto e_alloc_failure;
-
-                        continue;
-
-                     case '[':
-
-                        if (!new_value (&state, &top, &root, &alloc, json_array))
-                           goto e_alloc_failure;
-
-                        flags |= flag_seek_value;
-                        continue;
-
-                     case '"':
-
-                        if (!new_value (&state, &top, &root, &alloc, json_string))
-                           goto e_alloc_failure;
-
-                        flags |= flag_string;
-
-                        string = top->u.string.ptr;
-                        string_length = 0;
-
-                        continue;
-
-                     case 't':
-
-                        if ((end - state.ptr) < 3 || *(++ state.ptr) != 'r' ||
-                            *(++ state.ptr) != 'u' || *(++ state.ptr) != 'e')
-                        {
-                           goto e_unknown_value;
-                        }
-
-                        if (!new_value (&state, &top, &root, &alloc, json_boolean))
-                           goto e_alloc_failure;
-
-                        top->u.boolean = 1;
-
-                        flags |= flag_next;
-                        break;
-
-                     case 'f':
-
-                        if ((end - state.ptr) < 4 || *(++ state.ptr) != 'a' ||
-                            *(++ state.ptr) != 'l' || *(++ state.ptr) != 's' ||
-                            *(++ state.ptr) != 'e')
-                        {
-                           goto e_unknown_value;
-                        }
-
-                        if (!new_value (&state, &top, &root, &alloc, json_boolean))
-                           goto e_alloc_failure;
-
-                        flags |= flag_next;
-                        break;
-
-                     case 'n':
-
-                        if ((end - state.ptr) < 3 || *(++ state.ptr) != 'u' ||
-                            *(++ state.ptr) != 'l' || *(++ state.ptr) != 'l')
-                        {
-                           goto e_unknown_value;
-                        }
-
-                        if (!new_value (&state, &top, &root, &alloc, json_null))
-                           goto e_alloc_failure;
-
-                        flags |= flag_next;
-                        break;
-
-                     default:
-
-                        if (isdigit (b) || b == '-')
-                        {
-                           if (!new_value (&state, &top, &root, &alloc, json_integer))
-                              goto e_alloc_failure;
-
-                           if (!state.first_pass)
-                           {
-                              while (isdigit (b) || b == '+' || b == '-'
-                                        || b == 'e' || b == 'E' || b == '.')
-                              {
-                                 if ( (++ state.ptr) == end)
-                                 {
-                                    b = 0;
-                                    break;
-                                 }
-
-                                 b = *state.ptr;
-                              }
-
-                              flags |= flag_next | flag_reproc;
-                              break;
-                           }
-
-                           flags &= ~ (flag_num_negative | flag_num_e |
-                                        flag_num_e_got_sign | flag_num_e_negative |
-                                           flag_num_zero);
-
-                           num_digits = 0;
-                           num_fraction = 0;
-                           num_e = 0;
-
-                           if (b != '-')
-                           {
-                              flags |= flag_reproc;
-                              break;
-                           }
-
-                           flags |= flag_num_negative;
-                           continue;
-                        }
-                        else
-                        {  sprintf (error, "%d:%d: Unexpected %c when seeking value", line_and_col, b);
-                           goto e_failed;
-                        }
-                  };
-            };
-         }
-         else
-         {
-            switch (top->type)
-            {
-            case json_object:
-               
-               switch (b)
-               {
-                  whitespace:
-                     continue;
-
-                  case '"':
-
-                     if (flags & flag_need_comma)
-                     {  sprintf (error, "%d:%d: Expected , before \"", line_and_col);
-                        goto e_failed;
-                     }
-
-                     flags |= flag_string;
-
-                     string = (json_char *) top->_reserved.object_mem;
-                     string_length = 0;
-
-                     break;
-                  
-                  case '}':
-
-                     flags = (flags & ~ flag_need_comma) | flag_next;
-                     break;
-
-                  case ',':
-
-                     if (flags & flag_need_comma)
-                     {
-                        flags &= ~ flag_need_comma;
-                        break;
-                     }
-
-                  default:
-                     sprintf (error, "%d:%d: Unexpected `%c` in object", line_and_col, b);
-                     goto e_failed;
-               };
-
-               break;
-
-            case json_integer:
-            case json_double:
-
-               if (isdigit (b))
-               {
-                  ++ num_digits;
-
-                  if (top->type == json_integer || flags & flag_num_e)
-                  {
-                     if (! (flags & flag_num_e))
-                     {
-                        if (flags & flag_num_zero)
-                        {  sprintf (error, "%d:%d: Unexpected `0` before `%c`", line_and_col, b);
-                           goto e_failed;
-                        }
-
-                        if (num_digits == 1 && b == '0')
-                           flags |= flag_num_zero;
-                     }
-                     else
-                     {
-                        flags |= flag_num_e_got_sign;
-                        num_e = (num_e * 10) + (b - '0');
-                        continue;
-                     }
-
-                     top->u.integer = (top->u.integer * 10) + (b - '0');
-                     continue;
-                  }
-
-                  num_fraction = (num_fraction * 10) + (b - '0');
-                  continue;
-               }
-
-               if (b == '+' || b == '-')
-               {
-                  if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign))
-                  {
-                     flags |= flag_num_e_got_sign;
-
-                     if (b == '-')
-                        flags |= flag_num_e_negative;
-
-                     continue;
-                  }
-               }
-               else if (b == '.' && top->type == json_integer)
-               {
-                  if (!num_digits)
-                  {  sprintf (error, "%d:%d: Expected digit before `.`", line_and_col);
-                     goto e_failed;
-                  }
-
-                  top->type = json_double;
-                  top->u.dbl = (double) top->u.integer;
-
-                  num_digits = 0;
-                  continue;
-               }
-
-               if (! (flags & flag_num_e))
-               {
-                  if (top->type == json_double)
-                  {
-                     if (!num_digits)
-                     {  sprintf (error, "%d:%d: Expected digit after `.`", line_and_col);
-                        goto e_failed;
-                     }
-
-                     top->u.dbl += ((double) num_fraction) / (pow (10.0, (double) num_digits));
-                  }
-
-                  if (b == 'e' || b == 'E')
-                  {
-                     flags |= flag_num_e;
-
-                     if (top->type == json_integer)
-                     {
-                        top->type = json_double;
-                        top->u.dbl = (double) top->u.integer;
-                     }
-
-                     num_digits = 0;
-                     flags &= ~ flag_num_zero;
-
-                     continue;
-                  }
-               }
-               else
-               {
-                  if (!num_digits)
-                  {  sprintf (error, "%d:%d: Expected digit after `e`", line_and_col);
-                     goto e_failed;
-                  }
-
-                  top->u.dbl *= pow (10.0, (double)
-                      (flags & flag_num_e_negative ? - num_e : num_e));
-               }
-
-               if (flags & flag_num_negative)
-               {
-                  if (top->type == json_integer)
-                     top->u.integer = - top->u.integer;
-                  else
-                     top->u.dbl = - top->u.dbl;
-               }
-
-               flags |= flag_next | flag_reproc;
-               break;
-
-            default:
-               break;
-            };
-         }
-
-         if (flags & flag_reproc)
-         {
-            flags &= ~ flag_reproc;
-            -- state.ptr;
-         }
-
-         if (flags & flag_next)
-         {
-            flags = (flags & ~ flag_next) | flag_need_comma;
-
-            if (!top->parent)
-            {
-               /* root value done */
-
-               flags |= flag_done;
-               continue;
-            }
-
-            if (top->parent->type == json_array)
-               flags |= flag_seek_value;
-               
-            if (!state.first_pass)
-            {
-               json_value * parent = top->parent;
-
-               switch (parent->type)
-               {
-                  case json_object:
-
-                     parent->u.object.values
-                        [parent->u.object.length].value = top;
-
-                     break;
-
-                  case json_array:
-
-                     parent->u.array.values
-                           [parent->u.array.length] = top;
-
-                     break;
-
-                  default:
-                     break;
-               };
-            }
-
-            if ( (++ top->parent->u.array.length) > state.uint_max)
-               goto e_overflow;
-
-            top = top->parent;
-
-            continue;
-         }
-      }
-
-      alloc = root;
-   }
-
-   return root;
-
-e_unknown_value:
-
-   sprintf (error, "%d:%d: Unknown value", line_and_col);
-   goto e_failed;
-
-e_alloc_failure:
-
-   strcpy (error, "Memory allocation failure");
-   goto e_failed;
-
-e_overflow:
-
-   sprintf (error, "%d:%d: Too long (caught overflow)", line_and_col);
-   goto e_failed;
-
-e_failed:
-
-   if (error_buf)
-   {
-      if (*error)
-         strcpy (error_buf, error);
-      else
-         strcpy (error_buf, "Unknown error");
-   }
-
-   if (state.first_pass)
-      alloc = root;
-
-   while (alloc)
-   {
-      top = alloc->_reserved.next_alloc;
-      state.settings.mem_free (alloc, state.settings.user_data);
-      alloc = top;
-   }
-
-   if (!state.first_pass)
-      json_value_free_ex (&state.settings, root);
-
-   return 0;
-}
-
-json_value * json_parse (const json_char * json, size_t length)
-{
-   json_settings settings = { 0 };
-   return json_parse_ex (&settings, json, length, 0);
-}
-
-void json_value_free_ex (json_settings * settings, json_value * value)
-{
-   json_value * cur_value;
-
-   if (!value)
-      return;
-
-   value->parent = 0;
-
-   while (value)
-   {
-      switch (value->type)
-      {
-         case json_array:
-
-            if (!value->u.array.length)
-            {
-               settings->mem_free (value->u.array.values, settings->user_data);
-               break;
-            }
-
-            value = value->u.array.values [-- value->u.array.length];
-            continue;
-
-         case json_object:
-
-            if (!value->u.object.length)
-            {
-               settings->mem_free (value->u.object.values, settings->user_data);
-               break;
-            }
-
-            value = value->u.object.values [-- value->u.object.length].value;
-            continue;
-
-         case json_string:
-
-            settings->mem_free (value->u.string.ptr, settings->user_data);
-            break;
-
-         default:
-            break;
-      };
-
-      cur_value = value;
-      value = value->parent;
-      settings->mem_free (cur_value, settings->user_data);
-   }
-}
-
-void json_value_free (json_value * value)
-{
-   json_settings settings = { 0 };
-   settings.mem_free = default_free;
-   json_value_free_ex (&settings, value);
-}
-

+ 0 - 283
ext/json-parser/json.h

@@ -1,283 +0,0 @@
-
-/* vim: set et ts=3 sw=3 sts=3 ft=c:
- *
- * Copyright (C) 2012, 2013, 2014 James McLaughlin et al.  All rights reserved.
- * https://github.com/udp/json-parser
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _JSON_H
-#define _JSON_H
-
-#ifndef json_char
-   #define json_char char
-#endif
-
-#ifndef json_int_t
-   #ifndef _MSC_VER
-      #include <inttypes.h>
-      #define json_int_t int64_t
-   #else
-      #define json_int_t __int64
-   #endif
-#endif
-
-#include <stdlib.h>
-
-#ifdef __cplusplus
-
-   #include <string.h>
-
-   extern "C"
-   {
-
-#endif
-
-typedef struct
-{
-   unsigned long max_memory;
-   int settings;
-
-   /* Custom allocator support (leave null to use malloc/free)
-    */
-
-   void * (* mem_alloc) (size_t, int zero, void * user_data);
-   void (* mem_free) (void *, void * user_data);
-
-   void * user_data;  /* will be passed to mem_alloc and mem_free */
-
-   size_t value_extra;  /* how much extra space to allocate for values? */
-
-} json_settings;
-
-#define json_enable_comments  0x01
-
-typedef enum
-{
-   json_none,
-   json_object,
-   json_array,
-   json_integer,
-   json_double,
-   json_string,
-   json_boolean,
-   json_null
-
-} json_type;
-
-extern const struct _json_value json_value_none;
-       
-typedef struct _json_object_entry
-{
-    json_char * name;
-    unsigned int name_length;
-    
-    struct _json_value * value;
-    
-} json_object_entry;
-
-typedef struct _json_value
-{
-   struct _json_value * parent;
-
-   json_type type;
-
-   union
-   {
-      int boolean;
-      json_int_t integer;
-      double dbl;
-
-      struct
-      {
-         unsigned int length;
-         json_char * ptr; /* null terminated */
-
-      } string;
-
-      struct
-      {
-         unsigned int length;
-
-         json_object_entry * values;
-
-         #if defined(__cplusplus) && __cplusplus >= 201103L
-         decltype(values) begin () const
-         {  return values;
-         }
-         decltype(values) end () const
-         {  return values + length;
-         }
-         #endif
-
-      } object;
-
-      struct
-      {
-         unsigned int length;
-         struct _json_value ** values;
-
-         #if defined(__cplusplus) && __cplusplus >= 201103L
-         decltype(values) begin () const
-         {  return values;
-         }
-         decltype(values) end () const
-         {  return values + length;
-         }
-         #endif
-
-      } array;
-
-   } u;
-
-   union
-   {
-      struct _json_value * next_alloc;
-      void * object_mem;
-
-   } _reserved;
-
-   #ifdef JSON_TRACK_SOURCE
-
-      /* Location of the value in the source JSON
-       */
-      unsigned int line, col;
-
-   #endif
-
-
-   /* Some C++ operator sugar */
-
-   #ifdef __cplusplus
-
-      public:
-
-         inline _json_value ()
-         {  memset (this, 0, sizeof (_json_value));
-         }
-
-         inline const struct _json_value &operator [] (int index) const
-         {
-            if (type != json_array || index < 0
-                     || ((unsigned int) index) >= u.array.length)
-            {
-               return json_value_none;
-            }
-
-            return *u.array.values [index];
-         }
-
-         inline const struct _json_value &operator [] (const char * index) const
-         { 
-            if (type != json_object)
-               return json_value_none;
-
-            for (unsigned int i = 0; i < u.object.length; ++ i)
-               if (!strcmp (u.object.values [i].name, index))
-                  return *u.object.values [i].value;
-
-            return json_value_none;
-         }
-
-         inline operator const char * () const
-         {  
-            switch (type)
-            {
-               case json_string:
-                  return u.string.ptr;
-
-               default:
-                  return "";
-            };
-         }
-
-         inline operator json_int_t () const
-         {  
-            switch (type)
-            {
-               case json_integer:
-                  return u.integer;
-
-               case json_double:
-                  return (json_int_t) u.dbl;
-
-               default:
-                  return 0;
-            };
-         }
-
-         inline operator bool () const
-         {  
-            if (type != json_boolean)
-               return false;
-
-            return u.boolean != 0;
-         }
-
-         inline operator double () const
-         {  
-            switch (type)
-            {
-               case json_integer:
-                  return (double) u.integer;
-
-               case json_double:
-                  return u.dbl;
-
-               default:
-                  return 0;
-            };
-         }
-
-   #endif
-
-} json_value;
-       
-json_value * json_parse (const json_char * json,
-                         size_t length);
-
-#define json_error_max 128
-json_value * json_parse_ex (json_settings * settings,
-                            const json_char * json,
-                            size_t length,
-                            char * error);
-
-void json_value_free (json_value *);
-
-
-/* Not usually necessary, unless you used a custom mem_alloc and now want to
- * use a custom mem_free.
- */
-void json_value_free_ex (json_settings * settings,
-                         json_value *);
-
-
-#ifdef __cplusplus
-   } /* extern "C" */
-#endif
-
-#endif
-
-

+ 14 - 0
include/ZeroTierOne.h

@@ -468,6 +468,11 @@ enum ZT_VirtualNetworkType
 	ZT_NETWORK_TYPE_PUBLIC = 1
 };
 
+/*
+ - TEE : should use a field to indicate how many bytes of each packet max are TEE'd
+ - Controller : web hooks for auth, optional required re-auth? or auth for a period of time? auto-expiring auth?
+*/
+
 /**
  * The type of a virtual network rules table entry
  *
@@ -721,6 +726,15 @@ typedef struct
 			uint32_t id;
 			uint32_t value;
 		} tag;
+
+		/**
+		 * Destinations for TEE and REDIRECT
+		 */
+		struct {
+			uint64_t address;
+			uint32_t flags;
+			uint16_t length;
+		} fwd;
 	} v;
 } ZT_VirtualNetworkRule;
 

+ 1 - 1
make-freebsd.mk

@@ -6,7 +6,7 @@ DEFS=
 LIBS=
 
 include objects.mk
-OBJS+=osdep/BSDEthernetTap.o ext/lz4/lz4.o ext/json-parser/json.o ext/http-parser/http_parser.o
+OBJS+=osdep/BSDEthernetTap.o ext/lz4/lz4.o ext/http-parser/http_parser.o
 
 # "make official" is a shortcut for this
 ifeq ($(ZT_OFFICIAL_RELEASE),1)

+ 0 - 6
make-linux.mk

@@ -51,12 +51,6 @@ else
 	LDLIBS+=-lhttp_parser
 	DEFS+=-DZT_USE_SYSTEM_HTTP_PARSER
 endif
-ifeq ($(wildcard /usr/include/json-parser/json.h),)
-	OBJS+=ext/json-parser/json.o
-else
-	LDLIBS+=-ljsonparser
-	DEFS+=-DZT_USE_SYSTEM_JSON_PARSER
-endif
 
 ifeq ($(ZT_USE_MINIUPNPC),1)
 	OBJS+=osdep/PortMapper.o

+ 1 - 1
make-mac.mk

@@ -11,7 +11,7 @@ LIBS=
 ARCH_FLAGS=-arch x86_64
 
 include objects.mk
-OBJS+=osdep/OSXEthernetTap.o ext/lz4/lz4.o ext/json-parser/json.o ext/http-parser/http_parser.o
+OBJS+=osdep/OSXEthernetTap.o ext/lz4/lz4.o ext/http-parser/http_parser.o
 
 # Disable codesign since open source users will not have ZeroTier's certs
 CODESIGN=echo

+ 9 - 0
node/Capability.hpp

@@ -181,6 +181,11 @@ public:
 					break;
 				case ZT_NETWORK_RULE_ACTION_TEE:
 				case ZT_NETWORK_RULE_ACTION_REDIRECT:
+					b.append((uint8_t)14);
+					b.append((uint64_t)rules[i].v.fwd.address);
+					b.append((uint32_t)rules[i].v.fwd.flags);
+					b.append((uint16_t)rules[i].v.fwd.length);
+					break;
 				case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
 				case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
 					b.append((uint8_t)5);
@@ -266,6 +271,10 @@ public:
 					break;
 				case ZT_NETWORK_RULE_ACTION_TEE:
 				case ZT_NETWORK_RULE_ACTION_REDIRECT:
+					rules[ruleCount].v.fwd.address = b.template at<uint64_t>(p);
+					rules[ruleCount].v.fwd.flags = b.template at<uint32_t>(p + 8);
+					rules[ruleCount].v.fwd.length = b.template at<uint16_t>(p + 12);
+					break;
 				case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
 				case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
 					rules[ruleCount].v.zt = Address(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH).toInt();

+ 3 - 3
node/Network.cpp

@@ -154,13 +154,13 @@ static int _doZtFilter(
 				break;
 			case ZT_NETWORK_RULE_ACTION_TEE:
 			case ZT_NETWORK_RULE_ACTION_REDIRECT: {
-				Packet outp(Address(rules[rn].v.zt),RR->identity.address(),Packet::VERB_EXT_FRAME);
+				Packet outp(Address(rules[rn].v.fwd.address),RR->identity.address(),Packet::VERB_EXT_FRAME);
 				outp.append(nconf.networkId);
-				outp.append((uint8_t)((rt == ZT_NETWORK_RULE_ACTION_REDIRECT) ? 0x04 : 0x02));
+				outp.append((uint8_t)( ((rt == ZT_NETWORK_RULE_ACTION_REDIRECT) ? 0x04 : 0x02) | (inbound ? 0x08 : 0x00) ));
 				macDest.appendTo(outp);
 				macSource.appendTo(outp);
 				outp.append((uint16_t)etherType);
-				outp.append(frameData,frameLen);
+				outp.append(frameData,(rules[rn].v.fwd.length != 0) ? ((frameLen < (unsigned int)rules[rn].v.fwd.length) ? frameLen : (unsigned int)rules[rn].v.fwd.length) : frameLen);
 				outp.compress();
 				RR->sw->send(outp,true);
 

+ 1 - 0
node/Packet.hpp

@@ -657,6 +657,7 @@ public:
 		 *   0x01 - Certificate of network membership attached (DEPRECATED)
 		 *   0x02 - Packet is a TEE'd packet
 		 *   0x04 - Packet is a REDIRECT'ed packet
+		 *   0x08 - TEE/REDIRECT'ed packet is on inbound side of connection
 		 *
 		 * An extended frame carries full MAC addressing, making them a
 		 * superset of VERB_FRAME. They're used for bridging or when we

+ 100 - 188
one.cpp

@@ -48,16 +48,12 @@
 
 #include <string>
 #include <stdexcept>
+#include <iostream>
+#include <sstream>
 
 #include "version.h"
 #include "include/ZeroTierOne.h"
 
-#ifdef ZT_USE_SYSTEM_JSON_PARSER
-#include <json-parser/json.h>
-#else
-#include "ext/json-parser/json.h"
-#endif
-
 #include "node/Identity.hpp"
 #include "node/CertificateOfMembership.hpp"
 #include "node/Utils.hpp"
@@ -68,6 +64,8 @@
 
 #include "service/OneService.hpp"
 
+#include "ext/json/json.hpp"
+
 #define ZT_PID_PATH "zerotier-one.pid"
 
 using namespace ZeroTier;
@@ -283,221 +281,135 @@ static int cli(int argc,char **argv)
 			return 1;
 		}
 	} else if ((command == "info")||(command == "status")) {
-		unsigned int scode = Http::GET(
-			1024 * 1024 * 16,
-			60000,
-			(const struct sockaddr *)&addr,
-			"/status",
-			requestHeaders,
-			responseHeaders,
-			responseBody);
+		const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/status",requestHeaders,responseHeaders,responseBody);
+
+		nlohmann::json j;
+		try {
+			j = nlohmann::json::parse(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) {
+			std::ostringstream out;
 			if (json) {
-				printf("%s",cliFixJsonCRs(responseBody).c_str());
-				return 0;
+				out << j.dump(2) << ZT_EOL_S;
 			} else {
-				json_value *j = json_parse(responseBody.c_str(),responseBody.length());
-				bool good = false;
-				if (j) {
-					if (j->type == json_object) {
-						const char *address = (const char *)0;
-						bool online = false;
-						const char *version = (const char *)0;
-						for(unsigned int k=0;k<j->u.object.length;++k) {
-							if ((!strcmp(j->u.object.values[k].name,"address"))&&(j->u.object.values[k].value->type == json_string))
-								address = j->u.object.values[k].value->u.string.ptr;
-							else if ((!strcmp(j->u.object.values[k].name,"version"))&&(j->u.object.values[k].value->type == json_string))
-								version = j->u.object.values[k].value->u.string.ptr;
-							else if ((!strcmp(j->u.object.values[k].name,"online"))&&(j->u.object.values[k].value->type == json_boolean))
-								online = (j->u.object.values[k].value->u.boolean != 0);
-						}
-						if ((address)&&(version)) {
-							printf("200 info %s %s %s" ZT_EOL_S,address,(online ? "ONLINE" : "OFFLINE"),version);
-							good = true;
-						}
-					}
-					json_value_free(j);
-				}
-				if (good) {
-					return 0;
-				} else {
-					printf("%u %s invalid JSON response" ZT_EOL_S,scode,command.c_str());
-					return 1;
-				}
+				if (j.is_object())
+					out << "200 info " << j["address"].get<std::string>() << " " << j["version"].get<std::string>() << " " << ((j["tcpFallbackActive"]) ? "TUNNELED" : ((j["online"]) ? "ONLINE" : "OFFLINE")) << ZT_EOL_S;
 			}
+			printf("%s",out.str().c_str());
+			return 0;
 		} else {
 			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
 			return 1;
 		}
 	} else if (command == "listpeers") {
-		unsigned int scode = Http::GET(
-			1024 * 1024 * 16,
-			60000,
-			(const struct sockaddr *)&addr,
-			"/peer",
-			requestHeaders,
-			responseHeaders,
-			responseBody);
+		const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/peer",requestHeaders,responseHeaders,responseBody);
+
+		nlohmann::json j;
+		try {
+			j = nlohmann::json::parse(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) {
+			std::ostringstream out;
 			if (json) {
-				printf("%s",cliFixJsonCRs(responseBody).c_str());
-				return 0;
+				out << j.dump(2) << ZT_EOL_S;
 			} else {
-				printf("200 listpeers <ztaddr> <paths> <latency> <version> <role>" ZT_EOL_S);
-				json_value *j = json_parse(responseBody.c_str(),responseBody.length());
-				if (j) {
-					if (j->type == json_array) {
-						for(unsigned int p=0;p<j->u.array.length;++p) {
-							json_value *jp = j->u.array.values[p];
-							if (jp->type == json_object) {
-								const char *address = (const char *)0;
-								std::string paths;
-								int64_t latency = 0;
-								int64_t versionMajor = -1,versionMinor = -1,versionRev = -1;
-								const char *role = (const char *)0;
-								for(unsigned int k=0;k<jp->u.object.length;++k) {
-									if ((!strcmp(jp->u.object.values[k].name,"address"))&&(jp->u.object.values[k].value->type == json_string))
-										address = jp->u.object.values[k].value->u.string.ptr;
-									else if ((!strcmp(jp->u.object.values[k].name,"versionMajor"))&&(jp->u.object.values[k].value->type == json_integer))
-										versionMajor = jp->u.object.values[k].value->u.integer;
-									else if ((!strcmp(jp->u.object.values[k].name,"versionMinor"))&&(jp->u.object.values[k].value->type == json_integer))
-										versionMinor = jp->u.object.values[k].value->u.integer;
-									else if ((!strcmp(jp->u.object.values[k].name,"versionRev"))&&(jp->u.object.values[k].value->type == json_integer))
-										versionRev = jp->u.object.values[k].value->u.integer;
-									else if ((!strcmp(jp->u.object.values[k].name,"role"))&&(jp->u.object.values[k].value->type == json_string))
-										role = jp->u.object.values[k].value->u.string.ptr;
-									else if ((!strcmp(jp->u.object.values[k].name,"latency"))&&(jp->u.object.values[k].value->type == json_integer))
-										latency = jp->u.object.values[k].value->u.integer;
-									else if ((!strcmp(jp->u.object.values[k].name,"paths"))&&(jp->u.object.values[k].value->type == json_array)) {
-										for(unsigned int pp=0;pp<jp->u.object.values[k].value->u.array.length;++pp) {
-											json_value *jpath = jp->u.object.values[k].value->u.array.values[pp];
-											if (jpath->type == json_object) {
-												const char *paddr = (const char *)0;
-												int64_t lastSend = 0;
-												int64_t lastReceive = 0;
-												bool preferred = false;
-												bool active = false;
-												for(unsigned int kk=0;kk<jpath->u.object.length;++kk) {
-													if ((!strcmp(jpath->u.object.values[kk].name,"address"))&&(jpath->u.object.values[kk].value->type == json_string))
-														paddr = jpath->u.object.values[kk].value->u.string.ptr;
-													else if ((!strcmp(jpath->u.object.values[kk].name,"lastSend"))&&(jpath->u.object.values[kk].value->type == json_integer))
-														lastSend = jpath->u.object.values[kk].value->u.integer;
-													else if ((!strcmp(jpath->u.object.values[kk].name,"lastReceive"))&&(jpath->u.object.values[kk].value->type == json_integer))
-														lastReceive = jpath->u.object.values[kk].value->u.integer;
-													else if ((!strcmp(jpath->u.object.values[kk].name,"preferred"))&&(jpath->u.object.values[kk].value->type == json_boolean))
-														preferred = (jpath->u.object.values[kk].value->u.boolean != 0);
-													else if ((!strcmp(jpath->u.object.values[kk].name,"active"))&&(jpath->u.object.values[kk].value->type == json_boolean))
-														active = (jpath->u.object.values[kk].value->u.boolean != 0);
-												}
-												if ((paddr)&&(active)) {
-													int64_t now = (int64_t)OSUtils::now();
-													if (lastSend > 0)
-														lastSend = now - lastSend;
-													if (lastReceive > 0)
-														lastReceive = now - lastReceive;
-													char pathtmp[256];
-													Utils::snprintf(pathtmp,sizeof(pathtmp),"%s;%lld;%lld;%s",
-														paddr,
-														lastSend,
-														lastReceive,
-														(preferred ? "preferred" : "active"));
-													if (paths.length())
-														paths.push_back(',');
-													paths.append(pathtmp);
-												}
-											}
-										}
-									}
-								}
-								if ((address)&&(role)) {
-									char verstr[64];
-									if ((versionMajor >= 0)&&(versionMinor >= 0)&&(versionRev >= 0))
-										Utils::snprintf(verstr,sizeof(verstr),"%lld.%lld.%lld",versionMajor,versionMinor,versionRev);
-									else {
-										verstr[0] = '-';
-										verstr[1] = (char)0;
-									}
-									printf("200 listpeers %s %s %lld %s %s" ZT_EOL_S,address,(paths.length()) ? paths.c_str() : "-",(long long)latency,verstr,role);
+				out << "200 listpeers <ztaddr> <path> <latency> <version> <role>" << ZT_EOL_S;
+				if (j.is_array()) {
+					for(unsigned long k=0;k<j.size();++k) {
+						auto p = j[k];
+						std::string bestPath;
+						auto paths = p["paths"];
+						if (paths.is_array()) {
+							for(unsigned long i=0;i<paths.size();++i) {
+								auto path = paths[i];
+								if (path["preferred"]) {
+									char tmp[256];
+									std::string addr = path["address"];
+									const uint64_t now = OSUtils::now();
+									Utils::snprintf(tmp,sizeof(tmp),"%s;%llu;%llu",addr.c_str(),now - (uint64_t)path["lastSend"],now - (uint64_t)path["lastReceive"]);
+									bestPath = tmp;
+									break;
 								}
 							}
 						}
+						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) {
+							Utils::snprintf(ver,sizeof(ver),"%lld.%lld.%lld",vmaj,vmin,vrev);
+						} else {
+							ver[0] = '-';
+							ver[1] = (char)0;
+						}
+						out << "200 listpeers " << p["address"].get<std::string>() << " " << bestPath << " " << p["latency"] << " " << ver << " " << p["role"].get<std::string>() << ZT_EOL_S;
 					}
-					json_value_free(j);
 				}
-				return 0;
 			}
+			printf("%s",out.str().c_str());
+			return 0;
 		} else {
 			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
 			return 1;
 		}
 	} else if (command == "listnetworks") {
-		unsigned int scode = Http::GET(
-			1024 * 1024 * 16,
-			60000,
-			(const struct sockaddr *)&addr,
-			"/network",
-			requestHeaders,
-			responseHeaders,
-			responseBody);
+		const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/network",requestHeaders,responseHeaders,responseBody);
+
+		nlohmann::json j;
+		try {
+			j = nlohmann::json::parse(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) {
+			std::ostringstream out;
 			if (json) {
-				printf("%s",cliFixJsonCRs(responseBody).c_str());
-				return 0;
+				out << j.dump(2) << ZT_EOL_S;
 			} else {
-				printf("200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>" ZT_EOL_S);
-				json_value *j = json_parse(responseBody.c_str(),responseBody.length());
-				if (j) {
-					if (j->type == json_array) {
-						for(unsigned int p=0;p<j->u.array.length;++p) {
-							json_value *jn = j->u.array.values[p];
-							if (jn->type == json_object) {
-								const char *nwid = (const char *)0;
-								const char *name = "";
-								const char *mac = (const char *)0;
-								const char *status = (const char *)0;
-								const char *type = (const char *)0;
-								const char *portDeviceName = "";
-								std::string ips;
-								for(unsigned int k=0;k<jn->u.object.length;++k) {
-									if ((!strcmp(jn->u.object.values[k].name,"nwid"))&&(jn->u.object.values[k].value->type == json_string))
-										nwid = jn->u.object.values[k].value->u.string.ptr;
-									else if ((!strcmp(jn->u.object.values[k].name,"name"))&&(jn->u.object.values[k].value->type == json_string))
-										name = jn->u.object.values[k].value->u.string.ptr;
-									else if ((!strcmp(jn->u.object.values[k].name,"mac"))&&(jn->u.object.values[k].value->type == json_string))
-										mac = jn->u.object.values[k].value->u.string.ptr;
-									else if ((!strcmp(jn->u.object.values[k].name,"status"))&&(jn->u.object.values[k].value->type == json_string))
-										status = jn->u.object.values[k].value->u.string.ptr;
-									else if ((!strcmp(jn->u.object.values[k].name,"type"))&&(jn->u.object.values[k].value->type == json_string))
-										type = jn->u.object.values[k].value->u.string.ptr;
-									else if ((!strcmp(jn->u.object.values[k].name,"portDeviceName"))&&(jn->u.object.values[k].value->type == json_string))
-										portDeviceName = jn->u.object.values[k].value->u.string.ptr;
-									else if ((!strcmp(jn->u.object.values[k].name,"assignedAddresses"))&&(jn->u.object.values[k].value->type == json_array)) {
-										for(unsigned int a=0;a<jn->u.object.values[k].value->u.array.length;++a) {
-											json_value *aa = jn->u.object.values[k].value->u.array.values[a];
-											if (aa->type == json_string) {
-												if (ips.length())
-													ips.push_back(',');
-												ips.append(aa->u.string.ptr);
-											}
-										}
+				out << "200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>" << ZT_EOL_S;
+				if (j.is_array()) {
+					for(unsigned long i=0;i<j.size();++i) {
+						auto n = j[i];
+						if (n.is_object()) {
+							std::string aa;
+							auto assignedAddresses = n["assignedAddresses"];
+							if (assignedAddresses.is_array()) {
+								for(unsigned long j=0;j<assignedAddresses.size();++j) {
+									auto addr = assignedAddresses[j];
+									if (addr.is_string()) {
+										if (aa.length() > 0) aa.push_back(',');
+										aa.append(addr);
 									}
 								}
-								if ((nwid)&&(mac)&&(status)&&(type)) {
-									printf("200 listnetworks %s %s %s %s %s %s %s" ZT_EOL_S,
-										nwid,
-										(((name)&&(name[0])) ? name : "-"),
-										mac,
-										status,
-										type,
-										(((portDeviceName)&&(portDeviceName[0])) ? portDeviceName : "-"),
-										((ips.length() > 0) ? ips.c_str() : "-"));
-								}
 							}
+							if (aa.length() == 0) aa = "-";
+							out << "200 listnetworks " << n["nwid"].get<std::string>() << " " << n["name"].get<std::string>() << " " << n["mac"].get<std::string>() << " " << n["status"].get<std::string>() << " " << n["type"].get<std::string>() << " " << n["portDeviceName"].get<std::string>() << " " << aa << ZT_EOL_S;
 						}
 					}
-					json_value_free(j);
 				}
 			}
+			printf("%s",out.str().c_str());
+			return 0;
 		} else {
 			printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
 			return 1;

+ 12 - 21
service/ControlPlane.cpp

@@ -28,11 +28,7 @@
 #include "../ext/http-parser/http_parser.h"
 #endif
 
-#ifdef ZT_USE_SYSTEM_JSON_PARSER
-#include <json-parser/json.h>
-#else
-#include "../ext/json-parser/json.h"
-#endif
+#include "../ext/json/json.hpp"
 
 #include "../controller/EmbeddedNetworkController.hpp"
 
@@ -519,23 +515,18 @@ unsigned int ControlPlane::handleRequest(
 								OneService::NetworkSettings localSettings;
 								_svc->getNetworkSettings(nws->networks[i].nwid,localSettings);
 
-								json_value *j = json_parse(body.c_str(),body.length());
-								if (j) {
-									if (j->type == json_object) {
-										for(unsigned int k=0;k<j->u.object.length;++k) {
-											if (!strcmp(j->u.object.values[k].name,"allowManaged")) {
-												if (j->u.object.values[k].value->type == json_boolean)
-													localSettings.allowManaged = (j->u.object.values[k].value->u.boolean != 0);
-											} else if (!strcmp(j->u.object.values[k].name,"allowGlobal")) {
-												if (j->u.object.values[k].value->type == json_boolean)
-													localSettings.allowGlobal = (j->u.object.values[k].value->u.boolean != 0);
-											} else if (!strcmp(j->u.object.values[k].name,"allowDefault")) {
-												if (j->u.object.values[k].value->type == json_boolean)
-													localSettings.allowDefault = (j->u.object.values[k].value->u.boolean != 0);
-											}
-										}
+								try {
+									nlohmann::json j(nlohmann::json::parse(body));
+									if (j.is_object()) {
+										auto allowManaged = j["allowManaged"];
+										if (allowManaged.is_boolean()) localSettings.allowManaged = (bool)allowManaged;
+										auto allowGlobal = j["allowGlobal"];
+										if (allowGlobal.is_boolean()) localSettings.allowGlobal = (bool)allowGlobal;
+										auto allowDefault = j["allowDefault"];
+										if (allowDefault.is_boolean()) localSettings.allowDefault = (bool)allowDefault;
 									}
-									json_value_free(j);
+								} catch ( ... ) {
+									// discard invalid JSON
 								}
 
 								_svc->setNetworkSettings(nws->networks[i].nwid,localSettings);