summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS.txt6
-rw-r--r--Makefile.linux6
-rw-r--r--Makefile.mac18
-rw-r--r--cli.cpp124
-rw-r--r--ext/http-parser/AUTHORS41
-rw-r--r--ext/http-parser/CONTRIBUTIONS4
-rw-r--r--ext/http-parser/LICENSE-MIT23
-rw-r--r--ext/http-parser/README.md180
-rw-r--r--ext/http-parser/contrib/parsertrace.c156
-rw-r--r--ext/http-parser/contrib/url_parser.c44
-rw-r--r--ext/http-parser/http_parser.c2175
-rw-r--r--ext/http-parser/http_parser.gyp111
-rw-r--r--ext/http-parser/http_parser.h304
-rw-r--r--ext/http-parser/test.c3425
-rw-r--r--ext/huffandpuff/huffman.c252
-rw-r--r--ext/huffandpuff/huffman.h52
-rw-r--r--ext/jsoncpp/include/json/autolink.h24
-rw-r--r--ext/jsoncpp/include/json/config.h96
-rw-r--r--ext/jsoncpp/include/json/features.h49
-rw-r--r--ext/jsoncpp/include/json/forwards.h44
-rw-r--r--ext/jsoncpp/include/json/json.h15
-rw-r--r--ext/jsoncpp/include/json/reader.h214
-rw-r--r--ext/jsoncpp/include/json/value.h1103
-rw-r--r--ext/jsoncpp/include/json/writer.h185
-rw-r--r--ext/jsoncpp/src/json_batchallocator.h130
-rw-r--r--ext/jsoncpp/src/json_internalarray.inl456
-rw-r--r--ext/jsoncpp/src/json_internalmap.inl615
-rw-r--r--ext/jsoncpp/src/json_reader.cpp880
-rw-r--r--ext/jsoncpp/src/json_tool.h93
-rw-r--r--ext/jsoncpp/src/json_value.cpp1829
-rw-r--r--ext/jsoncpp/src/json_valueiterator.inl299
-rw-r--r--ext/jsoncpp/src/json_writer.cpp838
-rw-r--r--idtool.cpp2
-rw-r--r--main.cpp21
-rw-r--r--makekeypair.cpp47
-rw-r--r--netconf-service/Makefile7
-rw-r--r--netconf-service/netconf-test.cpp83
-rw-r--r--netconf-service/netconf.cpp345
-rw-r--r--node/Address.hpp186
-rw-r--r--node/BlobArray.hpp94
-rw-r--r--node/Constants.hpp47
-rw-r--r--node/Defaults.cpp4
-rw-r--r--node/Defaults.hpp10
-rw-r--r--node/Demarc.cpp4
-rw-r--r--node/Demarc.hpp3
-rw-r--r--node/Dictionary.hpp207
-rw-r--r--node/EllipticCurveKey.hpp12
-rw-r--r--node/EllipticCurveKeyPair.cpp20
-rw-r--r--node/EllipticCurveKeyPair.hpp2
-rw-r--r--node/EthernetTap.cpp14
-rw-r--r--node/Filter.cpp301
-rw-r--r--node/Filter.hpp71
-rw-r--r--node/Http.cpp323
-rw-r--r--node/Http.hpp129
-rw-r--r--node/Identity.cpp12
-rw-r--r--node/Identity.hpp8
-rw-r--r--node/Multicaster.hpp16
-rw-r--r--node/Network.cpp123
-rw-r--r--node/Network.hpp348
-rw-r--r--node/Node.cpp278
-rw-r--r--node/Node.hpp53
-rw-r--r--node/NodeConfig.cpp313
-rw-r--r--node/NodeConfig.hpp90
-rw-r--r--node/Pack.cpp159
-rw-r--r--node/Pack.hpp141
-rw-r--r--node/Packet.cpp4
-rw-r--r--node/Packet.hpp78
-rw-r--r--node/PacketDecoder.cpp97
-rw-r--r--node/PacketDecoder.hpp3
-rw-r--r--node/RuntimeEnvironment.hpp21
-rw-r--r--node/Service.cpp241
-rw-r--r--node/Service.hpp129
-rw-r--r--node/Switch.cpp10
-rw-r--r--node/Thread.cpp12
-rw-r--r--node/Thread.hpp5
-rw-r--r--node/Topology.cpp20
-rw-r--r--node/Topology.hpp37
-rw-r--r--node/UdpSocket.cpp9
-rw-r--r--node/UdpSocket.hpp2
-rw-r--r--node/Utils.cpp46
-rw-r--r--node/Utils.hpp70
-rw-r--r--objects.mk8
-rw-r--r--packtool.cpp182
-rw-r--r--selftest.cpp81
84 files changed, 2955 insertions, 15364 deletions
diff --git a/AUTHORS.txt b/AUTHORS.txt
index 7374b7c8..f304f6af 100644
--- a/AUTHORS.txt
+++ b/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/
diff --git a/Makefile.linux b/Makefile.linux
index dd570911..33b88bad 100644
--- a/Makefile.linux
+++ b/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
diff --git a/Makefile.mac b/Makefile.mac
index 89203243..d47d18cc 100644
--- a/Makefile.mac
+++ b/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-*
diff --git a/cli.cpp b/cli.cpp
new file mode 100644
index 00000000..a72a0890
--- /dev/null
+++ b/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;
+}
diff --git a/ext/http-parser/AUTHORS b/ext/http-parser/AUTHORS
deleted file mode 100644
index 92ee45ca..00000000
--- a/ext/http-parser/AUTHORS
+++ /dev/null
@@ -1,41 +0,0 @@
-# Authors ordered by first contribution.
-Ryan Dahl <ry@tinyclouds.org>
-Jeremy Hinegardner <jeremy@hinegardner.org>
-Sergey Shepelev <temotor@gmail.com>
-Joe Damato <ice799@gmail.com>
-tomika <tomika_nospam@freemail.hu>
-Phoenix Sol <phoenix@burninglabs.com>
-Cliff Frey <cliff@meraki.com>
-Ewen Cheslack-Postava <ewencp@cs.stanford.edu>
-Santiago Gala <sgala@apache.org>
-Tim Becker <tim.becker@syngenio.de>
-Jeff Terrace <jterrace@gmail.com>
-Ben Noordhuis <info@bnoordhuis.nl>
-Nathan Rajlich <nathan@tootallnate.net>
-Mark Nottingham <mnot@mnot.net>
-Aman Gupta <aman@tmm1.net>
-Tim Becker <tim.becker@kuriositaet.de>
-Sean Cunningham <sean.cunningham@mandiant.com>
-Peter Griess <pg@std.in>
-Salman Haq <salman.haq@asti-usa.com>
-Cliff Frey <clifffrey@gmail.com>
-Jon Kolb <jon@b0g.us>
-Fouad Mardini <f.mardini@gmail.com>
-Paul Querna <pquerna@apache.org>
-Felix Geisendörfer <felix@debuggable.com>
-koichik <koichik@improvement.jp>
-Andre Caron <andre.l.caron@gmail.com>
-Ivo Raisr <ivosh@ivosh.net>
-James McLaughlin <jamie@lacewing-project.org>
-David Gwynne <loki@animata.net>
-Thomas LE ROUX <thomas@november-eleven.fr>
-Randy Rizun <rrizun@ortivawireless.com>
-Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
-Simon Zimmermann <simonz05@gmail.com>
-Erik Dubbelboer <erik@dubbelboer.com>
-Martell Malone <martellmalone@gmail.com>
-Bertrand Paquet <bpaquet@octo.com>
-BogDan Vatra <bogdan@kde.org>
-Peter Faiman <peter@thepicard.org>
-Corey Richardson <corey@octayn.net>
-Tóth Tamás <tomika_nospam@freemail.hu>
diff --git a/ext/http-parser/CONTRIBUTIONS b/ext/http-parser/CONTRIBUTIONS
deleted file mode 100644
index 11ba31e4..00000000
--- a/ext/http-parser/CONTRIBUTIONS
+++ /dev/null
@@ -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
diff --git a/ext/http-parser/LICENSE-MIT b/ext/http-parser/LICENSE-MIT
deleted file mode 100644
index 58010b38..00000000
--- a/ext/http-parser/LICENSE-MIT
+++ /dev/null
@@ -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.
diff --git a/ext/http-parser/README.md b/ext/http-parser/README.md
deleted file mode 100644
index b63418af..00000000
--- a/ext/http-parser/README.md
+++ /dev/null
@@ -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
diff --git a/ext/http-parser/contrib/parsertrace.c b/ext/http-parser/contrib/parsertrace.c
deleted file mode 100644
index c9bc71ec..00000000
--- a/ext/http-parser/contrib/parsertrace.c
+++ /dev/null
@@ -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;
-}
diff --git a/ext/http-parser/contrib/url_parser.c b/ext/http-parser/contrib/url_parser.c
deleted file mode 100644
index b1f9c979..00000000
--- a/ext/http-parser/contrib/url_parser.c
+++ /dev/null
@@ -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;
-} \ No newline at end of file
diff --git a/ext/http-parser/http_parser.c b/ext/http-parser/http_parser.c
deleted file mode 100644
index ed3a9232..00000000
--- a/ext/http-parser/http_parser.c
+++ /dev/null
@@ -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;
-}
diff --git a/ext/http-parser/http_parser.gyp b/ext/http-parser/http_parser.gyp
deleted file mode 100644
index ef34ecae..00000000
--- a/ext/http-parser/http_parser.gyp
+++ /dev/null
@@ -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' ]
- }
- ]
-}
diff --git a/ext/http-parser/http_parser.h b/ext/http-parser/http_parser.h
deleted file mode 100644
index a992c742..00000000
--- a/ext/http-parser/http_parser.h
+++ /dev/null
@@ -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
diff --git a/ext/http-parser/test.c b/ext/http-parser/test.c
deleted file mode 100644
index 83723b7f..00000000
--- a/ext/http-parser/test.c
+++ /dev/null
@@ -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!&*$@hypnotoad.org: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!&*$@hypnotoad.org: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:b@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) | (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:b@host.com/"
- ,.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::b@host.com/"
- ,.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!;-_!=+$@host.com/"
- ,.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;
-}
diff --git a/ext/huffandpuff/huffman.c b/ext/huffandpuff/huffman.c
deleted file mode 100644
index f4d8314a..00000000
--- a/ext/huffandpuff/huffman.c
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Huffandpuff minimal Huffman coder
- *
- * (c)2013 Adam Ierymenko <adam.ierymenko@zerotier.com>
- * 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
diff --git a/ext/huffandpuff/huffman.h b/ext/huffandpuff/huffman.h
deleted file mode 100644
index 5e2f8996..00000000
--- a/ext/huffandpuff/huffman.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Huffandpuff minimal Huffman coder
- *
- * (c)2013 Adam Ierymenko <adam.ierymenko@zerotier.com>
- * 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
diff --git a/ext/jsoncpp/include/json/autolink.h b/ext/jsoncpp/include/json/autolink.h
deleted file mode 100644
index 02328d1f..00000000
--- a/ext/jsoncpp/include/json/autolink.h
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/include/json/config.h b/ext/jsoncpp/include/json/config.h
deleted file mode 100644
index 7609d45e..00000000
--- a/ext/jsoncpp/include/json/config.h
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/include/json/features.h b/ext/jsoncpp/include/json/features.h
deleted file mode 100644
index 43532784..00000000
--- a/ext/jsoncpp/include/json/features.h
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/include/json/forwards.h b/ext/jsoncpp/include/json/forwards.h
deleted file mode 100644
index ab863da8..00000000
--- a/ext/jsoncpp/include/json/forwards.h
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/include/json/json.h b/ext/jsoncpp/include/json/json.h
deleted file mode 100644
index da5fc967..00000000
--- a/ext/jsoncpp/include/json/json.h
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/include/json/reader.h b/ext/jsoncpp/include/json/reader.h
deleted file mode 100644
index 0a324dfc..00000000
--- a/ext/jsoncpp/include/json/reader.h
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/include/json/value.h b/ext/jsoncpp/include/json/value.h
deleted file mode 100644
index 32e3455e..00000000
--- a/ext/jsoncpp/include/json/value.h
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/include/json/writer.h b/ext/jsoncpp/include/json/writer.h
deleted file mode 100644
index 47893636..00000000
--- a/ext/jsoncpp/include/json/writer.h
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/src/json_batchallocator.h b/ext/jsoncpp/src/json_batchallocator.h
deleted file mode 100644
index 173e2ed2..00000000
--- a/ext/jsoncpp/src/json_batchallocator.h
+++ /dev/null
@@ -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
-
diff --git a/ext/jsoncpp/src/json_internalarray.inl b/ext/jsoncpp/src/json_internalarray.inl
deleted file mode 100644
index 3a532ad7..00000000
--- a/ext/jsoncpp/src/json_internalarray.inl
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/src/json_internalmap.inl b/ext/jsoncpp/src/json_internalmap.inl
deleted file mode 100644
index f2fa1606..00000000
--- a/ext/jsoncpp/src/json_internalmap.inl
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/src/json_reader.cpp b/ext/jsoncpp/src/json_reader.cpp
deleted file mode 100644
index 8bb03042..00000000
--- a/ext/jsoncpp/src/json_reader.cpp
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/src/json_tool.h b/ext/jsoncpp/src/json_tool.h
deleted file mode 100644
index 658031bb..00000000
--- a/ext/jsoncpp/src/json_tool.h
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/src/json_value.cpp b/ext/jsoncpp/src/json_value.cpp
deleted file mode 100644
index ff98f635..00000000
--- a/ext/jsoncpp/src/json_value.cpp
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/src/json_valueiterator.inl b/ext/jsoncpp/src/json_valueiterator.inl
deleted file mode 100644
index 7457ca38..00000000
--- a/ext/jsoncpp/src/json_valueiterator.inl
+++ /dev/null
@@ -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
diff --git a/ext/jsoncpp/src/json_writer.cpp b/ext/jsoncpp/src/json_writer.cpp
deleted file mode 100644
index 1bda183f..00000000
--- a/ext/jsoncpp/src/json_writer.cpp
+++ /dev/null
@@ -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
diff --git a/idtool.cpp b/idtool.cpp
index 8068fc14..00511a0f 100644
--- a/idtool.cpp
+++ b/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;
diff --git a/main.cpp b/main.cpp
index 13379ad6..8b355a22 100644
--- a/main.cpp
+++ b/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;
diff --git a/makekeypair.cpp b/makekeypair.cpp
new file mode 100644
index 00000000..b8736a4e
--- /dev/null
+++ b/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;
+}
diff --git a/netconf-service/Makefile b/netconf-service/Makefile
new file mode 100644
index 00000000..af737a97
--- /dev/null
+++ b/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
diff --git a/netconf-service/netconf-test.cpp b/netconf-service/netconf-test.cpp
new file mode 100644
index 00000000..6c7ef9f2
--- /dev/null
+++ b/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);
+ }
+}
diff --git a/netconf-service/netconf.cpp b/netconf-service/netconf.cpp
new file mode 100644
index 00000000..af6ed4e6
--- /dev/null
+++ b/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");
+ }
+ }
+}
diff --git a/node/Address.hpp b/node/Address.hpp
index 4c912540..fce4f20c 100644
--- a/node/Address.hpp
+++ b/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
diff --git a/node/BlobArray.hpp b/node/BlobArray.hpp
deleted file mode 100644
index d78bcffa..00000000
--- a/node/BlobArray.hpp
+++ /dev/null
@@ -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
-
diff --git a/node/Constants.hpp b/node/Constants.hpp
index fbb309e6..36973b38 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -118,6 +118,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
*
* In the future we might support UDP path MTU discovery, but for now we
@@ -152,13 +157,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
*
* The actual spec allows 16, but this is the most we'll support right
@@ -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,21 +231,16 @@ 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
*
* Announcement occurs when a multicast group is locally joined, but all
@@ -282,23 +275,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
*
* This is the shortest of the check delays/periods.
@@ -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
diff --git a/node/Defaults.cpp b/node/Defaults.cpp
index f1454796..3e936b2d 100644
--- a/node/Defaults.cpp
+++ b/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())
{
}
diff --git a/node/Defaults.hpp b/node/Defaults.hpp
index b9c8ecf5..3a350865 100644
--- a/node/Defaults.hpp
+++ b/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;
diff --git a/node/Demarc.cpp b/node/Demarc.cpp
index 5efe55bc..b935ce53 100644
--- a/node/Demarc.cpp
+++ b/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);
diff --git a/node/Demarc.hpp b/node/Demarc.hpp
index e13ed0cb..721dc0c8 100644
--- a/node/Demarc.hpp
+++ b/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
{
diff --git a/node/Dictionary.hpp b/node/Dictionary.hpp
new file mode 100644
index 00000000..f706168f
--- /dev/null
+++ b/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
diff --git a/node/EllipticCurveKey.hpp b/node/EllipticCurveKey.hpp
index 5a7b895f..b32f702d 100644
--- a/node/EllipticCurveKey.hpp
+++ b/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)
diff --git a/node/EllipticCurveKeyPair.cpp b/node/EllipticCurveKeyPair.cpp
index bed0725e..66acf320 100644
--- a/node/EllipticCurveKeyPair.cpp
+++ b/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
diff --git a/node/EllipticCurveKeyPair.hpp b/node/EllipticCurveKeyPair.hpp
index 2649f4c4..dbe08cc9 100644
--- a/node/EllipticCurveKeyPair.hpp
+++ b/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
{
diff --git a/node/EthernetTap.cpp b/node/EthernetTap.cpp
index 8fb5e788..c45d50c2 100644
--- a/node/EthernetTap.cpp
+++ b/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 {
diff --git a/node/Filter.cpp b/node/Filter.cpp
index 5cd295c3..a0412173 100644
--- a/node/Filter.cpp
+++ b/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
diff --git a/node/Filter.hpp b/node/Filter.hpp
index 861603f0..8b86b48f 100644
--- a/node/Filter.hpp
+++ b/node/Filter.hpp
@@ -33,6 +33,7 @@
#include <string>
#include <vector>
#include <utility>
+#include <stdexcept>
#include "Mutex.hpp"
#include "Range.hpp"
@@ -130,6 +131,19 @@ 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
*
* This behaves as an immutable value object.
@@ -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;
};
diff --git a/node/Http.cpp b/node/Http.cpp
deleted file mode 100644
index 86ae2d25..00000000
--- a/node/Http.cpp
+++ /dev/null
@@ -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
diff --git a/node/Http.hpp b/node/Http.hpp
deleted file mode 100644
index a099b15d..00000000
--- a/node/Http.hpp
+++ /dev/null
@@ -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
diff --git a/node/Identity.cpp b/node/Identity.cpp
index f16947a0..e9cbef3d 100644
--- a/node/Identity.cpp
+++ b/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
diff --git a/node/Identity.hpp b/node/Identity.hpp
index 5cdfe9f8..1cce4fb0 100644
--- a/node/Identity.hpp
+++ b/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)
diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp
index 1addf42d..ac89f84b 100644
--- a/node/Multicaster.hpp
+++ b/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;
}
diff --git a/node/Network.cpp b/node/Network.cpp
index f34e07e0..a50d56dc 100644
--- a/node/Network.cpp
+++ b/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;
diff --git a/node/Network.hpp b/node/Network.hpp
index 417688f0..637c6664 100644
--- a/node/Network.hpp
+++ b/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;
diff --git a/node/Node.cpp b/node/Node.cpp
index 3b18d7ba..8079e801 100644
--- a/node/Node.cpp
+++ b/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:
diff --git a/node/Node.hpp b/node/Node.hpp
index df6b946f..b716b556 100644
--- a/node/Node.hpp
+++ b/node/Node.hpp
@@ -41,6 +41,44 @@ 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
*/
enum ReasonForTermination
@@ -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();
@@ -99,12 +134,6 @@ public:
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
*
* @return Version in string form
@@ -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
};
diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp
index fcbbc6bd..4a174535 100644
--- a/node/NodeConfig.cpp
+++ b/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
diff --git a/node/NodeConfig.hpp b/node/NodeConfig.hpp
index 5caf18ab..62b23609 100644
--- a/node/NodeConfig.hpp
+++ b/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;
};
diff --git a/node/Pack.cpp b/node/Pack.cpp
deleted file mode 100644
index 8bac1300..00000000
--- a/node/Pack.cpp
+++ /dev/null
@@ -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
diff --git a/node/Pack.hpp b/node/Pack.hpp
deleted file mode 100644
index a0aecd6e..00000000
--- a/node/Pack.hpp
+++ /dev/null
@@ -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
diff --git a/node/Packet.cpp b/node/Packet.cpp
index d12f396d..94d9164b 100644
--- a/node/Packet.cpp
+++ b/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)";
}
diff --git a/node/Packet.hpp b/node/Packet.hpp
index a5c450fb..0e7ccea3 100644
--- a/node/Packet.hpp
+++ b/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
diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp
index ba1a9001..518ed9e7 100644
--- a/node/PacketDecoder.cpp
+++ b/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
diff --git a/node/PacketDecoder.hpp b/node/PacketDecoder.hpp
index e595d326..51408ba5 100644
--- a/node/PacketDecoder.hpp
+++ b/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;
diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp
index 5f76c8ac..00d393af 100644
--- a/node/RuntimeEnvironment.hpp
+++ b/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
diff --git a/node/Service.cpp b/node/Service.cpp
new file mode 100644
index 00000000..c614e4e4
--- /dev/null
+++ b/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__
+
diff --git a/node/Service.hpp b/node/Service.hpp
new file mode 100644
index 00000000..c8b729c7
--- /dev/null
+++ b/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
diff --git a/node/Switch.cpp b/node/Switch.cpp
index 5275f5ad..bb10b412 100644
--- a/node/Switch.cpp
+++ b/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());
diff --git a/node/Thread.cpp b/node/Thread.cpp
index 37a1a5a5..f650f6fc 100644
--- a/node/Thread.cpp
+++ b/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;
diff --git a/node/Thread.hpp b/node/Thread.hpp
index b023fbae..9b399a00 100644
--- a/node/Thread.hpp
+++ b/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;
diff --git a/node/Topology.cpp b/node/Topology.cpp
index e627e767..8487684e 100644
--- a/node/Topology.cpp
+++ b/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();
diff --git a/node/Topology.hpp b/node/Topology.hpp
index ae1a15b4..104ad344 100644
--- a/node/Topology.hpp
+++ b/node/Topology.hpp
@@ -163,6 +163,11 @@ public:
}
/**
+ * @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)
*/
void clean();
@@ -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;
};
diff --git a/node/UdpSocket.cpp b/node/UdpSocket.cpp
index 95156fcc..6178d16e 100644
--- a/node/UdpSocket.cpp
+++ b/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");
diff --git a/node/UdpSocket.hpp b/node/UdpSocket.hpp
index be407d56..a3223f1f 100644
--- a/node/UdpSocket.hpp
+++ b/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),
diff --git a/node/Utils.cpp b/node/Utils.cpp
index b9db07b9..7a4d51ba 100644
--- a/node/Utils.cpp
+++ b/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
diff --git a/node/Utils.hpp b/node/Utils.hpp
index e6bef5e5..a222ca4f 100644
--- a/node/Utils.hpp
+++ b/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"
@@ -58,6 +59,16 @@ 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
* @return Hexadecimal string
@@ -109,6 +120,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;
@@ -392,6 +390,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
*
* @param v 32-bit integer
diff --git a/objects.mk b/objects.mk
index 04a9ad3f..be5cdc4e 100644
--- a/objects.mk
+++ b/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 \
diff --git a/packtool.cpp b/packtool.cpp
deleted file mode 100644
index 6726c8d7..00000000
--- a/packtool.cpp
+++ /dev/null
@@ -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;
-}
diff --git a/selftest.cpp b/selftest.cpp
index d5a2bc45..edbdcaf1 100644
--- a/selftest.cpp
+++ b/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();