Browse Source

Merge my adamierymenko-dev into the new master that incorporates Raspberry Pi build changes in order to keep everything in sync.

Adam Ierymenko 12 years ago
parent
commit
63fa4a684d
84 changed files with 2955 additions and 15364 deletions
  1. 0 6
      AUTHORS.txt
  2. 1 5
      Makefile.linux
  3. 9 9
      Makefile.mac
  4. 124 0
      cli.cpp
  5. 0 41
      ext/http-parser/AUTHORS
  6. 0 4
      ext/http-parser/CONTRIBUTIONS
  7. 0 23
      ext/http-parser/LICENSE-MIT
  8. 0 180
      ext/http-parser/README.md
  9. 0 156
      ext/http-parser/contrib/parsertrace.c
  10. 0 44
      ext/http-parser/contrib/url_parser.c
  11. 0 2175
      ext/http-parser/http_parser.c
  12. 0 111
      ext/http-parser/http_parser.gyp
  13. 0 304
      ext/http-parser/http_parser.h
  14. 0 3425
      ext/http-parser/test.c
  15. 0 252
      ext/huffandpuff/huffman.c
  16. 0 52
      ext/huffandpuff/huffman.h
  17. 0 24
      ext/jsoncpp/include/json/autolink.h
  18. 0 96
      ext/jsoncpp/include/json/config.h
  19. 0 49
      ext/jsoncpp/include/json/features.h
  20. 0 44
      ext/jsoncpp/include/json/forwards.h
  21. 0 15
      ext/jsoncpp/include/json/json.h
  22. 0 214
      ext/jsoncpp/include/json/reader.h
  23. 0 1103
      ext/jsoncpp/include/json/value.h
  24. 0 185
      ext/jsoncpp/include/json/writer.h
  25. 0 130
      ext/jsoncpp/src/json_batchallocator.h
  26. 0 456
      ext/jsoncpp/src/json_internalarray.inl
  27. 0 615
      ext/jsoncpp/src/json_internalmap.inl
  28. 0 880
      ext/jsoncpp/src/json_reader.cpp
  29. 0 93
      ext/jsoncpp/src/json_tool.h
  30. 0 1829
      ext/jsoncpp/src/json_value.cpp
  31. 0 299
      ext/jsoncpp/src/json_valueiterator.inl
  32. 0 838
      ext/jsoncpp/src/json_writer.cpp
  33. 1 1
      idtool.cpp
  34. 7 14
      main.cpp
  35. 47 0
      makekeypair.cpp
  36. 7 0
      netconf-service/Makefile
  37. 83 0
      netconf-service/netconf-test.cpp
  38. 345 0
      netconf-service/netconf.cpp
  39. 132 54
      node/Address.hpp
  40. 0 94
      node/BlobArray.hpp
  41. 9 38
      node/Constants.hpp
  42. 1 3
      node/Defaults.cpp
  43. 0 10
      node/Defaults.hpp
  44. 2 2
      node/Demarc.cpp
  45. 3 0
      node/Demarc.hpp
  46. 207 0
      node/Dictionary.hpp
  47. 8 4
      node/EllipticCurveKey.hpp
  48. 16 4
      node/EllipticCurveKeyPair.cpp
  49. 2 0
      node/EllipticCurveKeyPair.hpp
  50. 7 7
      node/EthernetTap.cpp
  51. 216 85
      node/Filter.cpp
  52. 61 10
      node/Filter.hpp
  53. 0 323
      node/Http.cpp
  54. 0 129
      node/Http.hpp
  55. 8 4
      node/Identity.cpp
  56. 4 4
      node/Identity.hpp
  57. 14 2
      node/Multicaster.hpp
  58. 119 4
      node/Network.cpp
  59. 305 43
      node/Network.hpp
  60. 200 78
      node/Node.cpp
  61. 43 10
      node/Node.hpp
  62. 179 134
      node/NodeConfig.cpp
  63. 65 25
      node/NodeConfig.hpp
  64. 0 159
      node/Pack.cpp
  65. 0 141
      node/Pack.hpp
  66. 4 0
      node/Packet.cpp
  67. 72 6
      node/Packet.hpp
  68. 88 9
      node/PacketDecoder.cpp
  69. 3 0
      node/PacketDecoder.hpp
  70. 14 7
      node/RuntimeEnvironment.hpp
  71. 241 0
      node/Service.cpp
  72. 129 0
      node/Service.hpp
  73. 5 5
      node/Switch.cpp
  74. 1 11
      node/Thread.cpp
  75. 0 5
      node/Thread.hpp
  76. 16 4
      node/Topology.cpp
  77. 8 29
      node/Topology.hpp
  78. 7 2
      node/UdpSocket.cpp
  79. 2 0
      node/UdpSocket.hpp
  80. 44 2
      node/Utils.cpp
  81. 40 30
      node/Utils.hpp
  82. 1 7
      objects.mk
  83. 0 182
      packtool.cpp
  84. 55 26
      selftest.cpp

+ 0 - 6
AUTHORS.txt

@@ -16,12 +16,6 @@ ZeroTier One includes or links with the following third party software:
  * LZ4 compression algorithm by Yann Collet (BSD license)
    http://code.google.com/p/lz4/
 
- * JsonCPP by Baptiste Lepilleur (public domain)
-   http://jsoncpp.sourceforge.net
-
- * http-parser, a simple C http parser library (MIT license)
-   https://github.com/joyent/http-parser
-
  * OpenSSL libcrypto (BSD-style OpenSSL license)
    http://www.openssl.org/
 

+ 1 - 5
Makefile.linux

@@ -21,7 +21,7 @@ CXXFLAGS=$(CFLAGS) -fno-rtti
 # separate binaries for the RedHat and Debian universes to distribute via
 # auto-update. This way we get one Linux binary for all systems of a given
 # architecture.
-LIBS=ext/bin/libcrypto/linux-$(ARCH)/libcrypto.a
+LIBS=ext/bin/libcrypto/linux-$(ARCH)/libcrypto.a -lm
 
 include objects.mk
 
@@ -39,10 +39,6 @@ idtool:	$(OBJS)
 	$(CXX) $(CXXFLAGS) -o zerotier-idtool idtool.cpp $(OBJS) $(LIBS)
 	$(STRIP) zerotier-idtool
 
-packtool:	$(OBJS)
-	$(CXX) $(CXXFLAGS) -o zerotier-packtool packtool.cpp $(OBJS) $(LIBS)
-	$(STRIP) zerotier-packtool
-
 launcher:
 	$(CC) -Os -o zerotier-launcher launcher.c
 	$(STRIP) zerotier-launcher

+ 9 - 9
Makefile.mac

@@ -13,19 +13,20 @@ STRIP=strip
 #STRIP=echo
 
 CXXFLAGS=$(CFLAGS) -fno-rtti
-
-# We statically link against libcrypto since Apple has apparently decided
-# to deprecate it and may remove it in future OS releases.
-LIBS=ext/bin/libcrypto/mac-x86_combined/libcrypto.a
+LIBS=-lcrypto -lm
 
 include objects.mk
 
-all: one launcher mac-tap
+all: one cli launcher mac-tap
 
 one:	$(OBJS)
 	$(CXX) $(CXXFLAGS) -o zerotier-one main.cpp $(OBJS) $(LIBS)
 	$(STRIP) zerotier-one
 
+cli:	$(OBJS)
+	$(CXX) $(CXXFLAGS) -o zerotier-cli cli.cpp $(OBJS) $(LIBS)
+	$(STRIP) zerotier-cli
+
 selftest: $(OBJS)
 	$(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.cpp $(OBJS) $(LIBS)
 	$(STRIP) zerotier-selftest
@@ -34,10 +35,6 @@ idtool: $(OBJS)
 	$(CXX) $(CXXFLAGS) -o zerotier-idtool idtool.cpp $(OBJS) $(LIBS)
 	$(STRIP) zerotier-idtool
 
-packtool: $(OBJS)
-	$(CXX) $(CXXFLAGS) -o zerotier-packtool packtool.cpp $(OBJS) $(LIBS)
-	$(STRIP) zerotier-packtool
-
 mac-tap: FORCE
 	cd mac-tap/tuntap ; make tap.kext
 
@@ -55,6 +52,9 @@ launcher-fakebin:
 	$(CC) $(CFLAGS) -DZEROTIER_FAKE_VERSION_MAJOR=1 -DZEROTIER_FAKE_VERSION_MINOR=2 -DZEROTIER_FAKE_VERSION_REV
 	$(CC) $(CFLAGS) -DZEROTIER_FAKE_VERSION_MAJOR=1 -DZEROTIER_FAKE_VERSION_MINOR=2 -DZEROTIER_FAKE_VERSION_REV
 
+makekeypair: $(OBJS)
+	$(CXX) $(CXXFLAGS) -o zerotier-makekeypair makekeypair.cpp $(OBJS) $(LIBS)
+
 clean:
 	rm -rf *.dSYM
 	rm -f $(OBJS) zerotier-*

+ 124 - 0
cli.cpp

@@ -0,0 +1,124 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013  ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __WINDOWS__
+#include <unistd.h>
+#endif
+
+#include "node/Node.hpp"
+#include "node/Constants.hpp"
+#include "node/Utils.hpp"
+#include "node/Thread.hpp"
+
+using namespace ZeroTier;
+
+static void printHelp(FILE *out,const char *exename)
+{
+	fprintf(out,"Usage: %s [-switches] <command>"ZT_EOL_S,exename);
+	fprintf(out,ZT_EOL_S);
+	fprintf(out,"Switches:"ZT_EOL_S);
+	fprintf(out,"  -t<token>        - Specify token on command line"ZT_EOL_S);
+	fprintf(out,"  -T<file>         - Read token from file"ZT_EOL_S);
+	fprintf(out,ZT_EOL_S);
+	fprintf(out,"Use the 'help' command to get help from ZeroTier One itself."ZT_EOL_S);
+}
+
+static volatile uint64_t lastResultTime = 0ULL;
+static volatile unsigned int numResults = 0;
+
+static void resultHandler(void *arg,unsigned long id,const char *line)
+{
+	lastResultTime = Utils::now();
+	++numResults;
+	fprintf(stdout,"%s"ZT_EOL_S,line);
+}
+
+int main(int argc,char **argv)
+{
+	if (argc <= 1) {
+		printHelp(stdout,argv[0]);
+		return -1;
+	}
+
+	std::string authToken;
+
+	for(int i=1;i<argc;++i) {
+		if (argv[i][0] == '-') {
+			if (strlen(argv[i]) <= 1) {
+				printHelp(stdout,argv[0]);
+				return -1;
+			}
+			switch(argv[i][1]) {
+				case 't':
+					authToken.assign(argv[i] + 2);
+					break;
+				case 'T':
+					if (!Utils::readFile(argv[i] + 2,authToken)) {
+						fprintf(stdout,"FATAL ERROR: unable to read token from '%s'"ZT_EOL_S,argv[i] + 2);
+						return -2;
+					}
+					break;
+				default:
+					return -1;
+			}
+		}
+	}
+
+	if (!authToken.length()) {
+		const char *home = getenv("HOME");
+		if (home) {
+			std::string dotZeroTierAuthToken(home);
+			dotZeroTierAuthToken.push_back(ZT_PATH_SEPARATOR);
+			dotZeroTierAuthToken.append(".zerotierOneAuthToken");
+			if (!Utils::readFile(dotZeroTierAuthToken.c_str(),authToken)) {
+				fprintf(stdout,"FATAL ERROR: no token specified on command line and could not read '%s'"ZT_EOL_S,dotZeroTierAuthToken.c_str());
+				return -2;
+			}
+		}
+	}
+	if (!authToken.length()) {
+		fprintf(stdout,"FATAL ERROR: could not find auth token"ZT_EOL_S);
+		return -2;
+	}
+
+	Node::LocalClient(authToken.c_str(),&resultHandler,(void *)0);
+
+	lastResultTime = Utils::now();
+	while ((Utils::now() - lastResultTime) < 300)
+		Thread::sleep(50);
+
+	if (!numResults) {
+		fprintf(stdout,"ERROR: no results received. Is ZeroTier One running?"ZT_EOL_S);
+		return -1;
+	}
+
+	return 0;
+}

+ 0 - 41
ext/http-parser/AUTHORS

@@ -1,41 +0,0 @@
-# Authors ordered by first contribution.
-Ryan Dahl <[email protected]>
-Jeremy Hinegardner <[email protected]>
-Sergey Shepelev <[email protected]>
-Joe Damato <[email protected]>
-tomika <[email protected]>
-Phoenix Sol <[email protected]>
-Cliff Frey <[email protected]>
-Ewen Cheslack-Postava <[email protected]>
-Santiago Gala <[email protected]>
-Tim Becker <[email protected]>
-Jeff Terrace <[email protected]>
-Ben Noordhuis <[email protected]>
-Nathan Rajlich <[email protected]>
-Mark Nottingham <[email protected]>
-Aman Gupta <[email protected]>
-Tim Becker <[email protected]>
-Sean Cunningham <[email protected]>
-Peter Griess <[email protected]>
-Salman Haq <[email protected]>
-Cliff Frey <[email protected]>
-Jon Kolb <[email protected]>
-Fouad Mardini <[email protected]>
-Paul Querna <[email protected]>
-Felix Geisendörfer <[email protected]>
-koichik <[email protected]>
-Andre Caron <[email protected]>
-Ivo Raisr <[email protected]>
-James McLaughlin <[email protected]>
-David Gwynne <[email protected]>
-Thomas LE ROUX <[email protected]>
-Randy Rizun <[email protected]>
-Andre Louis Caron <[email protected]>
-Simon Zimmermann <[email protected]>
-Erik Dubbelboer <[email protected]>
-Martell Malone <[email protected]>
-Bertrand Paquet <[email protected]>
-BogDan Vatra <[email protected]>
-Peter Faiman <[email protected]>
-Corey Richardson <[email protected]>
-Tóth Tamás <[email protected]>

+ 0 - 4
ext/http-parser/CONTRIBUTIONS

@@ -1,4 +0,0 @@
-Contributors must agree to the Contributor License Agreement before patches
-can be accepted.
-
-http://spreadsheets2.google.com/viewform?hl=en&formkey=dDJXOGUwbzlYaWM4cHN1MERwQS1CSnc6MQ

+ 0 - 23
ext/http-parser/LICENSE-MIT

@@ -1,23 +0,0 @@
-http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
-Igor Sysoev.
-
-Additional changes are licensed under the same terms as NGINX and
-copyright Joyent, Inc. and other Node contributors. All rights reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to
-deal in the Software without restriction, including without limitation the
-rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-sell copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-IN THE SOFTWARE. 

+ 0 - 180
ext/http-parser/README.md

@@ -1,180 +0,0 @@
-HTTP Parser
-===========
-
-[![Build Status](https://travis-ci.org/joyent/http-parser.png?branch=master)](https://travis-ci.org/joyent/http-parser)
-
-This is a parser for HTTP messages written in C. It parses both requests and
-responses. The parser is designed to be used in performance HTTP
-applications. It does not make any syscalls nor allocations, it does not
-buffer data, it can be interrupted at anytime. Depending on your
-architecture, it only requires about 40 bytes of data per message
-stream (in a web server that is per connection).
-
-Features:
-
-  * No dependencies
-  * Handles persistent streams (keep-alive).
-  * Decodes chunked encoding.
-  * Upgrade support
-  * Defends against buffer overflow attacks.
-
-The parser extracts the following information from HTTP messages:
-
-  * Header fields and values
-  * Content-Length
-  * Request method
-  * Response status code
-  * Transfer-Encoding
-  * HTTP version
-  * Request URL
-  * Message body
-
-
-Usage
------
-
-One `http_parser` object is used per TCP connection. Initialize the struct
-using `http_parser_init()` and set the callbacks. That might look something
-like this for a request parser:
-
-    http_parser_settings settings;
-    settings.on_url = my_url_callback;
-    settings.on_header_field = my_header_field_callback;
-    /* ... */
-
-    http_parser *parser = malloc(sizeof(http_parser));
-    http_parser_init(parser, HTTP_REQUEST);
-    parser->data = my_socket;
-
-When data is received on the socket execute the parser and check for errors.
-
-    size_t len = 80*1024, nparsed;
-    char buf[len];
-    ssize_t recved;
-
-    recved = recv(fd, buf, len, 0);
-
-    if (recved < 0) {
-      /* Handle error. */
-    }
-
-    /* Start up / continue the parser.
-     * Note we pass recved==0 to signal that EOF has been recieved.
-     */
-    nparsed = http_parser_execute(parser, &settings, buf, recved);
-
-    if (parser->upgrade) {
-      /* handle new protocol */
-    } else if (nparsed != recved) {
-      /* Handle error. Usually just close the connection. */
-    }
-
-HTTP needs to know where the end of the stream is. For example, sometimes
-servers send responses without Content-Length and expect the client to
-consume input (for the body) until EOF. To tell http_parser about EOF, give
-`0` as the forth parameter to `http_parser_execute()`. Callbacks and errors
-can still be encountered during an EOF, so one must still be prepared
-to receive them.
-
-Scalar valued message information such as `status_code`, `method`, and the
-HTTP version are stored in the parser structure. This data is only
-temporally stored in `http_parser` and gets reset on each new message. If
-this information is needed later, copy it out of the structure during the
-`headers_complete` callback.
-
-The parser decodes the transfer-encoding for both requests and responses
-transparently. That is, a chunked encoding is decoded before being sent to
-the on_body callback.
-
-
-The Special Problem of Upgrade
-------------------------------
-
-HTTP supports upgrading the connection to a different protocol. An
-increasingly common example of this is the Web Socket protocol which sends
-a request like
-
-        GET /demo HTTP/1.1
-        Upgrade: WebSocket
-        Connection: Upgrade
-        Host: example.com
-        Origin: http://example.com
-        WebSocket-Protocol: sample
-
-followed by non-HTTP data.
-
-(See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more
-information the Web Socket protocol.)
-
-To support this, the parser will treat this as a normal HTTP message without a
-body. Issuing both on_headers_complete and on_message_complete callbacks. However
-http_parser_execute() will stop parsing at the end of the headers and return.
-
-The user is expected to check if `parser->upgrade` has been set to 1 after
-`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
-offset by the return value of `http_parser_execute()`.
-
-
-Callbacks
----------
-
-During the `http_parser_execute()` call, the callbacks set in
-`http_parser_settings` will be executed. The parser maintains state and
-never looks behind, so buffering the data is not necessary. If you need to
-save certain data for later usage, you can do that from the callbacks.
-
-There are two types of callbacks:
-
-* notification `typedef int (*http_cb) (http_parser*);`
-    Callbacks: on_message_begin, on_headers_complete, on_message_complete.
-* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
-    Callbacks: (requests only) on_uri,
-               (common) on_header_field, on_header_value, on_body;
-
-Callbacks must return 0 on success. Returning a non-zero value indicates
-error to the parser, making it exit immediately.
-
-In case you parse HTTP message in chunks (i.e. `read()` request line
-from socket, parse, read half headers, parse, etc) your data callbacks
-may be called more than once. Http-parser guarantees that data pointer is only
-valid for the lifetime of callback. You can also `read()` into a heap allocated
-buffer to avoid copying memory around if this fits your application.
-
-Reading headers may be a tricky task if you read/parse headers partially.
-Basically, you need to remember whether last header callback was field or value
-and apply following logic:
-
-    (on_header_field and on_header_value shortened to on_h_*)
-     ------------------------ ------------ --------------------------------------------
-    | State (prev. callback) | Callback   | Description/action                         |
-     ------------------------ ------------ --------------------------------------------
-    | nothing (first call)   | on_h_field | Allocate new buffer and copy callback data |
-    |                        |            | into it                                    |
-     ------------------------ ------------ --------------------------------------------
-    | value                  | on_h_field | New header started.                        |
-    |                        |            | Copy current name,value buffers to headers |
-    |                        |            | list and allocate new buffer for new name  |
-     ------------------------ ------------ --------------------------------------------
-    | field                  | on_h_field | Previous name continues. Reallocate name   |
-    |                        |            | buffer and append callback data to it      |
-     ------------------------ ------------ --------------------------------------------
-    | field                  | on_h_value | Value for current header started. Allocate |
-    |                        |            | new buffer and copy callback data to it    |
-     ------------------------ ------------ --------------------------------------------
-    | value                  | on_h_value | Value continues. Reallocate value buffer   |
-    |                        |            | and append callback data to it             |
-     ------------------------ ------------ --------------------------------------------
-
-
-Parsing URLs
-------------
-
-A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`.
-Users of this library may wish to use it to parse URLs constructed from
-consecutive `on_url` callbacks.
-
-See examples of reading in headers:
-
-* [partial example](http://gist.github.com/155877) in C
-* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C
-* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript

+ 0 - 156
ext/http-parser/contrib/parsertrace.c

@@ -1,156 +0,0 @@
-/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
- *
- * Additional changes are licensed under the same terms as NGINX and
- * copyright Joyent, Inc. and other Node contributors. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-
-/* Dump what the parser finds to stdout as it happen */
-
-#include "http_parser.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-int on_message_begin(http_parser* _) {
-  (void)_;
-  printf("\n***MESSAGE BEGIN***\n\n");
-  return 0;
-}
-
-int on_headers_complete(http_parser* _) {
-  (void)_;
-  printf("\n***HEADERS COMPLETE***\n\n");
-  return 0;
-}
-
-int on_message_complete(http_parser* _) {
-  (void)_;
-  printf("\n***MESSAGE COMPLETE***\n\n");
-  return 0;
-}
-
-int on_url(http_parser* _, const char* at, size_t length) {
-  (void)_;
-  printf("Url: %.*s\n", (int)length, at);
-  return 0;
-}
-
-int on_header_field(http_parser* _, const char* at, size_t length) {
-  (void)_;
-  printf("Header field: %.*s\n", (int)length, at);
-  return 0;
-}
-
-int on_header_value(http_parser* _, const char* at, size_t length) {
-  (void)_;
-  printf("Header value: %.*s\n", (int)length, at);
-  return 0;
-}
-
-int on_body(http_parser* _, const char* at, size_t length) {
-  (void)_;
-  printf("Body: %.*s\n", (int)length, at);
-  return 0;
-}
-
-void usage(const char* name) {
-  fprintf(stderr,
-          "Usage: %s $type $filename\n"
-          "  type: -x, where x is one of {r,b,q}\n"
-          "  parses file as a Response, reQuest, or Both\n",
-          name);
-  exit(EXIT_FAILURE);
-}
-
-int main(int argc, char* argv[]) {
-  enum http_parser_type file_type;
-
-  if (argc != 3) {
-    usage(argv[0]);
-  }
-
-  char* type = argv[1];
-  if (type[0] != '-') {
-    usage(argv[0]);
-  }
-
-  switch (type[1]) {
-    /* in the case of "-", type[1] will be NUL */
-    case 'r':
-      file_type = HTTP_RESPONSE;
-      break;
-    case 'q':
-      file_type = HTTP_REQUEST;
-      break;
-    case 'b':
-      file_type = HTTP_BOTH;
-      break;
-    default:
-      usage(argv[0]);
-  }
-
-  char* filename = argv[2];
-  FILE* file = fopen(filename, "r");
-  if (file == NULL) {
-    perror("fopen");
-    return EXIT_FAILURE;
-  }
-
-  fseek(file, 0, SEEK_END);
-  long file_length = ftell(file);
-  if (file_length == -1) {
-    perror("ftell");
-    return EXIT_FAILURE;
-  }
-  fseek(file, 0, SEEK_SET);
-
-  char* data = malloc(file_length);
-  if (fread(data, 1, file_length, file) != (size_t)file_length) {
-    fprintf(stderr, "couldn't read entire file\n");
-    free(data);
-    return EXIT_FAILURE;
-  }
-
-  http_parser_settings settings;
-  memset(&settings, 0, sizeof(settings));
-  settings.on_message_begin = on_message_begin;
-  settings.on_url = on_url;
-  settings.on_header_field = on_header_field;
-  settings.on_header_value = on_header_value;
-  settings.on_headers_complete = on_headers_complete;
-  settings.on_body = on_body;
-  settings.on_message_complete = on_message_complete;
-
-  http_parser parser;
-  http_parser_init(&parser, file_type);
-  size_t nparsed = http_parser_execute(&parser, &settings, data, file_length);
-  free(data);
-
-  if (nparsed != (size_t)file_length) {
-    fprintf(stderr,
-            "Error: %s (%s)\n",
-            http_errno_description(HTTP_PARSER_ERRNO(&parser)),
-            http_errno_name(HTTP_PARSER_ERRNO(&parser)));
-    return EXIT_FAILURE;
-  }
-
-  return EXIT_SUCCESS;
-}

+ 0 - 44
ext/http-parser/contrib/url_parser.c

@@ -1,44 +0,0 @@
-#include "http_parser.h"
-#include <stdio.h>
-#include <string.h>
-
-void
-dump_url (const char *url, const struct http_parser_url *u)
-{
-  unsigned int i;
-
-  printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
-  for (i = 0; i < UF_MAX; i++) {
-    if ((u->field_set & (1 << i)) == 0) {
-      printf("\tfield_data[%u]: unset\n", i);
-      continue;
-    }
-
-    printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n",
-           i,
-           u->field_data[i].off,
-           u->field_data[i].len,
-           u->field_data[i].len,
-           url + u->field_data[i].off);
-  }
-}
-
-int main(int argc, char ** argv) {
-  if (argc != 3) {
-    printf("Syntax : %s connect|get url\n", argv[0]);
-    return 1;
-  }
-  struct http_parser_url u;
-  int len = strlen(argv[2]);
-  int connect = strcmp("connect", argv[1]) == 0 ? 1 : 0;
-  printf("Parsing %s, connect %d\n", argv[2], connect);
-
-  int result = http_parser_parse_url(argv[2], len, connect, &u);
-  if (result != 0) {
-    printf("Parse error : %d\n", result);
-    return result;
-  }
-  printf("Parse ok, result : \n");
-  dump_url(argv[2], &u);
-  return 0;
-}

+ 0 - 2175
ext/http-parser/http_parser.c

@@ -1,2175 +0,0 @@
-/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
- *
- * Additional changes are licensed under the same terms as NGINX and
- * copyright Joyent, Inc. and other Node contributors. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-#include "http_parser.h"
-#include <assert.h>
-#include <stddef.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
-
-#ifndef ULLONG_MAX
-# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
-#endif
-
-#ifndef MIN
-# define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
-#ifndef ARRAY_SIZE
-# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
-#endif
-
-#ifndef BIT_AT
-# define BIT_AT(a, i)                                                \
-  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \
-   (1 << ((unsigned int) (i) & 7))))
-#endif
-
-#ifndef ELEM_AT
-# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
-#endif
-
-#define SET_ERRNO(e)                                                 \
-do {                                                                 \
-  parser->http_errno = (e);                                          \
-} while(0)
-
-
-/* Run the notify callback FOR, returning ER if it fails */
-#define CALLBACK_NOTIFY_(FOR, ER)                                    \
-do {                                                                 \
-  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
-                                                                     \
-  if (settings->on_##FOR) {                                          \
-    if (0 != settings->on_##FOR(parser)) {                           \
-      SET_ERRNO(HPE_CB_##FOR);                                       \
-    }                                                                \
-                                                                     \
-    /* We either errored above or got paused; get out */             \
-    if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {                       \
-      return (ER);                                                   \
-    }                                                                \
-  }                                                                  \
-} while (0)
-
-/* Run the notify callback FOR and consume the current byte */
-#define CALLBACK_NOTIFY(FOR)            CALLBACK_NOTIFY_(FOR, p - data + 1)
-
-/* Run the notify callback FOR and don't consume the current byte */
-#define CALLBACK_NOTIFY_NOADVANCE(FOR)  CALLBACK_NOTIFY_(FOR, p - data)
-
-/* Run data callback FOR with LEN bytes, returning ER if it fails */
-#define CALLBACK_DATA_(FOR, LEN, ER)                                 \
-do {                                                                 \
-  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
-                                                                     \
-  if (FOR##_mark) {                                                  \
-    if (settings->on_##FOR) {                                        \
-      if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) {      \
-        SET_ERRNO(HPE_CB_##FOR);                                     \
-      }                                                              \
-                                                                     \
-      /* We either errored above or got paused; get out */           \
-      if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {                     \
-        return (ER);                                                 \
-      }                                                              \
-    }                                                                \
-    FOR##_mark = NULL;                                               \
-  }                                                                  \
-} while (0)
-  
-/* Run the data callback FOR and consume the current byte */
-#define CALLBACK_DATA(FOR)                                           \
-    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
-
-/* Run the data callback FOR and don't consume the current byte */
-#define CALLBACK_DATA_NOADVANCE(FOR)                                 \
-    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
-
-/* Set the mark FOR; non-destructive if mark is already set */
-#define MARK(FOR)                                                    \
-do {                                                                 \
-  if (!FOR##_mark) {                                                 \
-    FOR##_mark = p;                                                  \
-  }                                                                  \
-} while (0)
-
-
-#define PROXY_CONNECTION "proxy-connection"
-#define CONNECTION "connection"
-#define CONTENT_LENGTH "content-length"
-#define TRANSFER_ENCODING "transfer-encoding"
-#define UPGRADE "upgrade"
-#define CHUNKED "chunked"
-#define KEEP_ALIVE "keep-alive"
-#define CLOSE "close"
-
-
-static const char *method_strings[] =
-  {
-#define XX(num, name, string) #string,
-  HTTP_METHOD_MAP(XX)
-#undef XX
-  };
-
-
-/* Tokens as defined by rfc 2616. Also lowercases them.
- *        token       = 1*<any CHAR except CTLs or separators>
- *     separators     = "(" | ")" | "<" | ">" | "@"
- *                    | "," | ";" | ":" | "\" | <">
- *                    | "/" | "[" | "]" | "?" | "="
- *                    | "{" | "}" | SP | HT
- */
-static const char tokens[256] = {
-/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
-        0,       0,       0,       0,       0,       0,       0,       0,
-/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
-        0,       0,       0,       0,       0,       0,       0,       0,
-/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
-        0,       0,       0,       0,       0,       0,       0,       0,
-/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
-        0,       0,       0,       0,       0,       0,       0,       0,
-/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
-        0,      '!',      0,      '#',     '$',     '%',     '&',    '\'',
-/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
-        0,       0,      '*',     '+',      0,      '-',     '.',      0,
-/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
-       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
-/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
-       '8',     '9',      0,       0,       0,       0,       0,       0,
-/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
-        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
-/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
-       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
-/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
-       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
-/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
-       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
-/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
-       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
-/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
-       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
-/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
-       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
-/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
-       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };
-
-
-static const int8_t unhex[256] =
-  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
-  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  };
-
-
-#if HTTP_PARSER_STRICT
-# define T(v) 0
-#else
-# define T(v) v
-#endif
-
-
-static const uint8_t normal_url_char[32] = {
-/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
-        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
-/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
-        0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,
-/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
-        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
-/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
-        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
-/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
-        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,
-/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
-        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
-        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
-        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,
-/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
-        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
-        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
-        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
-        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
-        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
-        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
-        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
-/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
-        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };
-
-#undef T
-
-enum state
-  { s_dead = 1 /* important that this is > 0 */
-
-  , s_start_req_or_res
-  , s_res_or_resp_H
-  , s_start_res
-  , s_res_H
-  , s_res_HT
-  , s_res_HTT
-  , s_res_HTTP
-  , s_res_first_http_major
-  , s_res_http_major
-  , s_res_first_http_minor
-  , s_res_http_minor
-  , s_res_first_status_code
-  , s_res_status_code
-  , s_res_status
-  , s_res_line_almost_done
-
-  , s_start_req
-
-  , s_req_method
-  , s_req_spaces_before_url
-  , s_req_schema
-  , s_req_schema_slash
-  , s_req_schema_slash_slash
-  , s_req_server_start
-  , s_req_server
-  , s_req_server_with_at
-  , s_req_path
-  , s_req_query_string_start
-  , s_req_query_string
-  , s_req_fragment_start
-  , s_req_fragment
-  , s_req_http_start
-  , s_req_http_H
-  , s_req_http_HT
-  , s_req_http_HTT
-  , s_req_http_HTTP
-  , s_req_first_http_major
-  , s_req_http_major
-  , s_req_first_http_minor
-  , s_req_http_minor
-  , s_req_line_almost_done
-
-  , s_header_field_start
-  , s_header_field
-  , s_header_value_start
-  , s_header_value
-  , s_header_value_lws
-
-  , s_header_almost_done
-
-  , s_chunk_size_start
-  , s_chunk_size
-  , s_chunk_parameters
-  , s_chunk_size_almost_done
-
-  , s_headers_almost_done
-  , s_headers_done
-
-  /* Important: 's_headers_done' must be the last 'header' state. All
-   * states beyond this must be 'body' states. It is used for overflow
-   * checking. See the PARSING_HEADER() macro.
-   */
-
-  , s_chunk_data
-  , s_chunk_data_almost_done
-  , s_chunk_data_done
-
-  , s_body_identity
-  , s_body_identity_eof
-
-  , s_message_done
-  };
-
-
-#define PARSING_HEADER(state) (state <= s_headers_done)
-
-
-enum header_states
-  { h_general = 0
-  , h_C
-  , h_CO
-  , h_CON
-
-  , h_matching_connection
-  , h_matching_proxy_connection
-  , h_matching_content_length
-  , h_matching_transfer_encoding
-  , h_matching_upgrade
-
-  , h_connection
-  , h_content_length
-  , h_transfer_encoding
-  , h_upgrade
-
-  , h_matching_transfer_encoding_chunked
-  , h_matching_connection_keep_alive
-  , h_matching_connection_close
-
-  , h_transfer_encoding_chunked
-  , h_connection_keep_alive
-  , h_connection_close
-  };
-
-enum http_host_state
-  {
-    s_http_host_dead = 1
-  , s_http_userinfo_start
-  , s_http_userinfo
-  , s_http_host_start
-  , s_http_host_v6_start
-  , s_http_host
-  , s_http_host_v6
-  , s_http_host_v6_end
-  , s_http_host_port_start
-  , s_http_host_port
-};
-
-/* Macros for character classes; depends on strict-mode  */
-#define CR                  '\r'
-#define LF                  '\n'
-#define LOWER(c)            (unsigned char)(c | 0x20)
-#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')
-#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')
-#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))
-#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
-#define IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \
-  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
-  (c) == ')')
-#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
-  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
-  (c) == '$' || (c) == ',')
-
-#if HTTP_PARSER_STRICT
-#define TOKEN(c)            (tokens[(unsigned char)c])
-#define IS_URL_CHAR(c)      (BIT_AT(normal_url_char, (unsigned char)c))
-#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
-#else
-#define TOKEN(c)            ((c == ' ') ? ' ' : tokens[(unsigned char)c])
-#define IS_URL_CHAR(c)                                                         \
-  (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
-#define IS_HOST_CHAR(c)                                                        \
-  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
-#endif
-
-
-#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
-
-
-#if HTTP_PARSER_STRICT
-# define STRICT_CHECK(cond)                                          \
-do {                                                                 \
-  if (cond) {                                                        \
-    SET_ERRNO(HPE_STRICT);                                           \
-    goto error;                                                      \
-  }                                                                  \
-} while (0)
-# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
-#else
-# define STRICT_CHECK(cond)
-# define NEW_MESSAGE() start_state
-#endif
-
-
-/* Map errno values to strings for human-readable output */
-#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
-static struct {
-  const char *name;
-  const char *description;
-} http_strerror_tab[] = {
-  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
-};
-#undef HTTP_STRERROR_GEN
-
-int http_message_needs_eof(const http_parser *parser);
-
-/* Our URL parser.
- *
- * This is designed to be shared by http_parser_execute() for URL validation,
- * hence it has a state transition + byte-for-byte interface. In addition, it
- * is meant to be embedded in http_parser_parse_url(), which does the dirty
- * work of turning state transitions URL components for its API.
- *
- * This function should only be invoked with non-space characters. It is
- * assumed that the caller cares about (and can detect) the transition between
- * URL and non-URL states by looking for these.
- */
-static enum state
-parse_url_char(enum state s, const char ch)
-{
-  if (ch == ' ' || ch == '\r' || ch == '\n') {
-    return s_dead;
-  }
-
-#if HTTP_PARSER_STRICT
-  if (ch == '\t' || ch == '\f') {
-    return s_dead;
-  }
-#endif
-
-  switch (s) {
-    case s_req_spaces_before_url:
-      /* Proxied requests are followed by scheme of an absolute URI (alpha).
-       * All methods except CONNECT are followed by '/' or '*'.
-       */
-
-      if (ch == '/' || ch == '*') {
-        return s_req_path;
-      }
-
-      if (IS_ALPHA(ch)) {
-        return s_req_schema;
-      }
-
-      break;
-
-    case s_req_schema:
-      if (IS_ALPHA(ch)) {
-        return s;
-      }
-
-      if (ch == ':') {
-        return s_req_schema_slash;
-      }
-
-      break;
-
-    case s_req_schema_slash:
-      if (ch == '/') {
-        return s_req_schema_slash_slash;
-      }
-
-      break;
-
-    case s_req_schema_slash_slash:
-      if (ch == '/') {
-        return s_req_server_start;
-      }
-
-      break;
-
-    case s_req_server_with_at:
-      if (ch == '@') {
-        return s_dead;
-      }
-
-    /* FALLTHROUGH */
-    case s_req_server_start:
-    case s_req_server:
-      if (ch == '/') {
-        return s_req_path;
-      }
-
-      if (ch == '?') {
-        return s_req_query_string_start;
-      }
-
-      if (ch == '@') {
-        return s_req_server_with_at;
-      }
-
-      if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
-        return s_req_server;
-      }
-
-      break;
-
-    case s_req_path:
-      if (IS_URL_CHAR(ch)) {
-        return s;
-      }
-
-      switch (ch) {
-        case '?':
-          return s_req_query_string_start;
-
-        case '#':
-          return s_req_fragment_start;
-      }
-
-      break;
-
-    case s_req_query_string_start:
-    case s_req_query_string:
-      if (IS_URL_CHAR(ch)) {
-        return s_req_query_string;
-      }
-
-      switch (ch) {
-        case '?':
-          /* allow extra '?' in query string */
-          return s_req_query_string;
-
-        case '#':
-          return s_req_fragment_start;
-      }
-
-      break;
-
-    case s_req_fragment_start:
-      if (IS_URL_CHAR(ch)) {
-        return s_req_fragment;
-      }
-
-      switch (ch) {
-        case '?':
-          return s_req_fragment;
-
-        case '#':
-          return s;
-      }
-
-      break;
-
-    case s_req_fragment:
-      if (IS_URL_CHAR(ch)) {
-        return s;
-      }
-
-      switch (ch) {
-        case '?':
-        case '#':
-          return s;
-      }
-
-      break;
-
-    default:
-      break;
-  }
-
-  /* We should never fall out of the switch above unless there's an error */
-  return s_dead;
-}
-
-size_t http_parser_execute (http_parser *parser,
-                            const http_parser_settings *settings,
-                            const char *data,
-                            size_t len)
-{
-  char c, ch;
-  int8_t unhex_val;
-  const char *p = data;
-  const char *header_field_mark = 0;
-  const char *header_value_mark = 0;
-  const char *url_mark = 0;
-  const char *body_mark = 0;
-
-  /* We're in an error state. Don't bother doing anything. */
-  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
-    return 0;
-  }
-
-  if (len == 0) {
-    switch (parser->state) {
-      case s_body_identity_eof:
-        /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
-         * we got paused.
-         */
-        CALLBACK_NOTIFY_NOADVANCE(message_complete);
-        return 0;
-
-      case s_dead:
-      case s_start_req_or_res:
-      case s_start_res:
-      case s_start_req:
-        return 0;
-
-      default:
-        SET_ERRNO(HPE_INVALID_EOF_STATE);
-        return 1;
-    }
-  }
-
-
-  if (parser->state == s_header_field)
-    header_field_mark = data;
-  if (parser->state == s_header_value)
-    header_value_mark = data;
-  switch (parser->state) {
-  case s_req_path:
-  case s_req_schema:
-  case s_req_schema_slash:
-  case s_req_schema_slash_slash:
-  case s_req_server_start:
-  case s_req_server:
-  case s_req_server_with_at:
-  case s_req_query_string_start:
-  case s_req_query_string:
-  case s_req_fragment_start:
-  case s_req_fragment:
-    url_mark = data;
-    break;
-  }
-
-  for (p=data; p != data + len; p++) {
-    ch = *p;
-
-    if (PARSING_HEADER(parser->state)) {
-      ++parser->nread;
-      /* Buffer overflow attack */
-      if (parser->nread > HTTP_MAX_HEADER_SIZE) {
-        SET_ERRNO(HPE_HEADER_OVERFLOW);
-        goto error;
-      }
-    }
-
-    reexecute_byte:
-    switch (parser->state) {
-
-      case s_dead:
-        /* this state is used after a 'Connection: close' message
-         * the parser will error out if it reads another message
-         */
-        if (ch == CR || ch == LF)
-          break;
-
-        SET_ERRNO(HPE_CLOSED_CONNECTION);
-        goto error;
-
-      case s_start_req_or_res:
-      {
-        if (ch == CR || ch == LF)
-          break;
-        parser->flags = 0;
-        parser->content_length = ULLONG_MAX;
-
-        if (ch == 'H') {
-          parser->state = s_res_or_resp_H;
-
-          CALLBACK_NOTIFY(message_begin);
-        } else {
-          parser->type = HTTP_REQUEST;
-          parser->state = s_start_req;
-          goto reexecute_byte;
-        }
-
-        break;
-      }
-
-      case s_res_or_resp_H:
-        if (ch == 'T') {
-          parser->type = HTTP_RESPONSE;
-          parser->state = s_res_HT;
-        } else {
-          if (ch != 'E') {
-            SET_ERRNO(HPE_INVALID_CONSTANT);
-            goto error;
-          }
-
-          parser->type = HTTP_REQUEST;
-          parser->method = HTTP_HEAD;
-          parser->index = 2;
-          parser->state = s_req_method;
-        }
-        break;
-
-      case s_start_res:
-      {
-        parser->flags = 0;
-        parser->content_length = ULLONG_MAX;
-
-        switch (ch) {
-          case 'H':
-            parser->state = s_res_H;
-            break;
-
-          case CR:
-          case LF:
-            break;
-
-          default:
-            SET_ERRNO(HPE_INVALID_CONSTANT);
-            goto error;
-        }
-
-        CALLBACK_NOTIFY(message_begin);
-        break;
-      }
-
-      case s_res_H:
-        STRICT_CHECK(ch != 'T');
-        parser->state = s_res_HT;
-        break;
-
-      case s_res_HT:
-        STRICT_CHECK(ch != 'T');
-        parser->state = s_res_HTT;
-        break;
-
-      case s_res_HTT:
-        STRICT_CHECK(ch != 'P');
-        parser->state = s_res_HTTP;
-        break;
-
-      case s_res_HTTP:
-        STRICT_CHECK(ch != '/');
-        parser->state = s_res_first_http_major;
-        break;
-
-      case s_res_first_http_major:
-        if (ch < '0' || ch > '9') {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_major = ch - '0';
-        parser->state = s_res_http_major;
-        break;
-
-      /* major HTTP version or dot */
-      case s_res_http_major:
-      {
-        if (ch == '.') {
-          parser->state = s_res_first_http_minor;
-          break;
-        }
-
-        if (!IS_NUM(ch)) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_major *= 10;
-        parser->http_major += ch - '0';
-
-        if (parser->http_major > 999) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        break;
-      }
-
-      /* first digit of minor HTTP version */
-      case s_res_first_http_minor:
-        if (!IS_NUM(ch)) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_minor = ch - '0';
-        parser->state = s_res_http_minor;
-        break;
-
-      /* minor HTTP version or end of request line */
-      case s_res_http_minor:
-      {
-        if (ch == ' ') {
-          parser->state = s_res_first_status_code;
-          break;
-        }
-
-        if (!IS_NUM(ch)) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_minor *= 10;
-        parser->http_minor += ch - '0';
-
-        if (parser->http_minor > 999) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        break;
-      }
-
-      case s_res_first_status_code:
-      {
-        if (!IS_NUM(ch)) {
-          if (ch == ' ') {
-            break;
-          }
-
-          SET_ERRNO(HPE_INVALID_STATUS);
-          goto error;
-        }
-        parser->status_code = ch - '0';
-        parser->state = s_res_status_code;
-        break;
-      }
-
-      case s_res_status_code:
-      {
-        if (!IS_NUM(ch)) {
-          switch (ch) {
-            case ' ':
-              parser->state = s_res_status;
-              break;
-            case CR:
-              parser->state = s_res_line_almost_done;
-              break;
-            case LF:
-              parser->state = s_header_field_start;
-              break;
-            default:
-              SET_ERRNO(HPE_INVALID_STATUS);
-              goto error;
-          }
-          break;
-        }
-
-        parser->status_code *= 10;
-        parser->status_code += ch - '0';
-
-        if (parser->status_code > 999) {
-          SET_ERRNO(HPE_INVALID_STATUS);
-          goto error;
-        }
-
-        break;
-      }
-
-      case s_res_status:
-        /* the human readable status. e.g. "NOT FOUND"
-         * we are not humans so just ignore this */
-        if (ch == CR) {
-          parser->state = s_res_line_almost_done;
-          break;
-        }
-
-        if (ch == LF) {
-          parser->state = s_header_field_start;
-          break;
-        }
-        break;
-
-      case s_res_line_almost_done:
-        STRICT_CHECK(ch != LF);
-        parser->state = s_header_field_start;
-        CALLBACK_NOTIFY(status_complete);
-        break;
-
-      case s_start_req:
-      {
-        if (ch == CR || ch == LF)
-          break;
-        parser->flags = 0;
-        parser->content_length = ULLONG_MAX;
-
-        if (!IS_ALPHA(ch)) {
-          SET_ERRNO(HPE_INVALID_METHOD);
-          goto error;
-        }
-
-        parser->method = (enum http_method) 0;
-        parser->index = 1;
-        switch (ch) {
-          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
-          case 'D': parser->method = HTTP_DELETE; break;
-          case 'G': parser->method = HTTP_GET; break;
-          case 'H': parser->method = HTTP_HEAD; break;
-          case 'L': parser->method = HTTP_LOCK; break;
-          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
-          case 'N': parser->method = HTTP_NOTIFY; break;
-          case 'O': parser->method = HTTP_OPTIONS; break;
-          case 'P': parser->method = HTTP_POST;
-            /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
-            break;
-          case 'R': parser->method = HTTP_REPORT; break;
-          case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
-          case 'T': parser->method = HTTP_TRACE; break;
-          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
-          default:
-            SET_ERRNO(HPE_INVALID_METHOD);
-            goto error;
-        }
-        parser->state = s_req_method;
-
-        CALLBACK_NOTIFY(message_begin);
-
-        break;
-      }
-
-      case s_req_method:
-      {
-        const char *matcher;
-        if (ch == '\0') {
-          SET_ERRNO(HPE_INVALID_METHOD);
-          goto error;
-        }
-
-        matcher = method_strings[parser->method];
-        if (ch == ' ' && matcher[parser->index] == '\0') {
-          parser->state = s_req_spaces_before_url;
-        } else if (ch == matcher[parser->index]) {
-          ; /* nada */
-        } else if (parser->method == HTTP_CONNECT) {
-          if (parser->index == 1 && ch == 'H') {
-            parser->method = HTTP_CHECKOUT;
-          } else if (parser->index == 2  && ch == 'P') {
-            parser->method = HTTP_COPY;
-          } else {
-            goto error;
-          }
-        } else if (parser->method == HTTP_MKCOL) {
-          if (parser->index == 1 && ch == 'O') {
-            parser->method = HTTP_MOVE;
-          } else if (parser->index == 1 && ch == 'E') {
-            parser->method = HTTP_MERGE;
-          } else if (parser->index == 1 && ch == '-') {
-            parser->method = HTTP_MSEARCH;
-          } else if (parser->index == 2 && ch == 'A') {
-            parser->method = HTTP_MKACTIVITY;
-          } else {
-            goto error;
-          }
-        } else if (parser->method == HTTP_SUBSCRIBE) {
-          if (parser->index == 1 && ch == 'E') {
-            parser->method = HTTP_SEARCH;
-          } else {
-            goto error;
-          }
-        } else if (parser->index == 1 && parser->method == HTTP_POST) {
-          if (ch == 'R') {
-            parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
-          } else if (ch == 'U') {
-            parser->method = HTTP_PUT; /* or HTTP_PURGE */
-          } else if (ch == 'A') {
-            parser->method = HTTP_PATCH;
-          } else {
-            goto error;
-          }
-        } else if (parser->index == 2) {
-          if (parser->method == HTTP_PUT) {
-            if (ch == 'R') parser->method = HTTP_PURGE;
-          } else if (parser->method == HTTP_UNLOCK) {
-            if (ch == 'S') parser->method = HTTP_UNSUBSCRIBE;
-          }
-        } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
-          parser->method = HTTP_PROPPATCH;
-        } else {
-          SET_ERRNO(HPE_INVALID_METHOD);
-          goto error;
-        }
-
-        ++parser->index;
-        break;
-      }
-
-      case s_req_spaces_before_url:
-      {
-        if (ch == ' ') break;
-
-        MARK(url);
-        if (parser->method == HTTP_CONNECT) {
-          parser->state = s_req_server_start;
-        }
-
-        parser->state = parse_url_char((enum state)parser->state, ch);
-        if (parser->state == s_dead) {
-          SET_ERRNO(HPE_INVALID_URL);
-          goto error;
-        }
-
-        break;
-      }
-
-      case s_req_schema:
-      case s_req_schema_slash:
-      case s_req_schema_slash_slash:
-      case s_req_server_start:
-      {
-        switch (ch) {
-          /* No whitespace allowed here */
-          case ' ':
-          case CR:
-          case LF:
-            SET_ERRNO(HPE_INVALID_URL);
-            goto error;
-          default:
-            parser->state = parse_url_char((enum state)parser->state, ch);
-            if (parser->state == s_dead) {
-              SET_ERRNO(HPE_INVALID_URL);
-              goto error;
-            }
-        }
-
-        break;
-      }
-
-      case s_req_server:
-      case s_req_server_with_at:
-      case s_req_path:
-      case s_req_query_string_start:
-      case s_req_query_string:
-      case s_req_fragment_start:
-      case s_req_fragment:
-      {
-        switch (ch) {
-          case ' ':
-            parser->state = s_req_http_start;
-            CALLBACK_DATA(url);
-            break;
-          case CR:
-          case LF:
-            parser->http_major = 0;
-            parser->http_minor = 9;
-            parser->state = (ch == CR) ?
-              s_req_line_almost_done :
-              s_header_field_start;
-            CALLBACK_DATA(url);
-            break;
-          default:
-            parser->state = parse_url_char((enum state)parser->state, ch);
-            if (parser->state == s_dead) {
-              SET_ERRNO(HPE_INVALID_URL);
-              goto error;
-            }
-        }
-        break;
-      }
-
-      case s_req_http_start:
-        switch (ch) {
-          case 'H':
-            parser->state = s_req_http_H;
-            break;
-          case ' ':
-            break;
-          default:
-            SET_ERRNO(HPE_INVALID_CONSTANT);
-            goto error;
-        }
-        break;
-
-      case s_req_http_H:
-        STRICT_CHECK(ch != 'T');
-        parser->state = s_req_http_HT;
-        break;
-
-      case s_req_http_HT:
-        STRICT_CHECK(ch != 'T');
-        parser->state = s_req_http_HTT;
-        break;
-
-      case s_req_http_HTT:
-        STRICT_CHECK(ch != 'P');
-        parser->state = s_req_http_HTTP;
-        break;
-
-      case s_req_http_HTTP:
-        STRICT_CHECK(ch != '/');
-        parser->state = s_req_first_http_major;
-        break;
-
-      /* first digit of major HTTP version */
-      case s_req_first_http_major:
-        if (ch < '1' || ch > '9') {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_major = ch - '0';
-        parser->state = s_req_http_major;
-        break;
-
-      /* major HTTP version or dot */
-      case s_req_http_major:
-      {
-        if (ch == '.') {
-          parser->state = s_req_first_http_minor;
-          break;
-        }
-
-        if (!IS_NUM(ch)) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_major *= 10;
-        parser->http_major += ch - '0';
-
-        if (parser->http_major > 999) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        break;
-      }
-
-      /* first digit of minor HTTP version */
-      case s_req_first_http_minor:
-        if (!IS_NUM(ch)) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_minor = ch - '0';
-        parser->state = s_req_http_minor;
-        break;
-
-      /* minor HTTP version or end of request line */
-      case s_req_http_minor:
-      {
-        if (ch == CR) {
-          parser->state = s_req_line_almost_done;
-          break;
-        }
-
-        if (ch == LF) {
-          parser->state = s_header_field_start;
-          break;
-        }
-
-        /* XXX allow spaces after digit? */
-
-        if (!IS_NUM(ch)) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_minor *= 10;
-        parser->http_minor += ch - '0';
-
-        if (parser->http_minor > 999) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        break;
-      }
-
-      /* end of request line */
-      case s_req_line_almost_done:
-      {
-        if (ch != LF) {
-          SET_ERRNO(HPE_LF_EXPECTED);
-          goto error;
-        }
-
-        parser->state = s_header_field_start;
-        break;
-      }
-
-      case s_header_field_start:
-      {
-        if (ch == CR) {
-          parser->state = s_headers_almost_done;
-          break;
-        }
-
-        if (ch == LF) {
-          /* they might be just sending \n instead of \r\n so this would be
-           * the second \n to denote the end of headers*/
-          parser->state = s_headers_almost_done;
-          goto reexecute_byte;
-        }
-
-        c = TOKEN(ch);
-
-        if (!c) {
-          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
-          goto error;
-        }
-
-        MARK(header_field);
-
-        parser->index = 0;
-        parser->state = s_header_field;
-
-        switch (c) {
-          case 'c':
-            parser->header_state = h_C;
-            break;
-
-          case 'p':
-            parser->header_state = h_matching_proxy_connection;
-            break;
-
-          case 't':
-            parser->header_state = h_matching_transfer_encoding;
-            break;
-
-          case 'u':
-            parser->header_state = h_matching_upgrade;
-            break;
-
-          default:
-            parser->header_state = h_general;
-            break;
-        }
-        break;
-      }
-
-      case s_header_field:
-      {
-        c = TOKEN(ch);
-
-        if (c) {
-          switch (parser->header_state) {
-            case h_general:
-              break;
-
-            case h_C:
-              parser->index++;
-              parser->header_state = (c == 'o' ? h_CO : h_general);
-              break;
-
-            case h_CO:
-              parser->index++;
-              parser->header_state = (c == 'n' ? h_CON : h_general);
-              break;
-
-            case h_CON:
-              parser->index++;
-              switch (c) {
-                case 'n':
-                  parser->header_state = h_matching_connection;
-                  break;
-                case 't':
-                  parser->header_state = h_matching_content_length;
-                  break;
-                default:
-                  parser->header_state = h_general;
-                  break;
-              }
-              break;
-
-            /* connection */
-
-            case h_matching_connection:
-              parser->index++;
-              if (parser->index > sizeof(CONNECTION)-1
-                  || c != CONNECTION[parser->index]) {
-                parser->header_state = h_general;
-              } else if (parser->index == sizeof(CONNECTION)-2) {
-                parser->header_state = h_connection;
-              }
-              break;
-
-            /* proxy-connection */
-
-            case h_matching_proxy_connection:
-              parser->index++;
-              if (parser->index > sizeof(PROXY_CONNECTION)-1
-                  || c != PROXY_CONNECTION[parser->index]) {
-                parser->header_state = h_general;
-              } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
-                parser->header_state = h_connection;
-              }
-              break;
-
-            /* content-length */
-
-            case h_matching_content_length:
-              parser->index++;
-              if (parser->index > sizeof(CONTENT_LENGTH)-1
-                  || c != CONTENT_LENGTH[parser->index]) {
-                parser->header_state = h_general;
-              } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
-                parser->header_state = h_content_length;
-              }
-              break;
-
-            /* transfer-encoding */
-
-            case h_matching_transfer_encoding:
-              parser->index++;
-              if (parser->index > sizeof(TRANSFER_ENCODING)-1
-                  || c != TRANSFER_ENCODING[parser->index]) {
-                parser->header_state = h_general;
-              } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
-                parser->header_state = h_transfer_encoding;
-              }
-              break;
-
-            /* upgrade */
-
-            case h_matching_upgrade:
-              parser->index++;
-              if (parser->index > sizeof(UPGRADE)-1
-                  || c != UPGRADE[parser->index]) {
-                parser->header_state = h_general;
-              } else if (parser->index == sizeof(UPGRADE)-2) {
-                parser->header_state = h_upgrade;
-              }
-              break;
-
-            case h_connection:
-            case h_content_length:
-            case h_transfer_encoding:
-            case h_upgrade:
-              if (ch != ' ') parser->header_state = h_general;
-              break;
-
-            default:
-              assert(0 && "Unknown header_state");
-              break;
-          }
-          break;
-        }
-
-        if (ch == ':') {
-          parser->state = s_header_value_start;
-          CALLBACK_DATA(header_field);
-          break;
-        }
-
-        if (ch == CR) {
-          parser->state = s_header_almost_done;
-          CALLBACK_DATA(header_field);
-          break;
-        }
-
-        if (ch == LF) {
-          parser->state = s_header_field_start;
-          CALLBACK_DATA(header_field);
-          break;
-        }
-
-        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
-        goto error;
-      }
-
-      case s_header_value_start:
-      {
-        if (ch == ' ' || ch == '\t') break;
-
-        MARK(header_value);
-
-        parser->state = s_header_value;
-        parser->index = 0;
-
-        if (ch == CR) {
-          parser->header_state = h_general;
-          parser->state = s_header_almost_done;
-          CALLBACK_DATA(header_value);
-          break;
-        }
-
-        if (ch == LF) {
-          parser->state = s_header_field_start;
-          CALLBACK_DATA(header_value);
-          break;
-        }
-
-        c = LOWER(ch);
-
-        switch (parser->header_state) {
-          case h_upgrade:
-            parser->flags |= F_UPGRADE;
-            parser->header_state = h_general;
-            break;
-
-          case h_transfer_encoding:
-            /* looking for 'Transfer-Encoding: chunked' */
-            if ('c' == c) {
-              parser->header_state = h_matching_transfer_encoding_chunked;
-            } else {
-              parser->header_state = h_general;
-            }
-            break;
-
-          case h_content_length:
-            if (!IS_NUM(ch)) {
-              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
-              goto error;
-            }
-
-            parser->content_length = ch - '0';
-            break;
-
-          case h_connection:
-            /* looking for 'Connection: keep-alive' */
-            if (c == 'k') {
-              parser->header_state = h_matching_connection_keep_alive;
-            /* looking for 'Connection: close' */
-            } else if (c == 'c') {
-              parser->header_state = h_matching_connection_close;
-            } else {
-              parser->header_state = h_general;
-            }
-            break;
-
-          default:
-            parser->header_state = h_general;
-            break;
-        }
-        break;
-      }
-
-      case s_header_value:
-      {
-
-        if (ch == CR) {
-          parser->state = s_header_almost_done;
-          CALLBACK_DATA(header_value);
-          break;
-        }
-
-        if (ch == LF) {
-          parser->state = s_header_almost_done;
-          CALLBACK_DATA_NOADVANCE(header_value);
-          goto reexecute_byte;
-        }
-
-        c = LOWER(ch);
-
-        switch (parser->header_state) {
-          case h_general:
-            break;
-
-          case h_connection:
-          case h_transfer_encoding:
-            assert(0 && "Shouldn't get here.");
-            break;
-
-          case h_content_length:
-          {
-            uint64_t t;
-
-            if (ch == ' ') break;
-
-            if (!IS_NUM(ch)) {
-              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
-              goto error;
-            }
-
-            t = parser->content_length;
-            t *= 10;
-            t += ch - '0';
-
-            /* Overflow? */
-            if (t < parser->content_length || t == ULLONG_MAX) {
-              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
-              goto error;
-            }
-
-            parser->content_length = t;
-            break;
-          }
-
-          /* Transfer-Encoding: chunked */
-          case h_matching_transfer_encoding_chunked:
-            parser->index++;
-            if (parser->index > sizeof(CHUNKED)-1
-                || c != CHUNKED[parser->index]) {
-              parser->header_state = h_general;
-            } else if (parser->index == sizeof(CHUNKED)-2) {
-              parser->header_state = h_transfer_encoding_chunked;
-            }
-            break;
-
-          /* looking for 'Connection: keep-alive' */
-          case h_matching_connection_keep_alive:
-            parser->index++;
-            if (parser->index > sizeof(KEEP_ALIVE)-1
-                || c != KEEP_ALIVE[parser->index]) {
-              parser->header_state = h_general;
-            } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
-              parser->header_state = h_connection_keep_alive;
-            }
-            break;
-
-          /* looking for 'Connection: close' */
-          case h_matching_connection_close:
-            parser->index++;
-            if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
-              parser->header_state = h_general;
-            } else if (parser->index == sizeof(CLOSE)-2) {
-              parser->header_state = h_connection_close;
-            }
-            break;
-
-          case h_transfer_encoding_chunked:
-          case h_connection_keep_alive:
-          case h_connection_close:
-            if (ch != ' ') parser->header_state = h_general;
-            break;
-
-          default:
-            parser->state = s_header_value;
-            parser->header_state = h_general;
-            break;
-        }
-        break;
-      }
-
-      case s_header_almost_done:
-      {
-        STRICT_CHECK(ch != LF);
-
-        parser->state = s_header_value_lws;
-
-        switch (parser->header_state) {
-          case h_connection_keep_alive:
-            parser->flags |= F_CONNECTION_KEEP_ALIVE;
-            break;
-          case h_connection_close:
-            parser->flags |= F_CONNECTION_CLOSE;
-            break;
-          case h_transfer_encoding_chunked:
-            parser->flags |= F_CHUNKED;
-            break;
-          default:
-            break;
-        }
-
-        break;
-      }
-
-      case s_header_value_lws:
-      {
-        if (ch == ' ' || ch == '\t')
-          parser->state = s_header_value_start;
-        else
-        {
-          parser->state = s_header_field_start;
-          goto reexecute_byte;
-        }
-        break;
-      }
-
-      case s_headers_almost_done:
-      {
-        STRICT_CHECK(ch != LF);
-
-        if (parser->flags & F_TRAILING) {
-          /* End of a chunked request */
-          parser->state = NEW_MESSAGE();
-          CALLBACK_NOTIFY(message_complete);
-          break;
-        }
-
-        parser->state = s_headers_done;
-
-        /* Set this here so that on_headers_complete() callbacks can see it */
-        parser->upgrade =
-          (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT);
-
-        /* Here we call the headers_complete callback. This is somewhat
-         * different than other callbacks because if the user returns 1, we
-         * will interpret that as saying that this message has no body. This
-         * is needed for the annoying case of recieving a response to a HEAD
-         * request.
-         *
-         * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
-         * we have to simulate it by handling a change in errno below.
-         */
-        if (settings->on_headers_complete) {
-          switch (settings->on_headers_complete(parser)) {
-            case 0:
-              break;
-
-            case 1:
-              parser->flags |= F_SKIPBODY;
-              break;
-
-            default:
-              SET_ERRNO(HPE_CB_headers_complete);
-              return p - data; /* Error */
-          }
-        }
-
-        if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
-          return p - data;
-        }
-
-        goto reexecute_byte;
-      }
-
-      case s_headers_done:
-      {
-        STRICT_CHECK(ch != LF);
-
-        parser->nread = 0;
-
-        /* Exit, the rest of the connect is in a different protocol. */
-        if (parser->upgrade) {
-          parser->state = NEW_MESSAGE();
-          CALLBACK_NOTIFY(message_complete);
-          return (p - data) + 1;
-        }
-
-        if (parser->flags & F_SKIPBODY) {
-          parser->state = NEW_MESSAGE();
-          CALLBACK_NOTIFY(message_complete);
-        } else if (parser->flags & F_CHUNKED) {
-          /* chunked encoding - ignore Content-Length header */
-          parser->state = s_chunk_size_start;
-        } else {
-          if (parser->content_length == 0) {
-            /* Content-Length header given but zero: Content-Length: 0\r\n */
-            parser->state = NEW_MESSAGE();
-            CALLBACK_NOTIFY(message_complete);
-          } else if (parser->content_length != ULLONG_MAX) {
-            /* Content-Length header given and non-zero */
-            parser->state = s_body_identity;
-          } else {
-            if (parser->type == HTTP_REQUEST ||
-                !http_message_needs_eof(parser)) {
-              /* Assume content-length 0 - read the next */
-              parser->state = NEW_MESSAGE();
-              CALLBACK_NOTIFY(message_complete);
-            } else {
-              /* Read body until EOF */
-              parser->state = s_body_identity_eof;
-            }
-          }
-        }
-
-        break;
-      }
-
-      case s_body_identity:
-      {
-        uint64_t to_read = MIN(parser->content_length,
-                               (uint64_t) ((data + len) - p));
-
-        assert(parser->content_length != 0
-            && parser->content_length != ULLONG_MAX);
-
-        /* The difference between advancing content_length and p is because
-         * the latter will automaticaly advance on the next loop iteration.
-         * Further, if content_length ends up at 0, we want to see the last
-         * byte again for our message complete callback.
-         */
-        MARK(body);
-        parser->content_length -= to_read;
-        p += to_read - 1;
-
-        if (parser->content_length == 0) {
-          parser->state = s_message_done;
-
-          /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
-           *
-           * The alternative to doing this is to wait for the next byte to
-           * trigger the data callback, just as in every other case. The
-           * problem with this is that this makes it difficult for the test
-           * harness to distinguish between complete-on-EOF and
-           * complete-on-length. It's not clear that this distinction is
-           * important for applications, but let's keep it for now.
-           */
-          CALLBACK_DATA_(body, p - body_mark + 1, p - data);
-          goto reexecute_byte;
-        }
-
-        break;
-      }
-
-      /* read until EOF */
-      case s_body_identity_eof:
-        MARK(body);
-        p = data + len - 1;
-
-        break;
-
-      case s_message_done:
-        parser->state = NEW_MESSAGE();
-        CALLBACK_NOTIFY(message_complete);
-        break;
-
-      case s_chunk_size_start:
-      {
-        assert(parser->nread == 1);
-        assert(parser->flags & F_CHUNKED);
-
-        unhex_val = unhex[(unsigned char)ch];
-        if (unhex_val == -1) {
-          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
-          goto error;
-        }
-
-        parser->content_length = unhex_val;
-        parser->state = s_chunk_size;
-        break;
-      }
-
-      case s_chunk_size:
-      {
-        uint64_t t;
-
-        assert(parser->flags & F_CHUNKED);
-
-        if (ch == CR) {
-          parser->state = s_chunk_size_almost_done;
-          break;
-        }
-
-        unhex_val = unhex[(unsigned char)ch];
-
-        if (unhex_val == -1) {
-          if (ch == ';' || ch == ' ') {
-            parser->state = s_chunk_parameters;
-            break;
-          }
-
-          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
-          goto error;
-        }
-
-        t = parser->content_length;
-        t *= 16;
-        t += unhex_val;
-
-        /* Overflow? */
-        if (t < parser->content_length || t == ULLONG_MAX) {
-          SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
-          goto error;
-        }
-
-        parser->content_length = t;
-        break;
-      }
-
-      case s_chunk_parameters:
-      {
-        assert(parser->flags & F_CHUNKED);
-        /* just ignore this shit. TODO check for overflow */
-        if (ch == CR) {
-          parser->state = s_chunk_size_almost_done;
-          break;
-        }
-        break;
-      }
-
-      case s_chunk_size_almost_done:
-      {
-        assert(parser->flags & F_CHUNKED);
-        STRICT_CHECK(ch != LF);
-
-        parser->nread = 0;
-
-        if (parser->content_length == 0) {
-          parser->flags |= F_TRAILING;
-          parser->state = s_header_field_start;
-        } else {
-          parser->state = s_chunk_data;
-        }
-        break;
-      }
-
-      case s_chunk_data:
-      {
-        uint64_t to_read = MIN(parser->content_length,
-                               (uint64_t) ((data + len) - p));
-
-        assert(parser->flags & F_CHUNKED);
-        assert(parser->content_length != 0
-            && parser->content_length != ULLONG_MAX);
-
-        /* See the explanation in s_body_identity for why the content
-         * length and data pointers are managed this way.
-         */
-        MARK(body);
-        parser->content_length -= to_read;
-        p += to_read - 1;
-
-        if (parser->content_length == 0) {
-          parser->state = s_chunk_data_almost_done;
-        }
-
-        break;
-      }
-
-      case s_chunk_data_almost_done:
-        assert(parser->flags & F_CHUNKED);
-        assert(parser->content_length == 0);
-        STRICT_CHECK(ch != CR);
-        parser->state = s_chunk_data_done;
-        CALLBACK_DATA(body);
-        break;
-
-      case s_chunk_data_done:
-        assert(parser->flags & F_CHUNKED);
-        STRICT_CHECK(ch != LF);
-        parser->nread = 0;
-        parser->state = s_chunk_size_start;
-        break;
-
-      default:
-        assert(0 && "unhandled state");
-        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
-        goto error;
-    }
-  }
-
-  /* Run callbacks for any marks that we have leftover after we ran our of
-   * bytes. There should be at most one of these set, so it's OK to invoke
-   * them in series (unset marks will not result in callbacks).
-   *
-   * We use the NOADVANCE() variety of callbacks here because 'p' has already
-   * overflowed 'data' and this allows us to correct for the off-by-one that
-   * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
-   * value that's in-bounds).
-   */
-
-  assert(((header_field_mark ? 1 : 0) +
-          (header_value_mark ? 1 : 0) +
-          (url_mark ? 1 : 0)  +
-          (body_mark ? 1 : 0)) <= 1);
-
-  CALLBACK_DATA_NOADVANCE(header_field);
-  CALLBACK_DATA_NOADVANCE(header_value);
-  CALLBACK_DATA_NOADVANCE(url);
-  CALLBACK_DATA_NOADVANCE(body);
-
-  return len;
-
-error:
-  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
-    SET_ERRNO(HPE_UNKNOWN);
-  }
-
-  return (p - data);
-}
-
-
-/* Does the parser need to see an EOF to find the end of the message? */
-int
-http_message_needs_eof (const http_parser *parser)
-{
-  if (parser->type == HTTP_REQUEST) {
-    return 0;
-  }
-
-  /* See RFC 2616 section 4.4 */
-  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
-      parser->status_code == 204 ||     /* No Content */
-      parser->status_code == 304 ||     /* Not Modified */
-      parser->flags & F_SKIPBODY) {     /* response to a HEAD request */
-    return 0;
-  }
-
-  if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
-    return 0;
-  }
-
-  return 1;
-}
-
-
-int
-http_should_keep_alive (const http_parser *parser)
-{
-  if (parser->http_major > 0 && parser->http_minor > 0) {
-    /* HTTP/1.1 */
-    if (parser->flags & F_CONNECTION_CLOSE) {
-      return 0;
-    }
-  } else {
-    /* HTTP/1.0 or earlier */
-    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
-      return 0;
-    }
-  }
-
-  return !http_message_needs_eof(parser);
-}
-
-
-const char *
-http_method_str (enum http_method m)
-{
-  return ELEM_AT(method_strings, m, "<unknown>");
-}
-
-
-void
-http_parser_init (http_parser *parser, enum http_parser_type t)
-{
-  void *data = parser->data; /* preserve application data */
-  memset(parser, 0, sizeof(*parser));
-  parser->data = data;
-  parser->type = t;
-  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
-  parser->http_errno = HPE_OK;
-}
-
-const char *
-http_errno_name(enum http_errno err) {
-  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
-  return http_strerror_tab[err].name;
-}
-
-const char *
-http_errno_description(enum http_errno err) {
-  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
-  return http_strerror_tab[err].description;
-}
-
-static enum http_host_state
-http_parse_host_char(enum http_host_state s, const char ch) {
-  switch(s) {
-    case s_http_userinfo:
-    case s_http_userinfo_start:
-      if (ch == '@') {
-        return s_http_host_start;
-      }
-
-      if (IS_USERINFO_CHAR(ch)) {
-        return s_http_userinfo;
-      }
-      break;
-
-    case s_http_host_start:
-      if (ch == '[') {
-        return s_http_host_v6_start;
-      }
-
-      if (IS_HOST_CHAR(ch)) {
-        return s_http_host;
-      }
-
-      break;
-
-    case s_http_host:
-      if (IS_HOST_CHAR(ch)) {
-        return s_http_host;
-      }
-
-    /* FALLTHROUGH */
-    case s_http_host_v6_end:
-      if (ch == ':') {
-        return s_http_host_port_start;
-      }
-
-      break;
-
-    case s_http_host_v6:
-      if (ch == ']') {
-        return s_http_host_v6_end;
-      }
-
-    /* FALLTHROUGH */
-    case s_http_host_v6_start:
-      if (IS_HEX(ch) || ch == ':' || ch == '.') {
-        return s_http_host_v6;
-      }
-
-      break;
-
-    case s_http_host_port:
-    case s_http_host_port_start:
-      if (IS_NUM(ch)) {
-        return s_http_host_port;
-      }
-
-      break;
-
-    default:
-      break;
-  }
-  return s_http_host_dead;
-}
-
-static int
-http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
-  enum http_host_state s;
-
-  const char *p;
-  size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
-
-  u->field_data[UF_HOST].len = 0;
-
-  s = found_at ? s_http_userinfo_start : s_http_host_start;
-
-  for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
-    enum http_host_state new_s = http_parse_host_char(s, *p);
-
-    if (new_s == s_http_host_dead) {
-      return 1;
-    }
-
-    switch(new_s) {
-      case s_http_host:
-        if (s != s_http_host) {
-          u->field_data[UF_HOST].off = p - buf;
-        }
-        u->field_data[UF_HOST].len++;
-        break;
-
-      case s_http_host_v6:
-        if (s != s_http_host_v6) {
-          u->field_data[UF_HOST].off = p - buf;
-        }
-        u->field_data[UF_HOST].len++;
-        break;
-
-      case s_http_host_port:
-        if (s != s_http_host_port) {
-          u->field_data[UF_PORT].off = p - buf;
-          u->field_data[UF_PORT].len = 0;
-          u->field_set |= (1 << UF_PORT);
-        }
-        u->field_data[UF_PORT].len++;
-        break;
-
-      case s_http_userinfo:
-        if (s != s_http_userinfo) {
-          u->field_data[UF_USERINFO].off = p - buf ;
-          u->field_data[UF_USERINFO].len = 0;
-          u->field_set |= (1 << UF_USERINFO);
-        }
-        u->field_data[UF_USERINFO].len++;
-        break;
-
-      default:
-        break;
-    }
-    s = new_s;
-  }
-
-  /* Make sure we don't end somewhere unexpected */
-  switch (s) {
-    case s_http_host_start:
-    case s_http_host_v6_start:
-    case s_http_host_v6:
-    case s_http_host_port_start:
-    case s_http_userinfo:
-    case s_http_userinfo_start:
-      return 1;
-    default:
-      break;
-  }
-
-  return 0;
-}
-
-int
-http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
-                      struct http_parser_url *u)
-{
-  enum state s;
-  const char *p;
-  enum http_parser_url_fields uf, old_uf;
-  int found_at = 0;
-
-  u->port = u->field_set = 0;
-  s = is_connect ? s_req_server_start : s_req_spaces_before_url;
-  uf = old_uf = UF_MAX;
-
-  for (p = buf; p < buf + buflen; p++) {
-    s = parse_url_char(s, *p);
-
-    /* Figure out the next field that we're operating on */
-    switch (s) {
-      case s_dead:
-        return 1;
-
-      /* Skip delimeters */
-      case s_req_schema_slash:
-      case s_req_schema_slash_slash:
-      case s_req_server_start:
-      case s_req_query_string_start:
-      case s_req_fragment_start:
-        continue;
-
-      case s_req_schema:
-        uf = UF_SCHEMA;
-        break;
-
-      case s_req_server_with_at:
-        found_at = 1;
-
-      /* FALLTROUGH */
-      case s_req_server:
-        uf = UF_HOST;
-        break;
-
-      case s_req_path:
-        uf = UF_PATH;
-        break;
-
-      case s_req_query_string:
-        uf = UF_QUERY;
-        break;
-
-      case s_req_fragment:
-        uf = UF_FRAGMENT;
-        break;
-
-      default:
-        assert(!"Unexpected state");
-        return 1;
-    }
-
-    /* Nothing's changed; soldier on */
-    if (uf == old_uf) {
-      u->field_data[uf].len++;
-      continue;
-    }
-
-    u->field_data[uf].off = p - buf;
-    u->field_data[uf].len = 1;
-
-    u->field_set |= (1 << uf);
-    old_uf = uf;
-  }
-
-  /* host must be present if there is a schema */
-  /* parsing http:///toto will fail */
-  if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) {
-    if (http_parse_host(buf, u, found_at) != 0) {
-      return 1;
-    }
-  }
-
-  /* CONNECT requests can only contain "hostname:port" */
-  if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
-    return 1;
-  }
-
-  if (u->field_set & (1 << UF_PORT)) {
-    /* Don't bother with endp; we've already validated the string */
-    unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
-
-    /* Ports have a max value of 2^16 */
-    if (v > 0xffff) {
-      return 1;
-    }
-
-    u->port = (uint16_t) v;
-  }
-
-  return 0;
-}
-
-void
-http_parser_pause(http_parser *parser, int paused) {
-  /* Users should only be pausing/unpausing a parser that is not in an error
-   * state. In non-debug builds, there's not much that we can do about this
-   * other than ignore it.
-   */
-  if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
-      HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
-    SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
-  } else {
-    assert(0 && "Attempting to pause parser in error state");
-  }
-}
-
-int
-http_body_is_final(const struct http_parser *parser) {
-    return parser->state == s_message_done;
-}

+ 0 - 111
ext/http-parser/http_parser.gyp

@@ -1,111 +0,0 @@
-# This file is used with the GYP meta build system.
-# http://code.google.com/p/gyp/
-# To build try this:
-#   svn co http://gyp.googlecode.com/svn/trunk gyp
-#   ./gyp/gyp -f make --depth=`pwd` http_parser.gyp 
-#   ./out/Debug/test 
-{
-  'target_defaults': {
-    'default_configuration': 'Debug',
-    'configurations': {
-      # TODO: hoist these out and put them somewhere common, because
-      #       RuntimeLibrary MUST MATCH across the entire project
-      'Debug': {
-        'defines': [ 'DEBUG', '_DEBUG' ],
-        'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ],
-        'msvs_settings': {
-          'VCCLCompilerTool': {
-            'RuntimeLibrary': 1, # static debug
-          },
-        },
-      },
-      'Release': {
-        'defines': [ 'NDEBUG' ],
-        'cflags': [ '-Wall', '-Wextra', '-O3' ],
-        'msvs_settings': {
-          'VCCLCompilerTool': {
-            'RuntimeLibrary': 0, # static release
-          },
-        },
-      }
-    },
-    'msvs_settings': {
-      'VCCLCompilerTool': {
-      },
-      'VCLibrarianTool': {
-      },
-      'VCLinkerTool': {
-        'GenerateDebugInformation': 'true',
-      },
-    },
-    'conditions': [
-      ['OS == "win"', {
-        'defines': [
-          'WIN32'
-        ],
-      }]
-    ],
-  },
-
-  'targets': [
-    {
-      'target_name': 'http_parser',
-      'type': 'static_library',
-      'include_dirs': [ '.' ],
-      'direct_dependent_settings': {
-        'defines': [ 'HTTP_PARSER_STRICT=0' ],
-        'include_dirs': [ '.' ],
-      },
-      'defines': [ 'HTTP_PARSER_STRICT=0' ],
-      'sources': [ './http_parser.c', ],
-      'conditions': [
-        ['OS=="win"', {
-          'msvs_settings': {
-            'VCCLCompilerTool': {
-              # Compile as C++. http_parser.c is actually C99, but C++ is
-              # close enough in this case.
-              'CompileAs': 2,
-            },
-          },
-        }]
-      ],
-    },
-
-    {
-      'target_name': 'http_parser_strict',
-      'type': 'static_library',
-      'include_dirs': [ '.' ],
-      'direct_dependent_settings': {
-        'defines': [ 'HTTP_PARSER_STRICT=1' ],
-        'include_dirs': [ '.' ],
-      },
-      'defines': [ 'HTTP_PARSER_STRICT=1' ],
-      'sources': [ './http_parser.c', ],
-      'conditions': [
-        ['OS=="win"', {
-          'msvs_settings': {
-            'VCCLCompilerTool': {
-              # Compile as C++. http_parser.c is actually C99, but C++ is
-              # close enough in this case.
-              'CompileAs': 2,
-            },
-          },
-        }]
-      ],
-    },
-
-    {
-      'target_name': 'test-nonstrict',
-      'type': 'executable',
-      'dependencies': [ 'http_parser' ],
-      'sources': [ 'test.c' ]
-    },
-
-    {
-      'target_name': 'test-strict',
-      'type': 'executable',
-      'dependencies': [ 'http_parser_strict' ],
-      'sources': [ 'test.c' ]
-    }
-  ]
-}

+ 0 - 304
ext/http-parser/http_parser.h

@@ -1,304 +0,0 @@
-/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-#ifndef http_parser_h
-#define http_parser_h
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define HTTP_PARSER_VERSION_MAJOR 2
-#define HTTP_PARSER_VERSION_MINOR 0
-
-#include <sys/types.h>
-#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
-#include <BaseTsd.h>
-#include <stddef.h>
-typedef __int8 int8_t;
-typedef unsigned __int8 uint8_t;
-typedef __int16 int16_t;
-typedef unsigned __int16 uint16_t;
-typedef __int32 int32_t;
-typedef unsigned __int32 uint32_t;
-typedef __int64 int64_t;
-typedef unsigned __int64 uint64_t;
-#else
-#include <stdint.h>
-#endif
-
-/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
- * faster
- */
-#ifndef HTTP_PARSER_STRICT
-# define HTTP_PARSER_STRICT 1
-#endif
-
-/* Maximium header size allowed */
-#define HTTP_MAX_HEADER_SIZE (80*1024)
-
-
-typedef struct http_parser http_parser;
-typedef struct http_parser_settings http_parser_settings;
-
-
-/* Callbacks should return non-zero to indicate an error. The parser will
- * then halt execution.
- *
- * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
- * returning '1' from on_headers_complete will tell the parser that it
- * should not expect a body. This is used when receiving a response to a
- * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
- * chunked' headers that indicate the presence of a body.
- *
- * http_data_cb does not return data chunks. It will be call arbitrarally
- * many times for each string. E.G. you might get 10 callbacks for "on_url"
- * each providing just a few characters more data.
- */
-typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
-typedef int (*http_cb) (http_parser*);
-
-
-/* Request Methods */
-#define HTTP_METHOD_MAP(XX)         \
-  XX(0,  DELETE,      DELETE)       \
-  XX(1,  GET,         GET)          \
-  XX(2,  HEAD,        HEAD)         \
-  XX(3,  POST,        POST)         \
-  XX(4,  PUT,         PUT)          \
-  /* pathological */                \
-  XX(5,  CONNECT,     CONNECT)      \
-  XX(6,  OPTIONS,     OPTIONS)      \
-  XX(7,  TRACE,       TRACE)        \
-  /* webdav */                      \
-  XX(8,  COPY,        COPY)         \
-  XX(9,  LOCK,        LOCK)         \
-  XX(10, MKCOL,       MKCOL)        \
-  XX(11, MOVE,        MOVE)         \
-  XX(12, PROPFIND,    PROPFIND)     \
-  XX(13, PROPPATCH,   PROPPATCH)    \
-  XX(14, SEARCH,      SEARCH)       \
-  XX(15, UNLOCK,      UNLOCK)       \
-  /* subversion */                  \
-  XX(16, REPORT,      REPORT)       \
-  XX(17, MKACTIVITY,  MKACTIVITY)   \
-  XX(18, CHECKOUT,    CHECKOUT)     \
-  XX(19, MERGE,       MERGE)        \
-  /* upnp */                        \
-  XX(20, MSEARCH,     M-SEARCH)     \
-  XX(21, NOTIFY,      NOTIFY)       \
-  XX(22, SUBSCRIBE,   SUBSCRIBE)    \
-  XX(23, UNSUBSCRIBE, UNSUBSCRIBE)  \
-  /* RFC-5789 */                    \
-  XX(24, PATCH,       PATCH)        \
-  XX(25, PURGE,       PURGE)        \
-
-enum http_method
-  {
-#define XX(num, name, string) HTTP_##name = num,
-  HTTP_METHOD_MAP(XX)
-#undef XX
-  };
-
-
-enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
-
-
-/* Flag values for http_parser.flags field */
-enum flags
-  { F_CHUNKED               = 1 << 0
-  , F_CONNECTION_KEEP_ALIVE = 1 << 1
-  , F_CONNECTION_CLOSE      = 1 << 2
-  , F_TRAILING              = 1 << 3
-  , F_UPGRADE               = 1 << 4
-  , F_SKIPBODY              = 1 << 5
-  };
-
-
-/* Map for errno-related constants
- * 
- * The provided argument should be a macro that takes 2 arguments.
- */
-#define HTTP_ERRNO_MAP(XX)                                           \
-  /* No error */                                                     \
-  XX(OK, "success")                                                  \
-                                                                     \
-  /* Callback-related errors */                                      \
-  XX(CB_message_begin, "the on_message_begin callback failed")       \
-  XX(CB_status_complete, "the on_status_complete callback failed")   \
-  XX(CB_url, "the on_url callback failed")                           \
-  XX(CB_header_field, "the on_header_field callback failed")         \
-  XX(CB_header_value, "the on_header_value callback failed")         \
-  XX(CB_headers_complete, "the on_headers_complete callback failed") \
-  XX(CB_body, "the on_body callback failed")                         \
-  XX(CB_message_complete, "the on_message_complete callback failed") \
-                                                                     \
-  /* Parsing-related errors */                                       \
-  XX(INVALID_EOF_STATE, "stream ended at an unexpected time")        \
-  XX(HEADER_OVERFLOW,                                                \
-     "too many header bytes seen; overflow detected")                \
-  XX(CLOSED_CONNECTION,                                              \
-     "data received after completed connection: close message")      \
-  XX(INVALID_VERSION, "invalid HTTP version")                        \
-  XX(INVALID_STATUS, "invalid HTTP status code")                     \
-  XX(INVALID_METHOD, "invalid HTTP method")                          \
-  XX(INVALID_URL, "invalid URL")                                     \
-  XX(INVALID_HOST, "invalid host")                                   \
-  XX(INVALID_PORT, "invalid port")                                   \
-  XX(INVALID_PATH, "invalid path")                                   \
-  XX(INVALID_QUERY_STRING, "invalid query string")                   \
-  XX(INVALID_FRAGMENT, "invalid fragment")                           \
-  XX(LF_EXPECTED, "LF character expected")                           \
-  XX(INVALID_HEADER_TOKEN, "invalid character in header")            \
-  XX(INVALID_CONTENT_LENGTH,                                         \
-     "invalid character in content-length header")                   \
-  XX(INVALID_CHUNK_SIZE,                                             \
-     "invalid character in chunk size header")                       \
-  XX(INVALID_CONSTANT, "invalid constant string")                    \
-  XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
-  XX(STRICT, "strict mode assertion failed")                         \
-  XX(PAUSED, "parser is paused")                                     \
-  XX(UNKNOWN, "an unknown error occurred")
-
-
-/* Define HPE_* values for each errno value above */
-#define HTTP_ERRNO_GEN(n, s) HPE_##n,
-enum http_errno {
-  HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
-};
-#undef HTTP_ERRNO_GEN
-
-
-/* Get an http_errno value from an http_parser */
-#define HTTP_PARSER_ERRNO(p)            ((enum http_errno) (p)->http_errno)
-
-
-struct http_parser {
-  /** PRIVATE **/
-  unsigned char type : 2;     /* enum http_parser_type */
-  unsigned char flags : 6;    /* F_* values from 'flags' enum; semi-public */
-  unsigned char state;        /* enum state from http_parser.c */
-  unsigned char header_state; /* enum header_state from http_parser.c */
-  unsigned char index;        /* index into current matcher */
-
-  uint32_t nread;          /* # bytes read in various scenarios */
-  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
-
-  /** READ-ONLY **/
-  unsigned short http_major;
-  unsigned short http_minor;
-  unsigned short status_code; /* responses only */
-  unsigned char method;       /* requests only */
-  unsigned char http_errno : 7;
-
-  /* 1 = Upgrade header was present and the parser has exited because of that.
-   * 0 = No upgrade header present.
-   * Should be checked when http_parser_execute() returns in addition to
-   * error checking.
-   */
-  unsigned char upgrade : 1;
-
-  /** PUBLIC **/
-  void *data; /* A pointer to get hook to the "connection" or "socket" object */
-};
-
-
-struct http_parser_settings {
-  http_cb      on_message_begin;
-  http_data_cb on_url;
-  http_cb      on_status_complete;
-  http_data_cb on_header_field;
-  http_data_cb on_header_value;
-  http_cb      on_headers_complete;
-  http_data_cb on_body;
-  http_cb      on_message_complete;
-};
-
-
-enum http_parser_url_fields
-  { UF_SCHEMA           = 0
-  , UF_HOST             = 1
-  , UF_PORT             = 2
-  , UF_PATH             = 3
-  , UF_QUERY            = 4
-  , UF_FRAGMENT         = 5
-  , UF_USERINFO         = 6
-  , UF_MAX              = 7
-  };
-
-
-/* Result structure for http_parser_parse_url().
- *
- * Callers should index into field_data[] with UF_* values iff field_set
- * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
- * because we probably have padding left over), we convert any port to
- * a uint16_t.
- */
-struct http_parser_url {
-  uint16_t field_set;           /* Bitmask of (1 << UF_*) values */
-  uint16_t port;                /* Converted UF_PORT string */
-
-  struct {
-    uint16_t off;               /* Offset into buffer in which field starts */
-    uint16_t len;               /* Length of run in buffer */
-  } field_data[UF_MAX];
-};
-
-
-void http_parser_init(http_parser *parser, enum http_parser_type type);
-
-
-size_t http_parser_execute(http_parser *parser,
-                           const http_parser_settings *settings,
-                           const char *data,
-                           size_t len);
-
-
-/* If http_should_keep_alive() in the on_headers_complete or
- * on_message_complete callback returns 0, then this should be
- * the last message on the connection.
- * If you are the server, respond with the "Connection: close" header.
- * If you are the client, close the connection.
- */
-int http_should_keep_alive(const http_parser *parser);
-
-/* Returns a string version of the HTTP method. */
-const char *http_method_str(enum http_method m);
-
-/* Return a string name of the given error */
-const char *http_errno_name(enum http_errno err);
-
-/* Return a string description of the given error */
-const char *http_errno_description(enum http_errno err);
-
-/* Parse a URL; return nonzero on failure */
-int http_parser_parse_url(const char *buf, size_t buflen,
-                          int is_connect,
-                          struct http_parser_url *u);
-
-/* Pause or un-pause the parser; a nonzero value pauses */
-void http_parser_pause(http_parser *parser, int paused);
-
-/* Checks if this is the final chunk of the body. */
-int http_body_is_final(const http_parser *parser);
-
-#ifdef __cplusplus
-}
-#endif
-#endif

+ 0 - 3425
ext/http-parser/test.c

@@ -1,3425 +0,0 @@
-/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to
- * deal in the Software without restriction, including without limitation the
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- * sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
-#include "http_parser.h"
-#include <stdlib.h>
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h> /* rand */
-#include <string.h>
-#include <stdarg.h>
-
-#undef TRUE
-#define TRUE 1
-#undef FALSE
-#define FALSE 0
-
-#define MAX_HEADERS 13
-#define MAX_ELEMENT_SIZE 2048
-
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-
-static http_parser *parser;
-
-struct message {
-  const char *name; // for debugging purposes
-  const char *raw;
-  enum http_parser_type type;
-  enum http_method method;
-  int status_code;
-  char request_path[MAX_ELEMENT_SIZE];
-  char request_url[MAX_ELEMENT_SIZE];
-  char fragment[MAX_ELEMENT_SIZE];
-  char query_string[MAX_ELEMENT_SIZE];
-  char body[MAX_ELEMENT_SIZE];
-  size_t body_size;
-  const char *host;
-  const char *userinfo;
-  uint16_t port;
-  int num_headers;
-  enum { NONE=0, FIELD, VALUE } last_header_element;
-  char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
-  int should_keep_alive;
-
-  const char *upgrade; // upgraded body
-
-  unsigned short http_major;
-  unsigned short http_minor;
-
-  int message_begin_cb_called;
-  int headers_complete_cb_called;
-  int message_complete_cb_called;
-  int message_complete_on_eof;
-  int body_is_final;
-};
-
-static int currently_parsing_eof;
-
-static struct message messages[5];
-static int num_messages;
-static http_parser_settings *current_pause_parser;
-
-/* * R E Q U E S T S * */
-const struct message requests[] =
-#define CURL_GET 0
-{ {.name= "curl get"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /test HTTP/1.1\r\n"
-         "User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n"
-         "Host: 0.0.0.0=5000\r\n"
-         "Accept: */*\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/test"
-  ,.request_url= "/test"
-  ,.num_headers= 3
-  ,.headers=
-    { { "User-Agent", "curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1" }
-    , { "Host", "0.0.0.0=5000" }
-    , { "Accept", "*/*" }
-    }
-  ,.body= ""
-  }
-
-#define FIREFOX_GET 1
-, {.name= "firefox get"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /favicon.ico HTTP/1.1\r\n"
-         "Host: 0.0.0.0=5000\r\n"
-         "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
-         "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
-         "Accept-Language: en-us,en;q=0.5\r\n"
-         "Accept-Encoding: gzip,deflate\r\n"
-         "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
-         "Keep-Alive: 300\r\n"
-         "Connection: keep-alive\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/favicon.ico"
-  ,.request_url= "/favicon.ico"
-  ,.num_headers= 8
-  ,.headers=
-    { { "Host", "0.0.0.0=5000" }
-    , { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" }
-    , { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }
-    , { "Accept-Language", "en-us,en;q=0.5" }
-    , { "Accept-Encoding", "gzip,deflate" }
-    , { "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7" }
-    , { "Keep-Alive", "300" }
-    , { "Connection", "keep-alive" }
-    }
-  ,.body= ""
-  }
-
-#define DUMBFUCK 2
-, {.name= "dumbfuck"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /dumbfuck HTTP/1.1\r\n"
-         "aaaaaaaaaaaaa:++++++++++\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/dumbfuck"
-  ,.request_url= "/dumbfuck"
-  ,.num_headers= 1
-  ,.headers=
-    { { "aaaaaaaaaaaaa",  "++++++++++" }
-    }
-  ,.body= ""
-  }
-
-#define FRAGMENT_IN_URI 3
-, {.name= "fragment in url"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= "page=1"
-  ,.fragment= "posts-17408"
-  ,.request_path= "/forums/1/topics/2375"
-  /* XXX request url does include fragment? */
-  ,.request_url= "/forums/1/topics/2375?page=1#posts-17408"
-  ,.num_headers= 0
-  ,.body= ""
-  }
-
-#define GET_NO_HEADERS_NO_BODY 4
-, {.name= "get no headers no body"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE /* would need Connection: close */
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/get_no_headers_no_body/world"
-  ,.request_url= "/get_no_headers_no_body/world"
-  ,.num_headers= 0
-  ,.body= ""
-  }
-
-#define GET_ONE_HEADER_NO_BODY 5
-, {.name= "get one header no body"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
-         "Accept: */*\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE /* would need Connection: close */
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/get_one_header_no_body"
-  ,.request_url= "/get_one_header_no_body"
-  ,.num_headers= 1
-  ,.headers=
-    { { "Accept" , "*/*" }
-    }
-  ,.body= ""
-  }
-
-#define GET_FUNKY_CONTENT_LENGTH 6
-, {.name= "get funky content length body hello"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
-         "conTENT-Length: 5\r\n"
-         "\r\n"
-         "HELLO"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 0
-  ,.method= HTTP_GET
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/get_funky_content_length_body_hello"
-  ,.request_url= "/get_funky_content_length_body_hello"
-  ,.num_headers= 1
-  ,.headers=
-    { { "conTENT-Length" , "5" }
-    }
-  ,.body= "HELLO"
-  }
-
-#define POST_IDENTITY_BODY_WORLD 7
-, {.name= "post identity body world"
-  ,.type= HTTP_REQUEST
-  ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
-         "Accept: */*\r\n"
-         "Transfer-Encoding: identity\r\n"
-         "Content-Length: 5\r\n"
-         "\r\n"
-         "World"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_POST
-  ,.query_string= "q=search"
-  ,.fragment= "hey"
-  ,.request_path= "/post_identity_body_world"
-  ,.request_url= "/post_identity_body_world?q=search#hey"
-  ,.num_headers= 3
-  ,.headers=
-    { { "Accept", "*/*" }
-    , { "Transfer-Encoding", "identity" }
-    , { "Content-Length", "5" }
-    }
-  ,.body= "World"
-  }
-
-#define POST_CHUNKED_ALL_YOUR_BASE 8
-, {.name= "post - chunked body: all your base are belong to us"
-  ,.type= HTTP_REQUEST
-  ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
-         "Transfer-Encoding: chunked\r\n"
-         "\r\n"
-         "1e\r\nall your base are belong to us\r\n"
-         "0\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_POST
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/post_chunked_all_your_base"
-  ,.request_url= "/post_chunked_all_your_base"
-  ,.num_headers= 1
-  ,.headers=
-    { { "Transfer-Encoding" , "chunked" }
-    }
-  ,.body= "all your base are belong to us"
-  }
-
-#define TWO_CHUNKS_MULT_ZERO_END 9
-, {.name= "two chunks ; triple zero ending"
-  ,.type= HTTP_REQUEST
-  ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
-         "Transfer-Encoding: chunked\r\n"
-         "\r\n"
-         "5\r\nhello\r\n"
-         "6\r\n world\r\n"
-         "000\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_POST
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/two_chunks_mult_zero_end"
-  ,.request_url= "/two_chunks_mult_zero_end"
-  ,.num_headers= 1
-  ,.headers=
-    { { "Transfer-Encoding", "chunked" }
-    }
-  ,.body= "hello world"
-  }
-
-#define CHUNKED_W_TRAILING_HEADERS 10
-, {.name= "chunked with trailing headers. blech."
-  ,.type= HTTP_REQUEST
-  ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
-         "Transfer-Encoding: chunked\r\n"
-         "\r\n"
-         "5\r\nhello\r\n"
-         "6\r\n world\r\n"
-         "0\r\n"
-         "Vary: *\r\n"
-         "Content-Type: text/plain\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_POST
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/chunked_w_trailing_headers"
-  ,.request_url= "/chunked_w_trailing_headers"
-  ,.num_headers= 3
-  ,.headers=
-    { { "Transfer-Encoding",  "chunked" }
-    , { "Vary", "*" }
-    , { "Content-Type", "text/plain" }
-    }
-  ,.body= "hello world"
-  }
-
-#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
-, {.name= "with bullshit after the length"
-  ,.type= HTTP_REQUEST
-  ,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
-         "Transfer-Encoding: chunked\r\n"
-         "\r\n"
-         "5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
-         "6; blahblah; blah\r\n world\r\n"
-         "0\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_POST
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/chunked_w_bullshit_after_length"
-  ,.request_url= "/chunked_w_bullshit_after_length"
-  ,.num_headers= 1
-  ,.headers=
-    { { "Transfer-Encoding", "chunked" }
-    }
-  ,.body= "hello world"
-  }
-
-#define WITH_QUOTES 12
-, {.name= "with quotes"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= "foo=\"bar\""
-  ,.fragment= ""
-  ,.request_path= "/with_\"stupid\"_quotes"
-  ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\""
-  ,.num_headers= 0
-  ,.headers= { }
-  ,.body= ""
-  }
-
-#define APACHEBENCH_GET 13
-/* The server receiving this request SHOULD NOT wait for EOF
- * to know that content-length == 0.
- * How to represent this in a unit test? message_complete_on_eof
- * Compare with NO_CONTENT_LENGTH_RESPONSE.
- */
-, {.name = "apachebench get"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /test HTTP/1.0\r\n"
-         "Host: 0.0.0.0:5000\r\n"
-         "User-Agent: ApacheBench/2.3\r\n"
-         "Accept: */*\r\n\r\n"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 0
-  ,.method= HTTP_GET
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/test"
-  ,.request_url= "/test"
-  ,.num_headers= 3
-  ,.headers= { { "Host", "0.0.0.0:5000" }
-             , { "User-Agent", "ApacheBench/2.3" }
-             , { "Accept", "*/*" }
-             }
-  ,.body= ""
-  }
-
-#define QUERY_URL_WITH_QUESTION_MARK_GET 14
-/* Some clients include '?' characters in query strings.
- */
-, {.name = "query url with question mark"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= "foo=bar?baz"
-  ,.fragment= ""
-  ,.request_path= "/test.cgi"
-  ,.request_url= "/test.cgi?foo=bar?baz"
-  ,.num_headers= 0
-  ,.headers= {}
-  ,.body= ""
-  }
-
-#define PREFIX_NEWLINE_GET 15
-/* Some clients, especially after a POST in a keep-alive connection,
- * will send an extra CRLF before the next request
- */
-, {.name = "newline prefix get"
-  ,.type= HTTP_REQUEST
-  ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/test"
-  ,.request_url= "/test"
-  ,.num_headers= 0
-  ,.headers= { }
-  ,.body= ""
-  }
-
-#define UPGRADE_REQUEST 16
-, {.name = "upgrade request"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /demo HTTP/1.1\r\n"
-         "Host: example.com\r\n"
-         "Connection: Upgrade\r\n"
-         "Sec-WebSocket-Key2: 12998 5 Y3 1  .P00\r\n"
-         "Sec-WebSocket-Protocol: sample\r\n"
-         "Upgrade: WebSocket\r\n"
-         "Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5\r\n"
-         "Origin: http://example.com\r\n"
-         "\r\n"
-         "Hot diggity dogg"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/demo"
-  ,.request_url= "/demo"
-  ,.num_headers= 7
-  ,.upgrade="Hot diggity dogg"
-  ,.headers= { { "Host", "example.com" }
-             , { "Connection", "Upgrade" }
-             , { "Sec-WebSocket-Key2", "12998 5 Y3 1  .P00" }
-             , { "Sec-WebSocket-Protocol", "sample" }
-             , { "Upgrade", "WebSocket" }
-             , { "Sec-WebSocket-Key1", "4 @1  46546xW%0l 1 5" }
-             , { "Origin", "http://example.com" }
-             }
-  ,.body= ""
-  }
-
-#define CONNECT_REQUEST 17
-, {.name = "connect request"
-  ,.type= HTTP_REQUEST
-  ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n"
-         "User-agent: Mozilla/1.1N\r\n"
-         "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
-         "\r\n"
-         "some data\r\n"
-         "and yet even more data"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 0
-  ,.method= HTTP_CONNECT
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= ""
-  ,.request_url= "0-home0.netscape.com:443"
-  ,.num_headers= 2
-  ,.upgrade="some data\r\nand yet even more data"
-  ,.headers= { { "User-agent", "Mozilla/1.1N" }
-             , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
-             }
-  ,.body= ""
-  }
-
-#define REPORT_REQ 18
-, {.name= "report request"
-  ,.type= HTTP_REQUEST
-  ,.raw= "REPORT /test HTTP/1.1\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_REPORT
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/test"
-  ,.request_url= "/test"
-  ,.num_headers= 0
-  ,.headers= {}
-  ,.body= ""
-  }
-
-#define NO_HTTP_VERSION 19
-, {.name= "request with no http version"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /\r\n"
-         "\r\n"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 0
-  ,.http_minor= 9
-  ,.method= HTTP_GET
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/"
-  ,.request_url= "/"
-  ,.num_headers= 0
-  ,.headers= {}
-  ,.body= ""
-  }
-
-#define MSEARCH_REQ 20
-, {.name= "m-search request"
-  ,.type= HTTP_REQUEST
-  ,.raw= "M-SEARCH * HTTP/1.1\r\n"
-         "HOST: 239.255.255.250:1900\r\n"
-         "MAN: \"ssdp:discover\"\r\n"
-         "ST: \"ssdp:all\"\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_MSEARCH
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "*"
-  ,.request_url= "*"
-  ,.num_headers= 3
-  ,.headers= { { "HOST", "239.255.255.250:1900" }
-             , { "MAN", "\"ssdp:discover\"" }
-             , { "ST", "\"ssdp:all\"" }
-             }
-  ,.body= ""
-  }
-
-#define LINE_FOLDING_IN_HEADER 21
-, {.name= "line folding in header value"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET / HTTP/1.1\r\n"
-         "Line1:   abc\r\n"
-         "\tdef\r\n"
-         " ghi\r\n"
-         "\t\tjkl\r\n"
-         "  mno \r\n"
-         "\t \tqrs\r\n"
-         "Line2: \t line2\t\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/"
-  ,.request_url= "/"
-  ,.num_headers= 2
-  ,.headers= { { "Line1", "abcdefghijklmno qrs" }
-             , { "Line2", "line2\t" }
-             }
-  ,.body= ""
-  }
-
-
-#define QUERY_TERMINATED_HOST 22
-, {.name= "host terminated by a query string"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= "hail=all"
-  ,.fragment= ""
-  ,.request_path= ""
-  ,.request_url= "http://hypnotoad.org?hail=all"
-  ,.host= "hypnotoad.org"
-  ,.num_headers= 0
-  ,.headers= { }
-  ,.body= ""
-  }
-
-#define QUERY_TERMINATED_HOSTPORT 23
-, {.name= "host:port terminated by a query string"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= "hail=all"
-  ,.fragment= ""
-  ,.request_path= ""
-  ,.request_url= "http://hypnotoad.org:1234?hail=all"
-  ,.host= "hypnotoad.org"
-  ,.port= 1234
-  ,.num_headers= 0
-  ,.headers= { }
-  ,.body= ""
-  }
-
-#define SPACE_TERMINATED_HOSTPORT 24
-, {.name= "host:port terminated by a space"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= ""
-  ,.request_url= "http://hypnotoad.org:1234"
-  ,.host= "hypnotoad.org"
-  ,.port= 1234
-  ,.num_headers= 0
-  ,.headers= { }
-  ,.body= ""
-  }
-
-#define PATCH_REQ 25
-, {.name = "PATCH request"
-  ,.type= HTTP_REQUEST
-  ,.raw= "PATCH /file.txt HTTP/1.1\r\n"
-         "Host: www.example.com\r\n"
-         "Content-Type: application/example\r\n"
-         "If-Match: \"e0023aa4e\"\r\n"
-         "Content-Length: 10\r\n"
-         "\r\n"
-         "cccccccccc"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_PATCH
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/file.txt"
-  ,.request_url= "/file.txt"
-  ,.num_headers= 4
-  ,.headers= { { "Host", "www.example.com" }
-             , { "Content-Type", "application/example" }
-             , { "If-Match", "\"e0023aa4e\"" }
-             , { "Content-Length", "10" }
-             }
-  ,.body= "cccccccccc"
-  }
-
-#define CONNECT_CAPS_REQUEST 26
-, {.name = "connect caps request"
-  ,.type= HTTP_REQUEST
-  ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n"
-         "User-agent: Mozilla/1.1N\r\n"
-         "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
-         "\r\n"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 0
-  ,.method= HTTP_CONNECT
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= ""
-  ,.request_url= "HOME0.NETSCAPE.COM:443"
-  ,.num_headers= 2
-  ,.upgrade=""
-  ,.headers= { { "User-agent", "Mozilla/1.1N" }
-             , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
-             }
-  ,.body= ""
-  }
-
-#if !HTTP_PARSER_STRICT
-#define UTF8_PATH_REQ 27
-, {.name= "utf-8 path request"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n"
-         "Host: github.com\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.query_string= "q=1"
-  ,.fragment= "narf"
-  ,.request_path= "/δ¶/δt/pope"
-  ,.request_url= "/δ¶/δt/pope?q=1#narf"
-  ,.num_headers= 1
-  ,.headers= { {"Host", "github.com" }
-             }
-  ,.body= ""
-  }
-
-#define HOSTNAME_UNDERSCORE 28
-, {.name = "hostname underscore"
-  ,.type= HTTP_REQUEST
-  ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n"
-         "User-agent: Mozilla/1.1N\r\n"
-         "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
-         "\r\n"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 0
-  ,.method= HTTP_CONNECT
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= ""
-  ,.request_url= "home_0.netscape.com:443"
-  ,.num_headers= 2
-  ,.upgrade=""
-  ,.headers= { { "User-agent", "Mozilla/1.1N" }
-             , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
-             }
-  ,.body= ""
-  }
-#endif  /* !HTTP_PARSER_STRICT */
-
-/* see https://github.com/ry/http-parser/issues/47 */
-#define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29
-, {.name = "eat CRLF between requests, no \"Connection: close\" header"
-  ,.raw= "POST / HTTP/1.1\r\n"
-         "Host: www.example.com\r\n"
-         "Content-Type: application/x-www-form-urlencoded\r\n"
-         "Content-Length: 4\r\n"
-         "\r\n"
-         "q=42\r\n" /* note the trailing CRLF */
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_POST
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/"
-  ,.request_url= "/"
-  ,.num_headers= 3
-  ,.upgrade= 0
-  ,.headers= { { "Host", "www.example.com" }
-             , { "Content-Type", "application/x-www-form-urlencoded" }
-             , { "Content-Length", "4" }
-             }
-  ,.body= "q=42"
-  }
-
-/* see https://github.com/ry/http-parser/issues/47 */
-#define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30
-, {.name = "eat CRLF between requests even if \"Connection: close\" is set"
-  ,.raw= "POST / HTTP/1.1\r\n"
-         "Host: www.example.com\r\n"
-         "Content-Type: application/x-www-form-urlencoded\r\n"
-         "Content-Length: 4\r\n"
-         "Connection: close\r\n"
-         "\r\n"
-         "q=42\r\n" /* note the trailing CRLF */
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_POST
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/"
-  ,.request_url= "/"
-  ,.num_headers= 4
-  ,.upgrade= 0
-  ,.headers= { { "Host", "www.example.com" }
-             , { "Content-Type", "application/x-www-form-urlencoded" }
-             , { "Content-Length", "4" }
-             , { "Connection", "close" }
-             }
-  ,.body= "q=42"
-  }
-
-#define PURGE_REQ 31
-, {.name = "PURGE request"
-  ,.type= HTTP_REQUEST
-  ,.raw= "PURGE /file.txt HTTP/1.1\r\n"
-         "Host: www.example.com\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_PURGE
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/file.txt"
-  ,.request_url= "/file.txt"
-  ,.num_headers= 1
-  ,.headers= { { "Host", "www.example.com" } }
-  ,.body= ""
-  }
-
-#define SEARCH_REQ 32
-, {.name = "SEARCH request"
-  ,.type= HTTP_REQUEST
-  ,.raw= "SEARCH / HTTP/1.1\r\n"
-         "Host: www.example.com\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_SEARCH
-  ,.query_string= ""
-  ,.fragment= ""
-  ,.request_path= "/"
-  ,.request_url= "/"
-  ,.num_headers= 1
-  ,.headers= { { "Host", "www.example.com" } }
-  ,.body= ""
-  }
-
-#define PROXY_WITH_BASIC_AUTH 33
-, {.name= "host:port and basic_auth"
-  ,.type= HTTP_REQUEST
-  ,.raw= "GET http://a%12:b!&*[email protected]:1234/toto HTTP/1.1\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.method= HTTP_GET
-  ,.fragment= ""
-  ,.request_path= "/toto"
-  ,.request_url= "http://a%12:b!&*[email protected]:1234/toto"
-  ,.host= "hypnotoad.org"
-  ,.userinfo= "a%12:b!&*$"
-  ,.port= 1234
-  ,.num_headers= 0
-  ,.headers= { }
-  ,.body= ""
-  }
-
-
-, {.name= NULL } /* sentinel */
-};
-
-/* * R E S P O N S E S * */
-const struct message responses[] =
-#define GOOGLE_301 0
-{ {.name= "google 301"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 301 Moved Permanently\r\n"
-         "Location: http://www.google.com/\r\n"
-         "Content-Type: text/html; charset=UTF-8\r\n"
-         "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n"
-         "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n"
-         "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */
-         "Cache-Control: public, max-age=2592000\r\n"
-         "Server: gws\r\n"
-         "Content-Length:  219  \r\n"
-         "\r\n"
-         "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
-         "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
-         "<H1>301 Moved</H1>\n"
-         "The document has moved\n"
-         "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
-         "</BODY></HTML>\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 301
-  ,.num_headers= 8
-  ,.headers=
-    { { "Location", "http://www.google.com/" }
-    , { "Content-Type", "text/html; charset=UTF-8" }
-    , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" }
-    , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" }
-    , { "X-$PrototypeBI-Version", "1.6.0.3" }
-    , { "Cache-Control", "public, max-age=2592000" }
-    , { "Server", "gws" }
-    , { "Content-Length", "219  " }
-    }
-  ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n"
-          "<TITLE>301 Moved</TITLE></HEAD><BODY>\n"
-          "<H1>301 Moved</H1>\n"
-          "The document has moved\n"
-          "<A HREF=\"http://www.google.com/\">here</A>.\r\n"
-          "</BODY></HTML>\r\n"
-  }
-
-#define NO_CONTENT_LENGTH_RESPONSE 1
-/* The client should wait for the server's EOF. That is, when content-length
- * is not specified, and "Connection: close", the end of body is specified
- * by the EOF.
- * Compare with APACHEBENCH_GET
- */
-, {.name= "no content-length response"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 200 OK\r\n"
-         "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n"
-         "Server: Apache\r\n"
-         "X-Powered-By: Servlet/2.5 JSP/2.1\r\n"
-         "Content-Type: text/xml; charset=utf-8\r\n"
-         "Connection: close\r\n"
-         "\r\n"
-         "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-         "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
-         "  <SOAP-ENV:Body>\n"
-         "    <SOAP-ENV:Fault>\n"
-         "       <faultcode>SOAP-ENV:Client</faultcode>\n"
-         "       <faultstring>Client Error</faultstring>\n"
-         "    </SOAP-ENV:Fault>\n"
-         "  </SOAP-ENV:Body>\n"
-         "</SOAP-ENV:Envelope>"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= TRUE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 200
-  ,.num_headers= 5
-  ,.headers=
-    { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" }
-    , { "Server", "Apache" }
-    , { "X-Powered-By", "Servlet/2.5 JSP/2.1" }
-    , { "Content-Type", "text/xml; charset=utf-8" }
-    , { "Connection", "close" }
-    }
-  ,.body= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
-          "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n"
-          "  <SOAP-ENV:Body>\n"
-          "    <SOAP-ENV:Fault>\n"
-          "       <faultcode>SOAP-ENV:Client</faultcode>\n"
-          "       <faultstring>Client Error</faultstring>\n"
-          "    </SOAP-ENV:Fault>\n"
-          "  </SOAP-ENV:Body>\n"
-          "</SOAP-ENV:Envelope>"
-  }
-
-#define NO_HEADERS_NO_BODY_404 2
-, {.name= "404 no headers no body"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= TRUE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 404
-  ,.num_headers= 0
-  ,.headers= {}
-  ,.body_size= 0
-  ,.body= ""
-  }
-
-#define NO_REASON_PHRASE 3
-, {.name= "301 no response phrase"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 301\r\n\r\n"
-  ,.should_keep_alive = FALSE
-  ,.message_complete_on_eof= TRUE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 301
-  ,.num_headers= 0
-  ,.headers= {}
-  ,.body= ""
-  }
-
-#define TRAILING_SPACE_ON_CHUNKED_BODY 4
-, {.name="200 trailing space on chunked body"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 200 OK\r\n"
-         "Content-Type: text/plain\r\n"
-         "Transfer-Encoding: chunked\r\n"
-         "\r\n"
-         "25  \r\n"
-         "This is the data in the first chunk\r\n"
-         "\r\n"
-         "1C\r\n"
-         "and this is the second one\r\n"
-         "\r\n"
-         "0  \r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 200
-  ,.num_headers= 2
-  ,.headers=
-    { {"Content-Type", "text/plain" }
-    , {"Transfer-Encoding", "chunked" }
-    }
-  ,.body_size = 37+28
-  ,.body =
-         "This is the data in the first chunk\r\n"
-         "and this is the second one\r\n"
-
-  }
-
-#define NO_CARRIAGE_RET 5
-, {.name="no carriage ret"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 200 OK\n"
-         "Content-Type: text/html; charset=utf-8\n"
-         "Connection: close\n"
-         "\n"
-         "these headers are from http://news.ycombinator.com/"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= TRUE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 200
-  ,.num_headers= 2
-  ,.headers=
-    { {"Content-Type", "text/html; charset=utf-8" }
-    , {"Connection", "close" }
-    }
-  ,.body= "these headers are from http://news.ycombinator.com/"
-  }
-
-#define PROXY_CONNECTION 6
-, {.name="proxy connection"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 200 OK\r\n"
-         "Content-Type: text/html; charset=UTF-8\r\n"
-         "Content-Length: 11\r\n"
-         "Proxy-Connection: close\r\n"
-         "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n"
-         "\r\n"
-         "hello world"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 200
-  ,.num_headers= 4
-  ,.headers=
-    { {"Content-Type", "text/html; charset=UTF-8" }
-    , {"Content-Length", "11" }
-    , {"Proxy-Connection", "close" }
-    , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"}
-    }
-  ,.body= "hello world"
-  }
-
-#define UNDERSTORE_HEADER_KEY 7
-  // shown by
-  // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;"
-, {.name="underscore header key"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 200 OK\r\n"
-         "Server: DCLK-AdSvr\r\n"
-         "Content-Type: text/xml\r\n"
-         "Content-Length: 0\r\n"
-         "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 200
-  ,.num_headers= 4
-  ,.headers=
-    { {"Server", "DCLK-AdSvr" }
-    , {"Content-Type", "text/xml" }
-    , {"Content-Length", "0" }
-    , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" }
-    }
-  ,.body= ""
-  }
-
-#define BONJOUR_MADAME_FR 8
-/* The client should not merge two headers fields when the first one doesn't
- * have a value.
- */
-, {.name= "bonjourmadame.fr"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.0 301 Moved Permanently\r\n"
-         "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n"
-         "Server: Apache/2.2.3 (Red Hat)\r\n"
-         "Cache-Control: public\r\n"
-         "Pragma: \r\n"
-         "Location: http://www.bonjourmadame.fr/\r\n"
-         "Vary: Accept-Encoding\r\n"
-         "Content-Length: 0\r\n"
-         "Content-Type: text/html; charset=UTF-8\r\n"
-         "Connection: keep-alive\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 0
-  ,.status_code= 301
-  ,.num_headers= 9
-  ,.headers=
-    { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" }
-    , { "Server", "Apache/2.2.3 (Red Hat)" }
-    , { "Cache-Control", "public" }
-    , { "Pragma", "" }
-    , { "Location", "http://www.bonjourmadame.fr/" }
-    , { "Vary",  "Accept-Encoding" }
-    , { "Content-Length", "0" }
-    , { "Content-Type", "text/html; charset=UTF-8" }
-    , { "Connection", "keep-alive" }
-    }
-  ,.body= ""
-  }
-
-#define RES_FIELD_UNDERSCORE 9
-/* Should handle spaces in header fields */
-, {.name= "field underscore"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 200 OK\r\n"
-         "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n"
-         "Server: Apache\r\n"
-         "Cache-Control: no-cache, must-revalidate\r\n"
-         "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
-         ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n"
-         "Vary: Accept-Encoding\r\n"
-         "_eep-Alive: timeout=45\r\n" /* semantic value ignored */
-         "_onnection: Keep-Alive\r\n" /* semantic value ignored */
-         "Transfer-Encoding: chunked\r\n"
-         "Content-Type: text/html\r\n"
-         "Connection: close\r\n"
-         "\r\n"
-         "0\r\n\r\n"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 200
-  ,.num_headers= 11
-  ,.headers=
-    { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" }
-    , { "Server", "Apache" }
-    , { "Cache-Control", "no-cache, must-revalidate" }
-    , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" }
-    , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" }
-    , { "Vary", "Accept-Encoding" }
-    , { "_eep-Alive", "timeout=45" }
-    , { "_onnection", "Keep-Alive" }
-    , { "Transfer-Encoding", "chunked" }
-    , { "Content-Type", "text/html" }
-    , { "Connection", "close" }
-    }
-  ,.body= ""
-  }
-
-#define NON_ASCII_IN_STATUS_LINE 10
-/* Should handle non-ASCII in status line */
-, {.name= "non-ASCII in status line"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n"
-         "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n"
-         "Content-Length: 0\r\n"
-         "Connection: close\r\n"
-         "\r\n"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 500
-  ,.num_headers= 3
-  ,.headers=
-    { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" }
-    , { "Content-Length", "0" }
-    , { "Connection", "close" }
-    }
-  ,.body= ""
-  }
-
-#define HTTP_VERSION_0_9 11
-/* Should handle HTTP/0.9 */
-, {.name= "http version 0.9"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/0.9 200 OK\r\n"
-         "\r\n"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= TRUE
-  ,.http_major= 0
-  ,.http_minor= 9
-  ,.status_code= 200
-  ,.num_headers= 0
-  ,.headers=
-    {}
-  ,.body= ""
-  }
-
-#define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12
-/* The client should wait for the server's EOF. That is, when neither
- * content-length nor transfer-encoding is specified, the end of body
- * is specified by the EOF.
- */
-, {.name= "neither content-length nor transfer-encoding response"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 200 OK\r\n"
-         "Content-Type: text/plain\r\n"
-         "\r\n"
-         "hello world"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= TRUE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 200
-  ,.num_headers= 1
-  ,.headers=
-    { { "Content-Type", "text/plain" }
-    }
-  ,.body= "hello world"
-  }
-
-#define NO_BODY_HTTP10_KA_200 13
-, {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.0 200 OK\r\n"
-         "Connection: keep-alive\r\n"
-         "\r\n"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= TRUE
-  ,.http_major= 1
-  ,.http_minor= 0
-  ,.status_code= 200
-  ,.num_headers= 1
-  ,.headers=
-    { { "Connection", "keep-alive" }
-    }
-  ,.body_size= 0
-  ,.body= ""
-  }
-
-#define NO_BODY_HTTP10_KA_204 14
-, {.name= "HTTP/1.0 with keep-alive and a 204 status"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.0 204 No content\r\n"
-         "Connection: keep-alive\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 0
-  ,.status_code= 204
-  ,.num_headers= 1
-  ,.headers=
-    { { "Connection", "keep-alive" }
-    }
-  ,.body_size= 0
-  ,.body= ""
-  }
-
-#define NO_BODY_HTTP11_KA_200 15
-, {.name= "HTTP/1.1 with an EOF-terminated 200 status"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 200 OK\r\n"
-         "\r\n"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= TRUE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 200
-  ,.num_headers= 0
-  ,.headers={}
-  ,.body_size= 0
-  ,.body= ""
-  }
-
-#define NO_BODY_HTTP11_KA_204 16
-, {.name= "HTTP/1.1 with a 204 status"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 204 No content\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 204
-  ,.num_headers= 0
-  ,.headers={}
-  ,.body_size= 0
-  ,.body= ""
-  }
-
-#define NO_BODY_HTTP11_NOKA_204 17
-, {.name= "HTTP/1.1 with a 204 status and keep-alive disabled"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 204 No content\r\n"
-         "Connection: close\r\n"
-         "\r\n"
-  ,.should_keep_alive= FALSE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 204
-  ,.num_headers= 1
-  ,.headers=
-    { { "Connection", "close" }
-    }
-  ,.body_size= 0
-  ,.body= ""
-  }
-
-#define NO_BODY_HTTP11_KA_CHUNKED_200 18
-, {.name= "HTTP/1.1 with chunked endocing and a 200 response"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 200 OK\r\n"
-         "Transfer-Encoding: chunked\r\n"
-         "\r\n"
-         "0\r\n"
-         "\r\n"
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 200
-  ,.num_headers= 1
-  ,.headers=
-    { { "Transfer-Encoding", "chunked" }
-    }
-  ,.body_size= 0
-  ,.body= ""
-  }
-
-#if !HTTP_PARSER_STRICT
-#define SPACE_IN_FIELD_RES 19
-/* Should handle spaces in header fields */
-, {.name= "field space"
-  ,.type= HTTP_RESPONSE
-  ,.raw= "HTTP/1.1 200 OK\r\n"
-         "Server: Microsoft-IIS/6.0\r\n"
-         "X-Powered-By: ASP.NET\r\n"
-         "en-US Content-Type: text/xml\r\n" /* this is the problem */
-         "Content-Type: text/xml\r\n"
-         "Content-Length: 16\r\n"
-         "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n"
-         "Connection: keep-alive\r\n"
-         "\r\n"
-         "<xml>hello</xml>" /* fake body */
-  ,.should_keep_alive= TRUE
-  ,.message_complete_on_eof= FALSE
-  ,.http_major= 1
-  ,.http_minor= 1
-  ,.status_code= 200
-  ,.num_headers= 7
-  ,.headers=
-    { { "Server",  "Microsoft-IIS/6.0" }
-    , { "X-Powered-By", "ASP.NET" }
-    , { "en-US Content-Type", "text/xml" }
-    , { "Content-Type", "text/xml" }
-    , { "Content-Length", "16" }
-    , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" }
-    , { "Connection", "keep-alive" }
-    }
-  ,.body= "<xml>hello</xml>"
-  }
-#endif /* !HTTP_PARSER_STRICT */
-
-, {.name= NULL } /* sentinel */
-};
-
-/* strnlen() is a POSIX.2008 addition. Can't rely on it being available so
- * define it ourselves.
- */
-size_t
-strnlen(const char *s, size_t maxlen)
-{
-  const char *p;
-
-  p = memchr(s, '\0', maxlen);
-  if (p == NULL)
-    return maxlen;
-
-  return p - s;
-}
-
-size_t
-strlncat(char *dst, size_t len, const char *src, size_t n)
-{
-  size_t slen;
-  size_t dlen;
-  size_t rlen;
-  size_t ncpy;
-
-  slen = strnlen(src, n);
-  dlen = strnlen(dst, len);
-
-  if (dlen < len) {
-    rlen = len - dlen;
-    ncpy = slen < rlen ? slen : (rlen - 1);
-    memcpy(dst + dlen, src, ncpy);
-    dst[dlen + ncpy] = '\0';
-  }
-
-  assert(len > slen + dlen);
-  return slen + dlen;
-}
-
-size_t
-strlcat(char *dst, const char *src, size_t len)
-{
-  return strlncat(dst, len, src, (size_t) -1);
-}
-
-size_t
-strlncpy(char *dst, size_t len, const char *src, size_t n)
-{
-  size_t slen;
-  size_t ncpy;
-
-  slen = strnlen(src, n);
-
-  if (len > 0) {
-    ncpy = slen < len ? slen : (len - 1);
-    memcpy(dst, src, ncpy);
-    dst[ncpy] = '\0';
-  }
-
-  assert(len > slen);
-  return slen;
-}
-
-size_t
-strlcpy(char *dst, const char *src, size_t len)
-{
-  return strlncpy(dst, len, src, (size_t) -1);
-}
-
-int
-request_url_cb (http_parser *p, const char *buf, size_t len)
-{
-  assert(p == parser);
-  strlncat(messages[num_messages].request_url,
-           sizeof(messages[num_messages].request_url),
-           buf,
-           len);
-  return 0;
-}
-
-int
-status_complete_cb (http_parser *p) {
-  assert(p == parser);
-  p->data++;
-  return 0;
-}
-
-int
-header_field_cb (http_parser *p, const char *buf, size_t len)
-{
-  assert(p == parser);
-  struct message *m = &messages[num_messages];
-
-  if (m->last_header_element != FIELD)
-    m->num_headers++;
-
-  strlncat(m->headers[m->num_headers-1][0],
-           sizeof(m->headers[m->num_headers-1][0]),
-           buf,
-           len);
-
-  m->last_header_element = FIELD;
-
-  return 0;
-}
-
-int
-header_value_cb (http_parser *p, const char *buf, size_t len)
-{
-  assert(p == parser);
-  struct message *m = &messages[num_messages];
-
-  strlncat(m->headers[m->num_headers-1][1],
-           sizeof(m->headers[m->num_headers-1][1]),
-           buf,
-           len);
-
-  m->last_header_element = VALUE;
-
-  return 0;
-}
-
-void
-check_body_is_final (const http_parser *p)
-{
-  if (messages[num_messages].body_is_final) {
-    fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
-                    "on last on_body callback call "
-                    "but it doesn't! ***\n\n");
-    assert(0);
-    abort();
-  }
-  messages[num_messages].body_is_final = http_body_is_final(p);
-}
-
-int
-body_cb (http_parser *p, const char *buf, size_t len)
-{
-  assert(p == parser);
-  strlncat(messages[num_messages].body,
-           sizeof(messages[num_messages].body),
-           buf,
-           len);
-  messages[num_messages].body_size += len;
-  check_body_is_final(p);
- // printf("body_cb: '%s'\n", requests[num_messages].body);
-  return 0;
-}
-
-int
-count_body_cb (http_parser *p, const char *buf, size_t len)
-{
-  assert(p == parser);
-  assert(buf);
-  messages[num_messages].body_size += len;
-  check_body_is_final(p);
-  return 0;
-}
-
-int
-message_begin_cb (http_parser *p)
-{
-  assert(p == parser);
-  messages[num_messages].message_begin_cb_called = TRUE;
-  return 0;
-}
-
-int
-headers_complete_cb (http_parser *p)
-{
-  assert(p == parser);
-  messages[num_messages].method = parser->method;
-  messages[num_messages].status_code = parser->status_code;
-  messages[num_messages].http_major = parser->http_major;
-  messages[num_messages].http_minor = parser->http_minor;
-  messages[num_messages].headers_complete_cb_called = TRUE;
-  messages[num_messages].should_keep_alive = http_should_keep_alive(parser);
-  return 0;
-}
-
-int
-message_complete_cb (http_parser *p)
-{
-  assert(p == parser);
-  if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser))
-  {
-    fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same "
-                    "value in both on_message_complete and on_headers_complete "
-                    "but it doesn't! ***\n\n");
-    assert(0);
-    abort();
-  }
-
-  if (messages[num_messages].body_size &&
-      http_body_is_final(p) &&
-      !messages[num_messages].body_is_final)
-  {
-    fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 "
-                    "on last on_body callback call "
-                    "but it doesn't! ***\n\n");
-    assert(0);
-    abort();
-  }
-
-  messages[num_messages].message_complete_cb_called = TRUE;
-
-  messages[num_messages].message_complete_on_eof = currently_parsing_eof;
-
-  num_messages++;
-  return 0;
-}
-
-/* These dontcall_* callbacks exist so that we can verify that when we're
- * paused, no additional callbacks are invoked */
-int
-dontcall_message_begin_cb (http_parser *p)
-{
-  if (p) { } // gcc
-  fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n");
-  abort();
-}
-
-int
-dontcall_header_field_cb (http_parser *p, const char *buf, size_t len)
-{
-  if (p || buf || len) { } // gcc
-  fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n");
-  abort();
-}
-
-int
-dontcall_header_value_cb (http_parser *p, const char *buf, size_t len)
-{
-  if (p || buf || len) { } // gcc
-  fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n");
-  abort();
-}
-
-int
-dontcall_request_url_cb (http_parser *p, const char *buf, size_t len)
-{
-  if (p || buf || len) { } // gcc
-  fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n");
-  abort();
-}
-
-int
-dontcall_body_cb (http_parser *p, const char *buf, size_t len)
-{
-  if (p || buf || len) { } // gcc
-  fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n");
-  abort();
-}
-
-int
-dontcall_headers_complete_cb (http_parser *p)
-{
-  if (p) { } // gcc
-  fprintf(stderr, "\n\n*** on_headers_complete() called on paused "
-                  "parser ***\n\n");
-  abort();
-}
-
-int
-dontcall_message_complete_cb (http_parser *p)
-{
-  if (p) { } // gcc
-  fprintf(stderr, "\n\n*** on_message_complete() called on paused "
-                  "parser ***\n\n");
-  abort();
-}
-
-static http_parser_settings settings_dontcall =
-  {.on_message_begin = dontcall_message_begin_cb
-  ,.on_header_field = dontcall_header_field_cb
-  ,.on_header_value = dontcall_header_value_cb
-  ,.on_url = dontcall_request_url_cb
-  ,.on_body = dontcall_body_cb
-  ,.on_headers_complete = dontcall_headers_complete_cb
-  ,.on_message_complete = dontcall_message_complete_cb
-  };
-
-/* These pause_* callbacks always pause the parser and just invoke the regular
- * callback that tracks content. Before returning, we overwrite the parser
- * settings to point to the _dontcall variety so that we can verify that
- * the pause actually did, you know, pause. */
-int
-pause_message_begin_cb (http_parser *p)
-{
-  http_parser_pause(p, 1);
-  *current_pause_parser = settings_dontcall;
-  return message_begin_cb(p);
-}
-
-int
-pause_header_field_cb (http_parser *p, const char *buf, size_t len)
-{
-  http_parser_pause(p, 1);
-  *current_pause_parser = settings_dontcall;
-  return header_field_cb(p, buf, len);
-}
-
-int
-pause_header_value_cb (http_parser *p, const char *buf, size_t len)
-{
-  http_parser_pause(p, 1);
-  *current_pause_parser = settings_dontcall;
-  return header_value_cb(p, buf, len);
-}
-
-int
-pause_request_url_cb (http_parser *p, const char *buf, size_t len)
-{
-  http_parser_pause(p, 1);
-  *current_pause_parser = settings_dontcall;
-  return request_url_cb(p, buf, len);
-}
-
-int
-pause_body_cb (http_parser *p, const char *buf, size_t len)
-{
-  http_parser_pause(p, 1);
-  *current_pause_parser = settings_dontcall;
-  return body_cb(p, buf, len);
-}
-
-int
-pause_headers_complete_cb (http_parser *p)
-{
-  http_parser_pause(p, 1);
-  *current_pause_parser = settings_dontcall;
-  return headers_complete_cb(p);
-}
-
-int
-pause_message_complete_cb (http_parser *p)
-{
-  http_parser_pause(p, 1);
-  *current_pause_parser = settings_dontcall;
-  return message_complete_cb(p);
-}
-
-static http_parser_settings settings_pause =
-  {.on_message_begin = pause_message_begin_cb
-  ,.on_header_field = pause_header_field_cb
-  ,.on_header_value = pause_header_value_cb
-  ,.on_url = pause_request_url_cb
-  ,.on_body = pause_body_cb
-  ,.on_headers_complete = pause_headers_complete_cb
-  ,.on_message_complete = pause_message_complete_cb
-  };
-
-static http_parser_settings settings =
-  {.on_message_begin = message_begin_cb
-  ,.on_header_field = header_field_cb
-  ,.on_header_value = header_value_cb
-  ,.on_url = request_url_cb
-  ,.on_body = body_cb
-  ,.on_headers_complete = headers_complete_cb
-  ,.on_message_complete = message_complete_cb
-  };
-
-static http_parser_settings settings_count_body =
-  {.on_message_begin = message_begin_cb
-  ,.on_header_field = header_field_cb
-  ,.on_header_value = header_value_cb
-  ,.on_url = request_url_cb
-  ,.on_body = count_body_cb
-  ,.on_headers_complete = headers_complete_cb
-  ,.on_message_complete = message_complete_cb
-  };
-
-static http_parser_settings settings_null =
-  {.on_message_begin = 0
-  ,.on_header_field = 0
-  ,.on_header_value = 0
-  ,.on_url = 0
-  ,.on_body = 0
-  ,.on_headers_complete = 0
-  ,.on_message_complete = 0
-  };
-
-void
-parser_init (enum http_parser_type type)
-{
-  num_messages = 0;
-
-  assert(parser == NULL);
-
-  parser = malloc(sizeof(http_parser));
-
-  http_parser_init(parser, type);
-
-  memset(&messages, 0, sizeof messages);
-
-}
-
-void
-parser_free ()
-{
-  assert(parser);
-  free(parser);
-  parser = NULL;
-}
-
-size_t parse (const char *buf, size_t len)
-{
-  size_t nparsed;
-  currently_parsing_eof = (len == 0);
-  nparsed = http_parser_execute(parser, &settings, buf, len);
-  return nparsed;
-}
-
-size_t parse_count_body (const char *buf, size_t len)
-{
-  size_t nparsed;
-  currently_parsing_eof = (len == 0);
-  nparsed = http_parser_execute(parser, &settings_count_body, buf, len);
-  return nparsed;
-}
-
-size_t parse_pause (const char *buf, size_t len)
-{
-  size_t nparsed;
-  http_parser_settings s = settings_pause;
-
-  currently_parsing_eof = (len == 0);
-  current_pause_parser = &s;
-  nparsed = http_parser_execute(parser, current_pause_parser, buf, len);
-  return nparsed;
-}
-
-static inline int
-check_str_eq (const struct message *m,
-              const char *prop,
-              const char *expected,
-              const char *found) {
-  if ((expected == NULL) != (found == NULL)) {
-    printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
-    printf("expected %s\n", (expected == NULL) ? "NULL" : expected);
-    printf("   found %s\n", (found == NULL) ? "NULL" : found);
-    return 0;
-  }
-  if (expected != NULL && 0 != strcmp(expected, found)) {
-    printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
-    printf("expected '%s'\n", expected);
-    printf("   found '%s'\n", found);
-    return 0;
-  }
-  return 1;
-}
-
-static inline int
-check_num_eq (const struct message *m,
-              const char *prop,
-              int expected,
-              int found) {
-  if (expected != found) {
-    printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name);
-    printf("expected %d\n", expected);
-    printf("   found %d\n", found);
-    return 0;
-  }
-  return 1;
-}
-
-#define MESSAGE_CHECK_STR_EQ(expected, found, prop) \
-  if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0
-
-#define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \
-  if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0
-
-#define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn)           \
-do {                                                                 \
-  char ubuf[256];                                                    \
-                                                                     \
-  if ((u)->field_set & (1 << (fn))) {                                \
-    memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off,   \
-      (u)->field_data[(fn)].len);                                    \
-    ubuf[(u)->field_data[(fn)].len] = '\0';                          \
-  } else {                                                           \
-    ubuf[0] = '\0';                                                  \
-  }                                                                  \
-                                                                     \
-  check_str_eq(expected, #prop, expected->prop, ubuf);               \
-} while(0)
-
-int
-message_eq (int index, const struct message *expected)
-{
-  int i;
-  struct message *m = &messages[index];
-
-  MESSAGE_CHECK_NUM_EQ(expected, m, http_major);
-  MESSAGE_CHECK_NUM_EQ(expected, m, http_minor);
-
-  if (expected->type == HTTP_REQUEST) {
-    MESSAGE_CHECK_NUM_EQ(expected, m, method);
-  } else {
-    MESSAGE_CHECK_NUM_EQ(expected, m, status_code);
-  }
-
-  MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive);
-  MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof);
-
-  assert(m->message_begin_cb_called);
-  assert(m->headers_complete_cb_called);
-  assert(m->message_complete_cb_called);
-
-
-  MESSAGE_CHECK_STR_EQ(expected, m, request_url);
-
-  /* Check URL components; we can't do this w/ CONNECT since it doesn't
-   * send us a well-formed URL.
-   */
-  if (*m->request_url && m->method != HTTP_CONNECT) {
-    struct http_parser_url u;
-
-    if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) {
-      fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n",
-        m->request_url);
-      abort();
-    }
-
-    if (expected->host) {
-      MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST);
-    }
-
-    if (expected->userinfo) {
-      MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO);
-    }
-
-    m->port = (u.field_set & (1 << UF_PORT)) ?
-      u.port : 0;
-
-    MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY);
-    MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT);
-    MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH);
-    MESSAGE_CHECK_NUM_EQ(expected, m, port);
-  }
-
-  if (expected->body_size) {
-    MESSAGE_CHECK_NUM_EQ(expected, m, body_size);
-  } else {
-    MESSAGE_CHECK_STR_EQ(expected, m, body);
-  }
-
-  MESSAGE_CHECK_NUM_EQ(expected, m, num_headers);
-
-  int r;
-  for (i = 0; i < m->num_headers; i++) {
-    r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]);
-    if (!r) return 0;
-    r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]);
-    if (!r) return 0;
-  }
-
-  MESSAGE_CHECK_STR_EQ(expected, m, upgrade);
-
-  return 1;
-}
-
-/* Given a sequence of varargs messages, return the number of them that the
- * parser should successfully parse, taking into account that upgraded
- * messages prevent all subsequent messages from being parsed.
- */
-size_t
-count_parsed_messages(const size_t nmsgs, ...) {
-  size_t i;
-  va_list ap;
-
-  va_start(ap, nmsgs);
-
-  for (i = 0; i < nmsgs; i++) {
-    struct message *m = va_arg(ap, struct message *);
-
-    if (m->upgrade) {
-      va_end(ap);
-      return i + 1;
-    }
-  }
-
-  va_end(ap);
-  return nmsgs;
-}
-
-/* Given a sequence of bytes and the number of these that we were able to
- * parse, verify that upgrade bodies are correct.
- */
-void
-upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) {
-  va_list ap;
-  size_t i;
-  size_t off = 0;
- 
-  va_start(ap, nmsgs);
-
-  for (i = 0; i < nmsgs; i++) {
-    struct message *m = va_arg(ap, struct message *);
-
-    off += strlen(m->raw);
-
-    if (m->upgrade) {
-      off -= strlen(m->upgrade);
-
-      /* Check the portion of the response after its specified upgrade */
-      if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
-        abort();
-      }
-
-      /* Fix up the response so that message_eq() will verify the beginning
-       * of the upgrade */
-      *(body + nread + strlen(m->upgrade)) = '\0';
-      messages[num_messages -1 ].upgrade = body + nread;
-
-      va_end(ap);
-      return;
-    }
-  }
-
-  va_end(ap);
-  printf("\n\n*** Error: expected a message with upgrade ***\n");
-
-  abort();
-}
-
-static void
-print_error (const char *raw, size_t error_location)
-{
-  fprintf(stderr, "\n*** %s ***\n\n",
-          http_errno_description(HTTP_PARSER_ERRNO(parser)));
-
-  int this_line = 0, char_len = 0;
-  size_t i, j, len = strlen(raw), error_location_line = 0;
-  for (i = 0; i < len; i++) {
-    if (i == error_location) this_line = 1;
-    switch (raw[i]) {
-      case '\r':
-        char_len = 2;
-        fprintf(stderr, "\\r");
-        break;
-
-      case '\n':
-        char_len = 2;
-        fprintf(stderr, "\\n\n");
-
-        if (this_line) goto print;
-
-        error_location_line = 0;
-        continue;
-
-      default:
-        char_len = 1;
-        fputc(raw[i], stderr);
-        break;
-    }
-    if (!this_line) error_location_line += char_len;
-  }
-
-  fprintf(stderr, "[eof]\n");
-
- print:
-  for (j = 0; j < error_location_line; j++) {
-    fputc(' ', stderr);
-  }
-  fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location);
-}
-
-void
-test_preserve_data (void)
-{
-  char my_data[] = "application-specific data";
-  http_parser parser;
-  parser.data = my_data;
-  http_parser_init(&parser, HTTP_REQUEST);
-  if (parser.data != my_data) {
-    printf("\n*** parser.data not preserved accross http_parser_init ***\n\n");
-    abort();
-  }
-}
-
-struct url_test {
-  const char *name;
-  const char *url;
-  int is_connect;
-  struct http_parser_url u;
-  int rv;
-};
-
-const struct url_test url_tests[] =
-{ {.name="proxy request"
-  ,.url="http://hostname/"
-  ,.is_connect=0
-  ,.u=
-    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
-    ,.port=0
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{  7,  8 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{ 15,  1 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="proxy request with port"
-  ,.url="http://hostname:444/"
-  ,.is_connect=0
-  ,.u=
-    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
-    ,.port=444
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{  7,  8 } /* UF_HOST */
-      ,{ 16,  3 } /* UF_PORT */
-      ,{ 19,  1 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="CONNECT request"
-  ,.url="hostname:443"
-  ,.is_connect=1
-  ,.u=
-    {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
-    ,.port=443
-    ,.field_data=
-      {{  0,  0 } /* UF_SCHEMA */
-      ,{  0,  8 } /* UF_HOST */
-      ,{  9,  3 } /* UF_PORT */
-      ,{  0,  0 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="CONNECT request but not connect"
-  ,.url="hostname:443"
-  ,.is_connect=0
-  ,.rv=1
-  }
-
-, {.name="proxy ipv6 request"
-  ,.url="http://[1:2::3:4]/"
-  ,.is_connect=0
-  ,.u=
-    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
-    ,.port=0
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{  8,  8 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{ 17,  1 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="proxy ipv6 request with port"
-  ,.url="http://[1:2::3:4]:67/"
-  ,.is_connect=0
-  ,.u=
-    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH)
-    ,.port=67
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{  8,  8 } /* UF_HOST */
-      ,{ 18,  2 } /* UF_PORT */
-      ,{ 20,  1 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="CONNECT ipv6 address"
-  ,.url="[1:2::3:4]:443"
-  ,.is_connect=1
-  ,.u=
-    {.field_set=(1 << UF_HOST) | (1 << UF_PORT)
-    ,.port=443
-    ,.field_data=
-      {{  0,  0 } /* UF_SCHEMA */
-      ,{  1,  8 } /* UF_HOST */
-      ,{ 11,  3 } /* UF_PORT */
-      ,{  0,  0 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="ipv4 in ipv6 address"
-  ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/"
-  ,.is_connect=0
-  ,.u=
-    {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH)
-    ,.port=0
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{  8, 37 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{ 46,  1 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="extra ? in query string"
-  ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css,"
-  "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css,"
-  "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css"
-  ,.is_connect=0
-  ,.u=
-    {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY)
-    ,.port=0
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{  7, 10 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{ 17, 12 } /* UF_PATH */
-      ,{ 30,187 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="space URL encoded"
-  ,.url="/toto.html?toto=a%20b"
-  ,.is_connect=0
-  ,.u=
-    {.field_set= (1<<UF_PATH) | (1<<UF_QUERY)
-    ,.port=0
-    ,.field_data=
-      {{  0,  0 } /* UF_SCHEMA */
-      ,{  0,  0 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{  0, 10 } /* UF_PATH */
-      ,{ 11, 10 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-
-, {.name="URL fragment"
-  ,.url="/toto.html#titi"
-  ,.is_connect=0
-  ,.u=
-    {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT)
-    ,.port=0
-    ,.field_data=
-      {{  0,  0 } /* UF_SCHEMA */
-      ,{  0,  0 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{  0, 10 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{ 11,  4 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="complex URL fragment"
-  ,.url="http://www.webmasterworld.com/r.cgi?f=21&d=8405&url="
-    "http://www.example.com/index.html?foo=bar&hello=world#midpage"
-  ,.is_connect=0
-  ,.u=
-    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\
-      (1<<UF_FRAGMENT)
-    ,.port=0
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{  7, 22 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{ 29,  6 } /* UF_PATH */
-      ,{ 36, 69 } /* UF_QUERY */
-      ,{106,  7 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="complex URL from node js url parser doc"
-  ,.url="http://host.com:8080/p/a/t/h?query=string#hash"
-  ,.is_connect=0
-  ,.u=
-    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
-      (1<<UF_QUERY) | (1<<UF_FRAGMENT)
-    ,.port=8080
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{  7,  8 } /* UF_HOST */
-      ,{ 16,  4 } /* UF_PORT */
-      ,{ 20,  8 } /* UF_PATH */
-      ,{ 29, 12 } /* UF_QUERY */
-      ,{ 42,  4 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="complex URL with basic auth from node js url parser doc"
-  ,.url="http://a:[email protected]:8080/p/a/t/h?query=string#hash"
-  ,.is_connect=0
-  ,.u=
-    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\
-      (1<<UF_QUERY) | (1<<UF_FRAGMENT) | (1<<UF_USERINFO)
-    ,.port=8080
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{ 11,  8 } /* UF_HOST */
-      ,{ 20,  4 } /* UF_PORT */
-      ,{ 24,  8 } /* UF_PATH */
-      ,{ 33, 12 } /* UF_QUERY */
-      ,{ 46,  4 } /* UF_FRAGMENT */
-      ,{  7,  3 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="double @"
-  ,.url="http://a:b@@hostname:443/"
-  ,.is_connect=0
-  ,.rv=1
-  }
-
-, {.name="proxy empty host"
-  ,.url="http://:443/"
-  ,.is_connect=0
-  ,.rv=1
-  }
-
-, {.name="proxy empty port"
-  ,.url="http://hostname:/"
-  ,.is_connect=0
-  ,.rv=1
-  }
-
-, {.name="CONNECT with basic auth"
-  ,.url="a:b@hostname:443"
-  ,.is_connect=1
-  ,.rv=1
-  }
-
-, {.name="CONNECT empty host"
-  ,.url=":443"
-  ,.is_connect=1
-  ,.rv=1
-  }
-
-, {.name="CONNECT empty port"
-  ,.url="hostname:"
-  ,.is_connect=1
-  ,.rv=1
-  }
-
-, {.name="CONNECT with extra bits"
-  ,.url="hostname:443/"
-  ,.is_connect=1
-  ,.rv=1
-  }
-
-, {.name="space in URL"
-  ,.url="/foo bar/"
-  ,.rv=1 /* s_dead */
-  }
-
-, {.name="proxy basic auth with space url encoded"
-  ,.url="http://a%20:[email protected]/"
-  ,.is_connect=0
-  ,.u=
-    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
-    ,.port=0
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{ 14,  8 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{ 22,  1 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  7,  6 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="carriage return in URL"
-  ,.url="/foo\rbar/"
-  ,.rv=1 /* s_dead */
-  }
-
-, {.name="proxy double : in URL"
-  ,.url="http://hostname::443/"
-  ,.rv=1 /* s_dead */
-  }
-
-, {.name="proxy basic auth with double :"
-  ,.url="http://a::[email protected]/"
-  ,.is_connect=0
-  ,.u=
-    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
-    ,.port=0
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{ 12,  8 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{ 20,  1 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  7,  4 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="line feed in URL"
-  ,.url="/foo\nbar/"
-  ,.rv=1 /* s_dead */
-  }
-
-, {.name="proxy empty basic auth"
-  ,.url="http://@hostname/fo"
-  ,.u=
-    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH)
-    ,.port=0
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{  8,  8 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{ 16,  3 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-, {.name="proxy line feed in hostname"
-  ,.url="http://host\name/fo"
-  ,.rv=1 /* s_dead */
-  }
-
-, {.name="proxy % in hostname"
-  ,.url="http://host%name/fo"
-  ,.rv=1 /* s_dead */
-  }
-
-, {.name="proxy ; in hostname"
-  ,.url="http://host;ame/fo"
-  ,.rv=1 /* s_dead */
-  }
-
-, {.name="proxy basic auth with unreservedchars"
-  ,.url="http://a!;[email protected]/"
-  ,.is_connect=0
-  ,.u=
-    {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO)
-    ,.port=0
-    ,.field_data=
-      {{  0,  4 } /* UF_SCHEMA */
-      ,{ 17,  8 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{ 25,  1 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  7,  9 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="proxy only empty basic auth"
-  ,.url="http://@/fo"
-  ,.rv=1 /* s_dead */
-  }
-
-, {.name="proxy only basic auth"
-  ,.url="http://toto@/fo"
-  ,.rv=1 /* s_dead */
-  }
-
-, {.name="proxy emtpy hostname"
-  ,.url="http:///fo"
-  ,.rv=1 /* s_dead */
-  }
-
-, {.name="proxy = in URL"
-  ,.url="http://host=ame/fo"
-  ,.rv=1 /* s_dead */
-  }
-
-#if HTTP_PARSER_STRICT
-
-, {.name="tab in URL"
-  ,.url="/foo\tbar/"
-  ,.rv=1 /* s_dead */
-  }
-
-, {.name="form feed in URL"
-  ,.url="/foo\fbar/"
-  ,.rv=1 /* s_dead */
-  }
-
-#else /* !HTTP_PARSER_STRICT */
-
-, {.name="tab in URL"
-  ,.url="/foo\tbar/"
-  ,.u=
-    {.field_set=(1 << UF_PATH)
-    ,.field_data=
-      {{  0,  0 } /* UF_SCHEMA */
-      ,{  0,  0 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{  0,  9 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-
-, {.name="form feed in URL"
-  ,.url="/foo\fbar/"
-  ,.u=
-    {.field_set=(1 << UF_PATH)
-    ,.field_data=
-      {{  0,  0 } /* UF_SCHEMA */
-      ,{  0,  0 } /* UF_HOST */
-      ,{  0,  0 } /* UF_PORT */
-      ,{  0,  9 } /* UF_PATH */
-      ,{  0,  0 } /* UF_QUERY */
-      ,{  0,  0 } /* UF_FRAGMENT */
-      ,{  0,  0 } /* UF_USERINFO */
-      }
-    }
-  ,.rv=0
-  }
-#endif
-};
-
-void
-dump_url (const char *url, const struct http_parser_url *u)
-{
-  unsigned int i;
-
-  printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
-  for (i = 0; i < UF_MAX; i++) {
-    if ((u->field_set & (1 << i)) == 0) {
-      printf("\tfield_data[%u]: unset\n", i);
-      continue;
-    }
-
-    printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"",
-           i,
-           u->field_data[i].off,
-           u->field_data[i].len,
-           u->field_data[i].len,
-           url + u->field_data[i].off);
-  }
-}
-
-void
-test_parse_url (void)
-{
-  struct http_parser_url u;
-  const struct url_test *test;
-  unsigned int i;
-  int rv;
-
-  for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) {
-    test = &url_tests[i];
-    memset(&u, 0, sizeof(u));
-
-    rv = http_parser_parse_url(test->url,
-                               strlen(test->url),
-                               test->is_connect,
-                               &u);
-
-    if (test->rv == 0) {
-      if (rv != 0) {
-        printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
-               "unexpected rv %d ***\n\n", test->url, test->name, rv);
-        abort();
-      }
-
-      if (memcmp(&u, &test->u, sizeof(u)) != 0) {
-        printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n",
-               test->url, test->name);
-
-        printf("target http_parser_url:\n");
-        dump_url(test->url, &test->u);
-        printf("result http_parser_url:\n");
-        dump_url(test->url, &u);
-
-        abort();
-      }
-    } else {
-      /* test->rv != 0 */
-      if (rv == 0) {
-        printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, "
-               "unexpected rv %d ***\n\n", test->url, test->name, rv);
-        abort();
-      }
-    }
-  }
-}
-
-void
-test_method_str (void)
-{
-  assert(0 == strcmp("GET", http_method_str(HTTP_GET)));
-  assert(0 == strcmp("<unknown>", http_method_str(1337)));
-}
-
-void
-test_message (const struct message *message)
-{
-  size_t raw_len = strlen(message->raw);
-  size_t msg1len;
-  for (msg1len = 0; msg1len < raw_len; msg1len++) {
-    parser_init(message->type);
-
-    size_t read;
-    const char *msg1 = message->raw;
-    const char *msg2 = msg1 + msg1len;
-    size_t msg2len = raw_len - msg1len;
-
-    if (msg1len) {
-      read = parse(msg1, msg1len);
-
-      if (message->upgrade && parser->upgrade) {
-        messages[num_messages - 1].upgrade = msg1 + read;
-        goto test;
-      }
-
-      if (read != msg1len) {
-        print_error(msg1, read);
-        abort();
-      }
-    }
-
-
-    read = parse(msg2, msg2len);
-
-    if (message->upgrade && parser->upgrade) {
-      messages[num_messages - 1].upgrade = msg2 + read;
-      goto test;
-    }
-
-    if (read != msg2len) {
-      print_error(msg2, read);
-      abort();
-    }
-
-    read = parse(NULL, 0);
-
-    if (read != 0) {
-      print_error(message->raw, read);
-      abort();
-    }
-
-  test:
-
-    if (num_messages != 1) {
-      printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
-      abort();
-    }
-
-    if(!message_eq(0, message)) abort();
-
-    parser_free();
-  }
-}
-
-void
-test_message_count_body (const struct message *message)
-{
-  parser_init(message->type);
-
-  size_t read;
-  size_t l = strlen(message->raw);
-  size_t i, toread;
-  size_t chunk = 4024;
-
-  for (i = 0; i < l; i+= chunk) {
-    toread = MIN(l-i, chunk);
-    read = parse_count_body(message->raw + i, toread);
-    if (read != toread) {
-      print_error(message->raw, read);
-      abort();
-    }
-  }
-
-
-  read = parse_count_body(NULL, 0);
-  if (read != 0) {
-    print_error(message->raw, read);
-    abort();
-  }
-
-  if (num_messages != 1) {
-    printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name);
-    abort();
-  }
-
-  if(!message_eq(0, message)) abort();
-
-  parser_free();
-}
-
-void
-test_simple (const char *buf, enum http_errno err_expected)
-{
-  parser_init(HTTP_REQUEST);
-
-  size_t parsed;
-  int pass;
-  enum http_errno err;
-
-  parsed = parse(buf, strlen(buf));
-  pass = (parsed == strlen(buf));
-  err = HTTP_PARSER_ERRNO(parser);
-  parsed = parse(NULL, 0);
-  pass &= (parsed == 0);
-
-  parser_free();
-
-  /* In strict mode, allow us to pass with an unexpected HPE_STRICT as
-   * long as the caller isn't expecting success.
-   */
-#if HTTP_PARSER_STRICT
-  if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) {
-#else
-  if (err_expected != err) {
-#endif
-    fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n",
-        http_errno_name(err_expected), http_errno_name(err), buf);
-    abort();
-  }
-}
-
-void
-test_header_overflow_error (int req)
-{
-  http_parser parser;
-  http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
-  size_t parsed;
-  const char *buf;
-  buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.0 200 OK\r\n";
-  parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf));
-  assert(parsed == strlen(buf));
-
-  buf = "header-key: header-value\r\n";
-  size_t buflen = strlen(buf);
-
-  int i;
-  for (i = 0; i < 10000; i++) {
-    parsed = http_parser_execute(&parser, &settings_null, buf, buflen);
-    if (parsed != buflen) {
-      //fprintf(stderr, "error found on iter %d\n", i);
-      assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW);
-      return;
-    }
-  }
-
-  fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n");
-  abort();
-}
-
-static void
-test_content_length_overflow (const char *buf, size_t buflen, int expect_ok)
-{
-  http_parser parser;
-  http_parser_init(&parser, HTTP_RESPONSE);
-  http_parser_execute(&parser, &settings_null, buf, buflen);
-
-  if (expect_ok)
-    assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK);
-  else
-    assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH);
-}
-
-void
-test_header_content_length_overflow_error (void)
-{
-#define X(size)                                                               \
-  "HTTP/1.1 200 OK\r\n"                                                       \
-  "Content-Length: " #size "\r\n"                                             \
-  "\r\n"
-  const char a[] = X(18446744073709551614); /* 2^64-2 */
-  const char b[] = X(18446744073709551615); /* 2^64-1 */
-  const char c[] = X(18446744073709551616); /* 2^64   */
-#undef X
-  test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok      */
-  test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
-  test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
-}
-
-void
-test_chunk_content_length_overflow_error (void)
-{
-#define X(size)                                                               \
-    "HTTP/1.1 200 OK\r\n"                                                     \
-    "Transfer-Encoding: chunked\r\n"                                          \
-    "\r\n"                                                                    \
-    #size "\r\n"                                                              \
-    "..."
-  const char a[] = X(FFFFFFFFFFFFFFFE);  /* 2^64-2 */
-  const char b[] = X(FFFFFFFFFFFFFFFF);  /* 2^64-1 */
-  const char c[] = X(10000000000000000); /* 2^64   */
-#undef X
-  test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok      */
-  test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */
-  test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */
-}
-
-void
-test_no_overflow_long_body (int req, size_t length)
-{
-  http_parser parser;
-  http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE);
-  size_t parsed;
-  size_t i;
-  char buf1[3000];
-  size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n",
-      req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length);
-  parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
-  if (parsed != buf1len)
-    goto err;
-
-  for (i = 0; i < length; i++) {
-    char foo = 'a';
-    parsed = http_parser_execute(&parser, &settings_null, &foo, 1);
-    if (parsed != 1)
-      goto err;
-  }
-
-  parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len);
-  if (parsed != buf1len) goto err;
-  return;
-
- err:
-  fprintf(stderr,
-          "\n*** error in test_no_overflow_long_body %s of length %lu ***\n",
-          req ? "REQUEST" : "RESPONSE",
-          (unsigned long)length);
-  abort();
-}
-
-void
-test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3)
-{
-  int message_count = count_parsed_messages(3, r1, r2, r3);
-
-  char total[ strlen(r1->raw)
-            + strlen(r2->raw)
-            + strlen(r3->raw)
-            + 1
-            ];
-  total[0] = '\0';
-
-  strcat(total, r1->raw);
-  strcat(total, r2->raw);
-  strcat(total, r3->raw);
-
-  parser_init(r1->type);
-
-  size_t read;
-
-  read = parse(total, strlen(total));
-
-  if (parser->upgrade) {
-    upgrade_message_fix(total, read, 3, r1, r2, r3);
-    goto test;
-  }
-
-  if (read != strlen(total)) {
-    print_error(total, read);
-    abort();
-  }
-
-  read = parse(NULL, 0);
-
-  if (read != 0) {
-    print_error(total, read);
-    abort();
-  }
-
-test:
-
-  if (message_count != num_messages) {
-    fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages);
-    abort();
-  }
-
-  if (!message_eq(0, r1)) abort();
-  if (message_count > 1 && !message_eq(1, r2)) abort();
-  if (message_count > 2 && !message_eq(2, r3)) abort();
-
-  parser_free();
-}
-
-/* SCAN through every possible breaking to make sure the
- * parser can handle getting the content in any chunks that
- * might come from the socket
- */
-void
-test_scan (const struct message *r1, const struct message *r2, const struct message *r3)
-{
-  char total[80*1024] = "\0";
-  char buf1[80*1024] = "\0";
-  char buf2[80*1024] = "\0";
-  char buf3[80*1024] = "\0";
-
-  strcat(total, r1->raw);
-  strcat(total, r2->raw);
-  strcat(total, r3->raw);
-
-  size_t read;
-
-  int total_len = strlen(total);
-
-  int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2;
-  int ops = 0 ;
-
-  size_t buf1_len, buf2_len, buf3_len;
-  int message_count = count_parsed_messages(3, r1, r2, r3);
-
-  int i,j,type_both;
-  for (type_both = 0; type_both < 2; type_both ++ ) {
-    for (j = 2; j < total_len; j ++ ) {
-      for (i = 1; i < j; i ++ ) {
-
-        if (ops % 1000 == 0)  {
-          printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops);
-          fflush(stdout);
-        }
-        ops += 1;
-
-        parser_init(type_both ? HTTP_BOTH : r1->type);
-
-        buf1_len = i;
-        strlncpy(buf1, sizeof(buf1), total, buf1_len);
-        buf1[buf1_len] = 0;
-
-        buf2_len = j - i;
-        strlncpy(buf2, sizeof(buf1), total+i, buf2_len);
-        buf2[buf2_len] = 0;
-
-        buf3_len = total_len - j;
-        strlncpy(buf3, sizeof(buf1), total+j, buf3_len);
-        buf3[buf3_len] = 0;
-
-        read = parse(buf1, buf1_len);
-
-        if (parser->upgrade) goto test;
-
-        if (read != buf1_len) {
-          print_error(buf1, read);
-          goto error;
-        }
-
-        read += parse(buf2, buf2_len);
-
-        if (parser->upgrade) goto test;
-
-        if (read != buf1_len + buf2_len) {
-          print_error(buf2, read);
-          goto error;
-        }
-
-        read += parse(buf3, buf3_len);
-
-        if (parser->upgrade) goto test;
-
-        if (read != buf1_len + buf2_len + buf3_len) {
-          print_error(buf3, read);
-          goto error;
-        }
-
-        parse(NULL, 0);
-
-test:
-        if (parser->upgrade) {
-          upgrade_message_fix(total, read, 3, r1, r2, r3);
-        }
-
-        if (message_count != num_messages) {
-          fprintf(stderr, "\n\nParser didn't see %d messages only %d\n",
-            message_count, num_messages);
-          goto error;
-        }
-
-        if (!message_eq(0, r1)) {
-          fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n");
-          goto error;
-        }
-
-        if (message_count > 1 && !message_eq(1, r2)) {
-          fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n");
-          goto error;
-        }
-
-        if (message_count > 2 && !message_eq(2, r3)) {
-          fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n");
-          goto error;
-        }
-
-        parser_free();
-      }
-    }
-  }
-  puts("\b\b\b\b100%");
-  return;
-
- error:
-  fprintf(stderr, "i=%d  j=%d\n", i, j);
-  fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1);
-  fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2);
-  fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3);
-  abort();
-}
-
-// user required to free the result
-// string terminated by \0
-char *
-create_large_chunked_message (int body_size_in_kb, const char* headers)
-{
-  int i;
-  size_t wrote = 0;
-  size_t headers_len = strlen(headers);
-  size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6;
-  char * buf = malloc(bufsize);
-
-  memcpy(buf, headers, headers_len);
-  wrote += headers_len;
-
-  for (i = 0; i < body_size_in_kb; i++) {
-    // write 1kb chunk into the body.
-    memcpy(buf + wrote, "400\r\n", 5);
-    wrote += 5;
-    memset(buf + wrote, 'C', 1024);
-    wrote += 1024;
-    strcpy(buf + wrote, "\r\n");
-    wrote += 2;
-  }
-
-  memcpy(buf + wrote, "0\r\n\r\n", 6);
-  wrote += 6;
-  assert(wrote == bufsize);
-
-  return buf;
-}
-
-void
-test_status_complete (void)
-{
-  parser_init(HTTP_RESPONSE);
-  parser->data = 0;
-  http_parser_settings settings = settings_null;
-  settings.on_status_complete = status_complete_cb;
-
-  char *response = "don't mind me, just a simple response";
-  http_parser_execute(parser, &settings, response, strlen(response));
-  assert(parser->data == (void*)0); // the status_complete callback was never called
-  assert(parser->http_errno == HPE_INVALID_CONSTANT); // the errno for an invalid status line
-}
-
-/* Verify that we can pause parsing at any of the bytes in the
- * message and still get the result that we're expecting. */
-void
-test_message_pause (const struct message *msg)
-{
-  char *buf = (char*) msg->raw;
-  size_t buflen = strlen(msg->raw);
-  size_t nread;
-
-  parser_init(msg->type);
-
-  do {
-    nread = parse_pause(buf, buflen);
-
-    // We can only set the upgrade buffer once we've gotten our message
-    // completion callback.
-    if (messages[0].message_complete_cb_called &&
-        msg->upgrade &&
-        parser->upgrade) {
-      messages[0].upgrade = buf + nread;
-      goto test;
-    }
-
-    if (nread < buflen) {
-
-      // Not much do to if we failed a strict-mode check
-      if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) {
-        parser_free();
-        return;
-      }
-
-      assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED);
-    }
-
-    buf += nread;
-    buflen -= nread;
-    http_parser_pause(parser, 0);
-  } while (buflen > 0);
-
-  nread = parse_pause(NULL, 0);
-  assert (nread == 0);
-
-test:
-  if (num_messages != 1) {
-    printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name);
-    abort();
-  }
-
-  if(!message_eq(0, msg)) abort();
-
-  parser_free();
-}
-
-int
-main (void)
-{
-  parser = NULL;
-  int i, j, k;
-  int request_count;
-  int response_count;
-
-  printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser));
-
-  for (request_count = 0; requests[request_count].name; request_count++);
-  for (response_count = 0; responses[response_count].name; response_count++);
-
-  //// API
-  test_preserve_data();
-  test_parse_url();
-  test_method_str();
-
-  //// OVERFLOW CONDITIONS
-
-  test_header_overflow_error(HTTP_REQUEST);
-  test_no_overflow_long_body(HTTP_REQUEST, 1000);
-  test_no_overflow_long_body(HTTP_REQUEST, 100000);
-
-  test_header_overflow_error(HTTP_RESPONSE);
-  test_no_overflow_long_body(HTTP_RESPONSE, 1000);
-  test_no_overflow_long_body(HTTP_RESPONSE, 100000);
-
-  test_header_content_length_overflow_error();
-  test_chunk_content_length_overflow_error();
-
-  //// RESPONSES
-
-  for (i = 0; i < response_count; i++) {
-    test_message(&responses[i]);
-  }
-
-  for (i = 0; i < response_count; i++) {
-    test_message_pause(&responses[i]);
-  }
-
-  for (i = 0; i < response_count; i++) {
-    if (!responses[i].should_keep_alive) continue;
-    for (j = 0; j < response_count; j++) {
-      if (!responses[j].should_keep_alive) continue;
-      for (k = 0; k < response_count; k++) {
-        test_multiple3(&responses[i], &responses[j], &responses[k]);
-      }
-    }
-  }
-
-  test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]);
-  test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]);
-
-  // test very large chunked response
-  {
-    char * msg = create_large_chunked_message(31337,
-      "HTTP/1.0 200 OK\r\n"
-      "Transfer-Encoding: chunked\r\n"
-      "Content-Type: text/plain\r\n"
-      "\r\n");
-    struct message large_chunked =
-      {.name= "large chunked"
-      ,.type= HTTP_RESPONSE
-      ,.raw= msg
-      ,.should_keep_alive= FALSE
-      ,.message_complete_on_eof= FALSE
-      ,.http_major= 1
-      ,.http_minor= 0
-      ,.status_code= 200
-      ,.num_headers= 2
-      ,.headers=
-        { { "Transfer-Encoding", "chunked" }
-        , { "Content-Type", "text/plain" }
-        }
-      ,.body_size= 31337*1024
-      };
-    test_message_count_body(&large_chunked);
-    free(msg);
-  }
-
-
-
-  printf("response scan 1/2      ");
-  test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY]
-           , &responses[NO_BODY_HTTP10_KA_204]
-           , &responses[NO_REASON_PHRASE]
-           );
-
-  printf("response scan 2/2      ");
-  test_scan( &responses[BONJOUR_MADAME_FR]
-           , &responses[UNDERSTORE_HEADER_KEY]
-           , &responses[NO_CARRIAGE_RET]
-           );
-
-  puts("responses okay");
-
-
-  /// REQUESTS
-
-  test_simple("hello world", HPE_INVALID_METHOD);
-  test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
-
-
-  test_simple("ASDF / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
-  test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
-  test_simple("GETA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD);
-
-  // Well-formed but incomplete
-  test_simple("GET / HTTP/1.1\r\n"
-              "Content-Type: text/plain\r\n"
-              "Content-Length: 6\r\n"
-              "\r\n"
-              "fooba",
-              HPE_OK);
-
-  static const char *all_methods[] = {
-    "DELETE",
-    "GET",
-    "HEAD",
-    "POST",
-    "PUT",
-    //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel
-    "OPTIONS",
-    "TRACE",
-    "COPY",
-    "LOCK",
-    "MKCOL",
-    "MOVE",
-    "PROPFIND",
-    "PROPPATCH",
-    "UNLOCK",
-    "REPORT",
-    "MKACTIVITY",
-    "CHECKOUT",
-    "MERGE",
-    "M-SEARCH",
-    "NOTIFY",
-    "SUBSCRIBE",
-    "UNSUBSCRIBE",
-    "PATCH",
-    0 };
-  const char **this_method;
-  for (this_method = all_methods; *this_method; this_method++) {
-    char buf[200];
-    sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
-    test_simple(buf, HPE_OK);
-  }
-
-  static const char *bad_methods[] = {
-      "C******",
-      "M****",
-      0 };
-  for (this_method = bad_methods; *this_method; this_method++) {
-    char buf[200];
-    sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method);
-    test_simple(buf, HPE_UNKNOWN);
-  }
-
-  const char *dumbfuck2 =
-    "GET / HTTP/1.1\r\n"
-    "X-SSL-Bullshit:   -----BEGIN CERTIFICATE-----\r\n"
-    "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n"
-    "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n"
-    "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n"
-    "\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n"
-    "\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n"
-    "\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n"
-    "\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n"
-    "\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n"
-    "\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n"
-    "\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n"
-    "\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n"
-    "\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n"
-    "\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n"
-    "\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n"
-    "\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n"
-    "\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n"
-    "\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n"
-    "\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n"
-    "\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n"
-    "\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n"
-    "\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n"
-    "\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n"
-    "\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n"
-    "\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n"
-    "\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n"
-    "\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n"
-    "\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n"
-    "\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n"
-    "\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n"
-    "\tRA==\r\n"
-    "\t-----END CERTIFICATE-----\r\n"
-    "\r\n";
-  test_simple(dumbfuck2, HPE_OK);
-
-#if 0
-  // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
-  // until EOF.
-  //
-  // no content-length
-  // error if there is a body without content length
-  const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n"
-                                           "Accept: */*\r\n"
-                                           "\r\n"
-                                           "HELLO";
-  test_simple(bad_get_no_headers_no_body, 0);
-#endif
-  /* TODO sending junk and large headers gets rejected */
-
-
-  /* check to make sure our predefined requests are okay */
-  for (i = 0; requests[i].name; i++) {
-    test_message(&requests[i]);
-  }
-
-  for (i = 0; i < request_count; i++) {
-    test_message_pause(&requests[i]);
-  }
-
-  for (i = 0; i < request_count; i++) {
-    if (!requests[i].should_keep_alive) continue;
-    for (j = 0; j < request_count; j++) {
-      if (!requests[j].should_keep_alive) continue;
-      for (k = 0; k < request_count; k++) {
-        test_multiple3(&requests[i], &requests[j], &requests[k]);
-      }
-    }
-  }
-
-  printf("request scan 1/4      ");
-  test_scan( &requests[GET_NO_HEADERS_NO_BODY]
-           , &requests[GET_ONE_HEADER_NO_BODY]
-           , &requests[GET_NO_HEADERS_NO_BODY]
-           );
-
-  printf("request scan 2/4      ");
-  test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE]
-           , &requests[POST_IDENTITY_BODY_WORLD]
-           , &requests[GET_FUNKY_CONTENT_LENGTH]
-           );
-
-  printf("request scan 3/4      ");
-  test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END]
-           , &requests[CHUNKED_W_TRAILING_HEADERS]
-           , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH]
-           );
-
-  printf("request scan 4/4      ");
-  test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET]
-           , &requests[PREFIX_NEWLINE_GET ]
-           , &requests[CONNECT_REQUEST]
-           );
-
-  test_status_complete();
-
-  puts("requests okay");
-
-  return 0;
-}

+ 0 - 252
ext/huffandpuff/huffman.c

@@ -1,252 +0,0 @@
-/*
- * Huffandpuff minimal Huffman coder
- *
- * (c)2013 Adam Ierymenko <[email protected]>
- * This code is in the public domain and is distributed with NO WARRANTY.
- */
-
-#include "huffman.h"
-
-struct _huffman_node
-{
-	struct _huffman_node *lr[2];
-	struct _huffman_node *qprev,*qnext;
-	double prob;
-	unsigned long c;
-};
-
-struct _huffman_encode_table
-{
-	unsigned long code;
-	unsigned long bits;
-};
-
-static void _huffman_write_tree_and_make_encode_table(unsigned char *out,unsigned long *outbitctr,unsigned long outlen,struct _huffman_encode_table *et,unsigned long code,unsigned int bits,struct _huffman_node *t)
-{
-	struct _huffman_encode_table *eti;
-	unsigned int i;
-	unsigned long byte_index;
-
-	byte_index = (*outbitctr)++ >> 3;
-	byte_index *= (byte_index < outlen);
-	if (t->lr[0]) {
-		out[byte_index] <<= 1;
-		_huffman_write_tree_and_make_encode_table(out,outbitctr,outlen,et,code,bits + 1,t->lr[0]);
-		_huffman_write_tree_and_make_encode_table(out,outbitctr,outlen,et,code | (1 << bits),bits + 1,t->lr[1]);
-	} else {
-		out[byte_index] = (out[byte_index] << 1) | 1;
-		for(i=0;i<9;++i) {
-			byte_index = (*outbitctr)++ >> 3;
-			if (byte_index >= outlen) return;
-			out[byte_index] = (out[byte_index] << 1) | ((unsigned char)((t->c >> i) & 1));
-		}
-		eti = &(et[t->c]);
-		eti->code = code;
-		eti->bits = bits;
-	}
-}
-
-static struct _huffman_node *_huffman_read_tree(const unsigned char *in,unsigned long *inbitctr,unsigned long inlen,unsigned char **heapptr,unsigned char *heapend)
-{
-	struct _huffman_node *n;
-	unsigned int i;
-	unsigned long byte_index;
-
-	n = (struct _huffman_node *)(*heapptr);
-	*heapptr += sizeof(struct _huffman_node);
-	if (*heapptr > heapend) return (struct _huffman_node *)0;
-
-	byte_index = *inbitctr >> 3;
-	byte_index *= (byte_index < inlen);
-	if (((in[byte_index] >> (~((*inbitctr)++) & 7)) & 1)) {
-		n->lr[0] = (struct _huffman_node *)0;
-		n->lr[1] = (struct _huffman_node *)0;
-		n->c = 0;
-		for(i=0;i<9;++i) {
-			byte_index = *inbitctr >> 3;
-			if (byte_index >= inlen) return (struct _huffman_node *)0;
-			n->c |= (((unsigned int)(in[byte_index] >> (~((*inbitctr)++) & 7))) & 1) << i;
-		}
-	} else {
-		n->lr[0] = _huffman_read_tree(in,inbitctr,inlen,heapptr,heapend);
-		n->lr[1] = _huffman_read_tree(in,inbitctr,inlen,heapptr,heapend);
-		if (!((n->lr[0])&&(n->lr[1])))
-			return (struct _huffman_node *)0;
-	}
-
-	return n;
-}
-
-unsigned long huffman_compress(const unsigned char *in,unsigned long inlen,unsigned char *out,unsigned long outlen,void *huffheap)
-{
-	struct _huffman_encode_table *et,*eti;
-	struct _huffman_node *t,*n;
-	struct _huffman_node *pair[2];
-	unsigned char *heapptr = (unsigned char *)huffheap;
-	unsigned long i,code,byte_index,outbitctr;
-	unsigned int bits,b;
-	double *counts,lowest_prob,total_symbols;
-
-	counts = (double *)heapptr;
-	heapptr += (sizeof(double) * 257);
-	for(i=0;i<256;++i)
-		counts[i] = 0.0;
-	counts[256] = 1.0; /* one stop code at end */
-	for(i=0;i<inlen;++i)
-		counts[(unsigned long)in[i]] += 1.0;
-
-	t = (struct _huffman_node *)0;
-	total_symbols = (double)(inlen + 1);
-	for(i=0;i<=256;++i) {
-		if (counts[i] > 0.0) {
-			n = (struct _huffman_node *)heapptr;
-			heapptr += sizeof(struct _huffman_node);
-			if (t)
-				t->qprev = n;
-			n->qprev = (struct _huffman_node *)0;
-			n->qnext = t;
-			n->lr[0] = (struct _huffman_node *)0;
-			n->lr[1] = (struct _huffman_node *)0;
-			n->prob = counts[i] / total_symbols;
-			n->c = (unsigned int)i;
-			t = n;
-		}
-	}
-
-	while (t->qnext) {
-		for(i=0;i<2;++i) {
-			lowest_prob = 1.0;
-			pair[i] = (struct _huffman_node *)0;
-			n = t;
-			while (n) {
-				if (n->prob <= lowest_prob) {
-					lowest_prob = n->prob;
-					pair[i] = n;
-				}
-				n = n->qnext;
-			}
-			if (pair[i]->qprev)
-				pair[i]->qprev->qnext = pair[i]->qnext;
-			else t = pair[i]->qnext;
-			if (pair[i]->qnext)
-				pair[i]->qnext->qprev = pair[i]->qprev;
-		}
-		n = (struct _huffman_node *)heapptr;
-		heapptr += sizeof(struct _huffman_node);
-		n->lr[0] = pair[0];
-		n->lr[1] = pair[1];
-		n->prob = pair[0]->prob + pair[1]->prob;
-		if (t)
-			t->qprev = n;
-		n->qprev = (struct _huffman_node *)0;
-		n->qnext = t;
-		t = n;
-	}
-
-	et = (struct _huffman_encode_table *)heapptr;
-	heapptr += (sizeof(struct _huffman_encode_table) * 257);
-	outbitctr = 0;
-	_huffman_write_tree_and_make_encode_table(out,&outbitctr,outlen,et,0,0,t);
-
-	for(i=0;i<inlen;++i) {
-		eti = &(et[(unsigned long)in[i]]);
-		code = eti->code;
-		bits = eti->bits;
-		for(b=0;b<bits;++b) {
-			byte_index = outbitctr++ >> 3;
-			if (byte_index >= outlen) return 0;
-			out[byte_index] = (out[byte_index] << 1) | (unsigned char)(code & 1);
-			code >>= 1;
-		}
-	}
-	code = et[256].code;
-	bits = et[256].bits;
-	for(b=0;b<bits;++b) {
-		byte_index = outbitctr++ >> 3;
-		if (byte_index >= outlen) return 0;
-		out[byte_index] = (out[byte_index] << 1) | (unsigned char)(code & 1);
-		code >>= 1;
-	}
-
-	if (outbitctr > (outlen << 3))
-		return 0;
-	else if ((outbitctr & 7)) {
-		out[i = (outbitctr >> 3)] <<= 8 - (outbitctr & 7);
-		return (i + 1);
-	} else return (outbitctr >> 3);
-}
-
-unsigned long huffman_decompress(const unsigned char *in,unsigned long inlen,unsigned char *out,unsigned long outlen,void *huffheap)
-{
-	struct _huffman_node *t,*n;
-	unsigned char *heapptr = (unsigned char *)huffheap;
-	unsigned long inbitctr,outptr,byte_index = 0;
-
-	inbitctr = 0;
-	t = _huffman_read_tree(in,&inbitctr,inlen,&heapptr,heapptr + HUFFHEAP_SIZE);
-	if (!t) return 0;
-	outptr = 0;
-	for(;;) {
-		n = t;
-		while (n->lr[0]) {
-			byte_index = inbitctr >> 3;
-			if (byte_index >= inlen) return 0;
-			n = n->lr[((unsigned long)(in[byte_index] >> (~(inbitctr++) & 7))) & 1];
-		}
-		if (n->c == 256) return outptr;
-		if (outptr == outlen) return 0;
-		out[outptr++] = (unsigned char)n->c;
-	}
-}
-
-#ifdef HUFFANDPUFF_TEST
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#define HUFFANDPUFF_TEST_MAXLEN 1048576
-#define HUFFANDPUFF_TEST_ITER 1024
-
-static unsigned char testin[HUFFANDPUFF_TEST_MAXLEN];
-static unsigned char testout[HUFFANDPUFF_TEST_MAXLEN * 2];
-static unsigned char testver[HUFFANDPUFF_TEST_MAXLEN];
-static unsigned char huffbuf[HUFFHEAP_SIZE];
-
-int main(int argc,char **argv)
-{
-	unsigned long i,k,l,cl,dcl;
-	int v;
-	unsigned char mask;
-
-	srand(time(0));
-
-	for(k=0;k<HUFFANDPUFF_TEST_ITER;++k) {
-		l = (rand() % HUFFANDPUFF_TEST_MAXLEN) + 1;
-		mask = (rand() & 0xff);
-		for(i=0;i<l;++i)
-			testin[i] = (unsigned char)(rand() & 0xff) & mask;
-		cl = huffman_compress(testin,l,testout,sizeof(testout),huffbuf);
-		if (cl) {
-			memset(testver,0,sizeof(testver));
-			dcl = huffman_decompress(testout,cl,testver,sizeof(testver),huffbuf);
-			v = ((dcl)&&(!memcmp(testver,testin,l)));
-			printf("[%d] in: %d, out: %d, verified: %s\n",(int)k,(int)l,(int)cl,(v) ? "OK" : "FAIL");
-		} else printf("[%d] in: %d, out: FAIL\n",(int)k,(int)l);
-	}
-
-	printf("\nFuzzing decompress function...\n");
-	for(;;) {
-		l = (rand() % HUFFANDPUFF_TEST_MAXLEN) + 1;
-		mask = (rand() & 0xff);
-		for(i=0;i<l;++i)
-			testin[i] = (unsigned char)(rand() & 0xff) & mask;
-		huffman_decompress(testin,l,testver,sizeof(testver),huffbuf);
-		printf("."); fflush(stdout);
-	}
-
-	return 0;
-}
-
-#endif

+ 0 - 52
ext/huffandpuff/huffman.h

@@ -1,52 +0,0 @@
-/*
- * Huffandpuff minimal Huffman coder
- *
- * (c)2013 Adam Ierymenko <[email protected]>
- * This code is in the public domain and is distributed with NO WARRANTY.
- */
-
-#ifndef ____HUFFMAN_H
-#define ____HUFFMAN_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Required size of huffheap parameter to compress and decompress
- *
- * Note: if you change any of the data types in the _huffman_node
- * or _huffman_encode_table structs in huffman.c, this also must be
- * changed.
- */
-#define HUFFHEAP_SIZE ((sizeof(double) * 257) + (((sizeof(void *) * 4) + sizeof(double) + sizeof(unsigned long)) * (257 * 3)) + ((sizeof(unsigned long) + sizeof(unsigned long)) * 257))
-
-/**
- * Huffman encode a block of data
- *
- * @param in Input data
- * @param inlen Input data length
- * @param out Output buffer
- * @param outlen Output buffer length
- * @param huffheap Heap memory to use for compression (must be HUFFHEAP_SIZE in size)
- * @return Size of encoded result or 0 on out buffer overrun
- */
-extern unsigned long huffman_compress(const unsigned char *in,unsigned long inlen,unsigned char *out,unsigned long outlen,void *huffheap);
-
-/**
- * Huffman decode a block of data
- *
- * @param in Input data
- * @param inlen Length of input data
- * @param out Output buffer
- * @param outlen Length of output buffer
- * @param huffheap Heap memory to use for decompression (must be HUFFHEAP_SIZE in size)
- * @return Size of decoded result or 0 on out buffer overrun or corrupt input data
- */
-extern unsigned long huffman_decompress(const unsigned char *in,unsigned long inlen,unsigned char *out,unsigned long outlen,void *huffheap);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif

+ 0 - 24
ext/jsoncpp/include/json/autolink.h

@@ -1,24 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSON_AUTOLINK_H_INCLUDED
-# define JSON_AUTOLINK_H_INCLUDED
-
-# include "config.h"
-
-# ifdef JSON_IN_CPPTL
-#  include <cpptl/cpptl_autolink.h>
-# endif
-
-# if !defined(JSON_NO_AUTOLINK)  &&  !defined(JSON_DLL_BUILD)  &&  !defined(JSON_IN_CPPTL)
-#  define CPPTL_AUTOLINK_NAME "json"
-#  undef CPPTL_AUTOLINK_DLL
-#  ifdef JSON_DLL
-#   define CPPTL_AUTOLINK_DLL
-#  endif
-#  include "autolink.h"
-# endif
-
-#endif // JSON_AUTOLINK_H_INCLUDED

+ 0 - 96
ext/jsoncpp/include/json/config.h

@@ -1,96 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSON_CONFIG_H_INCLUDED
-# define JSON_CONFIG_H_INCLUDED
-
-/// If defined, indicates that json library is embedded in CppTL library.
-//# define JSON_IN_CPPTL 1
-
-/// If defined, indicates that json may leverage CppTL library
-//#  define JSON_USE_CPPTL 1
-/// If defined, indicates that cpptl vector based map should be used instead of std::map
-/// as Value container.
-//#  define JSON_USE_CPPTL_SMALLMAP 1
-/// If defined, indicates that Json specific container should be used
-/// (hash table & simple deque container with customizable allocator).
-/// THIS FEATURE IS STILL EXPERIMENTAL! There is know bugs: See #3177332
-//#  define JSON_VALUE_USE_INTERNAL_MAP 1
-/// Force usage of standard new/malloc based allocator instead of memory pool based allocator.
-/// The memory pools allocator used optimization (initializing Value and ValueInternalLink
-/// as if it was a POD) that may cause some validation tool to report errors.
-/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined.
-//#  define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1
-
-/// If defined, indicates that Json use exception to report invalid type manipulation
-/// instead of C assert macro.
-# define JSON_USE_EXCEPTION 1
-
-/// If defined, indicates that the source file is amalgated
-/// to prevent private header inclusion.
-/// Remarks: it is automatically defined in the generated amalgated header.
-// #define JSON_IS_AMALGAMATION
-
-
-# ifdef JSON_IN_CPPTL
-#  include <cpptl/config.h>
-#  ifndef JSON_USE_CPPTL
-#   define JSON_USE_CPPTL 1
-#  endif
-# endif
-
-# ifdef JSON_IN_CPPTL
-#  define JSON_API CPPTL_API
-# elif defined(JSON_DLL_BUILD)
-#  define JSON_API __declspec(dllexport)
-# elif defined(JSON_DLL)
-#  define JSON_API __declspec(dllimport)
-# else
-#  define JSON_API
-# endif
-
-// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for integer
-// Storages, and 64 bits integer support is disabled.
-// #define JSON_NO_INT64 1
-
-#if defined(_MSC_VER)  &&  _MSC_VER <= 1200 // MSVC 6
-// Microsoft Visual Studio 6 only support conversion from __int64 to double
-// (no conversion from unsigned __int64).
-#define JSON_USE_INT64_DOUBLE_CONVERSION 1
-#endif // if defined(_MSC_VER)  &&  _MSC_VER < 1200 // MSVC 6
-
-#if defined(_MSC_VER)  &&  _MSC_VER >= 1500 // MSVC 2008
-/// Indicates that the following function is deprecated.
-# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
-#endif
-
-#if !defined(JSONCPP_DEPRECATED)
-# define JSONCPP_DEPRECATED(message)
-#endif // if !defined(JSONCPP_DEPRECATED)
-
-namespace Json {
-   typedef int Int;
-   typedef unsigned int UInt;
-# if defined(JSON_NO_INT64)
-   typedef int LargestInt;
-   typedef unsigned int LargestUInt;
-#  undef JSON_HAS_INT64
-# else // if defined(JSON_NO_INT64)
-   // For Microsoft Visual use specific types as long long is not supported
-#  if defined(_MSC_VER) // Microsoft Visual Studio
-   typedef __int64 Int64;
-   typedef unsigned __int64 UInt64;
-#  else // if defined(_MSC_VER) // Other platforms, use long long
-   typedef long long int Int64;
-   typedef unsigned long long int UInt64;
-#  endif // if defined(_MSC_VER)
-   typedef Int64 LargestInt;
-   typedef UInt64 LargestUInt;
-#  define JSON_HAS_INT64
-# endif // if defined(JSON_NO_INT64)
-} // end namespace Json
-
-
-#endif // JSON_CONFIG_H_INCLUDED

+ 0 - 49
ext/jsoncpp/include/json/features.h

@@ -1,49 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
-# define CPPTL_JSON_FEATURES_H_INCLUDED
-
-#if !defined(JSON_IS_AMALGAMATION)
-# include "forwards.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-
-namespace Json {
-
-   /** \brief Configuration passed to reader and writer.
-    * This configuration object can be used to force the Reader or Writer
-    * to behave in a standard conforming way.
-    */
-   class JSON_API Features
-   {
-   public:
-      /** \brief A configuration that allows all features and assumes all strings are UTF-8.
-       * - C & C++ comments are allowed
-       * - Root object can be any JSON value
-       * - Assumes Value strings are encoded in UTF-8
-       */
-      static Features all();
-
-      /** \brief A configuration that is strictly compatible with the JSON specification.
-       * - Comments are forbidden.
-       * - Root object must be either an array or an object value.
-       * - Assumes Value strings are encoded in UTF-8
-       */
-      static Features strictMode();
-
-      /** \brief Initialize the configuration like JsonConfig::allFeatures;
-       */
-      Features();
-
-      /// \c true if comments are allowed. Default: \c true.
-      bool allowComments_;
-
-      /// \c true if root must be either an array or an object value. Default: \c false.
-      bool strictRoot_;
-   };
-
-} // namespace Json
-
-#endif // CPPTL_JSON_FEATURES_H_INCLUDED

+ 0 - 44
ext/jsoncpp/include/json/forwards.h

@@ -1,44 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSON_FORWARDS_H_INCLUDED
-# define JSON_FORWARDS_H_INCLUDED
-
-#if !defined(JSON_IS_AMALGAMATION)
-# include "config.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-
-namespace Json {
-
-   // writer.h
-   class FastWriter;
-   class StyledWriter;
-
-   // reader.h
-   class Reader;
-
-   // features.h
-   class Features;
-
-   // value.h
-   typedef unsigned int ArrayIndex;
-   class StaticString;
-   class Path;
-   class PathArgument;
-   class Value;
-   class ValueIteratorBase;
-   class ValueIterator;
-   class ValueConstIterator;
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
-   class ValueMapAllocator;
-   class ValueInternalLink;
-   class ValueInternalArray;
-   class ValueInternalMap;
-#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP
-
-} // namespace Json
-
-
-#endif // JSON_FORWARDS_H_INCLUDED

+ 0 - 15
ext/jsoncpp/include/json/json.h

@@ -1,15 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSON_JSON_H_INCLUDED
-# define JSON_JSON_H_INCLUDED
-
-# include "autolink.h"
-# include "value.h"
-# include "reader.h"
-# include "writer.h"
-# include "features.h"
-
-#endif // JSON_JSON_H_INCLUDED

+ 0 - 214
ext/jsoncpp/include/json/reader.h

@@ -1,214 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef CPPTL_JSON_READER_H_INCLUDED
-# define CPPTL_JSON_READER_H_INCLUDED
-
-#if !defined(JSON_IS_AMALGAMATION)
-# include "features.h"
-# include "value.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-# include <deque>
-# include <stack>
-# include <string>
-# include <iostream>
-
-namespace Json {
-
-   /** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a Value.
-    *
-    */
-   class JSON_API Reader
-   {
-   public:
-      typedef char Char;
-      typedef const Char *Location;
-
-      /** \brief Constructs a Reader allowing all features
-       * for parsing.
-       */
-      Reader();
-
-      /** \brief Constructs a Reader allowing the specified feature set
-       * for parsing.
-       */
-      Reader( const Features &features );
-
-      /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
-       * \param document UTF-8 encoded string containing the document to read.
-       * \param root [out] Contains the root value of the document if it was
-       *             successfully parsed.
-       * \param collectComments \c true to collect comment and allow writing them back during
-       *                        serialization, \c false to discard comments.
-       *                        This parameter is ignored if Features::allowComments_
-       *                        is \c false.
-       * \return \c true if the document was successfully parsed, \c false if an error occurred.
-       */
-      bool parse( const std::string &document, 
-                  Value &root,
-                  bool collectComments = true );
-
-      /** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
-       * \param beginDoc Pointer on the beginning of the UTF-8 encoded string of the document to read.
-       * \param endDoc Pointer on the end of the UTF-8 encoded string of the document to read. 
-       \               Must be >= beginDoc.
-       * \param root [out] Contains the root value of the document if it was
-       *             successfully parsed.
-       * \param collectComments \c true to collect comment and allow writing them back during
-       *                        serialization, \c false to discard comments.
-       *                        This parameter is ignored if Features::allowComments_
-       *                        is \c false.
-       * \return \c true if the document was successfully parsed, \c false if an error occurred.
-       */
-      bool parse( const char *beginDoc, const char *endDoc, 
-                  Value &root,
-                  bool collectComments = true );
-
-      /// \brief Parse from input stream.
-      /// \see Json::operator>>(std::istream&, Json::Value&).
-      bool parse( std::istream &is,
-                  Value &root,
-                  bool collectComments = true );
-
-      /** \brief Returns a user friendly string that list errors in the parsed document.
-       * \return Formatted error message with the list of errors with their location in 
-       *         the parsed document. An empty string is returned if no error occurred
-       *         during parsing.
-       * \deprecated Use getFormattedErrorMessages() instead (typo fix).
-       */
-      JSONCPP_DEPRECATED("Use getFormattedErrorMessages instead") 
-      std::string getFormatedErrorMessages() const;
-
-      /** \brief Returns a user friendly string that list errors in the parsed document.
-       * \return Formatted error message with the list of errors with their location in 
-       *         the parsed document. An empty string is returned if no error occurred
-       *         during parsing.
-       */
-      std::string getFormattedErrorMessages() const;
-
-   private:
-      enum TokenType
-      {
-         tokenEndOfStream = 0,
-         tokenObjectBegin,
-         tokenObjectEnd,
-         tokenArrayBegin,
-         tokenArrayEnd,
-         tokenString,
-         tokenNumber,
-         tokenTrue,
-         tokenFalse,
-         tokenNull,
-         tokenArraySeparator,
-         tokenMemberSeparator,
-         tokenComment,
-         tokenError
-      };
-
-      class Token
-      {
-      public:
-         TokenType type_;
-         Location start_;
-         Location end_;
-      };
-
-      class ErrorInfo
-      {
-      public:
-         Token token_;
-         std::string message_;
-         Location extra_;
-      };
-
-      typedef std::deque<ErrorInfo> Errors;
-
-      bool expectToken( TokenType type, Token &token, const char *message );
-      bool readToken( Token &token );
-      void skipSpaces();
-      bool match( Location pattern, 
-                  int patternLength );
-      bool readComment();
-      bool readCStyleComment();
-      bool readCppStyleComment();
-      bool readString();
-      void readNumber();
-      bool readValue();
-      bool readObject( Token &token );
-      bool readArray( Token &token );
-      bool decodeNumber( Token &token );
-      bool decodeString( Token &token );
-      bool decodeString( Token &token, std::string &decoded );
-      bool decodeDouble( Token &token );
-      bool decodeUnicodeCodePoint( Token &token, 
-                                   Location &current, 
-                                   Location end, 
-                                   unsigned int &unicode );
-      bool decodeUnicodeEscapeSequence( Token &token, 
-                                        Location &current, 
-                                        Location end, 
-                                        unsigned int &unicode );
-      bool addError( const std::string &message, 
-                     Token &token,
-                     Location extra = 0 );
-      bool recoverFromError( TokenType skipUntilToken );
-      bool addErrorAndRecover( const std::string &message, 
-                               Token &token,
-                               TokenType skipUntilToken );
-      void skipUntilSpace();
-      Value &currentValue();
-      Char getNextChar();
-      void getLocationLineAndColumn( Location location,
-                                     int &line,
-                                     int &column ) const;
-      std::string getLocationLineAndColumn( Location location ) const;
-      void addComment( Location begin, 
-                       Location end, 
-                       CommentPlacement placement );
-      void skipCommentTokens( Token &token );
-   
-      typedef std::stack<Value *> Nodes;
-      Nodes nodes_;
-      Errors errors_;
-      std::string document_;
-      Location begin_;
-      Location end_;
-      Location current_;
-      Location lastValueEnd_;
-      Value *lastValue_;
-      std::string commentsBefore_;
-      Features features_;
-      bool collectComments_;
-   };
-
-   /** \brief Read from 'sin' into 'root'.
-
-    Always keep comments from the input JSON.
-
-    This can be used to read a file into a particular sub-object.
-    For example:
-    \code
-    Json::Value root;
-    cin >> root["dir"]["file"];
-    cout << root;
-    \endcode
-    Result:
-    \verbatim
-    {
-    "dir": {
-        "file": {
-        // The input stream JSON would be nested here.
-        }
-    }
-    }
-    \endverbatim
-    \throw std::exception on parse error.
-    \see Json::operator<<()
-   */
-   std::istream& operator>>( std::istream&, Value& );
-
-} // namespace Json
-
-#endif // CPPTL_JSON_READER_H_INCLUDED

+ 0 - 1103
ext/jsoncpp/include/json/value.h

@@ -1,1103 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef CPPTL_JSON_H_INCLUDED
-# define CPPTL_JSON_H_INCLUDED
-
-#if !defined(JSON_IS_AMALGAMATION)
-# include "forwards.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-# include <string>
-# include <vector>
-
-# ifndef JSON_USE_CPPTL_SMALLMAP
-#  include <map>
-# else
-#  include <cpptl/smallmap.h>
-# endif
-# ifdef JSON_USE_CPPTL
-#  include <cpptl/forwards.h>
-# endif
-
-/** \brief JSON (JavaScript Object Notation).
- */
-namespace Json {
-
-   /** \brief Type of the value held by a Value object.
-    */
-   enum ValueType
-   {
-      nullValue = 0, ///< 'null' value
-      intValue,      ///< signed integer value
-      uintValue,     ///< unsigned integer value
-      realValue,     ///< double value
-      stringValue,   ///< UTF-8 string value
-      booleanValue,  ///< bool value
-      arrayValue,    ///< array value (ordered list)
-      objectValue    ///< object value (collection of name/value pairs).
-   };
-
-   enum CommentPlacement
-   {
-      commentBefore = 0,        ///< a comment placed on the line before a value
-      commentAfterOnSameLine,   ///< a comment just after a value on the same line
-      commentAfter,             ///< a comment on the line after a value (only make sense for root value)
-      numberOfCommentPlacement
-   };
-
-//# ifdef JSON_USE_CPPTL
-//   typedef CppTL::AnyEnumerator<const char *> EnumMemberNames;
-//   typedef CppTL::AnyEnumerator<const Value &> EnumValues;
-//# endif
-
-   /** \brief Lightweight wrapper to tag static string.
-    *
-    * Value constructor and objectValue member assignement takes advantage of the
-    * StaticString and avoid the cost of string duplication when storing the
-    * string or the member name.
-    *
-    * Example of usage:
-    * \code
-    * Json::Value aValue( StaticString("some text") );
-    * Json::Value object;
-    * static const StaticString code("code");
-    * object[code] = 1234;
-    * \endcode
-    */
-   class JSON_API StaticString
-   {
-   public:
-      explicit StaticString( const char *czstring )
-         : str_( czstring )
-      {
-      }
-
-      operator const char *() const
-      {
-         return str_;
-      }
-
-      const char *c_str() const
-      {
-         return str_;
-      }
-
-   private:
-      const char *str_;
-   };
-
-   /** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
-    *
-    * This class is a discriminated union wrapper that can represents a:
-    * - signed integer [range: Value::minInt - Value::maxInt]
-    * - unsigned integer (range: 0 - Value::maxUInt)
-    * - double
-    * - UTF-8 string
-    * - boolean
-    * - 'null'
-    * - an ordered list of Value
-    * - collection of name/value pairs (javascript object)
-    *
-    * The type of the held value is represented by a #ValueType and 
-    * can be obtained using type().
-    *
-    * values of an #objectValue or #arrayValue can be accessed using operator[]() methods. 
-    * Non const methods will automatically create the a #nullValue element 
-    * if it does not exist. 
-    * The sequence of an #arrayValue will be automatically resize and initialized 
-    * with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
-    *
-    * The get() methods can be used to obtanis default value in the case the required element
-    * does not exist.
-    *
-    * It is possible to iterate over the list of a #objectValue values using 
-    * the getMemberNames() method.
-    */
-   class JSON_API Value 
-   {
-      friend class ValueIteratorBase;
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-      friend class ValueInternalLink;
-      friend class ValueInternalMap;
-# endif
-   public:
-      typedef std::vector<std::string> Members;
-      typedef ValueIterator iterator;
-      typedef ValueConstIterator const_iterator;
-      typedef Json::UInt UInt;
-      typedef Json::Int Int;
-# if defined(JSON_HAS_INT64)
-      typedef Json::UInt64 UInt64;
-      typedef Json::Int64 Int64;
-#endif // defined(JSON_HAS_INT64)
-      typedef Json::LargestInt LargestInt;
-      typedef Json::LargestUInt LargestUInt;
-      typedef Json::ArrayIndex ArrayIndex;
-
-      static const Value null;
-      /// Minimum signed integer value that can be stored in a Json::Value.
-      static const LargestInt minLargestInt;
-      /// Maximum signed integer value that can be stored in a Json::Value.
-      static const LargestInt maxLargestInt;
-      /// Maximum unsigned integer value that can be stored in a Json::Value.
-      static const LargestUInt maxLargestUInt;
-
-      /// Minimum signed int value that can be stored in a Json::Value.
-      static const Int minInt;
-      /// Maximum signed int value that can be stored in a Json::Value.
-      static const Int maxInt;
-      /// Maximum unsigned int value that can be stored in a Json::Value.
-      static const UInt maxUInt;
-
-      /// Minimum signed 64 bits int value that can be stored in a Json::Value.
-      static const Int64 minInt64;
-      /// Maximum signed 64 bits int value that can be stored in a Json::Value.
-      static const Int64 maxInt64;
-      /// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
-      static const UInt64 maxUInt64;
-
-   private:
-#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-# ifndef JSON_VALUE_USE_INTERNAL_MAP
-      class CZString 
-      {
-      public:
-         enum DuplicationPolicy 
-         {
-            noDuplication = 0,
-            duplicate,
-            duplicateOnCopy
-         };
-         CZString( ArrayIndex index );
-         CZString( const char *cstr, DuplicationPolicy allocate );
-         CZString( const CZString &other );
-         ~CZString();
-         CZString &operator =( const CZString &other );
-         bool operator<( const CZString &other ) const;
-         bool operator==( const CZString &other ) const;
-         ArrayIndex index() const;
-         const char *c_str() const;
-         bool isStaticString() const;
-      private:
-         void swap( CZString &other );
-         const char *cstr_;
-         ArrayIndex index_;
-      };
-
-   public:
-#  ifndef JSON_USE_CPPTL_SMALLMAP
-      typedef std::map<CZString, Value> ObjectValues;
-#  else
-      typedef CppTL::SmallMap<CZString, Value> ObjectValues;
-#  endif // ifndef JSON_USE_CPPTL_SMALLMAP
-# endif // ifndef JSON_VALUE_USE_INTERNAL_MAP
-#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-
-   public:
-      /** \brief Create a default Value of the given type.
-
-        This is a very useful constructor.
-        To create an empty array, pass arrayValue.
-        To create an empty object, pass objectValue.
-        Another Value can then be set to this one by assignment.
-    This is useful since clear() and resize() will not alter types.
-
-        Examples:
-    \code
-    Json::Value null_value; // null
-    Json::Value arr_value(Json::arrayValue); // []
-    Json::Value obj_value(Json::objectValue); // {}
-    \endcode
-      */
-      Value( ValueType type = nullValue );
-      Value( Int value );
-      Value( UInt value );
-#if defined(JSON_HAS_INT64)
-      Value( Int64 value );
-      Value( UInt64 value );
-#endif // if defined(JSON_HAS_INT64)
-      Value( double value );
-      Value( const char *value );
-      Value( const char *beginValue, const char *endValue );
-      /** \brief Constructs a value from a static string.
-
-       * Like other value string constructor but do not duplicate the string for
-       * internal storage. The given string must remain alive after the call to this
-       * constructor.
-       * Example of usage:
-       * \code
-       * Json::Value aValue( StaticString("some text") );
-       * \endcode
-       */
-      Value( const StaticString &value );
-      Value( const std::string &value );
-# ifdef JSON_USE_CPPTL
-      Value( const CppTL::ConstString &value );
-# endif
-      Value( bool value );
-      Value( const Value &other );
-      ~Value();
-
-      Value &operator=( const Value &other );
-      /// Swap values.
-      /// \note Currently, comments are intentionally not swapped, for
-      /// both logic and efficiency.
-      void swap( Value &other );
-
-      ValueType type() const;
-
-      bool operator <( const Value &other ) const;
-      bool operator <=( const Value &other ) const;
-      bool operator >=( const Value &other ) const;
-      bool operator >( const Value &other ) const;
-
-      bool operator ==( const Value &other ) const;
-      bool operator !=( const Value &other ) const;
-
-      int compare( const Value &other ) const;
-
-      const char *asCString() const;
-      std::string asString() const;
-# ifdef JSON_USE_CPPTL
-      CppTL::ConstString asConstString() const;
-# endif
-      Int asInt() const;
-      UInt asUInt() const;
-      Int64 asInt64() const;
-      UInt64 asUInt64() const;
-      LargestInt asLargestInt() const;
-      LargestUInt asLargestUInt() const;
-      float asFloat() const;
-      double asDouble() const;
-      bool asBool() const;
-
-      bool isNull() const;
-      bool isBool() const;
-      bool isInt() const;
-      bool isUInt() const;
-      bool isIntegral() const;
-      bool isDouble() const;
-      bool isNumeric() const;
-      bool isString() const;
-      bool isArray() const;
-      bool isObject() const;
-
-      bool isConvertibleTo( ValueType other ) const;
-
-      /// Number of values in array or object
-      ArrayIndex size() const;
-
-      /// \brief Return true if empty array, empty object, or null;
-      /// otherwise, false.
-      bool empty() const;
-
-      /// Return isNull()
-      bool operator!() const;
-
-      /// Remove all object members and array elements.
-      /// \pre type() is arrayValue, objectValue, or nullValue
-      /// \post type() is unchanged
-      void clear();
-
-      /// Resize the array to size elements. 
-      /// New elements are initialized to null.
-      /// May only be called on nullValue or arrayValue.
-      /// \pre type() is arrayValue or nullValue
-      /// \post type() is arrayValue
-      void resize( ArrayIndex size );
-
-      /// Access an array element (zero based index ).
-      /// If the array contains less than index element, then null value are inserted
-      /// in the array so that its size is index+1.
-      /// (You may need to say 'value[0u]' to get your compiler to distinguish
-      ///  this from the operator[] which takes a string.)
-      Value &operator[]( ArrayIndex index );
-
-      /// Access an array element (zero based index ).
-      /// If the array contains less than index element, then null value are inserted
-      /// in the array so that its size is index+1.
-      /// (You may need to say 'value[0u]' to get your compiler to distinguish
-      ///  this from the operator[] which takes a string.)
-      Value &operator[]( int index );
-
-      /// Access an array element (zero based index )
-      /// (You may need to say 'value[0u]' to get your compiler to distinguish
-      ///  this from the operator[] which takes a string.)
-      const Value &operator[]( ArrayIndex index ) const;
-
-      /// Access an array element (zero based index )
-      /// (You may need to say 'value[0u]' to get your compiler to distinguish
-      ///  this from the operator[] which takes a string.)
-      const Value &operator[]( int index ) const;
-
-      /// If the array contains at least index+1 elements, returns the element value, 
-      /// otherwise returns defaultValue.
-      Value get( ArrayIndex index, 
-                 const Value &defaultValue ) const;
-      /// Return true if index < size().
-      bool isValidIndex( ArrayIndex index ) const;
-      /// \brief Append value to array at the end.
-      ///
-      /// Equivalent to jsonvalue[jsonvalue.size()] = value;
-      Value &append( const Value &value );
-
-      /// Access an object value by name, create a null member if it does not exist.
-      Value &operator[]( const char *key );
-      /// Access an object value by name, returns null if there is no member with that name.
-      const Value &operator[]( const char *key ) const;
-      /// Access an object value by name, create a null member if it does not exist.
-      Value &operator[]( const std::string &key );
-      /// Access an object value by name, returns null if there is no member with that name.
-      const Value &operator[]( const std::string &key ) const;
-      /** \brief Access an object value by name, create a null member if it does not exist.
-
-       * If the object as no entry for that name, then the member name used to store
-       * the new entry is not duplicated.
-       * Example of use:
-       * \code
-       * Json::Value object;
-       * static const StaticString code("code");
-       * object[code] = 1234;
-       * \endcode
-       */
-      Value &operator[]( const StaticString &key );
-# ifdef JSON_USE_CPPTL
-      /// Access an object value by name, create a null member if it does not exist.
-      Value &operator[]( const CppTL::ConstString &key );
-      /// Access an object value by name, returns null if there is no member with that name.
-      const Value &operator[]( const CppTL::ConstString &key ) const;
-# endif
-      /// Return the member named key if it exist, defaultValue otherwise.
-      Value get( const char *key, 
-                 const Value &defaultValue ) const;
-      /// Return the member named key if it exist, defaultValue otherwise.
-      Value get( const std::string &key,
-                 const Value &defaultValue ) const;
-# ifdef JSON_USE_CPPTL
-      /// Return the member named key if it exist, defaultValue otherwise.
-      Value get( const CppTL::ConstString &key,
-                 const Value &defaultValue ) const;
-# endif
-      /// \brief Remove and return the named member.  
-      ///
-      /// Do nothing if it did not exist.
-      /// \return the removed Value, or null.
-      /// \pre type() is objectValue or nullValue
-      /// \post type() is unchanged
-      Value removeMember( const char* key );
-      /// Same as removeMember(const char*)
-      Value removeMember( const std::string &key );
-
-      /// Return true if the object has a member named key.
-      bool isMember( const char *key ) const;
-      /// Return true if the object has a member named key.
-      bool isMember( const std::string &key ) const;
-# ifdef JSON_USE_CPPTL
-      /// Return true if the object has a member named key.
-      bool isMember( const CppTL::ConstString &key ) const;
-# endif
-
-      /// \brief Return a list of the member names.
-      ///
-      /// If null, return an empty list.
-      /// \pre type() is objectValue or nullValue
-      /// \post if type() was nullValue, it remains nullValue
-      Members getMemberNames() const;
-
-//# ifdef JSON_USE_CPPTL
-//      EnumMemberNames enumMemberNames() const;
-//      EnumValues enumValues() const;
-//# endif
-
-      /// Comments must be //... or /* ... */
-      void setComment( const char *comment,
-                       CommentPlacement placement );
-      /// Comments must be //... or /* ... */
-      void setComment( const std::string &comment,
-                       CommentPlacement placement );
-      bool hasComment( CommentPlacement placement ) const;
-      /// Include delimiters and embedded newlines.
-      std::string getComment( CommentPlacement placement ) const;
-
-      std::string toStyledString() const;
-
-      const_iterator begin() const;
-      const_iterator end() const;
-
-      iterator begin();
-      iterator end();
-
-   private:
-      Value &resolveReference( const char *key, 
-                               bool isStatic );
-
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-      inline bool isItemAvailable() const
-      {
-         return itemIsUsed_ == 0;
-      }
-
-      inline void setItemUsed( bool isUsed = true )
-      {
-         itemIsUsed_ = isUsed ? 1 : 0;
-      }
-
-      inline bool isMemberNameStatic() const
-      {
-         return memberNameIsStatic_ == 0;
-      }
-
-      inline void setMemberNameIsStatic( bool isStatic )
-      {
-         memberNameIsStatic_ = isStatic ? 1 : 0;
-      }
-# endif // # ifdef JSON_VALUE_USE_INTERNAL_MAP
-
-   private:
-      struct CommentInfo
-      {
-         CommentInfo();
-         ~CommentInfo();
-
-         void setComment( const char *text );
-
-         char *comment_;
-      };
-
-      //struct MemberNamesTransform
-      //{
-      //   typedef const char *result_type;
-      //   const char *operator()( const CZString &name ) const
-      //   {
-      //      return name.c_str();
-      //   }
-      //};
-
-      union ValueHolder
-      {
-         LargestInt int_;
-         LargestUInt uint_;
-         double real_;
-         bool bool_;
-         char *string_;
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-         ValueInternalArray *array_;
-         ValueInternalMap *map_;
-#else
-         ObjectValues *map_;
-# endif
-      } value_;
-      ValueType type_ : 8;
-      int allocated_ : 1;     // Notes: if declared as bool, bitfield is useless.
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-      unsigned int itemIsUsed_ : 1;      // used by the ValueInternalMap container.
-      int memberNameIsStatic_ : 1;       // used by the ValueInternalMap container.
-# endif
-      CommentInfo *comments_;
-   };
-
-
-   /** \brief Experimental and untested: represents an element of the "path" to access a node.
-    */
-   class PathArgument
-   {
-   public:
-      friend class Path;
-
-      PathArgument();
-      PathArgument( ArrayIndex index );
-      PathArgument( const char *key );
-      PathArgument( const std::string &key );
-
-   private:
-      enum Kind
-      {
-         kindNone = 0,
-         kindIndex,
-         kindKey
-      };
-      std::string key_;
-      ArrayIndex index_;
-      Kind kind_;
-   };
-
-   /** \brief Experimental and untested: represents a "path" to access a node.
-    *
-    * Syntax:
-    * - "." => root node
-    * - ".[n]" => elements at index 'n' of root node (an array value)
-    * - ".name" => member named 'name' of root node (an object value)
-    * - ".name1.name2.name3"
-    * - ".[0][1][2].name1[3]"
-    * - ".%" => member name is provided as parameter
-    * - ".[%]" => index is provied as parameter
-    */
-   class Path
-   {
-   public:
-      Path( const std::string &path,
-            const PathArgument &a1 = PathArgument(),
-            const PathArgument &a2 = PathArgument(),
-            const PathArgument &a3 = PathArgument(),
-            const PathArgument &a4 = PathArgument(),
-            const PathArgument &a5 = PathArgument() );
-
-      const Value &resolve( const Value &root ) const;
-      Value resolve( const Value &root, 
-                     const Value &defaultValue ) const;
-      /// Creates the "path" to access the specified node and returns a reference on the node.
-      Value &make( Value &root ) const;
-
-   private:
-      typedef std::vector<const PathArgument *> InArgs;
-      typedef std::vector<PathArgument> Args;
-
-      void makePath( const std::string &path,
-                     const InArgs &in );
-      void addPathInArg( const std::string &path, 
-                         const InArgs &in, 
-                         InArgs::const_iterator &itInArg, 
-                         PathArgument::Kind kind );
-      void invalidPath( const std::string &path, 
-                        int location );
-
-      Args args_;
-   };
-
-
-
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
-   /** \brief Allocator to customize Value internal map.
-    * Below is an example of a simple implementation (default implementation actually
-    * use memory pool for speed).
-    * \code
-      class DefaultValueMapAllocator : public ValueMapAllocator
-      {
-      public: // overridden from ValueMapAllocator
-         virtual ValueInternalMap *newMap()
-         {
-            return new ValueInternalMap();
-         }
-
-         virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other )
-         {
-            return new ValueInternalMap( other );
-         }
-
-         virtual void destructMap( ValueInternalMap *map )
-         {
-            delete map;
-         }
-
-         virtual ValueInternalLink *allocateMapBuckets( unsigned int size )
-         {
-            return new ValueInternalLink[size];
-         }
-
-         virtual void releaseMapBuckets( ValueInternalLink *links )
-         {
-            delete [] links;
-         }
-
-         virtual ValueInternalLink *allocateMapLink()
-         {
-            return new ValueInternalLink();
-         }
-
-         virtual void releaseMapLink( ValueInternalLink *link )
-         {
-            delete link;
-         }
-      };
-    * \endcode
-    */ 
-   class JSON_API ValueMapAllocator
-   {
-   public:
-      virtual ~ValueMapAllocator();
-      virtual ValueInternalMap *newMap() = 0;
-      virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) = 0;
-      virtual void destructMap( ValueInternalMap *map ) = 0;
-      virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) = 0;
-      virtual void releaseMapBuckets( ValueInternalLink *links ) = 0;
-      virtual ValueInternalLink *allocateMapLink() = 0;
-      virtual void releaseMapLink( ValueInternalLink *link ) = 0;
-   };
-
-   /** \brief ValueInternalMap hash-map bucket chain link (for internal use only).
-    * \internal previous_ & next_ allows for bidirectional traversal.
-    */
-   class JSON_API ValueInternalLink
-   {
-   public:
-      enum { itemPerLink = 6 };  // sizeof(ValueInternalLink) = 128 on 32 bits architecture.
-      enum InternalFlags { 
-         flagAvailable = 0,
-         flagUsed = 1
-      };
-
-      ValueInternalLink();
-
-      ~ValueInternalLink();
-
-      Value items_[itemPerLink];
-      char *keys_[itemPerLink];
-      ValueInternalLink *previous_;
-      ValueInternalLink *next_;
-   };
-
-
-   /** \brief A linked page based hash-table implementation used internally by Value.
-    * \internal ValueInternalMap is a tradional bucket based hash-table, with a linked
-    * list in each bucket to handle collision. There is an addional twist in that
-    * each node of the collision linked list is a page containing a fixed amount of
-    * value. This provides a better compromise between memory usage and speed.
-    * 
-    * Each bucket is made up of a chained list of ValueInternalLink. The last
-    * link of a given bucket can be found in the 'previous_' field of the following bucket.
-    * The last link of the last bucket is stored in tailLink_ as it has no following bucket.
-    * Only the last link of a bucket may contains 'available' item. The last link always
-    * contains at least one element unless is it the bucket one very first link.
-    */
-   class JSON_API ValueInternalMap
-   {
-      friend class ValueIteratorBase;
-      friend class Value;
-   public:
-      typedef unsigned int HashKey;
-      typedef unsigned int BucketIndex;
-
-# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-      struct IteratorState
-      {
-         IteratorState() 
-            : map_(0)
-            , link_(0)
-            , itemIndex_(0)
-            , bucketIndex_(0) 
-         {
-         }
-         ValueInternalMap *map_;
-         ValueInternalLink *link_;
-         BucketIndex itemIndex_;
-         BucketIndex bucketIndex_;
-      };
-# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-
-      ValueInternalMap();
-      ValueInternalMap( const ValueInternalMap &other );
-      ValueInternalMap &operator =( const ValueInternalMap &other );
-      ~ValueInternalMap();
-
-      void swap( ValueInternalMap &other );
-
-      BucketIndex size() const;
-
-      void clear();
-
-      bool reserveDelta( BucketIndex growth );
-
-      bool reserve( BucketIndex newItemCount );
-
-      const Value *find( const char *key ) const;
-
-      Value *find( const char *key );
-
-      Value &resolveReference( const char *key, 
-                               bool isStatic );
-
-      void remove( const char *key );
-
-      void doActualRemove( ValueInternalLink *link, 
-                           BucketIndex index,
-                           BucketIndex bucketIndex );
-
-      ValueInternalLink *&getLastLinkInBucket( BucketIndex bucketIndex );
-
-      Value &setNewItem( const char *key, 
-                         bool isStatic, 
-                         ValueInternalLink *link, 
-                         BucketIndex index );
-
-      Value &unsafeAdd( const char *key, 
-                        bool isStatic, 
-                        HashKey hashedKey );
-
-      HashKey hash( const char *key ) const;
-
-      int compare( const ValueInternalMap &other ) const;
-
-   private:
-      void makeBeginIterator( IteratorState &it ) const;
-      void makeEndIterator( IteratorState &it ) const;
-      static bool equals( const IteratorState &x, const IteratorState &other );
-      static void increment( IteratorState &iterator );
-      static void incrementBucket( IteratorState &iterator );
-      static void decrement( IteratorState &iterator );
-      static const char *key( const IteratorState &iterator );
-      static const char *key( const IteratorState &iterator, bool &isStatic );
-      static Value &value( const IteratorState &iterator );
-      static int distance( const IteratorState &x, const IteratorState &y );
-
-   private:
-      ValueInternalLink *buckets_;
-      ValueInternalLink *tailLink_;
-      BucketIndex bucketsSize_;
-      BucketIndex itemCount_;
-   };
-
-   /** \brief A simplified deque implementation used internally by Value.
-   * \internal
-   * It is based on a list of fixed "page", each page contains a fixed number of items.
-   * Instead of using a linked-list, a array of pointer is used for fast item look-up.
-   * Look-up for an element is as follow:
-   * - compute page index: pageIndex = itemIndex / itemsPerPage
-   * - look-up item in page: pages_[pageIndex][itemIndex % itemsPerPage]
-   *
-   * Insertion is amortized constant time (only the array containing the index of pointers
-   * need to be reallocated when items are appended).
-   */
-   class JSON_API ValueInternalArray
-   {
-      friend class Value;
-      friend class ValueIteratorBase;
-   public:
-      enum { itemsPerPage = 8 };    // should be a power of 2 for fast divide and modulo.
-      typedef Value::ArrayIndex ArrayIndex;
-      typedef unsigned int PageIndex;
-
-# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-      struct IteratorState // Must be a POD
-      {
-         IteratorState() 
-            : array_(0)
-            , currentPageIndex_(0)
-            , currentItemIndex_(0) 
-         {
-         }
-         ValueInternalArray *array_;
-         Value **currentPageIndex_;
-         unsigned int currentItemIndex_;
-      };
-# endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-
-      ValueInternalArray();
-      ValueInternalArray( const ValueInternalArray &other );
-      ValueInternalArray &operator =( const ValueInternalArray &other );
-      ~ValueInternalArray();
-      void swap( ValueInternalArray &other );
-
-      void clear();
-      void resize( ArrayIndex newSize );
-
-      Value &resolveReference( ArrayIndex index );
-
-      Value *find( ArrayIndex index ) const;
-
-      ArrayIndex size() const;
-
-      int compare( const ValueInternalArray &other ) const;
-
-   private:
-      static bool equals( const IteratorState &x, const IteratorState &other );
-      static void increment( IteratorState &iterator );
-      static void decrement( IteratorState &iterator );
-      static Value &dereference( const IteratorState &iterator );
-      static Value &unsafeDereference( const IteratorState &iterator );
-      static int distance( const IteratorState &x, const IteratorState &y );
-      static ArrayIndex indexOf( const IteratorState &iterator );
-      void makeBeginIterator( IteratorState &it ) const;
-      void makeEndIterator( IteratorState &it ) const;
-      void makeIterator( IteratorState &it, ArrayIndex index ) const;
-
-      void makeIndexValid( ArrayIndex index );
-
-      Value **pages_;
-      ArrayIndex size_;
-      PageIndex pageCount_;
-   };
-
-   /** \brief Experimental: do not use. Allocator to customize Value internal array.
-    * Below is an example of a simple implementation (actual implementation use
-    * memory pool).
-      \code
-class DefaultValueArrayAllocator : public ValueArrayAllocator
-{
-public: // overridden from ValueArrayAllocator
-   virtual ~DefaultValueArrayAllocator()
-   {
-   }
-
-   virtual ValueInternalArray *newArray()
-   {
-      return new ValueInternalArray();
-   }
-
-   virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other )
-   {
-      return new ValueInternalArray( other );
-   }
-
-   virtual void destruct( ValueInternalArray *array )
-   {
-      delete array;
-   }
-
-   virtual void reallocateArrayPageIndex( Value **&indexes, 
-                                          ValueInternalArray::PageIndex &indexCount,
-                                          ValueInternalArray::PageIndex minNewIndexCount )
-   {
-      ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
-      if ( minNewIndexCount > newIndexCount )
-         newIndexCount = minNewIndexCount;
-      void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
-      if ( !newIndexes )
-         throw std::bad_alloc();
-      indexCount = newIndexCount;
-      indexes = static_cast<Value **>( newIndexes );
-   }
-   virtual void releaseArrayPageIndex( Value **indexes, 
-                                       ValueInternalArray::PageIndex indexCount )
-   {
-      if ( indexes )
-         free( indexes );
-   }
-
-   virtual Value *allocateArrayPage()
-   {
-      return static_cast<Value *>( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) );
-   }
-
-   virtual void releaseArrayPage( Value *value )
-   {
-      if ( value )
-         free( value );
-   }
-};
-      \endcode
-    */ 
-   class JSON_API ValueArrayAllocator
-   {
-   public:
-      virtual ~ValueArrayAllocator();
-      virtual ValueInternalArray *newArray() = 0;
-      virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other ) = 0;
-      virtual void destructArray( ValueInternalArray *array ) = 0;
-      /** \brief Reallocate array page index.
-       * Reallocates an array of pointer on each page.
-       * \param indexes [input] pointer on the current index. May be \c NULL.
-       *                [output] pointer on the new index of at least 
-       *                         \a minNewIndexCount pages. 
-       * \param indexCount [input] current number of pages in the index.
-       *                   [output] number of page the reallocated index can handle.
-       *                            \b MUST be >= \a minNewIndexCount.
-       * \param minNewIndexCount Minimum number of page the new index must be able to
-       *                         handle.
-       */
-      virtual void reallocateArrayPageIndex( Value **&indexes, 
-                                             ValueInternalArray::PageIndex &indexCount,
-                                             ValueInternalArray::PageIndex minNewIndexCount ) = 0;
-      virtual void releaseArrayPageIndex( Value **indexes, 
-                                          ValueInternalArray::PageIndex indexCount ) = 0;
-      virtual Value *allocateArrayPage() = 0;
-      virtual void releaseArrayPage( Value *value ) = 0;
-   };
-#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP
-
-
-   /** \brief base class for Value iterators.
-    *
-    */
-   class ValueIteratorBase
-   {
-   public:
-      typedef unsigned int size_t;
-      typedef int difference_type;
-      typedef ValueIteratorBase SelfType;
-
-      ValueIteratorBase();
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-      explicit ValueIteratorBase( const Value::ObjectValues::iterator &current );
-#else
-      ValueIteratorBase( const ValueInternalArray::IteratorState &state );
-      ValueIteratorBase( const ValueInternalMap::IteratorState &state );
-#endif
-
-      bool operator ==( const SelfType &other ) const
-      {
-         return isEqual( other );
-      }
-
-      bool operator !=( const SelfType &other ) const
-      {
-         return !isEqual( other );
-      }
-
-      difference_type operator -( const SelfType &other ) const
-      {
-         return computeDistance( other );
-      }
-
-      /// Return either the index or the member name of the referenced value as a Value.
-      Value key() const;
-
-      /// Return the index of the referenced Value. -1 if it is not an arrayValue.
-      UInt index() const;
-
-      /// Return the member name of the referenced Value. "" if it is not an objectValue.
-      const char *memberName() const;
-
-   protected:
-      Value &deref() const;
-
-      void increment();
-
-      void decrement();
-
-      difference_type computeDistance( const SelfType &other ) const;
-
-      bool isEqual( const SelfType &other ) const;
-
-      void copy( const SelfType &other );
-
-   private:
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-      Value::ObjectValues::iterator current_;
-      // Indicates that iterator is for a null value.
-      bool isNull_;
-#else
-      union
-      {
-         ValueInternalArray::IteratorState array_;
-         ValueInternalMap::IteratorState map_;
-      } iterator_;
-      bool isArray_;
-#endif
-   };
-
-   /** \brief const iterator for object and array value.
-    *
-    */
-   class ValueConstIterator : public ValueIteratorBase
-   {
-      friend class Value;
-   public:
-      typedef unsigned int size_t;
-      typedef int difference_type;
-      typedef const Value &reference;
-      typedef const Value *pointer;
-      typedef ValueConstIterator SelfType;
-
-      ValueConstIterator();
-   private:
-      /*! \internal Use by Value to create an iterator.
-       */
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-      explicit ValueConstIterator( const Value::ObjectValues::iterator &current );
-#else
-      ValueConstIterator( const ValueInternalArray::IteratorState &state );
-      ValueConstIterator( const ValueInternalMap::IteratorState &state );
-#endif
-   public:
-      SelfType &operator =( const ValueIteratorBase &other );
-
-      SelfType operator++( int )
-      {
-         SelfType temp( *this );
-         ++*this;
-         return temp;
-      }
-
-      SelfType operator--( int )
-      {
-         SelfType temp( *this );
-         --*this;
-         return temp;
-      }
-
-      SelfType &operator--()
-      {
-         decrement();
-         return *this;
-      }
-
-      SelfType &operator++()
-      {
-         increment();
-         return *this;
-      }
-
-      reference operator *() const
-      {
-         return deref();
-      }
-   };
-
-
-   /** \brief Iterator for object and array value.
-    */
-   class ValueIterator : public ValueIteratorBase
-   {
-      friend class Value;
-   public:
-      typedef unsigned int size_t;
-      typedef int difference_type;
-      typedef Value &reference;
-      typedef Value *pointer;
-      typedef ValueIterator SelfType;
-
-      ValueIterator();
-      ValueIterator( const ValueConstIterator &other );
-      ValueIterator( const ValueIterator &other );
-   private:
-      /*! \internal Use by Value to create an iterator.
-       */
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-      explicit ValueIterator( const Value::ObjectValues::iterator &current );
-#else
-      ValueIterator( const ValueInternalArray::IteratorState &state );
-      ValueIterator( const ValueInternalMap::IteratorState &state );
-#endif
-   public:
-
-      SelfType &operator =( const SelfType &other );
-
-      SelfType operator++( int )
-      {
-         SelfType temp( *this );
-         ++*this;
-         return temp;
-      }
-
-      SelfType operator--( int )
-      {
-         SelfType temp( *this );
-         --*this;
-         return temp;
-      }
-
-      SelfType &operator--()
-      {
-         decrement();
-         return *this;
-      }
-
-      SelfType &operator++()
-      {
-         increment();
-         return *this;
-      }
-
-      reference operator *() const
-      {
-         return deref();
-      }
-   };
-
-
-} // namespace Json
-
-
-#endif // CPPTL_JSON_H_INCLUDED

+ 0 - 185
ext/jsoncpp/include/json/writer.h

@@ -1,185 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSON_WRITER_H_INCLUDED
-# define JSON_WRITER_H_INCLUDED
-
-#if !defined(JSON_IS_AMALGAMATION)
-# include "value.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-# include <vector>
-# include <string>
-# include <iostream>
-
-namespace Json {
-
-   class Value;
-
-   /** \brief Abstract class for writers.
-    */
-   class JSON_API Writer
-   {
-   public:
-      virtual ~Writer();
-
-      virtual std::string write( const Value &root ) = 0;
-   };
-
-   /** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format without formatting (not human friendly).
-    *
-    * The JSON document is written in a single line. It is not intended for 'human' consumption,
-    * but may be usefull to support feature such as RPC where bandwith is limited.
-    * \sa Reader, Value
-    */
-   class JSON_API FastWriter : public Writer
-   {
-   public:
-      FastWriter();
-      virtual ~FastWriter(){}
-
-      void enableYAMLCompatibility();
-
-   public: // overridden from Writer
-      virtual std::string write( const Value &root );
-
-   private:
-      void writeValue( const Value &value );
-
-      std::string document_;
-      bool yamlCompatiblityEnabled_;
-   };
-
-   /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way.
-    *
-    * The rules for line break and indent are as follow:
-    * - Object value:
-    *     - if empty then print {} without indent and line break
-    *     - if not empty the print '{', line break & indent, print one value per line
-    *       and then unindent and line break and print '}'.
-    * - Array value:
-    *     - if empty then print [] without indent and line break
-    *     - if the array contains no object value, empty array or some other value types,
-    *       and all the values fit on one lines, then print the array on a single line.
-    *     - otherwise, it the values do not fit on one line, or the array contains
-    *       object or non empty array, then print one value per line.
-    *
-    * If the Value have comments then they are outputed according to their #CommentPlacement.
-    *
-    * \sa Reader, Value, Value::setComment()
-    */
-   class JSON_API StyledWriter: public Writer
-   {
-   public:
-      StyledWriter();
-      virtual ~StyledWriter(){}
-
-   public: // overridden from Writer
-      /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
-       * \param root Value to serialize.
-       * \return String containing the JSON document that represents the root value.
-       */
-      virtual std::string write( const Value &root );
-
-   private:
-      void writeValue( const Value &value );
-      void writeArrayValue( const Value &value );
-      bool isMultineArray( const Value &value );
-      void pushValue( const std::string &value );
-      void writeIndent();
-      void writeWithIndent( const std::string &value );
-      void indent();
-      void unindent();
-      void writeCommentBeforeValue( const Value &root );
-      void writeCommentAfterValueOnSameLine( const Value &root );
-      bool hasCommentForValue( const Value &value );
-      static std::string normalizeEOL( const std::string &text );
-
-      typedef std::vector<std::string> ChildValues;
-
-      ChildValues childValues_;
-      std::string document_;
-      std::string indentString_;
-      int rightMargin_;
-      int indentSize_;
-      bool addChildValues_;
-   };
-
-   /** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way,
-        to a stream rather than to a string.
-    *
-    * The rules for line break and indent are as follow:
-    * - Object value:
-    *     - if empty then print {} without indent and line break
-    *     - if not empty the print '{', line break & indent, print one value per line
-    *       and then unindent and line break and print '}'.
-    * - Array value:
-    *     - if empty then print [] without indent and line break
-    *     - if the array contains no object value, empty array or some other value types,
-    *       and all the values fit on one lines, then print the array on a single line.
-    *     - otherwise, it the values do not fit on one line, or the array contains
-    *       object or non empty array, then print one value per line.
-    *
-    * If the Value have comments then they are outputed according to their #CommentPlacement.
-    *
-    * \param indentation Each level will be indented by this amount extra.
-    * \sa Reader, Value, Value::setComment()
-    */
-   class JSON_API StyledStreamWriter
-   {
-   public:
-      StyledStreamWriter( std::string indentation="\t" );
-      ~StyledStreamWriter(){}
-
-   public:
-      /** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
-       * \param out Stream to write to. (Can be ostringstream, e.g.)
-       * \param root Value to serialize.
-       * \note There is no point in deriving from Writer, since write() should not return a value.
-       */
-      void write( std::ostream &out, const Value &root );
-
-   private:
-      void writeValue( const Value &value );
-      void writeArrayValue( const Value &value );
-      bool isMultineArray( const Value &value );
-      void pushValue( const std::string &value );
-      void writeIndent();
-      void writeWithIndent( const std::string &value );
-      void indent();
-      void unindent();
-      void writeCommentBeforeValue( const Value &root );
-      void writeCommentAfterValueOnSameLine( const Value &root );
-      bool hasCommentForValue( const Value &value );
-      static std::string normalizeEOL( const std::string &text );
-
-      typedef std::vector<std::string> ChildValues;
-
-      ChildValues childValues_;
-      std::ostream* document_;
-      std::string indentString_;
-      int rightMargin_;
-      std::string indentation_;
-      bool addChildValues_;
-   };
-
-# if defined(JSON_HAS_INT64)
-   std::string JSON_API valueToString( Int value );
-   std::string JSON_API valueToString( UInt value );
-# endif // if defined(JSON_HAS_INT64)
-   std::string JSON_API valueToString( LargestInt value );
-   std::string JSON_API valueToString( LargestUInt value );
-   std::string JSON_API valueToString( double value );
-   std::string JSON_API valueToString( bool value );
-   std::string JSON_API valueToQuotedString( const char *value );
-
-   /// \brief Output using the StyledStreamWriter.
-   /// \see Json::operator>>()
-   std::ostream& operator<<( std::ostream&, const Value &root );
-
-} // namespace Json
-
-
-
-#endif // JSON_WRITER_H_INCLUDED

+ 0 - 130
ext/jsoncpp/src/json_batchallocator.h

@@ -1,130 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED
-# define JSONCPP_BATCHALLOCATOR_H_INCLUDED
-
-# include <stdlib.h>
-# include <assert.h>
-
-# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
-
-namespace Json {
-
-/* Fast memory allocator.
- *
- * This memory allocator allocates memory for a batch of object (specified by
- * the page size, the number of object in each page).
- *
- * It does not allow the destruction of a single object. All the allocated objects
- * can be destroyed at once. The memory can be either released or reused for future
- * allocation.
- * 
- * The in-place new operator must be used to construct the object using the pointer
- * returned by allocate.
- */
-template<typename AllocatedType
-        ,const unsigned int objectPerAllocation>
-class BatchAllocator
-{
-public:
-   typedef AllocatedType Type;
-
-   BatchAllocator( unsigned int objectsPerPage = 255 )
-      : freeHead_( 0 )
-      , objectsPerPage_( objectsPerPage )
-   {
-//      printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() );
-      assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space.
-      assert( objectsPerPage >= 16 );
-      batches_ = allocateBatch( 0 );   // allocated a dummy page
-      currentBatch_ = batches_;
-   }
-
-   ~BatchAllocator()
-   {
-      for ( BatchInfo *batch = batches_; batch;  )
-      {
-         BatchInfo *nextBatch = batch->next_;
-         free( batch );
-         batch = nextBatch;
-      }
-   }
-
-   /// allocate space for an array of objectPerAllocation object.
-   /// @warning it is the responsability of the caller to call objects constructors.
-   AllocatedType *allocate()
-   {
-      if ( freeHead_ ) // returns node from free list.
-      {
-         AllocatedType *object = freeHead_;
-         freeHead_ = *(AllocatedType **)object;
-         return object;
-      }
-      if ( currentBatch_->used_ == currentBatch_->end_ )
-      {
-         currentBatch_ = currentBatch_->next_;
-         while ( currentBatch_  &&  currentBatch_->used_ == currentBatch_->end_ )
-            currentBatch_ = currentBatch_->next_;
-
-         if ( !currentBatch_  ) // no free batch found, allocate a new one
-         { 
-            currentBatch_ = allocateBatch( objectsPerPage_ );
-            currentBatch_->next_ = batches_; // insert at the head of the list
-            batches_ = currentBatch_;
-         }
-      }
-      AllocatedType *allocated = currentBatch_->used_;
-      currentBatch_->used_ += objectPerAllocation;
-      return allocated;
-   }
-
-   /// Release the object.
-   /// @warning it is the responsability of the caller to actually destruct the object.
-   void release( AllocatedType *object )
-   {
-      assert( object != 0 );
-      *(AllocatedType **)object = freeHead_;
-      freeHead_ = object;
-   }
-
-private:
-   struct BatchInfo
-   {
-      BatchInfo *next_;
-      AllocatedType *used_;
-      AllocatedType *end_;
-      AllocatedType buffer_[objectPerAllocation];
-   };
-
-   // disabled copy constructor and assignement operator.
-   BatchAllocator( const BatchAllocator & );
-   void operator =( const BatchAllocator &);
-
-   static BatchInfo *allocateBatch( unsigned int objectsPerPage )
-   {
-      const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation
-                                + sizeof(AllocatedType) * objectPerAllocation * objectsPerPage;
-      BatchInfo *batch = static_cast<BatchInfo*>( malloc( mallocSize ) );
-      batch->next_ = 0;
-      batch->used_ = batch->buffer_;
-      batch->end_ = batch->buffer_ + objectsPerPage;
-      return batch;
-   }
-
-   BatchInfo *batches_;
-   BatchInfo *currentBatch_;
-   /// Head of a single linked list within the allocated space of freeed object
-   AllocatedType *freeHead_;
-   unsigned int objectsPerPage_;
-};
-
-
-} // namespace Json
-
-# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION
-
-#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED
-

+ 0 - 456
ext/jsoncpp/src/json_internalarray.inl

@@ -1,456 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-// included by json_value.cpp
-
-namespace Json {
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class ValueInternalArray
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-ValueArrayAllocator::~ValueArrayAllocator()
-{
-}
-
-// //////////////////////////////////////////////////////////////////
-// class DefaultValueArrayAllocator
-// //////////////////////////////////////////////////////////////////
-#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
-class DefaultValueArrayAllocator : public ValueArrayAllocator
-{
-public: // overridden from ValueArrayAllocator
-   virtual ~DefaultValueArrayAllocator()
-   {
-   }
-
-   virtual ValueInternalArray *newArray()
-   {
-      return new ValueInternalArray();
-   }
-
-   virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other )
-   {
-      return new ValueInternalArray( other );
-   }
-
-   virtual void destructArray( ValueInternalArray *array )
-   {
-      delete array;
-   }
-
-   virtual void reallocateArrayPageIndex( Value **&indexes, 
-                                          ValueInternalArray::PageIndex &indexCount,
-                                          ValueInternalArray::PageIndex minNewIndexCount )
-   {
-      ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
-      if ( minNewIndexCount > newIndexCount )
-         newIndexCount = minNewIndexCount;
-      void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
-      if ( !newIndexes )
-         throw std::bad_alloc();
-      indexCount = newIndexCount;
-      indexes = static_cast<Value **>( newIndexes );
-   }
-   virtual void releaseArrayPageIndex( Value **indexes, 
-                                       ValueInternalArray::PageIndex indexCount )
-   {
-      if ( indexes )
-         free( indexes );
-   }
-
-   virtual Value *allocateArrayPage()
-   {
-      return static_cast<Value *>( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) );
-   }
-
-   virtual void releaseArrayPage( Value *value )
-   {
-      if ( value )
-         free( value );
-   }
-};
-
-#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
-/// @todo make this thread-safe (lock when accessign batch allocator)
-class DefaultValueArrayAllocator : public ValueArrayAllocator
-{
-public: // overridden from ValueArrayAllocator
-   virtual ~DefaultValueArrayAllocator()
-   {
-   }
-
-   virtual ValueInternalArray *newArray()
-   {
-      ValueInternalArray *array = arraysAllocator_.allocate();
-      new (array) ValueInternalArray(); // placement new
-      return array;
-   }
-
-   virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other )
-   {
-      ValueInternalArray *array = arraysAllocator_.allocate();
-      new (array) ValueInternalArray( other ); // placement new
-      return array;
-   }
-
-   virtual void destructArray( ValueInternalArray *array )
-   {
-      if ( array )
-      {
-         array->~ValueInternalArray();
-         arraysAllocator_.release( array );
-      }
-   }
-
-   virtual void reallocateArrayPageIndex( Value **&indexes, 
-                                          ValueInternalArray::PageIndex &indexCount,
-                                          ValueInternalArray::PageIndex minNewIndexCount )
-   {
-      ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
-      if ( minNewIndexCount > newIndexCount )
-         newIndexCount = minNewIndexCount;
-      void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
-      if ( !newIndexes )
-         throw std::bad_alloc();
-      indexCount = newIndexCount;
-      indexes = static_cast<Value **>( newIndexes );
-   }
-   virtual void releaseArrayPageIndex( Value **indexes, 
-                                       ValueInternalArray::PageIndex indexCount )
-   {
-      if ( indexes )
-         free( indexes );
-   }
-
-   virtual Value *allocateArrayPage()
-   {
-      return static_cast<Value *>( pagesAllocator_.allocate() );
-   }
-
-   virtual void releaseArrayPage( Value *value )
-   {
-      if ( value )
-         pagesAllocator_.release( value );
-   }
-private:
-   BatchAllocator<ValueInternalArray,1> arraysAllocator_;
-   BatchAllocator<Value,ValueInternalArray::itemsPerPage> pagesAllocator_;
-};
-#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
-
-static ValueArrayAllocator *&arrayAllocator()
-{
-   static DefaultValueArrayAllocator defaultAllocator;
-   static ValueArrayAllocator *arrayAllocator = &defaultAllocator;
-   return arrayAllocator;
-}
-
-static struct DummyArrayAllocatorInitializer {
-   DummyArrayAllocatorInitializer() 
-   {
-      arrayAllocator();      // ensure arrayAllocator() statics are initialized before main().
-   }
-} dummyArrayAllocatorInitializer;
-
-// //////////////////////////////////////////////////////////////////
-// class ValueInternalArray
-// //////////////////////////////////////////////////////////////////
-bool 
-ValueInternalArray::equals( const IteratorState &x, 
-                            const IteratorState &other )
-{
-   return x.array_ == other.array_  
-          &&  x.currentItemIndex_ == other.currentItemIndex_  
-          &&  x.currentPageIndex_ == other.currentPageIndex_;
-}
-
-
-void 
-ValueInternalArray::increment( IteratorState &it )
-{
-   JSON_ASSERT_MESSAGE( it.array_  &&
-      (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_
-      != it.array_->size_,
-      "ValueInternalArray::increment(): moving iterator beyond end" );
-   ++(it.currentItemIndex_);
-   if ( it.currentItemIndex_ == itemsPerPage )
-   {
-      it.currentItemIndex_ = 0;
-      ++(it.currentPageIndex_);
-   }
-}
-
-
-void 
-ValueInternalArray::decrement( IteratorState &it )
-{
-   JSON_ASSERT_MESSAGE( it.array_  &&  it.currentPageIndex_ == it.array_->pages_ 
-                        &&  it.currentItemIndex_ == 0,
-      "ValueInternalArray::decrement(): moving iterator beyond end" );
-   if ( it.currentItemIndex_ == 0 )
-   {
-      it.currentItemIndex_ = itemsPerPage-1;
-      --(it.currentPageIndex_);
-   }
-   else
-   {
-      --(it.currentItemIndex_);
-   }
-}
-
-
-Value &
-ValueInternalArray::unsafeDereference( const IteratorState &it )
-{
-   return (*(it.currentPageIndex_))[it.currentItemIndex_];
-}
-
-
-Value &
-ValueInternalArray::dereference( const IteratorState &it )
-{
-   JSON_ASSERT_MESSAGE( it.array_  &&
-      (it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_
-      < it.array_->size_,
-      "ValueInternalArray::dereference(): dereferencing invalid iterator" );
-   return unsafeDereference( it );
-}
-
-void 
-ValueInternalArray::makeBeginIterator( IteratorState &it ) const
-{
-   it.array_ = const_cast<ValueInternalArray *>( this );
-   it.currentItemIndex_ = 0;
-   it.currentPageIndex_ = pages_;
-}
-
-
-void 
-ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const
-{
-   it.array_ = const_cast<ValueInternalArray *>( this );
-   it.currentItemIndex_ = index % itemsPerPage;
-   it.currentPageIndex_ = pages_ + index / itemsPerPage;
-}
-
-
-void 
-ValueInternalArray::makeEndIterator( IteratorState &it ) const
-{
-   makeIterator( it, size_ );
-}
-
-
-ValueInternalArray::ValueInternalArray()
-   : pages_( 0 )
-   , size_( 0 )
-   , pageCount_( 0 )
-{
-}
-
-
-ValueInternalArray::ValueInternalArray( const ValueInternalArray &other )
-   : pages_( 0 )
-   , pageCount_( 0 )
-   , size_( other.size_ )
-{
-   PageIndex minNewPages = other.size_ / itemsPerPage;
-   arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages );
-   JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, 
-                        "ValueInternalArray::reserve(): bad reallocation" );
-   IteratorState itOther;
-   other.makeBeginIterator( itOther );
-   Value *value;
-   for ( ArrayIndex index = 0; index < size_; ++index, increment(itOther) )
-   {
-      if ( index % itemsPerPage == 0 )
-      {
-         PageIndex pageIndex = index / itemsPerPage;
-         value = arrayAllocator()->allocateArrayPage();
-         pages_[pageIndex] = value;
-      }
-      new (value) Value( dereference( itOther ) );
-   }
-}
-
-
-ValueInternalArray &
-ValueInternalArray::operator =( const ValueInternalArray &other )
-{
-   ValueInternalArray temp( other );
-   swap( temp );
-   return *this;
-}
-
-
-ValueInternalArray::~ValueInternalArray()
-{
-   // destroy all constructed items
-   IteratorState it;
-   IteratorState itEnd;
-   makeBeginIterator( it);
-   makeEndIterator( itEnd );
-   for ( ; !equals(it,itEnd); increment(it) )
-   {
-      Value *value = &dereference(it);
-      value->~Value();
-   }
-   // release all pages
-   PageIndex lastPageIndex = size_ / itemsPerPage;
-   for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex )
-      arrayAllocator()->releaseArrayPage( pages_[pageIndex] );
-   // release pages index
-   arrayAllocator()->releaseArrayPageIndex( pages_, pageCount_ );
-}
-
-
-void 
-ValueInternalArray::swap( ValueInternalArray &other )
-{
-   Value **tempPages = pages_;
-   pages_ = other.pages_;
-   other.pages_ = tempPages;
-   ArrayIndex tempSize = size_;
-   size_ = other.size_;
-   other.size_ = tempSize;
-   PageIndex tempPageCount = pageCount_;
-   pageCount_ = other.pageCount_;
-   other.pageCount_ = tempPageCount;
-}
-
-void 
-ValueInternalArray::clear()
-{
-   ValueInternalArray dummy;
-   swap( dummy );
-}
-
-
-void 
-ValueInternalArray::resize( ArrayIndex newSize )
-{
-   if ( newSize == 0 )
-      clear();
-   else if ( newSize < size_ )
-   {
-      IteratorState it;
-      IteratorState itEnd;
-      makeIterator( it, newSize );
-      makeIterator( itEnd, size_ );
-      for ( ; !equals(it,itEnd); increment(it) )
-      {
-         Value *value = &dereference(it);
-         value->~Value();
-      }
-      PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage;
-      PageIndex lastPageIndex = size_ / itemsPerPage;
-      for ( ; pageIndex < lastPageIndex; ++pageIndex )
-         arrayAllocator()->releaseArrayPage( pages_[pageIndex] );
-      size_ = newSize;
-   }
-   else if ( newSize > size_ )
-      resolveReference( newSize );
-}
-
-
-void 
-ValueInternalArray::makeIndexValid( ArrayIndex index )
-{
-   // Need to enlarge page index ?
-   if ( index >= pageCount_ * itemsPerPage )
-   {
-      PageIndex minNewPages = (index + 1) / itemsPerPage;
-      arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages );
-      JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" );
-   }
-
-   // Need to allocate new pages ?
-   ArrayIndex nextPageIndex = 
-      (size_ % itemsPerPage) != 0 ? size_ - (size_%itemsPerPage) + itemsPerPage
-                                  : size_;
-   if ( nextPageIndex <= index )
-   {
-      PageIndex pageIndex = nextPageIndex / itemsPerPage;
-      PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1;
-      for ( ; pageToAllocate-- > 0; ++pageIndex )
-         pages_[pageIndex] = arrayAllocator()->allocateArrayPage();
-   }
-
-   // Initialize all new entries
-   IteratorState it;
-   IteratorState itEnd;
-   makeIterator( it, size_ );
-   size_ = index + 1;
-   makeIterator( itEnd, size_ );
-   for ( ; !equals(it,itEnd); increment(it) )
-   {
-      Value *value = &dereference(it);
-      new (value) Value(); // Construct a default value using placement new
-   }
-}
-
-Value &
-ValueInternalArray::resolveReference( ArrayIndex index )
-{
-   if ( index >= size_ )
-      makeIndexValid( index );
-   return pages_[index/itemsPerPage][index%itemsPerPage];
-}
-
-Value *
-ValueInternalArray::find( ArrayIndex index ) const
-{
-   if ( index >= size_ )
-      return 0;
-   return &(pages_[index/itemsPerPage][index%itemsPerPage]);
-}
-
-ValueInternalArray::ArrayIndex 
-ValueInternalArray::size() const
-{
-   return size_;
-}
-
-int 
-ValueInternalArray::distance( const IteratorState &x, const IteratorState &y )
-{
-   return indexOf(y) - indexOf(x);
-}
-
-
-ValueInternalArray::ArrayIndex 
-ValueInternalArray::indexOf( const IteratorState &iterator )
-{
-   if ( !iterator.array_ )
-      return ArrayIndex(-1);
-   return ArrayIndex(
-      (iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage 
-      + iterator.currentItemIndex_ );
-}
-
-
-int 
-ValueInternalArray::compare( const ValueInternalArray &other ) const
-{
-   int sizeDiff( size_ - other.size_ );
-   if ( sizeDiff != 0 )
-      return sizeDiff;
-   
-   for ( ArrayIndex index =0; index < size_; ++index )
-   {
-      int diff = pages_[index/itemsPerPage][index%itemsPerPage].compare( 
-         other.pages_[index/itemsPerPage][index%itemsPerPage] );
-      if ( diff != 0 )
-         return diff;
-   }
-   return 0;
-}
-
-} // namespace Json

+ 0 - 615
ext/jsoncpp/src/json_internalmap.inl

@@ -1,615 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-// included by json_value.cpp
-
-namespace Json {
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class ValueInternalMap
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-/** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) );
-   * This optimization is used by the fast allocator.
-   */
-ValueInternalLink::ValueInternalLink()
-   : previous_( 0 )
-   , next_( 0 )
-{
-}
-
-ValueInternalLink::~ValueInternalLink()
-{ 
-   for ( int index =0; index < itemPerLink; ++index )
-   {
-      if ( !items_[index].isItemAvailable() )
-      {
-         if ( !items_[index].isMemberNameStatic() )
-            free( keys_[index] );
-      }
-      else
-         break;
-   }
-}
-
-
-
-ValueMapAllocator::~ValueMapAllocator()
-{
-}
-
-#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
-class DefaultValueMapAllocator : public ValueMapAllocator
-{
-public: // overridden from ValueMapAllocator
-   virtual ValueInternalMap *newMap()
-   {
-      return new ValueInternalMap();
-   }
-
-   virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other )
-   {
-      return new ValueInternalMap( other );
-   }
-
-   virtual void destructMap( ValueInternalMap *map )
-   {
-      delete map;
-   }
-
-   virtual ValueInternalLink *allocateMapBuckets( unsigned int size )
-   {
-      return new ValueInternalLink[size];
-   }
-
-   virtual void releaseMapBuckets( ValueInternalLink *links )
-   {
-      delete [] links;
-   }
-
-   virtual ValueInternalLink *allocateMapLink()
-   {
-      return new ValueInternalLink();
-   }
-
-   virtual void releaseMapLink( ValueInternalLink *link )
-   {
-      delete link;
-   }
-};
-#else
-/// @todo make this thread-safe (lock when accessign batch allocator)
-class DefaultValueMapAllocator : public ValueMapAllocator
-{
-public: // overridden from ValueMapAllocator
-   virtual ValueInternalMap *newMap()
-   {
-      ValueInternalMap *map = mapsAllocator_.allocate();
-      new (map) ValueInternalMap(); // placement new
-      return map;
-   }
-
-   virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other )
-   {
-      ValueInternalMap *map = mapsAllocator_.allocate();
-      new (map) ValueInternalMap( other ); // placement new
-      return map;
-   }
-
-   virtual void destructMap( ValueInternalMap *map )
-   {
-      if ( map )
-      {
-         map->~ValueInternalMap();
-         mapsAllocator_.release( map );
-      }
-   }
-
-   virtual ValueInternalLink *allocateMapBuckets( unsigned int size )
-   {
-      return new ValueInternalLink[size];
-   }
-
-   virtual void releaseMapBuckets( ValueInternalLink *links )
-   {
-      delete [] links;
-   }
-
-   virtual ValueInternalLink *allocateMapLink()
-   {
-      ValueInternalLink *link = linksAllocator_.allocate();
-      memset( link, 0, sizeof(ValueInternalLink) );
-      return link;
-   }
-
-   virtual void releaseMapLink( ValueInternalLink *link )
-   {
-      link->~ValueInternalLink();
-      linksAllocator_.release( link );
-   }
-private:
-   BatchAllocator<ValueInternalMap,1> mapsAllocator_;
-   BatchAllocator<ValueInternalLink,1> linksAllocator_;
-};
-#endif
-
-static ValueMapAllocator *&mapAllocator()
-{
-   static DefaultValueMapAllocator defaultAllocator;
-   static ValueMapAllocator *mapAllocator = &defaultAllocator;
-   return mapAllocator;
-}
-
-static struct DummyMapAllocatorInitializer {
-   DummyMapAllocatorInitializer() 
-   {
-      mapAllocator();      // ensure mapAllocator() statics are initialized before main().
-   }
-} dummyMapAllocatorInitializer;
-
-
-
-// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32.
-
-/*
-use linked list hash map. 
-buckets array is a container.
-linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124)
-value have extra state: valid, available, deleted
-*/
-
-
-ValueInternalMap::ValueInternalMap()
-   : buckets_( 0 )
-   , tailLink_( 0 )
-   , bucketsSize_( 0 )
-   , itemCount_( 0 )
-{
-}
-
-
-ValueInternalMap::ValueInternalMap( const ValueInternalMap &other )
-   : buckets_( 0 )
-   , tailLink_( 0 )
-   , bucketsSize_( 0 )
-   , itemCount_( 0 )
-{
-   reserve( other.itemCount_ );
-   IteratorState it;
-   IteratorState itEnd;
-   other.makeBeginIterator( it );
-   other.makeEndIterator( itEnd );
-   for ( ; !equals(it,itEnd); increment(it) )
-   {
-      bool isStatic;
-      const char *memberName = key( it, isStatic );
-      const Value &aValue = value( it );
-      resolveReference(memberName, isStatic) = aValue;
-   }
-}
-
-
-ValueInternalMap &
-ValueInternalMap::operator =( const ValueInternalMap &other )
-{
-   ValueInternalMap dummy( other );
-   swap( dummy );
-   return *this;
-}
-
-
-ValueInternalMap::~ValueInternalMap()
-{
-   if ( buckets_ )
-   {
-      for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex )
-      {
-         ValueInternalLink *link = buckets_[bucketIndex].next_;
-         while ( link )
-         {
-            ValueInternalLink *linkToRelease = link;
-            link = link->next_;
-            mapAllocator()->releaseMapLink( linkToRelease );
-         }
-      }
-      mapAllocator()->releaseMapBuckets( buckets_ );
-   }
-}
-
-
-void 
-ValueInternalMap::swap( ValueInternalMap &other )
-{
-   ValueInternalLink *tempBuckets = buckets_;
-   buckets_ = other.buckets_;
-   other.buckets_ = tempBuckets;
-   ValueInternalLink *tempTailLink = tailLink_;
-   tailLink_ = other.tailLink_;
-   other.tailLink_ = tempTailLink;
-   BucketIndex tempBucketsSize = bucketsSize_;
-   bucketsSize_ = other.bucketsSize_;
-   other.bucketsSize_ = tempBucketsSize;
-   BucketIndex tempItemCount = itemCount_;
-   itemCount_ = other.itemCount_;
-   other.itemCount_ = tempItemCount;
-}
-
-
-void 
-ValueInternalMap::clear()
-{
-   ValueInternalMap dummy;
-   swap( dummy );
-}
-
-
-ValueInternalMap::BucketIndex 
-ValueInternalMap::size() const
-{
-   return itemCount_;
-}
-
-bool 
-ValueInternalMap::reserveDelta( BucketIndex growth )
-{
-   return reserve( itemCount_ + growth );
-}
-
-bool 
-ValueInternalMap::reserve( BucketIndex newItemCount )
-{
-   if ( !buckets_  &&  newItemCount > 0 )
-   {
-      buckets_ = mapAllocator()->allocateMapBuckets( 1 );
-      bucketsSize_ = 1;
-      tailLink_ = &buckets_[0];
-   }
-//   BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink;
-   return true;
-}
-
-
-const Value *
-ValueInternalMap::find( const char *key ) const
-{
-   if ( !bucketsSize_ )
-      return 0;
-   HashKey hashedKey = hash( key );
-   BucketIndex bucketIndex = hashedKey % bucketsSize_;
-   for ( const ValueInternalLink *current = &buckets_[bucketIndex]; 
-         current != 0; 
-         current = current->next_ )
-   {
-      for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index )
-      {
-         if ( current->items_[index].isItemAvailable() )
-            return 0;
-         if ( strcmp( key, current->keys_[index] ) == 0 )
-            return &current->items_[index];
-      }
-   }
-   return 0;
-}
-
-
-Value *
-ValueInternalMap::find( const char *key )
-{
-   const ValueInternalMap *constThis = this;
-   return const_cast<Value *>( constThis->find( key ) );
-}
-
-
-Value &
-ValueInternalMap::resolveReference( const char *key,
-                                    bool isStatic )
-{
-   HashKey hashedKey = hash( key );
-   if ( bucketsSize_ )
-   {
-      BucketIndex bucketIndex = hashedKey % bucketsSize_;
-      ValueInternalLink **previous = 0;
-      BucketIndex index;
-      for ( ValueInternalLink *current = &buckets_[bucketIndex]; 
-            current != 0; 
-            previous = &current->next_, current = current->next_ )
-      {
-         for ( index=0; index < ValueInternalLink::itemPerLink; ++index )
-         {
-            if ( current->items_[index].isItemAvailable() )
-               return setNewItem( key, isStatic, current, index );
-            if ( strcmp( key, current->keys_[index] ) == 0 )
-               return current->items_[index];
-         }
-      }
-   }
-
-   reserveDelta( 1 );
-   return unsafeAdd( key, isStatic, hashedKey );
-}
-
-
-void 
-ValueInternalMap::remove( const char *key )
-{
-   HashKey hashedKey = hash( key );
-   if ( !bucketsSize_ )
-      return;
-   BucketIndex bucketIndex = hashedKey % bucketsSize_;
-   for ( ValueInternalLink *link = &buckets_[bucketIndex]; 
-         link != 0; 
-         link = link->next_ )
-   {
-      BucketIndex index;
-      for ( index =0; index < ValueInternalLink::itemPerLink; ++index )
-      {
-         if ( link->items_[index].isItemAvailable() )
-            return;
-         if ( strcmp( key, link->keys_[index] ) == 0 )
-         {
-            doActualRemove( link, index, bucketIndex );
-            return;
-         }
-      }
-   }
-}
-
-void 
-ValueInternalMap::doActualRemove( ValueInternalLink *link, 
-                                  BucketIndex index,
-                                  BucketIndex bucketIndex )
-{
-   // find last item of the bucket and swap it with the 'removed' one.
-   // set removed items flags to 'available'.
-   // if last page only contains 'available' items, then desallocate it (it's empty)
-   ValueInternalLink *&lastLink = getLastLinkInBucket( index );
-   BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1
-   for ( ;   
-         lastItemIndex < ValueInternalLink::itemPerLink; 
-         ++lastItemIndex ) // may be optimized with dicotomic search
-   {
-      if ( lastLink->items_[lastItemIndex].isItemAvailable() )
-         break;
-   }
-   
-   BucketIndex lastUsedIndex = lastItemIndex - 1;
-   Value *valueToDelete = &link->items_[index];
-   Value *valueToPreserve = &lastLink->items_[lastUsedIndex];
-   if ( valueToDelete != valueToPreserve )
-      valueToDelete->swap( *valueToPreserve );
-   if ( lastUsedIndex == 0 )  // page is now empty
-   {  // remove it from bucket linked list and delete it.
-      ValueInternalLink *linkPreviousToLast = lastLink->previous_;
-      if ( linkPreviousToLast != 0 )   // can not deleted bucket link.
-      {
-         mapAllocator()->releaseMapLink( lastLink );
-         linkPreviousToLast->next_ = 0;
-         lastLink = linkPreviousToLast;
-      }
-   }
-   else
-   {
-      Value dummy;
-      valueToPreserve->swap( dummy ); // restore deleted to default Value.
-      valueToPreserve->setItemUsed( false );
-   }
-   --itemCount_;
-}
-
-
-ValueInternalLink *&
-ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex )
-{
-   if ( bucketIndex == bucketsSize_ - 1 )
-      return tailLink_;
-   ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_;
-   if ( !previous )
-      previous = &buckets_[bucketIndex];
-   return previous;
-}
-
-
-Value &
-ValueInternalMap::setNewItem( const char *key, 
-                              bool isStatic,
-                              ValueInternalLink *link, 
-                              BucketIndex index )
-{
-   char *duplicatedKey = makeMemberName( key );
-   ++itemCount_;
-   link->keys_[index] = duplicatedKey;
-   link->items_[index].setItemUsed();
-   link->items_[index].setMemberNameIsStatic( isStatic );
-   return link->items_[index]; // items already default constructed.
-}
-
-
-Value &
-ValueInternalMap::unsafeAdd( const char *key, 
-                             bool isStatic, 
-                             HashKey hashedKey )
-{
-   JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." );
-   BucketIndex bucketIndex = hashedKey % bucketsSize_;
-   ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex );
-   ValueInternalLink *link = previousLink;
-   BucketIndex index;
-   for ( index =0; index < ValueInternalLink::itemPerLink; ++index )
-   {
-      if ( link->items_[index].isItemAvailable() )
-         break;
-   }
-   if ( index == ValueInternalLink::itemPerLink ) // need to add a new page
-   {
-      ValueInternalLink *newLink = mapAllocator()->allocateMapLink();
-      index = 0;
-      link->next_ = newLink;
-      previousLink = newLink;
-      link = newLink;
-   }
-   return setNewItem( key, isStatic, link, index );
-}
-
-
-ValueInternalMap::HashKey 
-ValueInternalMap::hash( const char *key ) const
-{
-   HashKey hash = 0;
-   while ( *key )
-      hash += *key++ * 37;
-   return hash;
-}
-
-
-int 
-ValueInternalMap::compare( const ValueInternalMap &other ) const
-{
-   int sizeDiff( itemCount_ - other.itemCount_ );
-   if ( sizeDiff != 0 )
-      return sizeDiff;
-   // Strict order guaranty is required. Compare all keys FIRST, then compare values.
-   IteratorState it;
-   IteratorState itEnd;
-   makeBeginIterator( it );
-   makeEndIterator( itEnd );
-   for ( ; !equals(it,itEnd); increment(it) )
-   {
-      if ( !other.find( key( it ) ) )
-         return 1;
-   }
-
-   // All keys are equals, let's compare values
-   makeBeginIterator( it );
-   for ( ; !equals(it,itEnd); increment(it) )
-   {
-      const Value *otherValue = other.find( key( it ) );
-      int valueDiff = value(it).compare( *otherValue );
-      if ( valueDiff != 0 )
-         return valueDiff;
-   }
-   return 0;
-}
-
-
-void 
-ValueInternalMap::makeBeginIterator( IteratorState &it ) const
-{
-   it.map_ = const_cast<ValueInternalMap *>( this );
-   it.bucketIndex_ = 0;
-   it.itemIndex_ = 0;
-   it.link_ = buckets_;
-}
-
-
-void 
-ValueInternalMap::makeEndIterator( IteratorState &it ) const
-{
-   it.map_ = const_cast<ValueInternalMap *>( this );
-   it.bucketIndex_ = bucketsSize_;
-   it.itemIndex_ = 0;
-   it.link_ = 0;
-}
-
-
-bool 
-ValueInternalMap::equals( const IteratorState &x, const IteratorState &other )
-{
-   return x.map_ == other.map_  
-          &&  x.bucketIndex_ == other.bucketIndex_  
-          &&  x.link_ == other.link_
-          &&  x.itemIndex_ == other.itemIndex_;
-}
-
-
-void 
-ValueInternalMap::incrementBucket( IteratorState &iterator )
-{
-   ++iterator.bucketIndex_;
-   JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_,
-      "ValueInternalMap::increment(): attempting to iterate beyond end." );
-   if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ )
-      iterator.link_ = 0;
-   else
-      iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]);
-   iterator.itemIndex_ = 0;
-}
-
-
-void 
-ValueInternalMap::increment( IteratorState &iterator )
-{
-   JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." );
-   ++iterator.itemIndex_;
-   if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink )
-   {
-      JSON_ASSERT_MESSAGE( iterator.link_ != 0,
-         "ValueInternalMap::increment(): attempting to iterate beyond end." );
-      iterator.link_ = iterator.link_->next_;
-      if ( iterator.link_ == 0 )
-         incrementBucket( iterator );
-   }
-   else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() )
-   {
-      incrementBucket( iterator );
-   }
-}
-
-
-void 
-ValueInternalMap::decrement( IteratorState &iterator )
-{
-   if ( iterator.itemIndex_ == 0 )
-   {
-      JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." );
-      if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] )
-      {
-         JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." );
-         --(iterator.bucketIndex_);
-      }
-      iterator.link_ = iterator.link_->previous_;
-      iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1;
-   }
-}
-
-
-const char *
-ValueInternalMap::key( const IteratorState &iterator )
-{
-   JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." );
-   return iterator.link_->keys_[iterator.itemIndex_];
-}
-
-const char *
-ValueInternalMap::key( const IteratorState &iterator, bool &isStatic )
-{
-   JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." );
-   isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic();
-   return iterator.link_->keys_[iterator.itemIndex_];
-}
-
-
-Value &
-ValueInternalMap::value( const IteratorState &iterator )
-{
-   JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." );
-   return iterator.link_->items_[iterator.itemIndex_];
-}
-
-
-int 
-ValueInternalMap::distance( const IteratorState &x, const IteratorState &y )
-{
-   int offset = 0;
-   IteratorState it = x;
-   while ( !equals( it, y ) )
-      increment( it );
-   return offset;
-}
-
-} // namespace Json

+ 0 - 880
ext/jsoncpp/src/json_reader.cpp

@@ -1,880 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#if !defined(JSON_IS_AMALGAMATION)
-# include <json/reader.h>
-# include <json/value.h>
-# include "json_tool.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-#include <utility>
-#include <cstdio>
-#include <cassert>
-#include <cstring>
-#include <iostream>
-#include <stdexcept>
-
-#if _MSC_VER >= 1400 // VC++ 8.0
-#pragma warning( disable : 4996 )   // disable warning about strdup being deprecated.
-#endif
-
-namespace Json {
-
-// Implementation of class Features
-// ////////////////////////////////
-
-Features::Features()
-   : allowComments_( true )
-   , strictRoot_( false )
-{
-}
-
-
-Features 
-Features::all()
-{
-   return Features();
-}
-
-
-Features 
-Features::strictMode()
-{
-   Features features;
-   features.allowComments_ = false;
-   features.strictRoot_ = true;
-   return features;
-}
-
-// Implementation of class Reader
-// ////////////////////////////////
-
-
-static inline bool 
-in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 )
-{
-   return c == c1  ||  c == c2  ||  c == c3  ||  c == c4;
-}
-
-static inline bool 
-in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 )
-{
-   return c == c1  ||  c == c2  ||  c == c3  ||  c == c4  ||  c == c5;
-}
-
-
-static bool 
-containsNewLine( Reader::Location begin, 
-                 Reader::Location end )
-{
-   for ( ;begin < end; ++begin )
-      if ( *begin == '\n'  ||  *begin == '\r' )
-         return true;
-   return false;
-}
-
-
-// Class Reader
-// //////////////////////////////////////////////////////////////////
-
-Reader::Reader()
-   : features_( Features::all() )
-{
-}
-
-
-Reader::Reader( const Features &features )
-   : features_( features )
-{
-}
-
-
-bool
-Reader::parse( const std::string &document, 
-               Value &root,
-               bool collectComments )
-{
-   document_ = document;
-   const char *begin = document_.c_str();
-   const char *end = begin + document_.length();
-   return parse( begin, end, root, collectComments );
-}
-
-
-bool
-Reader::parse( std::istream& sin,
-               Value &root,
-               bool collectComments )
-{
-   //std::istream_iterator<char> begin(sin);
-   //std::istream_iterator<char> end;
-   // Those would allow streamed input from a file, if parse() were a
-   // template function.
-
-   // Since std::string is reference-counted, this at least does not
-   // create an extra copy.
-   std::string doc;
-   std::getline(sin, doc, (char)EOF);
-   return parse( doc, root, collectComments );
-}
-
-bool 
-Reader::parse( const char *beginDoc, const char *endDoc, 
-               Value &root,
-               bool collectComments )
-{
-   if ( !features_.allowComments_ )
-   {
-      collectComments = false;
-   }
-
-   begin_ = beginDoc;
-   end_ = endDoc;
-   collectComments_ = collectComments;
-   current_ = begin_;
-   lastValueEnd_ = 0;
-   lastValue_ = 0;
-   commentsBefore_ = "";
-   errors_.clear();
-   while ( !nodes_.empty() )
-      nodes_.pop();
-   nodes_.push( &root );
-   
-   bool successful = readValue();
-   Token token;
-   skipCommentTokens( token );
-   if ( collectComments_  &&  !commentsBefore_.empty() )
-      root.setComment( commentsBefore_, commentAfter );
-   if ( features_.strictRoot_ )
-   {
-      if ( !root.isArray()  &&  !root.isObject() )
-      {
-         // Set error location to start of doc, ideally should be first token found in doc
-         token.type_ = tokenError;
-         token.start_ = beginDoc;
-         token.end_ = endDoc;
-         addError( "A valid JSON document must be either an array or an object value.",
-                   token );
-         return false;
-      }
-   }
-   return successful;
-}
-
-
-bool
-Reader::readValue()
-{
-   Token token;
-   skipCommentTokens( token );
-   bool successful = true;
-
-   if ( collectComments_  &&  !commentsBefore_.empty() )
-   {
-      currentValue().setComment( commentsBefore_, commentBefore );
-      commentsBefore_ = "";
-   }
-
-
-   switch ( token.type_ )
-   {
-   case tokenObjectBegin:
-      successful = readObject( token );
-      break;
-   case tokenArrayBegin:
-      successful = readArray( token );
-      break;
-   case tokenNumber:
-      successful = decodeNumber( token );
-      break;
-   case tokenString:
-      successful = decodeString( token );
-      break;
-   case tokenTrue:
-      currentValue() = true;
-      break;
-   case tokenFalse:
-      currentValue() = false;
-      break;
-   case tokenNull:
-      currentValue() = Value();
-      break;
-   default:
-      return addError( "Syntax error: value, object or array expected.", token );
-   }
-
-   if ( collectComments_ )
-   {
-      lastValueEnd_ = current_;
-      lastValue_ = &currentValue();
-   }
-
-   return successful;
-}
-
-
-void 
-Reader::skipCommentTokens( Token &token )
-{
-   if ( features_.allowComments_ )
-   {
-      do
-      {
-         readToken( token );
-      }
-      while ( token.type_ == tokenComment );
-   }
-   else
-   {
-      readToken( token );
-   }
-}
-
-
-bool 
-Reader::expectToken( TokenType type, Token &token, const char *message )
-{
-   readToken( token );
-   if ( token.type_ != type )
-      return addError( message, token );
-   return true;
-}
-
-
-bool 
-Reader::readToken( Token &token )
-{
-   skipSpaces();
-   token.start_ = current_;
-   Char c = getNextChar();
-   bool ok = true;
-   switch ( c )
-   {
-   case '{':
-      token.type_ = tokenObjectBegin;
-      break;
-   case '}':
-      token.type_ = tokenObjectEnd;
-      break;
-   case '[':
-      token.type_ = tokenArrayBegin;
-      break;
-   case ']':
-      token.type_ = tokenArrayEnd;
-      break;
-   case '"':
-      token.type_ = tokenString;
-      ok = readString();
-      break;
-   case '/':
-      token.type_ = tokenComment;
-      ok = readComment();
-      break;
-   case '0':
-   case '1':
-   case '2':
-   case '3':
-   case '4':
-   case '5':
-   case '6':
-   case '7':
-   case '8':
-   case '9':
-   case '-':
-      token.type_ = tokenNumber;
-      readNumber();
-      break;
-   case 't':
-      token.type_ = tokenTrue;
-      ok = match( "rue", 3 );
-      break;
-   case 'f':
-      token.type_ = tokenFalse;
-      ok = match( "alse", 4 );
-      break;
-   case 'n':
-      token.type_ = tokenNull;
-      ok = match( "ull", 3 );
-      break;
-   case ',':
-      token.type_ = tokenArraySeparator;
-      break;
-   case ':':
-      token.type_ = tokenMemberSeparator;
-      break;
-   case 0:
-      token.type_ = tokenEndOfStream;
-      break;
-   default:
-      ok = false;
-      break;
-   }
-   if ( !ok )
-      token.type_ = tokenError;
-   token.end_ = current_;
-   return true;
-}
-
-
-void 
-Reader::skipSpaces()
-{
-   while ( current_ != end_ )
-   {
-      Char c = *current_;
-      if ( c == ' '  ||  c == '\t'  ||  c == '\r'  ||  c == '\n' )
-         ++current_;
-      else
-         break;
-   }
-}
-
-
-bool 
-Reader::match( Location pattern, 
-               int patternLength )
-{
-   if ( end_ - current_ < patternLength )
-      return false;
-   int index = patternLength;
-   while ( index-- )
-      if ( current_[index] != pattern[index] )
-         return false;
-   current_ += patternLength;
-   return true;
-}
-
-
-bool
-Reader::readComment()
-{
-   Location commentBegin = current_ - 1;
-   Char c = getNextChar();
-   bool successful = false;
-   if ( c == '*' )
-      successful = readCStyleComment();
-   else if ( c == '/' )
-      successful = readCppStyleComment();
-   if ( !successful )
-      return false;
-
-   if ( collectComments_ )
-   {
-      CommentPlacement placement = commentBefore;
-      if ( lastValueEnd_  &&  !containsNewLine( lastValueEnd_, commentBegin ) )
-      {
-         if ( c != '*'  ||  !containsNewLine( commentBegin, current_ ) )
-            placement = commentAfterOnSameLine;
-      }
-
-      addComment( commentBegin, current_, placement );
-   }
-   return true;
-}
-
-
-void 
-Reader::addComment( Location begin, 
-                    Location end, 
-                    CommentPlacement placement )
-{
-   assert( collectComments_ );
-   if ( placement == commentAfterOnSameLine )
-   {
-      assert( lastValue_ != 0 );
-      lastValue_->setComment( std::string( begin, end ), placement );
-   }
-   else
-   {
-      if ( !commentsBefore_.empty() )
-         commentsBefore_ += "\n";
-      commentsBefore_ += std::string( begin, end );
-   }
-}
-
-
-bool 
-Reader::readCStyleComment()
-{
-   while ( current_ != end_ )
-   {
-      Char c = getNextChar();
-      if ( c == '*'  &&  *current_ == '/' )
-         break;
-   }
-   return getNextChar() == '/';
-}
-
-
-bool 
-Reader::readCppStyleComment()
-{
-   while ( current_ != end_ )
-   {
-      Char c = getNextChar();
-      if (  c == '\r'  ||  c == '\n' )
-         break;
-   }
-   return true;
-}
-
-
-void 
-Reader::readNumber()
-{
-   while ( current_ != end_ )
-   {
-      if ( !(*current_ >= '0'  &&  *current_ <= '9')  &&
-           !in( *current_, '.', 'e', 'E', '+', '-' ) )
-         break;
-      ++current_;
-   }
-}
-
-bool
-Reader::readString()
-{
-   Char c = 0;
-   while ( current_ != end_ )
-   {
-      c = getNextChar();
-      if ( c == '\\' )
-         getNextChar();
-      else if ( c == '"' )
-         break;
-   }
-   return c == '"';
-}
-
-
-bool 
-Reader::readObject( Token &/*tokenStart*/ )
-{
-   Token tokenName;
-   std::string name;
-   currentValue() = Value( objectValue );
-   while ( readToken( tokenName ) )
-   {
-      bool initialTokenOk = true;
-      while ( tokenName.type_ == tokenComment  &&  initialTokenOk )
-         initialTokenOk = readToken( tokenName );
-      if  ( !initialTokenOk )
-         break;
-      if ( tokenName.type_ == tokenObjectEnd  &&  name.empty() )  // empty object
-         return true;
-      if ( tokenName.type_ != tokenString )
-         break;
-      
-      name = "";
-      if ( !decodeString( tokenName, name ) )
-         return recoverFromError( tokenObjectEnd );
-
-      Token colon;
-      if ( !readToken( colon ) ||  colon.type_ != tokenMemberSeparator )
-      {
-         return addErrorAndRecover( "Missing ':' after object member name", 
-                                    colon, 
-                                    tokenObjectEnd );
-      }
-      Value &value = currentValue()[ name ];
-      nodes_.push( &value );
-      bool ok = readValue();
-      nodes_.pop();
-      if ( !ok ) // error already set
-         return recoverFromError( tokenObjectEnd );
-
-      Token comma;
-      if ( !readToken( comma )
-            ||  ( comma.type_ != tokenObjectEnd  &&  
-                  comma.type_ != tokenArraySeparator &&
-                  comma.type_ != tokenComment ) )
-      {
-         return addErrorAndRecover( "Missing ',' or '}' in object declaration", 
-                                    comma, 
-                                    tokenObjectEnd );
-      }
-      bool finalizeTokenOk = true;
-      while ( comma.type_ == tokenComment &&
-              finalizeTokenOk )
-         finalizeTokenOk = readToken( comma );
-      if ( comma.type_ == tokenObjectEnd )
-         return true;
-   }
-   return addErrorAndRecover( "Missing '}' or object member name", 
-                              tokenName, 
-                              tokenObjectEnd );
-}
-
-
-bool 
-Reader::readArray( Token &/*tokenStart*/ )
-{
-   currentValue() = Value( arrayValue );
-   skipSpaces();
-   if ( *current_ == ']' ) // empty array
-   {
-      Token endArray;
-      readToken( endArray );
-      return true;
-   }
-   int index = 0;
-   for (;;)
-   {
-      Value &value = currentValue()[ index++ ];
-      nodes_.push( &value );
-      bool ok = readValue();
-      nodes_.pop();
-      if ( !ok ) // error already set
-         return recoverFromError( tokenArrayEnd );
-
-      Token token;
-      // Accept Comment after last item in the array.
-      ok = readToken( token );
-      while ( token.type_ == tokenComment  &&  ok )
-      {
-         ok = readToken( token );
-      }
-      bool badTokenType = ( token.type_ != tokenArraySeparator  &&
-                            token.type_ != tokenArrayEnd );
-      if ( !ok  ||  badTokenType )
-      {
-         return addErrorAndRecover( "Missing ',' or ']' in array declaration", 
-                                    token, 
-                                    tokenArrayEnd );
-      }
-      if ( token.type_ == tokenArrayEnd )
-         break;
-   }
-   return true;
-}
-
-
-bool 
-Reader::decodeNumber( Token &token )
-{
-   bool isDouble = false;
-   for ( Location inspect = token.start_; inspect != token.end_; ++inspect )
-   {
-      isDouble = isDouble  
-                 ||  in( *inspect, '.', 'e', 'E', '+' )  
-                 ||  ( *inspect == '-'  &&  inspect != token.start_ );
-   }
-   if ( isDouble )
-      return decodeDouble( token );
-   // Attempts to parse the number as an integer. If the number is
-   // larger than the maximum supported value of an integer then
-   // we decode the number as a double.
-   Location current = token.start_;
-   bool isNegative = *current == '-';
-   if ( isNegative )
-      ++current;
-   Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) 
-                                                   : Value::maxLargestUInt;
-   Value::LargestUInt threshold = maxIntegerValue / 10;
-   Value::UInt lastDigitThreshold = Value::UInt( maxIntegerValue % 10 );
-   assert( lastDigitThreshold >=0  &&  lastDigitThreshold <= 9 );
-   Value::LargestUInt value = 0;
-   while ( current < token.end_ )
-   {
-      Char c = *current++;
-      if ( c < '0'  ||  c > '9' )
-         return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
-      Value::UInt digit(c - '0');
-      if ( value >= threshold )
-      {
-         // If the current digit is not the last one, or if it is
-         // greater than the last digit of the maximum integer value,
-         // the parse the number as a double.
-         if ( current != token.end_  ||  digit > lastDigitThreshold )
-         {
-            return decodeDouble( token );
-         }
-      }
-      value = value * 10 + digit;
-   }
-   if ( isNegative )
-      currentValue() = -Value::LargestInt( value );
-   else if ( value <= Value::LargestUInt(Value::maxInt) )
-      currentValue() = Value::LargestInt( value );
-   else
-      currentValue() = value;
-   return true;
-}
-
-
-bool 
-Reader::decodeDouble( Token &token )
-{
-   double value = 0;
-   const int bufferSize = 32;
-   int count;
-   int length = int(token.end_ - token.start_);
-   if ( length <= bufferSize )
-   {
-      Char buffer[bufferSize+1];
-      memcpy( buffer, token.start_, length );
-      buffer[length] = 0;
-      count = sscanf( buffer, "%lf", &value );
-   }
-   else
-   {
-      std::string buffer( token.start_, token.end_ );
-      count = sscanf( buffer.c_str(), "%lf", &value );
-   }
-
-   if ( count != 1 )
-      return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
-   currentValue() = value;
-   return true;
-}
-
-
-bool 
-Reader::decodeString( Token &token )
-{
-   std::string decoded;
-   if ( !decodeString( token, decoded ) )
-      return false;
-   currentValue() = decoded;
-   return true;
-}
-
-
-bool 
-Reader::decodeString( Token &token, std::string &decoded )
-{
-   decoded.reserve( token.end_ - token.start_ - 2 );
-   Location current = token.start_ + 1; // skip '"'
-   Location end = token.end_ - 1;      // do not include '"'
-   while ( current != end )
-   {
-      Char c = *current++;
-      if ( c == '"' )
-         break;
-      else if ( c == '\\' )
-      {
-         if ( current == end )
-            return addError( "Empty escape sequence in string", token, current );
-         Char escape = *current++;
-         switch ( escape )
-         {
-         case '"': decoded += '"'; break;
-         case '/': decoded += '/'; break;
-         case '\\': decoded += '\\'; break;
-         case 'b': decoded += '\b'; break;
-         case 'f': decoded += '\f'; break;
-         case 'n': decoded += '\n'; break;
-         case 'r': decoded += '\r'; break;
-         case 't': decoded += '\t'; break;
-         case 'u':
-            {
-               unsigned int unicode;
-               if ( !decodeUnicodeCodePoint( token, current, end, unicode ) )
-                  return false;
-               decoded += codePointToUTF8(unicode);
-            }
-            break;
-         default:
-            return addError( "Bad escape sequence in string", token, current );
-         }
-      }
-      else
-      {
-         decoded += c;
-      }
-   }
-   return true;
-}
-
-bool
-Reader::decodeUnicodeCodePoint( Token &token, 
-                                     Location &current, 
-                                     Location end, 
-                                     unsigned int &unicode )
-{
-
-   if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) )
-      return false;
-   if (unicode >= 0xD800 && unicode <= 0xDBFF)
-   {
-      // surrogate pairs
-      if (end - current < 6)
-         return addError( "additional six characters expected to parse unicode surrogate pair.", token, current );
-      unsigned int surrogatePair;
-      if (*(current++) == '\\' && *(current++)== 'u')
-      {
-         if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
-         {
-            unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
-         } 
-         else
-            return false;
-      } 
-      else
-         return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current );
-   }
-   return true;
-}
-
-bool 
-Reader::decodeUnicodeEscapeSequence( Token &token, 
-                                     Location &current, 
-                                     Location end, 
-                                     unsigned int &unicode )
-{
-   if ( end - current < 4 )
-      return addError( "Bad unicode escape sequence in string: four digits expected.", token, current );
-   unicode = 0;
-   for ( int index =0; index < 4; ++index )
-   {
-      Char c = *current++;
-      unicode *= 16;
-      if ( c >= '0'  &&  c <= '9' )
-         unicode += c - '0';
-      else if ( c >= 'a'  &&  c <= 'f' )
-         unicode += c - 'a' + 10;
-      else if ( c >= 'A'  &&  c <= 'F' )
-         unicode += c - 'A' + 10;
-      else
-         return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current );
-   }
-   return true;
-}
-
-
-bool 
-Reader::addError( const std::string &message, 
-                  Token &token,
-                  Location extra )
-{
-   ErrorInfo info;
-   info.token_ = token;
-   info.message_ = message;
-   info.extra_ = extra;
-   errors_.push_back( info );
-   return false;
-}
-
-
-bool 
-Reader::recoverFromError( TokenType skipUntilToken )
-{
-   int errorCount = int(errors_.size());
-   Token skip;
-   for (;;)
-   {
-      if ( !readToken(skip) )
-         errors_.resize( errorCount ); // discard errors caused by recovery
-      if ( skip.type_ == skipUntilToken  ||  skip.type_ == tokenEndOfStream )
-         break;
-   }
-   errors_.resize( errorCount );
-   return false;
-}
-
-
-bool 
-Reader::addErrorAndRecover( const std::string &message, 
-                            Token &token,
-                            TokenType skipUntilToken )
-{
-   addError( message, token );
-   return recoverFromError( skipUntilToken );
-}
-
-
-Value &
-Reader::currentValue()
-{
-   return *(nodes_.top());
-}
-
-
-Reader::Char 
-Reader::getNextChar()
-{
-   if ( current_ == end_ )
-      return 0;
-   return *current_++;
-}
-
-
-void 
-Reader::getLocationLineAndColumn( Location location,
-                                  int &line,
-                                  int &column ) const
-{
-   Location current = begin_;
-   Location lastLineStart = current;
-   line = 0;
-   while ( current < location  &&  current != end_ )
-   {
-      Char c = *current++;
-      if ( c == '\r' )
-      {
-         if ( *current == '\n' )
-            ++current;
-         lastLineStart = current;
-         ++line;
-      }
-      else if ( c == '\n' )
-      {
-         lastLineStart = current;
-         ++line;
-      }
-   }
-   // column & line start at 1
-   column = int(location - lastLineStart) + 1;
-   ++line;
-}
-
-
-std::string
-Reader::getLocationLineAndColumn( Location location ) const
-{
-   int line, column;
-   getLocationLineAndColumn( location, line, column );
-   char buffer[18+16+16+1];
-   sprintf( buffer, "Line %d, Column %d", line, column );
-   return buffer;
-}
-
-
-// Deprecated. Preserved for backward compatibility
-std::string 
-Reader::getFormatedErrorMessages() const
-{
-    return getFormattedErrorMessages();
-}
-
-
-std::string 
-Reader::getFormattedErrorMessages() const
-{
-   std::string formattedMessage;
-   for ( Errors::const_iterator itError = errors_.begin();
-         itError != errors_.end();
-         ++itError )
-   {
-      const ErrorInfo &error = *itError;
-      formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n";
-      formattedMessage += "  " + error.message_ + "\n";
-      if ( error.extra_ )
-         formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n";
-   }
-   return formattedMessage;
-}
-
-
-std::istream& operator>>( std::istream &sin, Value &root )
-{
-    Json::Reader reader;
-    bool ok = reader.parse(sin, root, true);
-    //JSON_ASSERT( ok );
-    if (!ok) throw std::runtime_error(reader.getFormattedErrorMessages());
-    return sin;
-}
-
-
-} // namespace Json

+ 0 - 93
ext/jsoncpp/src/json_tool.h

@@ -1,93 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
-# define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
-
-/* This header provides common string manipulation support, such as UTF-8,
- * portable conversion from/to string...
- *
- * It is an internal header that must not be exposed.
- */
-
-namespace Json {
-
-/// Converts a unicode code-point to UTF-8.
-static inline std::string 
-codePointToUTF8(unsigned int cp)
-{
-   std::string result;
-   
-   // based on description from http://en.wikipedia.org/wiki/UTF-8
-
-   if (cp <= 0x7f) 
-   {
-      result.resize(1);
-      result[0] = static_cast<char>(cp);
-   } 
-   else if (cp <= 0x7FF) 
-   {
-      result.resize(2);
-      result[1] = static_cast<char>(0x80 | (0x3f & cp));
-      result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
-   } 
-   else if (cp <= 0xFFFF) 
-   {
-      result.resize(3);
-      result[2] = static_cast<char>(0x80 | (0x3f & cp));
-      result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
-      result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
-   }
-   else if (cp <= 0x10FFFF) 
-   {
-      result.resize(4);
-      result[3] = static_cast<char>(0x80 | (0x3f & cp));
-      result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
-      result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
-      result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
-   }
-
-   return result;
-}
-
-
-/// Returns true if ch is a control character (in range [0,32[).
-static inline bool 
-isControlCharacter(char ch)
-{
-   return ch > 0 && ch <= 0x1F;
-}
-
-
-enum { 
-   /// Constant that specify the size of the buffer that must be passed to uintToString.
-   uintToStringBufferSize = 3*sizeof(LargestUInt)+1 
-};
-
-// Defines a char buffer for use with uintToString().
-typedef char UIntToStringBuffer[uintToStringBufferSize];
-
-
-/** Converts an unsigned integer to string.
- * @param value Unsigned interger to convert to string
- * @param current Input/Output string buffer. 
- *        Must have at least uintToStringBufferSize chars free.
- */
-static inline void 
-uintToString( LargestUInt value, 
-              char *&current )
-{
-   *--current = 0;
-   do
-   {
-      *--current = char(value % 10) + '0';
-      value /= 10;
-   }
-   while ( value != 0 );
-}
-
-} // namespace Json {
-
-#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED

+ 0 - 1829
ext/jsoncpp/src/json_value.cpp

@@ -1,1829 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#if !defined(JSON_IS_AMALGAMATION)
-# include <json/value.h>
-# include <json/writer.h>
-# ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
-#  include "json_batchallocator.h"
-# endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
-#endif // if !defined(JSON_IS_AMALGAMATION)
-#include <iostream>
-#include <utility>
-#include <stdexcept>
-#include <cstring>
-#include <cassert>
-#ifdef JSON_USE_CPPTL
-# include <cpptl/conststring.h>
-#endif
-#include <cstddef>    // size_t
-
-#define JSON_ASSERT_UNREACHABLE assert( false )
-#define JSON_ASSERT( condition ) assert( condition );  // @todo <= change this into an exception throw
-#define JSON_FAIL_MESSAGE( message ) throw std::runtime_error( message );
-#define JSON_ASSERT_MESSAGE( condition, message ) if (!( condition )) JSON_FAIL_MESSAGE( message )
-
-namespace Json {
-
-const Value Value::null;
-const Int Value::minInt = Int( ~(UInt(-1)/2) );
-const Int Value::maxInt = Int( UInt(-1)/2 );
-const UInt Value::maxUInt = UInt(-1);
-const Int64 Value::minInt64 = Int64( ~(UInt64(-1)/2) );
-const Int64 Value::maxInt64 = Int64( UInt64(-1)/2 );
-const UInt64 Value::maxUInt64 = UInt64(-1);
-const LargestInt Value::minLargestInt = LargestInt( ~(LargestUInt(-1)/2) );
-const LargestInt Value::maxLargestInt = LargestInt( LargestUInt(-1)/2 );
-const LargestUInt Value::maxLargestUInt = LargestUInt(-1);
-
-
-/// Unknown size marker
-static const unsigned int unknown = (unsigned)-1;
-
-
-/** Duplicates the specified string value.
- * @param value Pointer to the string to duplicate. Must be zero-terminated if
- *              length is "unknown".
- * @param length Length of the value. if equals to unknown, then it will be
- *               computed using strlen(value).
- * @return Pointer on the duplicate instance of string.
- */
-static inline char *
-duplicateStringValue( const char *value, 
-                      unsigned int length = unknown )
-{
-   if ( length == unknown )
-      length = (unsigned int)strlen(value);
-   char *newString = static_cast<char *>( malloc( length + 1 ) );
-   JSON_ASSERT_MESSAGE( newString != 0, "Failed to allocate string value buffer" );
-   memcpy( newString, value, length );
-   newString[length] = 0;
-   return newString;
-}
-
-
-/** Free the string duplicated by duplicateStringValue().
- */
-static inline void 
-releaseStringValue( char *value )
-{
-   if ( value )
-      free( value );
-}
-
-} // namespace Json
-
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// ValueInternals...
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-#if !defined(JSON_IS_AMALGAMATION)
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-#  include "json_internalarray.inl"
-#  include "json_internalmap.inl"
-# endif // JSON_VALUE_USE_INTERNAL_MAP
-
-# include "json_valueiterator.inl"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-
-namespace Json {
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class Value::CommentInfo
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-
-Value::CommentInfo::CommentInfo()
-   : comment_( 0 )
-{
-}
-
-Value::CommentInfo::~CommentInfo()
-{
-   if ( comment_ )
-      releaseStringValue( comment_ );
-}
-
-
-void 
-Value::CommentInfo::setComment( const char *text )
-{
-   if ( comment_ )
-      releaseStringValue( comment_ );
-   JSON_ASSERT( text != 0 );
-   JSON_ASSERT_MESSAGE( text[0]=='\0' || text[0]=='/', "Comments must start with /");
-   // It seems that /**/ style comments are acceptable as well.
-   comment_ = duplicateStringValue( text );
-}
-
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class Value::CZString
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-# ifndef JSON_VALUE_USE_INTERNAL_MAP
-
-// Notes: index_ indicates if the string was allocated when
-// a string is stored.
-
-Value::CZString::CZString( ArrayIndex index )
-   : cstr_( 0 )
-   , index_( index )
-{
-}
-
-Value::CZString::CZString( const char *cstr, DuplicationPolicy allocate )
-   : cstr_( allocate == duplicate ? duplicateStringValue(cstr) 
-                                  : cstr )
-   , index_( allocate )
-{
-}
-
-Value::CZString::CZString( const CZString &other )
-: cstr_( other.index_ != noDuplication &&  other.cstr_ != 0
-                ?  duplicateStringValue( other.cstr_ )
-                : other.cstr_ )
-   , index_( other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate)
-                         : other.index_ )
-{
-}
-
-Value::CZString::~CZString()
-{
-   if ( cstr_  &&  index_ == duplicate )
-      releaseStringValue( const_cast<char *>( cstr_ ) );
-}
-
-void 
-Value::CZString::swap( CZString &other )
-{
-   std::swap( cstr_, other.cstr_ );
-   std::swap( index_, other.index_ );
-}
-
-Value::CZString &
-Value::CZString::operator =( const CZString &other )
-{
-   CZString temp( other );
-   swap( temp );
-   return *this;
-}
-
-bool 
-Value::CZString::operator<( const CZString &other ) const 
-{
-   if ( cstr_ )
-      return strcmp( cstr_, other.cstr_ ) < 0;
-   return index_ < other.index_;
-}
-
-bool 
-Value::CZString::operator==( const CZString &other ) const 
-{
-   if ( cstr_ )
-      return strcmp( cstr_, other.cstr_ ) == 0;
-   return index_ == other.index_;
-}
-
-
-ArrayIndex 
-Value::CZString::index() const
-{
-   return index_;
-}
-
-
-const char *
-Value::CZString::c_str() const
-{
-   return cstr_;
-}
-
-bool 
-Value::CZString::isStaticString() const
-{
-   return index_ == noDuplication;
-}
-
-#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP
-
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class Value::Value
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-/*! \internal Default constructor initialization must be equivalent to:
- * memset( this, 0, sizeof(Value) )
- * This optimization is used in ValueInternalMap fast allocator.
- */
-Value::Value( ValueType type )
-   : type_( type )
-   , allocated_( 0 )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   switch ( type )
-   {
-   case nullValue:
-      break;
-   case intValue:
-   case uintValue:
-      value_.int_ = 0;
-      break;
-   case realValue:
-      value_.real_ = 0.0;
-      break;
-   case stringValue:
-      value_.string_ = 0;
-      break;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   case arrayValue:
-   case objectValue:
-      value_.map_ = new ObjectValues();
-      break;
-#else
-   case arrayValue:
-      value_.array_ = arrayAllocator()->newArray();
-      break;
-   case objectValue:
-      value_.map_ = mapAllocator()->newMap();
-      break;
-#endif
-   case booleanValue:
-      value_.bool_ = false;
-      break;
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-}
-
-
-#if defined(JSON_HAS_INT64)
-Value::Value( UInt value )
-   : type_( uintValue )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   value_.uint_ = value;
-}
-
-Value::Value( Int value )
-   : type_( intValue )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   value_.int_ = value;
-}
-
-#endif // if defined(JSON_HAS_INT64)
-
-
-Value::Value( Int64 value )
-   : type_( intValue )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   value_.int_ = value;
-}
-
-
-Value::Value( UInt64 value )
-   : type_( uintValue )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   value_.uint_ = value;
-}
-
-Value::Value( double value )
-   : type_( realValue )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   value_.real_ = value;
-}
-
-Value::Value( const char *value )
-   : type_( stringValue )
-   , allocated_( true )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   value_.string_ = duplicateStringValue( value );
-}
-
-
-Value::Value( const char *beginValue, 
-              const char *endValue )
-   : type_( stringValue )
-   , allocated_( true )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   value_.string_ = duplicateStringValue( beginValue, 
-                                          (unsigned int)(endValue - beginValue) );
-}
-
-
-Value::Value( const std::string &value )
-   : type_( stringValue )
-   , allocated_( true )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   value_.string_ = duplicateStringValue( value.c_str(), 
-                                          (unsigned int)value.length() );
-
-}
-
-Value::Value( const StaticString &value )
-   : type_( stringValue )
-   , allocated_( false )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   value_.string_ = const_cast<char *>( value.c_str() );
-}
-
-
-# ifdef JSON_USE_CPPTL
-Value::Value( const CppTL::ConstString &value )
-   : type_( stringValue )
-   , allocated_( true )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   value_.string_ = duplicateStringValue( value, value.length() );
-}
-# endif
-
-Value::Value( bool value )
-   : type_( booleanValue )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   value_.bool_ = value;
-}
-
-
-Value::Value( const Value &other )
-   : type_( other.type_ )
-   , comments_( 0 )
-# ifdef JSON_VALUE_USE_INTERNAL_MAP
-   , itemIsUsed_( 0 )
-#endif
-{
-   switch ( type_ )
-   {
-   case nullValue:
-   case intValue:
-   case uintValue:
-   case realValue:
-   case booleanValue:
-      value_ = other.value_;
-      break;
-   case stringValue:
-      if ( other.value_.string_ )
-      {
-         value_.string_ = duplicateStringValue( other.value_.string_ );
-         allocated_ = true;
-      }
-      else
-         value_.string_ = 0;
-      break;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   case arrayValue:
-   case objectValue:
-      value_.map_ = new ObjectValues( *other.value_.map_ );
-      break;
-#else
-   case arrayValue:
-      value_.array_ = arrayAllocator()->newArrayCopy( *other.value_.array_ );
-      break;
-   case objectValue:
-      value_.map_ = mapAllocator()->newMapCopy( *other.value_.map_ );
-      break;
-#endif
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   if ( other.comments_ )
-   {
-      comments_ = new CommentInfo[numberOfCommentPlacement];
-      for ( int comment =0; comment < numberOfCommentPlacement; ++comment )
-      {
-         const CommentInfo &otherComment = other.comments_[comment];
-         if ( otherComment.comment_ )
-            comments_[comment].setComment( otherComment.comment_ );
-      }
-   }
-}
-
-
-Value::~Value()
-{
-   switch ( type_ )
-   {
-   case nullValue:
-   case intValue:
-   case uintValue:
-   case realValue:
-   case booleanValue:
-      break;
-   case stringValue:
-      if ( allocated_ )
-         releaseStringValue( value_.string_ );
-      break;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   case arrayValue:
-   case objectValue:
-      delete value_.map_;
-      break;
-#else
-   case arrayValue:
-      arrayAllocator()->destructArray( value_.array_ );
-      break;
-   case objectValue:
-      mapAllocator()->destructMap( value_.map_ );
-      break;
-#endif
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-
-   if ( comments_ )
-      delete[] comments_;
-}
-
-Value &
-Value::operator=( const Value &other )
-{
-   Value temp( other );
-   swap( temp );
-   return *this;
-}
-
-void 
-Value::swap( Value &other )
-{
-   ValueType temp = type_;
-   type_ = other.type_;
-   other.type_ = temp;
-   std::swap( value_, other.value_ );
-   int temp2 = allocated_;
-   allocated_ = other.allocated_;
-   other.allocated_ = temp2;
-}
-
-ValueType 
-Value::type() const
-{
-   return type_;
-}
-
-
-int 
-Value::compare( const Value &other ) const
-{
-   if ( *this < other )
-      return -1;
-   if ( *this > other )
-      return 1;
-   return 0;
-}
-
-
-bool 
-Value::operator <( const Value &other ) const
-{
-   int typeDelta = type_ - other.type_;
-   if ( typeDelta )
-      return typeDelta < 0 ? true : false;
-   switch ( type_ )
-   {
-   case nullValue:
-      return false;
-   case intValue:
-      return value_.int_ < other.value_.int_;
-   case uintValue:
-      return value_.uint_ < other.value_.uint_;
-   case realValue:
-      return value_.real_ < other.value_.real_;
-   case booleanValue:
-      return value_.bool_ < other.value_.bool_;
-   case stringValue:
-      return ( value_.string_ == 0  &&  other.value_.string_ )
-             || ( other.value_.string_  
-                  &&  value_.string_  
-                  && strcmp( value_.string_, other.value_.string_ ) < 0 );
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   case arrayValue:
-   case objectValue:
-      {
-         int delta = int( value_.map_->size() - other.value_.map_->size() );
-         if ( delta )
-            return delta < 0;
-         return (*value_.map_) < (*other.value_.map_);
-      }
-#else
-   case arrayValue:
-      return value_.array_->compare( *(other.value_.array_) ) < 0;
-   case objectValue:
-      return value_.map_->compare( *(other.value_.map_) ) < 0;
-#endif
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   return false;  // unreachable
-}
-
-bool 
-Value::operator <=( const Value &other ) const
-{
-   return !(other < *this);
-}
-
-bool 
-Value::operator >=( const Value &other ) const
-{
-   return !(*this < other);
-}
-
-bool 
-Value::operator >( const Value &other ) const
-{
-   return other < *this;
-}
-
-bool 
-Value::operator ==( const Value &other ) const
-{
-   //if ( type_ != other.type_ )
-   // GCC 2.95.3 says:
-   // attempt to take address of bit-field structure member `Json::Value::type_'
-   // Beats me, but a temp solves the problem.
-   int temp = other.type_;
-   if ( type_ != temp )
-      return false;
-   switch ( type_ )
-   {
-   case nullValue:
-      return true;
-   case intValue:
-      return value_.int_ == other.value_.int_;
-   case uintValue:
-      return value_.uint_ == other.value_.uint_;
-   case realValue:
-      return value_.real_ == other.value_.real_;
-   case booleanValue:
-      return value_.bool_ == other.value_.bool_;
-   case stringValue:
-      return ( value_.string_ == other.value_.string_ )
-             || ( other.value_.string_  
-                  &&  value_.string_  
-                  && strcmp( value_.string_, other.value_.string_ ) == 0 );
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   case arrayValue:
-   case objectValue:
-      return value_.map_->size() == other.value_.map_->size()
-             && (*value_.map_) == (*other.value_.map_);
-#else
-   case arrayValue:
-      return value_.array_->compare( *(other.value_.array_) ) == 0;
-   case objectValue:
-      return value_.map_->compare( *(other.value_.map_) ) == 0;
-#endif
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   return false;  // unreachable
-}
-
-bool 
-Value::operator !=( const Value &other ) const
-{
-   return !( *this == other );
-}
-
-const char *
-Value::asCString() const
-{
-   JSON_ASSERT( type_ == stringValue );
-   return value_.string_;
-}
-
-
-std::string 
-Value::asString() const
-{
-   switch ( type_ )
-   {
-   case nullValue:
-      return "";
-   case stringValue:
-      return value_.string_ ? value_.string_ : "";
-   case booleanValue:
-      return value_.bool_ ? "true" : "false";
-   case intValue:
-   case uintValue:
-   case realValue:
-   case arrayValue:
-   case objectValue:
-      JSON_FAIL_MESSAGE( "Type is not convertible to string" );
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   return ""; // unreachable
-}
-
-# ifdef JSON_USE_CPPTL
-CppTL::ConstString 
-Value::asConstString() const
-{
-   return CppTL::ConstString( asString().c_str() );
-}
-# endif
-
-
-Value::Int 
-Value::asInt() const
-{
-   switch ( type_ )
-   {
-   case nullValue:
-      return 0;
-   case intValue:
-      JSON_ASSERT_MESSAGE( value_.int_ >= minInt  &&  value_.int_ <= maxInt, "unsigned integer out of signed int range" );
-      return Int(value_.int_);
-   case uintValue:
-      JSON_ASSERT_MESSAGE( value_.uint_ <= UInt(maxInt), "unsigned integer out of signed int range" );
-      return Int(value_.uint_);
-   case realValue:
-      JSON_ASSERT_MESSAGE( value_.real_ >= minInt  &&  value_.real_ <= maxInt, "Real out of signed integer range" );
-      return Int( value_.real_ );
-   case booleanValue:
-      return value_.bool_ ? 1 : 0;
-   case stringValue:
-   case arrayValue:
-   case objectValue:
-      JSON_FAIL_MESSAGE( "Type is not convertible to int" );
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   return 0; // unreachable;
-}
-
-
-Value::UInt 
-Value::asUInt() const
-{
-   switch ( type_ )
-   {
-   case nullValue:
-      return 0;
-   case intValue:
-      JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to unsigned integer" );
-      JSON_ASSERT_MESSAGE( value_.int_ <= maxUInt, "signed integer out of UInt range" );
-      return UInt(value_.int_);
-   case uintValue:
-      JSON_ASSERT_MESSAGE( value_.uint_ <= maxUInt, "unsigned integer out of UInt range" );
-      return UInt(value_.uint_);
-   case realValue:
-      JSON_ASSERT_MESSAGE( value_.real_ >= 0  &&  value_.real_ <= maxUInt,  "Real out of unsigned integer range" );
-      return UInt( value_.real_ );
-   case booleanValue:
-      return value_.bool_ ? 1 : 0;
-   case stringValue:
-   case arrayValue:
-   case objectValue:
-      JSON_FAIL_MESSAGE( "Type is not convertible to uint" );
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   return 0; // unreachable;
-}
-
-
-# if defined(JSON_HAS_INT64)
-
-Value::Int64
-Value::asInt64() const
-{
-   switch ( type_ )
-   {
-   case nullValue:
-      return 0;
-   case intValue:
-      return value_.int_;
-   case uintValue:
-      JSON_ASSERT_MESSAGE( value_.uint_ <= UInt64(maxInt64), "unsigned integer out of Int64 range" );
-      return value_.uint_;
-   case realValue:
-      JSON_ASSERT_MESSAGE( value_.real_ >= minInt64  &&  value_.real_ <= maxInt64, "Real out of Int64 range" );
-      return Int( value_.real_ );
-   case booleanValue:
-      return value_.bool_ ? 1 : 0;
-   case stringValue:
-   case arrayValue:
-   case objectValue:
-      JSON_FAIL_MESSAGE( "Type is not convertible to Int64" );
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   return 0; // unreachable;
-}
-
-
-Value::UInt64
-Value::asUInt64() const
-{
-   switch ( type_ )
-   {
-   case nullValue:
-      return 0;
-   case intValue:
-      JSON_ASSERT_MESSAGE( value_.int_ >= 0, "Negative integer can not be converted to UInt64" );
-      return value_.int_;
-   case uintValue:
-      return value_.uint_;
-   case realValue:
-      JSON_ASSERT_MESSAGE( value_.real_ >= 0  &&  value_.real_ <= maxUInt64,  "Real out of UInt64 range" );
-      return UInt( value_.real_ );
-   case booleanValue:
-      return value_.bool_ ? 1 : 0;
-   case stringValue:
-   case arrayValue:
-   case objectValue:
-      JSON_FAIL_MESSAGE( "Type is not convertible to UInt64" );
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   return 0; // unreachable;
-}
-# endif // if defined(JSON_HAS_INT64)
-
-
-LargestInt 
-Value::asLargestInt() const
-{
-#if defined(JSON_NO_INT64)
-    return asInt();
-#else
-    return asInt64();
-#endif
-}
-
-
-LargestUInt 
-Value::asLargestUInt() const
-{
-#if defined(JSON_NO_INT64)
-    return asUInt();
-#else
-    return asUInt64();
-#endif
-}
-
-
-double 
-Value::asDouble() const
-{
-   switch ( type_ )
-   {
-   case nullValue:
-      return 0.0;
-   case intValue:
-      return static_cast<double>( value_.int_ );
-   case uintValue:
-#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-      return static_cast<double>( value_.uint_ );
-#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-      return static_cast<double>( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1);
-#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-   case realValue:
-      return value_.real_;
-   case booleanValue:
-      return value_.bool_ ? 1.0 : 0.0;
-   case stringValue:
-   case arrayValue:
-   case objectValue:
-      JSON_FAIL_MESSAGE( "Type is not convertible to double" );
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   return 0; // unreachable;
-}
-
-float
-Value::asFloat() const
-{
-   switch ( type_ )
-   {
-   case nullValue:
-      return 0.0f;
-   case intValue:
-      return static_cast<float>( value_.int_ );
-   case uintValue:
-#if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-      return static_cast<float>( value_.uint_ );
-#else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-      return static_cast<float>( Int(value_.uint_/2) ) * 2 + Int(value_.uint_ & 1);
-#endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION)
-   case realValue:
-      return static_cast<float>( value_.real_ );
-   case booleanValue:
-      return value_.bool_ ? 1.0f : 0.0f;
-   case stringValue:
-   case arrayValue:
-   case objectValue:
-      JSON_FAIL_MESSAGE( "Type is not convertible to float" );
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   return 0.0f; // unreachable;
-}
-
-bool 
-Value::asBool() const
-{
-   switch ( type_ )
-   {
-   case nullValue:
-      return false;
-   case intValue:
-   case uintValue:
-      return value_.int_ != 0;
-   case realValue:
-      return value_.real_ != 0.0;
-   case booleanValue:
-      return value_.bool_;
-   case stringValue:
-      return value_.string_  &&  value_.string_[0] != 0;
-   case arrayValue:
-   case objectValue:
-      return value_.map_->size() != 0;
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   return false; // unreachable;
-}
-
-
-bool 
-Value::isConvertibleTo( ValueType other ) const
-{
-   switch ( type_ )
-   {
-   case nullValue:
-      return true;
-   case intValue:
-      return ( other == nullValue  &&  value_.int_ == 0 )
-             || other == intValue
-             || ( other == uintValue  && value_.int_ >= 0 )
-             || other == realValue
-             || other == stringValue
-             || other == booleanValue;
-   case uintValue:
-      return ( other == nullValue  &&  value_.uint_ == 0 )
-             || ( other == intValue  && value_.uint_ <= (unsigned)maxInt )
-             || other == uintValue
-             || other == realValue
-             || other == stringValue
-             || other == booleanValue;
-   case realValue:
-      return ( other == nullValue  &&  value_.real_ == 0.0 )
-             || ( other == intValue  &&  value_.real_ >= minInt  &&  value_.real_ <= maxInt )
-             || ( other == uintValue  &&  value_.real_ >= 0  &&  value_.real_ <= maxUInt )
-             || other == realValue
-             || other == stringValue
-             || other == booleanValue;
-   case booleanValue:
-      return ( other == nullValue  &&  value_.bool_ == false )
-             || other == intValue
-             || other == uintValue
-             || other == realValue
-             || other == stringValue
-             || other == booleanValue;
-   case stringValue:
-      return other == stringValue
-             || ( other == nullValue  &&  (!value_.string_  ||  value_.string_[0] == 0) );
-   case arrayValue:
-      return other == arrayValue
-             ||  ( other == nullValue  &&  value_.map_->size() == 0 );
-   case objectValue:
-      return other == objectValue
-             ||  ( other == nullValue  &&  value_.map_->size() == 0 );
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   return false; // unreachable;
-}
-
-
-/// Number of values in array or object
-ArrayIndex 
-Value::size() const
-{
-   switch ( type_ )
-   {
-   case nullValue:
-   case intValue:
-   case uintValue:
-   case realValue:
-   case booleanValue:
-   case stringValue:
-      return 0;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   case arrayValue:  // size of the array is highest index + 1
-      if ( !value_.map_->empty() )
-      {
-         ObjectValues::const_iterator itLast = value_.map_->end();
-         --itLast;
-         return (*itLast).first.index()+1;
-      }
-      return 0;
-   case objectValue:
-      return ArrayIndex( value_.map_->size() );
-#else
-   case arrayValue:
-      return Int( value_.array_->size() );
-   case objectValue:
-      return Int( value_.map_->size() );
-#endif
-   default:
-      JSON_ASSERT_UNREACHABLE;
-   }
-   return 0; // unreachable;
-}
-
-
-bool 
-Value::empty() const
-{
-   if ( isNull() || isArray() || isObject() )
-      return size() == 0u;
-   else
-      return false;
-}
-
-
-bool
-Value::operator!() const
-{
-   return isNull();
-}
-
-
-void 
-Value::clear()
-{
-   JSON_ASSERT( type_ == nullValue  ||  type_ == arrayValue  || type_ == objectValue );
-
-   switch ( type_ )
-   {
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   case arrayValue:
-   case objectValue:
-      value_.map_->clear();
-      break;
-#else
-   case arrayValue:
-      value_.array_->clear();
-      break;
-   case objectValue:
-      value_.map_->clear();
-      break;
-#endif
-   default:
-      break;
-   }
-}
-
-void 
-Value::resize( ArrayIndex newSize )
-{
-   JSON_ASSERT( type_ == nullValue  ||  type_ == arrayValue );
-   if ( type_ == nullValue )
-      *this = Value( arrayValue );
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   ArrayIndex oldSize = size();
-   if ( newSize == 0 )
-      clear();
-   else if ( newSize > oldSize )
-      (*this)[ newSize - 1 ];
-   else
-   {
-      for ( ArrayIndex index = newSize; index < oldSize; ++index )
-      {
-         value_.map_->erase( index );
-      }
-      assert( size() == newSize );
-   }
-#else
-   value_.array_->resize( newSize );
-#endif
-}
-
-
-Value &
-Value::operator[]( ArrayIndex index )
-{
-   JSON_ASSERT( type_ == nullValue  ||  type_ == arrayValue );
-   if ( type_ == nullValue )
-      *this = Value( arrayValue );
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   CZString key( index );
-   ObjectValues::iterator it = value_.map_->lower_bound( key );
-   if ( it != value_.map_->end()  &&  (*it).first == key )
-      return (*it).second;
-
-   ObjectValues::value_type defaultValue( key, null );
-   it = value_.map_->insert( it, defaultValue );
-   return (*it).second;
-#else
-   return value_.array_->resolveReference( index );
-#endif
-}
-
-
-Value &
-Value::operator[]( int index )
-{
-   JSON_ASSERT( index >= 0 );
-   return (*this)[ ArrayIndex(index) ];
-}
-
-
-const Value &
-Value::operator[]( ArrayIndex index ) const
-{
-   JSON_ASSERT( type_ == nullValue  ||  type_ == arrayValue );
-   if ( type_ == nullValue )
-      return null;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   CZString key( index );
-   ObjectValues::const_iterator it = value_.map_->find( key );
-   if ( it == value_.map_->end() )
-      return null;
-   return (*it).second;
-#else
-   Value *value = value_.array_->find( index );
-   return value ? *value : null;
-#endif
-}
-
-
-const Value &
-Value::operator[]( int index ) const
-{
-   JSON_ASSERT( index >= 0 );
-   return (*this)[ ArrayIndex(index) ];
-}
-
-
-Value &
-Value::operator[]( const char *key )
-{
-   return resolveReference( key, false );
-}
-
-
-Value &
-Value::resolveReference( const char *key, 
-                         bool isStatic )
-{
-   JSON_ASSERT( type_ == nullValue  ||  type_ == objectValue );
-   if ( type_ == nullValue )
-      *this = Value( objectValue );
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   CZString actualKey( key, isStatic ? CZString::noDuplication 
-                                     : CZString::duplicateOnCopy );
-   ObjectValues::iterator it = value_.map_->lower_bound( actualKey );
-   if ( it != value_.map_->end()  &&  (*it).first == actualKey )
-      return (*it).second;
-
-   ObjectValues::value_type defaultValue( actualKey, null );
-   it = value_.map_->insert( it, defaultValue );
-   Value &value = (*it).second;
-   return value;
-#else
-   return value_.map_->resolveReference( key, isStatic );
-#endif
-}
-
-
-Value 
-Value::get( ArrayIndex index, 
-            const Value &defaultValue ) const
-{
-   const Value *value = &((*this)[index]);
-   return value == &null ? defaultValue : *value;
-}
-
-
-bool 
-Value::isValidIndex( ArrayIndex index ) const
-{
-   return index < size();
-}
-
-
-
-const Value &
-Value::operator[]( const char *key ) const
-{
-   JSON_ASSERT( type_ == nullValue  ||  type_ == objectValue );
-   if ( type_ == nullValue )
-      return null;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   CZString actualKey( key, CZString::noDuplication );
-   ObjectValues::const_iterator it = value_.map_->find( actualKey );
-   if ( it == value_.map_->end() )
-      return null;
-   return (*it).second;
-#else
-   const Value *value = value_.map_->find( key );
-   return value ? *value : null;
-#endif
-}
-
-
-Value &
-Value::operator[]( const std::string &key )
-{
-   return (*this)[ key.c_str() ];
-}
-
-
-const Value &
-Value::operator[]( const std::string &key ) const
-{
-   return (*this)[ key.c_str() ];
-}
-
-Value &
-Value::operator[]( const StaticString &key )
-{
-   return resolveReference( key, true );
-}
-
-
-# ifdef JSON_USE_CPPTL
-Value &
-Value::operator[]( const CppTL::ConstString &key )
-{
-   return (*this)[ key.c_str() ];
-}
-
-
-const Value &
-Value::operator[]( const CppTL::ConstString &key ) const
-{
-   return (*this)[ key.c_str() ];
-}
-# endif
-
-
-Value &
-Value::append( const Value &value )
-{
-   return (*this)[size()] = value;
-}
-
-
-Value 
-Value::get( const char *key, 
-            const Value &defaultValue ) const
-{
-   const Value *value = &((*this)[key]);
-   return value == &null ? defaultValue : *value;
-}
-
-
-Value 
-Value::get( const std::string &key,
-            const Value &defaultValue ) const
-{
-   return get( key.c_str(), defaultValue );
-}
-
-Value
-Value::removeMember( const char* key )
-{
-   JSON_ASSERT( type_ == nullValue  ||  type_ == objectValue );
-   if ( type_ == nullValue )
-      return null;
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   CZString actualKey( key, CZString::noDuplication );
-   ObjectValues::iterator it = value_.map_->find( actualKey );
-   if ( it == value_.map_->end() )
-      return null;
-   Value old(it->second);
-   value_.map_->erase(it);
-   return old;
-#else
-   Value *value = value_.map_->find( key );
-   if (value){
-      Value old(*value);
-      value_.map_.remove( key );
-      return old;
-   } else {
-      return null;
-   }
-#endif
-}
-
-Value
-Value::removeMember( const std::string &key )
-{
-   return removeMember( key.c_str() );
-}
-
-# ifdef JSON_USE_CPPTL
-Value 
-Value::get( const CppTL::ConstString &key,
-            const Value &defaultValue ) const
-{
-   return get( key.c_str(), defaultValue );
-}
-# endif
-
-bool 
-Value::isMember( const char *key ) const
-{
-   const Value *value = &((*this)[key]);
-   return value != &null;
-}
-
-
-bool 
-Value::isMember( const std::string &key ) const
-{
-   return isMember( key.c_str() );
-}
-
-
-# ifdef JSON_USE_CPPTL
-bool 
-Value::isMember( const CppTL::ConstString &key ) const
-{
-   return isMember( key.c_str() );
-}
-#endif
-
-Value::Members 
-Value::getMemberNames() const
-{
-   JSON_ASSERT( type_ == nullValue  ||  type_ == objectValue );
-   if ( type_ == nullValue )
-       return Value::Members();
-   Members members;
-   members.reserve( value_.map_->size() );
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   ObjectValues::const_iterator it = value_.map_->begin();
-   ObjectValues::const_iterator itEnd = value_.map_->end();
-   for ( ; it != itEnd; ++it )
-      members.push_back( std::string( (*it).first.c_str() ) );
-#else
-   ValueInternalMap::IteratorState it;
-   ValueInternalMap::IteratorState itEnd;
-   value_.map_->makeBeginIterator( it );
-   value_.map_->makeEndIterator( itEnd );
-   for ( ; !ValueInternalMap::equals( it, itEnd ); ValueInternalMap::increment(it) )
-      members.push_back( std::string( ValueInternalMap::key( it ) ) );
-#endif
-   return members;
-}
-//
-//# ifdef JSON_USE_CPPTL
-//EnumMemberNames
-//Value::enumMemberNames() const
-//{
-//   if ( type_ == objectValue )
-//   {
-//      return CppTL::Enum::any(  CppTL::Enum::transform(
-//         CppTL::Enum::keys( *(value_.map_), CppTL::Type<const CZString &>() ),
-//         MemberNamesTransform() ) );
-//   }
-//   return EnumMemberNames();
-//}
-//
-//
-//EnumValues 
-//Value::enumValues() const
-//{
-//   if ( type_ == objectValue  ||  type_ == arrayValue )
-//      return CppTL::Enum::anyValues( *(value_.map_), 
-//                                     CppTL::Type<const Value &>() );
-//   return EnumValues();
-//}
-//
-//# endif
-
-
-bool
-Value::isNull() const
-{
-   return type_ == nullValue;
-}
-
-
-bool 
-Value::isBool() const
-{
-   return type_ == booleanValue;
-}
-
-
-bool 
-Value::isInt() const
-{
-   return type_ == intValue;
-}
-
-
-bool 
-Value::isUInt() const
-{
-   return type_ == uintValue;
-}
-
-
-bool 
-Value::isIntegral() const
-{
-   return type_ == intValue  
-          ||  type_ == uintValue  
-          ||  type_ == booleanValue;
-}
-
-
-bool 
-Value::isDouble() const
-{
-   return type_ == realValue;
-}
-
-
-bool 
-Value::isNumeric() const
-{
-   return isIntegral() || isDouble();
-}
-
-
-bool 
-Value::isString() const
-{
-   return type_ == stringValue;
-}
-
-
-bool 
-Value::isArray() const
-{
-   return type_ == nullValue  ||  type_ == arrayValue;
-}
-
-
-bool 
-Value::isObject() const
-{
-   return type_ == nullValue  ||  type_ == objectValue;
-}
-
-
-void 
-Value::setComment( const char *comment,
-                   CommentPlacement placement )
-{
-   if ( !comments_ )
-      comments_ = new CommentInfo[numberOfCommentPlacement];
-   comments_[placement].setComment( comment );
-}
-
-
-void 
-Value::setComment( const std::string &comment,
-                   CommentPlacement placement )
-{
-   setComment( comment.c_str(), placement );
-}
-
-
-bool 
-Value::hasComment( CommentPlacement placement ) const
-{
-   return comments_ != 0  &&  comments_[placement].comment_ != 0;
-}
-
-std::string 
-Value::getComment( CommentPlacement placement ) const
-{
-   if ( hasComment(placement) )
-      return comments_[placement].comment_;
-   return "";
-}
-
-
-std::string 
-Value::toStyledString() const
-{
-   StyledWriter writer;
-   return writer.write( *this );
-}
-
-
-Value::const_iterator 
-Value::begin() const
-{
-   switch ( type_ )
-   {
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
-   case arrayValue:
-      if ( value_.array_ )
-      {
-         ValueInternalArray::IteratorState it;
-         value_.array_->makeBeginIterator( it );
-         return const_iterator( it );
-      }
-      break;
-   case objectValue:
-      if ( value_.map_ )
-      {
-         ValueInternalMap::IteratorState it;
-         value_.map_->makeBeginIterator( it );
-         return const_iterator( it );
-      }
-      break;
-#else
-   case arrayValue:
-   case objectValue:
-      if ( value_.map_ )
-         return const_iterator( value_.map_->begin() );
-      break;
-#endif
-   default:
-      break;
-   }
-   return const_iterator();
-}
-
-Value::const_iterator 
-Value::end() const
-{
-   switch ( type_ )
-   {
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
-   case arrayValue:
-      if ( value_.array_ )
-      {
-         ValueInternalArray::IteratorState it;
-         value_.array_->makeEndIterator( it );
-         return const_iterator( it );
-      }
-      break;
-   case objectValue:
-      if ( value_.map_ )
-      {
-         ValueInternalMap::IteratorState it;
-         value_.map_->makeEndIterator( it );
-         return const_iterator( it );
-      }
-      break;
-#else
-   case arrayValue:
-   case objectValue:
-      if ( value_.map_ )
-         return const_iterator( value_.map_->end() );
-      break;
-#endif
-   default:
-      break;
-   }
-   return const_iterator();
-}
-
-
-Value::iterator 
-Value::begin()
-{
-   switch ( type_ )
-   {
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
-   case arrayValue:
-      if ( value_.array_ )
-      {
-         ValueInternalArray::IteratorState it;
-         value_.array_->makeBeginIterator( it );
-         return iterator( it );
-      }
-      break;
-   case objectValue:
-      if ( value_.map_ )
-      {
-         ValueInternalMap::IteratorState it;
-         value_.map_->makeBeginIterator( it );
-         return iterator( it );
-      }
-      break;
-#else
-   case arrayValue:
-   case objectValue:
-      if ( value_.map_ )
-         return iterator( value_.map_->begin() );
-      break;
-#endif
-   default:
-      break;
-   }
-   return iterator();
-}
-
-Value::iterator 
-Value::end()
-{
-   switch ( type_ )
-   {
-#ifdef JSON_VALUE_USE_INTERNAL_MAP
-   case arrayValue:
-      if ( value_.array_ )
-      {
-         ValueInternalArray::IteratorState it;
-         value_.array_->makeEndIterator( it );
-         return iterator( it );
-      }
-      break;
-   case objectValue:
-      if ( value_.map_ )
-      {
-         ValueInternalMap::IteratorState it;
-         value_.map_->makeEndIterator( it );
-         return iterator( it );
-      }
-      break;
-#else
-   case arrayValue:
-   case objectValue:
-      if ( value_.map_ )
-         return iterator( value_.map_->end() );
-      break;
-#endif
-   default:
-      break;
-   }
-   return iterator();
-}
-
-
-// class PathArgument
-// //////////////////////////////////////////////////////////////////
-
-PathArgument::PathArgument()
-   : kind_( kindNone )
-{
-}
-
-
-PathArgument::PathArgument( ArrayIndex index )
-   : index_( index )
-   , kind_( kindIndex )
-{
-}
-
-
-PathArgument::PathArgument( const char *key )
-   : key_( key )
-   , kind_( kindKey )
-{
-}
-
-
-PathArgument::PathArgument( const std::string &key )
-   : key_( key.c_str() )
-   , kind_( kindKey )
-{
-}
-
-// class Path
-// //////////////////////////////////////////////////////////////////
-
-Path::Path( const std::string &path,
-            const PathArgument &a1,
-            const PathArgument &a2,
-            const PathArgument &a3,
-            const PathArgument &a4,
-            const PathArgument &a5 )
-{
-   InArgs in;
-   in.push_back( &a1 );
-   in.push_back( &a2 );
-   in.push_back( &a3 );
-   in.push_back( &a4 );
-   in.push_back( &a5 );
-   makePath( path, in );
-}
-
-
-void 
-Path::makePath( const std::string &path,
-                const InArgs &in )
-{
-   const char *current = path.c_str();
-   const char *end = current + path.length();
-   InArgs::const_iterator itInArg = in.begin();
-   while ( current != end )
-   {
-      if ( *current == '[' )
-      {
-         ++current;
-         if ( *current == '%' )
-            addPathInArg( path, in, itInArg, PathArgument::kindIndex );
-         else
-         {
-            ArrayIndex index = 0;
-            for ( ; current != end && *current >= '0'  &&  *current <= '9'; ++current )
-               index = index * 10 + ArrayIndex(*current - '0');
-            args_.push_back( index );
-         }
-         if ( current == end  ||  *current++ != ']' )
-            invalidPath( path, int(current - path.c_str()) );
-      }
-      else if ( *current == '%' )
-      {
-         addPathInArg( path, in, itInArg, PathArgument::kindKey );
-         ++current;
-      }
-      else if ( *current == '.' )
-      {
-         ++current;
-      }
-      else
-      {
-         const char *beginName = current;
-         while ( current != end  &&  !strchr( "[.", *current ) )
-            ++current;
-         args_.push_back( std::string( beginName, current ) );
-      }
-   }
-}
-
-
-void 
-Path::addPathInArg( const std::string &path, 
-                    const InArgs &in, 
-                    InArgs::const_iterator &itInArg, 
-                    PathArgument::Kind kind )
-{
-   if ( itInArg == in.end() )
-   {
-      // Error: missing argument %d
-   }
-   else if ( (*itInArg)->kind_ != kind )
-   {
-      // Error: bad argument type
-   }
-   else
-   {
-      args_.push_back( **itInArg );
-   }
-}
-
-
-void 
-Path::invalidPath( const std::string &path, 
-                   int location )
-{
-   // Error: invalid path.
-}
-
-
-const Value &
-Path::resolve( const Value &root ) const
-{
-   const Value *node = &root;
-   for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it )
-   {
-      const PathArgument &arg = *it;
-      if ( arg.kind_ == PathArgument::kindIndex )
-      {
-         if ( !node->isArray()  ||  node->isValidIndex( arg.index_ ) )
-         {
-            // Error: unable to resolve path (array value expected at position...
-         }
-         node = &((*node)[arg.index_]);
-      }
-      else if ( arg.kind_ == PathArgument::kindKey )
-      {
-         if ( !node->isObject() )
-         {
-            // Error: unable to resolve path (object value expected at position...)
-         }
-         node = &((*node)[arg.key_]);
-         if ( node == &Value::null )
-         {
-            // Error: unable to resolve path (object has no member named '' at position...)
-         }
-      }
-   }
-   return *node;
-}
-
-
-Value 
-Path::resolve( const Value &root, 
-               const Value &defaultValue ) const
-{
-   const Value *node = &root;
-   for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it )
-   {
-      const PathArgument &arg = *it;
-      if ( arg.kind_ == PathArgument::kindIndex )
-      {
-         if ( !node->isArray()  ||  node->isValidIndex( arg.index_ ) )
-            return defaultValue;
-         node = &((*node)[arg.index_]);
-      }
-      else if ( arg.kind_ == PathArgument::kindKey )
-      {
-         if ( !node->isObject() )
-            return defaultValue;
-         node = &((*node)[arg.key_]);
-         if ( node == &Value::null )
-            return defaultValue;
-      }
-   }
-   return *node;
-}
-
-
-Value &
-Path::make( Value &root ) const
-{
-   Value *node = &root;
-   for ( Args::const_iterator it = args_.begin(); it != args_.end(); ++it )
-   {
-      const PathArgument &arg = *it;
-      if ( arg.kind_ == PathArgument::kindIndex )
-      {
-         if ( !node->isArray() )
-         {
-            // Error: node is not an array at position ...
-         }
-         node = &((*node)[arg.index_]);
-      }
-      else if ( arg.kind_ == PathArgument::kindKey )
-      {
-         if ( !node->isObject() )
-         {
-            // Error: node is not an object at position...
-         }
-         node = &((*node)[arg.key_]);
-      }
-   }
-   return *node;
-}
-
-
-} // namespace Json

+ 0 - 299
ext/jsoncpp/src/json_valueiterator.inl

@@ -1,299 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-// included by json_value.cpp
-
-namespace Json {
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class ValueIteratorBase
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-ValueIteratorBase::ValueIteratorBase()
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   : current_()
-   , isNull_( true )
-{
-}
-#else
-   : isArray_( true )
-   , isNull_( true )
-{
-   iterator_.array_ = ValueInternalArray::IteratorState();
-}
-#endif
-
-
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator &current )
-   : current_( current )
-   , isNull_( false )
-{
-}
-#else
-ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state )
-   : isArray_( true )
-{
-   iterator_.array_ = state;
-}
-
-
-ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state )
-   : isArray_( false )
-{
-   iterator_.map_ = state;
-}
-#endif
-
-Value &
-ValueIteratorBase::deref() const
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   return current_->second;
-#else
-   if ( isArray_ )
-      return ValueInternalArray::dereference( iterator_.array_ );
-   return ValueInternalMap::value( iterator_.map_ );
-#endif
-}
-
-
-void 
-ValueIteratorBase::increment()
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   ++current_;
-#else
-   if ( isArray_ )
-      ValueInternalArray::increment( iterator_.array_ );
-   ValueInternalMap::increment( iterator_.map_ );
-#endif
-}
-
-
-void 
-ValueIteratorBase::decrement()
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   --current_;
-#else
-   if ( isArray_ )
-      ValueInternalArray::decrement( iterator_.array_ );
-   ValueInternalMap::decrement( iterator_.map_ );
-#endif
-}
-
-
-ValueIteratorBase::difference_type 
-ValueIteratorBase::computeDistance( const SelfType &other ) const
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-# ifdef JSON_USE_CPPTL_SMALLMAP
-   return current_ - other.current_;
-# else
-   // Iterator for null value are initialized using the default
-   // constructor, which initialize current_ to the default
-   // std::map::iterator. As begin() and end() are two instance 
-   // of the default std::map::iterator, they can not be compared.
-   // To allow this, we handle this comparison specifically.
-   if ( isNull_  &&  other.isNull_ )
-   {
-      return 0;
-   }
-
-
-   // Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL,
-   // which is the one used by default).
-   // Using a portable hand-made version for non random iterator instead:
-   //   return difference_type( std::distance( current_, other.current_ ) );
-   difference_type myDistance = 0;
-   for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it )
-   {
-      ++myDistance;
-   }
-   return myDistance;
-# endif
-#else
-   if ( isArray_ )
-      return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ );
-   return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ );
-#endif
-}
-
-
-bool 
-ValueIteratorBase::isEqual( const SelfType &other ) const
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   if ( isNull_ )
-   {
-      return other.isNull_;
-   }
-   return current_ == other.current_;
-#else
-   if ( isArray_ )
-      return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ );
-   return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ );
-#endif
-}
-
-
-void 
-ValueIteratorBase::copy( const SelfType &other )
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   current_ = other.current_;
-#else
-   if ( isArray_ )
-      iterator_.array_ = other.iterator_.array_;
-   iterator_.map_ = other.iterator_.map_;
-#endif
-}
-
-
-Value 
-ValueIteratorBase::key() const
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   const Value::CZString czstring = (*current_).first;
-   if ( czstring.c_str() )
-   {
-      if ( czstring.isStaticString() )
-         return Value( StaticString( czstring.c_str() ) );
-      return Value( czstring.c_str() );
-   }
-   return Value( czstring.index() );
-#else
-   if ( isArray_ )
-      return Value( ValueInternalArray::indexOf( iterator_.array_ ) );
-   bool isStatic;
-   const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic );
-   if ( isStatic )
-      return Value( StaticString( memberName ) );
-   return Value( memberName );
-#endif
-}
-
-
-UInt 
-ValueIteratorBase::index() const
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   const Value::CZString czstring = (*current_).first;
-   if ( !czstring.c_str() )
-      return czstring.index();
-   return Value::UInt( -1 );
-#else
-   if ( isArray_ )
-      return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) );
-   return Value::UInt( -1 );
-#endif
-}
-
-
-const char *
-ValueIteratorBase::memberName() const
-{
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-   const char *name = (*current_).first.c_str();
-   return name ? name : "";
-#else
-   if ( !isArray_ )
-      return ValueInternalMap::key( iterator_.map_ );
-   return "";
-#endif
-}
-
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class ValueConstIterator
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-ValueConstIterator::ValueConstIterator()
-{
-}
-
-
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator &current )
-   : ValueIteratorBase( current )
-{
-}
-#else
-ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state )
-   : ValueIteratorBase( state )
-{
-}
-
-ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state )
-   : ValueIteratorBase( state )
-{
-}
-#endif
-
-ValueConstIterator &
-ValueConstIterator::operator =( const ValueIteratorBase &other )
-{
-   copy( other );
-   return *this;
-}
-
-
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// class ValueIterator
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-// //////////////////////////////////////////////////////////////////
-
-ValueIterator::ValueIterator()
-{
-}
-
-
-#ifndef JSON_VALUE_USE_INTERNAL_MAP
-ValueIterator::ValueIterator( const Value::ObjectValues::iterator &current )
-   : ValueIteratorBase( current )
-{
-}
-#else
-ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state )
-   : ValueIteratorBase( state )
-{
-}
-
-ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state )
-   : ValueIteratorBase( state )
-{
-}
-#endif
-
-ValueIterator::ValueIterator( const ValueConstIterator &other )
-   : ValueIteratorBase( other )
-{
-}
-
-ValueIterator::ValueIterator( const ValueIterator &other )
-   : ValueIteratorBase( other )
-{
-}
-
-ValueIterator &
-ValueIterator::operator =( const SelfType &other )
-{
-   copy( other );
-   return *this;
-}
-
-} // namespace Json

+ 0 - 838
ext/jsoncpp/src/json_writer.cpp

@@ -1,838 +0,0 @@
-// Copyright 2007-2010 Baptiste Lepilleur
-// Distributed under MIT license, or public domain if desired and
-// recognized in your jurisdiction.
-// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
-
-#if !defined(JSON_IS_AMALGAMATION)
-# include <json/writer.h>
-# include "json_tool.h"
-#endif // if !defined(JSON_IS_AMALGAMATION)
-#include <utility>
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include <iostream>
-#include <sstream>
-#include <iomanip>
-
-#if _MSC_VER >= 1400 // VC++ 8.0
-#pragma warning( disable : 4996 )   // disable warning about strdup being deprecated.
-#endif
-
-namespace Json {
-
-static bool containsControlCharacter( const char* str )
-{
-   while ( *str ) 
-   {
-      if ( isControlCharacter( *(str++) ) )
-         return true;
-   }
-   return false;
-}
-
-
-std::string valueToString( LargestInt value )
-{
-   UIntToStringBuffer buffer;
-   char *current = buffer + sizeof(buffer);
-   bool isNegative = value < 0;
-   if ( isNegative )
-      value = -value;
-   uintToString( LargestUInt(value), current );
-   if ( isNegative )
-      *--current = '-';
-   assert( current >= buffer );
-   return current;
-}
-
-
-std::string valueToString( LargestUInt value )
-{
-   UIntToStringBuffer buffer;
-   char *current = buffer + sizeof(buffer);
-   uintToString( value, current );
-   assert( current >= buffer );
-   return current;
-}
-
-#if defined(JSON_HAS_INT64)
-
-std::string valueToString( Int value )
-{
-   return valueToString( LargestInt(value) );
-}
-
-
-std::string valueToString( UInt value )
-{
-   return valueToString( LargestUInt(value) );
-}
-
-#endif // # if defined(JSON_HAS_INT64)
-
-
-std::string valueToString( double value )
-{
-   char buffer[32];
-#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning. 
-   sprintf_s(buffer, sizeof(buffer), "%#.16g", value); 
-#else	
-   sprintf(buffer, "%#.16g", value); 
-#endif
-   char* ch = buffer + strlen(buffer) - 1;
-   if (*ch != '0') return buffer; // nothing to truncate, so save time
-   while(ch > buffer && *ch == '0'){
-     --ch;
-   }
-   char* last_nonzero = ch;
-   while(ch >= buffer){
-     switch(*ch){
-     case '0':
-     case '1':
-     case '2':
-     case '3':
-     case '4':
-     case '5':
-     case '6':
-     case '7':
-     case '8':
-     case '9':
-       --ch;
-       continue;
-     case '.':
-       // Truncate zeroes to save bytes in output, but keep one.
-       *(last_nonzero+2) = '\0';
-       return buffer;
-     default:
-       return buffer;
-     }
-   }
-   return buffer;
-}
-
-
-std::string valueToString( bool value )
-{
-   return value ? "true" : "false";
-}
-
-std::string valueToQuotedString( const char *value )
-{
-   // Not sure how to handle unicode...
-   if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
-      return std::string("\"") + value + "\"";
-   // We have to walk value and escape any special characters.
-   // Appending to std::string is not efficient, but this should be rare.
-   // (Note: forward slashes are *not* rare, but I am not escaping them.)
-   std::string::size_type maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
-   std::string result;
-   result.reserve(maxsize); // to avoid lots of mallocs
-   result += "\"";
-   for (const char* c=value; *c != 0; ++c)
-   {
-      switch(*c)
-      {
-         case '\"':
-            result += "\\\"";
-            break;
-         case '\\':
-            result += "\\\\";
-            break;
-         case '\b':
-            result += "\\b";
-            break;
-         case '\f':
-            result += "\\f";
-            break;
-         case '\n':
-            result += "\\n";
-            break;
-         case '\r':
-            result += "\\r";
-            break;
-         case '\t':
-            result += "\\t";
-            break;
-         //case '/':
-            // Even though \/ is considered a legal escape in JSON, a bare
-            // slash is also legal, so I see no reason to escape it.
-            // (I hope I am not misunderstanding something.
-            // blep notes: actually escaping \/ may be useful in javascript to avoid </ 
-            // sequence.
-            // Should add a flag to allow this compatibility mode and prevent this 
-            // sequence from occurring.
-         default:
-            if ( isControlCharacter( *c ) )
-            {
-               std::ostringstream oss;
-               oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
-               result += oss.str();
-            }
-            else
-            {
-               result += *c;
-            }
-            break;
-      }
-   }
-   result += "\"";
-   return result;
-}
-
-// Class Writer
-// //////////////////////////////////////////////////////////////////
-Writer::~Writer()
-{
-}
-
-
-// Class FastWriter
-// //////////////////////////////////////////////////////////////////
-
-FastWriter::FastWriter()
-   : yamlCompatiblityEnabled_( false )
-{
-}
-
-
-void 
-FastWriter::enableYAMLCompatibility()
-{
-   yamlCompatiblityEnabled_ = true;
-}
-
-
-std::string 
-FastWriter::write( const Value &root )
-{
-   document_ = "";
-   writeValue( root );
-   document_ += "\n";
-   return document_;
-}
-
-
-void 
-FastWriter::writeValue( const Value &value )
-{
-   switch ( value.type() )
-   {
-   case nullValue:
-      document_ += "null";
-      break;
-   case intValue:
-      document_ += valueToString( value.asLargestInt() );
-      break;
-   case uintValue:
-      document_ += valueToString( value.asLargestUInt() );
-      break;
-   case realValue:
-      document_ += valueToString( value.asDouble() );
-      break;
-   case stringValue:
-      document_ += valueToQuotedString( value.asCString() );
-      break;
-   case booleanValue:
-      document_ += valueToString( value.asBool() );
-      break;
-   case arrayValue:
-      {
-         document_ += "[";
-         int size = value.size();
-         for ( int index =0; index < size; ++index )
-         {
-            if ( index > 0 )
-               document_ += ",";
-            writeValue( value[index] );
-         }
-         document_ += "]";
-      }
-      break;
-   case objectValue:
-      {
-         Value::Members members( value.getMemberNames() );
-         document_ += "{";
-         for ( Value::Members::iterator it = members.begin(); 
-               it != members.end(); 
-               ++it )
-         {
-            const std::string &name = *it;
-            if ( it != members.begin() )
-               document_ += ",";
-            document_ += valueToQuotedString( name.c_str() );
-            document_ += yamlCompatiblityEnabled_ ? ": " 
-                                                  : ":";
-            writeValue( value[name] );
-         }
-         document_ += "}";
-      }
-      break;
-   }
-}
-
-
-// Class StyledWriter
-// //////////////////////////////////////////////////////////////////
-
-StyledWriter::StyledWriter()
-   : rightMargin_( 74 )
-   , indentSize_( 3 )
-{
-}
-
-
-std::string 
-StyledWriter::write( const Value &root )
-{
-   document_ = "";
-   addChildValues_ = false;
-   indentString_ = "";
-   writeCommentBeforeValue( root );
-   writeValue( root );
-   writeCommentAfterValueOnSameLine( root );
-   document_ += "\n";
-   return document_;
-}
-
-
-void 
-StyledWriter::writeValue( const Value &value )
-{
-   switch ( value.type() )
-   {
-   case nullValue:
-      pushValue( "null" );
-      break;
-   case intValue:
-      pushValue( valueToString( value.asLargestInt() ) );
-      break;
-   case uintValue:
-      pushValue( valueToString( value.asLargestUInt() ) );
-      break;
-   case realValue:
-      pushValue( valueToString( value.asDouble() ) );
-      break;
-   case stringValue:
-      pushValue( valueToQuotedString( value.asCString() ) );
-      break;
-   case booleanValue:
-      pushValue( valueToString( value.asBool() ) );
-      break;
-   case arrayValue:
-      writeArrayValue( value);
-      break;
-   case objectValue:
-      {
-         Value::Members members( value.getMemberNames() );
-         if ( members.empty() )
-            pushValue( "{}" );
-         else
-         {
-            writeWithIndent( "{" );
-            indent();
-            Value::Members::iterator it = members.begin();
-            for (;;)
-            {
-               const std::string &name = *it;
-               const Value &childValue = value[name];
-               writeCommentBeforeValue( childValue );
-               writeWithIndent( valueToQuotedString( name.c_str() ) );
-               document_ += " : ";
-               writeValue( childValue );
-               if ( ++it == members.end() )
-               {
-                  writeCommentAfterValueOnSameLine( childValue );
-                  break;
-               }
-               document_ += ",";
-               writeCommentAfterValueOnSameLine( childValue );
-            }
-            unindent();
-            writeWithIndent( "}" );
-         }
-      }
-      break;
-   }
-}
-
-
-void 
-StyledWriter::writeArrayValue( const Value &value )
-{
-   unsigned size = value.size();
-   if ( size == 0 )
-      pushValue( "[]" );
-   else
-   {
-      bool isArrayMultiLine = isMultineArray( value );
-      if ( isArrayMultiLine )
-      {
-         writeWithIndent( "[" );
-         indent();
-         bool hasChildValue = !childValues_.empty();
-         unsigned index =0;
-         for (;;)
-         {
-            const Value &childValue = value[index];
-            writeCommentBeforeValue( childValue );
-            if ( hasChildValue )
-               writeWithIndent( childValues_[index] );
-            else
-            {
-               writeIndent();
-               writeValue( childValue );
-            }
-            if ( ++index == size )
-            {
-               writeCommentAfterValueOnSameLine( childValue );
-               break;
-            }
-            document_ += ",";
-            writeCommentAfterValueOnSameLine( childValue );
-         }
-         unindent();
-         writeWithIndent( "]" );
-      }
-      else // output on a single line
-      {
-         assert( childValues_.size() == size );
-         document_ += "[ ";
-         for ( unsigned index =0; index < size; ++index )
-         {
-            if ( index > 0 )
-               document_ += ", ";
-            document_ += childValues_[index];
-         }
-         document_ += " ]";
-      }
-   }
-}
-
-
-bool 
-StyledWriter::isMultineArray( const Value &value )
-{
-   int size = value.size();
-   bool isMultiLine = size*3 >= rightMargin_ ;
-   childValues_.clear();
-   for ( int index =0; index < size  &&  !isMultiLine; ++index )
-   {
-      const Value &childValue = value[index];
-      isMultiLine = isMultiLine  ||
-                     ( (childValue.isArray()  ||  childValue.isObject())  &&  
-                        childValue.size() > 0 );
-   }
-   if ( !isMultiLine ) // check if line length > max line length
-   {
-      childValues_.reserve( size );
-      addChildValues_ = true;
-      int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
-      for ( int index =0; index < size  &&  !isMultiLine; ++index )
-      {
-         writeValue( value[index] );
-         lineLength += int( childValues_[index].length() );
-         isMultiLine = isMultiLine  &&  hasCommentForValue( value[index] );
-      }
-      addChildValues_ = false;
-      isMultiLine = isMultiLine  ||  lineLength >= rightMargin_;
-   }
-   return isMultiLine;
-}
-
-
-void 
-StyledWriter::pushValue( const std::string &value )
-{
-   if ( addChildValues_ )
-      childValues_.push_back( value );
-   else
-      document_ += value;
-}
-
-
-void 
-StyledWriter::writeIndent()
-{
-   if ( !document_.empty() )
-   {
-      char last = document_[document_.length()-1];
-      if ( last == ' ' )     // already indented
-         return;
-      if ( last != '\n' )    // Comments may add new-line
-         document_ += '\n';
-   }
-   document_ += indentString_;
-}
-
-
-void 
-StyledWriter::writeWithIndent( const std::string &value )
-{
-   writeIndent();
-   document_ += value;
-}
-
-
-void 
-StyledWriter::indent()
-{
-   indentString_ += std::string( indentSize_, ' ' );
-}
-
-
-void 
-StyledWriter::unindent()
-{
-   assert( int(indentString_.size()) >= indentSize_ );
-   indentString_.resize( indentString_.size() - indentSize_ );
-}
-
-
-void 
-StyledWriter::writeCommentBeforeValue( const Value &root )
-{
-   if ( !root.hasComment( commentBefore ) )
-      return;
-   document_ += normalizeEOL( root.getComment( commentBefore ) );
-   document_ += "\n";
-}
-
-
-void 
-StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
-{
-   if ( root.hasComment( commentAfterOnSameLine ) )
-      document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
-
-   if ( root.hasComment( commentAfter ) )
-   {
-      document_ += "\n";
-      document_ += normalizeEOL( root.getComment( commentAfter ) );
-      document_ += "\n";
-   }
-}
-
-
-bool 
-StyledWriter::hasCommentForValue( const Value &value )
-{
-   return value.hasComment( commentBefore )
-          ||  value.hasComment( commentAfterOnSameLine )
-          ||  value.hasComment( commentAfter );
-}
-
-
-std::string 
-StyledWriter::normalizeEOL( const std::string &text )
-{
-   std::string normalized;
-   normalized.reserve( text.length() );
-   const char *begin = text.c_str();
-   const char *end = begin + text.length();
-   const char *current = begin;
-   while ( current != end )
-   {
-      char c = *current++;
-      if ( c == '\r' ) // mac or dos EOL
-      {
-         if ( *current == '\n' ) // convert dos EOL
-            ++current;
-         normalized += '\n';
-      }
-      else // handle unix EOL & other char
-         normalized += c;
-   }
-   return normalized;
-}
-
-
-// Class StyledStreamWriter
-// //////////////////////////////////////////////////////////////////
-
-StyledStreamWriter::StyledStreamWriter( std::string indentation )
-   : document_(NULL)
-   , rightMargin_( 74 )
-   , indentation_( indentation )
-{
-}
-
-
-void
-StyledStreamWriter::write( std::ostream &out, const Value &root )
-{
-   document_ = &out;
-   addChildValues_ = false;
-   indentString_ = "";
-   writeCommentBeforeValue( root );
-   writeValue( root );
-   writeCommentAfterValueOnSameLine( root );
-   *document_ << "\n";
-   document_ = NULL; // Forget the stream, for safety.
-}
-
-
-void 
-StyledStreamWriter::writeValue( const Value &value )
-{
-   switch ( value.type() )
-   {
-   case nullValue:
-      pushValue( "null" );
-      break;
-   case intValue:
-      pushValue( valueToString( value.asLargestInt() ) );
-      break;
-   case uintValue:
-      pushValue( valueToString( value.asLargestUInt() ) );
-      break;
-   case realValue:
-      pushValue( valueToString( value.asDouble() ) );
-      break;
-   case stringValue:
-      pushValue( valueToQuotedString( value.asCString() ) );
-      break;
-   case booleanValue:
-      pushValue( valueToString( value.asBool() ) );
-      break;
-   case arrayValue:
-      writeArrayValue( value);
-      break;
-   case objectValue:
-      {
-         Value::Members members( value.getMemberNames() );
-         if ( members.empty() )
-            pushValue( "{}" );
-         else
-         {
-            writeWithIndent( "{" );
-            indent();
-            Value::Members::iterator it = members.begin();
-            for (;;)
-            {
-               const std::string &name = *it;
-               const Value &childValue = value[name];
-               writeCommentBeforeValue( childValue );
-               writeWithIndent( valueToQuotedString( name.c_str() ) );
-               *document_ << " : ";
-               writeValue( childValue );
-               if ( ++it == members.end() )
-               {
-                  writeCommentAfterValueOnSameLine( childValue );
-                  break;
-               }
-               *document_ << ",";
-               writeCommentAfterValueOnSameLine( childValue );
-            }
-            unindent();
-            writeWithIndent( "}" );
-         }
-      }
-      break;
-   }
-}
-
-
-void 
-StyledStreamWriter::writeArrayValue( const Value &value )
-{
-   unsigned size = value.size();
-   if ( size == 0 )
-      pushValue( "[]" );
-   else
-   {
-      bool isArrayMultiLine = isMultineArray( value );
-      if ( isArrayMultiLine )
-      {
-         writeWithIndent( "[" );
-         indent();
-         bool hasChildValue = !childValues_.empty();
-         unsigned index =0;
-         for (;;)
-         {
-            const Value &childValue = value[index];
-            writeCommentBeforeValue( childValue );
-            if ( hasChildValue )
-               writeWithIndent( childValues_[index] );
-            else
-            {
-               writeIndent();
-               writeValue( childValue );
-            }
-            if ( ++index == size )
-            {
-               writeCommentAfterValueOnSameLine( childValue );
-               break;
-            }
-            *document_ << ",";
-            writeCommentAfterValueOnSameLine( childValue );
-         }
-         unindent();
-         writeWithIndent( "]" );
-      }
-      else // output on a single line
-      {
-         assert( childValues_.size() == size );
-         *document_ << "[ ";
-         for ( unsigned index =0; index < size; ++index )
-         {
-            if ( index > 0 )
-               *document_ << ", ";
-            *document_ << childValues_[index];
-         }
-         *document_ << " ]";
-      }
-   }
-}
-
-
-bool 
-StyledStreamWriter::isMultineArray( const Value &value )
-{
-   int size = value.size();
-   bool isMultiLine = size*3 >= rightMargin_ ;
-   childValues_.clear();
-   for ( int index =0; index < size  &&  !isMultiLine; ++index )
-   {
-      const Value &childValue = value[index];
-      isMultiLine = isMultiLine  ||
-                     ( (childValue.isArray()  ||  childValue.isObject())  &&  
-                        childValue.size() > 0 );
-   }
-   if ( !isMultiLine ) // check if line length > max line length
-   {
-      childValues_.reserve( size );
-      addChildValues_ = true;
-      int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
-      for ( int index =0; index < size  &&  !isMultiLine; ++index )
-      {
-         writeValue( value[index] );
-         lineLength += int( childValues_[index].length() );
-         isMultiLine = isMultiLine  &&  hasCommentForValue( value[index] );
-      }
-      addChildValues_ = false;
-      isMultiLine = isMultiLine  ||  lineLength >= rightMargin_;
-   }
-   return isMultiLine;
-}
-
-
-void 
-StyledStreamWriter::pushValue( const std::string &value )
-{
-   if ( addChildValues_ )
-      childValues_.push_back( value );
-   else
-      *document_ << value;
-}
-
-
-void 
-StyledStreamWriter::writeIndent()
-{
-  /*
-    Some comments in this method would have been nice. ;-)
-
-   if ( !document_.empty() )
-   {
-      char last = document_[document_.length()-1];
-      if ( last == ' ' )     // already indented
-         return;
-      if ( last != '\n' )    // Comments may add new-line
-         *document_ << '\n';
-   }
-  */
-   *document_ << '\n' << indentString_;
-}
-
-
-void 
-StyledStreamWriter::writeWithIndent( const std::string &value )
-{
-   writeIndent();
-   *document_ << value;
-}
-
-
-void 
-StyledStreamWriter::indent()
-{
-   indentString_ += indentation_;
-}
-
-
-void 
-StyledStreamWriter::unindent()
-{
-   assert( indentString_.size() >= indentation_.size() );
-   indentString_.resize( indentString_.size() - indentation_.size() );
-}
-
-
-void 
-StyledStreamWriter::writeCommentBeforeValue( const Value &root )
-{
-   if ( !root.hasComment( commentBefore ) )
-      return;
-   *document_ << normalizeEOL( root.getComment( commentBefore ) );
-   *document_ << "\n";
-}
-
-
-void 
-StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
-{
-   if ( root.hasComment( commentAfterOnSameLine ) )
-      *document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
-
-   if ( root.hasComment( commentAfter ) )
-   {
-      *document_ << "\n";
-      *document_ << normalizeEOL( root.getComment( commentAfter ) );
-      *document_ << "\n";
-   }
-}
-
-
-bool 
-StyledStreamWriter::hasCommentForValue( const Value &value )
-{
-   return value.hasComment( commentBefore )
-          ||  value.hasComment( commentAfterOnSameLine )
-          ||  value.hasComment( commentAfter );
-}
-
-
-std::string 
-StyledStreamWriter::normalizeEOL( const std::string &text )
-{
-   std::string normalized;
-   normalized.reserve( text.length() );
-   const char *begin = text.c_str();
-   const char *end = begin + text.length();
-   const char *current = begin;
-   while ( current != end )
-   {
-      char c = *current++;
-      if ( c == '\r' ) // mac or dos EOL
-      {
-         if ( *current == '\n' ) // convert dos EOL
-            ++current;
-         normalized += '\n';
-      }
-      else // handle unix EOL & other char
-         normalized += c;
-   }
-   return normalized;
-}
-
-
-std::ostream& operator<<( std::ostream &sout, const Value &root )
-{
-   Json::StyledStreamWriter writer;
-   writer.write(sout, root);
-   return sout;
-}
-
-
-} // namespace Json

+ 1 - 1
idtool.cpp

@@ -159,7 +159,7 @@ int main(int argc,char **argv)
 		}
 
 		std::string signature(Utils::base64Decode(argv[4],strlen(argv[4])));
-		if ((signature.length() > ZEROTIER_ADDRESS_LENGTH)&&(id.verifySignature(inf.data(),inf.length(),signature))) {
+		if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verifySignature(inf.data(),inf.length(),signature.data(),signature.length()))) {
 			std::cout << argv[3] << " signature valid" << std::endl;
 		} else {
 			std::cerr << argv[3] << " signature check FAILED" << std::endl;

+ 7 - 14
main.cpp

@@ -46,7 +46,6 @@
 
 #include "node/Node.hpp"
 #include "node/Utils.hpp"
-#include "node/Defaults.hpp"
 
 #include "launcher.h"
 
@@ -67,24 +66,15 @@ static void sighandlerQuit(int sig)
 		n->terminate();
 	else exit(0);
 }
-static void sighandlerUsr(int sig)
-{
-}
-static void sighandlerHup(int sig)
-{
-	Node *n = node;
-	if (n)
-		n->updateStatusNow();
-}
 #endif
 
 int main(int argc,char **argv)
 {
 #ifndef _WIN32
-	signal(SIGHUP,&sighandlerHup);
+	signal(SIGHUP,SIG_IGN);
 	signal(SIGPIPE,SIG_IGN);
-	signal(SIGUSR1,&sighandlerUsr);
-	signal(SIGUSR2,&sighandlerUsr);
+	signal(SIGUSR1,SIG_IGN);
+	signal(SIGUSR2,SIG_IGN);
 	signal(SIGALRM,SIG_IGN);
 	signal(SIGINT,&sighandlerQuit);
 	signal(SIGTERM,&sighandlerQuit);
@@ -124,13 +114,16 @@ int main(int argc,char **argv)
 
 	int exitCode = ZT_EXEC_RETURN_VALUE_NORMAL_TERMINATION;
 
-	node = new Node(homeDir,ZT_DEFAULTS.configUrlPrefix.c_str(),ZT_DEFAULTS.configAuthority.c_str());
+	node = new Node(homeDir);
+	const char *termReason = (char *)0;
 	switch(node->run()) {
 		case Node::NODE_RESTART_FOR_RECONFIGURATION:
 			exitCode = ZT_EXEC_RETURN_VALUE_PLEASE_RESTART;
 			break;
 		case Node::NODE_UNRECOVERABLE_ERROR:
 			exitCode = ZT_EXEC_RETURN_VALUE_UNRECOVERABLE_ERROR;
+			termReason = node->reasonForTermination();
+			fprintf(stderr,"%s: abnormal termination: %s\n",argv[0],(termReason) ? termReason : "(unknown reason)");
 			break;
 		case Node::NODE_NEW_VERSION_AVAILABLE:
 			exitCode = ZT_EXEC_RETURN_VALUE_TERMINATED_FOR_UPGRADE;

+ 47 - 0
makekeypair.cpp

@@ -0,0 +1,47 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013  ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <iostream>
+#include <string>
+#include "node/EllipticCurveKeyPair.hpp"
+#include "node/EllipticCurveKey.hpp"
+#include "node/Utils.hpp"
+
+using namespace ZeroTier;
+
+int main(int argc,char **argv)
+{
+	std::cout << "[generating]" << std::endl;
+
+	EllipticCurveKeyPair kp;
+	kp.generate();
+
+	std::cout << "PUBLIC: " << kp.pub().toHex() << std::endl;
+	std::cout << "PRIVATE: " << kp.priv().toHex() << std::endl;
+
+	return 0;
+}

+ 7 - 0
netconf-service/Makefile

@@ -0,0 +1,7 @@
+all:
+	gcc -O6 -c ../ext/lz4/lz4hc.c ../ext/lz4/lz4.c
+	g++ -DZT_OSNAME="linux" -DZT_ARCH="x86_64" -I/usr/include/mysql -I../ext/bin/libcrypto/include -O -pthread -o netconf.service netconf.cpp ../node/Utils.cpp ../node/Identity.cpp ../node/EllipticCurveKeyPair.cpp ../node/Salsa20.cpp ../node/HMAC.cpp lz4.o lz4hc.o ../ext/bin/libcrypto/linux-x86_64/libcrypto.a -lmysqlpp
+	g++ -DZT_OSNAME="linux" -DZT_ARCH="x86_64" -I/usr/include/mysql -I../ext/bin/libcrypto/include -O -pthread -o netconf-test netconf-test.cpp ../node/Utils.cpp ../node/Identity.cpp ../node/EllipticCurveKeyPair.cpp ../node/Salsa20.cpp ../node/HMAC.cpp ../node/Logger.cpp ../node/Service.cpp ../node/Thread.cpp lz4.o lz4hc.o ../ext/bin/libcrypto/linux-x86_64/libcrypto.a
+
+clean:
+	rm -f *.o netconf.service netconf-test

+ 83 - 0
netconf-service/netconf-test.cpp

@@ -0,0 +1,83 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013  ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+/* Self-tester that makes both new and repeated requests to netconf */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <vector>
+#include <string>
+#include <iostream>
+
+#include "../node/Dictionary.hpp"
+#include "../node/Service.hpp"
+#include "../node/Identity.hpp"
+#include "../node/RuntimeEnvironment.hpp"
+#include "../node/Logger.hpp"
+#include "../node/Thread.hpp"
+
+using namespace ZeroTier;
+
+static void svcHandler(void *arg,Service &svc,const Dictionary &msg)
+{
+	std::cout << msg.toString();
+}
+
+int main(int argc,char **argv)
+{
+	RuntimeEnvironment renv;
+	renv.log = new Logger((const char *)0,(const char *)0,0);
+	Service svc(&renv,"netconf","./netconf.service",&svcHandler,(void *)0);
+
+	srand(time(0));
+
+	std::vector<Identity> population;
+	for(;;) {
+		Identity id;
+		if ((population.empty())||(rand() < (RAND_MAX / 4))) {
+			id.generate();
+			population.push_back(id);
+			std::cout << "Testing with new identity: " << id.address().toString() << std::endl;
+		} else {
+			id = population[rand() % population.size()];
+			Thread::sleep(1000);
+			std::cout << "Testing with existing identity: " << id.address().toString() << std::endl;
+		}
+
+		Dictionary request;
+		request["type"] = "netconf-request";
+		request["peerId"] = id.toString(false);
+		request["nwid"] = "6c92786fee000001";
+		request["requestId"] = "12345";
+
+		svc.send(request);
+	}
+}

+ 345 - 0
netconf-service/netconf.cpp

@@ -0,0 +1,345 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013  ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+/*
+ * This is the netconf service. It's currently used only by netconf nodes that
+ * are run by ZeroTier itself. There is nothing to prevent you from running
+ * your own if you wanted to create your own networks outside our system.
+ *
+ * That being said, we'd like to charge for private networks to support
+ * ZeroTier One and future development efforts. So while this software is
+ * open source and we're not going to stop you from sidestepping this, we
+ * do ask -- honor system here -- that you pay for private networks if you
+ * are going to use them for any commercial purpose such as a business VPN
+ * alternative.
+ *
+ * This will at the moment only build on Linux and requires the mysql++
+ * library, which is available here:
+ *
+ * http://tangentsoft.net/mysql++/
+ *
+ * (Packages are available for CentOS via EPEL and for any Debian distro.)
+ *
+ * This program must be built and installed in the services.d subfolder of
+ * the ZeroTier One home folder of the node designated to act as a master
+ * for networks. Doing so will enable the NETWORK_CONFIG_REQUEST protocol
+ * verb.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#include <iostream>
+#include <string>
+#include <map>
+#include <list>
+#include <vector>
+#include <algorithm>
+
+#include <mysql++/mysql++.h>
+
+#include "../node/Dictionary.hpp"
+#include "../node/Identity.hpp"
+#include "../node/Utils.hpp"
+#include "../node/Mutex.hpp"
+
+using namespace ZeroTier;
+using namespace mysqlpp;
+
+static Mutex stdoutWriteLock;
+static Connection *dbCon = (Connection *)0;
+static char mysqlHost[64],mysqlPort[64],mysqlDatabase[64],mysqlUser[64],mysqlPassword[64];
+
+static void connectOrReconnect()
+{
+	for(;;) {
+		delete dbCon;
+		try {
+			dbCon = new Connection(mysqlDatabase,mysqlHost,mysqlUser,mysqlPassword,(unsigned int)strtol(mysqlPort,(char **)0,10));
+			if (dbCon->connected()) {
+				fprintf(stderr,"(re?)-connected to mysql server successfully\n");
+				break;
+			} else {
+				fprintf(stderr,"unable to connect to database server (connection closed), trying again in 1s...\n");
+				usleep(1000000);
+			}
+		} catch (std::exception &exc) {
+			fprintf(stderr,"unable to connect to database server (%s), trying again in 1s...\n",exc.what());
+			usleep(1000000);
+		} catch ( ... ) {
+			fprintf(stderr,"unable to connect to database server (unknown exception), trying again in 1s...\n");
+			usleep(1000000);
+		}
+	}
+}
+
+int main(int argc,char **argv)
+{
+	{
+		char *ee = getenv("ZT_NETCONF_MYSQL_HOST");
+		if (!ee) {
+			fprintf(stderr,"missing environment variable: ZT_NETCONF_MYSQL_HOST\n");
+			return -1;
+		}
+		strcpy(mysqlHost,ee);
+		ee = getenv("ZT_NETCONF_MYSQL_PORT");
+		if (!ee)
+			strcpy(mysqlPort,"3306");
+		else strcpy(mysqlPort,ee);
+		ee = getenv("ZT_NETCONF_MYSQL_DATABASE");
+		if (!ee) {
+			fprintf(stderr,"missing environment variable: ZT_NETCONF_MYSQL_DATABASE\n");
+			return -1;
+		}
+		strcpy(mysqlDatabase,ee);
+		ee = getenv("ZT_NETCONF_MYSQL_USER");
+		if (!ee) {
+			fprintf(stderr,"missing environment variable: ZT_NETCONF_MYSQL_USER\n");
+			return -1;
+		}
+		strcpy(mysqlUser,ee);
+		ee = getenv("ZT_NETCONF_MYSQL_PASSWORD");
+		if (!ee) {
+			fprintf(stderr,"missing environment variable: ZT_NETCONF_MYSQL_PASSWORD\n");
+			return -1;
+		}
+		strcpy(mysqlPassword,ee);
+	}
+
+	char buf[131072];
+	std::string dictBuf;
+
+	connectOrReconnect();
+	for(;;) {
+		for(int l=0;l<4;) {
+			int n = (int)read(STDIN_FILENO,buf + l,4 - l);
+			if (n < 0) {
+				fprintf(stderr,"error reading frame size from stdin: %s\n",strerror(errno));
+				return -1;
+			}
+			l += n;
+		}
+		unsigned int fsize = (unsigned int)ntohl(*((const uint32_t *)buf));
+
+		while (dictBuf.length() < fsize) {
+			int n = (int)read(STDIN_FILENO,buf,std::min((int)sizeof(buf),(int)(fsize - dictBuf.length())));
+			if (n < 0) {
+				fprintf(stderr,"error reading frame from stdin: %s\n",strerror(errno));
+				return -1;
+			}
+			for(int i=0;i<n;++i)
+				dictBuf.push_back(buf[i]);
+		}
+		Dictionary request(dictBuf);
+		dictBuf = "";
+
+		if (!dbCon->connected())
+			connectOrReconnect();
+
+		try {
+			const std::string &reqType = request.get("type");
+			if (reqType == "netconf-request") { // NETWORK_CONFIG_REQUEST packet
+				Identity peerIdentity(request.get("peerId"));
+				uint64_t nwid = strtoull(request.get("nwid").c_str(),(char **)0,16);
+				Dictionary meta;
+				if (request.contains("meta"))
+					meta.fromString(request.get("meta"));
+
+				// Do quick signature check / sanity check
+				if (!peerIdentity.locallyValidate(false)) {
+					fprintf(stderr,"identity failed signature check: %s\n",peerIdentity.toString(false).c_str());
+					continue;
+				}
+
+				// Save identity if unknown
+				{
+					Query q = dbCon->query();
+					q << "SELECT identity,identityValidated FROM Node WHERE id = " << peerIdentity.address().toInt();
+					StoreQueryResult rs = q.store();
+					if (rs.num_rows() > 0) {
+						if (rs[0]["identity"] != peerIdentity.toString(false)) {
+							// TODO: handle collisions...
+							continue;
+						} else if ((int)rs[0]["identityValidated"] == 0) {
+							// TODO: launch background validation
+						}
+					} else {
+						q = dbCon->query();
+						q << "INSERT INTO Node (id,creationTime,lastSeen,identity) VALUES (" << peerIdentity.address().toInt() << "," << Utils::now() << ",0," << quote << peerIdentity.toString(false) << ")";
+						if (!q.exec()) {
+							fprintf(stderr,"error inserting Node row for peer %s, aborting netconf request\n",peerIdentity.address().toString().c_str());
+							continue;
+						}
+						// TODO: launch background validation
+					}
+				}
+
+				// Update lastSeen
+				{
+					Query q = dbCon->query();
+					q << "UPDATE Node SET lastSeen = " << Utils::now() << " WHERE id = " << peerIdentity.address().toInt();
+					q.exec();
+				}
+
+				bool isOpen = false;
+				{
+					Query q = dbCon->query();
+					q << "SELECT isOpen FROM Network WHERE id = " << nwid;
+					StoreQueryResult rs = q.store();
+					if (rs.num_rows() > 0)
+						isOpen = ((int)rs[0]["isOpen"] > 0);
+					else {
+						Dictionary response;
+						response["peer"] = peerIdentity.address().toString();
+						response["nwid"] = request.get("nwid");
+						response["type"] = "netconf-response";
+						response["requestId"] = request.get("requestId");
+						response["error"] = "NOT_FOUND";
+						std::string respm = response.toString();
+						uint32_t respml = (uint32_t)htonl((uint32_t)respm.length());
+
+						stdoutWriteLock.lock();
+						write(STDOUT_FILENO,&respml,4);
+						write(STDOUT_FILENO,respm.data(),respm.length());
+						stdoutWriteLock.unlock();
+						continue;
+					}
+				}
+
+				Dictionary netconf;
+
+				netconf["peer"] = peerIdentity.address().toString();
+				sprintf(buf,"%.16llx",(unsigned long long)nwid);
+				netconf["nwid"] = buf;
+				netconf["isOpen"] = (isOpen ? "1" : "0");
+
+				if (!isOpen) {
+					// TODO: handle closed networks, look up private membership,
+					// generate signed cert.
+				}
+
+				std::string ipv4Static,ipv6Static;
+
+				{
+					// Check for IPv4 static assignments
+					Query q = dbCon->query();
+					q << "SELECT INET_NTOA(ip) AS ip,netmaskBits FROM IPv4Static WHERE Node_id = " << peerIdentity.address().toInt() << " AND Network_id = " << nwid;
+					StoreQueryResult rs = q.store();
+					if (rs.num_rows() > 0) {
+						for(int i=0;i<rs.num_rows();++i) {
+							if (ipv4Static.length())
+								ipv4Static.push_back(',');
+							ipv4Static.append(rs[i]["ip"].c_str());
+							ipv4Static.push_back('/');
+							ipv4Static.append(rs[i]["netmaskBits"].c_str());
+						}
+					}
+
+					// Try to auto-assign if there's any auto-assign networks with space
+					// available.
+					if (!ipv4Static.length()) {
+						unsigned char addressBytes[5];
+						peerIdentity.address().copyTo(addressBytes,5);
+
+						q = dbCon->query();
+						q << "SELECT ipNet,netmaskBits FROM IPv4AutoAssign WHERE Network_id = " << nwid;
+						rs = q.store();
+						if (rs.num_rows() > 0) {
+							for(int aaRow=0;aaRow<rs.num_rows();++aaRow) {
+								uint32_t ipNet = (uint32_t)((unsigned long)rs[aaRow]["ipNet"]);
+								unsigned int netmaskBits = (unsigned int)rs[aaRow]["netmaskBits"];
+
+								uint32_t tryIp = (((uint32_t)addressBytes[1]) << 24) |
+								                 (((uint32_t)addressBytes[2]) << 16) |
+								                 (((uint32_t)addressBytes[3]) << 8) |
+								                 ((((uint32_t)addressBytes[4]) % 254) + 1);
+								tryIp &= (0xffffffff >> netmaskBits);
+								tryIp |= ipNet;
+
+								for(int k=0;k<100000;++k) {
+									Query q2 = dbCon->query();
+									q2 << "INSERT INTO IPv4Static (Network_id,Node_id,ip,netmaskBits) VALUES (" << nwid << "," << peerIdentity.address().toInt() << "," << tryIp << "," << netmaskBits << ")";
+									if (q2.exec()) {
+										sprintf(buf,"%u.%u.%u.%u",(unsigned int)((tryIp >> 24) & 0xff),(unsigned int)((tryIp >> 16) & 0xff),(unsigned int)((tryIp >> 8) & 0xff),(unsigned int)(tryIp & 0xff));
+										if (ipv4Static.length())
+											ipv4Static.push_back(',');
+										ipv4Static.append(buf);
+										ipv4Static.push_back('/');
+										sprintf(buf,"%u",netmaskBits);
+										ipv4Static.append(buf);
+										break;
+									} else { // insert will fail if IP is in use due to uniqueness constraints in DB
+										++tryIp;
+										if ((tryIp & 0xff) == 0)
+											tryIp |= 1;
+										tryIp &= (0xffffffff >> netmaskBits);
+										tryIp |= ipNet;
+									}
+								}
+
+								if (ipv4Static.length())
+									break;
+							}
+						}
+					}
+				}
+
+				if (ipv4Static.length())
+					netconf["ipv4Static"] = ipv4Static;
+				if (ipv6Static.length())
+					netconf["ipv6Static"] = ipv6Static;
+
+				{
+					Dictionary response;
+					response["peer"] = peerIdentity.address().toString();
+					response["nwid"] = request.get("nwid");
+					response["type"] = "netconf-response";
+					response["requestId"] = request.get("requestId");
+					response["netconf"] = netconf.toString();
+					std::string respm = response.toString();
+					uint32_t respml = (uint32_t)htonl((uint32_t)respm.length());
+
+					stdoutWriteLock.lock();
+					write(STDOUT_FILENO,&respml,4);
+					write(STDOUT_FILENO,respm.data(),respm.length());
+					stdoutWriteLock.unlock();
+				}
+			}
+		} catch (std::exception &exc) {
+			fprintf(stderr,"unexpected exception handling message: %s\n",exc.what());
+		} catch ( ... ) {
+			fprintf(stderr,"unexpected exception handling message: unknown exception\n");
+		}
+	}
+}

+ 132 - 54
node/Address.hpp

@@ -28,72 +28,156 @@
 #ifndef _ZT_ADDRESS_HPP
 #define _ZT_ADDRESS_HPP
 
+#include <stdio.h>
+#include <stdlib.h>
 #include <stdint.h>
+#include <string.h>
 #include <string>
 #include "Utils.hpp"
 #include "MAC.hpp"
 #include "Constants.hpp"
+#include "Buffer.hpp"
 
 namespace ZeroTier {
 
 /**
- * ZeroTier address, which doubles as the last 5 octets of the MAC on taps
- *
- * Natural sort order will differ on big vs. little endian machines, but that
- * won't matter when it's used as a local map/set key.
+ * A ZeroTier address
  */
 class Address
 {
-private:
-	union {
-		unsigned char o[ZT_ADDRESS_LENGTH];
-		uint64_t v;
-	} _a;
-
 public:
 	Address()
-		throw()
+		throw() :
+		_a(0)
 	{
-		_a.v = 0;
 	}
 
 	Address(const Address &a)
+		throw() :
+		_a(a._a)
+	{
+	}
+
+	Address(uint64_t a)
+		throw() :
+		_a(a & 0xffffffffffULL)
+	{
+	}
+
+	Address(const char *s)
 		throw()
 	{
-		_a.v = a._a.v;
+		unsigned char foo[ZT_ADDRESS_LENGTH];
+		setTo(foo,Utils::unhex(s,foo,ZT_ADDRESS_LENGTH));
 	}
 
-	/**
-	 * Create from a ZeroTier MAC
-	 *
-	 * @param m MAC (assumed to be a ZeroTier MAC)
-	 */
-	Address(const MAC &m)
+	Address(const std::string &s)
 		throw()
 	{
-		_a.v = 0;
-		for(int i=0;i<ZT_ADDRESS_LENGTH;++i)
-			_a.o[i] = m.data[i + 1];
+		unsigned char foo[ZT_ADDRESS_LENGTH];
+		setTo(foo,Utils::unhex(s.c_str(),foo,ZT_ADDRESS_LENGTH));
 	}
 
 	/**
-	 * @param bits Raw address -- 5 bytes in length
+	 * @param bits Raw address -- 5 bytes, big-endian byte order
+	 * @param len Length of array
 	 */
-	Address(const void *bits)
+	Address(const void *bits,unsigned int len)
 		throw()
 	{
-		_a.v = 0;
-		for(int i=0;i<ZT_ADDRESS_LENGTH;++i)
-			_a.o[i] = ((const unsigned char *)bits)[i];
+		setTo(bits,len);
 	}
 
 	inline Address &operator=(const Address &a)
 		throw()
 	{
-		_a.v = a._a.v;
+		_a = a._a;
 		return *this;
 	}
 
+	inline Address &operator=(const uint64_t a)
+		throw()
+	{
+		_a = (a & 0xffffffffffULL);
+		return *this;
+	}
+
+	/**
+	 * @param bits Raw address -- 5 bytes, big-endian byte order
+	 * @param len Length of array
+	 */
+	inline void setTo(const void *bits,unsigned int len)
+		throw()
+	{
+		if (len < ZT_ADDRESS_LENGTH) {
+			_a = 0;
+			return;
+		}
+		const unsigned char *b = (const unsigned char *)bits;
+		uint64_t a = ((uint64_t)*b++) << 32;
+		a |= ((uint64_t)*b++) << 24;
+		a |= ((uint64_t)*b++) << 16;
+		a |= ((uint64_t)*b++) << 8;
+		a |= ((uint64_t)*b);
+		_a = a;
+	}
+
+	/**
+	 * @param bits Buffer to hold 5-byte address in big-endian byte order
+	 * @param len Length of array
+	 */
+	inline void copyTo(void *bits,unsigned int len) const
+		throw()
+	{
+		if (len < ZT_ADDRESS_LENGTH)
+			return;
+		unsigned char *b = (unsigned char *)bits;
+		*(b++) = (unsigned char)((_a >> 32) & 0xff);
+		*(b++) = (unsigned char)((_a >> 24) & 0xff);
+		*(b++) = (unsigned char)((_a >> 16) & 0xff);
+		*(b++) = (unsigned char)((_a >> 8) & 0xff);
+		*b = (unsigned char)(_a & 0xff);
+	}
+
+	/**
+	 * Append to a buffer in big-endian byte order
+	 *
+	 * @param b Buffer to append to
+	 */
+	template<unsigned int C>
+	inline void appendTo(Buffer<C> &b) const
+		throw(std::out_of_range)
+	{
+		b.append((unsigned char)((_a >> 32) & 0xff));
+		b.append((unsigned char)((_a >> 24) & 0xff));
+		b.append((unsigned char)((_a >> 16) & 0xff));
+		b.append((unsigned char)((_a >> 8) & 0xff));
+		b.append((unsigned char)(_a & 0xff));
+	}
+
+	/**
+	 * @return String containing address as 5 binary bytes
+	 */
+	inline std::string toBinaryString() const
+	{
+		std::string b;
+		b.push_back((char)((_a >> 32) & 0xff));
+		b.push_back((char)((_a >> 24) & 0xff));
+		b.push_back((char)((_a >> 16) & 0xff));
+		b.push_back((char)((_a >> 8) & 0xff));
+		b.push_back((char)(_a & 0xff));
+		return b;
+	}
+
+	/**
+	 * @return Integer containing address (0 to 2^40)
+	 */
+	inline uint64_t toInt() const
+		throw()
+	{
+		return _a;
+	}
+
 	/**
 	 * Derive a MAC whose first octet is the ZeroTier LAN standard
 	 * 
@@ -104,8 +188,7 @@ public:
 	{
 		MAC m;
 		m.data[0] = ZT_MAC_FIRST_OCTET;
-		for(int i=1;i<6;++i)
-			m.data[i] = _a.o[i - 1];
+		copyTo(m.data + 1,ZT_ADDRESS_LENGTH);
 		return m;
 	}
 
@@ -114,18 +197,15 @@ public:
 	 */
 	inline std::string toString() const
 	{
-		return Utils::hex(_a.o,ZT_ADDRESS_LENGTH);
+		char buf[16];
+		sprintf(buf,"%.10llx",(unsigned long long)_a);
+		return std::string(buf);
 	};
 
-	/**
-	 * Set address to zero
-	 */
-	inline void zero() throw() { _a.v = 0; }
-
 	/**
 	 * @return True if this address is not zero
 	 */
-	inline operator bool() const throw() { return (_a.v); }
+	inline operator bool() const throw() { return (_a); }
 
 	/**
 	 * @return Sum of all bytes in address
@@ -133,10 +213,7 @@ public:
 	inline unsigned int sum() const
 		throw()
 	{
-		unsigned int s = 0;
-		for(unsigned int i=0;i<ZT_ADDRESS_LENGTH;++i)
-			s += _a.o[i];
-		return s;
+		return (unsigned int)(((_a >> 32) & 0xff) + ((_a >> 24) & 0xff) + ((_a >> 16) & 0xff) + ((_a >> 8) & 0xff) + (_a & 0xff));
 	}
 
 	/**
@@ -151,23 +228,24 @@ public:
 	inline bool isReserved() const
 		throw()
 	{
-		return ((!_a.v)||(_a.o[0] == ZT_ADDRESS_RESERVED_PREFIX));
+		return ((!_a)||((_a >> 32) == ZT_ADDRESS_RESERVED_PREFIX));
 	}
 
-	inline unsigned char *data() throw() { return _a.o; }
-	inline const unsigned char *data() const throw() { return _a.o; }
-
-	inline unsigned int size() const throw() { return ZT_ADDRESS_LENGTH; }
+	/**
+	 * @param i Value from 0 to 4 (inclusive)
+	 * @return Byte at said position (address interpreted in big-endian order)
+	 */
+	inline unsigned char operator[](unsigned int i) const throw() { return (unsigned char)((_a >> (32 - (i * 8))) & 0xff); }
 
-	inline unsigned char &operator[](unsigned int i) throw() { return _a.o[i]; }
-	inline unsigned char operator[](unsigned int i) const throw() { return _a.o[i]; }
+	inline bool operator==(const Address &a) const throw() { return (_a == a._a); }
+	inline bool operator!=(const Address &a) const throw() { return (_a != a._a); }
+	inline bool operator>(const Address &a) const throw() { return (_a > a._a); }
+	inline bool operator<(const Address &a) const throw() { return (_a < a._a); }
+	inline bool operator>=(const Address &a) const throw() { return (_a >= a._a); }
+	inline bool operator<=(const Address &a) const throw() { return (_a <= a._a); }
 
-	inline bool operator==(const Address &a) const throw() { return (_a.v == a._a.v); }
-	inline bool operator!=(const Address &a) const throw() { return (_a.v != a._a.v); }
-	inline bool operator<(const Address &a) const throw() { return (_a.v < a._a.v); }
-	inline bool operator>(const Address &a) const throw() { return (_a.v > a._a.v); }
-	inline bool operator<=(const Address &a) const throw() { return (_a.v <= a._a.v); }
-	inline bool operator>=(const Address &a) const throw() { return (_a.v >= a._a.v); }
+private:
+	uint64_t _a;
 };
 
 } // namespace ZeroTier

+ 0 - 94
node/BlobArray.hpp

@@ -1,94 +0,0 @@
-/*
- * ZeroTier One - Global Peer to Peer Ethernet
- * Copyright (C) 2012-2013  ZeroTier Networks LLC
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-#ifndef _ZT_BLOBARRAY_HPP
-#define _ZT_BLOBARRAY_HPP
-
-#include <vector>
-#include <string>
-#include <algorithm>
-
-namespace ZeroTier {
-
-/**
- * A vector of binary strings serializable in a packed format
- *
- * The format uses variable-length integers to indicate the length of each
- * field. Each byte of the length has another byte with seven more significant
- * bits if its 8th bit is set. Fields can be up to 2^28 in length.
- */
-class BlobArray : public std::vector<std::string>
-{
-public:
-	inline std::string serialize() const
-	{
-		std::string r;
-		for(BlobArray::const_iterator i=begin();i!=end();++i) {
-			unsigned int flen = (unsigned int)i->length();
-			do {
-				unsigned char flenb = (unsigned char)(flen & 0x7f);
-				flen >>= 7;
-				flenb |= (flen) ? 0x80 : 0;
-				r.push_back((char)flenb);
-			} while (flen);
-			r.append(*i);
-		}
-		return r;
-	}
-
-	/**
-	 * Deserialize, replacing the current contents of this array
-	 *
-	 * @param data Serialized binary data
-	 * @param len Length of serialized data
-	 */
-	inline void deserialize(const void *data,unsigned int len)
-	{
-		clear();
-		for(unsigned int i=0;i<len;) {
-			unsigned int flen = 0;
-			unsigned int chunk = 0;
-			while (i < len) {
-				flen |= ((unsigned int)(((const unsigned char *)data)[i] & 0x7f)) << (7 * chunk++);
-				if (!(((const unsigned char *)data)[i++] & 0x80))
-					break;
-			}
-			flen = std::min(flen,len - i);
-			push_back(std::string(((const char *)data) + i,flen));
-			i += flen;
-		}
-	}
-	inline void deserialize(const std::string &data)
-	{
-		deserialize(data.data(),(unsigned int)data.length());
-	}
-};
-
-} // namespace ZeroTier
-
-#endif
-

+ 9 - 38
node/Constants.hpp

@@ -117,6 +117,11 @@ error_no_ZT_ARCH_defined;
  */
 #define ZT_DEFAULT_UDP_PORT 8993
 
+/**
+ * Local control port, also used for multiple invocation check
+ */
+#define ZT_CONTROL_UDP_PORT 39393
+
 /**
  * Default payload MTU for UDP packets
  *
@@ -151,13 +156,6 @@ error_no_ZT_ARCH_defined;
  */
 #define ZT_IF_MTU 2800
 
-/**
- * Maximum number of networks we can be a member of
- *
- * This is a safe value that's within the tap device limit on all known OSes.
- */
-#define ZT_MAX_NETWORK_MEMBERSHIPS 16
-
 /**
  * Maximum number of packet fragments we'll support
  * 
@@ -185,9 +183,9 @@ error_no_ZT_ARCH_defined;
 #define ZT_MAC_FIRST_OCTET 0x32
 
 /**
- * How often Topology::clean() is called in ms
+ * How often Topology::clean() and Network::clean() are called in ms
  */
-#define ZT_TOPOLOGY_CLEAN_PERIOD 300000
+#define ZT_DB_CLEAN_PERIOD 300000
 
 /**
  * Delay between WHOIS retries in ms
@@ -233,20 +231,15 @@ error_no_ZT_ARCH_defined;
 #define ZT_MULTICAST_PROPAGATION_DEPTH 7
 
 /**
- * Length of circular ring buffer history of multicast packets
+ * Length of ring buffer history of recent multicast packets
  */
 #define ZT_MULTICAST_DEDUP_HISTORY_LENGTH 1024
 
 /**
- * Expiration time in ms for multicast history items
+ * Expiration time in ms for multicast deduplication history items
  */
 #define ZT_MULTICAST_DEDUP_HISTORY_EXPIRE 4000
 
-/**
- * Number of bits to randomly "decay" in bloom filter per hop
- */
-#define ZT_MULTICAST_BLOOM_FILTER_DECAY_RATE 2
-
 /**
  * Period between announcements of all multicast 'likes' in ms
  *
@@ -281,23 +274,6 @@ error_no_ZT_ARCH_defined;
  */
 #define ZT_PEER_DIRECT_PING_DELAY 120000
 
-/**
- * Period between rechecks of autoconfigure URL
- *
- * This is in the absence of an external message ordering a recheck.
- */
-#define ZT_AUTOCONFIGURE_INTERVAL 3600000
-
-/**
- * Period between autoconfigure attempts if no successful autoconfig
- */
-#define ZT_AUTOCONFIGURE_CHECK_DELAY 15000
-
-/**
- * Delay between updates of status file in home directory
- */
-#define ZT_STATUS_OUTPUT_PERIOD 120000
-
 /**
  * Minimum delay in Node service loop
  * 
@@ -348,9 +324,4 @@ error_no_ZT_ARCH_defined;
  */
 #define ZT_RENDEZVOUS_NAT_T_DELAY 500
 
-/**
- * Generate a new ownership verify secret on launch if older than this
- */
-#define ZT_OVS_GENERATE_NEW_IF_OLDER_THAN 86400000
-
 #endif

+ 1 - 3
node/Defaults.cpp

@@ -68,9 +68,7 @@ static inline std::map< Identity,std::vector<InetAddress> > _mkSupernodeMap()
 
 Defaults::Defaults()
 	throw(std::runtime_error) :
-	supernodes(_mkSupernodeMap()),
-	configUrlPrefix("http://api.zerotier.com/one/nc/"),
-	configAuthority("f9f34184ac:1:AwGgrWjb8dARXzruqxiy1+Qf+gz4iM5IMfQTCWrJXkwERdvbvxTPZvtIyitw4gS90TGIxW+e7uJxweg9Vyq5lZJBrg==:QeEQLm9ymLC3EcnIw2OUqufUwb2wgHSAg6wQOXKyhT779p/8Hz5485PZLJCbr/aVHjwzop8APJk9B45Zm0Mb/LEhQTBMH2jvc7qqoYnMCNCO9jpADeMJwMW5e1VFgIObWl9uNjhRbf5/m8dZcn0pKKGwjSoP1QTeVWOC8GkZhE25bUWj")
+	supernodes(_mkSupernodeMap())
 {
 }
 

+ 0 - 10
node/Defaults.hpp

@@ -55,16 +55,6 @@ public:
 	 * Supernodes on the ZeroTier network
 	 */
 	const std::map< Identity,std::vector<InetAddress> > supernodes;
-
-	/**
-	 * URL prefix for autoconfiguration
-	 */
-	const std::string configUrlPrefix;
-
-	/**
-	 * Identity used to encrypt and authenticate configuration from URL
-	 */
-	const std::string configAuthority;
 };
 
 extern const Defaults ZT_DEFAULTS;

+ 2 - 2
node/Demarc.cpp

@@ -100,7 +100,7 @@ bool Demarc::bindLocalUdp(unsigned int localPort)
 		DemarcPortObj *v4r = &(_ports[(Port)v4p]);
 		v4r->port = (Port)v4p;
 		v4r->parent = this;
-		v4r->obj = v4 = new UdpSocket(localPort,false,&Demarc::_CBudpSocketPacketHandler,v4r);
+		v4r->obj = v4 = new UdpSocket(false,localPort,false,&Demarc::_CBudpSocketPacketHandler,v4r);
 		v4r->type = PORT_TYPE_UDP_SOCKET_V4;
 	} catch ( ... ) {
 		_ports.erase((Port)v4p);
@@ -112,7 +112,7 @@ bool Demarc::bindLocalUdp(unsigned int localPort)
 		DemarcPortObj *v6r = &(_ports[(Port)v6p]);
 		v6r->port = (Port)v6p;
 		v6r->parent = this;
-		v6r->obj = v6 = new UdpSocket(localPort,true,&Demarc::_CBudpSocketPacketHandler,v6r);
+		v6r->obj = v6 = new UdpSocket(false,localPort,true,&Demarc::_CBudpSocketPacketHandler,v6r);
 		v6r->type = PORT_TYPE_UDP_SOCKET_V6;
 	} catch ( ... ) {
 		_ports.erase((Port)v6p);

+ 3 - 0
node/Demarc.hpp

@@ -49,6 +49,9 @@ class UdpSocket;
  * about what they actually are.
  *
  * All ports are closed when this class is destroyed.
+ *
+ * Its name "demarcation point" comes from the telco/cable terminology for
+ * the box where wires terminate at a customer's property.
  */
 class Demarc
 {

+ 207 - 0
node/Dictionary.hpp

@@ -0,0 +1,207 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013  ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_DICTIONARY_HPP
+#define _ZT_DICTIONARY_HPP
+
+#include <string>
+#include <map>
+#include <stdexcept>
+#include "Constants.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Simple key/value dictionary with string serialization
+ *
+ * The serialization format is a flat key=value with backslash escape.
+ * It does not support comments or other syntactic complexities. It is
+ * human-readable if the keys and values in the dictionary are also
+ * human-readable. Otherwise it might contain unprintable characters.
+ */
+class Dictionary : public std::map<std::string,std::string>
+{
+public:
+	Dictionary()
+	{
+	}
+
+	/**
+	 * @param s String-serialized dictionary
+	 */
+	Dictionary(const char *s)
+	{
+		fromString(s);
+	}
+
+	/**
+	 * @param s String-serialized dictionary
+	 */
+	Dictionary(const std::string &s)
+	{
+		fromString(s.c_str());
+	}
+
+	/**
+	 * Get a key, throwing an exception if it is not present
+	 *
+	 * @param key Key to look up
+	 * @return Reference to value
+	 * @throws std::invalid_argument Key not found
+	 */
+	inline const std::string &get(const std::string &key) const
+		throw(std::invalid_argument)
+	{
+		const_iterator e(find(key));
+		if (e == end())
+			throw std::invalid_argument(std::string("missing required field: ")+key);
+		return e->second;
+	}
+
+	/**
+	 * Get a key, returning a default if not present
+	 *
+	 * @param key Key to look up
+	 * @param dfl Default if not present
+	 * @return Value or default
+	 */
+	inline const std::string &get(const std::string &key,const std::string &dfl) const
+	{
+		const_iterator e(find(key));
+		if (e == end())
+			return dfl;
+		return e->second;
+	}
+
+	/**
+	 * @param key Key to check
+	 * @return True if dictionary contains key
+	 */
+	inline bool contains(const std::string &key) const
+	{
+		return (find(key) != end());
+	}
+
+	/**
+	 * @return String-serialized dictionary
+	 */
+	inline std::string toString() const
+	{
+		std::string s;
+
+		for(const_iterator kv(begin());kv!=end();++kv) {
+			_appendEsc(kv->first.data(),kv->first.length(),s);
+			s.push_back('=');
+			_appendEsc(kv->second.data(),kv->second.length(),s);
+			s.append(ZT_EOL_S);
+		}
+
+		return s;
+	}
+
+	/**
+	 * Clear and initialize from a string
+	 *
+	 * @param s String-serialized dictionary
+	 */
+	inline void fromString(const char *s)
+	{
+		clear();
+		bool escapeState = false;
+		std::string keyBuf;
+		std::string *element = &keyBuf;
+		while (*s) {
+			if (escapeState) {
+				escapeState = false;
+				switch(*s) {
+					case '0':
+						element->push_back((char)0);
+						break;
+					case 'r':
+						element->push_back('\r');
+						break;
+					case 'n':
+						element->push_back('\n');
+						break;
+					default:
+						element->push_back(*s);
+						break;
+				}
+			} else {
+				if (*s == '\\') {
+					escapeState = true;
+				} else if (*s == '=') {
+					if (element == &keyBuf)
+						element = &((*this)[keyBuf]);
+				} else if ((*s == '\r')||(*s == '\n')) {
+					if ((element == &keyBuf)&&(keyBuf.length() > 0))
+						(*this)[keyBuf];
+					keyBuf = "";
+					element = &keyBuf;
+				} else element->push_back(*s);
+			}
+			++s;
+		}
+		if ((element == &keyBuf)&&(keyBuf.length() > 0))
+			(*this)[keyBuf];
+	}
+	inline void fromString(const std::string &s)
+	{
+		fromString(s.c_str());
+	}
+
+private:
+	static inline void _appendEsc(const char *data,unsigned int len,std::string &to)
+	{
+		for(unsigned int i=0;i<len;++i) {
+			switch(data[i]) {
+				case 0:
+					to.append("\\0");
+					break;
+				case '\r':
+					to.append("\\r");
+					break;
+				case '\n':
+					to.append("\\n");
+					break;
+				case '\\':
+					to.append("\\\\");
+					break;
+				case '=':
+					to.append("\\=");
+					break;
+				default:
+					to.push_back(data[i]);
+					break;
+			}
+		}
+	}
+};
+
+} // namespace ZeroTier
+
+#endif

+ 8 - 4
node/EllipticCurveKey.hpp

@@ -65,15 +65,19 @@ public:
 		throw() :
 		_bytes(0)
 	{
+		memset(_key,0,sizeof(_key));
 	}
 
 	EllipticCurveKey(const void *data,unsigned int len)
 		throw()
 	{
-		if (len <= ZT_EC_MAX_BYTES) {
-			_bytes = len;
-			memcpy(_key,data,len);
-		} else _bytes = 0;
+		set(data,len);
+	}
+
+	EllipticCurveKey(const std::string &data)
+		throw()
+	{
+		set(data.data(),data.length());
 	}
 
 	EllipticCurveKey(const EllipticCurveKey &k)

+ 16 - 4
node/EllipticCurveKeyPair.cpp

@@ -55,7 +55,20 @@ public:
 };
 static _EC_Group ZT_EC_GROUP;
 
-/* Key derivation function */
+/**
+ * Key derivation function
+ *
+ * TODO:
+ * If/when we document the protocol, this will have to be documented as
+ * well. It's a fairly standard KDF that uses SHA-256 to transform the
+ * raw EC key. It's generally considered good crypto practice to do this
+ * to eliminate the possibility of leaking information from EC exchange to
+ * downstream algorithms.
+ *
+ * In our code it is used to produce a two 32-bit keys. One key is used
+ * for Salsa20 and the other for HMAC-SHA-256. They are generated together
+ * as a single 64-bit key.
+ */
 static void *_zt_EC_KDF(const void *in,size_t inlen,void *out,size_t *outlen)
 {
 	SHA256_CTX sha;
@@ -130,9 +143,8 @@ bool EllipticCurveKeyPair::generate()
 			fread(tmp,sizeof(tmp),1,rf);
 			fclose(rf);
 		} else {
-			fprintf(stderr,"WARNING: cannot open /dev/urandom\n");
-			for(unsigned int i=0;i<sizeof(tmp);++i)
-				tmp[i] = (unsigned char)(rand() >> 3);
+			fprintf(stderr,"FATAL: could not open /dev/urandom\n");
+			exit(-1);
 		}
 		RAND_seed(tmp,sizeof(tmp));
 #else

+ 2 - 0
node/EllipticCurveKeyPair.hpp

@@ -35,6 +35,8 @@ namespace ZeroTier {
 
 /**
  * An elliptic curve key pair supporting generation and key agreement
+ *
+ * This is basically OpenSSL libcrypto glue.
  */
 class EllipticCurveKeyPair
 {

+ 7 - 7
node/EthernetTap.cpp

@@ -218,7 +218,7 @@ EthernetTap::EthernetTap(
 		int kextpid;
 		char tmp[4096];
 		strcpy(tmp,_r->homePath.c_str());
-		if ((kextpid = (int)fork()) == 0) {
+		if ((kextpid = (int)vfork()) == 0) {
 			chdir(tmp);
 			execl(ZT_MAC_KEXTLOAD,ZT_MAC_KEXTLOAD,"-q","-repository",tmp,"tap.kext",(const char *)0);
 			exit(-1);
@@ -255,7 +255,7 @@ EthernetTap::EthernetTap(
 
 	// Configure MAC address and MTU, bring interface up
 	long cpid;
-	if ((cpid = (long)fork()) == 0) {
+	if ((cpid = (long)vfork()) == 0) {
 		execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,"lladdr",ethaddr,"mtu",mtustr,"up",(const char *)0);
 		exit(-1);
 	} else {
@@ -285,7 +285,7 @@ EthernetTap::~EthernetTap()
 #ifdef __APPLE__
 void EthernetTap::whack()
 {
-	long cpid = (long)fork();
+	long cpid = (long)vfork();
 	if (cpid == 0) {
 		execl(ZT_MAC_IPCONFIG,ZT_MAC_IPCONFIG,"set",_dev,"AUTOMATIC-V6",(const char *)0);
 		exit(-1);
@@ -304,7 +304,7 @@ void EthernetTap::whack() {}
 #ifdef __LINUX__
 static bool ___removeIp(const char *_dev,const InetAddress &ip)
 {
-	long cpid = (long)fork();
+	long cpid = (long)vfork();
 	if (cpid == 0) {
 		execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","del",ip.toString().c_str(),"dev",_dev,(const char *)0);
 		exit(1); /* not reached unless exec fails */
@@ -337,7 +337,7 @@ bool EthernetTap::addIP(const InetAddress &ip)
 	}
 
 	long cpid;
-	if ((cpid = (long)fork()) == 0) {
+	if ((cpid = (long)vfork()) == 0) {
 		execl(ZT_ETHERTAP_IP_COMMAND,ZT_ETHERTAP_IP_COMMAND,"addr","add",ip.toString().c_str(),"dev",_dev,(const char *)0);
 		exit(-1);
 	} else {
@@ -357,7 +357,7 @@ bool EthernetTap::addIP(const InetAddress &ip)
 static bool ___removeIp(const char *_dev,const InetAddress &ip)
 {
 	int cpid;
-	if ((cpid = (int)fork()) == 0) {
+	if ((cpid = (int)vfork()) == 0) {
 		execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
 		exit(-1);
 	} else {
@@ -390,7 +390,7 @@ bool EthernetTap::addIP(const InetAddress &ip)
 	}
 
 	int cpid;
-	if ((cpid = (int)fork()) == 0) {
+	if ((cpid = (int)vfork()) == 0) {
 		execl(ZT_ETHERTAP_IFCONFIG,ZT_ETHERTAP_IFCONFIG,_dev,ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
 		exit(-1);
 	} else {

+ 216 - 85
node/Filter.cpp

@@ -25,6 +25,9 @@
  * LLC. Start here: http://www.zerotier.com/
  */
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <stdint.h>
 
 #include "RuntimeEnvironment.hpp"
@@ -34,21 +37,20 @@
 
 namespace ZeroTier {
 
+const char *const Filter::UNKNOWN_NAME = "(unknown)";
+const Range<unsigned int> Filter::ANY;
+
 bool Filter::Rule::operator()(unsigned int etype,const void *data,unsigned int len) const
+	throw(std::invalid_argument)
 {
 	if ((!_etherType)||(_etherType(etype))) { // ethertype is ANY, or matches
 		// Ethertype determines meaning of protocol and port
 		switch(etype) {
-			default:
-				if ((!_protocol)&&(!_port))
-					return true; // match other ethertypes if protocol and port are ANY, since we don't know what to do with them
-				break;
-
 			case ZT_ETHERTYPE_IPV4:
 				if (len > 20) {
-					if ((!_protocol)||(_protocol(((const uint8_t *)data)[9]))) { // IP protocol
-						if (!_port)
-							return true; // protocol matches or is ANY, port is ANY
+					if ((!_protocol)||(_protocol(((const uint8_t *)data)[9]))) { // protocol is ANY or match
+						if (!_port) // port is ANY
+							return true;
 
 						// Don't match on fragments beyond fragment 0. If we've blocked
 						// fragment 0, further fragments will fall on deaf ears anyway.
@@ -60,22 +62,27 @@ bool Filter::Rule::operator()(unsigned int etype,const void *data,unsigned int l
 
 						switch(((const uint8_t *)data)[9]) { // port's meaning depends on IP protocol
 							case ZT_IPPROTO_ICMP:
-								return _port(((const uint8_t *)data)[ihl]); // port = ICMP type
+								// For ICMP, port is ICMP type
+								return _port(((const uint8_t *)data)[ihl]);
 							case ZT_IPPROTO_TCP:
 							case ZT_IPPROTO_UDP:
 							case ZT_IPPROTO_SCTP:
 							case ZT_IPPROTO_UDPLITE:
-								return _port(((const uint16_t *)data)[(ihl / 2) + 1]); // destination port
+								// For these, port is destination port. Protocol designers were
+								// nice enough to put the field in the same place.
+								return _port(((const uint16_t *)data)[(ihl / 2) + 1]);
+							default:
+								// port has no meaning for other IP types, so ignore it
+								return true;
 						}
 
 						return false; // no match on port
 					}
-				}
+				} else throw std::invalid_argument("undersized IPv4 packet");
 				break;
 
 			case ZT_ETHERTYPE_IPV6:
 				if (len > 40) {
-					// see: http://stackoverflow.com/questions/17518951/is-the-ipv6-header-really-this-nutty
 					int nextHeader = ((const uint8_t *)data)[6];
 					unsigned int pos = 40;
 					while ((pos < len)&&(nextHeader >= 0)&&(nextHeader != 59)) { // 59 == no next header
@@ -102,9 +109,11 @@ bool Filter::Rule::operator()(unsigned int etype,const void *data,unsigned int l
 							case ZT_IPPROTO_ESP: // ESP
 								return _protocol(ZT_IPPROTO_ESP); // true if ESP is matched protocol, otherwise false since packet will be IPsec
 							case ZT_IPPROTO_ICMPV6:
-								if (_protocol(ZT_IPPROTO_ICMPV6)) { // only match ICMPv6 if specified
+								// Only match ICMPv6 if we've selected it specifically
+								if (_protocol(ZT_IPPROTO_ICMPV6)) {
+									// Port is interpreted as ICMPv6 type
 									if ((!_port)||(_port(((const uint8_t *)data)[pos])))
-										return true; // protocol matches, port is ANY or matches ICMP type
+										return true;
 								}
 								break;
 							case ZT_IPPROTO_TCP:
@@ -118,25 +127,75 @@ bool Filter::Rule::operator()(unsigned int etype,const void *data,unsigned int l
 										return true; // protocol matches or is ANY, port is ANY or matches
 								}
 								break;
+							default: {
+								char foo[128];
+								sprintf(foo,"unrecognized IPv6 header type %d",(int)nextHeader);
+								throw std::invalid_argument(foo);
+							}
 						}
 
 						fprintf(stderr,"[rule] V6: end header parse, next header %.2x, new pos %d\n",nextHeader,pos);
 					}
-				}
+				} else throw std::invalid_argument("undersized IPv6 packet");
 				break;
+
+			default:
+				// For other ethertypes, protocol and port are ignored. What would they mean?
+				return true;
 		}
 	}
 
 	return false;
 }
 
-Filter::Filter(const RuntimeEnvironment *renv) :
-	_r(renv)
+std::string Filter::Rule::toString() const
 {
-}
+	char buf[128];
+	std::string s;
 
-Filter::~Filter()
-{
+	switch(_etherType.magnitude()) {
+		case 0:
+			s.push_back('*');
+			break;
+		case 1:
+			sprintf(buf,"%u",_etherType.start);
+			s.append(buf);
+			break;
+		default:
+			sprintf(buf,"%u-%u",_etherType.start,_etherType.end);
+			s.append(buf);
+			break;
+	}
+	s.push_back('/');
+	switch(_protocol.magnitude()) {
+		case 0:
+			s.push_back('*');
+			break;
+		case 1:
+			sprintf(buf,"%u",_protocol.start);
+			s.append(buf);
+			break;
+		default:
+			sprintf(buf,"%u-%u",_protocol.start,_protocol.end);
+			s.append(buf);
+			break;
+	}
+	s.push_back('/');
+	switch(_port.magnitude()) {
+		case 0:
+			s.push_back('*');
+			break;
+		case 1:
+			sprintf(buf,"%u",_port.start);
+			s.append(buf);
+			break;
+		default:
+			sprintf(buf,"%u-%u",_port.start,_port.end);
+			s.append(buf);
+			break;
+	}
+
+	return s;
 }
 
 void Filter::add(const Rule &r,const Action &a)
@@ -153,89 +212,161 @@ void Filter::add(const Rule &r,const Action &a)
 
 std::string Filter::toString(const char *sep) const
 {
-	char buf[256];
-
 	if (!sep)
 		sep = ",";
 
 	std::string s;
 
+	bool first = true;
 	Mutex::Lock _l(_chain_m);
 	for(std::vector<Entry>::const_iterator i(_chain.begin());i!=_chain.end();++i) {
-		bool first = (i == _chain.begin());
+		s.append(i->rule.toString());
+		if (first)
+			first = false;
+		else s.append(sep);
+	}
 
-		s.push_back('[');
+	return s;
+}
 
-		if (i->rule.etherType()) {
-			if (i->rule.etherType().magnitude() > 1)
-				sprintf(buf,"%u-%u",i->rule.etherType().start,i->rule.etherType().end);
-			else sprintf(buf,"%u",i->rule.etherType().start);
-			s.append(buf);
-		} else s.push_back('*');
+const char *Filter::etherTypeName(const unsigned int etherType)
+	throw()
+{
+	switch(etherType) {
+		case ZT_ETHERTYPE_IPV4:  return "ETHERTYPE_IPV4";
+		case ZT_ETHERTYPE_ARP:   return "ETHERTYPE_ARP";
+		case ZT_ETHERTYPE_RARP:  return "ETHERTYPE_RARP";
+		case ZT_ETHERTYPE_ATALK: return "ETHERTYPE_ATALK";
+		case ZT_ETHERTYPE_AARP:  return "ETHERTYPE_AARP";
+		case ZT_ETHERTYPE_IPX_A: return "ETHERTYPE_IPX_A";
+		case ZT_ETHERTYPE_IPX_B: return "ETHERTYPE_IPX_B";
+		case ZT_ETHERTYPE_IPV6:  return "ETHERTYPE_IPV6";
+	}
+	return UNKNOWN_NAME;
+}
+
+const char *Filter::ipProtocolName(const unsigned int ipp)
+	throw()
+{
+	switch(ipp) {
+		case ZT_IPPROTO_ICMP:    return "IPPROTO_ICMP";
+		case ZT_IPPROTO_IGMP:    return "IPPROTO_IGMP";
+		case ZT_IPPROTO_TCP:     return "IPPROTO_TCP";
+		case ZT_IPPROTO_UDP:     return "IPPROTO_UDP";
+		case ZT_IPPROTO_GRE:     return "IPPROTO_GRE";
+		case ZT_IPPROTO_ESP:     return "IPPROTO_ESP";
+		case ZT_IPPROTO_AH:      return "IPPROTO_AH";
+		case ZT_IPPROTO_ICMPV6:  return "IPPROTO_ICMPV6";
+		case ZT_IPPROTO_OSPF:    return "IPPROTO_OSPF";
+		case ZT_IPPROTO_IPIP:    return "IPPROTO_IPIP";
+		case ZT_IPPROTO_IPCOMP:  return "IPPROTO_IPCOMP";
+		case ZT_IPPROTO_L2TP:    return "IPPROTO_L2TP";
+		case ZT_IPPROTO_SCTP:    return "IPPROTO_SCTP";
+		case ZT_IPPROTO_FC:      return "IPPROTO_FC";
+		case ZT_IPPROTO_UDPLITE: return "IPPROTO_UDPLITE";
+		case ZT_IPPROTO_HIP:     return "IPPROTO_HIP";
+	}
+	return UNKNOWN_NAME;
+}
 
-		s.push_back(';');
+const char *Filter::icmpTypeName(const unsigned int icmpType)
+	throw()
+{
+	switch(icmpType) {
+		case ZT_ICMP_ECHO_REPLY:                  return "ICMP_ECHO_REPLY";
+		case ZT_ICMP_DESTINATION_UNREACHABLE:     return "ICMP_DESTINATION_UNREACHABLE";
+		case ZT_ICMP_SOURCE_QUENCH:               return "ICMP_SOURCE_QUENCH";
+		case ZT_ICMP_REDIRECT:                    return "ICMP_REDIRECT";
+		case ZT_ICMP_ALTERNATE_HOST_ADDRESS:      return "ICMP_ALTERNATE_HOST_ADDRESS";
+		case ZT_ICMP_ECHO_REQUEST:                return "ICMP_ECHO_REQUEST";
+		case ZT_ICMP_ROUTER_ADVERTISEMENT:        return "ICMP_ROUTER_ADVERTISEMENT";
+		case ZT_ICMP_ROUTER_SOLICITATION:         return "ICMP_ROUTER_SOLICITATION";
+		case ZT_ICMP_TIME_EXCEEDED:               return "ICMP_TIME_EXCEEDED";
+		case ZT_ICMP_BAD_IP_HEADER:               return "ICMP_BAD_IP_HEADER";
+		case ZT_ICMP_TIMESTAMP:                   return "ICMP_TIMESTAMP";
+		case ZT_ICMP_TIMESTAMP_REPLY:             return "ICMP_TIMESTAMP_REPLY";
+		case ZT_ICMP_INFORMATION_REQUEST:         return "ICMP_INFORMATION_REQUEST";
+		case ZT_ICMP_INFORMATION_REPLY:           return "ICMP_INFORMATION_REPLY";
+		case ZT_ICMP_ADDRESS_MASK_REQUEST:        return "ICMP_ADDRESS_MASK_REQUEST";
+		case ZT_ICMP_ADDRESS_MASK_REPLY:          return "ICMP_ADDRESS_MASK_REPLY";
+		case ZT_ICMP_TRACEROUTE:                  return "ICMP_TRACEROUTE";
+		case ZT_ICMP_MOBILE_HOST_REDIRECT:        return "ICMP_MOBILE_HOST_REDIRECT";
+		case ZT_ICMP_MOBILE_REGISTRATION_REQUEST: return "ICMP_MOBILE_REGISTRATION_REQUEST";
+		case ZT_ICMP_MOBILE_REGISTRATION_REPLY:   return "ICMP_MOBILE_REGISTRATION_REPLY";
+	}
+	return UNKNOWN_NAME;
+}
 
-		if (i->rule.protocol()) {
-			if (i->rule.protocol().magnitude() > 1)
-				sprintf(buf,"%u-%u",i->rule.protocol().start,i->rule.protocol().end);
-			else sprintf(buf,"%u",i->rule.protocol().start);
-			s.append(buf);
-		} else s.push_back('*');
+const char *Filter::icmp6TypeName(const unsigned int icmp6Type)
+	throw()
+{
+	switch(icmp6Type) {
+		case ZT_ICMP6_DESTINATION_UNREACHABLE:              return "ICMP6_DESTINATION_UNREACHABLE";
+		case ZT_ICMP6_PACKET_TOO_BIG:                       return "ICMP6_PACKET_TOO_BIG";
+		case ZT_ICMP6_TIME_EXCEEDED:                        return "ICMP6_TIME_EXCEEDED";
+		case ZT_ICMP6_PARAMETER_PROBLEM:                    return "ICMP6_PARAMETER_PROBLEM";
+		case ZT_ICMP6_ECHO_REQUEST:                         return "ICMP6_ECHO_REQUEST";
+		case ZT_ICMP6_ECHO_REPLY:                           return "ICMP6_ECHO_REPLY";
+		case ZT_ICMP6_MULTICAST_LISTENER_QUERY:             return "ICMP6_MULTICAST_LISTENER_QUERY";
+		case ZT_ICMP6_MULTICAST_LISTENER_REPORT:            return "ICMP6_MULTICAST_LISTENER_REPORT";
+		case ZT_ICMP6_MULTICAST_LISTENER_DONE:              return "ICMP6_MULTICAST_LISTENER_DONE";
+		case ZT_ICMP6_ROUTER_SOLICITATION:                  return "ICMP6_ROUTER_SOLICITATION";
+		case ZT_ICMP6_ROUTER_ADVERTISEMENT:                 return "ICMP6_ROUTER_ADVERTISEMENT";
+		case ZT_ICMP6_NEIGHBOR_SOLICITATION:                return "ICMP6_NEIGHBOR_SOLICITATION";
+		case ZT_ICMP6_NEIGHBOR_ADVERTISEMENT:               return "ICMP6_NEIGHBOR_ADVERTISEMENT";
+		case ZT_ICMP6_REDIRECT_MESSAGE:                     return "ICMP6_REDIRECT_MESSAGE";
+		case ZT_ICMP6_ROUTER_RENUMBERING:                   return "ICMP6_ROUTER_RENUMBERING";
+		case ZT_ICMP6_NODE_INFORMATION_QUERY:               return "ICMP6_NODE_INFORMATION_QUERY";
+		case ZT_ICMP6_NODE_INFORMATION_RESPONSE:            return "ICMP6_NODE_INFORMATION_RESPONSE";
+		case ZT_ICMP6_INV_NEIGHBOR_SOLICITATION:            return "ICMP6_INV_NEIGHBOR_SOLICITATION";
+		case ZT_ICMP6_INV_NEIGHBOR_ADVERTISEMENT:           return "ICMP6_INV_NEIGHBOR_ADVERTISEMENT";
+		case ZT_ICMP6_MLDV2:                                return "ICMP6_MLDV2";
+		case ZT_ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REQUEST: return "ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REQUEST";
+		case ZT_ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REPLY:   return "ICMP6_HOME_AGENT_ADDRESS_DISCOVERY_REPLY";
+		case ZT_ICMP6_MOBILE_PREFIX_SOLICITATION:           return "ICMP6_MOBILE_PREFIX_SOLICITATION";
+		case ZT_ICMP6_MOBILE_PREFIX_ADVERTISEMENT:          return "ICMP6_MOBILE_PREFIX_ADVERTISEMENT";
+		case ZT_ICMP6_CERTIFICATION_PATH_SOLICITATION:      return "ICMP6_CERTIFICATION_PATH_SOLICITATION";
+		case ZT_ICMP6_CERTIFICATION_PATH_ADVERTISEMENT:     return "ICMP6_CERTIFICATION_PATH_ADVERTISEMENT";
+		case ZT_ICMP6_MULTICAST_ROUTER_ADVERTISEMENT:       return "ICMP6_MULTICAST_ROUTER_ADVERTISEMENT";
+		case ZT_ICMP6_MULTICAST_ROUTER_SOLICITATION:        return "ICMP6_MULTICAST_ROUTER_SOLICITATION";
+		case ZT_ICMP6_MULTICAST_ROUTER_TERMINATION:         return "ICMP6_MULTICAST_ROUTER_TERMINATION";
+		case ZT_ICMP6_RPL_CONTROL_MESSAGE:                  return "ICMP6_RPL_CONTROL_MESSAGE";
+	}
+	return UNKNOWN_NAME;
+}
 
-		s.push_back(';');
+Filter::Action Filter::operator()(const RuntimeEnvironment *_r,unsigned int etherType,const void *frame,unsigned int len) const
+{
+	Mutex::Lock _l(_chain_m);
 
-		if (i->rule.port()) {
-			if (i->rule.port().magnitude() > 1)
-				sprintf(buf,"%u-%u",i->rule.port().start,i->rule.port().end);
-			else sprintf(buf,"%u",i->rule.port().start);
-			s.append(buf);
-		} else s.push_back('*');
+	TRACE("starting match against %d rules",(int)_chain.size());
 
-		s.append("]:");
+	int ruleNo = 0;
+	for(std::vector<Entry>::const_iterator r(_chain.begin());r!=_chain.end();++r,++ruleNo) {
+		try {
+			if (r->rule(etherType,frame,len)) {
+				TRACE("match: %s",r->rule.toString().c_str());
 
-		switch(i->action) {
-			case ACTION_DENY:
-				s.append("DENY");
-				break;
-			case ACTION_ALLOW:
-				s.append("ALLOW");
-				break;
-			case ACTION_LOG:
-				s.append("LOG");
-				break;
+				switch(r->action) {
+					case ACTION_ALLOW:
+					case ACTION_DENY:
+						return r->action;
+					default:
+						break;
+				}
+			} else {
+				TRACE("no match: %s",r->rule.toString().c_str());
+			}
+		} catch (std::invalid_argument &exc) {
+			LOG("filter: unable to parse packet on rule %s (%d): %s",r->rule.toString().c_str(),ruleNo,exc.what());
+			return ACTION_UNPARSEABLE;
+		} catch ( ... ) {
+			LOG("filter: unable to parse packet on rule %s (%d): unknown exception",r->rule.toString().c_str(),ruleNo);
+			return ACTION_UNPARSEABLE;
 		}
-
-		if (!first)
-			s.append(sep);
 	}
 
-	return s;
-}
-
-const char *Filter::etherTypeName(const unsigned int etherType)
-	throw()
-{
-	static char tmp[6];
-	switch(etherType) {
-		case ZT_ETHERTYPE_IPV4:
-			return "IPV4";
-		case ZT_ETHERTYPE_ARP:
-			return "ARP";
-		case ZT_ETHERTYPE_RARP:
-			return "RARP";
-		case ZT_ETHERTYPE_ATALK:
-			return "ATALK";
-		case ZT_ETHERTYPE_AARP:
-			return "AARP";
-		case ZT_ETHERTYPE_IPX_A:
-			return "IPX_A";
-		case ZT_ETHERTYPE_IPX_B:
-			return "IPX_B";
-		case ZT_ETHERTYPE_IPV6:
-			return "IPV6";
-	}
-	sprintf(tmp,"%.4x",etherType);
-	return tmp; // technically not thread safe, but we're only going to see this in debugging if ever
+	return ACTION_ALLOW;
 }
 
 } // namespace ZeroTier

+ 61 - 10
node/Filter.hpp

@@ -33,6 +33,7 @@
 #include <string>
 #include <vector>
 #include <utility>
+#include <stdexcept>
 
 #include "Mutex.hpp"
 #include "Range.hpp"
@@ -129,6 +130,19 @@ class RuntimeEnvironment;
 class Filter
 {
 public:
+	/**
+	 * Value returned by etherTypeName, etc. on unknown
+	 *
+	 * These static methods return precisely this, so a pointer equality
+	 * check will work.
+	 */
+	static const char *const UNKNOWN_NAME;
+
+	/**
+	 * An empty range as a more idiomatic way of specifying a wildcard match
+	 */
+	static const Range<unsigned int> ANY;
+
 	/**
 	 * A filter rule
 	 *
@@ -171,8 +185,15 @@ public:
 		 * @param data Ethernet frame data
 		 * @param len Length of ethernet frame
 		 * @return True if rule matches
+		 * @throws std::invalid_argument Frame invalid or not parseable
 		 */
-		bool operator()(unsigned int etype,const void *data,unsigned int len) const;
+		bool operator()(unsigned int etype,const void *data,unsigned int len) const
+			throw(std::invalid_argument);
+
+		/**
+		 * @return Human readable representation of rule
+		 */
+		std::string toString() const;
 
 		inline bool operator==(const Rule &r) const throw() { return ((_etherType == r._etherType)&&(_protocol == r._protocol)&&(_port == r._port)); }
 		inline bool operator!=(const Rule &r) const throw() { return !(*this == r); }
@@ -208,7 +229,7 @@ public:
 	{
 		ACTION_DENY = 0,
 		ACTION_ALLOW = 1,
-		ACTION_LOG = 2
+		ACTION_UNPARSEABLE = 2
 	};
 
 	/**
@@ -227,8 +248,27 @@ public:
 		Action action;
 	};
 
-	Filter(const RuntimeEnvironment *renv);
-	~Filter();
+	Filter() :
+		_chain(),
+		_chain_m()
+	{
+	}
+
+	Filter(const Filter &f) :
+		_chain(),
+		_chain_m()
+	{
+		Mutex::Lock _l(f._chain_m);
+		_chain = f._chain;
+	}
+
+	inline Filter &operator=(const Filter &f)
+	{
+		Mutex::Lock _l1(_chain_m);
+		Mutex::Lock _l2(f._chain_m);
+		_chain = f._chain;
+		return *this;
+	}
 
 	/**
 	 * Remove all filter entries
@@ -281,16 +321,27 @@ public:
 	 */
 	std::string toString(const char *sep = (const char *)0) const;
 
-	/**
-	 * @param etherType Ethernet type ID
-	 * @return Name of Ethernet protocol (e.g. ARP, IPV4)
-	 */
 	static const char *etherTypeName(const unsigned int etherType)
 		throw();
+	static const char *ipProtocolName(const unsigned int ipp)
+		throw();
+	static const char *icmpTypeName(const unsigned int icmpType)
+		throw();
+	static const char *icmp6TypeName(const unsigned int icmp6Type)
+		throw();
 
-private:
-	const RuntimeEnvironment *_r;
+	/**
+	 * Match against an Ethernet frame
+	 *
+	 * @param _r Runtime environment
+	 * @param etherType Ethernet frame type
+	 * @param frame Ethernet frame data
+	 * @param len Length of frame in bytes
+	 * @return Action if matched or ACTION_ALLOW if not matched
+	 */
+	Action operator()(const RuntimeEnvironment *_r,unsigned int etherType,const void *frame,unsigned int len) const;
 
+private:
 	std::vector<Entry> _chain;
 	Mutex _chain_m;
 };

+ 0 - 323
node/Http.cpp

@@ -1,323 +0,0 @@
-/*
- * ZeroTier One - Global Peer to Peer Ethernet
- * Copyright (C) 2012-2013  ZeroTier Networks LLC
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <vector>
-#include <set>
-#include <list>
-
-#ifndef _WIN32
-#include <unistd.h>
-#endif
-
-#include "Http.hpp"
-#include "Utils.hpp"
-#include "InetAddress.hpp"
-
-static http_parser_settings _http_parser_settings;
-
-namespace ZeroTier {
-
-static bool _sendAll(int fd,const void *buf,unsigned int len)
-{
-	for(;;) {
-		int n = (int)::send(fd,buf,len,0);
-		if ((n < 0)&&(errno == EINTR))
-			continue;
-		return (n == (int)len);
-	}
-}
-
-const std::map<std::string,std::string> Http::EMPTY_HEADERS;
-
-Http::Request::Request(
-	Http::Method m,
-	const std::string &url,
-	const std::map<std::string,std::string> &rh,
-	const std::string &rb,
-	bool (*handler)(Request *,void *,const std::string &,int,const std::map<std::string,std::string> &,const std::string &),
-	void *arg) :
-	_url(url),
-	_requestHeaders(rh),
-	_handler(handler),
-	_arg(arg),
-	_method(m),
-	_fd(0)
-{
-	_http_parser_settings.on_message_begin = &Http::Request::_http_on_message_begin;
-	_http_parser_settings.on_url = &Http::Request::_http_on_url;
-	_http_parser_settings.on_status_complete = &Http::Request::_http_on_status_complete;
-	_http_parser_settings.on_header_field = &Http::Request::_http_on_header_field;
-	_http_parser_settings.on_header_value = &Http::Request::_http_on_header_value;
-	_http_parser_settings.on_headers_complete = &Http::Request::_http_on_headers_complete;
-	_http_parser_settings.on_body = &Http::Request::_http_on_body;
-	_http_parser_settings.on_message_complete = &Http::Request::_http_on_message_complete;
-
-	start();
-}
-
-Http::Request::~Request()
-{
-	if (_fd > 0)
-		::close(_fd);
-	join();
-}
-
-void Http::Request::main()
-	throw()
-{
-	char buf[131072];
-
-	try {
-		http_parser_init(&_parser,HTTP_RESPONSE);
-		_parser.data = this;
-
-		http_parser_url urlParsed;
-		if (http_parser_parse_url(_url.c_str(),_url.length(),0,&urlParsed)) {
-			suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"URL parse error");
-			return;
-		}
-		if (!(urlParsed.field_set & (1 << UF_SCHEMA))) {
-			suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"URL specifies no schema");
-			return;
-		}
-
-		std::string schema(_url.substr(urlParsed.field_data[UF_SCHEMA].off,urlParsed.field_data[UF_SCHEMA].len));
-
-		if (schema == "file") {
-			const std::string filePath(_url.substr(urlParsed.field_data[UF_PATH].off,urlParsed.field_data[UF_PATH].len));
-
-			uint64_t lm = Utils::getLastModified(filePath.c_str());
-			if (lm) {
-				const std::map<std::string,std::string>::const_iterator ifModSince(_requestHeaders.find("If-Modified-Since"));
-				if ((ifModSince != _requestHeaders.end())&&(ifModSince->second.length())) {
-					uint64_t t64 = Utils::fromRfc1123(ifModSince->second);
-					if ((t64)&&(lm > t64)) {
-						suicidalThread = !_handler(this,_arg,_url,304,_responseHeaders,"");
-						return;
-					}
-				}
-
-				if (Utils::readFile(filePath.c_str(),_responseBody)) {
-					_responseHeaders["Last-Modified"] = Utils::toRfc1123(lm);
-					suicidalThread = !_handler(this,_arg,_url,200,_responseHeaders,_responseBody);
-					return;
-				}
-			}
-
-			suicidalThread = !_handler(this,_arg,_url,404,_responseHeaders,"file not found or not readable");
-			return;
-		} else if (schema == "http") {
-			if (!(urlParsed.field_set & (1 << UF_HOST))) {
-				suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"URL contains no host");
-				return;
-			}
-			std::string host(_url.substr(urlParsed.field_data[UF_HOST].off,urlParsed.field_data[UF_HOST].len));
-
-			std::list<InetAddress> v4,v6;
-			{
-				struct addrinfo *res = (struct addrinfo *)0;
-				if (!getaddrinfo(host.c_str(),(const char *)0,(const struct addrinfo *)0,&res)) {
-					struct addrinfo *p = res;
-					do {
-						if (p->ai_family == AF_INET)
-							v4.push_back(InetAddress(p->ai_addr));
-						else if (p->ai_family == AF_INET6)
-							v6.push_back(InetAddress(p->ai_addr));
-					} while ((p = p->ai_next));
-					freeaddrinfo(res);
-				}
-			}
-
-			std::list<InetAddress> *addrList;
-			if (v4.empty()&&v6.empty()) {
-				suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"could not find address for host in URL");
-				return;
-			} else if (v4.empty()) {
-				addrList = &v6;
-			} else {
-				addrList = &v4;
-			}
-			InetAddress *addr;
-			{
-				addrList->sort();
-				addrList->unique();
-				unsigned int i = 0,k = 0;
-				k = rand() % addrList->size();
-				std::list<InetAddress>::iterator a(addrList->begin());
-				while (i++ != k) ++a;
-				addr = &(*a);
-			}
-
-			int remotePort = ((urlParsed.field_set & (1 << UF_PORT))&&(urlParsed.port)) ? (int)urlParsed.port : (int)80;
-			if ((remotePort <= 0)||(remotePort > 0xffff)) {
-				suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"URL port out of range");
-				return;
-			}
-			addr->setPort(remotePort);
-
-			_fd = socket(addr->isV6() ? AF_INET6 : AF_INET,SOCK_STREAM,0);
-			if (_fd <= 0) {
-				suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"could not open socket");
-				return;
-			}
-
-			for(;;) {
-				if (connect(_fd,addr->saddr(),addr->saddrLen())) {
-					if (errno == EINTR)
-						continue;
-					::close(_fd); _fd = 0;
-					suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"connection failed to remote host");
-					return;
-				} else break;
-			}
-
-			const char *mstr = "GET";
-			switch(_method) {
-				case HTTP_METHOD_HEAD: mstr = "HEAD"; break;
-				default: break;
-			}
-			int mlen = (int)snprintf(buf,sizeof(buf),"%s %s HTTP/1.1\r\nAccept-Encoding: \r\nHost: %s\r\n",mstr,_url.substr(urlParsed.field_data[UF_PATH].off,urlParsed.field_data[UF_PATH].len).c_str(),host.c_str());
-			if (mlen >= (int)sizeof(buf)) {
-				::close(_fd); _fd = 0;
-				suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"URL too long");
-				return;
-			}
-			if (!_sendAll(_fd,buf,mlen)) {
-				::close(_fd); _fd = 0;
-				suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"write error");
-				return;
-			}
-
-			for(std::map<std::string,std::string>::const_iterator rh(_requestHeaders.begin());rh!=_requestHeaders.end();++rh) {
-				mlen = (int)snprintf(buf,sizeof(buf),"%s: %s\r\n",rh->first.c_str(),rh->second.c_str());
-				if (mlen >= (int)sizeof(buf)) {
-					::close(_fd); _fd = 0;
-					suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"header too long");
-					return;
-				}
-				if (!_sendAll(_fd,buf,mlen)) {
-					::close(_fd); _fd = 0;
-					suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"write error");
-					return;
-				}
-			}
-
-			if (!_sendAll(_fd,"\r\n",2)) {
-				::close(_fd); _fd = 0;
-				suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"write error");
-				return;
-			}
-
-			_responseStatusCode = 0;
-			_messageComplete = false;
-			for(;;) {
-				mlen = (int)::recv(_fd,buf,sizeof(buf),0);
-				if (mlen < 0) {
-					if (errno != EINTR)
-						break;
-					else continue;
-				}
-				if (((int)http_parser_execute(&_parser,&_http_parser_settings,buf,mlen) != mlen)||(_parser.upgrade)) {
-					::close(_fd); _fd = 0;
-					suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"invalid HTTP response from server");
-					return;
-				}
-				if (_messageComplete) {
-					::close(_fd); _fd = 0;
-					suicidalThread = !_handler(this,_arg,_url,_responseStatusCode,_responseHeaders,_responseBody);
-					return;
-				}
-			}
-
-			::close(_fd); _fd = 0;
-			suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"empty HTTP response from server");
-			return;
-		} else {
-			suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"only 'file' and 'http' methods are supported");
-			return;
-		}
-	} catch ( ... ) {
-		suicidalThread = !_handler(this,_arg,_url,0,_responseHeaders,"unexpected exception retrieving URL");
-		return;
-	}
-}
-
-int Http::Request::_http_on_message_begin(http_parser *parser)
-{
-	return 0;
-}
-int Http::Request::_http_on_url(http_parser *parser,const char *data,size_t length)
-{
-	return 0;
-}
-int Http::Request::_http_on_status_complete(http_parser *parser)
-{
-	Http::Request *r = (Http::Request *)parser->data;
-	r->_responseStatusCode = parser->status_code;
-	return 0;
-}
-int Http::Request::_http_on_header_field(http_parser *parser,const char *data,size_t length)
-{
-	Http::Request *r = (Http::Request *)parser->data;
-	if ((r->_currentHeaderField.length())&&(r->_responseHeaders.find(r->_currentHeaderField) != r->_responseHeaders.end()))
-		r->_currentHeaderField.assign("");
-	r->_currentHeaderField.append(data,length);
-	return 0;
-}
-int Http::Request::_http_on_header_value(http_parser *parser,const char *data,size_t length)
-{
-	Http::Request *r = (Http::Request *)parser->data;
-	if (r->_currentHeaderField.length())
-		r->_responseHeaders[r->_currentHeaderField].append(data,length);
-	return 0;
-}
-int Http::Request::_http_on_headers_complete(http_parser *parser)
-{
-	Http::Request *r = (Http::Request *)parser->data;
-	return ((r->_method == Http::HTTP_METHOD_HEAD) ? 1 : 0);
-}
-int Http::Request::_http_on_body(http_parser *parser,const char *data,size_t length)
-{
-	Http::Request *r = (Http::Request *)parser->data;
-	r->_responseBody.append(data,length);
-	return 0;
-}
-int Http::Request::_http_on_message_complete(http_parser *parser)
-{
-	Http::Request *r = (Http::Request *)parser->data;
-	r->_messageComplete = true;
-	return 0;
-}
-
-} // namespace ZeroTier

+ 0 - 129
node/Http.hpp

@@ -1,129 +0,0 @@
-/*
- * ZeroTier One - Global Peer to Peer Ethernet
- * Copyright (C) 2012-2013  ZeroTier Networks LLC
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-#ifndef _ZT_HTTP_HPP
-#define _ZT_HTTP_HPP
-
-#include <map>
-#include <string>
-#include <stdexcept>
-#include "Thread.hpp"
-
-#include "../ext/http-parser/http_parser.h"
-
-namespace ZeroTier {
-
-class Http
-{
-public:
-	/**
-	 * HTTP request methods
-	 */
-	enum Method
-	{
-		HTTP_METHOD_GET,
-		HTTP_METHOD_HEAD
-	};
-
-	/**
-	 * An empty headers map for convenience
-	 */
-	static const std::map<std::string,std::string> EMPTY_HEADERS;
-
-	/**
-	 * HTTP request
-	 */
-	class Request : protected Thread
-	{
-	public:
-		/**
-		 * Create and issue an HTTP request
-		 *
-		 * The supplied handler is called when the request is
-		 * complete or if an error occurs. A code of zero indicates
-		 * that the server could not be reached, and a description
-		 * of the error will be in 'body'. If the handler returns
-		 * false the Request object deletes itself. Otherwise the
-		 * object must be deleted by other code.
-		 *
-		 * @param m Request method
-		 * @param url Destination URL
-		 * @param rh Request headers
-		 * @param rb Request body or empty string for none (currently unused)
-		 * @param handler Request handler function
-		 * @param arg First argument to request handler
-		 */
-		Request(
-			Http::Method m,
-			const std::string &url,
-			const std::map<std::string,std::string> &rh,
-			const std::string &rb,
-			bool (*handler)(Request *,void *,const std::string &,int,const std::map<std::string,std::string> &,const std::string &),
-			void *arg);
-
-		/**
-		 * Destruction cancels any in-progress request
-		 */
-		virtual ~Request();
-
-	protected:
-		virtual void main()
-			throw();
-
-	private:
-		// HTTP parser handlers
-		static int _http_on_message_begin(http_parser *parser);
-		static int _http_on_url(http_parser *parser,const char *data,size_t length);
-		static int _http_on_status_complete(http_parser *parser);
-		static int _http_on_header_field(http_parser *parser,const char *data,size_t length);
-		static int _http_on_header_value(http_parser *parser,const char *data,size_t length);
-		static int _http_on_headers_complete(http_parser *parser);
-		static int _http_on_body(http_parser *parser,const char *data,size_t length);
-		static int _http_on_message_complete(http_parser *parser);
-
-		http_parser _parser;
-		std::string _url;
-
-		std::map<std::string,std::string> _requestHeaders;
-		std::map<std::string,std::string> _responseHeaders;
-
-		std::string _currentHeaderField;
-		std::string _responseBody;
-
-		bool (*_handler)(Request *,void *,const std::string &,int,const std::map<std::string,std::string> &,const std::string &);
-		void *_arg;
-
-		Http::Method _method;
-		int _responseStatusCode;
-		bool _messageComplete;
-		volatile int _fd;
-	};
-};
-
-} // namespace ZeroTier
-
-#endif

+ 8 - 4
node/Identity.cpp

@@ -57,11 +57,13 @@ void Identity::generate()
 	// the address of an identity will be detected as its signature will be
 	// invalid. Of course, deep verification of address/key relationship is
 	// required to cover the more elaborate address claim jump attempt case.
+	unsigned char atmp[ZT_ADDRESS_LENGTH];
+	_address.copyTo(atmp,ZT_ADDRESS_LENGTH);
 	SHA256_CTX sha;
 	unsigned char dig[32];
 	unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0;
 	SHA256_Init(&sha);
-	SHA256_Update(&sha,_address.data(),ZT_ADDRESS_LENGTH);
+	SHA256_Update(&sha,atmp,ZT_ADDRESS_LENGTH);
 	SHA256_Update(&sha,&zero,1);
 	SHA256_Update(&sha,&idtype,1);
 	SHA256_Update(&sha,&zero,1);
@@ -73,11 +75,13 @@ void Identity::generate()
 
 bool Identity::locallyValidate(bool doAddressDerivationCheck) const
 {
+	unsigned char atmp[ZT_ADDRESS_LENGTH];
+	_address.copyTo(atmp,ZT_ADDRESS_LENGTH);
 	SHA256_CTX sha;
 	unsigned char dig[32];
 	unsigned char idtype = IDENTITY_TYPE_NIST_P_521,zero = 0;
 	SHA256_Init(&sha);
-	SHA256_Update(&sha,_address.data(),ZT_ADDRESS_LENGTH);
+	SHA256_Update(&sha,atmp,ZT_ADDRESS_LENGTH);
 	SHA256_Update(&sha,&zero,1);
 	SHA256_Update(&sha,&idtype,1);
 	SHA256_Update(&sha,&zero,1);
@@ -119,7 +123,7 @@ bool Identity::fromString(const char *str)
 	std::string b(Utils::unhex(fields[0]));
 	if (b.length() != ZT_ADDRESS_LENGTH)
 		return false;
-	_address = b.data();
+	_address.setTo(b.data(),ZT_ADDRESS_LENGTH);
 
 	b = Utils::base64Decode(fields[2]);
 	if ((!b.length())||(b.length() > ZT_EC_MAX_BYTES))
@@ -218,7 +222,7 @@ Address Identity::deriveAddress(const void *keyBytes,unsigned int keyLen)
 
 	delete [] ram;
 
-	return Address(dig); // first 5 bytes of dig[]
+	return Address(dig,ZT_ADDRESS_LENGTH); // first 5 bytes of dig[]
 }
 
 std::string Identity::encrypt(const Identity &to,const void *data,unsigned int len) const

+ 4 - 4
node/Identity.hpp

@@ -104,7 +104,7 @@ public:
 		_keyPair((EllipticCurveKeyPair *)0)
 	{
 		if (!fromString(str))
-			throw std::invalid_argument("invalid string-serialized identity");
+			throw std::invalid_argument(std::string("invalid string-serialized identity: ") + str);
 	}
 
 	Identity(const std::string &str)
@@ -112,7 +112,7 @@ public:
 		_keyPair((EllipticCurveKeyPair *)0)
 	{
 		if (!fromString(str))
-			throw std::invalid_argument("invalid string-serialized identity");
+			throw std::invalid_argument(std::string("invalid string-serialized identity: ") + str);
 	}
 
 	template<unsigned int C>
@@ -307,7 +307,7 @@ public:
 	inline void serialize(Buffer<C> &b,bool includePrivate = false) const
 		throw(std::out_of_range)
 	{
-		b.append(_address.data(),ZT_ADDRESS_LENGTH);
+		_address.appendTo(b);
 		b.append((unsigned char)IDENTITY_TYPE_NIST_P_521);
 		b.append((unsigned char)(_publicKey.size() & 0xff));
 		b.append(_publicKey.data(),_publicKey.size());
@@ -340,7 +340,7 @@ public:
 
 		unsigned int p = startAt;
 
-		_address = b.field(p,ZT_ADDRESS_LENGTH);
+		_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
 		p += ZT_ADDRESS_LENGTH;
 
 		if (b[p++] != IDENTITY_TYPE_NIST_P_521)

+ 14 - 2
node/Multicaster.hpp

@@ -261,9 +261,21 @@ public:
 			bf.set((peers[chosen++] = *i)->address().sum());
 
 		// Add a supernode if there are fewer than the desired
-		// number of recipients.
+		// number of recipients. Note that we do not use the bloom
+		// filter to track visits to supernodes, intentionally
+		// allowing multicasts to ping pong between supernodes.
+		// Supernodes propagate even messages they've already seen,
+		// while regular nodes do not. Thus this ping-ponging will
+		// cause the supernodes to pick new starting points for
+		// peer to peer graph traversal multiple times. It's a
+		// simple, stateless way to increase supernode-driven
+		// propagation of a multicast in the event that peer to
+		// peer connectivity for its group is sparse.
 		if (chosen < max) {
-			P peer = topology.getBestSupernode(&originalSubmitter,1,true);
+			Address avoid[2];
+			avoid[0] = originalSubmitter;
+			avoid[1] = upstream;
+			P peer = topology.getBestSupernode(avoid,2,true);
 			if (peer)
 				peers[chosen++] = peer;
 		}

+ 119 - 4
node/Network.cpp

@@ -25,19 +25,87 @@
  * LLC. Start here: http://www.zerotier.com/
  */
 
+#include <stdlib.h>
+#include <math.h>
+
+#include <openssl/sha.h>
+
+#include "RuntimeEnvironment.hpp"
+#include "NodeConfig.hpp"
 #include "Network.hpp"
 #include "Switch.hpp"
+#include "Packet.hpp"
 
 namespace ZeroTier {
 
+void Network::Certificate::_shaForSignature(unsigned char *dig) const
+{
+	SHA256_CTX sha;
+	SHA256_Init(&sha);
+	unsigned char zero = 0;
+	for(const_iterator i(begin());i!=end();++i) {
+		SHA256_Update(&sha,&zero,1);
+		SHA256_Update(&sha,(const unsigned char *)i->first.data(),i->first.length());
+		SHA256_Update(&sha,&zero,1);
+		SHA256_Update(&sha,(const unsigned char *)i->second.data(),i->second.length());
+		SHA256_Update(&sha,&zero,1);
+	}
+	SHA256_Final(dig,&sha);
+}
+
+static const std::string _DELTA_PREFIX("~");
+bool Network::Certificate::qualifyMembership(const Network::Certificate &mc) const
+{
+	// Note: optimization probably needed here, probably via some kind of
+	// memoization / dynamic programming.
+
+	for(const_iterator myField(begin());myField!=end();++myField) {
+		if (!((myField->first.length() > 1)&&(myField->first[0] == '~'))) { // ~fields are max delta range specs
+			// If they lack the same field, comparison fails.
+			const_iterator theirField(mc.find(myField->first));
+			if (theirField == mc.end())
+				return false;
+
+			const_iterator deltaField(find(_DELTA_PREFIX + myField->first));
+			if (deltaField == end()) {
+				// If there is no delta, compare on simple equality
+				if (myField->second != theirField->second)
+					return false;
+			} else {
+				// Otherwise compare range with max delta. Presence of a dot in delta
+				// indicates a floating point comparison. Otherwise an integer
+				// comparison occurs.
+				if (deltaField->second.find('.') != std::string::npos) {
+					double my = strtod(myField->second.c_str(),(char **)0);
+					double their = strtod(theirField->second.c_str(),(char **)0);
+					double delta = strtod(deltaField->second.c_str(),(char **)0);
+					if (fabs(my - their) > delta)
+						return false;
+				} else {
+					int64_t my = strtoll(myField->second.c_str(),(char **)0,10);
+					int64_t their = strtoll(theirField->second.c_str(),(char **)0,10);
+					int64_t delta = strtoll(deltaField->second.c_str(),(char **)0,10);
+					if (my > their) {
+						if ((my - their) > delta)
+							return false;
+					} else {
+						if ((their - my) > delta)
+							return false;
+					}
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
 Network::Network(const RuntimeEnvironment *renv,uint64_t id)
 	throw(std::runtime_error) :
 	_r(renv),
-	_id(id),
 	_tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,this),
-	_members(),
-	_open(false),
-	_lock()
+	_lastConfigUpdate(0),
+	_id(id)
 {
 }
 
@@ -45,6 +113,53 @@ Network::~Network()
 {
 }
 
+void Network::setConfiguration(const Network::Config &conf)
+{
+	Mutex::Lock _l(_lock);
+	if ((conf.networkId() == _id)&&(conf.peerAddress() == _r->identity.address())) { // sanity check
+		_configuration = conf;
+		_myCertificate = conf.certificateOfMembership();
+		_lastConfigUpdate = Utils::now();
+	}
+}
+
+void Network::requestConfiguration()
+{
+	Packet outp(controller(),_r->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST);
+	outp.append((uint64_t)_id);
+	_r->sw->send(outp,true);
+}
+
+bool Network::isAllowed(const Address &peer) const
+{
+	// Exceptions can occur if we do not yet have *our* configuration.
+	try {
+		Mutex::Lock _l(_lock);
+		if (_configuration.isOpen())
+			return true;
+		std::map<Address,Certificate>::const_iterator pc(_membershipCertificates.find(peer));
+		if (pc == _membershipCertificates.end())
+			return false;
+		return _myCertificate.qualifyMembership(pc->second);
+	} catch (std::exception &exc) {
+		TRACE("isAllowed() check failed for peer %s: unexpected exception: %s",peer.toString().c_str(),exc.what());
+		return false;
+	} catch ( ... ) {
+		TRACE("isAllowed() check failed for peer %s: unexpected exception: unknown exception",peer.toString().c_str());
+		return false;
+	}
+}
+
+void Network::clean()
+{
+	Mutex::Lock _l(_lock);
+	for(std::map<Address,Certificate>::iterator i=(_membershipCertificates.begin());i!=_membershipCertificates.end();) {
+		if (_myCertificate.qualifyMembership(i->second))
+			++i;
+		else _membershipCertificates.erase(i++);
+	}
+}
+
 void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data)
 {
 	const RuntimeEnvironment *_r = ((Network *)arg)->_r;

+ 305 - 43
node/Network.hpp

@@ -30,33 +30,277 @@
 
 #include <string>
 #include <set>
+#include <map>
 #include <vector>
 #include <stdexcept>
+
+#include "Constants.hpp"
+#include "Utils.hpp"
 #include "EthernetTap.hpp"
 #include "Address.hpp"
 #include "Mutex.hpp"
-#include "InetAddress.hpp"
-#include "Constants.hpp"
 #include "SharedPtr.hpp"
 #include "AtomicCounter.hpp"
-#include "RuntimeEnvironment.hpp"
 #include "MulticastGroup.hpp"
 #include "NonCopyable.hpp"
 #include "MAC.hpp"
+#include "Dictionary.hpp"
+#include "Identity.hpp"
+#include "InetAddress.hpp"
 
 namespace ZeroTier {
 
+class RuntimeEnvironment;
 class NodeConfig;
 
 /**
- * Local network endpoint
+ * A virtual LAN
+ *
+ * Networks can be open or closed. Each network has an ID whose most
+ * significant 40 bits are the ZeroTier address of the node that should
+ * be contacted for network configuration. The least significant 24
+ * bits are arbitrary, allowing up to 2^24 networks per managing
+ * node.
+ *
+ * Open networks do not track membership. Anyone is allowed to communicate
+ * over them.
+ *
+ * Closed networks track membership by way of timestamped signatures. When
+ * the network requests its configuration, one of the fields returned is
+ * a signature for the identity of the peer on the network. This signature
+ * includes a timestamp. When a peer communicates with other peers on a
+ * closed network, it periodically (and pre-emptively) propagates this
+ * signature to the peers with which it is communicating. Peers reject
+ * packets with an error if no recent signature is on file.
  */
 class Network : NonCopyable
 {
 	friend class SharedPtr<Network>;
 	friend class NodeConfig;
 
+public:
+	/**
+	 * A certificate of network membership
+	 */
+	class Certificate : private Dictionary
+	{
+	public:
+		Certificate()
+		{
+		}
+
+		Certificate(const char *s) :
+			Dictionary(s)
+		{
+		}
+
+		Certificate(const std::string &s) :
+			Dictionary(s)
+		{
+		}
+
+		/**
+		 * @return Read-only underlying dictionary
+		 */
+		inline const Dictionary &dictionary() const { return *this; }
+
+		inline void setNetworkId(uint64_t id)
+		{
+			char buf[32];
+			sprintf(buf,"%.16llx",(unsigned long long)id);
+			(*this)["nwid"] = buf;
+		}
+
+		inline uint64_t networkId() const
+			throw(std::invalid_argument)
+		{
+			return strtoull(get("nwid").c_str(),(char **)0,16);
+		}
+
+		inline void setPeerAddress(Address &a)
+		{
+			(*this)["peer"] = a.toString();
+		}
+
+		inline Address peerAddress() const
+			throw(std::invalid_argument)
+		{
+			return Address(get("peer"));
+		}
+
+		/**
+		 * Set the timestamp and timestamp max-delta
+		 *
+		 * @param ts Timestamp in ms since epoch
+		 * @param maxDelta Maximum difference between two peers on the same network
+		 */
+		inline void setTimestamp(uint64_t ts,uint64_t maxDelta)
+		{
+			char foo[32];
+			sprintf(foo,"%llu",(unsigned long long)ts);
+			(*this)["ts"] = foo;
+			sprintf(foo,"%llu",(unsigned long long)maxDelta);
+			(*this)["~ts"] = foo;
+		}
+
+		/**
+		 * Sign this certificate
+		 *
+		 * @param with Signing identity -- the identity of this network's controller
+		 * @return Signature or empty string on failure
+		 */
+		inline std::string sign(const Identity &with) const
+		{
+			unsigned char dig[32];
+			_shaForSignature(dig);
+			return with.sign(dig);
+		}
+
+		/**
+		 * Verify this certificate's signature
+		 *
+		 * @param with Signing identity -- the identity of this network's controller
+		 * @param sig Signature
+		 * @param siglen Length of signature in bytes
+		 */
+		inline bool verify(const Identity &with,const void *sig,unsigned int siglen) const
+		{
+			unsigned char dig[32];
+			_shaForSignature(dig);
+			return with.verifySignature(dig,sig,siglen);
+		}
+
+		/**
+		 * Check if another peer is indeed a current member of this network
+		 *
+		 * Fields with companion ~fields are compared with the defined maximum
+		 * delta in this certificate. Fields without ~fields are compared for
+		 * equality.
+		 *
+		 * This does not verify the certificate's signature!
+		 * 
+		 * @param mc Peer membership certificate
+		 * @return True if mc's membership in this network is current
+		 */
+		bool qualifyMembership(const Certificate &mc) const;
+
+	private:
+		void _shaForSignature(unsigned char *dig) const;
+	};
+
+	/**
+	 * A network configuration for a given node
+	 */
+	class Config : private Dictionary
+	{
+	public:
+		Config()
+		{
+		}
+
+		Config(const char *s) :
+			Dictionary(s)
+		{
+		}
+
+		Config(const std::string &s) :
+			Dictionary(s)
+		{
+		}
+
+		inline void setNetworkId(uint64_t id)
+		{
+			char buf[32];
+			sprintf(buf,"%.16llx",(unsigned long long)id);
+			(*this)["nwid"] = buf;
+		}
+
+		inline uint64_t networkId() const
+			throw(std::invalid_argument)
+		{
+			return strtoull(get("nwid").c_str(),(char **)0,16);
+		}
+
+		inline void setPeerAddress(Address &a)
+		{
+			(*this)["peer"] = a.toString();
+		}
+
+		inline Address peerAddress() const
+			throw(std::invalid_argument)
+		{
+			return Address(get("peer"));
+		}
+
+		/**
+		 * @return Certificate of membership for this network, or empty cert if none
+		 */
+		inline Certificate certificateOfMembership() const
+		{
+			return Certificate(get("com",""));
+		}
+
+		/**
+		 * @return True if this is an open non-access-controlled network
+		 */
+		inline bool isOpen() const
+		{
+			return (get("isOpen") == "1");
+		}
+
+		/**
+		 * @return All static addresses / netmasks, IPv4 or IPv6
+		 */
+		inline std::set<InetAddress> staticAddresses() const
+		{
+			std::set<InetAddress> sa;
+			std::vector<std::string> ips(Utils::split(get("ipv4Static","").c_str(),",","",""));
+			for(std::vector<std::string>::const_iterator i(ips.begin());i!=ips.end();++i)
+				sa.insert(InetAddress(*i));
+			ips = Utils::split(get("ipv6Static","").c_str(),",","","");
+			for(std::vector<std::string>::const_iterator i(ips.begin());i!=ips.end();++i)
+				sa.insert(InetAddress(*i));
+			return sa;
+		}
+
+		/**
+		 * Set static IPv4 and IPv6 addresses
+		 *
+		 * This sets the ipv4Static and ipv6Static fields to comma-delimited
+		 * lists of assignments. The port field in InetAddress must be the
+		 * number of bits in the netmask.
+		 *
+		 * @param begin Start of container or array of addresses (InetAddress)
+		 * @param end End of container or array of addresses (InetAddress)
+		 * @tparam I Type of container or array
+		 */
+		template<typename I>
+		inline void setStaticInetAddresses(const I &begin,const I &end)
+		{
+			std::string v4;
+			std::string v6;
+			for(I i(begin);i!=end;++i) {
+				if (i->isV4()) {
+					if (v4.length())
+						v4.push_back(',');
+					v4.append(i->toString());
+				} else if (i->isV6()) {
+					if (v6.length())
+						v6.push_back(',');
+					v6.append(i->toString());
+				}
+			}
+			if (v4.length())
+				(*this)["ipv4Static"] = v4;
+			else erase("ipv4Static");
+			if (v6.length())
+				(*this)["ipv6Static"] = v6;
+			else erase("ipv6Static");
+		}
+	};
+
 private:
+	// Only NodeConfig can create, only SharedPtr can delete
 	Network(const RuntimeEnvironment *renv,uint64_t id)
 		throw(std::runtime_error);
 
@@ -74,83 +318,101 @@ public:
 	inline EthernetTap &tap() throw() { return _tap; }
 
 	/**
-	 * Get this network's members
-	 * 
-	 * If this is an open network, membership isn't relevant and this doesn't
-	 * mean much. If it's a closed network, frames will only be exchanged to/from
-	 * members.
-	 * 
-	 * @return Members of this network
+	 * @return Address of network's controlling node
 	 */
-	inline std::set<Address> members() const
-	{
-		Mutex::Lock _l(_lock);
-		return _members;
-	}
+	inline Address controller() throw() { return Address(_id >> 24); }
 
 	/**
-	 * @param addr Address to check
-	 * @return True if address is a member
+	 * @return True if network is open (no membership required)
 	 */
-	inline bool isMember(const Address &addr) const
+	inline bool isOpen() const
 		throw()
 	{
-		Mutex::Lock _l(_lock);
-		return (_members.count(addr) > 0);
+		try {
+			Mutex::Lock _l(_lock);
+			return _configuration.isOpen();
+		} catch ( ... ) {
+			return false;
+		}
 	}
 
 	/**
-	 * Shortcut to check open() and then isMember()
-	 * 
-	 * @param addr Address to check
-	 * @return True if network is open or if address is a member
+	 * Update multicast groups for this network's tap
+	 *
+	 * @return True if internal multicast group set has changed
 	 */
-	inline bool isAllowed(const Address &addr) const
-		throw()
+	inline bool updateMulticastGroups()
 	{
 		Mutex::Lock _l(_lock);
-		return ((_open)||(_members.count(addr) > 0));
+		return _tap.updateMulticastGroups(_multicastGroups);
 	}
 
 	/**
-	 * @return True if network is open (no membership required)
+	 * @return Latest set of multicast groups for this network's tap
 	 */
-	inline bool open() const
-		throw()
+	inline std::set<MulticastGroup> multicastGroups() const
 	{
 		Mutex::Lock _l(_lock);
-		return _open;
+		return _multicastGroups;
 	}
 
 	/**
-	 * Update internal multicast group set and return true if changed
+	 * Set or update this network's configuration
 	 *
-	 * @return True if internal multicast group set has changed
+	 * This is called by PacketDecoder when an update comes over the wire, or
+	 * internally when an old config is reloaded from disk.
+	 *
+	 * @param conf Configuration in key/value dictionary form
 	 */
-	inline bool updateMulticastGroups()
+	void setConfiguration(const Config &conf);
+
+	/**
+	 * Causes this network to request an updated configuration from its master node now
+	 */
+	void requestConfiguration();
+
+	/**
+	 * Add or update a peer's membership certificate
+	 *
+	 * The certificate must already have been validated via signature checking.
+	 *
+	 * @param peer Peer that owns certificate
+	 * @param cert Certificate itself
+	 */
+	inline void addMembershipCertificate(const Address &peer,const Certificate &cert)
 	{
 		Mutex::Lock _l(_lock);
-		return _tap.updateMulticastGroups(_multicastGroups);
+		_membershipCertificates[peer] = cert;
 	}
 
+	bool isAllowed(const Address &peer) const;
+
 	/**
-	 * @return Latest set of multicast groups
+	 * Perform periodic database cleaning such as removing expired membership certificates
 	 */
-	inline std::set<MulticastGroup> multicastGroups() const
+	void clean();
+
+	/**
+	 * @return Time of last updated configuration or 0 if none
+	 */
+	inline uint64_t lastConfigUpdate() const
+		throw()
 	{
-		Mutex::Lock _l(_lock);
-		return _multicastGroups;
+		return _lastConfigUpdate;
 	}
 
 private:
 	static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data);
 
 	const RuntimeEnvironment *_r;
-	uint64_t _id;
+
 	EthernetTap _tap;
-	std::set<Address> _members;
 	std::set<MulticastGroup> _multicastGroups;
-	bool _open;
+	std::map<Address,Certificate> _membershipCertificates;
+	Config _configuration;
+	Certificate _myCertificate;
+	uint64_t _lastConfigUpdate;
+	uint64_t _id;
 	Mutex _lock;
 
 	AtomicCounter __refCount;

+ 200 - 78
node/Node.cpp

@@ -37,26 +37,28 @@
 #include <vector>
 #include <string>
 
-#ifndef _WIN32
+#ifdef _WIN32
+#include <Windows.h>
+#else
 #include <fcntl.h>
 #include <unistd.h>
 #include <signal.h>
 #include <sys/file.h>
 #endif
 
-#include <openssl/sha.h>
-
 #include "Condition.hpp"
 #include "Node.hpp"
 #include "Topology.hpp"
 #include "Demarc.hpp"
+#include "Packet.hpp"
 #include "Switch.hpp"
 #include "Utils.hpp"
 #include "EthernetTap.hpp"
 #include "Logger.hpp"
 #include "Constants.hpp"
 #include "InetAddress.hpp"
-#include "Pack.hpp"
+#include "Salsa20.hpp"
+#include "HMAC.hpp"
 #include "RuntimeEnvironment.hpp"
 #include "NodeConfig.hpp"
 #include "Defaults.hpp"
@@ -66,11 +68,109 @@
 #include "Mutex.hpp"
 #include "Multicaster.hpp"
 #include "CMWC4096.hpp"
+#include "Service.hpp"
 
 #include "../version.h"
 
 namespace ZeroTier {
 
+struct _LocalClientImpl
+{
+	unsigned char key[32];
+	UdpSocket *sock;
+	void (*resultHandler)(void *,unsigned long,const char *);
+	void *arg;
+	InetAddress localDestAddr;
+	Mutex inUseLock;
+};
+
+static void _CBlocalClientHandler(UdpSocket *sock,void *arg,const InetAddress &remoteAddr,const void *data,unsigned int len)
+{
+	_LocalClientImpl *impl = (_LocalClientImpl *)arg;
+	if (!impl)
+		return;
+	if (!impl->resultHandler)
+		return; // sanity check
+	Mutex::Lock _l(impl->inUseLock);
+
+	try {
+		unsigned long convId = 0;
+		std::vector<std::string> results;
+		if (!NodeConfig::decodeControlMessagePacket(impl->key,data,len,convId,results))
+			return;
+		for(std::vector<std::string>::iterator r(results.begin());r!=results.end();++r)
+			impl->resultHandler(impl->arg,convId,r->c_str());
+	} catch ( ... ) {}
+}
+
+Node::LocalClient::LocalClient(const char *authToken,void (*resultHandler)(void *,unsigned long,const char *),void *arg)
+	throw() :
+	_impl((void *)0)
+{
+	_LocalClientImpl *impl = new _LocalClientImpl;
+
+	UdpSocket *sock = (UdpSocket *)0;
+	for(unsigned int i=0;i<5000;++i) {
+		try {
+			sock = new UdpSocket(true,32768 + (rand() % 20000),false,&_CBlocalClientHandler,impl);
+			break;
+		} catch ( ... ) {
+			sock = (UdpSocket *)0;
+		}
+	}
+
+	// If socket fails to bind, there's a big problem like missing IPv4 stack
+	if (sock) {
+		SHA256_CTX sha;
+		SHA256_Init(&sha);
+		SHA256_Update(&sha,authToken,strlen(authToken));
+		SHA256_Final(impl->key,&sha);
+
+		impl->sock = sock;
+		impl->resultHandler = resultHandler;
+		impl->arg = arg;
+		impl->localDestAddr = InetAddress::LO4;
+		impl->localDestAddr.setPort(ZT_CONTROL_UDP_PORT);
+		_impl = impl;
+	} else delete impl;
+}
+
+Node::LocalClient::~LocalClient()
+{
+	if (_impl) {
+		((_LocalClientImpl *)_impl)->inUseLock.lock();
+		delete ((_LocalClientImpl *)_impl)->sock;
+		((_LocalClientImpl *)_impl)->inUseLock.unlock();
+		delete ((_LocalClientImpl *)_impl);
+	}
+}
+
+unsigned long Node::LocalClient::send(const char *command)
+	throw()
+{
+	if (!_impl)
+		return 0;
+	_LocalClientImpl *impl = (_LocalClientImpl *)_impl;
+	Mutex::Lock _l(impl->inUseLock);
+
+	try {
+		uint32_t convId = (uint32_t)rand();
+		if (!convId)
+			convId = 1;
+
+		std::vector<std::string> tmp;
+		tmp.push_back(std::string(command));
+		std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > packets(NodeConfig::encodeControlMessage(impl->key,convId,tmp));
+
+		for(std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> >::iterator p(packets.begin());p!=packets.end();++p)
+			impl->sock->send(impl->localDestAddr,p->data(),p->size(),-1);
+
+		return convId;
+	} catch ( ... ) {
+		return 0;
+	}
+}
+
 struct _NodeImpl
 {
 	RuntimeEnvironment renv;
@@ -78,7 +178,6 @@ struct _NodeImpl
 	Node::ReasonForTermination reasonForTermination;
 	volatile bool started;
 	volatile bool running;
-	volatile bool updateStatusNow;
 	volatile bool terminateNow;
 
 	// Helper used to rapidly terminate from run()
@@ -94,20 +193,66 @@ struct _NodeImpl
 	}
 };
 
-Node::Node(const char *hp,const char *urlPrefix,const char *configAuthorityIdentity)
+#ifndef __WINDOWS__
+static void _netconfServiceMessageHandler(void *renv,Service &svc,const Dictionary &msg)
+{
+	if (!renv)
+		return; // sanity check
+	const RuntimeEnvironment *_r = (const RuntimeEnvironment *)renv;
+
+	try {
+		const std::string &type = msg.get("type");
+		if (type == "netconf-response") {
+			uint64_t inRePacketId = strtoull(msg.get("requestId").c_str(),(char **)0,16);
+			SharedPtr<Network> network = _r->nc->network(strtoull(msg.get("nwid").c_str(),(char **)0,16));
+			Address peerAddress(msg.get("peer").c_str());
+
+			if ((network)&&(peerAddress)) {
+				if (msg.contains("error")) {
+					Packet::ErrorCode errCode = Packet::ERROR_INVALID_REQUEST;
+					const std::string &err = msg.get("error");
+					if (err == "NOT_FOUND")
+						errCode = Packet::ERROR_NOT_FOUND;
+
+					Packet outp(peerAddress,_r->identity.address(),Packet::VERB_ERROR);
+					outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
+					outp.append(inRePacketId);
+					outp.append((unsigned char)errCode);
+					outp.append(network->id());
+					_r->sw->send(outp,true);
+				} else if (msg.contains("netconf")) {
+					const std::string &netconf = msg.get("netconf");
+					if (netconf.length() < 2048) { // sanity check
+						Packet outp(peerAddress,_r->identity.address(),Packet::VERB_OK);
+						outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
+						outp.append(inRePacketId);
+						outp.append(network->id());
+						outp.append((uint16_t)netconf.length());
+						outp.append(netconf.data(),netconf.length());
+						_r->sw->send(outp,true);
+					}
+				}
+			}
+		}
+	} catch (std::exception &exc) {
+		LOG("unexpected exception parsing response from netconf service: %s",exc.what());
+	} catch ( ... ) {
+		LOG("unexpected exception parsing response from netconf service: unknown exception");
+	}
+}
+#endif // !__WINDOWS__
+
+Node::Node(const char *hp)
 	throw() :
 	_impl(new _NodeImpl)
 {
 	_NodeImpl *impl = (_NodeImpl *)_impl;
 
 	impl->renv.homePath = hp;
-	impl->renv.autoconfUrlPrefix = urlPrefix;
-	impl->renv.configAuthorityIdentityStr = configAuthorityIdentity;
 
 	impl->reasonForTermination = Node::NODE_RUNNING;
 	impl->started = false;
 	impl->running = false;
-	impl->updateStatusNow = false;
 	impl->terminateNow = false;
 }
 
@@ -115,6 +260,10 @@ Node::~Node()
 {
 	_NodeImpl *impl = (_NodeImpl *)_impl;
 
+#ifndef __WINDOWS__
+	delete impl->renv.netconfService;
+#endif
+
 	delete impl->renv.sysEnv;
 	delete impl->renv.topology;
 	delete impl->renv.sw;
@@ -155,11 +304,9 @@ Node::ReasonForTermination Node::run()
 
 		TRACE("initializing...");
 
+		// Create non-crypto PRNG right away in case other code in init wants to use it
 		_r->prng = new CMWC4096();
 
-		if (!_r->configAuthority.fromString(_r->configAuthorityIdentityStr))
-			return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"configuration authority identity is not valid");
-
 		bool gotId = false;
 		std::string identitySecretPath(_r->homePath + ZT_PATH_SEPARATOR_S + "identity.secret");
 		std::string identityPublicPath(_r->homePath + ZT_PATH_SEPARATOR_S + "identity.public");
@@ -188,37 +335,35 @@ Node::ReasonForTermination Node::run()
 		}
 		Utils::lockDownFile(identitySecretPath.c_str(),false);
 
-		// Generate ownership verification secret, which can be presented to
-		// a controlling web site (like ours) to prove ownership of a node and
-		// permit its configuration to be centrally modified. When ZeroTier One
-		// requests its config it sends a hash of this secret, and so the
-		// config server can verify this hash to determine if the secret the
-		// user presents is correct.
-		std::string ovsPath(_r->homePath + ZT_PATH_SEPARATOR_S + "thisdeviceismine");
-		if (((Utils::now() - Utils::getLastModified(ovsPath.c_str())) >= ZT_OVS_GENERATE_NEW_IF_OLDER_THAN)||(!Utils::readFile(ovsPath.c_str(),_r->ownershipVerificationSecret))) {
-			_r->ownershipVerificationSecret = "";
-			unsigned int securern = 0;
+		// Clean up some obsolete files if present -- this will be removed later
+		unlink((_r->homePath + ZT_PATH_SEPARATOR_S + "status").c_str());
+		unlink((_r->homePath + ZT_PATH_SEPARATOR_S + "thisdeviceismine").c_str());
+
+		// Load or generate config authentication secret
+		std::string configAuthTokenPath(_r->homePath + ZT_PATH_SEPARATOR_S + "authtoken.secret");
+		std::string configAuthToken;
+		if (!Utils::readFile(configAuthTokenPath.c_str(),configAuthToken)) {
+			configAuthToken = "";
+			unsigned int sr = 0;
 			for(unsigned int i=0;i<24;++i) {
-				Utils::getSecureRandom(&securern,sizeof(securern));
-				_r->ownershipVerificationSecret.push_back("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[securern % 62]);
+				Utils::getSecureRandom(&sr,sizeof(sr));
+				configAuthToken.push_back("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[sr % 62]);
 			}
-			_r->ownershipVerificationSecret.append(ZT_EOL_S);
-			if (!Utils::writeFile(ovsPath.c_str(),_r->ownershipVerificationSecret))
-				return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"could not write 'thisdeviceismine' (home path not writable?)");
+			if (!Utils::writeFile(configAuthTokenPath.c_str(),configAuthToken))
+				return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"could not write authtoken.secret (home path not writable?)");
 		}
-		Utils::lockDownFile(ovsPath.c_str(),false);
-		_r->ownershipVerificationSecret = Utils::trim(_r->ownershipVerificationSecret); // trim off CR file is saved with
-		unsigned char ovsDig[32];
-		SHA256_CTX sha;
-		SHA256_Init(&sha);
-		SHA256_Update(&sha,_r->ownershipVerificationSecret.data(),_r->ownershipVerificationSecret.length());
-		SHA256_Final(ovsDig,&sha);
-		_r->ownershipVerificationSecretHash = Utils::base64Encode(ovsDig,32);
+		Utils::lockDownFile(configAuthTokenPath.c_str(),false);
 
 		// Create the core objects in RuntimeEnvironment: node config, demarcation
 		// point, switch, network topology database, and system environment
 		// watcher.
-		_r->nc = new NodeConfig(_r,_r->autoconfUrlPrefix + _r->identity.address().toString());
+		try {
+			_r->nc = new NodeConfig(_r,configAuthToken.c_str());
+		} catch ( ... ) {
+			// An exception here currently means that another instance of ZeroTier
+			// One is running.
+			return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"another instance of ZeroTier One appears to be running, or local control UDP port cannot be bound");
+		}
 		_r->demarc = new Demarc(_r);
 		_r->multicaster = new Multicaster();
 		_r->sw = new Switch(_r);
@@ -247,17 +392,26 @@ Node::ReasonForTermination Node::run()
 		return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"unknown exception during initialization");
 	}
 
+#ifndef __WINDOWS__
 	try {
-		std::string statusPath(_r->homePath + ZT_PATH_SEPARATOR_S + "status");
+		std::string netconfServicePath(_r->homePath + ZT_PATH_SEPARATOR_S + "services.d" + ZT_PATH_SEPARATOR_S + "netconf.service");
+		if (Utils::fileExists(netconfServicePath.c_str())) {
+			LOG("netconf.d/netconfi.service appears to exist, starting...");
+			_r->netconfService = new Service(_r,"netconf",netconfServicePath.c_str(),&_netconfServiceMessageHandler,_r);
+		}
+	} catch ( ... ) {
+		LOG("unexpected exception attempting to start services");
+	}
+#endif
 
+	try {
 		uint64_t lastPingCheck = 0;
-		uint64_t lastTopologyClean = Utils::now(); // don't need to do this immediately
+		uint64_t lastClean = Utils::now(); // don't need to do this immediately
 		uint64_t lastNetworkFingerprintCheck = 0;
 		uint64_t lastAutoconfigureCheck = 0;
 		uint64_t networkConfigurationFingerprint = _r->sysEnv->getNetworkConfigurationFingerprint();
 		uint64_t lastMulticastCheck = 0;
 		uint64_t lastMulticastAnnounceAll = 0;
-		uint64_t lastStatusUpdate = 0;
 		long lastDelayDelta = 0;
 
 		LOG("%s starting version %s",_r->identity.address().toString().c_str(),versionString());
@@ -292,16 +446,6 @@ Node::ReasonForTermination Node::run()
 				}
 			}
 
-			if ((now - lastAutoconfigureCheck) >= ZT_AUTOCONFIGURE_CHECK_DELAY) {
-				// It seems odd to only do this simple check every so often, but the purpose is to
-				// delay between calls to refreshConfiguration() enough that the previous attempt
-				// has time to either succeed or fail. Otherwise we'll block the whole loop, since
-				// config update is guarded by a Mutex.
-				lastAutoconfigureCheck = now;
-				if ((now - _r->nc->lastAutoconfigure()) >= ZT_AUTOCONFIGURE_INTERVAL)
-					_r->nc->refreshConfiguration(); // happens in background
-			}
-
 			// Periodically check for changes in our local multicast subscriptions and broadcast
 			// those changes to peers.
 			if ((now - lastMulticastCheck) >= ZT_MULTICAST_LOCAL_POLL_PERIOD) {
@@ -337,11 +481,9 @@ Node::ReasonForTermination Node::run()
 			if ((now - lastPingCheck) >= ZT_PING_CHECK_DELAY) {
 				lastPingCheck = now;
 				try {
-					if (_r->topology->isSupernode(_r->identity.address())) {
-						// The only difference in how supernodes behave is here: they only
-						// actively ping each other and only passively listen for pings
-						// from anyone else. They also don't send firewall openers, since
-						// they're never firewalled.
+					if (_r->topology->amSupernode()) {
+						// Supernodes do not ping anyone but each other. They also don't
+						// send firewall openers, since they aren't ever firewalled.
 						std::vector< SharedPtr<Peer> > sns(_r->topology->supernodePeers());
 						for(std::vector< SharedPtr<Peer> >::const_iterator p(sns.begin());p!=sns.end();++p) {
 							if ((now - (*p)->lastDirectSend()) > ZT_PEER_DIRECT_PING_DELAY)
@@ -384,23 +526,10 @@ Node::ReasonForTermination Node::run()
 				}
 			}
 
-			if ((now - lastTopologyClean) >= ZT_TOPOLOGY_CLEAN_PERIOD) {
-				lastTopologyClean = now;
-				_r->topology->clean(); // happens in background
-			}
-
-			if (((now - lastStatusUpdate) >= ZT_STATUS_OUTPUT_PERIOD)||(impl->updateStatusNow)) {
-				lastStatusUpdate = now;
-				impl->updateStatusNow = false;
-				FILE *statusf = ::fopen(statusPath.c_str(),"w");
-				if (statusf) {
-					try {
-						_r->topology->eachPeer(Topology::DumpPeerStatistics(statusf));
-					} catch ( ... ) {
-						TRACE("unexpected exception updating status dump");
-					}
-					::fclose(statusf);
-				}
+			if ((now - lastClean) >= ZT_DB_CLEAN_PERIOD) {
+				lastClean = now;
+				_r->topology->clean();
+				_r->nc->cleanAllNetworks();
 			}
 
 			try {
@@ -436,13 +565,6 @@ void Node::terminate()
 	((_NodeImpl *)_impl)->renv.mainLoopWaitCondition.signal();
 }
 
-void Node::updateStatusNow()
-	throw()
-{
-	((_NodeImpl *)_impl)->updateStatusNow = true;
-	((_NodeImpl *)_impl)->renv.mainLoopWaitCondition.signal();
-}
-
 class _VersionStringMaker
 {
 public:

+ 43 - 10
node/Node.hpp

@@ -40,6 +40,44 @@ namespace ZeroTier {
 class Node
 {
 public:
+	/**
+	 * Client for controlling a local ZeroTier One node
+	 */
+	class LocalClient
+	{
+	public:
+		/**
+		 * Create a new node config client
+		 *
+		 * @param authToken Authentication token
+		 * @param resultHandler Function to call when commands provide results
+		 */
+		LocalClient(const char *authToken,void (*resultHandler)(void *,unsigned long,const char *),void *arg)
+			throw();
+
+		~LocalClient();
+
+		/**
+		 * Send a command to the local node
+		 *
+		 * Note that the returned conversation ID will never be 0. A return value
+		 * of 0 indicates a fatal error such as failure to bind to any local UDP
+		 * port.
+		 *
+		 * @param command
+		 * @return Conversation ID that will be provided to result handler when/if results are sent back
+		 */
+		unsigned long send(const char *command)
+			throw();
+
+	private:
+		// LocalClient is not copyable
+		LocalClient(const LocalClient&);
+		const LocalClient& operator=(const LocalClient&);
+
+		void *_impl;
+	};
+
 	/**
 	 * Returned by node main if/when it terminates
 	 */
@@ -58,11 +96,8 @@ public:
 	 * The node is not executed until run() is called.
 	 *
 	 * @param hp Home directory path
-	 * @param url URL prefix for autoconfiguration (http and file permitted)
-	 * @param configAuthorityIdentity Public identity used to encrypt/authenticate configuration from this URL (ASCII string format)
-	 * @throws std::invalid_argument Invalid argument supplied to constructor
 	 */
-	Node(const char *hp,const char *urlPrefix,const char *configAuthorityIdentity)
+	Node(const char *hp)
 		throw();
 
 	~Node();
@@ -98,12 +133,6 @@ public:
 	void terminate()
 		throw();
 
-	/**
-	 * Update the status file in the home directory on next service loop
-	 */
-	void updateStatusNow()
-		throw();
-
 	/**
 	 * Get the ZeroTier version in major.minor.revision string format
 	 * 
@@ -117,6 +146,10 @@ public:
 	static unsigned int versionRevision() throw();
 
 private:
+	// Nodes are not copyable
+	Node(const Node&);
+	const Node& operator=(const Node&);
+
 	void *const _impl; // private implementation
 };
 

+ 179 - 134
node/NodeConfig.cpp

@@ -27,180 +27,225 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
 #include <memory>
 #include <string>
 
-#include <json/json.h>
+#include <openssl/sha.h>
 
 #include "NodeConfig.hpp"
 #include "RuntimeEnvironment.hpp"
 #include "Defaults.hpp"
 #include "Utils.hpp"
 #include "Logger.hpp"
+#include "Topology.hpp"
+#include "Demarc.hpp"
+#include "InetAddress.hpp"
+#include "Peer.hpp"
+#include "Salsa20.hpp"
+#include "HMAC.hpp"
 
 namespace ZeroTier {
 
-NodeConfig::NodeConfig(const RuntimeEnvironment *renv,const std::string &url) :
+NodeConfig::NodeConfig(const RuntimeEnvironment *renv,const char *authToken)
+	throw(std::runtime_error) :
 	_r(renv),
-	_lastAutoconfigure(0),
-	_lastAutoconfigureLastModified(),
-	_url(url),
-	_autoconfigureLock(),
-	_networks(),
-	_networks_m()
+	_controlSocket(true,ZT_CONTROL_UDP_PORT,false,&_CBcontrolPacketHandler,this)
 {
+	SHA256_CTX sha;
+	SHA256_Init(&sha);
+	SHA256_Update(&sha,authToken,strlen(authToken));
+	SHA256_Final(_controlSocketKey,&sha);
 }
 
 NodeConfig::~NodeConfig()
 {
-	_autoconfigureLock.lock(); // wait for any autoconfs to finish
-	_autoconfigureLock.unlock();
 }
 
-void NodeConfig::refreshConfiguration()
+void NodeConfig::whackAllTaps()
+{
+	std::vector< SharedPtr<Network> > nwlist;
+	Mutex::Lock _l(_networks_m);
+	for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
+		n->second->tap().whack();
+}
+
+void NodeConfig::cleanAllNetworks()
+{
+	Mutex::Lock _l(_networks_m);
+	for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
+		n->second->clean();
+}
+
+// Macro used in execute()
+#undef _P
+#define _P(f,...) { r.push_back(std::string()); Utils::stdsprintf(r.back(),(f),##__VA_ARGS__); }
+
+// Used with Topology::eachPeer to dump peer stats
+class _DumpPeerStatistics
 {
-	_autoconfigureLock.lock(); // unlocked when handler gets called
+public:
+	_DumpPeerStatistics(std::vector<std::string> &out) :
+		r(out),
+		_now(Utils::now())
+	{
+	}
+
+	inline void operator()(Topology &t,const SharedPtr<Peer> &p)
+	{
+		InetAddress v4(p->ipv4ActivePath(_now));
+		InetAddress v6(p->ipv6ActivePath(_now));
+		_P("200 listpeers %s %s %s %u",
+			p->address().toString().c_str(),
+			((v4) ? v4.toString().c_str() : "(none)"),
+			((v6) ? v6.toString().c_str() : "(none)"),
+			(((v4)||(v6)) ? p->latency() : 0));
+	}
 
-	TRACE("refreshing autoconfigure URL %s (if modified since: '%s')",_url.c_str(),_lastAutoconfigureLastModified.c_str());
+private:
+	std::vector<std::string> &r;
+	uint64_t _now;
+};
 
-	std::map<std::string,std::string> reqHeaders;
-	reqHeaders["X-ZT-ID"] = _r->identity.toString(false);
-	reqHeaders["X-ZT-OVSH"] = _r->ownershipVerificationSecretHash;
-	if (_lastAutoconfigureLastModified.length())
-		reqHeaders["If-Modified-Since"] = _lastAutoconfigureLastModified;
+std::vector<std::string> NodeConfig::execute(const char *command)
+{
+	std::vector<std::string> r;
+	std::vector<std::string> cmd(Utils::split(command,"\r\n \t","\\","'"));
+
+	//
+	// Not coincidentally, response type codes correspond with HTTP
+	// status codes.
+	//
+
+	if ((cmd.empty())||(cmd[0] == "help")) {
+		_P("200 help help");
+		_P("200 help listpeers");
+		_P("200 help listnetworks");
+		_P("200 help join <network ID> [<network invitation code>]");
+		_P("200 help leave <network ID>");
+	} else if (cmd[0] == "listpeers") {
+		_r->topology->eachPeer(_DumpPeerStatistics(r));
+	} else if (cmd[0] == "listnetworks") {
+		Mutex::Lock _l(_networks_m);
+		for(std::map< uint64_t,SharedPtr<Network> >::const_iterator nw(_networks.begin());nw!=_networks.end();++nw) {
+			_P("200 listnetworks %llu %s %s",
+				nw->first,
+				nw->second->tap().deviceName().c_str(),
+				(nw->second->isOpen() ? "public" : "private"));
+		}
+	} else if (cmd[0] == "join") {
+		_P("404 join Not implemented yet.");
+	} else if (cmd[0] == "leave") {
+		_P("404 leave Not implemented yet.");
+	} else {
+		_P("404 %s No such command. Use 'help' for help.",cmd[0].c_str());
+	}
 
-	new Http::Request(Http::HTTP_METHOD_GET,_url,reqHeaders,std::string(),&NodeConfig::_CBautoconfHandler,this);
+	return r;
 }
 
-void NodeConfig::__CBautoconfHandler(const std::string &lastModified,const std::string &body)
+std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > NodeConfig::encodeControlMessage(const void *key,unsigned long conversationId,const std::vector<std::string> &payload)
+	throw(std::out_of_range)
 {
-	try {
-		Json::Value root;
-		Json::Reader reader;
+	char hmac[32];
+	char keytmp[32];
+	std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > packets;
+	Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> packet;
 
-		std::string dec(_r->identity.decrypt(_r->configAuthority,body.data(),body.length()));
-		if (!dec.length()) {
-			LOG("autoconfigure from %s failed: data did not decrypt as from config authority %s",_url.c_str(),_r->configAuthority.address().toString().c_str());
-			return;
-		}
-		TRACE("decrypted autoconf: %s",dec.c_str());
+	packet.setSize(16); // HMAC and IV
+	packet.append((uint32_t)(conversationId & 0xffffffff));
+	for(unsigned int i=0;i<payload.size();++i) {
+		packet.append(payload[i]); // will throw if too big
+		packet.append((unsigned char)0);
 
-		if (!reader.parse(dec,root,false)) {
-			LOG("autoconfigure from %s failed: JSON parse error: %s",_url.c_str(),reader.getFormattedErrorMessages().c_str());
-			return;
-		}
+		if (((i + 1) >= payload.size())||((packet.size() + payload[i + 1].length() + 1) >= packet.capacity())) {
+			Utils::getSecureRandom(packet.field(8,8),8);
 
-		if (!root.isObject()) {
-			LOG("autoconfigure from %s failed: not a JSON object",_url.c_str());
-			return;
+			Salsa20 s20(key,256,packet.field(8,8));
+			s20.encrypt(packet.field(16,packet.size() - 16),packet.field(16,packet.size() - 16),packet.size() - 16);
+
+			memcpy(keytmp,key,32);
+			for(unsigned int i=0;i<32;++i)
+				keytmp[i] ^= 0x77; // use a different permutation of key for HMAC than for Salsa20
+			HMAC::sha256(keytmp,32,packet.field(16,packet.size() - 16),packet.size() - 16,hmac);
+			memcpy(packet.field(0,8),hmac,8);
+
+			packets.push_back(packet);
+
+			packet.setSize(16); // HMAC and IV
+			packet.append((uint32_t)(conversationId & 0xffffffff));
 		}
+	}
 
-		// Configure networks
-		const Json::Value &networks = root["_networks"];
-		if (networks.isArray()) {
-			Mutex::Lock _l(_networks_m);
-			for(unsigned int ni=0;ni<networks.size();++ni) {
-				if (networks[ni].isObject()) {
-					const Json::Value &nwid_ = networks[ni]["id"];
-					uint64_t nwid = nwid_.isNumeric() ? (uint64_t)nwid_.asUInt64() : (uint64_t)strtoull(networks[ni]["id"].asString().c_str(),(char **)0,10);
-
-					if (nwid) {
-						SharedPtr<Network> nw;
-						std::map< uint64_t,SharedPtr<Network> >::iterator nwent(_networks.find(nwid));
-						if (nwent != _networks.end())
-							nw = nwent->second;
-						else {
-							try {
-								nw = SharedPtr<Network>(new Network(_r,nwid));
-								_networks[nwid] = nw;
-							} catch (std::exception &exc) {
-								LOG("unable to create network %llu: %s",nwid,exc.what());
-							} catch ( ... ) {
-								LOG("unable to create network %llu: unknown exception",nwid);
-							}
-						}
-
-						if (nw) {
-							Mutex::Lock _l2(nw->_lock);
-							nw->_open = networks[ni]["isOpen"].asBool();
-
-							// Ensure that TAP device has all the right IP addresses
-							// TODO: IPv6 might work a tad differently
-							std::set<InetAddress> allIps;
-							const Json::Value &addresses = networks[ni]["_addresses"];
-							if (addresses.isArray()) {
-								for(unsigned int ai=0;ai<addresses.size();++ai) {
-									if (addresses[ai].isString()) {
-										InetAddress addr(addresses[ai].asString());
-										if (addr) {
-											TRACE("network %llu IP/netmask: %s",nwid,addr.toString().c_str());
-											allIps.insert(addr);
-										}
-									}
-								}
-							}
-							nw->_tap.setIps(allIps);
-
-							// NOTE: the _members field is optional for open networks,
-							// since members of open nets do not need to check membership
-							// of packet senders and mutlicasters.
-							const Json::Value &members = networks[ni]["_members"];
-							nw->_members.clear();
-							if (members.isArray()) {
-								for(unsigned int mi=0;mi<members.size();++mi) {
-									std::string rawAddr(Utils::unhex(members[mi].asString()));
-									if (rawAddr.length() == ZT_ADDRESS_LENGTH) {
-										Address addr(rawAddr.data());
-										if ((addr)&&(!addr.isReserved())) {
-											//TRACE("network %llu member: %s",nwid,addr.toString().c_str());
-											nw->_members.insert(addr);
-										}
-									}
-								}
-							}
-						}
-					} else {
-						TRACE("ignored networks[%u], 'id' field missing");
-					}
-				} else {
-					TRACE("ignored networks[%u], not a JSON object",ni);
-				}
-			}
+	return packets;
+}
+
+bool NodeConfig::decodeControlMessagePacket(const void *key,const void *data,unsigned int len,unsigned long &conversationId,std::vector<std::string> &payload)
+{
+	char hmac[32];
+	char keytmp[32];
+
+	try {
+		if (len < 20)
+			return false;
+
+		Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> packet(data,len);
+
+		memcpy(keytmp,key,32);
+		for(unsigned int i=0;i<32;++i)
+			keytmp[i] ^= 0x77; // use a different permutation of key for HMAC than for Salsa20
+		HMAC::sha256(keytmp,32,packet.field(16,packet.size() - 16),packet.size() - 16,hmac);
+		if (memcmp(packet.field(0,8),hmac,8))
+			return false;
+
+		Salsa20 s20(key,256,packet.field(8,8));
+		s20.decrypt(packet.field(16,packet.size() - 16),packet.field(16,packet.size() - 16),packet.size() - 16);
+
+		conversationId = packet.at<uint32_t>(16);
+
+		const char *pl = ((const char *)packet.data()) + 20;
+		unsigned int pll = packet.size() - 20;
+		for(unsigned int i=0;i<pll;) {
+			unsigned int eos = i;
+			while ((eos < pll)&&(pl[eos]))
+				++eos;
+			if (eos > i) {
+				payload.push_back(std::string(pl + i,eos - i));
+				i = eos + 1;
+			} else break;
 		}
 
-		_lastAutoconfigure = Utils::now();
-		_lastAutoconfigureLastModified = lastModified;
-	} catch (std::exception &exc) {
-		TRACE("exception parsing autoconf URL response: %s",exc.what());
+		return true;
 	} catch ( ... ) {
-		TRACE("unexpected exception parsing autoconf URL response");
+		return false;
 	}
 }
 
-bool NodeConfig::_CBautoconfHandler(Http::Request *req,void *arg,const std::string &url,int code,const std::map<std::string,std::string> &headers,const std::string &body)
+void NodeConfig::_CBcontrolPacketHandler(UdpSocket *sock,void *arg,const InetAddress &remoteAddr,const void *data,unsigned int len)
 {
-#ifdef ZT_TRACE
-	const RuntimeEnvironment *_r = ((NodeConfig *)arg)->_r;
-#endif
-
-	if (code == 200) {
-		TRACE("200 got autoconfigure response from %s: %u bytes",url.c_str(),(unsigned int)body.length());
-
-		std::map<std::string,std::string>::const_iterator lm(headers.find("Last-Modified"));
-		if (lm != headers.end())
-			((NodeConfig *)arg)->__CBautoconfHandler(lm->second,body);
-		else ((NodeConfig *)arg)->__CBautoconfHandler(std::string(),body);
-	} else if (code == 304) {
-		TRACE("304 autoconfigure deferred, remote URL %s not modified",url.c_str());
-		((NodeConfig *)arg)->_lastAutoconfigure = Utils::now(); // still considered a success
-	} else if (code == 409) { // conflict, ID address in use by another ID
-		TRACE("%d autoconfigure failed from %s",code,url.c_str());
-	} else {
-		TRACE("%d autoconfigure failed from %s",code,url.c_str());
-	}
+	NodeConfig *nc = (NodeConfig *)arg;
+	const RuntimeEnvironment *_r = nc->_r;
+
+	try {
+		unsigned long convId = 0;
+		std::vector<std::string> commands;
+
+		if (!decodeControlMessagePacket(nc->_controlSocketKey,data,len,convId,commands)) {
+			TRACE("control bus packet from %s failed decode, discarded",remoteAddr.toString().c_str());
+			return;
+		}
+		TRACE("control bus packet from %s, contains %d commands",remoteAddr.toString().c_str(),(int)commands.size());
 
-	((NodeConfig *)arg)->_autoconfigureLock.unlock();
-	return false; // causes Request to delete itself
+		for(std::vector<std::string>::iterator c(commands.begin());c!=commands.end();++c) {
+			std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > resultPackets(encodeControlMessage(nc->_controlSocketKey,convId,nc->execute(c->c_str())));
+			for(std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> >::iterator p(resultPackets.begin());p!=resultPackets.end();++p)
+				sock->send(remoteAddr,p->data(),p->size(),-1);
+		}
+	} catch ( ... ) {
+		TRACE("exception handling control bus packet from %s",remoteAddr.toString().c_str());
+	}
 }
 
 } // namespace ZeroTier

+ 65 - 25
node/NodeConfig.hpp

@@ -31,27 +31,51 @@
 #include <map>
 #include <set>
 #include <string>
+#include <vector>
+#include <stdexcept>
+
 #include <stdint.h>
 #include "SharedPtr.hpp"
 #include "Network.hpp"
 #include "Utils.hpp"
-#include "Http.hpp"
+#include "UdpSocket.hpp"
+#include "Buffer.hpp"
 
 namespace ZeroTier {
 
 class RuntimeEnvironment;
 
 /**
- * Node configuration holder and fetcher
+ * Maximum size of a packet for node configuration
+ */
+#define ZT_NODECONFIG_MAX_PACKET_SIZE 4096
+
+/**
+ * Node configuration endpoint
+ *
+ * Packet format for local UDP configuration packets:
+ *  [16] first 16 bytes of HMAC-SHA-256 of payload
+ *  [ -- begin HMAC'ed envelope -- ]
+ *  [8] random initialization vector
+ *  [ -- begin cryptographic envelope -- ]
+ *  [4] arbitrary tag, echoed in response
+ *  [...] payload
+ *
+ * For requests, the payload consists of a single ASCII command. For
+ * responses, the payload consists of one or more response lines delimited
+ * by NULL (0) characters. The tag field is replicated in the result
+ * packet.
  */
 class NodeConfig
 {
 public:
 	/**
 	 * @param renv Runtime environment
-	 * @param url Autoconfiguration URL (http:// or file://)
+	 * @param authToken Configuration authentication token
+	 * @throws std::runtime_error Unable to bind to local control port
 	 */
-	NodeConfig(const RuntimeEnvironment *renv,const std::string &url);
+	NodeConfig(const RuntimeEnvironment *renv,const char *authToken)
+		throw(std::runtime_error);
 
 	~NodeConfig();
 
@@ -81,13 +105,12 @@ public:
 	/**
 	 * Call whack() on all networks' tap devices
 	 */
-	inline void whackAllTaps()
-	{
-		std::vector< SharedPtr<Network> > nwlist;
-		Mutex::Lock _l(_networks_m);
-		for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
-			n->second->tap().whack();
-	}
+	void whackAllTaps();
+
+	/**
+	 * Call clean() on all networks
+	 */
+	void cleanAllNetworks();
 
 	/**
 	 * @param nwid Network ID
@@ -112,32 +135,49 @@ public:
 	}
 
 	/**
-	 * @return Time of last successful autoconfigure or refresh
+	 * Execute a command
+	 *
+	 * @param command Command and arguments separated by whitespace (must already be trimmed of CR+LF, etc.)
+	 * @return One or more command results (lines of output)
 	 */
-	inline uint64_t lastAutoconfigure() const { return _lastAutoconfigure; }
+	std::vector<std::string> execute(const char *command);
 
 	/**
-	 * @return Autoconfiguration URL
+	 * Armor payload for control bus
+	 *
+	 * Note that no single element of payload can be longer than the max packet
+	 * size. If this occurs out_of_range is thrown.
+	 *
+	 * @param key 32 byte key
+	 * @param conversationId 32-bit conversation ID (bits beyond 32 are ignored)
+	 * @param payload One or more strings to encode in packet
+	 * @return One or more transport armored packets (if payload too big)
+	 * @throws std::out_of_range An element of payload is too big
 	 */
-	inline const std::string &url() const { return _url; }
+	static std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > encodeControlMessage(const void *key,unsigned long conversationId,const std::vector<std::string> &payload)
+		throw(std::out_of_range);
 
 	/**
-	 * Refresh configuration from autoconf URL
+	 * Decode a packet from the control bus
+	 *
+	 * Note that 'payload' is appended to. Existing data is not cleared.
+	 *
+	 * @param key 32 byte key
+	 * @param data Packet data
+	 * @param len Packet length
+	 * @param conversationId Result parameter filled with conversation ID on success
+	 * @param payload Result parameter to which results are appended
+	 * @return True on success, false on invalid packet or packet that failed authentication
 	 */
-	void refreshConfiguration();
+	static bool decodeControlMessagePacket(const void *key,const void *data,unsigned int len,unsigned long &conversationId,std::vector<std::string> &payload);
 
 private:
-	void __CBautoconfHandler(const std::string &lastModified,const std::string &body);
-	static bool _CBautoconfHandler(Http::Request *req,void *arg,const std::string &url,int code,const std::map<std::string,std::string> &headers,const std::string &body);
+	static void _CBcontrolPacketHandler(UdpSocket *sock,void *arg,const InetAddress &remoteAddr,const void *data,unsigned int len);
 
 	const RuntimeEnvironment *_r;
 
-	volatile uint64_t _lastAutoconfigure;
-
-	std::string _lastAutoconfigureLastModified;
-	std::string _url;
-	Mutex _autoconfigureLock;
-
+	unsigned char _controlSocketKey[32];
+	UdpSocket _controlSocket;
 	std::map< uint64_t,SharedPtr<Network> > _networks;
 	Mutex _networks_m;
 };

+ 0 - 159
node/Pack.cpp

@@ -1,159 +0,0 @@
-/*
- * ZeroTier One - Global Peer to Peer Ethernet
- * Copyright (C) 2012-2013  ZeroTier Networks LLC
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-#include <iostream>
-#include <string.h>
-#include <stdlib.h>
-#include "Pack.hpp"
-#include "BlobArray.hpp"
-#include "Utils.hpp"
-
-#include <openssl/sha.h>
-
-namespace ZeroTier {
-
-std::vector<const Pack::Entry *> Pack::getAll() const
-{
-	std::vector<const Entry *> v;
-	for(std::map<std::string,Entry>::const_iterator e=_entries.begin();e!=_entries.end();++e)
-		v.push_back(&(e->second));
-	return v;
-}
-
-const Pack::Entry *Pack::get(const std::string &name) const
-{
-	std::map<std::string,Entry>::const_iterator e(_entries.find(name));
-	return ((e == _entries.end()) ? (const Entry *)0 : &(e->second));
-}
-
-const Pack::Entry *Pack::put(const std::string &name,const std::string &content)
-{
-	SHA256_CTX sha;
-
-	Pack::Entry &e = _entries[name];
-	e.name = name;
-	e.content = content;
-
-	SHA256_Init(&sha);
-	SHA256_Update(&sha,content.data(),content.length());
-	SHA256_Final(e.sha256,&sha);
-
-	e.signedBy.zero();
-	e.signature.assign((const char *)0,0);
-
-	return &e;
-}
-
-void Pack::clear()
-{
-	_entries.clear();
-}
-
-std::string Pack::serialize() const
-{
-	BlobArray archive;
-	for(std::map<std::string,Entry>::const_iterator e=_entries.begin();e!=_entries.end();++e) {
-		BlobArray entry;
-		entry.push_back(e->second.name);
-		entry.push_back(e->second.content);
-		entry.push_back(std::string((const char *)e->second.sha256,sizeof(e->second.sha256)));
-		entry.push_back(std::string((const char *)e->second.signedBy.data(),e->second.signedBy.size()));
-		entry.push_back(e->second.signature);
-		archive.push_back(entry.serialize());
-	}
-
-	std::string ser(archive.serialize());
-	std::string comp;
-	Utils::compress(ser.begin(),ser.end(),Utils::StringAppendOutput(comp));
-	return comp;
-}
-
-bool Pack::deserialize(const void *sd,unsigned int sdlen)
-{
-	unsigned char dig[32];
-	SHA256_CTX sha;
-
-	std::string decomp;
-	if (!Utils::decompress(((const char *)sd),((const char *)sd) + sdlen,Utils::StringAppendOutput(decomp)))
-		return false;
-
-	BlobArray archive;
-	archive.deserialize(decomp.data(),decomp.length());
-	clear();
-	for(BlobArray::const_iterator i=archive.begin();i!=archive.end();++i) {
-		BlobArray entry;
-		entry.deserialize(i->data(),i->length());
-
-		if (entry.size() != 5) return false;
-		if (entry[2].length() != 32) return false; // SHA-256
-		if (entry[3].length() != ZT_ADDRESS_LENGTH) return false; // Address
-
-		Pack::Entry &e = _entries[entry[0]];
-		e.name = entry[0];
-		e.content = entry[1];
-
-		SHA256_Init(&sha);
-		SHA256_Update(&sha,e.content.data(),e.content.length());
-		SHA256_Final(dig,&sha);
-		if (memcmp(dig,entry[2].data(),32)) return false; // integrity check failed
-		memcpy(e.sha256,dig,32);
-
-		if (entry[3].length() == ZT_ADDRESS_LENGTH)
-			e.signedBy = entry[3].data();
-		else e.signedBy.zero();
-		e.signature = entry[4];
-	}
-	return true;
-}
-
-bool Pack::signAll(const Identity &id)
-{
-	for(std::map<std::string,Entry>::iterator e=_entries.begin();e!=_entries.end();++e) {
-		e->second.signedBy = id.address();
-		e->second.signature = id.sign(e->second.sha256);
-		if (!e->second.signature.length())
-			return false;
-	}
-	return true;
-}
-
-std::vector<const Pack::Entry *> Pack::verifyAll(const Identity &id,bool mandatory) const
-{
-	std::vector<const Entry *> bad;
-	for(std::map<std::string,Entry>::const_iterator e=_entries.begin();e!=_entries.end();++e) {
-		if ((e->second.signedBy)&&(e->second.signature.length())) {
-			if (id.address() != e->second.signedBy)
-				bad.push_back(&(e->second));
-			else if (!id.verifySignature(e->second.sha256,e->second.signature.data(),e->second.signature.length()))
-				bad.push_back(&(e->second));
-		} else if (mandatory)
-			bad.push_back(&(e->second));
-	}
-	return bad;
-}
-
-} // namespace ZeroTier

+ 0 - 141
node/Pack.hpp

@@ -1,141 +0,0 @@
-/*
- * ZeroTier One - Global Peer to Peer Ethernet
- * Copyright (C) 2012-2013  ZeroTier Networks LLC
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-#ifndef _ZT_PACK_HPP
-#define _ZT_PACK_HPP
-
-#include <string>
-#include <map>
-#include <list>
-#include <stdexcept>
-#include "Address.hpp"
-#include "Identity.hpp"
-
-namespace ZeroTier {
-
-/**
- * A very simple archive format for distributing packs of files or resources
- * 
- * This is used for things like the auto-updater. It's not suitable for huge
- * files, since at present it must work in memory. Packs support signing with
- * identities and signature verification.
- */
-class Pack
-{
-public:
-	/**
-	 * Pack entry structure for looking up deserialized entries
-	 */
-	struct Entry
-	{
-		std::string name;
-		std::string content;
-		unsigned char sha256[32];
-		Address signedBy;
-		std::string signature;
-	};
-
-	Pack() {}
-	~Pack() {}
-
-	/**
-	 * @return Vector of all entries
-	 */
-	std::vector<const Entry *> getAll() const;
-
-	/**
-	 * Look up an entry
-	 * 
-	 * @param name Name to look up
-	 * @return Pointer to entry if it exists or NULL if not found
-	 */
-	const Entry *get(const std::string &name) const;
-
-	/**
-	 * Add an entry to this pack
-	 * 
-	 * @param name Entry to add
-	 * @param content Entry's contents
-	 * @return The new entry
-	 */
-	const Entry *put(const std::string &name,const std::string &content);
-
-	/**
-	 * Remove all entries
-	 */
-	void clear();
-
-	/**
-	 * @return Number of entries in pack
-	 */
-	inline unsigned int numEntries() const { return (unsigned int)_entries.size(); }
-
-	/**
-	 * Serialize this pack
-	 * 
-	 * @return Serialized form (compressed with LZ4)
-	 */
-	std::string serialize() const;
-
-	/**
-	 * Deserialize this pack
-	 * 
-	 * Any current contents are lost. This does not verify signatures,
-	 * but does check SHA256 hashes for entry integrity. If the return
-	 * value is false, the pack's contents are undefined.
-	 * 
-	 * @param sd Serialized data
-	 * @param sdlen Length of serialized data
-	 * @return True on success, false on deserialization error
-	 */
-	bool deserialize(const void *sd,unsigned int sdlen);
-	inline bool deserialize(const std::string &sd) { return deserialize(sd.data(),sd.length()); }
-
-	/**
-	 * Sign all entries in this pack with a given identity
-	 * 
-	 * @param id Identity to sign with
-	 * @return True on signature success, false if error
-	 */
-	bool signAll(const Identity &id);
-
-	/**
-	 * Verify all signed entries
-	 * 
-	 * @param id Identity to verify against
-	 * @param mandatory If true, require that all entries be signed and fail if no signature
-	 * @return Vector of entries that failed verification or empty vector if all passed
-	 */
-	std::vector<const Entry *> verifyAll(const Identity &id,bool mandatory) const;
-
-private:
-	std::map<std::string,Entry> _entries;
-};
-
-} // namespace ZeroTier
-
-#endif

+ 4 - 0
node/Packet.cpp

@@ -42,6 +42,9 @@ const char *Packet::verbString(Verb v)
 		case VERB_FRAME: return "FRAME";
 		case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
 		case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
+		case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE";
+		case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST";
+		case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH";
 	}
 	return "(unknown)";
 }
@@ -57,6 +60,7 @@ const char *Packet::errorString(ErrorCode e)
 		case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION";
 		case ERROR_IDENTITY_INVALID: return "IDENTITY_INVALID";
 		case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION";
+		case ERROR_NO_MEMBER_CERTIFICATE: return "NO_MEMBER_CERTIFICATE";
 	}
 	return "(unknown)";
 }

+ 72 - 6
node/Packet.hpp

@@ -132,27 +132,34 @@
 #define ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE_BYTES 64
 
 // Field incides for parsing verbs
+
 #define ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION (ZT_PACKET_IDX_PAYLOAD)
 #define ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION (ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION + 1)
 #define ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION (ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION + 1)
 #define ZT_PROTO_VERB_HELLO_IDX_REVISION (ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION + 1)
 #define ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP (ZT_PROTO_VERB_HELLO_IDX_REVISION + 2)
 #define ZT_PROTO_VERB_HELLO_IDX_IDENTITY (ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP + 8)
+
 #define ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB (ZT_PACKET_IDX_PAYLOAD)
 #define ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID (ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB + 1)
 #define ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE (ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID + 8)
 #define ZT_PROTO_VERB_ERROR_IDX_PAYLOAD (ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE + 1)
+
 #define ZT_PROTO_VERB_OK_IDX_IN_RE_VERB (ZT_PACKET_IDX_PAYLOAD)
 #define ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID (ZT_PROTO_VERB_OK_IDX_IN_RE_VERB + 1)
 #define ZT_PROTO_VERB_OK_IDX_PAYLOAD (ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID + 8)
+
 #define ZT_PROTO_VERB_WHOIS_IDX_ZTADDRESS (ZT_PACKET_IDX_PAYLOAD)
+
 #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS (ZT_PACKET_IDX_PAYLOAD)
 #define ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT (ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS + 5)
 #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN (ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT + 2)
 #define ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS (ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN + 1)
+
 #define ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
 #define ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE (ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID + 8)
 #define ZT_PROTO_VERB_FRAME_IDX_PAYLOAD (ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE + 2)
+
 #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS (ZT_PACKET_IDX_PAYLOAD)
 #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS + 1)
 #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SUBMITTER_ADDRESS (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID + 8)
@@ -166,6 +173,12 @@
 #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SIGNATURE_LENGTH (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD_LENGTH + 2)
 #define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SIGNATURE_LENGTH + 2)
 
+#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
+#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID + 8)
+#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN + 2)
+
+#define ZT_PROTO_VERB_NETWORK_CONFIG_REFRESH_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
+
 // Field indices for parsing OK and ERROR payloads of replies
 #define ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP (ZT_PROTO_VERB_OK_IDX_PAYLOAD)
 #define ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY (ZT_PROTO_VERB_OK_IDX_PAYLOAD)
@@ -287,7 +300,7 @@ public:
 		 * 
 		 * @return Destination ZT address
 		 */
-		inline Address destination() const { return Address(field(ZT_PACKET_FRAGMENT_IDX_DEST,ZT_ADDRESS_LENGTH)); }
+		inline Address destination() const { return Address(field(ZT_PACKET_FRAGMENT_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); }
 
 		/**
 		 * @return True if fragment is of a valid length
@@ -449,7 +462,7 @@ public:
 		 *   <[2] 16-bit length of payload>
 		 *   <[2] 16-bit length of signature>
 		 *   <[...] ethernet payload>
-		 *   <[...] ECDSA signature>
+		 *   <[...] ECDSA signature of SHA-256 hash (see below)>
 		 *
 		 * The signature is made using the key of the original submitter, and
 		 * can be used to authenticate the submitter for security and rate
@@ -463,7 +476,57 @@ public:
 		 *
 		 * No OK or ERROR is generated.
 		 */
-		VERB_MULTICAST_FRAME = 9
+		VERB_MULTICAST_FRAME = 9,
+
+		/* Network member certificate for sending peer:
+		 *   <[8] 64-bit network ID>
+		 *   <[2] 16-bit length of certificate>
+		 *   <[2] 16-bit length of signature>
+		 *   <[...] string-serialized certificate dictionary>
+		 *   <[...] ECDSA signature of certificate>
+		 *
+		 * OK is generated on acceptance. ERROR is returned on failure. In both
+		 * cases the payload is the network ID.
+		 */
+		VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10,
+
+		/* Network configuration request:
+		 *   <[8] 64-bit network ID>
+		 *   <[2] 16-bit length of request meta-data dictionary>
+		 *   <[...] string-serialized request meta-data>
+		 *
+		 * This message requests network configuration from a node capable of
+		 * providing it. Such nodes run the netconf service, which must be
+		 * installed into the ZeroTier home directory.
+		 *
+		 * OK response payload:
+		 *   <[8] 64-bit network ID>
+		 *   <[2] 16-bit length of network configuration dictionary>
+		 *   <[...] network configuration dictionary>
+		 *
+		 * OK returns a Dictionary (string serialized) containing the network's
+		 * configuration and IP address assignment information for the querying
+		 * node. It also contains a membership certificate that the querying
+		 * node can push to other peers to demonstrate its right to speak on
+		 * a given network.
+		 *
+		 * ERROR may be NOT_FOUND if no such network is known, or
+		 * UNSUPPORTED_OPERATION if the netconf service isn't available. The
+		 * payload will be the network ID.
+		 */
+		VERB_NETWORK_CONFIG_REQUEST = 11,
+
+		/* Network configuration refresh request:
+		 *   <[8] 64-bit network ID>
+		 *
+		 * This message can be sent by the network configuration master node
+		 * to request that nodes refresh their network configuration. It can
+		 * thus be used to "push" updates.
+		 *
+		 * It does not generate an OK or ERROR message, and is treated only as
+		 * a hint to refresh now.
+		 */
+		VERB_NETWORK_CONFIG_REFRESH = 12
 	};
 
 	/**
@@ -490,7 +553,10 @@ public:
 		ERROR_IDENTITY_INVALID = 5,
 
 		/* Verb or use case not supported/enabled by this node */
-		ERROR_UNSUPPORTED_OPERATION = 6
+		ERROR_UNSUPPORTED_OPERATION = 6,
+
+		/* Message to private network rejected -- no unexpired certificate on file */
+		ERROR_NO_MEMBER_CERTIFICATE = 7
 	};
 
 	/**
@@ -603,14 +669,14 @@ public:
 	 * 
 	 * @return Destination ZT address
 	 */
-	inline Address destination() const { return Address(field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH)); }
+	inline Address destination() const { return Address(field(ZT_PACKET_IDX_DEST,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); }
 
 	/**
 	 * Get this packet's source
 	 * 
 	 * @return Source ZT address
 	 */
-	inline Address source() const { return Address(field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH)); }
+	inline Address source() const { return Address(field(ZT_PACKET_IDX_SOURCE,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); }
 
 	/**
 	 * @return True if packet is of valid length

+ 88 - 9
node/PacketDecoder.cpp

@@ -25,6 +25,7 @@
  * LLC. Start here: http://www.zerotier.com/
  */
 
+#include "Constants.hpp"
 #include "RuntimeEnvironment.hpp"
 #include "Topology.hpp"
 #include "PacketDecoder.hpp"
@@ -32,6 +33,7 @@
 #include "Peer.hpp"
 #include "NodeConfig.hpp"
 #include "Filter.hpp"
+#include "Service.hpp"
 
 namespace ZeroTier {
 
@@ -102,6 +104,12 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r)
 				return _doMULTICAST_LIKE(_r,peer);
 			case Packet::VERB_MULTICAST_FRAME:
 				return _doMULTICAST_FRAME(_r,peer);
+			case Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE:
+				return _doNETWORK_MEMBERSHIP_CERTIFICATE(_r,peer);
+			case Packet::VERB_NETWORK_CONFIG_REQUEST:
+				return _doNETWORK_CONFIG_REQUEST(_r,peer);
+			case Packet::VERB_NETWORK_CONFIG_REFRESH:
+				return _doNETWORK_CONFIG_REFRESH(_r,peer);
 			default:
 				// This might be something from a new or old version of the protocol.
 				// Technically it passed HMAC so the packet is still valid, but we
@@ -305,7 +313,7 @@ bool PacketDecoder::_doOK(const RuntimeEnvironment *_r,const SharedPtr<Peer> &pe
 bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
 {
 	if (payloadLength() == ZT_ADDRESS_LENGTH) {
-		SharedPtr<Peer> p(_r->topology->getPeer(Address(payload())));
+		SharedPtr<Peer> p(_r->topology->getPeer(Address(payload(),ZT_ADDRESS_LENGTH)));
 		if (p) {
 			Packet outp(source(),_r->identity.address(),Packet::VERB_OK);
 			outp.append((unsigned char)Packet::VERB_WHOIS);
@@ -314,7 +322,7 @@ bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr<Peer>
 			outp.encrypt(peer->cryptKey());
 			outp.hmacSet(peer->macKey());
 			_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
-			TRACE("sent WHOIS response to %s for %s",source().toString().c_str(),Address(payload()).toString().c_str());
+			TRACE("sent WHOIS response to %s for %s",source().toString().c_str(),Address(payload(),ZT_ADDRESS_LENGTH).toString().c_str());
 		} else {
 			Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR);
 			outp.append((unsigned char)Packet::VERB_WHOIS);
@@ -324,7 +332,7 @@ bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr<Peer>
 			outp.encrypt(peer->cryptKey());
 			outp.hmacSet(peer->macKey());
 			_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
-			TRACE("sent WHOIS ERROR to %s for %s (not found)",source().toString().c_str(),Address(payload()).toString().c_str());
+			TRACE("sent WHOIS ERROR to %s for %s (not found)",source().toString().c_str(),Address(payload(),ZT_ADDRESS_LENGTH).toString().c_str());
 		}
 	} else {
 		TRACE("dropped WHOIS from %s(%s): missing or invalid address",source().toString().c_str(),_remoteAddress.toString().c_str());
@@ -335,7 +343,7 @@ bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr<Peer>
 bool PacketDecoder::_doRENDEZVOUS(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
 {
 	try {
-		Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH));
+		Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
 		SharedPtr<Peer> withPeer(_r->topology->getPeer(with));
 		if (withPeer) {
 			unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
@@ -433,7 +441,7 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
 			if (network->isAllowed(source())) {
 				if (size() > ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PAYLOAD) {
 
-					Address originalSubmitterAddress(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SUBMITTER_ADDRESS,ZT_ADDRESS_LENGTH));
+					Address originalSubmitterAddress(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SUBMITTER_ADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
 					MAC fromMac(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SOURCE_MAC,6));
 					MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DESTINATION_MAC,6)),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ADI));
 					unsigned int hops = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_HOP_COUNT];
@@ -450,19 +458,28 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
 						// Technically should not happen, since the original submitter is
 						// excluded from consideration as a propagation recipient.
 						TRACE("dropped boomerang MULTICAST_FRAME received from %s(%s)",source().toString().c_str(),_remoteAddress.toString().c_str());
-					} else if ((!isDuplicate)||(_r->topology->isSupernode(_r->identity.address()))) {
+					} else if ((!isDuplicate)||(_r->topology->amSupernode())) {
+						//
 						// If I am a supernode, I will repeatedly propagate duplicates. That's
 						// because supernodes are used to bridge sparse multicast groups. Non-
 						// supernodes will ignore duplicates completely.
+						//
+						// TODO: supernodes should keep a local bloom filter too and OR it with
+						// the bloom from the packet in order to pick different recipients each
+						// time a multicast returns to them for repropagation.
+						//
+
 						SharedPtr<Peer> originalSubmitter(_r->topology->getPeer(originalSubmitterAddress));
 						if (!originalSubmitter) {
 							TRACE("requesting WHOIS on original multicast frame submitter %s",originalSubmitterAddress.toString().c_str());
 							_r->sw->requestWhois(originalSubmitterAddress);
 							_step = DECODE_STEP_WAITING_FOR_ORIGINAL_SUBMITTER_LOOKUP;
-							return false;
+							return false; // try again if/when we get OK(WHOIS)
 						} else if (Multicaster::verifyMulticastPacket(originalSubmitter->identity(),network->id(),fromMac,mg,etherType,dataAndSignature,datalen,dataAndSignature + datalen,signaturelen)) {
 							_r->multicaster->addToDedupHistory(mccrc,now);
 
+							// Even if we are a supernode, we still don't repeatedly inject
+							// duplicates into our own tap.
 							if (!isDuplicate)
 								network->tap().put(fromMac,mg.mac(),etherType,dataAndSignature,datalen);
 
@@ -494,7 +511,7 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
 								compress();
 
 								for(unsigned int i=0;i<np;++i) {
-									TRACE("propagating multicast from original node %s: %s -> %s",originalSubmitterAddress.toString().c_str(),upstream.toString().c_str(),propPeers[i]->address().toString().c_str());
+									//TRACE("propagating multicast from original node %s: %s -> %s",originalSubmitterAddress.toString().c_str(),upstream.toString().c_str(),propPeers[i]->address().toString().c_str());
 									// Re-use this packet to re-send multicast frame to everyone
 									// downstream from us.
 									newInitializationVector();
@@ -504,7 +521,7 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
 
 								return true;
 							} else {
-								TRACE("terminating MULTICAST_FRAME propagation from %s(%s): max depth reached",source().toString().c_str(),_remoteAddress.toString().c_str());
+								//TRACE("terminating MULTICAST_FRAME propagation from %s(%s): max depth reached",source().toString().c_str(),_remoteAddress.toString().c_str());
 							}
 						} else {
 							LOG("rejected MULTICAST_FRAME from %s(%s) due to failed signature check (claims original sender %s)",source().toString().c_str(),_remoteAddress.toString().c_str(),originalSubmitterAddress.toString().c_str());
@@ -529,4 +546,66 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
 	return true;
 }
 
+bool PacketDecoder::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
+{
+	// TODO: not implemented yet, will be needed for private networks.
+
+	return true;
+}
+
+bool PacketDecoder::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
+{
+	char tmp[128];
+	try {
+		uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
+#ifndef __WINDOWS__
+		if (_r->netconfService) {
+			unsigned int dictLen = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN);
+			std::string dict((const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,dictLen),dictLen);
+
+			Dictionary request;
+			request["type"] = "netconf-request";
+			request["peerId"] = peer->identity().toString(false);
+			sprintf(tmp,"%llx",(unsigned long long)nwid);
+			request["nwid"] = tmp;
+			sprintf(tmp,"%llx",(unsigned long long)packetId());
+			request["requestId"] = tmp;
+			_r->netconfService->send(request);
+		} else {
+#endif // !__WINDOWS__
+			Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR);
+			outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
+			outp.append(packetId());
+			outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
+			outp.append(nwid);
+			outp.encrypt(peer->cryptKey());
+			outp.hmacSet(peer->macKey());
+			_r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
+			TRACE("sent ERROR(NETWORK_CONFIG_REQUEST,UNSUPPORTED_OPERATION) to %s(%s)",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
+#ifndef __WINDOWS__
+		}
+#endif // !__WINDOWS__
+	} catch (std::exception &exc) {
+		TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what());
+	} catch ( ... ) {
+		TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str());
+	}
+	return true;
+}
+
+bool PacketDecoder::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
+{
+	try {
+		uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REFRESH_IDX_NETWORK_ID);
+		SharedPtr<Network> nw(_r->nc->network(nwid));
+		if ((nw)&&(source() == nw->controller())) // only respond to requests from controller
+			nw->requestConfiguration();
+	} catch (std::exception &exc) {
+		TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what());
+	} catch ( ... ) {
+		TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str());
+	}
+	return true;
+}
+
 } // namespace ZeroTier

+ 3 - 0
node/PacketDecoder.hpp

@@ -122,6 +122,9 @@ private:
 	bool _doFRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
 	bool _doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
 	bool _doMULTICAST_FRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
+	bool _doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
+	bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
+	bool _doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
 
 	uint64_t _receiveTime;
 	Demarc::Port _localPort;

+ 14 - 7
node/RuntimeEnvironment.hpp

@@ -29,6 +29,7 @@
 #define _ZT_RUNTIMEENVIRONMENT_HPP
 
 #include <string>
+#include "Constants.hpp"
 #include "Identity.hpp"
 #include "Condition.hpp"
 
@@ -42,6 +43,7 @@ class Topology;
 class SysEnv;
 class Multicaster;
 class CMWC4096;
+class Service;
 
 /**
  * Holds global state for an instance of ZeroTier::Node
@@ -59,29 +61,30 @@ class RuntimeEnvironment
 {
 public:
 	RuntimeEnvironment() :
-		identity(),
 		log((Logger *)0),
 		prng((CMWC4096 *)0),
 		nc((NodeConfig *)0),
 		demarc((Demarc *)0),
 		multicaster((Multicaster *)0),
 		sw((Switch *)0),
-		topology((Topology *)0)
+		topology((Topology *)0),
+		sysEnv((SysEnv *)0)
+#ifndef __WINDOWS__
+		,netconfService((Service *)0)
+#endif
 	{
 	}
 
 	std::string homePath;
-	std::string autoconfUrlPrefix;
-	std::string configAuthorityIdentityStr;
-	std::string ownershipVerificationSecret;
-	std::string ownershipVerificationSecretHash; // base64 of SHA-256 X16 rounds
 
 	// signal() to prematurely interrupt main loop wait
 	Condition mainLoopWaitCondition;
 
-	Identity configAuthority;
 	Identity identity;
 
+	// Order matters a bit here. These are constructed in this order
+	// and then deleted in the opposite order on Node exit.
+
 	Logger *log; // may be null
 	CMWC4096 *prng;
 	NodeConfig *nc;
@@ -90,6 +93,10 @@ public:
 	Switch *sw;
 	Topology *topology;
 	SysEnv *sysEnv;
+
+#ifndef __WINDOWS__
+	Service *netconfService; // may be null
+#endif
 };
 
 } // namespace ZeroTier

+ 241 - 0
node/Service.cpp

@@ -0,0 +1,241 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013  ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include "Constants.hpp"
+
+#ifndef __WINDOWS__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+
+#include "Service.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Utils.hpp"
+#include "Logger.hpp"
+
+namespace ZeroTier {
+
+Service::Service(const RuntimeEnvironment *renv,const char *name,const char *path,void (*handler)(void *,Service &,const Dictionary &),void *arg) :
+	_r(renv),
+	_path(path),
+	_name(name),
+	_arg(arg),
+	_handler(handler),
+	_pid(-1),
+	_childStdin(0),
+	_childStdout(0),
+	_childStderr(0),
+	_run(true)
+{
+	start();
+}
+
+Service::~Service()
+{
+	_run = false;
+	long pid = _pid;
+	if (pid > 0) {
+		int st = 0;
+		::kill(pid,SIGTERM);
+		for(int i=0;i<20;++i) {
+			if (waitpid(pid,&st,WNOHANG) == pid) {
+				pid = 0;
+				break;
+			}
+			Thread::sleep(100);
+		}
+		if (pid > 0) {
+			::kill(pid,SIGKILL);
+			waitpid(pid,&st,0);
+		}
+	}
+	join();
+}
+
+bool Service::send(const Dictionary &msg)
+{
+	if (_childStdin <= 0)
+		return false;
+
+	std::string mser = msg.toString();
+	if (mser.length() > ZT_SERVICE_MAX_MESSAGE_SIZE)
+		return false;
+
+	// This can technically block. We'll fix this if it ends up being a
+	// problem.
+	uint32_t len = Utils::hton((uint32_t)mser.length());
+	if (write(_childStdin,&len,4) != 4)
+		return false;
+	if ((int)write(_childStdin,mser.data(),mser.length()) != (int)mser.length())
+		return false;
+
+	return true;
+}
+
+void Service::main()
+	throw()
+{
+	char buf[131072];
+	fd_set readfds,writefds,exceptfds;
+	struct timeval tv;
+
+	std::string stderrBuf;
+	std::string stdoutBuf;
+	unsigned int stdoutExpecting = 0;
+
+	while (_run) {
+		if (_pid <= 0) {
+			LOG("launching service %s...",_name.c_str());
+
+			int in[2],out[2],err[2];
+			pipe(in);
+			pipe(out);
+			pipe(err);
+
+			long pid = vfork();
+			if (pid < 0) {
+				LOG("service %s terminating: could not fork!",_name.c_str());
+				return;
+			} else if (pid) {
+				// Parent
+				close(in[0]);
+				close(out[1]);
+				close(err[1]);
+				Thread::sleep(500); // give child time to start
+				_childStdin = in[1];
+				_childStdout = out[0];
+				_childStderr = err[0];
+				fcntl(_childStdout,F_SETFL,O_NONBLOCK);
+				fcntl(_childStderr,F_SETFL,O_NONBLOCK);
+				_pid = pid;
+			} else {
+				// Child
+				close(in[1]);
+				close(out[0]);
+				close(err[0]);
+				dup2(in[0],STDIN_FILENO);
+				dup2(out[1],STDOUT_FILENO);
+				dup2(err[1],STDERR_FILENO);
+				execl(_path.c_str(),_path.c_str(),_r->homePath.c_str(),(const char *)0);
+				exit(-1);
+			}
+		} else {
+			int st = 0;
+			if (waitpid(_pid,&st,WNOHANG) == _pid) {
+				if (_childStdin > 0) close(_childStdin);
+				_childStdin = 0;
+				if (_childStdout > 0) close(_childStdout);
+				if (_childStderr > 0) close(_childStderr);
+				_pid = 0;
+
+				if (!_run)
+					return;
+
+				LOG("service %s exited with exit code: %d, delaying 1s to attempt relaunch",_name.c_str(),st);
+
+				Thread::sleep(1000); // wait to relaunch
+				continue;
+			}
+		}
+
+		// If we've made it here, _pid is running last we checked.
+
+		FD_ZERO(&readfds);
+		FD_ZERO(&writefds);
+		FD_ZERO(&exceptfds);
+
+		FD_SET(_childStdout,&readfds);
+		FD_SET(_childStderr,&readfds);
+
+		tv.tv_sec = 1;
+		tv.tv_usec = 0;
+		select(std::max(_childStdout,_childStderr)+1,&readfds,&writefds,&exceptfds,&tv);
+
+		if (!_run) {
+			if (_childStdin > 0) close(_childStdin);
+			_childStdin = 0;
+			if (_childStdout > 0) close(_childStdout);
+			if (_childStderr > 0) close(_childStderr);
+			return;
+		}
+
+		if ((_childStderr > 0)&&(FD_ISSET(_childStderr,&readfds))) {
+			int n = (int)read(_childStderr,buf,sizeof(buf));
+			for(int i=0;i<n;++i) {
+				if ((buf[i] == '\r')||(buf[i] == '\n')) {
+					stderrBuf = Utils::trim(stderrBuf);
+					if (stderrBuf.length())
+						LOG("service %s: %s",_name.c_str(),stderrBuf.c_str());
+					stderrBuf = "";
+				} else stderrBuf.push_back(buf[i]);
+			}
+		}
+
+		if ((_childStdout > 0)&&(FD_ISSET(_childStdout,&readfds))) {
+			int n = (int)read(_childStdout,buf,sizeof(buf));
+			for(int i=0;i<n;++i) {
+				stdoutBuf.push_back(buf[i]);
+				if (stdoutExpecting) {
+					if (stdoutBuf.length() == stdoutExpecting) {
+						try {
+							_handler(_arg,*this,Dictionary(stdoutBuf));
+						} catch ( ... ) {
+							LOG("unexpected exception handling message from service %s",_name.c_str());
+						}
+						stdoutBuf = "";
+						stdoutExpecting = 0;
+					}
+				} else if (stdoutBuf.length() == 4) {
+					stdoutExpecting = Utils::ntoh(*((const uint32_t *)stdoutBuf.data()));
+					stdoutBuf = "";
+					if (stdoutExpecting > ZT_SERVICE_MAX_MESSAGE_SIZE) {
+						LOG("message size overrun from service %s: %u bytes -- restarting service",_name.c_str(),stdoutExpecting);
+						stdoutExpecting = 0;
+						kill(_pid,SIGKILL);
+						break;
+					}
+				}
+			}
+		}
+	}
+}
+
+} // namespace ZeroTier
+
+#endif // __WINDOWS__
+

+ 129 - 0
node/Service.hpp

@@ -0,0 +1,129 @@
+/*
+ * ZeroTier One - Global Peer to Peer Ethernet
+ * Copyright (C) 2012-2013  ZeroTier Networks LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef _ZT_SERVICE_HPP
+#define _ZT_SERVICE_HPP
+
+#include <string>
+#include <stdexcept>
+
+#include "Constants.hpp"
+#include "Dictionary.hpp"
+#include "Thread.hpp"
+#include "Mutex.hpp"
+
+/**
+ * Maximum size of a service message in bytes (sanity limit)
+ */
+#define ZT_SERVICE_MAX_MESSAGE_SIZE 131072
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+#ifndef __WINDOWS__
+/**
+ * A subprocess that communicates with the host via a simple protocol
+ *
+ * This is currently only supported on *nix systems, and is used to implement
+ * special plugins that are used by supernodes and network configuration
+ * master nodes. Users will probably have no use for it.
+ *
+ * The simple binary protocol consists of a bidirectional stream of string-
+ * serialized Dictionaries prefixed by a 32-bit message length. Input
+ * messages are sent to the subprocess via its stdin, and output is read
+ * from its stdout. Messages printed by the subprocess on its stderr are
+ * logged via the standard Logger instance. If the subprocess dies, an
+ * attempt is made to restart it every second.
+ */
+class Service : protected Thread
+{
+public:
+	/**
+	 * Create and launch a new service
+	 *
+	 * @param renv Runtime environment
+	 * @param name Name of service
+	 * @param path Path to service binary
+	 * @param handler Handler function to call when service generates output
+	 * @param arg First argument to service
+	 */
+	Service(const RuntimeEnvironment *renv,
+	        const char *name,
+	        const char *path,
+	        void (*handler)(void *,Service &,const Dictionary &),
+	        void *arg);
+
+	virtual ~Service();
+
+	/**
+	 * Send a message to service subprocess
+	 *
+	 * @param msg Message in key/value dictionary form
+	 * @return True if message was sent
+	 */
+	bool send(const Dictionary &msg);
+
+	/**
+	 * @return Name of service
+	 */
+	inline const char *name() const
+		throw()
+	{
+		return _name.c_str();
+	}
+
+	/**
+	 * @return True if subprocess is running
+	 */
+	inline bool running() const
+		throw()
+	{
+		return (_pid > 0);
+	}
+
+protected:
+	virtual void main()
+		throw();
+
+private:
+	const RuntimeEnvironment *_r;
+	std::string _path;
+	std::string _name;
+	void *_arg;
+	void (*_handler)(void *,Service &,const Dictionary &);
+	long _pid;
+	int _childStdin;
+	int _childStdout;
+	int _childStderr;
+	volatile bool _run;
+};
+#endif // __WINDOWS__
+
+} // namespace ZeroTier
+
+#endif

+ 5 - 5
node/Switch.cpp

@@ -124,7 +124,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 		Packet outpTmpl(propPeers[0]->address(),_r->identity.address(),Packet::VERB_MULTICAST_FRAME);
 		outpTmpl.append((uint8_t)0);
 		outpTmpl.append((uint64_t)network->id());
-		outpTmpl.append(_r->identity.address().data(),ZT_ADDRESS_LENGTH);
+		_r->identity.address().appendTo(outpTmpl);
 		outpTmpl.append(from.data,6);
 		outpTmpl.append(mg.mac().data,6);
 		outpTmpl.append((uint32_t)mg.adi());
@@ -246,7 +246,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
 
 	{	// tell p1 where to find p2
 		Packet outp(p1,_r->identity.address(),Packet::VERB_RENDEZVOUS);
-		outp.append(p2.data(),ZT_ADDRESS_LENGTH);
+		p2.appendTo(outp);
 		outp.append((uint16_t)cg.first.port());
 		if (cg.first.isV6()) {
 			outp.append((unsigned char)16);
@@ -262,7 +262,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
 	}
 	{	// tell p2 where to find p1
 		Packet outp(p2,_r->identity.address(),Packet::VERB_RENDEZVOUS);
-		outp.append(p1.data(),ZT_ADDRESS_LENGTH);
+		p1.appendTo(outp);
 		outp.append((uint16_t)cg.second.port());
 		if (cg.second.isV6()) {
 			outp.append((unsigned char)16);
@@ -386,7 +386,7 @@ void Switch::announceMulticastGroups(const std::map< SharedPtr<Network>,std::set
 		Packet outp((*p)->address(),_r->identity.address(),Packet::VERB_MULTICAST_LIKE);
 
 		for(std::map< SharedPtr<Network>,std::set<MulticastGroup> >::const_iterator nwmgs(allMemberships.begin());nwmgs!=allMemberships.end();++nwmgs) {
-			if ((nwmgs->first->open())||(_r->topology->isSupernode((*p)->address()))||(nwmgs->first->isMember((*p)->address()))) {
+			if ((_r->topology->isSupernode((*p)->address()))||(nwmgs->first->isAllowed((*p)->address()))) {
 				for(std::set<MulticastGroup>::iterator mg(nwmgs->second.begin());mg!=nwmgs->second.end();++mg) {
 					if ((outp.size() + 18) > ZT_UDP_DEFAULT_PAYLOAD_MTU) {
 						send(outp,true);
@@ -592,7 +592,7 @@ Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlread
 	SharedPtr<Peer> supernode(_r->topology->getBestSupernode(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
 	if (supernode) {
 		Packet outp(supernode->address(),_r->identity.address(),Packet::VERB_WHOIS);
-		outp.append(addr.data(),ZT_ADDRESS_LENGTH);
+		addr.appendTo(outp);
 		outp.encrypt(supernode->cryptKey());
 		outp.hmacSet(supernode->macKey());
 

+ 1 - 11
node/Thread.cpp

@@ -47,7 +47,6 @@ static void *__m_thread_main(void *ptr)
 namespace ZeroTier {
 
 Thread::Thread() :
-	suicidalThread(false),
 	_impl(malloc(sizeof(pthread_t))),
 	_running()
 {
@@ -76,7 +75,7 @@ void Thread::join()
 
 void Thread::sleep(unsigned long ms)
 {
-	usleep(ms);
+	usleep(ms * 1000);
 }
 
 void Thread::__intl_run()
@@ -84,10 +83,6 @@ void Thread::__intl_run()
 	for(;;) {
 		_notInit = false;
 		this->main();
-		if (suicidalThread) {
-			delete this;
-			return;
-		}
 		if (_notInit) // UGLY ASS HACK: see main()
 			usleep(50);
 		else break;
@@ -127,7 +122,6 @@ struct __m_thread_info
 namespace ZeroTier {
 
 Thread::Thread() :
-	suicidalThread(false),
 	_impl(malloc(sizeof(__m_thread_info))),
 	_running()
 {
@@ -162,10 +156,6 @@ void Thread::__intl_run()
 	for(;;) {
 		_notInit = false;
 		this->main();
-		if (suicidalThread) {
-			delete this;
-			return;
-		}
 		if (_notInit)
 			Thread::sleep(50);
 		else break;

+ 0 - 5
node/Thread.hpp

@@ -78,11 +78,6 @@ protected:
 	virtual void main()
 		throw();
 
-	/**
-	 * Subclasses can set to true to cause Thread to delete itself on exit
-	 */
-	volatile bool suicidalThread;
-
 private:
 	void *_impl;
 	AtomicCounter _running;

+ 16 - 4
node/Topology.cpp

@@ -39,7 +39,8 @@ namespace ZeroTier {
 Topology::Topology(const RuntimeEnvironment *renv,const char *dbpath)
 	throw(std::runtime_error) :
 	Thread(),
-	_r(renv)
+	_r(renv),
+	_amSupernode(false)
 {
 	if (KISSDB_open(&_dbm,dbpath,KISSDB_OPEN_MODE_RWCREAT,ZT_KISSDB_HASH_TABLE_SIZE,ZT_KISSDB_KEY_SIZE,ZT_KISSDB_VALUE_SIZE)) {
 		if (KISSDB_open(&_dbm,dbpath,KISSDB_OPEN_MODE_RWREPLACE,ZT_KISSDB_HASH_TABLE_SIZE,ZT_KISSDB_KEY_SIZE,ZT_KISSDB_VALUE_SIZE))
@@ -77,9 +78,11 @@ Topology::~Topology()
 void Topology::setSupernodes(const std::map< Identity,std::vector<InetAddress> > &sn)
 {
 	Mutex::Lock _l(_supernodes_m);
+
 	_supernodes = sn;
 	_supernodeAddresses.clear();
 	_supernodePeers.clear();
+
 	for(std::map< Identity,std::vector<InetAddress> >::const_iterator i(sn.begin());i!=sn.end();++i) {
 		if (i->first != _r->identity) {
 			SharedPtr<Peer> p(getPeer(i->first.address()));
@@ -93,6 +96,8 @@ void Topology::setSupernodes(const std::map< Identity,std::vector<InetAddress> >
 		}
 		_supernodeAddresses.insert(i->first.address());
 	}
+
+	_amSupernode = (_supernodes.find(_r->identity) != _supernodes.end());
 }
 
 void Topology::addPeer(const SharedPtr<Peer> &candidate,void (*callback)(void *,const SharedPtr<Peer> &,Topology::PeerVerifyResult),void *arg)
@@ -127,9 +132,12 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
 			return ap->second;
 	}
 
+	unsigned char ztatmp[ZT_ADDRESS_LENGTH];
+	zta.copyTo(ztatmp,ZT_ADDRESS_LENGTH);
+
 	Buffer<ZT_KISSDB_VALUE_SIZE> b(ZT_KISSDB_VALUE_SIZE);
 	_dbm_m.lock();
-	if (!KISSDB_get(&_dbm,zta.data(),b.data())) {
+	if (!KISSDB_get(&_dbm,ztatmp,b.data())) {
 		_dbm_m.unlock();
 
 		SharedPtr<Peer> p(new Peer());
@@ -300,11 +308,13 @@ void Topology::main()
 					for(std::map< Address,SharedPtr<Peer> >::iterator p(_activePeers.begin());p!=_activePeers.end();++p) {
 						if (p->second->getAndResetDirty()) {
 							try {
+								uint64_t atmp[ZT_ADDRESS_LENGTH];
+								p->second->identity().address().copyTo(atmp,ZT_ADDRESS_LENGTH);
 								Buffer<ZT_PEER_MAX_SERIALIZED_LENGTH> b;
 								p->second->serialize(b);
 								b.zeroUnused();
 								_dbm_m.lock();
-								if (KISSDB_put(&_dbm,p->second->identity().address().data(),b.data())) {
+								if (KISSDB_put(&_dbm,atmp,b.data())) {
 									TRACE("error writing %s to peer.db",p->second->identity().address().toString().c_str());
 								}
 								_dbm_m.unlock();
@@ -329,11 +339,13 @@ void Topology::_reallyAddPeer(const SharedPtr<Peer> &p)
 		_activePeers[p->identity().address()] = p;
 	}
 	try {
+		uint64_t atmp[ZT_ADDRESS_LENGTH];
+		p->address().copyTo(atmp,ZT_ADDRESS_LENGTH);
 		Buffer<ZT_PEER_MAX_SERIALIZED_LENGTH> b;
 		p->serialize(b);
 		b.zeroUnused();
 		_dbm_m.lock();
-		if (KISSDB_put(&_dbm,p->identity().address().data(),b.data())) {
+		if (KISSDB_put(&_dbm,atmp,b.data())) {
 			TRACE("error writing %s to peerdb",p->address().toString().c_str());
 		} else p->getAndResetDirty();
 		_dbm_m.unlock();

+ 8 - 29
node/Topology.hpp

@@ -162,6 +162,11 @@ public:
 		return (_supernodeAddresses.count(zta) > 0);
 	}
 
+	/**
+	 * @return True if this node's identity is in the supernode set
+	 */
+	inline bool amSupernode() const { return _amSupernode; }
+
 	/**
 	 * Clean and flush database now (runs in the background)
 	 */
@@ -271,35 +276,6 @@ public:
 		std::vector< SharedPtr<Peer> > &_v;
 	};
 
-	/**
-	 * Dump peer I/O statistics to an open FILE (for status reporting and debug)
-	 */
-	class DumpPeerStatistics
-	{
-	public:
-		DumpPeerStatistics(FILE *out) :
-			_out(out),
-			_now(Utils::now())
-		{
-			fprintf(_out,"Peer       Direct IPv4           Direct IPv6                                         Latency(ms)"ZT_EOL_S);
-		}
-
-		inline void operator()(Topology &t,const SharedPtr<Peer> &p)
-		{
-			InetAddress v4(p->ipv4ActivePath(_now));
-			InetAddress v6(p->ipv6ActivePath(_now));
-			fprintf(_out,"%-10s %-21s %-51s %u"ZT_EOL_S,
-				p->address().toString().c_str(),
-				((v4) ? v4.toString().c_str() : "(none)"),
-				((v6) ? v6.toString().c_str() : "(none)"),
-				p->latency());
-		}
-
-	private:
-		FILE *_out;
-		uint64_t _now;
-	};
-
 protected:
 	virtual void main()
 		throw();
@@ -334,6 +310,9 @@ private:
 	std::vector< SharedPtr<Peer> > _supernodePeers;
 	Mutex _supernodes_m;
 
+	// Set to true if my identity is in _supernodes
+	volatile bool _amSupernode;
+
 	KISSDB _dbm;
 	Mutex _dbm_m;
 };

+ 7 - 2
node/UdpSocket.cpp

@@ -49,6 +49,7 @@
 namespace ZeroTier {
 
 UdpSocket::UdpSocket(
+	bool localOnly,
 	int localPort,
 	bool ipv6,
 	void (*packetHandler)(UdpSocket *,void *,const InetAddress &,const void *,unsigned int),
@@ -87,7 +88,9 @@ UdpSocket::UdpSocket(
 		memset(&sin6,0,sizeof(sin6));
 		sin6.sin6_family = AF_INET6;
 		sin6.sin6_port = htons(localPort);
-		memcpy(&(sin6.sin6_addr),&in6addr_any,sizeof(struct in6_addr));
+		if (localOnly)
+			memcpy(&(sin6.sin6_addr.s6_addr),InetAddress::LO6.rawIpData(),16);
+		else memcpy(&(sin6.sin6_addr),&in6addr_any,sizeof(struct in6_addr));
 		if (::bind(_sock,(const struct sockaddr *)&sin6,sizeof(sin6))) {
 			::close(_sock);
 			throw std::runtime_error("unable to bind to port");
@@ -109,7 +112,9 @@ UdpSocket::UdpSocket(
 		memset(&sin,0,sizeof(sin));
 		sin.sin_family = AF_INET;
 		sin.sin_port = htons(localPort);
-		sin.sin_addr.s_addr = INADDR_ANY;
+		if (localOnly)
+			memcpy(&(sin.sin_addr.s_addr),InetAddress::LO4.rawIpData(),4);
+		else sin.sin_addr.s_addr = INADDR_ANY;
 		if (::bind(_sock,(const struct sockaddr *)&sin,sizeof(sin))) {
 			::close(_sock);
 			throw std::runtime_error("unable to bind to port");

+ 2 - 0
node/UdpSocket.hpp

@@ -46,6 +46,7 @@ public:
 	/**
 	 * Create and bind a local UDP socket
 	 *
+	 * @param localOnly If true, bind to loopback address only
 	 * @param localPort Local port to listen to
 	 * @param ipv6 If true, bind this as an IPv6 socket, otherwise IPv4
 	 * @param packetHandler Function to call when packets are read
@@ -53,6 +54,7 @@ public:
 	 * @throws std::runtime_error Unable to bind
 	 */
 	UdpSocket(
+		bool localOnly,
 		int localPort,
 		bool ipv6,
 		void (*packetHandler)(UdpSocket *,void *,const InetAddress &,const void *,unsigned int),

+ 44 - 2
node/Utils.cpp

@@ -28,14 +28,14 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include "Utils.hpp"
-#include "Mutex.hpp"
+#include <stdarg.h>
 
 #if defined(__APPLE__) || defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux)
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <dirent.h>
 #endif
 
 #ifdef _WIN32
@@ -45,6 +45,9 @@
 #include <sys/stat.h>
 #include <openssl/rand.h>
 
+#include "Utils.hpp"
+#include "Mutex.hpp"
+
 namespace ZeroTier {
 
 const char Utils::HEXCHARS[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
@@ -213,6 +216,29 @@ const char Utils::base64DecMap[128] = {
 static const char *DAY_NAMES[7] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" };
 static const char *MONTH_NAMES[12] = { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" };
 
+std::map<std::string,bool> Utils::listDirectory(const char *path)
+{
+	struct dirent de;
+	struct dirent *dptr;
+	std::map<std::string,bool> r;
+
+	DIR *d = opendir(path);
+	if (!d)
+		return r;
+
+	dptr = (struct dirent *)0;
+	for(;;) {
+		if (readdir_r(d,&de,&dptr))
+			break;
+		if (dptr) {
+			if ((!strcmp(dptr->d_name,"."))&&(!strcmp(dptr->d_name,"..")))
+				r[std::string(dptr->d_name)] = (dptr->d_type == DT_DIR);
+		} else break;
+	}
+
+	return r;
+}
+
 std::string Utils::base64Encode(const void *data,unsigned int len)
 {
 	if (!len)
@@ -530,4 +556,20 @@ std::string Utils::trim(const std::string &s)
 	return s.substr(start,end - start);
 }
 
+void Utils::stdsprintf(std::string &s,const char *fmt,...)
+	throw(std::bad_alloc,std::length_error)
+{
+	char buf[65536];
+	va_list ap;
+
+	va_start(ap,fmt);
+	int n = vsnprintf(buf,sizeof(buf),fmt,ap);
+	va_end(ap);
+
+	if ((n >= (int)sizeof(buf))||(n < 0))
+		throw std::length_error("printf result too large");
+
+	s.append(buf);
+}
+
 } // namespace ZeroTier

+ 40 - 30
node/Utils.hpp

@@ -34,13 +34,14 @@
 #include <time.h>
 #include <sys/time.h>
 #include <arpa/inet.h>
+
 #include <string>
 #include <stdexcept>
 #include <vector>
+#include <map>
 
 #include "../ext/lz4/lz4.h"
 #include "../ext/lz4/lz4hc.h"
-#include "../ext/huffandpuff/huffman.h"
 
 #include "Constants.hpp"
 
@@ -57,6 +58,16 @@ namespace ZeroTier {
 class Utils
 {
 public:
+	/**
+	 * List a directory's contents
+	 *
+	 * @param path Path to list
+	 * @param files Set to fill with files
+	 * @param directories Set to fill with directories
+	 * @return Map of entries and whether or not they are also directories (empty on failure)
+	 */
+	static std::map<std::string,bool> listDirectory(const char *path);
+
 	/**
 	 * @param data Data to convert to hex
 	 * @param len Length of data
@@ -108,6 +119,15 @@ public:
 	 */
 	static uint64_t getLastModified(const char *path);
 
+	/**
+	 * @param path Path to check
+	 * @return True if file or directory exists at path location
+	 */
+	static inline bool fileExists(const char *path)
+	{
+		return (getLastModified(path) != 0);
+	}
+
 	/**
 	 * @param t64 Time in ms since epoch
 	 * @return RFC1123 date string
@@ -164,7 +184,6 @@ public:
 	template<typename I,typename O>
 	static inline void compress(I begin,I end,O out)
 	{
-		char huffheap[HUFFHEAP_SIZE];
 		unsigned int bufLen = LZ4_compressBound(ZT_COMPRESSION_BLOCK_SIZE);
 		char *buf = new char[bufLen * 2];
 		char *buf2 = buf + bufLen;
@@ -198,16 +217,9 @@ public:
 					continue;
 				}
 
-				unsigned long huffCompressedLen = huffman_compress((const unsigned char *)buf2,lz4CompressedLen,(unsigned char *)buf,bufLen,huffheap);
-				if ((!huffCompressedLen)||((int)huffCompressedLen >= lz4CompressedLen)) {
-					l = hton((uint32_t)lz4CompressedLen); // lz4 only
-					out((const void *)&l,4);
-					out((const void *)buf2,(unsigned int)lz4CompressedLen);
-				} else {
-					l = hton((uint32_t)0x80000000 | (uint32_t)huffCompressedLen); // lz4 with huffman
-					out((const void *)&l,4);
-					out((const void *)buf,(unsigned int)huffCompressedLen);
-				}
+				l = hton((uint32_t)lz4CompressedLen); // lz4 only
+				out((const void *)&l,4);
+				out((const void *)buf2,(unsigned int)lz4CompressedLen);
 			}
 
 			delete [] buf;
@@ -230,7 +242,6 @@ public:
 	template<typename I,typename O>
 	static inline bool decompress(I begin,I end,O out)
 	{
-		char huffheap[HUFFHEAP_SIZE];
 		volatile char i32c[4];
 		void *const i32cp = (void *)i32c;
 		unsigned int bufLen = LZ4_compressBound(ZT_COMPRESSION_BLOCK_SIZE);
@@ -267,23 +278,10 @@ public:
 						return false;
 					}
 
-					if ((_compressedSize & 0x80000000)) { // lz4 and huffman
-						unsigned long lz4CompressedSize = huffman_decompress((const unsigned char *)buf,compressedSize,(unsigned char *)buf2,bufLen,huffheap);
-						if (lz4CompressedSize) {
-							if (LZ4_uncompress_unknownOutputSize(buf2,buf,lz4CompressedSize,bufLen) != (int)originalSize) {
-								delete [] buf;
-								return false;
-							} else out((const void *)buf,(unsigned int)originalSize);
-						} else {
-							delete [] buf;
-							return false;
-						}
-					} else { // lz4 only
-						if (LZ4_uncompress_unknownOutputSize(buf,buf2,compressedSize,bufLen) != (int)originalSize) {
-							delete [] buf;
-							return false;
-						} else out((const void *)buf2,(unsigned int)originalSize);
-					}
+					if (LZ4_uncompress_unknownOutputSize(buf,buf2,compressedSize,bufLen) != (int)originalSize) {
+						delete [] buf;
+						return false;
+					} else out((const void *)buf2,(unsigned int)originalSize);
 				} else { // stored
 					if (originalSize > bufLen) {
 						delete [] buf;
@@ -391,6 +389,18 @@ public:
 	 */
 	static std::string trim(const std::string &s);
 
+	/**
+	 * Like sprintf, but appends to std::string
+	 *
+	 * @param s String to append to
+	 * @param fmt Printf format string
+	 * @param ... Format arguments
+	 * @throws std::bad_alloc Memory allocation failure
+	 * @throws std::length_error Format + args exceeds internal buffer maximum
+	 */
+	static void stdsprintf(std::string &s,const char *fmt,...)
+		throw(std::bad_alloc,std::length_error);
+
 	/**
 	 * Count the number of bits set in an integer
 	 *

+ 1 - 7
objects.mk

@@ -1,9 +1,4 @@
 OBJS=\
-	ext/http-parser/http_parser.o \
-	ext/huffandpuff/huffman.o \
-	ext/jsoncpp/src/json_reader.o \
-	ext/jsoncpp/src/json_value.o \
-	ext/jsoncpp/src/json_writer.o \
 	ext/kissdb/kissdb.o \
 	ext/lz4/lz4hc.o \
 	ext/lz4/lz4.o \
@@ -13,7 +8,6 @@ OBJS=\
 	node/EthernetTap.o \
 	node/Filter.o \
 	node/HMAC.o \
-	node/Http.o \
 	node/Identity.o \
 	node/InetAddress.o \
 	node/Logger.o \
@@ -22,9 +16,9 @@ OBJS=\
 	node/NodeConfig.o \
 	node/Packet.o \
 	node/PacketDecoder.o \
-	node/Pack.o \
 	node/Peer.o \
 	node/Salsa20.o \
+	node/Service.o \
 	node/Switch.o \
 	node/SysEnv.o \
 	node/Thread.o \

+ 0 - 182
packtool.cpp

@@ -1,182 +0,0 @@
-/*
- * ZeroTier One - Global Peer to Peer Ethernet
- * Copyright (C) 2012-2013  ZeroTier Networks LLC
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <iostream>
-#include <string>
-
-#include "node/Identity.hpp"
-#include "node/Pack.hpp"
-#include "node/Utils.hpp"
-
-#include <unistd.h>
-
-using namespace ZeroTier;
-
-static void printHelp(const char *pn)
-{
-	std::cout << "Usage: " << pn << " <command> [<args>]" << std::endl << std::endl;
-	std::cout << "Commands:" << std::endl;
-	std::cout << "	list <packfile> [<identity.secret/public>]" << std::endl;
-	std::cout << "	create <packfile> <identity.secret> <file> [<file> ...]" << std::endl;
-	std::cout << "	extract <packfile> <destination directory>" << std::endl;
-	std::cout << std::endl << "To check signatures, use 'list' with an identity argument." << std::endl;
-}
-
-static Pack *readPack(const char *path)
-{
-	std::string tmp;
-	if (!Utils::readFile(path,tmp))
-		return (Pack *)0;
-	Pack *p = new Pack();
-	if (!p->deserialize(tmp)) {
-		delete p;
-		return (Pack *)0;
-	}
-	return p;
-}
-
-int main(int argc,char **argv)
-{
-	if (argc < 2) {
-		printHelp(argv[0]);
-		return -1;
-	}
-
-	if (!strcmp(argv[1],"list")) {
-		if (argc < 3) {
-			printHelp(argv[0]);
-			return -1;
-		}
-
-		Pack *pack = readPack(argv[2]);
-		if (!pack) {
-			std::cout << "Could not read " << argv[2] << std::endl;
-			return -1;
-		}
-
-		std::vector<const Pack::Entry *> entries = pack->getAll();
-		for(std::vector<const Pack::Entry *>::iterator e=entries.begin();e!=entries.end();++e) {
-			std::cout << (*e)->name << '\t' << (*e)->content.length() << '\t' << Utils::hex((*e)->sha256,32) << "\tsigned by: " << (*e)->signedBy.toString() << std::endl;
-		}
-
-		if (argc >= 4) {
-			std::string idser;
-			if (!Utils::readFile(argv[3],idser)) {
-				std::cout << "Unable to read identity from " << argv[3] << std::endl;
-				return -1;
-			}
-			Identity id;
-			if (!id.fromString(idser)) {
-				std::cout << "Invalid identity" << std::endl;
-				return -1;
-			}
-
-			entries = pack->verifyAll(id,true);
-			for(std::vector<const Pack::Entry *>::iterator e=entries.begin();e!=entries.end();++e) {
-				std::cout << "!!! Signature verification FAILED for: " << (*e)->name << std::endl;
-			}
-			if (!entries.size())
-				std::cout << "Signature for all entries verified OK" << std::endl;
-		}
-
-		delete pack;
-	} else if (!strcmp(argv[1],"create")) {
-		if (argc < 5) {
-			printHelp(argv[0]);
-			return -1;
-		}
-
-		std::string idser;
-		if (!Utils::readFile(argv[3],idser)) {
-			std::cout << "Unable to read identity from " << argv[3] << std::endl;
-			return -1;
-		}
-		Identity id;
-		if (!id.fromString(idser)) {
-			std::cout << "Invalid identity" << std::endl;
-			return -1;
-		}
-
-		if (!id.hasPrivate()) {
-			std::cout << "Identity must include private key to sign" << std::endl;
-			return -1;
-		}
-
-		Pack pack;
-		for(int i=4;i<argc;++i) {
-			std::string fdata;
-			if (!Utils::readFile(argv[i],fdata)) {
-				std::cout << "Unable to read " << argv[i] << std::endl;
-				return -1;
-			}
-			pack.put(std::string(argv[i]),fdata);
-			std::cout << "Added " << argv[i] << std::endl;
-		}
-		if (!pack.signAll(id)) {
-			std::cout << "Unable to sign with identity" << std::endl;
-			return -1;
-		} else std::cout << "Signed all entries with identity " << id.address().toString() << std::endl;
-		std::string packser = pack.serialize();
-
-		if (!Utils::writeFile(argv[2],packser)) {
-			std::cout << "Unable to write " << argv[2] << std::endl;
-			return -1;
-		}
-		std::cout << "Wrote " << packser.length() << " bytes to " << argv[2] << std::endl;
-	} else if (!strcmp(argv[1],"extract")) {
-		if (argc < 4) {
-			printHelp(argv[0]);
-			return -1;
-		}
-
-		Pack *pack = readPack(argv[2]);
-		if (!pack) {
-			std::cout << "Could not read " << argv[2] << std::endl;
-			return -1;
-		}
-
-		if (chdir(argv[3])) {
-			std::cout << "Unable to change to " << argv[3] << " for output." << std::endl;
-			return -1;
-		}
-
-		std::vector<const Pack::Entry *> entries = pack->getAll();
-		for(std::vector<const Pack::Entry *>::iterator e=entries.begin();e!=entries.end();++e) {
-			if (!Utils::writeFile((*e)->name.c_str(),(*e)->content))
-				std::cout << "Error writing " << (*e)->name << std::endl;
-			else std::cout << "Wrote " << (*e)->name << " (" << (*e)->content.length() << ")" << std::endl;
-		}
-	} else {
-		printHelp(argv[0]);
-		return -1;
-	}
-
-	return 0;
-}

+ 55 - 26
selftest.cpp

@@ -43,8 +43,9 @@
 #include "node/HMAC.hpp"
 #include "node/MAC.hpp"
 #include "node/Peer.hpp"
-#include "node/Http.hpp"
 #include "node/Condition.hpp"
+#include "node/NodeConfig.hpp"
+#include "node/Dictionary.hpp"
 
 using namespace ZeroTier;
 
@@ -266,31 +267,60 @@ static int testOther()
 	}
 	std::cout << "PASS" << std::endl;
 
-	return 0;
-}
-
-static Condition testHttpDoneCondition;
-
-static bool testHttpHandler(Http::Request *req,void *arg,const std::string &url,int code,const std::map<std::string,std::string> &headers,const std::string &body)
-{
-	if (code)
-		std::cout << "[net] " << url << " " << code << " bytes: " << body.length() << std::endl;
-	else std::cout << "[net] " << url << " FAILED: " << body << std::endl;
-	testHttpDoneCondition.signal();
-	return false;
-}
+	std::cout << "[other] Testing command bus encode/decode... "; std::cout.flush();
+	try {
+		static char key[32] = { 0 };
+		for(unsigned int k=0;k<1000;++k) {
+			std::vector<std::string> original;
+			for(unsigned int i=0,j=rand() % 256,l=(rand() % 1024)+1;i<j;++i)
+				original.push_back(std::string(l,'x'));
+			std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> > packets(NodeConfig::encodeControlMessage(key,1,original));
+			//std::cout << packets.size() << ' '; std::cout.flush();
+			std::vector<std::string> after;
+			for(std::vector< Buffer<ZT_NODECONFIG_MAX_PACKET_SIZE> >::iterator i(packets.begin());i!=packets.end();++i) {
+				unsigned long convId = 9999;
+				if (!NodeConfig::decodeControlMessagePacket(key,i->data(),i->size(),convId,after)) {
+					std::cout << "FAIL (decode)" << std::endl;
+					return -1;
+				}
+				if (convId != 1) {
+					std::cout << "FAIL (conversation ID)" << std::endl;
+					return -1;
+				}
+			}
+			if (after != original) {
+				std::cout << "FAIL (compare)" << std::endl;
+				return -1;
+			}
+		}
+	} catch (std::exception &exc) {
+		std::cout << "FAIL (" << exc.what() << ")" << std::endl;
+		return -1;
+	}
+	std::cout << "PASS" << std::endl;
 
-static int testNet()
-{
-	std::cout << "[net] GET http://www.uc.edu/" << std::endl;
-	new Http::Request(Http::HTTP_METHOD_GET,"http://www.uc.edu/",Http::EMPTY_HEADERS,std::string(),&testHttpHandler,(void *)0);
-	testHttpDoneCondition.wait();
-	std::cout << "[net] GET http://zerotier.com/" << std::endl;
-	new Http::Request(Http::HTTP_METHOD_GET,"http://zerotier.com/",Http::EMPTY_HEADERS,std::string(),&testHttpHandler,(void *)0);
-	testHttpDoneCondition.wait();
-	std::cout << "[net] GET http://www.google.com/" << std::endl;
-	new Http::Request(Http::HTTP_METHOD_GET,"http://www.google.com/",Http::EMPTY_HEADERS,std::string(),&testHttpHandler,(void *)0);
-	testHttpDoneCondition.wait();
+	std::cout << "[other] Testing Dictionary... "; std::cout.flush();
+	for(int k=0;k<10000;++k) {
+		Dictionary a,b;
+		int nk = rand() % 32;
+		for(int q=0;q<nk;++q) {
+			std::string k,v;
+			int kl = (rand() % 512);
+			int vl = (rand() % 512);
+			for(int i=0;i<kl;++i)
+				k.push_back((char)rand());
+			for(int i=0;i<vl;++i)
+				v.push_back((char)rand());
+			a[k] = v;
+		}
+		std::string aser = a.toString();
+		b.fromString(aser);
+		if (a != b) {
+			std::cout << "FAIL!" << std::endl;
+			return -1;
+		}
+	}
+	std::cout << "PASS" << std::endl;
 
 	return 0;
 }
@@ -301,7 +331,6 @@ int main(int argc,char **argv)
 
 	srand(time(0));
 
-	r |= testNet();
 	r |= testCrypto();
 	r |= testPacket();
 	r |= testOther();