浏览代码

Merge branch 'dev' into libpq

Grant Limberg 6 年之前
父节点
当前提交
0510f85a8f
共有 76 个文件被更改,包括 14445 次插入1271 次删除
  1. 46 0
      .github/ISSUE_TEMPLATE/bug_report.md
  2. 17 0
      .github/ISSUE_TEMPLATE/feature_request.md
  3. 1 0
      .gitignore
  4. 4 4
      RELEASE-NOTES.md
  5. 3 1
      ext/arm32-neon-salsa2012-asm/salsa2012.h
  6. 2751 0
      ext/curl-7.58.0/Win32/include/curl/curl.h
  7. 77 0
      ext/curl-7.58.0/Win32/include/curl/curlver.h
  8. 102 0
      ext/curl-7.58.0/Win32/include/curl/easy.h
  9. 50 0
      ext/curl-7.58.0/Win32/include/curl/mprintf.h
  10. 439 0
      ext/curl-7.58.0/Win32/include/curl/multi.h
  11. 33 0
      ext/curl-7.58.0/Win32/include/curl/stdcheaders.h
  12. 473 0
      ext/curl-7.58.0/Win32/include/curl/system.h
  13. 683 0
      ext/curl-7.58.0/Win32/include/curl/typecheck-gcc.h
  14. 二进制
      ext/curl-7.58.0/Win32/lib/libcurl_a.lib
  15. 二进制
      ext/curl-7.58.0/Win32/lib/libcurl_a_debug.lib
  16. 2751 0
      ext/curl-7.58.0/x64/include/curl/curl.h
  17. 77 0
      ext/curl-7.58.0/x64/include/curl/curlver.h
  18. 102 0
      ext/curl-7.58.0/x64/include/curl/easy.h
  19. 50 0
      ext/curl-7.58.0/x64/include/curl/mprintf.h
  20. 439 0
      ext/curl-7.58.0/x64/include/curl/multi.h
  21. 33 0
      ext/curl-7.58.0/x64/include/curl/stdcheaders.h
  22. 473 0
      ext/curl-7.58.0/x64/include/curl/system.h
  23. 683 0
      ext/curl-7.58.0/x64/include/curl/typecheck-gcc.h
  24. 二进制
      ext/curl-7.58.0/x64/lib/libcurl_a.lib
  25. 二进制
      ext/curl-7.58.0/x64/lib/libcurl_a_debug.lib
  26. 12 6
      ext/installfiles/linux/zerotier-containerized/Dockerfile
  27. 220 81
      ext/json/README.md
  28. 766 823
      ext/json/json.hpp
  29. 5 2
      include/ZeroTierDebug.h
  30. 98 4
      include/ZeroTierOne.h
  31. 2 1
      make-bsd.mk
  32. 20 1
      make-linux.mk
  33. 10 2
      make-mac.mk
  34. 13 0
      nltest.cpp
  35. 3 3
      node/Capability.hpp
  36. 1 1
      node/CertificateOfMembership.hpp
  37. 190 0
      node/Constants.hpp
  38. 4 0
      node/Hashtable.hpp
  39. 2 2
      node/Identity.cpp
  40. 89 35
      node/IncomingPacket.cpp
  41. 2 0
      node/IncomingPacket.hpp
  42. 13 13
      node/InetAddress.cpp
  43. 1 1
      node/MulticastGroup.hpp
  44. 1 1
      node/Multicaster.cpp
  45. 16 6
      node/Network.cpp
  46. 10 2
      node/Network.hpp
  47. 1 1
      node/NetworkConfig.hpp
  48. 16 2
      node/Node.cpp
  49. 5 0
      node/Node.hpp
  50. 2 1
      node/OutboundMulticast.cpp
  51. 56 10
      node/Packet.hpp
  52. 443 5
      node/Path.hpp
  53. 394 36
      node/Peer.cpp
  54. 198 18
      node/Peer.hpp
  55. 345 0
      node/RingBuffer.hpp
  56. 1 1
      node/Salsa20.cpp
  57. 1 1
      node/SelfAwareness.cpp
  58. 273 10
      node/Switch.cpp
  59. 94 0
      node/Switch.hpp
  60. 1 1
      node/Tag.hpp
  61. 1 1
      node/Topology.cpp
  62. 1 1
      node/Topology.hpp
  63. 21 0
      node/Trace.cpp
  64. 6 0
      node/Trace.hpp
  65. 22 2
      node/Utils.hpp
  66. 1 0
      osdep/Binder.hpp
  67. 33 71
      osdep/LinuxEthernetTap.cpp
  68. 1080 0
      osdep/LinuxNetLink.cpp
  69. 129 0
      osdep/LinuxNetLink.hpp
  70. 9 1
      osdep/ManagedRoute.hpp
  71. 21 1
      osdep/OSUtils.cpp
  72. 68 3
      osdep/Phy.hpp
  73. 1 1
      osdep/WindowsEthernetTap.hpp
  74. 4 1
      rule-compiler/rule-compiler.js
  75. 452 113
      service/OneService.cpp
  76. 1 1
      version.h

+ 46 - 0
.github/ISSUE_TEMPLATE/bug_report.md

@@ -0,0 +1,46 @@
+---
+name: Bug report
+about: Create a report to help us improve
+
+---
+**Alternative, faster ways to get help**
+If you have just started using ZeroTier, here are some places to get help:
+- my.zerotier.com has a _Community_ tab. It's a live chat with other users and the developers. 
+- [ZeroTier Knowledge Base](https://support.zerotier.com/knowledgebase.php?s=MQ__)
+- www.zerotier.com has a Contact Us button
+- email [email protected]
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Create a Network '...'
+2. Install zerotier-one '....'
+3. '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots or console output to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. Mac, Linux, Windows, BSD]
+ - OS/Distribution Version
+ - ZeroTier Version [e.g. 1.2.4]
+ - Hardware [e.g. raspberry pi 3]
+ 
+**Smartphone (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Version [e.g. 1.2.4]
+
+**Additional context**
+Add any other context about the problem here.
+- ZeroTier Network Configuration
+- Router Config
+- Firewall Config (try turning the firewall off)
+- General Network Environment: [ e.g Home, University Campus, Corporate LAN ]
+

+ 17 - 0
.github/ISSUE_TEMPLATE/feature_request.md

@@ -0,0 +1,17 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.

+ 1 - 0
.gitignore

@@ -4,6 +4,7 @@
 /zerotier-cli
 /zerotier-selftest
 /zerotier
+/nltest
 
 # OS-created garbage files from various platforms
 .DS_Store

+ 4 - 4
RELEASE-NOTES.md

@@ -43,7 +43,7 @@ ZeroTier Release Notes
     * Fixed two very rare multithreading issues that were only observed on certain systems
  * Platform-Specific Changes
     * MacOS
-        * Installer now loads the kernel extension right away so that High Sierra users will see the prompt to authorize it. This is done in the "Security & Privacy" preference pane and must be done driectly on the console (not via remote desktop). On High Sierra and newer kexts must be authorized at the console via security settings system preferences pane.
+        * Installer now loads the kernel extension right away so that High Sierra users will see the prompt to authorize it. This is done in the "Security & Privacy" preference pane and must be done directly on the console (not via remote desktop). On High Sierra and newer kexts must be authorized at the console via security settings system preferences pane.
     * Windows
         * The Windows installer should now install the driver without requiring a special prompt in most cases. This should make it easier for our packages to be accepted into and updated in the Chocolatey repository and should make it easier to perform remote installs across groups of machines using IT management and provisioning tools.
         * The Windows official packages are now signed with an EV certificate (with hardware key).
@@ -85,7 +85,7 @@ The largest new feature in 1.2.0, and the product of many months of work, is our
 
 Rules allow you to filter packets on your network and vector traffic to security observers. Security observation can be performed in-band using REDIRECT or out of band using TEE.
 
-Tags and capabilites provide advanced methods for implementing fine grained permission structures and micro-segmentation schemes without bloating the size and complexity of your rules table.
+Tags and capabilities provide advanced methods for implementing fine grained permission structures and micro-segmentation schemes without bloating the size and complexity of your rules table.
 
 See the [rules engine announcement blog post](https://www.zerotier.com/blog/?p=927) for an in-depth discussion of theory and implementation. The [manual](https://www.zerotier.com/manual.shtml) contains detailed information on rule, tag, and capability use, and the `rule-compiler/` subfolder of the ZeroTier source tree contains a JavaScript function to compile rules in our human-readable rule definition language into rules suitable for import into a network controller. (ZeroTier Central uses this same script to compile rules on [my.zerotier.com](https://my.zerotier.com/).)
 
@@ -168,7 +168,7 @@ A special kind of public network called an ad-hoc network may be accessed by joi
     | Start of port range (hex)
     Reserved ZeroTier address prefix indicating a controller-less network
 
-Ad-hoc networks are public (no access control) networks that have no network controller. Instead their configuration and other credentials are generated locally. Ad-hoc networks permit only IPv6 UDP and TCP unicast traffic (no multicast or broadcast) using 6plane format NDP-emulated IPv6 addresses. In addition an ad-hoc network ID encodes an IP port range. UDP packets and TCP SYN (connection open) packets are only allowed to desintation ports within the encoded range.
+Ad-hoc networks are public (no access control) networks that have no network controller. Instead their configuration and other credentials are generated locally. Ad-hoc networks permit only IPv6 UDP and TCP unicast traffic (no multicast or broadcast) using 6plane format NDP-emulated IPv6 addresses. In addition an ad-hoc network ID encodes an IP port range. UDP packets and TCP SYN (connection open) packets are only allowed to destination ports within the encoded range.
 
 For example `ff00160016000000` is an ad-hoc network allowing only SSH, while `ff0000ffff000000` is an ad-hoc network allowing any UDP or TCP port.
 
@@ -183,7 +183,7 @@ If you have data in an old SQLite3 controller we've included a NodeJS script in
 ## Major Bug Fixes in 1.2.0
 
  * **The Windows HyperV 100% CPU bug is FINALLY DEAD**: This long-running problem turns out to have been an issue with Windows itself, but one we were triggering by placing invalid data into the Windows registry. Microsoft is aware of the issue but we've also fixed the triggering problem on our side. ZeroTier should now co-exist quite well with HyperV and should now be able to be bridged with a HyperV virtual switch.
- * **Segmenation faults on musl-libc based Linux systems**: Alpine Linux and some embedded Linux systems that use musl libc (a minimal libc) experienced segmentation faults. These were due to a smaller default stack size. A work-around that sets the stack size for new threads has been added.
+ * **Segmentation faults on musl-libc based Linux systems**: Alpine Linux and some embedded Linux systems that use musl libc (a minimal libc) experienced segmentation faults. These were due to a smaller default stack size. A work-around that sets the stack size for new threads has been added.
  * **Windows firewall blocks local JSON API**: On some Windows systems the firewall likes to block 127.0.0.1:9993 for mysterious reasons. This is now fixed in the installer via the addition of another firewall exemption rule.
  * **UI crash on embedded Windows due to missing fonts**: The MSI installer now ships fonts and will install them if they are not present, so this should be fixed.
 

+ 3 - 1
ext/arm32-neon-salsa2012-asm/salsa2012.h

@@ -5,8 +5,10 @@
 #include <sys/auxv.h>
 #include <asm/hwcap.h>
 #define zt_arm_has_neon() ((getauxval(AT_HWCAP) & HWCAP_NEON) != 0)
-#else
+#elif defined(__ARM_NEON__) || defined(__ARM_NEON)
 #define zt_arm_has_neon() (true)
+#else
+#define zt_arm_has_neon() (false)
 #endif
 
 #ifdef __cplusplus

+ 2751 - 0
ext/curl-7.58.0/Win32/include/curl/curl.h

@@ -0,0 +1,2751 @@
+#ifndef __CURL_CURL_H
+#define __CURL_CURL_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * If you have libcurl problems, all docs and details are found here:
+ *   https://curl.haxx.se/libcurl/
+ *
+ * curl-library mailing list subscription and unsubscription web interface:
+ *   https://cool.haxx.se/mailman/listinfo/curl-library/
+ */
+
+#ifdef CURL_NO_OLDIES
+#define CURL_STRICTER
+#endif
+
+#include "curlver.h"         /* libcurl version defines   */
+#include "system.h"          /* determine things run-time */
+
+/*
+ * Define WIN32 when build target is Win32 API
+ */
+
+#if (defined(_WIN32) || defined(__WIN32__)) && \
+     !defined(WIN32) && !defined(__SYMBIAN32__)
+#define WIN32
+#endif
+
+#include <stdio.h>
+#include <limits.h>
+
+#if defined(__FreeBSD__) && (__FreeBSD__ >= 2)
+/* Needed for __FreeBSD_version symbol definition */
+#include <osreldate.h>
+#endif
+
+/* The include stuff here below is mainly for time_t! */
+#include <sys/types.h>
+#include <time.h>
+
+#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__)
+#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \
+      defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H))
+/* The check above prevents the winsock2 inclusion if winsock.h already was
+   included, since they can't co-exist without problems */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+#endif
+
+/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish
+   libc5-based Linux systems. Only include it on systems that are known to
+   require it! */
+#if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \
+    defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \
+    defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \
+    defined(__CYGWIN__) || \
+   (defined(__FreeBSD_version) && (__FreeBSD_version < 800000))
+#include <sys/select.h>
+#endif
+
+#if !defined(WIN32) && !defined(_WIN32_WCE)
+#include <sys/socket.h>
+#endif
+
+#if !defined(WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__)
+#include <sys/time.h>
+#endif
+
+#ifdef __BEOS__
+#include <support/SupportDefs.h>
+#endif
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER)
+typedef struct Curl_easy CURL;
+typedef struct Curl_share CURLSH;
+#else
+typedef void CURL;
+typedef void CURLSH;
+#endif
+
+/*
+ * libcurl external API function linkage decorations.
+ */
+
+#ifdef CURL_STATICLIB
+#  define CURL_EXTERN
+#elif defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__)
+#  if defined(BUILDING_LIBCURL)
+#    define CURL_EXTERN  __declspec(dllexport)
+#  else
+#    define CURL_EXTERN  __declspec(dllimport)
+#  endif
+#elif defined(BUILDING_LIBCURL) && defined(CURL_HIDDEN_SYMBOLS)
+#  define CURL_EXTERN CURL_EXTERN_SYMBOL
+#else
+#  define CURL_EXTERN
+#endif
+
+#ifndef curl_socket_typedef
+/* socket typedef */
+#if defined(WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
+typedef SOCKET curl_socket_t;
+#define CURL_SOCKET_BAD INVALID_SOCKET
+#else
+typedef int curl_socket_t;
+#define CURL_SOCKET_BAD -1
+#endif
+#define curl_socket_typedef
+#endif /* curl_socket_typedef */
+
+/* enum for the different supported SSL backends */
+typedef enum {
+  CURLSSLBACKEND_NONE = 0,
+  CURLSSLBACKEND_OPENSSL = 1,
+  CURLSSLBACKEND_GNUTLS = 2,
+  CURLSSLBACKEND_NSS = 3,
+  CURLSSLBACKEND_OBSOLETE4 = 4,  /* Was QSOSSL. */
+  CURLSSLBACKEND_GSKIT = 5,
+  CURLSSLBACKEND_POLARSSL = 6,
+  CURLSSLBACKEND_WOLFSSL = 7,
+  CURLSSLBACKEND_SCHANNEL = 8,
+  CURLSSLBACKEND_DARWINSSL = 9,
+  CURLSSLBACKEND_AXTLS = 10,
+  CURLSSLBACKEND_MBEDTLS = 11
+} curl_sslbackend;
+
+/* aliases for library clones and renames */
+#define CURLSSLBACKEND_LIBRESSL CURLSSLBACKEND_OPENSSL
+#define CURLSSLBACKEND_BORINGSSL CURLSSLBACKEND_OPENSSL
+#define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL
+
+struct curl_httppost {
+  struct curl_httppost *next;       /* next entry in the list */
+  char *name;                       /* pointer to allocated name */
+  long namelength;                  /* length of name length */
+  char *contents;                   /* pointer to allocated data contents */
+  long contentslength;              /* length of contents field, see also
+                                       CURL_HTTPPOST_LARGE */
+  char *buffer;                     /* pointer to allocated buffer contents */
+  long bufferlength;                /* length of buffer field */
+  char *contenttype;                /* Content-Type */
+  struct curl_slist *contentheader; /* list of extra headers for this form */
+  struct curl_httppost *more;       /* if one field name has more than one
+                                       file, this link should link to following
+                                       files */
+  long flags;                       /* as defined below */
+
+/* specified content is a file name */
+#define CURL_HTTPPOST_FILENAME (1<<0)
+/* specified content is a file name */
+#define CURL_HTTPPOST_READFILE (1<<1)
+/* name is only stored pointer do not free in formfree */
+#define CURL_HTTPPOST_PTRNAME (1<<2)
+/* contents is only stored pointer do not free in formfree */
+#define CURL_HTTPPOST_PTRCONTENTS (1<<3)
+/* upload file from buffer */
+#define CURL_HTTPPOST_BUFFER (1<<4)
+/* upload file from pointer contents */
+#define CURL_HTTPPOST_PTRBUFFER (1<<5)
+/* upload file contents by using the regular read callback to get the data and
+   pass the given pointer as custom pointer */
+#define CURL_HTTPPOST_CALLBACK (1<<6)
+/* use size in 'contentlen', added in 7.46.0 */
+#define CURL_HTTPPOST_LARGE (1<<7)
+
+  char *showfilename;               /* The file name to show. If not set, the
+                                       actual file name will be used (if this
+                                       is a file part) */
+  void *userp;                      /* custom pointer used for
+                                       HTTPPOST_CALLBACK posts */
+  curl_off_t contentlen;            /* alternative length of contents
+                                       field. Used if CURL_HTTPPOST_LARGE is
+                                       set. Added in 7.46.0 */
+};
+
+/* This is the CURLOPT_PROGRESSFUNCTION callback proto. It is now considered
+   deprecated but was the only choice up until 7.31.0 */
+typedef int (*curl_progress_callback)(void *clientp,
+                                      double dltotal,
+                                      double dlnow,
+                                      double ultotal,
+                                      double ulnow);
+
+/* This is the CURLOPT_XFERINFOFUNCTION callback proto. It was introduced in
+   7.32.0, it avoids floating point and provides more detailed information. */
+typedef int (*curl_xferinfo_callback)(void *clientp,
+                                      curl_off_t dltotal,
+                                      curl_off_t dlnow,
+                                      curl_off_t ultotal,
+                                      curl_off_t ulnow);
+
+#ifndef CURL_MAX_READ_SIZE
+  /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */
+#define CURL_MAX_READ_SIZE 524288
+#endif
+
+#ifndef CURL_MAX_WRITE_SIZE
+  /* Tests have proven that 20K is a very bad buffer size for uploads on
+     Windows, while 16K for some odd reason performed a lot better.
+     We do the ifndef check to allow this value to easier be changed at build
+     time for those who feel adventurous. The practical minimum is about
+     400 bytes since libcurl uses a buffer of this size as a scratch area
+     (unrelated to network send operations). */
+#define CURL_MAX_WRITE_SIZE 16384
+#endif
+
+#ifndef CURL_MAX_HTTP_HEADER
+/* The only reason to have a max limit for this is to avoid the risk of a bad
+   server feeding libcurl with a never-ending header that will cause reallocs
+   infinitely */
+#define CURL_MAX_HTTP_HEADER (100*1024)
+#endif
+
+/* This is a magic return code for the write callback that, when returned,
+   will signal libcurl to pause receiving on the current transfer. */
+#define CURL_WRITEFUNC_PAUSE 0x10000001
+
+typedef size_t (*curl_write_callback)(char *buffer,
+                                      size_t size,
+                                      size_t nitems,
+                                      void *outstream);
+
+
+
+/* enumeration of file types */
+typedef enum {
+  CURLFILETYPE_FILE = 0,
+  CURLFILETYPE_DIRECTORY,
+  CURLFILETYPE_SYMLINK,
+  CURLFILETYPE_DEVICE_BLOCK,
+  CURLFILETYPE_DEVICE_CHAR,
+  CURLFILETYPE_NAMEDPIPE,
+  CURLFILETYPE_SOCKET,
+  CURLFILETYPE_DOOR, /* is possible only on Sun Solaris now */
+
+  CURLFILETYPE_UNKNOWN /* should never occur */
+} curlfiletype;
+
+#define CURLFINFOFLAG_KNOWN_FILENAME    (1<<0)
+#define CURLFINFOFLAG_KNOWN_FILETYPE    (1<<1)
+#define CURLFINFOFLAG_KNOWN_TIME        (1<<2)
+#define CURLFINFOFLAG_KNOWN_PERM        (1<<3)
+#define CURLFINFOFLAG_KNOWN_UID         (1<<4)
+#define CURLFINFOFLAG_KNOWN_GID         (1<<5)
+#define CURLFINFOFLAG_KNOWN_SIZE        (1<<6)
+#define CURLFINFOFLAG_KNOWN_HLINKCOUNT  (1<<7)
+
+/* Content of this structure depends on information which is known and is
+   achievable (e.g. by FTP LIST parsing). Please see the url_easy_setopt(3) man
+   page for callbacks returning this structure -- some fields are mandatory,
+   some others are optional. The FLAG field has special meaning. */
+struct curl_fileinfo {
+  char *filename;
+  curlfiletype filetype;
+  time_t time;
+  unsigned int perm;
+  int uid;
+  int gid;
+  curl_off_t size;
+  long int hardlinks;
+
+  struct {
+    /* If some of these fields is not NULL, it is a pointer to b_data. */
+    char *time;
+    char *perm;
+    char *user;
+    char *group;
+    char *target; /* pointer to the target filename of a symlink */
+  } strings;
+
+  unsigned int flags;
+
+  /* used internally */
+  char *b_data;
+  size_t b_size;
+  size_t b_used;
+};
+
+/* return codes for CURLOPT_CHUNK_BGN_FUNCTION */
+#define CURL_CHUNK_BGN_FUNC_OK      0
+#define CURL_CHUNK_BGN_FUNC_FAIL    1 /* tell the lib to end the task */
+#define CURL_CHUNK_BGN_FUNC_SKIP    2 /* skip this chunk over */
+
+/* if splitting of data transfer is enabled, this callback is called before
+   download of an individual chunk started. Note that parameter "remains" works
+   only for FTP wildcard downloading (for now), otherwise is not used */
+typedef long (*curl_chunk_bgn_callback)(const void *transfer_info,
+                                        void *ptr,
+                                        int remains);
+
+/* return codes for CURLOPT_CHUNK_END_FUNCTION */
+#define CURL_CHUNK_END_FUNC_OK      0
+#define CURL_CHUNK_END_FUNC_FAIL    1 /* tell the lib to end the task */
+
+/* If splitting of data transfer is enabled this callback is called after
+   download of an individual chunk finished.
+   Note! After this callback was set then it have to be called FOR ALL chunks.
+   Even if downloading of this chunk was skipped in CHUNK_BGN_FUNC.
+   This is the reason why we don't need "transfer_info" parameter in this
+   callback and we are not interested in "remains" parameter too. */
+typedef long (*curl_chunk_end_callback)(void *ptr);
+
+/* return codes for FNMATCHFUNCTION */
+#define CURL_FNMATCHFUNC_MATCH    0 /* string corresponds to the pattern */
+#define CURL_FNMATCHFUNC_NOMATCH  1 /* pattern doesn't match the string */
+#define CURL_FNMATCHFUNC_FAIL     2 /* an error occurred */
+
+/* callback type for wildcard downloading pattern matching. If the
+   string matches the pattern, return CURL_FNMATCHFUNC_MATCH value, etc. */
+typedef int (*curl_fnmatch_callback)(void *ptr,
+                                     const char *pattern,
+                                     const char *string);
+
+/* These are the return codes for the seek callbacks */
+#define CURL_SEEKFUNC_OK       0
+#define CURL_SEEKFUNC_FAIL     1 /* fail the entire transfer */
+#define CURL_SEEKFUNC_CANTSEEK 2 /* tell libcurl seeking can't be done, so
+                                    libcurl might try other means instead */
+typedef int (*curl_seek_callback)(void *instream,
+                                  curl_off_t offset,
+                                  int origin); /* 'whence' */
+
+/* This is a return code for the read callback that, when returned, will
+   signal libcurl to immediately abort the current transfer. */
+#define CURL_READFUNC_ABORT 0x10000000
+/* This is a return code for the read callback that, when returned, will
+   signal libcurl to pause sending data on the current transfer. */
+#define CURL_READFUNC_PAUSE 0x10000001
+
+typedef size_t (*curl_read_callback)(char *buffer,
+                                      size_t size,
+                                      size_t nitems,
+                                      void *instream);
+
+typedef enum {
+  CURLSOCKTYPE_IPCXN,  /* socket created for a specific IP connection */
+  CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */
+  CURLSOCKTYPE_LAST    /* never use */
+} curlsocktype;
+
+/* The return code from the sockopt_callback can signal information back
+   to libcurl: */
+#define CURL_SOCKOPT_OK 0
+#define CURL_SOCKOPT_ERROR 1 /* causes libcurl to abort and return
+                                CURLE_ABORTED_BY_CALLBACK */
+#define CURL_SOCKOPT_ALREADY_CONNECTED 2
+
+typedef int (*curl_sockopt_callback)(void *clientp,
+                                     curl_socket_t curlfd,
+                                     curlsocktype purpose);
+
+struct curl_sockaddr {
+  int family;
+  int socktype;
+  int protocol;
+  unsigned int addrlen; /* addrlen was a socklen_t type before 7.18.0 but it
+                           turned really ugly and painful on the systems that
+                           lack this type */
+  struct sockaddr addr;
+};
+
+typedef curl_socket_t
+(*curl_opensocket_callback)(void *clientp,
+                            curlsocktype purpose,
+                            struct curl_sockaddr *address);
+
+typedef int
+(*curl_closesocket_callback)(void *clientp, curl_socket_t item);
+
+typedef enum {
+  CURLIOE_OK,            /* I/O operation successful */
+  CURLIOE_UNKNOWNCMD,    /* command was unknown to callback */
+  CURLIOE_FAILRESTART,   /* failed to restart the read */
+  CURLIOE_LAST           /* never use */
+} curlioerr;
+
+typedef enum {
+  CURLIOCMD_NOP,         /* no operation */
+  CURLIOCMD_RESTARTREAD, /* restart the read stream from start */
+  CURLIOCMD_LAST         /* never use */
+} curliocmd;
+
+typedef curlioerr (*curl_ioctl_callback)(CURL *handle,
+                                         int cmd,
+                                         void *clientp);
+
+#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS
+/*
+ * The following typedef's are signatures of malloc, free, realloc, strdup and
+ * calloc respectively.  Function pointers of these types can be passed to the
+ * curl_global_init_mem() function to set user defined memory management
+ * callback routines.
+ */
+typedef void *(*curl_malloc_callback)(size_t size);
+typedef void (*curl_free_callback)(void *ptr);
+typedef void *(*curl_realloc_callback)(void *ptr, size_t size);
+typedef char *(*curl_strdup_callback)(const char *str);
+typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size);
+
+#define CURL_DID_MEMORY_FUNC_TYPEDEFS
+#endif
+
+/* the kind of data that is passed to information_callback*/
+typedef enum {
+  CURLINFO_TEXT = 0,
+  CURLINFO_HEADER_IN,    /* 1 */
+  CURLINFO_HEADER_OUT,   /* 2 */
+  CURLINFO_DATA_IN,      /* 3 */
+  CURLINFO_DATA_OUT,     /* 4 */
+  CURLINFO_SSL_DATA_IN,  /* 5 */
+  CURLINFO_SSL_DATA_OUT, /* 6 */
+  CURLINFO_END
+} curl_infotype;
+
+typedef int (*curl_debug_callback)
+       (CURL *handle,      /* the handle/transfer this concerns */
+        curl_infotype type, /* what kind of data */
+        char *data,        /* points to the data */
+        size_t size,       /* size of the data pointed to */
+        void *userptr);    /* whatever the user please */
+
+/* All possible error codes from all sorts of curl functions. Future versions
+   may return other values, stay prepared.
+
+   Always add new return codes last. Never *EVER* remove any. The return
+   codes must remain the same!
+ */
+
+typedef enum {
+  CURLE_OK = 0,
+  CURLE_UNSUPPORTED_PROTOCOL,    /* 1 */
+  CURLE_FAILED_INIT,             /* 2 */
+  CURLE_URL_MALFORMAT,           /* 3 */
+  CURLE_NOT_BUILT_IN,            /* 4 - [was obsoleted in August 2007 for
+                                    7.17.0, reused in April 2011 for 7.21.5] */
+  CURLE_COULDNT_RESOLVE_PROXY,   /* 5 */
+  CURLE_COULDNT_RESOLVE_HOST,    /* 6 */
+  CURLE_COULDNT_CONNECT,         /* 7 */
+  CURLE_WEIRD_SERVER_REPLY,      /* 8 */
+  CURLE_REMOTE_ACCESS_DENIED,    /* 9 a service was denied by the server
+                                    due to lack of access - when login fails
+                                    this is not returned. */
+  CURLE_FTP_ACCEPT_FAILED,       /* 10 - [was obsoleted in April 2006 for
+                                    7.15.4, reused in Dec 2011 for 7.24.0]*/
+  CURLE_FTP_WEIRD_PASS_REPLY,    /* 11 */
+  CURLE_FTP_ACCEPT_TIMEOUT,      /* 12 - timeout occurred accepting server
+                                    [was obsoleted in August 2007 for 7.17.0,
+                                    reused in Dec 2011 for 7.24.0]*/
+  CURLE_FTP_WEIRD_PASV_REPLY,    /* 13 */
+  CURLE_FTP_WEIRD_227_FORMAT,    /* 14 */
+  CURLE_FTP_CANT_GET_HOST,       /* 15 */
+  CURLE_HTTP2,                   /* 16 - A problem in the http2 framing layer.
+                                    [was obsoleted in August 2007 for 7.17.0,
+                                    reused in July 2014 for 7.38.0] */
+  CURLE_FTP_COULDNT_SET_TYPE,    /* 17 */
+  CURLE_PARTIAL_FILE,            /* 18 */
+  CURLE_FTP_COULDNT_RETR_FILE,   /* 19 */
+  CURLE_OBSOLETE20,              /* 20 - NOT USED */
+  CURLE_QUOTE_ERROR,             /* 21 - quote command failure */
+  CURLE_HTTP_RETURNED_ERROR,     /* 22 */
+  CURLE_WRITE_ERROR,             /* 23 */
+  CURLE_OBSOLETE24,              /* 24 - NOT USED */
+  CURLE_UPLOAD_FAILED,           /* 25 - failed upload "command" */
+  CURLE_READ_ERROR,              /* 26 - couldn't open/read from file */
+  CURLE_OUT_OF_MEMORY,           /* 27 */
+  /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error
+           instead of a memory allocation error if CURL_DOES_CONVERSIONS
+           is defined
+  */
+  CURLE_OPERATION_TIMEDOUT,      /* 28 - the timeout time was reached */
+  CURLE_OBSOLETE29,              /* 29 - NOT USED */
+  CURLE_FTP_PORT_FAILED,         /* 30 - FTP PORT operation failed */
+  CURLE_FTP_COULDNT_USE_REST,    /* 31 - the REST command failed */
+  CURLE_OBSOLETE32,              /* 32 - NOT USED */
+  CURLE_RANGE_ERROR,             /* 33 - RANGE "command" didn't work */
+  CURLE_HTTP_POST_ERROR,         /* 34 */
+  CURLE_SSL_CONNECT_ERROR,       /* 35 - wrong when connecting with SSL */
+  CURLE_BAD_DOWNLOAD_RESUME,     /* 36 - couldn't resume download */
+  CURLE_FILE_COULDNT_READ_FILE,  /* 37 */
+  CURLE_LDAP_CANNOT_BIND,        /* 38 */
+  CURLE_LDAP_SEARCH_FAILED,      /* 39 */
+  CURLE_OBSOLETE40,              /* 40 - NOT USED */
+  CURLE_FUNCTION_NOT_FOUND,      /* 41 - NOT USED starting with 7.53.0 */
+  CURLE_ABORTED_BY_CALLBACK,     /* 42 */
+  CURLE_BAD_FUNCTION_ARGUMENT,   /* 43 */
+  CURLE_OBSOLETE44,              /* 44 - NOT USED */
+  CURLE_INTERFACE_FAILED,        /* 45 - CURLOPT_INTERFACE failed */
+  CURLE_OBSOLETE46,              /* 46 - NOT USED */
+  CURLE_TOO_MANY_REDIRECTS,      /* 47 - catch endless re-direct loops */
+  CURLE_UNKNOWN_OPTION,          /* 48 - User specified an unknown option */
+  CURLE_TELNET_OPTION_SYNTAX,    /* 49 - Malformed telnet option */
+  CURLE_OBSOLETE50,              /* 50 - NOT USED */
+  CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint
+                                     wasn't verified fine */
+  CURLE_GOT_NOTHING,             /* 52 - when this is a specific error */
+  CURLE_SSL_ENGINE_NOTFOUND,     /* 53 - SSL crypto engine not found */
+  CURLE_SSL_ENGINE_SETFAILED,    /* 54 - can not set SSL crypto engine as
+                                    default */
+  CURLE_SEND_ERROR,              /* 55 - failed sending network data */
+  CURLE_RECV_ERROR,              /* 56 - failure in receiving network data */
+  CURLE_OBSOLETE57,              /* 57 - NOT IN USE */
+  CURLE_SSL_CERTPROBLEM,         /* 58 - problem with the local certificate */
+  CURLE_SSL_CIPHER,              /* 59 - couldn't use specified cipher */
+  CURLE_SSL_CACERT,              /* 60 - problem with the CA cert (path?) */
+  CURLE_BAD_CONTENT_ENCODING,    /* 61 - Unrecognized/bad encoding */
+  CURLE_LDAP_INVALID_URL,        /* 62 - Invalid LDAP URL */
+  CURLE_FILESIZE_EXCEEDED,       /* 63 - Maximum file size exceeded */
+  CURLE_USE_SSL_FAILED,          /* 64 - Requested FTP SSL level failed */
+  CURLE_SEND_FAIL_REWIND,        /* 65 - Sending the data requires a rewind
+                                    that failed */
+  CURLE_SSL_ENGINE_INITFAILED,   /* 66 - failed to initialise ENGINE */
+  CURLE_LOGIN_DENIED,            /* 67 - user, password or similar was not
+                                    accepted and we failed to login */
+  CURLE_TFTP_NOTFOUND,           /* 68 - file not found on server */
+  CURLE_TFTP_PERM,               /* 69 - permission problem on server */
+  CURLE_REMOTE_DISK_FULL,        /* 70 - out of disk space on server */
+  CURLE_TFTP_ILLEGAL,            /* 71 - Illegal TFTP operation */
+  CURLE_TFTP_UNKNOWNID,          /* 72 - Unknown transfer ID */
+  CURLE_REMOTE_FILE_EXISTS,      /* 73 - File already exists */
+  CURLE_TFTP_NOSUCHUSER,         /* 74 - No such user */
+  CURLE_CONV_FAILED,             /* 75 - conversion failed */
+  CURLE_CONV_REQD,               /* 76 - caller must register conversion
+                                    callbacks using curl_easy_setopt options
+                                    CURLOPT_CONV_FROM_NETWORK_FUNCTION,
+                                    CURLOPT_CONV_TO_NETWORK_FUNCTION, and
+                                    CURLOPT_CONV_FROM_UTF8_FUNCTION */
+  CURLE_SSL_CACERT_BADFILE,      /* 77 - could not load CACERT file, missing
+                                    or wrong format */
+  CURLE_REMOTE_FILE_NOT_FOUND,   /* 78 - remote file not found */
+  CURLE_SSH,                     /* 79 - error from the SSH layer, somewhat
+                                    generic so the error message will be of
+                                    interest when this has happened */
+
+  CURLE_SSL_SHUTDOWN_FAILED,     /* 80 - Failed to shut down the SSL
+                                    connection */
+  CURLE_AGAIN,                   /* 81 - socket is not ready for send/recv,
+                                    wait till it's ready and try again (Added
+                                    in 7.18.2) */
+  CURLE_SSL_CRL_BADFILE,         /* 82 - could not load CRL file, missing or
+                                    wrong format (Added in 7.19.0) */
+  CURLE_SSL_ISSUER_ERROR,        /* 83 - Issuer check failed.  (Added in
+                                    7.19.0) */
+  CURLE_FTP_PRET_FAILED,         /* 84 - a PRET command failed */
+  CURLE_RTSP_CSEQ_ERROR,         /* 85 - mismatch of RTSP CSeq numbers */
+  CURLE_RTSP_SESSION_ERROR,      /* 86 - mismatch of RTSP Session Ids */
+  CURLE_FTP_BAD_FILE_LIST,       /* 87 - unable to parse FTP file list */
+  CURLE_CHUNK_FAILED,            /* 88 - chunk callback reported error */
+  CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the
+                                    session will be queued */
+  CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not
+                                     match */
+  CURLE_SSL_INVALIDCERTSTATUS,   /* 91 - invalid certificate status */
+  CURLE_HTTP2_STREAM,            /* 92 - stream error in HTTP/2 framing layer
+                                    */
+  CURL_LAST /* never use! */
+} CURLcode;
+
+#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
+                          the obsolete stuff removed! */
+
+/* Previously obsolete error code re-used in 7.38.0 */
+#define CURLE_OBSOLETE16 CURLE_HTTP2
+
+/* Previously obsolete error codes re-used in 7.24.0 */
+#define CURLE_OBSOLETE10 CURLE_FTP_ACCEPT_FAILED
+#define CURLE_OBSOLETE12 CURLE_FTP_ACCEPT_TIMEOUT
+
+/*  compatibility with older names */
+#define CURLOPT_ENCODING CURLOPT_ACCEPT_ENCODING
+#define CURLE_FTP_WEIRD_SERVER_REPLY CURLE_WEIRD_SERVER_REPLY
+
+/* The following were added in 7.21.5, April 2011 */
+#define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION
+
+/* The following were added in 7.17.1 */
+/* These are scheduled to disappear by 2009 */
+#define CURLE_SSL_PEER_CERTIFICATE CURLE_PEER_FAILED_VERIFICATION
+
+/* The following were added in 7.17.0 */
+/* These are scheduled to disappear by 2009 */
+#define CURLE_OBSOLETE CURLE_OBSOLETE50 /* no one should be using this! */
+#define CURLE_BAD_PASSWORD_ENTERED CURLE_OBSOLETE46
+#define CURLE_BAD_CALLING_ORDER CURLE_OBSOLETE44
+#define CURLE_FTP_USER_PASSWORD_INCORRECT CURLE_OBSOLETE10
+#define CURLE_FTP_CANT_RECONNECT CURLE_OBSOLETE16
+#define CURLE_FTP_COULDNT_GET_SIZE CURLE_OBSOLETE32
+#define CURLE_FTP_COULDNT_SET_ASCII CURLE_OBSOLETE29
+#define CURLE_FTP_WEIRD_USER_REPLY CURLE_OBSOLETE12
+#define CURLE_FTP_WRITE_ERROR CURLE_OBSOLETE20
+#define CURLE_LIBRARY_NOT_FOUND CURLE_OBSOLETE40
+#define CURLE_MALFORMAT_USER CURLE_OBSOLETE24
+#define CURLE_SHARE_IN_USE CURLE_OBSOLETE57
+#define CURLE_URL_MALFORMAT_USER CURLE_NOT_BUILT_IN
+
+#define CURLE_FTP_ACCESS_DENIED CURLE_REMOTE_ACCESS_DENIED
+#define CURLE_FTP_COULDNT_SET_BINARY CURLE_FTP_COULDNT_SET_TYPE
+#define CURLE_FTP_QUOTE_ERROR CURLE_QUOTE_ERROR
+#define CURLE_TFTP_DISKFULL CURLE_REMOTE_DISK_FULL
+#define CURLE_TFTP_EXISTS CURLE_REMOTE_FILE_EXISTS
+#define CURLE_HTTP_RANGE_ERROR CURLE_RANGE_ERROR
+#define CURLE_FTP_SSL_FAILED CURLE_USE_SSL_FAILED
+
+/* The following were added earlier */
+
+#define CURLE_OPERATION_TIMEOUTED CURLE_OPERATION_TIMEDOUT
+
+#define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR
+#define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED
+#define CURLE_FTP_COULDNT_STOR_FILE CURLE_UPLOAD_FAILED
+
+#define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE
+#define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME
+
+/* This was the error code 50 in 7.7.3 and a few earlier versions, this
+   is no longer used by libcurl but is instead #defined here only to not
+   make programs break */
+#define CURLE_ALREADY_COMPLETE 99999
+
+/* Provide defines for really old option names */
+#define CURLOPT_FILE CURLOPT_WRITEDATA /* name changed in 7.9.7 */
+#define CURLOPT_INFILE CURLOPT_READDATA /* name changed in 7.9.7 */
+#define CURLOPT_WRITEHEADER CURLOPT_HEADERDATA
+
+/* Since long deprecated options with no code in the lib that does anything
+   with them. */
+#define CURLOPT_WRITEINFO CURLOPT_OBSOLETE40
+#define CURLOPT_CLOSEPOLICY CURLOPT_OBSOLETE72
+
+#endif /*!CURL_NO_OLDIES*/
+
+/* This prototype applies to all conversion callbacks */
+typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length);
+
+typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl,    /* easy handle */
+                                          void *ssl_ctx, /* actually an
+                                                            OpenSSL SSL_CTX */
+                                          void *userptr);
+
+typedef enum {
+  CURLPROXY_HTTP = 0,   /* added in 7.10, new in 7.19.4 default is to use
+                           CONNECT HTTP/1.1 */
+  CURLPROXY_HTTP_1_0 = 1,   /* added in 7.19.4, force to use CONNECT
+                               HTTP/1.0  */
+  CURLPROXY_HTTPS = 2, /* added in 7.52.0 */
+  CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already
+                           in 7.10 */
+  CURLPROXY_SOCKS5 = 5, /* added in 7.10 */
+  CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */
+  CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the
+                                   host name rather than the IP address. added
+                                   in 7.18.0 */
+} curl_proxytype;  /* this enum was added in 7.10 */
+
+/*
+ * Bitmasks for CURLOPT_HTTPAUTH and CURLOPT_PROXYAUTH options:
+ *
+ * CURLAUTH_NONE         - No HTTP authentication
+ * CURLAUTH_BASIC        - HTTP Basic authentication (default)
+ * CURLAUTH_DIGEST       - HTTP Digest authentication
+ * CURLAUTH_NEGOTIATE    - HTTP Negotiate (SPNEGO) authentication
+ * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated)
+ * CURLAUTH_NTLM         - HTTP NTLM authentication
+ * CURLAUTH_DIGEST_IE    - HTTP Digest authentication with IE flavour
+ * CURLAUTH_NTLM_WB      - HTTP NTLM authentication delegated to winbind helper
+ * CURLAUTH_ONLY         - Use together with a single other type to force no
+ *                         authentication or just that single type
+ * CURLAUTH_ANY          - All fine types set
+ * CURLAUTH_ANYSAFE      - All fine types except Basic
+ */
+
+#define CURLAUTH_NONE         ((unsigned long)0)
+#define CURLAUTH_BASIC        (((unsigned long)1)<<0)
+#define CURLAUTH_DIGEST       (((unsigned long)1)<<1)
+#define CURLAUTH_NEGOTIATE    (((unsigned long)1)<<2)
+/* Deprecated since the advent of CURLAUTH_NEGOTIATE */
+#define CURLAUTH_GSSNEGOTIATE CURLAUTH_NEGOTIATE
+/* Used for CURLOPT_SOCKS5_AUTH to stay terminologically correct */
+#define CURLAUTH_GSSAPI CURLAUTH_NEGOTIATE
+#define CURLAUTH_NTLM         (((unsigned long)1)<<3)
+#define CURLAUTH_DIGEST_IE    (((unsigned long)1)<<4)
+#define CURLAUTH_NTLM_WB      (((unsigned long)1)<<5)
+#define CURLAUTH_ONLY         (((unsigned long)1)<<31)
+#define CURLAUTH_ANY          (~CURLAUTH_DIGEST_IE)
+#define CURLAUTH_ANYSAFE      (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE))
+
+#define CURLSSH_AUTH_ANY       ~0     /* all types supported by the server */
+#define CURLSSH_AUTH_NONE      0      /* none allowed, silly but complete */
+#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */
+#define CURLSSH_AUTH_PASSWORD  (1<<1) /* password */
+#define CURLSSH_AUTH_HOST      (1<<2) /* host key files */
+#define CURLSSH_AUTH_KEYBOARD  (1<<3) /* keyboard interactive */
+#define CURLSSH_AUTH_AGENT     (1<<4) /* agent (ssh-agent, pageant...) */
+#define CURLSSH_AUTH_GSSAPI    (1<<5) /* gssapi (kerberos, ...) */
+#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY
+
+#define CURLGSSAPI_DELEGATION_NONE        0      /* no delegation (default) */
+#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */
+#define CURLGSSAPI_DELEGATION_FLAG        (1<<1) /* delegate always */
+
+#define CURL_ERROR_SIZE 256
+
+enum curl_khtype {
+  CURLKHTYPE_UNKNOWN,
+  CURLKHTYPE_RSA1,
+  CURLKHTYPE_RSA,
+  CURLKHTYPE_DSS,
+  CURLKHTYPE_ECDSA,
+  CURLKHTYPE_ED25519
+};
+
+struct curl_khkey {
+  const char *key; /* points to a zero-terminated string encoded with base64
+                      if len is zero, otherwise to the "raw" data */
+  size_t len;
+  enum curl_khtype keytype;
+};
+
+/* this is the set of return values expected from the curl_sshkeycallback
+   callback */
+enum curl_khstat {
+  CURLKHSTAT_FINE_ADD_TO_FILE,
+  CURLKHSTAT_FINE,
+  CURLKHSTAT_REJECT, /* reject the connection, return an error */
+  CURLKHSTAT_DEFER,  /* do not accept it, but we can't answer right now so
+                        this causes a CURLE_DEFER error but otherwise the
+                        connection will be left intact etc */
+  CURLKHSTAT_LAST    /* not for use, only a marker for last-in-list */
+};
+
+/* this is the set of status codes pass in to the callback */
+enum curl_khmatch {
+  CURLKHMATCH_OK,       /* match */
+  CURLKHMATCH_MISMATCH, /* host found, key mismatch! */
+  CURLKHMATCH_MISSING,  /* no matching host/key found */
+  CURLKHMATCH_LAST      /* not for use, only a marker for last-in-list */
+};
+
+typedef int
+  (*curl_sshkeycallback) (CURL *easy,     /* easy handle */
+                          const struct curl_khkey *knownkey, /* known */
+                          const struct curl_khkey *foundkey, /* found */
+                          enum curl_khmatch, /* libcurl's view on the keys */
+                          void *clientp); /* custom pointer passed from app */
+
+/* parameter for the CURLOPT_USE_SSL option */
+typedef enum {
+  CURLUSESSL_NONE,    /* do not attempt to use SSL */
+  CURLUSESSL_TRY,     /* try using SSL, proceed anyway otherwise */
+  CURLUSESSL_CONTROL, /* SSL for the control connection or fail */
+  CURLUSESSL_ALL,     /* SSL for all communication or fail */
+  CURLUSESSL_LAST     /* not an option, never use */
+} curl_usessl;
+
+/* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */
+
+/* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the
+   name of improving interoperability with older servers. Some SSL libraries
+   have introduced work-arounds for this flaw but those work-arounds sometimes
+   make the SSL communication fail. To regain functionality with those broken
+   servers, a user can this way allow the vulnerability back. */
+#define CURLSSLOPT_ALLOW_BEAST (1<<0)
+
+/* - NO_REVOKE tells libcurl to disable certificate revocation checks for those
+   SSL backends where such behavior is present. */
+#define CURLSSLOPT_NO_REVOKE (1<<1)
+
+#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
+                          the obsolete stuff removed! */
+
+/* Backwards compatibility with older names */
+/* These are scheduled to disappear by 2009 */
+
+#define CURLFTPSSL_NONE CURLUSESSL_NONE
+#define CURLFTPSSL_TRY CURLUSESSL_TRY
+#define CURLFTPSSL_CONTROL CURLUSESSL_CONTROL
+#define CURLFTPSSL_ALL CURLUSESSL_ALL
+#define CURLFTPSSL_LAST CURLUSESSL_LAST
+#define curl_ftpssl curl_usessl
+#endif /*!CURL_NO_OLDIES*/
+
+/* parameter for the CURLOPT_FTP_SSL_CCC option */
+typedef enum {
+  CURLFTPSSL_CCC_NONE,    /* do not send CCC */
+  CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */
+  CURLFTPSSL_CCC_ACTIVE,  /* Initiate the shutdown */
+  CURLFTPSSL_CCC_LAST     /* not an option, never use */
+} curl_ftpccc;
+
+/* parameter for the CURLOPT_FTPSSLAUTH option */
+typedef enum {
+  CURLFTPAUTH_DEFAULT, /* let libcurl decide */
+  CURLFTPAUTH_SSL,     /* use "AUTH SSL" */
+  CURLFTPAUTH_TLS,     /* use "AUTH TLS" */
+  CURLFTPAUTH_LAST /* not an option, never use */
+} curl_ftpauth;
+
+/* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */
+typedef enum {
+  CURLFTP_CREATE_DIR_NONE,  /* do NOT create missing dirs! */
+  CURLFTP_CREATE_DIR,       /* (FTP/SFTP) if CWD fails, try MKD and then CWD
+                               again if MKD succeeded, for SFTP this does
+                               similar magic */
+  CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD
+                               again even if MKD failed! */
+  CURLFTP_CREATE_DIR_LAST   /* not an option, never use */
+} curl_ftpcreatedir;
+
+/* parameter for the CURLOPT_FTP_FILEMETHOD option */
+typedef enum {
+  CURLFTPMETHOD_DEFAULT,   /* let libcurl pick */
+  CURLFTPMETHOD_MULTICWD,  /* single CWD operation for each path part */
+  CURLFTPMETHOD_NOCWD,     /* no CWD at all */
+  CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */
+  CURLFTPMETHOD_LAST       /* not an option, never use */
+} curl_ftpmethod;
+
+/* bitmask defines for CURLOPT_HEADEROPT */
+#define CURLHEADER_UNIFIED  0
+#define CURLHEADER_SEPARATE (1<<0)
+
+/* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */
+#define CURLPROTO_HTTP   (1<<0)
+#define CURLPROTO_HTTPS  (1<<1)
+#define CURLPROTO_FTP    (1<<2)
+#define CURLPROTO_FTPS   (1<<3)
+#define CURLPROTO_SCP    (1<<4)
+#define CURLPROTO_SFTP   (1<<5)
+#define CURLPROTO_TELNET (1<<6)
+#define CURLPROTO_LDAP   (1<<7)
+#define CURLPROTO_LDAPS  (1<<8)
+#define CURLPROTO_DICT   (1<<9)
+#define CURLPROTO_FILE   (1<<10)
+#define CURLPROTO_TFTP   (1<<11)
+#define CURLPROTO_IMAP   (1<<12)
+#define CURLPROTO_IMAPS  (1<<13)
+#define CURLPROTO_POP3   (1<<14)
+#define CURLPROTO_POP3S  (1<<15)
+#define CURLPROTO_SMTP   (1<<16)
+#define CURLPROTO_SMTPS  (1<<17)
+#define CURLPROTO_RTSP   (1<<18)
+#define CURLPROTO_RTMP   (1<<19)
+#define CURLPROTO_RTMPT  (1<<20)
+#define CURLPROTO_RTMPE  (1<<21)
+#define CURLPROTO_RTMPTE (1<<22)
+#define CURLPROTO_RTMPS  (1<<23)
+#define CURLPROTO_RTMPTS (1<<24)
+#define CURLPROTO_GOPHER (1<<25)
+#define CURLPROTO_SMB    (1<<26)
+#define CURLPROTO_SMBS   (1<<27)
+#define CURLPROTO_ALL    (~0) /* enable everything */
+
+/* long may be 32 or 64 bits, but we should never depend on anything else
+   but 32 */
+#define CURLOPTTYPE_LONG          0
+#define CURLOPTTYPE_OBJECTPOINT   10000
+#define CURLOPTTYPE_STRINGPOINT   10000
+#define CURLOPTTYPE_FUNCTIONPOINT 20000
+#define CURLOPTTYPE_OFF_T         30000
+
+/* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the
+   string options from the header file */
+
+/* name is uppercase CURLOPT_<name>,
+   type is one of the defined CURLOPTTYPE_<type>
+   number is unique identifier */
+#ifdef CINIT
+#undef CINIT
+#endif
+
+#ifdef CURL_ISOCPP
+#define CINIT(na,t,nu) CURLOPT_ ## na = CURLOPTTYPE_ ## t + nu
+#else
+/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */
+#define LONG          CURLOPTTYPE_LONG
+#define OBJECTPOINT   CURLOPTTYPE_OBJECTPOINT
+#define STRINGPOINT   CURLOPTTYPE_OBJECTPOINT
+#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT
+#define OFF_T         CURLOPTTYPE_OFF_T
+#define CINIT(name,type,number) CURLOPT_/**/name = type + number
+#endif
+
+/*
+ * This macro-mania below setups the CURLOPT_[what] enum, to be used with
+ * curl_easy_setopt(). The first argument in the CINIT() macro is the [what]
+ * word.
+ */
+
+typedef enum {
+  /* This is the FILE * or void * the regular output should be written to. */
+  CINIT(WRITEDATA, OBJECTPOINT, 1),
+
+  /* The full URL to get/put */
+  CINIT(URL, STRINGPOINT, 2),
+
+  /* Port number to connect to, if other than default. */
+  CINIT(PORT, LONG, 3),
+
+  /* Name of proxy to use. */
+  CINIT(PROXY, STRINGPOINT, 4),
+
+  /* "user:password;options" to use when fetching. */
+  CINIT(USERPWD, STRINGPOINT, 5),
+
+  /* "user:password" to use with proxy. */
+  CINIT(PROXYUSERPWD, STRINGPOINT, 6),
+
+  /* Range to get, specified as an ASCII string. */
+  CINIT(RANGE, STRINGPOINT, 7),
+
+  /* not used */
+
+  /* Specified file stream to upload from (use as input): */
+  CINIT(READDATA, OBJECTPOINT, 9),
+
+  /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE
+   * bytes big. */
+  CINIT(ERRORBUFFER, OBJECTPOINT, 10),
+
+  /* Function that will be called to store the output (instead of fwrite). The
+   * parameters will use fwrite() syntax, make sure to follow them. */
+  CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11),
+
+  /* Function that will be called to read the input (instead of fread). The
+   * parameters will use fread() syntax, make sure to follow them. */
+  CINIT(READFUNCTION, FUNCTIONPOINT, 12),
+
+  /* Time-out the read operation after this amount of seconds */
+  CINIT(TIMEOUT, LONG, 13),
+
+  /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about
+   * how large the file being sent really is. That allows better error
+   * checking and better verifies that the upload was successful. -1 means
+   * unknown size.
+   *
+   * For large file support, there is also a _LARGE version of the key
+   * which takes an off_t type, allowing platforms with larger off_t
+   * sizes to handle larger files.  See below for INFILESIZE_LARGE.
+   */
+  CINIT(INFILESIZE, LONG, 14),
+
+  /* POST static input fields. */
+  CINIT(POSTFIELDS, OBJECTPOINT, 15),
+
+  /* Set the referrer page (needed by some CGIs) */
+  CINIT(REFERER, STRINGPOINT, 16),
+
+  /* Set the FTP PORT string (interface name, named or numerical IP address)
+     Use i.e '-' to use default address. */
+  CINIT(FTPPORT, STRINGPOINT, 17),
+
+  /* Set the User-Agent string (examined by some CGIs) */
+  CINIT(USERAGENT, STRINGPOINT, 18),
+
+  /* If the download receives less than "low speed limit" bytes/second
+   * during "low speed time" seconds, the operations is aborted.
+   * You could i.e if you have a pretty high speed connection, abort if
+   * it is less than 2000 bytes/sec during 20 seconds.
+   */
+
+  /* Set the "low speed limit" */
+  CINIT(LOW_SPEED_LIMIT, LONG, 19),
+
+  /* Set the "low speed time" */
+  CINIT(LOW_SPEED_TIME, LONG, 20),
+
+  /* Set the continuation offset.
+   *
+   * Note there is also a _LARGE version of this key which uses
+   * off_t types, allowing for large file offsets on platforms which
+   * use larger-than-32-bit off_t's.  Look below for RESUME_FROM_LARGE.
+   */
+  CINIT(RESUME_FROM, LONG, 21),
+
+  /* Set cookie in request: */
+  CINIT(COOKIE, STRINGPOINT, 22),
+
+  /* This points to a linked list of headers, struct curl_slist kind. This
+     list is also used for RTSP (in spite of its name) */
+  CINIT(HTTPHEADER, OBJECTPOINT, 23),
+
+  /* This points to a linked list of post entries, struct curl_httppost */
+  CINIT(HTTPPOST, OBJECTPOINT, 24),
+
+  /* name of the file keeping your private SSL-certificate */
+  CINIT(SSLCERT, STRINGPOINT, 25),
+
+  /* password for the SSL or SSH private key */
+  CINIT(KEYPASSWD, STRINGPOINT, 26),
+
+  /* send TYPE parameter? */
+  CINIT(CRLF, LONG, 27),
+
+  /* send linked-list of QUOTE commands */
+  CINIT(QUOTE, OBJECTPOINT, 28),
+
+  /* send FILE * or void * to store headers to, if you use a callback it
+     is simply passed to the callback unmodified */
+  CINIT(HEADERDATA, OBJECTPOINT, 29),
+
+  /* point to a file to read the initial cookies from, also enables
+     "cookie awareness" */
+  CINIT(COOKIEFILE, STRINGPOINT, 31),
+
+  /* What version to specifically try to use.
+     See CURL_SSLVERSION defines below. */
+  CINIT(SSLVERSION, LONG, 32),
+
+  /* What kind of HTTP time condition to use, see defines */
+  CINIT(TIMECONDITION, LONG, 33),
+
+  /* Time to use with the above condition. Specified in number of seconds
+     since 1 Jan 1970 */
+  CINIT(TIMEVALUE, LONG, 34),
+
+  /* 35 = OBSOLETE */
+
+  /* Custom request, for customizing the get command like
+     HTTP: DELETE, TRACE and others
+     FTP: to use a different list command
+     */
+  CINIT(CUSTOMREQUEST, STRINGPOINT, 36),
+
+  /* FILE handle to use instead of stderr */
+  CINIT(STDERR, OBJECTPOINT, 37),
+
+  /* 38 is not used */
+
+  /* send linked-list of post-transfer QUOTE commands */
+  CINIT(POSTQUOTE, OBJECTPOINT, 39),
+
+  CINIT(OBSOLETE40, OBJECTPOINT, 40), /* OBSOLETE, do not use! */
+
+  CINIT(VERBOSE, LONG, 41),      /* talk a lot */
+  CINIT(HEADER, LONG, 42),       /* throw the header out too */
+  CINIT(NOPROGRESS, LONG, 43),   /* shut off the progress meter */
+  CINIT(NOBODY, LONG, 44),       /* use HEAD to get http document */
+  CINIT(FAILONERROR, LONG, 45),  /* no output on http error codes >= 400 */
+  CINIT(UPLOAD, LONG, 46),       /* this is an upload */
+  CINIT(POST, LONG, 47),         /* HTTP POST method */
+  CINIT(DIRLISTONLY, LONG, 48),  /* bare names when listing directories */
+
+  CINIT(APPEND, LONG, 50),       /* Append instead of overwrite on upload! */
+
+  /* Specify whether to read the user+password from the .netrc or the URL.
+   * This must be one of the CURL_NETRC_* enums below. */
+  CINIT(NETRC, LONG, 51),
+
+  CINIT(FOLLOWLOCATION, LONG, 52),  /* use Location: Luke! */
+
+  CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */
+  CINIT(PUT, LONG, 54),          /* HTTP PUT */
+
+  /* 55 = OBSOLETE */
+
+  /* DEPRECATED
+   * Function that will be called instead of the internal progress display
+   * function. This function should be defined as the curl_progress_callback
+   * prototype defines. */
+  CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56),
+
+  /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION
+     callbacks */
+  CINIT(PROGRESSDATA, OBJECTPOINT, 57),
+#define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA
+
+  /* We want the referrer field set automatically when following locations */
+  CINIT(AUTOREFERER, LONG, 58),
+
+  /* Port of the proxy, can be set in the proxy string as well with:
+     "[host]:[port]" */
+  CINIT(PROXYPORT, LONG, 59),
+
+  /* size of the POST input data, if strlen() is not good to use */
+  CINIT(POSTFIELDSIZE, LONG, 60),
+
+  /* tunnel non-http operations through a HTTP proxy */
+  CINIT(HTTPPROXYTUNNEL, LONG, 61),
+
+  /* Set the interface string to use as outgoing network interface */
+  CINIT(INTERFACE, STRINGPOINT, 62),
+
+  /* Set the krb4/5 security level, this also enables krb4/5 awareness.  This
+   * is a string, 'clear', 'safe', 'confidential' or 'private'.  If the string
+   * is set but doesn't match one of these, 'private' will be used.  */
+  CINIT(KRBLEVEL, STRINGPOINT, 63),
+
+  /* Set if we should verify the peer in ssl handshake, set 1 to verify. */
+  CINIT(SSL_VERIFYPEER, LONG, 64),
+
+  /* The CApath or CAfile used to validate the peer certificate
+     this option is used only if SSL_VERIFYPEER is true */
+  CINIT(CAINFO, STRINGPOINT, 65),
+
+  /* 66 = OBSOLETE */
+  /* 67 = OBSOLETE */
+
+  /* Maximum number of http redirects to follow */
+  CINIT(MAXREDIRS, LONG, 68),
+
+  /* Pass a long set to 1 to get the date of the requested document (if
+     possible)! Pass a zero to shut it off. */
+  CINIT(FILETIME, LONG, 69),
+
+  /* This points to a linked list of telnet options */
+  CINIT(TELNETOPTIONS, OBJECTPOINT, 70),
+
+  /* Max amount of cached alive connections */
+  CINIT(MAXCONNECTS, LONG, 71),
+
+  CINIT(OBSOLETE72, LONG, 72), /* OBSOLETE, do not use! */
+
+  /* 73 = OBSOLETE */
+
+  /* Set to explicitly use a new connection for the upcoming transfer.
+     Do not use this unless you're absolutely sure of this, as it makes the
+     operation slower and is less friendly for the network. */
+  CINIT(FRESH_CONNECT, LONG, 74),
+
+  /* Set to explicitly forbid the upcoming transfer's connection to be re-used
+     when done. Do not use this unless you're absolutely sure of this, as it
+     makes the operation slower and is less friendly for the network. */
+  CINIT(FORBID_REUSE, LONG, 75),
+
+  /* Set to a file name that contains random data for libcurl to use to
+     seed the random engine when doing SSL connects. */
+  CINIT(RANDOM_FILE, STRINGPOINT, 76),
+
+  /* Set to the Entropy Gathering Daemon socket pathname */
+  CINIT(EGDSOCKET, STRINGPOINT, 77),
+
+  /* Time-out connect operations after this amount of seconds, if connects are
+     OK within this time, then fine... This only aborts the connect phase. */
+  CINIT(CONNECTTIMEOUT, LONG, 78),
+
+  /* Function that will be called to store headers (instead of fwrite). The
+   * parameters will use fwrite() syntax, make sure to follow them. */
+  CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79),
+
+  /* Set this to force the HTTP request to get back to GET. Only really usable
+     if POST, PUT or a custom request have been used first.
+   */
+  CINIT(HTTPGET, LONG, 80),
+
+  /* Set if we should verify the Common name from the peer certificate in ssl
+   * handshake, set 1 to check existence, 2 to ensure that it matches the
+   * provided hostname. */
+  CINIT(SSL_VERIFYHOST, LONG, 81),
+
+  /* Specify which file name to write all known cookies in after completed
+     operation. Set file name to "-" (dash) to make it go to stdout. */
+  CINIT(COOKIEJAR, STRINGPOINT, 82),
+
+  /* Specify which SSL ciphers to use */
+  CINIT(SSL_CIPHER_LIST, STRINGPOINT, 83),
+
+  /* Specify which HTTP version to use! This must be set to one of the
+     CURL_HTTP_VERSION* enums set below. */
+  CINIT(HTTP_VERSION, LONG, 84),
+
+  /* Specifically switch on or off the FTP engine's use of the EPSV command. By
+     default, that one will always be attempted before the more traditional
+     PASV command. */
+  CINIT(FTP_USE_EPSV, LONG, 85),
+
+  /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */
+  CINIT(SSLCERTTYPE, STRINGPOINT, 86),
+
+  /* name of the file keeping your private SSL-key */
+  CINIT(SSLKEY, STRINGPOINT, 87),
+
+  /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */
+  CINIT(SSLKEYTYPE, STRINGPOINT, 88),
+
+  /* crypto engine for the SSL-sub system */
+  CINIT(SSLENGINE, STRINGPOINT, 89),
+
+  /* set the crypto engine for the SSL-sub system as default
+     the param has no meaning...
+   */
+  CINIT(SSLENGINE_DEFAULT, LONG, 90),
+
+  /* Non-zero value means to use the global dns cache */
+  CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* DEPRECATED, do not use! */
+
+  /* DNS cache timeout */
+  CINIT(DNS_CACHE_TIMEOUT, LONG, 92),
+
+  /* send linked-list of pre-transfer QUOTE commands */
+  CINIT(PREQUOTE, OBJECTPOINT, 93),
+
+  /* set the debug function */
+  CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94),
+
+  /* set the data for the debug function */
+  CINIT(DEBUGDATA, OBJECTPOINT, 95),
+
+  /* mark this as start of a cookie session */
+  CINIT(COOKIESESSION, LONG, 96),
+
+  /* The CApath directory used to validate the peer certificate
+     this option is used only if SSL_VERIFYPEER is true */
+  CINIT(CAPATH, STRINGPOINT, 97),
+
+  /* Instruct libcurl to use a smaller receive buffer */
+  CINIT(BUFFERSIZE, LONG, 98),
+
+  /* Instruct libcurl to not use any signal/alarm handlers, even when using
+     timeouts. This option is useful for multi-threaded applications.
+     See libcurl-the-guide for more background information. */
+  CINIT(NOSIGNAL, LONG, 99),
+
+  /* Provide a CURLShare for mutexing non-ts data */
+  CINIT(SHARE, OBJECTPOINT, 100),
+
+  /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default),
+     CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and
+     CURLPROXY_SOCKS5. */
+  CINIT(PROXYTYPE, LONG, 101),
+
+  /* Set the Accept-Encoding string. Use this to tell a server you would like
+     the response to be compressed. Before 7.21.6, this was known as
+     CURLOPT_ENCODING */
+  CINIT(ACCEPT_ENCODING, STRINGPOINT, 102),
+
+  /* Set pointer to private data */
+  CINIT(PRIVATE, OBJECTPOINT, 103),
+
+  /* Set aliases for HTTP 200 in the HTTP Response header */
+  CINIT(HTTP200ALIASES, OBJECTPOINT, 104),
+
+  /* Continue to send authentication (user+password) when following locations,
+     even when hostname changed. This can potentially send off the name
+     and password to whatever host the server decides. */
+  CINIT(UNRESTRICTED_AUTH, LONG, 105),
+
+  /* Specifically switch on or off the FTP engine's use of the EPRT command (
+     it also disables the LPRT attempt). By default, those ones will always be
+     attempted before the good old traditional PORT command. */
+  CINIT(FTP_USE_EPRT, LONG, 106),
+
+  /* Set this to a bitmask value to enable the particular authentications
+     methods you like. Use this in combination with CURLOPT_USERPWD.
+     Note that setting multiple bits may cause extra network round-trips. */
+  CINIT(HTTPAUTH, LONG, 107),
+
+  /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx
+     in second argument. The function must be matching the
+     curl_ssl_ctx_callback proto. */
+  CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108),
+
+  /* Set the userdata for the ssl context callback function's third
+     argument */
+  CINIT(SSL_CTX_DATA, OBJECTPOINT, 109),
+
+  /* FTP Option that causes missing dirs to be created on the remote server.
+     In 7.19.4 we introduced the convenience enums for this option using the
+     CURLFTP_CREATE_DIR prefix.
+  */
+  CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110),
+
+  /* Set this to a bitmask value to enable the particular authentications
+     methods you like. Use this in combination with CURLOPT_PROXYUSERPWD.
+     Note that setting multiple bits may cause extra network round-trips. */
+  CINIT(PROXYAUTH, LONG, 111),
+
+  /* FTP option that changes the timeout, in seconds, associated with
+     getting a response.  This is different from transfer timeout time and
+     essentially places a demand on the FTP server to acknowledge commands
+     in a timely manner. */
+  CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112),
+#define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT
+
+  /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to
+     tell libcurl to resolve names to those IP versions only. This only has
+     affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */
+  CINIT(IPRESOLVE, LONG, 113),
+
+  /* Set this option to limit the size of a file that will be downloaded from
+     an HTTP or FTP server.
+
+     Note there is also _LARGE version which adds large file support for
+     platforms which have larger off_t sizes.  See MAXFILESIZE_LARGE below. */
+  CINIT(MAXFILESIZE, LONG, 114),
+
+  /* See the comment for INFILESIZE above, but in short, specifies
+   * the size of the file being uploaded.  -1 means unknown.
+   */
+  CINIT(INFILESIZE_LARGE, OFF_T, 115),
+
+  /* Sets the continuation offset.  There is also a LONG version of this;
+   * look above for RESUME_FROM.
+   */
+  CINIT(RESUME_FROM_LARGE, OFF_T, 116),
+
+  /* Sets the maximum size of data that will be downloaded from
+   * an HTTP or FTP server.  See MAXFILESIZE above for the LONG version.
+   */
+  CINIT(MAXFILESIZE_LARGE, OFF_T, 117),
+
+  /* Set this option to the file name of your .netrc file you want libcurl
+     to parse (using the CURLOPT_NETRC option). If not set, libcurl will do
+     a poor attempt to find the user's home directory and check for a .netrc
+     file in there. */
+  CINIT(NETRC_FILE, STRINGPOINT, 118),
+
+  /* Enable SSL/TLS for FTP, pick one of:
+     CURLUSESSL_TRY     - try using SSL, proceed anyway otherwise
+     CURLUSESSL_CONTROL - SSL for the control connection or fail
+     CURLUSESSL_ALL     - SSL for all communication or fail
+  */
+  CINIT(USE_SSL, LONG, 119),
+
+  /* The _LARGE version of the standard POSTFIELDSIZE option */
+  CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120),
+
+  /* Enable/disable the TCP Nagle algorithm */
+  CINIT(TCP_NODELAY, LONG, 121),
+
+  /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */
+  /* 123 OBSOLETE. Gone in 7.16.0 */
+  /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */
+  /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */
+  /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */
+  /* 127 OBSOLETE. Gone in 7.16.0 */
+  /* 128 OBSOLETE. Gone in 7.16.0 */
+
+  /* When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option
+     can be used to change libcurl's default action which is to first try
+     "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK
+     response has been received.
+
+     Available parameters are:
+     CURLFTPAUTH_DEFAULT - let libcurl decide
+     CURLFTPAUTH_SSL     - try "AUTH SSL" first, then TLS
+     CURLFTPAUTH_TLS     - try "AUTH TLS" first, then SSL
+  */
+  CINIT(FTPSSLAUTH, LONG, 129),
+
+  CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130),
+  CINIT(IOCTLDATA, OBJECTPOINT, 131),
+
+  /* 132 OBSOLETE. Gone in 7.16.0 */
+  /* 133 OBSOLETE. Gone in 7.16.0 */
+
+  /* zero terminated string for pass on to the FTP server when asked for
+     "account" info */
+  CINIT(FTP_ACCOUNT, STRINGPOINT, 134),
+
+  /* feed cookie into cookie engine */
+  CINIT(COOKIELIST, STRINGPOINT, 135),
+
+  /* ignore Content-Length */
+  CINIT(IGNORE_CONTENT_LENGTH, LONG, 136),
+
+  /* Set to non-zero to skip the IP address received in a 227 PASV FTP server
+     response. Typically used for FTP-SSL purposes but is not restricted to
+     that. libcurl will then instead use the same IP address it used for the
+     control connection. */
+  CINIT(FTP_SKIP_PASV_IP, LONG, 137),
+
+  /* Select "file method" to use when doing FTP, see the curl_ftpmethod
+     above. */
+  CINIT(FTP_FILEMETHOD, LONG, 138),
+
+  /* Local port number to bind the socket to */
+  CINIT(LOCALPORT, LONG, 139),
+
+  /* Number of ports to try, including the first one set with LOCALPORT.
+     Thus, setting it to 1 will make no additional attempts but the first.
+  */
+  CINIT(LOCALPORTRANGE, LONG, 140),
+
+  /* no transfer, set up connection and let application use the socket by
+     extracting it with CURLINFO_LASTSOCKET */
+  CINIT(CONNECT_ONLY, LONG, 141),
+
+  /* Function that will be called to convert from the
+     network encoding (instead of using the iconv calls in libcurl) */
+  CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142),
+
+  /* Function that will be called to convert to the
+     network encoding (instead of using the iconv calls in libcurl) */
+  CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143),
+
+  /* Function that will be called to convert from UTF8
+     (instead of using the iconv calls in libcurl)
+     Note that this is used only for SSL certificate processing */
+  CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144),
+
+  /* if the connection proceeds too quickly then need to slow it down */
+  /* limit-rate: maximum number of bytes per second to send or receive */
+  CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145),
+  CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146),
+
+  /* Pointer to command string to send if USER/PASS fails. */
+  CINIT(FTP_ALTERNATIVE_TO_USER, STRINGPOINT, 147),
+
+  /* callback function for setting socket options */
+  CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148),
+  CINIT(SOCKOPTDATA, OBJECTPOINT, 149),
+
+  /* set to 0 to disable session ID re-use for this transfer, default is
+     enabled (== 1) */
+  CINIT(SSL_SESSIONID_CACHE, LONG, 150),
+
+  /* allowed SSH authentication methods */
+  CINIT(SSH_AUTH_TYPES, LONG, 151),
+
+  /* Used by scp/sftp to do public/private key authentication */
+  CINIT(SSH_PUBLIC_KEYFILE, STRINGPOINT, 152),
+  CINIT(SSH_PRIVATE_KEYFILE, STRINGPOINT, 153),
+
+  /* Send CCC (Clear Command Channel) after authentication */
+  CINIT(FTP_SSL_CCC, LONG, 154),
+
+  /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */
+  CINIT(TIMEOUT_MS, LONG, 155),
+  CINIT(CONNECTTIMEOUT_MS, LONG, 156),
+
+  /* set to zero to disable the libcurl's decoding and thus pass the raw body
+     data to the application even when it is encoded/compressed */
+  CINIT(HTTP_TRANSFER_DECODING, LONG, 157),
+  CINIT(HTTP_CONTENT_DECODING, LONG, 158),
+
+  /* Permission used when creating new files and directories on the remote
+     server for protocols that support it, SFTP/SCP/FILE */
+  CINIT(NEW_FILE_PERMS, LONG, 159),
+  CINIT(NEW_DIRECTORY_PERMS, LONG, 160),
+
+  /* Set the behaviour of POST when redirecting. Values must be set to one
+     of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */
+  CINIT(POSTREDIR, LONG, 161),
+
+  /* used by scp/sftp to verify the host's public key */
+  CINIT(SSH_HOST_PUBLIC_KEY_MD5, STRINGPOINT, 162),
+
+  /* Callback function for opening socket (instead of socket(2)). Optionally,
+     callback is able change the address or refuse to connect returning
+     CURL_SOCKET_BAD.  The callback should have type
+     curl_opensocket_callback */
+  CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163),
+  CINIT(OPENSOCKETDATA, OBJECTPOINT, 164),
+
+  /* POST volatile input fields. */
+  CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165),
+
+  /* set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy */
+  CINIT(PROXY_TRANSFER_MODE, LONG, 166),
+
+  /* Callback function for seeking in the input stream */
+  CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167),
+  CINIT(SEEKDATA, OBJECTPOINT, 168),
+
+  /* CRL file */
+  CINIT(CRLFILE, STRINGPOINT, 169),
+
+  /* Issuer certificate */
+  CINIT(ISSUERCERT, STRINGPOINT, 170),
+
+  /* (IPv6) Address scope */
+  CINIT(ADDRESS_SCOPE, LONG, 171),
+
+  /* Collect certificate chain info and allow it to get retrievable with
+     CURLINFO_CERTINFO after the transfer is complete. */
+  CINIT(CERTINFO, LONG, 172),
+
+  /* "name" and "pwd" to use when fetching. */
+  CINIT(USERNAME, STRINGPOINT, 173),
+  CINIT(PASSWORD, STRINGPOINT, 174),
+
+    /* "name" and "pwd" to use with Proxy when fetching. */
+  CINIT(PROXYUSERNAME, STRINGPOINT, 175),
+  CINIT(PROXYPASSWORD, STRINGPOINT, 176),
+
+  /* Comma separated list of hostnames defining no-proxy zones. These should
+     match both hostnames directly, and hostnames within a domain. For
+     example, local.com will match local.com and www.local.com, but NOT
+     notlocal.com or www.notlocal.com. For compatibility with other
+     implementations of this, .local.com will be considered to be the same as
+     local.com. A single * is the only valid wildcard, and effectively
+     disables the use of proxy. */
+  CINIT(NOPROXY, STRINGPOINT, 177),
+
+  /* block size for TFTP transfers */
+  CINIT(TFTP_BLKSIZE, LONG, 178),
+
+  /* Socks Service */
+  CINIT(SOCKS5_GSSAPI_SERVICE, STRINGPOINT, 179), /* DEPRECATED, do not use! */
+
+  /* Socks Service */
+  CINIT(SOCKS5_GSSAPI_NEC, LONG, 180),
+
+  /* set the bitmask for the protocols that are allowed to be used for the
+     transfer, which thus helps the app which takes URLs from users or other
+     external inputs and want to restrict what protocol(s) to deal
+     with. Defaults to CURLPROTO_ALL. */
+  CINIT(PROTOCOLS, LONG, 181),
+
+  /* set the bitmask for the protocols that libcurl is allowed to follow to,
+     as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs
+     to be set in both bitmasks to be allowed to get redirected to. Defaults
+     to all protocols except FILE and SCP. */
+  CINIT(REDIR_PROTOCOLS, LONG, 182),
+
+  /* set the SSH knownhost file name to use */
+  CINIT(SSH_KNOWNHOSTS, STRINGPOINT, 183),
+
+  /* set the SSH host key callback, must point to a curl_sshkeycallback
+     function */
+  CINIT(SSH_KEYFUNCTION, FUNCTIONPOINT, 184),
+
+  /* set the SSH host key callback custom pointer */
+  CINIT(SSH_KEYDATA, OBJECTPOINT, 185),
+
+  /* set the SMTP mail originator */
+  CINIT(MAIL_FROM, STRINGPOINT, 186),
+
+  /* set the list of SMTP mail receiver(s) */
+  CINIT(MAIL_RCPT, OBJECTPOINT, 187),
+
+  /* FTP: send PRET before PASV */
+  CINIT(FTP_USE_PRET, LONG, 188),
+
+  /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */
+  CINIT(RTSP_REQUEST, LONG, 189),
+
+  /* The RTSP session identifier */
+  CINIT(RTSP_SESSION_ID, STRINGPOINT, 190),
+
+  /* The RTSP stream URI */
+  CINIT(RTSP_STREAM_URI, STRINGPOINT, 191),
+
+  /* The Transport: header to use in RTSP requests */
+  CINIT(RTSP_TRANSPORT, STRINGPOINT, 192),
+
+  /* Manually initialize the client RTSP CSeq for this handle */
+  CINIT(RTSP_CLIENT_CSEQ, LONG, 193),
+
+  /* Manually initialize the server RTSP CSeq for this handle */
+  CINIT(RTSP_SERVER_CSEQ, LONG, 194),
+
+  /* The stream to pass to INTERLEAVEFUNCTION. */
+  CINIT(INTERLEAVEDATA, OBJECTPOINT, 195),
+
+  /* Let the application define a custom write method for RTP data */
+  CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196),
+
+  /* Turn on wildcard matching */
+  CINIT(WILDCARDMATCH, LONG, 197),
+
+  /* Directory matching callback called before downloading of an
+     individual file (chunk) started */
+  CINIT(CHUNK_BGN_FUNCTION, FUNCTIONPOINT, 198),
+
+  /* Directory matching callback called after the file (chunk)
+     was downloaded, or skipped */
+  CINIT(CHUNK_END_FUNCTION, FUNCTIONPOINT, 199),
+
+  /* Change match (fnmatch-like) callback for wildcard matching */
+  CINIT(FNMATCH_FUNCTION, FUNCTIONPOINT, 200),
+
+  /* Let the application define custom chunk data pointer */
+  CINIT(CHUNK_DATA, OBJECTPOINT, 201),
+
+  /* FNMATCH_FUNCTION user pointer */
+  CINIT(FNMATCH_DATA, OBJECTPOINT, 202),
+
+  /* send linked-list of name:port:address sets */
+  CINIT(RESOLVE, OBJECTPOINT, 203),
+
+  /* Set a username for authenticated TLS */
+  CINIT(TLSAUTH_USERNAME, STRINGPOINT, 204),
+
+  /* Set a password for authenticated TLS */
+  CINIT(TLSAUTH_PASSWORD, STRINGPOINT, 205),
+
+  /* Set authentication type for authenticated TLS */
+  CINIT(TLSAUTH_TYPE, STRINGPOINT, 206),
+
+  /* Set to 1 to enable the "TE:" header in HTTP requests to ask for
+     compressed transfer-encoded responses. Set to 0 to disable the use of TE:
+     in outgoing requests. The current default is 0, but it might change in a
+     future libcurl release.
+
+     libcurl will ask for the compressed methods it knows of, and if that
+     isn't any, it will not ask for transfer-encoding at all even if this
+     option is set to 1.
+
+  */
+  CINIT(TRANSFER_ENCODING, LONG, 207),
+
+  /* Callback function for closing socket (instead of close(2)). The callback
+     should have type curl_closesocket_callback */
+  CINIT(CLOSESOCKETFUNCTION, FUNCTIONPOINT, 208),
+  CINIT(CLOSESOCKETDATA, OBJECTPOINT, 209),
+
+  /* allow GSSAPI credential delegation */
+  CINIT(GSSAPI_DELEGATION, LONG, 210),
+
+  /* Set the name servers to use for DNS resolution */
+  CINIT(DNS_SERVERS, STRINGPOINT, 211),
+
+  /* Time-out accept operations (currently for FTP only) after this amount
+     of milliseconds. */
+  CINIT(ACCEPTTIMEOUT_MS, LONG, 212),
+
+  /* Set TCP keepalive */
+  CINIT(TCP_KEEPALIVE, LONG, 213),
+
+  /* non-universal keepalive knobs (Linux, AIX, HP-UX, more) */
+  CINIT(TCP_KEEPIDLE, LONG, 214),
+  CINIT(TCP_KEEPINTVL, LONG, 215),
+
+  /* Enable/disable specific SSL features with a bitmask, see CURLSSLOPT_* */
+  CINIT(SSL_OPTIONS, LONG, 216),
+
+  /* Set the SMTP auth originator */
+  CINIT(MAIL_AUTH, STRINGPOINT, 217),
+
+  /* Enable/disable SASL initial response */
+  CINIT(SASL_IR, LONG, 218),
+
+  /* Function that will be called instead of the internal progress display
+   * function. This function should be defined as the curl_xferinfo_callback
+   * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */
+  CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219),
+
+  /* The XOAUTH2 bearer token */
+  CINIT(XOAUTH2_BEARER, STRINGPOINT, 220),
+
+  /* Set the interface string to use as outgoing network
+   * interface for DNS requests.
+   * Only supported by the c-ares DNS backend */
+  CINIT(DNS_INTERFACE, STRINGPOINT, 221),
+
+  /* Set the local IPv4 address to use for outgoing DNS requests.
+   * Only supported by the c-ares DNS backend */
+  CINIT(DNS_LOCAL_IP4, STRINGPOINT, 222),
+
+  /* Set the local IPv4 address to use for outgoing DNS requests.
+   * Only supported by the c-ares DNS backend */
+  CINIT(DNS_LOCAL_IP6, STRINGPOINT, 223),
+
+  /* Set authentication options directly */
+  CINIT(LOGIN_OPTIONS, STRINGPOINT, 224),
+
+  /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */
+  CINIT(SSL_ENABLE_NPN, LONG, 225),
+
+  /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */
+  CINIT(SSL_ENABLE_ALPN, LONG, 226),
+
+  /* Time to wait for a response to a HTTP request containing an
+   * Expect: 100-continue header before sending the data anyway. */
+  CINIT(EXPECT_100_TIMEOUT_MS, LONG, 227),
+
+  /* This points to a linked list of headers used for proxy requests only,
+     struct curl_slist kind */
+  CINIT(PROXYHEADER, OBJECTPOINT, 228),
+
+  /* Pass in a bitmask of "header options" */
+  CINIT(HEADEROPT, LONG, 229),
+
+  /* The public key in DER form used to validate the peer public key
+     this option is used only if SSL_VERIFYPEER is true */
+  CINIT(PINNEDPUBLICKEY, STRINGPOINT, 230),
+
+  /* Path to Unix domain socket */
+  CINIT(UNIX_SOCKET_PATH, STRINGPOINT, 231),
+
+  /* Set if we should verify the certificate status. */
+  CINIT(SSL_VERIFYSTATUS, LONG, 232),
+
+  /* Set if we should enable TLS false start. */
+  CINIT(SSL_FALSESTART, LONG, 233),
+
+  /* Do not squash dot-dot sequences */
+  CINIT(PATH_AS_IS, LONG, 234),
+
+  /* Proxy Service Name */
+  CINIT(PROXY_SERVICE_NAME, STRINGPOINT, 235),
+
+  /* Service Name */
+  CINIT(SERVICE_NAME, STRINGPOINT, 236),
+
+  /* Wait/don't wait for pipe/mutex to clarify */
+  CINIT(PIPEWAIT, LONG, 237),
+
+  /* Set the protocol used when curl is given a URL without a protocol */
+  CINIT(DEFAULT_PROTOCOL, STRINGPOINT, 238),
+
+  /* Set stream weight, 1 - 256 (default is 16) */
+  CINIT(STREAM_WEIGHT, LONG, 239),
+
+  /* Set stream dependency on another CURL handle */
+  CINIT(STREAM_DEPENDS, OBJECTPOINT, 240),
+
+  /* Set E-xclusive stream dependency on another CURL handle */
+  CINIT(STREAM_DEPENDS_E, OBJECTPOINT, 241),
+
+  /* Do not send any tftp option requests to the server */
+  CINIT(TFTP_NO_OPTIONS, LONG, 242),
+
+  /* Linked-list of host:port:connect-to-host:connect-to-port,
+     overrides the URL's host:port (only for the network layer) */
+  CINIT(CONNECT_TO, OBJECTPOINT, 243),
+
+  /* Set TCP Fast Open */
+  CINIT(TCP_FASTOPEN, LONG, 244),
+
+  /* Continue to send data if the server responds early with an
+   * HTTP status code >= 300 */
+  CINIT(KEEP_SENDING_ON_ERROR, LONG, 245),
+
+  /* The CApath or CAfile used to validate the proxy certificate
+     this option is used only if PROXY_SSL_VERIFYPEER is true */
+  CINIT(PROXY_CAINFO, STRINGPOINT, 246),
+
+  /* The CApath directory used to validate the proxy certificate
+     this option is used only if PROXY_SSL_VERIFYPEER is true */
+  CINIT(PROXY_CAPATH, STRINGPOINT, 247),
+
+  /* Set if we should verify the proxy in ssl handshake,
+     set 1 to verify. */
+  CINIT(PROXY_SSL_VERIFYPEER, LONG, 248),
+
+  /* Set if we should verify the Common name from the proxy certificate in ssl
+   * handshake, set 1 to check existence, 2 to ensure that it matches
+   * the provided hostname. */
+  CINIT(PROXY_SSL_VERIFYHOST, LONG, 249),
+
+  /* What version to specifically try to use for proxy.
+     See CURL_SSLVERSION defines below. */
+  CINIT(PROXY_SSLVERSION, LONG, 250),
+
+  /* Set a username for authenticated TLS for proxy */
+  CINIT(PROXY_TLSAUTH_USERNAME, STRINGPOINT, 251),
+
+  /* Set a password for authenticated TLS for proxy */
+  CINIT(PROXY_TLSAUTH_PASSWORD, STRINGPOINT, 252),
+
+  /* Set authentication type for authenticated TLS for proxy */
+  CINIT(PROXY_TLSAUTH_TYPE, STRINGPOINT, 253),
+
+  /* name of the file keeping your private SSL-certificate for proxy */
+  CINIT(PROXY_SSLCERT, STRINGPOINT, 254),
+
+  /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") for
+     proxy */
+  CINIT(PROXY_SSLCERTTYPE, STRINGPOINT, 255),
+
+  /* name of the file keeping your private SSL-key for proxy */
+  CINIT(PROXY_SSLKEY, STRINGPOINT, 256),
+
+  /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") for
+     proxy */
+  CINIT(PROXY_SSLKEYTYPE, STRINGPOINT, 257),
+
+  /* password for the SSL private key for proxy */
+  CINIT(PROXY_KEYPASSWD, STRINGPOINT, 258),
+
+  /* Specify which SSL ciphers to use for proxy */
+  CINIT(PROXY_SSL_CIPHER_LIST, STRINGPOINT, 259),
+
+  /* CRL file for proxy */
+  CINIT(PROXY_CRLFILE, STRINGPOINT, 260),
+
+  /* Enable/disable specific SSL features with a bitmask for proxy, see
+     CURLSSLOPT_* */
+  CINIT(PROXY_SSL_OPTIONS, LONG, 261),
+
+  /* Name of pre proxy to use. */
+  CINIT(PRE_PROXY, STRINGPOINT, 262),
+
+  /* The public key in DER form used to validate the proxy public key
+     this option is used only if PROXY_SSL_VERIFYPEER is true */
+  CINIT(PROXY_PINNEDPUBLICKEY, STRINGPOINT, 263),
+
+  /* Path to an abstract Unix domain socket */
+  CINIT(ABSTRACT_UNIX_SOCKET, STRINGPOINT, 264),
+
+  /* Suppress proxy CONNECT response headers from user callbacks */
+  CINIT(SUPPRESS_CONNECT_HEADERS, LONG, 265),
+
+  /* The request target, instead of extracted from the URL */
+  CINIT(REQUEST_TARGET, STRINGPOINT, 266),
+
+  /* bitmask of allowed auth methods for connections to SOCKS5 proxies */
+  CINIT(SOCKS5_AUTH, LONG, 267),
+
+  /* Enable/disable SSH compression */
+  CINIT(SSH_COMPRESSION, LONG, 268),
+
+  /* Post MIME data. */
+  CINIT(MIMEPOST, OBJECTPOINT, 269),
+
+  CURLOPT_LASTENTRY /* the last unused */
+} CURLoption;
+
+#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
+                          the obsolete stuff removed! */
+
+/* Backwards compatibility with older names */
+/* These are scheduled to disappear by 2011 */
+
+/* This was added in version 7.19.1 */
+#define CURLOPT_POST301 CURLOPT_POSTREDIR
+
+/* These are scheduled to disappear by 2009 */
+
+/* The following were added in 7.17.0 */
+#define CURLOPT_SSLKEYPASSWD CURLOPT_KEYPASSWD
+#define CURLOPT_FTPAPPEND CURLOPT_APPEND
+#define CURLOPT_FTPLISTONLY CURLOPT_DIRLISTONLY
+#define CURLOPT_FTP_SSL CURLOPT_USE_SSL
+
+/* The following were added earlier */
+
+#define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD
+#define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL
+
+#else
+/* This is set if CURL_NO_OLDIES is defined at compile-time */
+#undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */
+#endif
+
+
+  /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host
+     name resolves addresses using more than one IP protocol version, this
+     option might be handy to force libcurl to use a specific IP version. */
+#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP
+                                     versions that your system allows */
+#define CURL_IPRESOLVE_V4       1 /* resolve to IPv4 addresses */
+#define CURL_IPRESOLVE_V6       2 /* resolve to IPv6 addresses */
+
+  /* three convenient "aliases" that follow the name scheme better */
+#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER
+
+  /* These enums are for use with the CURLOPT_HTTP_VERSION option. */
+enum {
+  CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd
+                             like the library to choose the best possible
+                             for us! */
+  CURL_HTTP_VERSION_1_0,  /* please use HTTP 1.0 in the request */
+  CURL_HTTP_VERSION_1_1,  /* please use HTTP 1.1 in the request */
+  CURL_HTTP_VERSION_2_0,  /* please use HTTP 2 in the request */
+  CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */
+  CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE,  /* please use HTTP 2 without HTTP/1.1
+                                           Upgrade */
+
+  CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */
+};
+
+/* Convenience definition simple because the name of the version is HTTP/2 and
+   not 2.0. The 2_0 version of the enum name was set while the version was
+   still planned to be 2.0 and we stick to it for compatibility. */
+#define CURL_HTTP_VERSION_2 CURL_HTTP_VERSION_2_0
+
+/*
+ * Public API enums for RTSP requests
+ */
+enum {
+    CURL_RTSPREQ_NONE, /* first in list */
+    CURL_RTSPREQ_OPTIONS,
+    CURL_RTSPREQ_DESCRIBE,
+    CURL_RTSPREQ_ANNOUNCE,
+    CURL_RTSPREQ_SETUP,
+    CURL_RTSPREQ_PLAY,
+    CURL_RTSPREQ_PAUSE,
+    CURL_RTSPREQ_TEARDOWN,
+    CURL_RTSPREQ_GET_PARAMETER,
+    CURL_RTSPREQ_SET_PARAMETER,
+    CURL_RTSPREQ_RECORD,
+    CURL_RTSPREQ_RECEIVE,
+    CURL_RTSPREQ_LAST /* last in list */
+};
+
+  /* These enums are for use with the CURLOPT_NETRC option. */
+enum CURL_NETRC_OPTION {
+  CURL_NETRC_IGNORED,     /* The .netrc will never be read.
+                           * This is the default. */
+  CURL_NETRC_OPTIONAL,    /* A user:password in the URL will be preferred
+                           * to one in the .netrc. */
+  CURL_NETRC_REQUIRED,    /* A user:password in the URL will be ignored.
+                           * Unless one is set programmatically, the .netrc
+                           * will be queried. */
+  CURL_NETRC_LAST
+};
+
+enum {
+  CURL_SSLVERSION_DEFAULT,
+  CURL_SSLVERSION_TLSv1, /* TLS 1.x */
+  CURL_SSLVERSION_SSLv2,
+  CURL_SSLVERSION_SSLv3,
+  CURL_SSLVERSION_TLSv1_0,
+  CURL_SSLVERSION_TLSv1_1,
+  CURL_SSLVERSION_TLSv1_2,
+  CURL_SSLVERSION_TLSv1_3,
+
+  CURL_SSLVERSION_LAST /* never use, keep last */
+};
+
+enum {
+  CURL_SSLVERSION_MAX_NONE =     0,
+  CURL_SSLVERSION_MAX_DEFAULT =  (CURL_SSLVERSION_TLSv1   << 16),
+  CURL_SSLVERSION_MAX_TLSv1_0 =  (CURL_SSLVERSION_TLSv1_0 << 16),
+  CURL_SSLVERSION_MAX_TLSv1_1 =  (CURL_SSLVERSION_TLSv1_1 << 16),
+  CURL_SSLVERSION_MAX_TLSv1_2 =  (CURL_SSLVERSION_TLSv1_2 << 16),
+  CURL_SSLVERSION_MAX_TLSv1_3 =  (CURL_SSLVERSION_TLSv1_3 << 16),
+
+  /* never use, keep last */
+  CURL_SSLVERSION_MAX_LAST =     (CURL_SSLVERSION_LAST    << 16)
+};
+
+enum CURL_TLSAUTH {
+  CURL_TLSAUTH_NONE,
+  CURL_TLSAUTH_SRP,
+  CURL_TLSAUTH_LAST /* never use, keep last */
+};
+
+/* symbols to use with CURLOPT_POSTREDIR.
+   CURL_REDIR_POST_301, CURL_REDIR_POST_302 and CURL_REDIR_POST_303
+   can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302
+   | CURL_REDIR_POST_303 == CURL_REDIR_POST_ALL */
+
+#define CURL_REDIR_GET_ALL  0
+#define CURL_REDIR_POST_301 1
+#define CURL_REDIR_POST_302 2
+#define CURL_REDIR_POST_303 4
+#define CURL_REDIR_POST_ALL \
+    (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303)
+
+typedef enum {
+  CURL_TIMECOND_NONE,
+
+  CURL_TIMECOND_IFMODSINCE,
+  CURL_TIMECOND_IFUNMODSINCE,
+  CURL_TIMECOND_LASTMOD,
+
+  CURL_TIMECOND_LAST
+} curl_TimeCond;
+
+/* Special size_t value signaling a zero-terminated string. */
+#define CURL_ZERO_TERMINATED ((size_t) -1)
+
+/* curl_strequal() and curl_strnequal() are subject for removal in a future
+   release */
+CURL_EXTERN int curl_strequal(const char *s1, const char *s2);
+CURL_EXTERN int curl_strnequal(const char *s1, const char *s2, size_t n);
+
+/* Mime/form handling support. */
+typedef struct curl_mime_s      curl_mime;      /* Mime context. */
+typedef struct curl_mimepart_s  curl_mimepart;  /* Mime part context. */
+
+/*
+ * NAME curl_mime_init()
+ *
+ * DESCRIPTION
+ *
+ * Create a mime context and return its handle. The easy parameter is the
+ * target handle.
+ */
+CURL_EXTERN curl_mime *curl_mime_init(CURL *easy);
+
+/*
+ * NAME curl_mime_free()
+ *
+ * DESCRIPTION
+ *
+ * release a mime handle and its substructures.
+ */
+CURL_EXTERN void curl_mime_free(curl_mime *mime);
+
+/*
+ * NAME curl_mime_addpart()
+ *
+ * DESCRIPTION
+ *
+ * Append a new empty part to the given mime context and return a handle to
+ * the created part.
+ */
+CURL_EXTERN curl_mimepart *curl_mime_addpart(curl_mime *mime);
+
+/*
+ * NAME curl_mime_name()
+ *
+ * DESCRIPTION
+ *
+ * Set mime/form part name.
+ */
+CURL_EXTERN CURLcode curl_mime_name(curl_mimepart *part, const char *name);
+
+/*
+ * NAME curl_mime_filename()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part remote file name.
+ */
+CURL_EXTERN CURLcode curl_mime_filename(curl_mimepart *part,
+                                        const char *filename);
+
+/*
+ * NAME curl_mime_type()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part type.
+ */
+CURL_EXTERN CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype);
+
+/*
+ * NAME curl_mime_encoder()
+ *
+ * DESCRIPTION
+ *
+ * Set mime data transfer encoder.
+ */
+CURL_EXTERN CURLcode curl_mime_encoder(curl_mimepart *part,
+                                       const char *encoding);
+
+/*
+ * NAME curl_mime_data()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part data source from memory data,
+ */
+CURL_EXTERN CURLcode curl_mime_data(curl_mimepart *part,
+                                    const char *data, size_t datasize);
+
+/*
+ * NAME curl_mime_filedata()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part data source from named file.
+ */
+CURL_EXTERN CURLcode curl_mime_filedata(curl_mimepart *part,
+                                        const char *filename);
+
+/*
+ * NAME curl_mime_data_cb()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part data source from callback function.
+ */
+CURL_EXTERN CURLcode curl_mime_data_cb(curl_mimepart *part,
+                                       curl_off_t datasize,
+                                       curl_read_callback readfunc,
+                                       curl_seek_callback seekfunc,
+                                       curl_free_callback freefunc,
+                                       void *arg);
+
+/*
+ * NAME curl_mime_subparts()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part data source from subparts.
+ */
+CURL_EXTERN CURLcode curl_mime_subparts(curl_mimepart *part,
+                                        curl_mime *subparts);
+/*
+ * NAME curl_mime_headers()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part headers.
+ */
+CURL_EXTERN CURLcode curl_mime_headers(curl_mimepart *part,
+                                       struct curl_slist *headers,
+                                       int take_ownership);
+
+/* Old form API. */
+/* name is uppercase CURLFORM_<name> */
+#ifdef CFINIT
+#undef CFINIT
+#endif
+
+#ifdef CURL_ISOCPP
+#define CFINIT(name) CURLFORM_ ## name
+#else
+/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */
+#define CFINIT(name) CURLFORM_/**/name
+#endif
+
+typedef enum {
+  CFINIT(NOTHING),        /********* the first one is unused ************/
+
+  /*  */
+  CFINIT(COPYNAME),
+  CFINIT(PTRNAME),
+  CFINIT(NAMELENGTH),
+  CFINIT(COPYCONTENTS),
+  CFINIT(PTRCONTENTS),
+  CFINIT(CONTENTSLENGTH),
+  CFINIT(FILECONTENT),
+  CFINIT(ARRAY),
+  CFINIT(OBSOLETE),
+  CFINIT(FILE),
+
+  CFINIT(BUFFER),
+  CFINIT(BUFFERPTR),
+  CFINIT(BUFFERLENGTH),
+
+  CFINIT(CONTENTTYPE),
+  CFINIT(CONTENTHEADER),
+  CFINIT(FILENAME),
+  CFINIT(END),
+  CFINIT(OBSOLETE2),
+
+  CFINIT(STREAM),
+  CFINIT(CONTENTLEN), /* added in 7.46.0, provide a curl_off_t length */
+
+  CURLFORM_LASTENTRY /* the last unused */
+} CURLformoption;
+
+#undef CFINIT /* done */
+
+/* structure to be used as parameter for CURLFORM_ARRAY */
+struct curl_forms {
+  CURLformoption option;
+  const char     *value;
+};
+
+/* use this for multipart formpost building */
+/* Returns code for curl_formadd()
+ *
+ * Returns:
+ * CURL_FORMADD_OK             on success
+ * CURL_FORMADD_MEMORY         if the FormInfo allocation fails
+ * CURL_FORMADD_OPTION_TWICE   if one option is given twice for one Form
+ * CURL_FORMADD_NULL           if a null pointer was given for a char
+ * CURL_FORMADD_MEMORY         if the allocation of a FormInfo struct failed
+ * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used
+ * CURL_FORMADD_INCOMPLETE     if the some FormInfo is not complete (or error)
+ * CURL_FORMADD_MEMORY         if a curl_httppost struct cannot be allocated
+ * CURL_FORMADD_MEMORY         if some allocation for string copying failed.
+ * CURL_FORMADD_ILLEGAL_ARRAY  if an illegal option is used in an array
+ *
+ ***************************************************************************/
+typedef enum {
+  CURL_FORMADD_OK, /* first, no error */
+
+  CURL_FORMADD_MEMORY,
+  CURL_FORMADD_OPTION_TWICE,
+  CURL_FORMADD_NULL,
+  CURL_FORMADD_UNKNOWN_OPTION,
+  CURL_FORMADD_INCOMPLETE,
+  CURL_FORMADD_ILLEGAL_ARRAY,
+  CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */
+
+  CURL_FORMADD_LAST /* last */
+} CURLFORMcode;
+
+/*
+ * NAME curl_formadd()
+ *
+ * DESCRIPTION
+ *
+ * Pretty advanced function for building multi-part formposts. Each invoke
+ * adds one part that together construct a full post. Then use
+ * CURLOPT_HTTPPOST to send it off to libcurl.
+ */
+CURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost,
+                                      struct curl_httppost **last_post,
+                                      ...);
+
+/*
+ * callback function for curl_formget()
+ * The void *arg pointer will be the one passed as second argument to
+ *   curl_formget().
+ * The character buffer passed to it must not be freed.
+ * Should return the buffer length passed to it as the argument "len" on
+ *   success.
+ */
+typedef size_t (*curl_formget_callback)(void *arg, const char *buf,
+                                        size_t len);
+
+/*
+ * NAME curl_formget()
+ *
+ * DESCRIPTION
+ *
+ * Serialize a curl_httppost struct built with curl_formadd().
+ * Accepts a void pointer as second argument which will be passed to
+ * the curl_formget_callback function.
+ * Returns 0 on success.
+ */
+CURL_EXTERN int curl_formget(struct curl_httppost *form, void *arg,
+                             curl_formget_callback append);
+/*
+ * NAME curl_formfree()
+ *
+ * DESCRIPTION
+ *
+ * Free a multipart formpost previously built with curl_formadd().
+ */
+CURL_EXTERN void curl_formfree(struct curl_httppost *form);
+
+/*
+ * NAME curl_getenv()
+ *
+ * DESCRIPTION
+ *
+ * Returns a malloc()'ed string that MUST be curl_free()ed after usage is
+ * complete. DEPRECATED - see lib/README.curlx
+ */
+CURL_EXTERN char *curl_getenv(const char *variable);
+
+/*
+ * NAME curl_version()
+ *
+ * DESCRIPTION
+ *
+ * Returns a static ascii string of the libcurl version.
+ */
+CURL_EXTERN char *curl_version(void);
+
+/*
+ * NAME curl_easy_escape()
+ *
+ * DESCRIPTION
+ *
+ * Escapes URL strings (converts all letters consider illegal in URLs to their
+ * %XX versions). This function returns a new allocated string or NULL if an
+ * error occurred.
+ */
+CURL_EXTERN char *curl_easy_escape(CURL *handle,
+                                   const char *string,
+                                   int length);
+
+/* the previous version: */
+CURL_EXTERN char *curl_escape(const char *string,
+                              int length);
+
+
+/*
+ * NAME curl_easy_unescape()
+ *
+ * DESCRIPTION
+ *
+ * Unescapes URL encoding in strings (converts all %XX codes to their 8bit
+ * versions). This function returns a new allocated string or NULL if an error
+ * occurred.
+ * Conversion Note: On non-ASCII platforms the ASCII %XX codes are
+ * converted into the host encoding.
+ */
+CURL_EXTERN char *curl_easy_unescape(CURL *handle,
+                                     const char *string,
+                                     int length,
+                                     int *outlength);
+
+/* the previous version */
+CURL_EXTERN char *curl_unescape(const char *string,
+                                int length);
+
+/*
+ * NAME curl_free()
+ *
+ * DESCRIPTION
+ *
+ * Provided for de-allocation in the same translation unit that did the
+ * allocation. Added in libcurl 7.10
+ */
+CURL_EXTERN void curl_free(void *p);
+
+/*
+ * NAME curl_global_init()
+ *
+ * DESCRIPTION
+ *
+ * curl_global_init() should be invoked exactly once for each application that
+ * uses libcurl and before any call of other libcurl functions.
+ *
+ * This function is not thread-safe!
+ */
+CURL_EXTERN CURLcode curl_global_init(long flags);
+
+/*
+ * NAME curl_global_init_mem()
+ *
+ * DESCRIPTION
+ *
+ * curl_global_init() or curl_global_init_mem() should be invoked exactly once
+ * for each application that uses libcurl.  This function can be used to
+ * initialize libcurl and set user defined memory management callback
+ * functions.  Users can implement memory management routines to check for
+ * memory leaks, check for mis-use of the curl library etc.  User registered
+ * callback routines with be invoked by this library instead of the system
+ * memory management routines like malloc, free etc.
+ */
+CURL_EXTERN CURLcode curl_global_init_mem(long flags,
+                                          curl_malloc_callback m,
+                                          curl_free_callback f,
+                                          curl_realloc_callback r,
+                                          curl_strdup_callback s,
+                                          curl_calloc_callback c);
+
+/*
+ * NAME curl_global_cleanup()
+ *
+ * DESCRIPTION
+ *
+ * curl_global_cleanup() should be invoked exactly once for each application
+ * that uses libcurl
+ */
+CURL_EXTERN void curl_global_cleanup(void);
+
+/* linked-list structure for the CURLOPT_QUOTE option (and other) */
+struct curl_slist {
+  char *data;
+  struct curl_slist *next;
+};
+
+/*
+ * NAME curl_global_sslset()
+ *
+ * DESCRIPTION
+ *
+ * When built with multiple SSL backends, curl_global_sslset() allows to
+ * choose one. This function can only be called once, and it must be called
+ * *before* curl_global_init().
+ *
+ * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The
+ * backend can also be specified via the name parameter (passing -1 as id).
+ * If both id and name are specified, the name will be ignored. If neither id
+ * nor name are specified, the function will fail with
+ * CURLSSLSET_UNKNOWN_BACKEND and set the "avail" pointer to the
+ * NULL-terminated list of available backends.
+ *
+ * Upon success, the function returns CURLSSLSET_OK.
+ *
+ * If the specified SSL backend is not available, the function returns
+ * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a NULL-terminated
+ * list of available SSL backends.
+ *
+ * The SSL backend can be set only once. If it has already been set, a
+ * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE.
+ */
+
+typedef struct {
+  curl_sslbackend id;
+  const char *name;
+} curl_ssl_backend;
+
+typedef enum {
+  CURLSSLSET_OK = 0,
+  CURLSSLSET_UNKNOWN_BACKEND,
+  CURLSSLSET_TOO_LATE,
+  CURLSSLSET_NO_BACKENDS /* libcurl was built without any SSL support */
+} CURLsslset;
+
+CURL_EXTERN CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
+                                          const curl_ssl_backend ***avail);
+
+/*
+ * NAME curl_slist_append()
+ *
+ * DESCRIPTION
+ *
+ * Appends a string to a linked list. If no list exists, it will be created
+ * first. Returns the new list, after appending.
+ */
+CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *,
+                                                 const char *);
+
+/*
+ * NAME curl_slist_free_all()
+ *
+ * DESCRIPTION
+ *
+ * free a previously built curl_slist.
+ */
+CURL_EXTERN void curl_slist_free_all(struct curl_slist *);
+
+/*
+ * NAME curl_getdate()
+ *
+ * DESCRIPTION
+ *
+ * Returns the time, in seconds since 1 Jan 1970 of the time string given in
+ * the first argument. The time argument in the second parameter is unused
+ * and should be set to NULL.
+ */
+CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused);
+
+/* info about the certificate chain, only for OpenSSL builds. Asked
+   for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */
+struct curl_certinfo {
+  int num_of_certs;             /* number of certificates with information */
+  struct curl_slist **certinfo; /* for each index in this array, there's a
+                                   linked list with textual information in the
+                                   format "name: value" */
+};
+
+/* Information about the SSL library used and the respective internal SSL
+   handle, which can be used to obtain further information regarding the
+   connection. Asked for with CURLINFO_TLS_SSL_PTR or CURLINFO_TLS_SESSION. */
+struct curl_tlssessioninfo {
+  curl_sslbackend backend;
+  void *internals;
+};
+
+#define CURLINFO_STRING   0x100000
+#define CURLINFO_LONG     0x200000
+#define CURLINFO_DOUBLE   0x300000
+#define CURLINFO_SLIST    0x400000
+#define CURLINFO_PTR      0x400000 /* same as SLIST */
+#define CURLINFO_SOCKET   0x500000
+#define CURLINFO_OFF_T    0x600000
+#define CURLINFO_MASK     0x0fffff
+#define CURLINFO_TYPEMASK 0xf00000
+
+typedef enum {
+  CURLINFO_NONE, /* first, never use this */
+  CURLINFO_EFFECTIVE_URL    = CURLINFO_STRING + 1,
+  CURLINFO_RESPONSE_CODE    = CURLINFO_LONG   + 2,
+  CURLINFO_TOTAL_TIME       = CURLINFO_DOUBLE + 3,
+  CURLINFO_NAMELOOKUP_TIME  = CURLINFO_DOUBLE + 4,
+  CURLINFO_CONNECT_TIME     = CURLINFO_DOUBLE + 5,
+  CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6,
+  CURLINFO_SIZE_UPLOAD      = CURLINFO_DOUBLE + 7,
+  CURLINFO_SIZE_UPLOAD_T    = CURLINFO_OFF_T  + 7,
+  CURLINFO_SIZE_DOWNLOAD    = CURLINFO_DOUBLE + 8,
+  CURLINFO_SIZE_DOWNLOAD_T  = CURLINFO_OFF_T  + 8,
+  CURLINFO_SPEED_DOWNLOAD   = CURLINFO_DOUBLE + 9,
+  CURLINFO_SPEED_DOWNLOAD_T = CURLINFO_OFF_T  + 9,
+  CURLINFO_SPEED_UPLOAD     = CURLINFO_DOUBLE + 10,
+  CURLINFO_SPEED_UPLOAD_T   = CURLINFO_OFF_T  + 10,
+  CURLINFO_HEADER_SIZE      = CURLINFO_LONG   + 11,
+  CURLINFO_REQUEST_SIZE     = CURLINFO_LONG   + 12,
+  CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG   + 13,
+  CURLINFO_FILETIME         = CURLINFO_LONG   + 14,
+  CURLINFO_CONTENT_LENGTH_DOWNLOAD   = CURLINFO_DOUBLE + 15,
+  CURLINFO_CONTENT_LENGTH_DOWNLOAD_T = CURLINFO_OFF_T  + 15,
+  CURLINFO_CONTENT_LENGTH_UPLOAD     = CURLINFO_DOUBLE + 16,
+  CURLINFO_CONTENT_LENGTH_UPLOAD_T   = CURLINFO_OFF_T  + 16,
+  CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17,
+  CURLINFO_CONTENT_TYPE     = CURLINFO_STRING + 18,
+  CURLINFO_REDIRECT_TIME    = CURLINFO_DOUBLE + 19,
+  CURLINFO_REDIRECT_COUNT   = CURLINFO_LONG   + 20,
+  CURLINFO_PRIVATE          = CURLINFO_STRING + 21,
+  CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG   + 22,
+  CURLINFO_HTTPAUTH_AVAIL   = CURLINFO_LONG   + 23,
+  CURLINFO_PROXYAUTH_AVAIL  = CURLINFO_LONG   + 24,
+  CURLINFO_OS_ERRNO         = CURLINFO_LONG   + 25,
+  CURLINFO_NUM_CONNECTS     = CURLINFO_LONG   + 26,
+  CURLINFO_SSL_ENGINES      = CURLINFO_SLIST  + 27,
+  CURLINFO_COOKIELIST       = CURLINFO_SLIST  + 28,
+  CURLINFO_LASTSOCKET       = CURLINFO_LONG   + 29,
+  CURLINFO_FTP_ENTRY_PATH   = CURLINFO_STRING + 30,
+  CURLINFO_REDIRECT_URL     = CURLINFO_STRING + 31,
+  CURLINFO_PRIMARY_IP       = CURLINFO_STRING + 32,
+  CURLINFO_APPCONNECT_TIME  = CURLINFO_DOUBLE + 33,
+  CURLINFO_CERTINFO         = CURLINFO_PTR    + 34,
+  CURLINFO_CONDITION_UNMET  = CURLINFO_LONG   + 35,
+  CURLINFO_RTSP_SESSION_ID  = CURLINFO_STRING + 36,
+  CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG   + 37,
+  CURLINFO_RTSP_SERVER_CSEQ = CURLINFO_LONG   + 38,
+  CURLINFO_RTSP_CSEQ_RECV   = CURLINFO_LONG   + 39,
+  CURLINFO_PRIMARY_PORT     = CURLINFO_LONG   + 40,
+  CURLINFO_LOCAL_IP         = CURLINFO_STRING + 41,
+  CURLINFO_LOCAL_PORT       = CURLINFO_LONG   + 42,
+  CURLINFO_TLS_SESSION      = CURLINFO_PTR    + 43,
+  CURLINFO_ACTIVESOCKET     = CURLINFO_SOCKET + 44,
+  CURLINFO_TLS_SSL_PTR      = CURLINFO_PTR    + 45,
+  CURLINFO_HTTP_VERSION     = CURLINFO_LONG   + 46,
+  CURLINFO_PROXY_SSL_VERIFYRESULT = CURLINFO_LONG + 47,
+  CURLINFO_PROTOCOL         = CURLINFO_LONG   + 48,
+  CURLINFO_SCHEME           = CURLINFO_STRING + 49,
+  /* Fill in new entries below here! */
+
+  CURLINFO_LASTONE          = 49
+} CURLINFO;
+
+/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
+   CURLINFO_HTTP_CODE */
+#define CURLINFO_HTTP_CODE CURLINFO_RESPONSE_CODE
+
+typedef enum {
+  CURLCLOSEPOLICY_NONE, /* first, never use this */
+
+  CURLCLOSEPOLICY_OLDEST,
+  CURLCLOSEPOLICY_LEAST_RECENTLY_USED,
+  CURLCLOSEPOLICY_LEAST_TRAFFIC,
+  CURLCLOSEPOLICY_SLOWEST,
+  CURLCLOSEPOLICY_CALLBACK,
+
+  CURLCLOSEPOLICY_LAST /* last, never use this */
+} curl_closepolicy;
+
+#define CURL_GLOBAL_SSL (1<<0) /* no purpose since since 7.57.0 */
+#define CURL_GLOBAL_WIN32 (1<<1)
+#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32)
+#define CURL_GLOBAL_NOTHING 0
+#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL
+#define CURL_GLOBAL_ACK_EINTR (1<<2)
+
+
+/*****************************************************************************
+ * Setup defines, protos etc for the sharing stuff.
+ */
+
+/* Different data locks for a single share */
+typedef enum {
+  CURL_LOCK_DATA_NONE = 0,
+  /*  CURL_LOCK_DATA_SHARE is used internally to say that
+   *  the locking is just made to change the internal state of the share
+   *  itself.
+   */
+  CURL_LOCK_DATA_SHARE,
+  CURL_LOCK_DATA_COOKIE,
+  CURL_LOCK_DATA_DNS,
+  CURL_LOCK_DATA_SSL_SESSION,
+  CURL_LOCK_DATA_CONNECT,
+  CURL_LOCK_DATA_LAST
+} curl_lock_data;
+
+/* Different lock access types */
+typedef enum {
+  CURL_LOCK_ACCESS_NONE = 0,   /* unspecified action */
+  CURL_LOCK_ACCESS_SHARED = 1, /* for read perhaps */
+  CURL_LOCK_ACCESS_SINGLE = 2, /* for write perhaps */
+  CURL_LOCK_ACCESS_LAST        /* never use */
+} curl_lock_access;
+
+typedef void (*curl_lock_function)(CURL *handle,
+                                   curl_lock_data data,
+                                   curl_lock_access locktype,
+                                   void *userptr);
+typedef void (*curl_unlock_function)(CURL *handle,
+                                     curl_lock_data data,
+                                     void *userptr);
+
+
+typedef enum {
+  CURLSHE_OK,  /* all is fine */
+  CURLSHE_BAD_OPTION, /* 1 */
+  CURLSHE_IN_USE,     /* 2 */
+  CURLSHE_INVALID,    /* 3 */
+  CURLSHE_NOMEM,      /* 4 out of memory */
+  CURLSHE_NOT_BUILT_IN, /* 5 feature not present in lib */
+  CURLSHE_LAST        /* never use */
+} CURLSHcode;
+
+typedef enum {
+  CURLSHOPT_NONE,  /* don't use */
+  CURLSHOPT_SHARE,   /* specify a data type to share */
+  CURLSHOPT_UNSHARE, /* specify which data type to stop sharing */
+  CURLSHOPT_LOCKFUNC,   /* pass in a 'curl_lock_function' pointer */
+  CURLSHOPT_UNLOCKFUNC, /* pass in a 'curl_unlock_function' pointer */
+  CURLSHOPT_USERDATA,   /* pass in a user data pointer used in the lock/unlock
+                           callback functions */
+  CURLSHOPT_LAST  /* never use */
+} CURLSHoption;
+
+CURL_EXTERN CURLSH *curl_share_init(void);
+CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...);
+CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *);
+
+/****************************************************************************
+ * Structures for querying information about the curl library at runtime.
+ */
+
+typedef enum {
+  CURLVERSION_FIRST,
+  CURLVERSION_SECOND,
+  CURLVERSION_THIRD,
+  CURLVERSION_FOURTH,
+  CURLVERSION_FIFTH,
+  CURLVERSION_LAST /* never actually use this */
+} CURLversion;
+
+/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by
+   basically all programs ever that want to get version information. It is
+   meant to be a built-in version number for what kind of struct the caller
+   expects. If the struct ever changes, we redefine the NOW to another enum
+   from above. */
+#define CURLVERSION_NOW CURLVERSION_FIFTH
+
+typedef struct {
+  CURLversion age;          /* age of the returned struct */
+  const char *version;      /* LIBCURL_VERSION */
+  unsigned int version_num; /* LIBCURL_VERSION_NUM */
+  const char *host;         /* OS/host/cpu/machine when configured */
+  int features;             /* bitmask, see defines below */
+  const char *ssl_version;  /* human readable string */
+  long ssl_version_num;     /* not used anymore, always 0 */
+  const char *libz_version; /* human readable string */
+  /* protocols is terminated by an entry with a NULL protoname */
+  const char * const *protocols;
+
+  /* The fields below this were added in CURLVERSION_SECOND */
+  const char *ares;
+  int ares_num;
+
+  /* This field was added in CURLVERSION_THIRD */
+  const char *libidn;
+
+  /* These field were added in CURLVERSION_FOURTH */
+
+  /* Same as '_libiconv_version' if built with HAVE_ICONV */
+  int iconv_ver_num;
+
+  const char *libssh_version; /* human readable string */
+
+  /* These fields were added in CURLVERSION_FIFTH */
+
+  unsigned int brotli_ver_num; /* Numeric Brotli version
+                                  (MAJOR << 24) | (MINOR << 12) | PATCH */
+  const char *brotli_version; /* human readable string. */
+
+} curl_version_info_data;
+
+#define CURL_VERSION_IPV6         (1<<0)  /* IPv6-enabled */
+#define CURL_VERSION_KERBEROS4    (1<<1)  /* Kerberos V4 auth is supported
+                                             (deprecated) */
+#define CURL_VERSION_SSL          (1<<2)  /* SSL options are present */
+#define CURL_VERSION_LIBZ         (1<<3)  /* libz features are present */
+#define CURL_VERSION_NTLM         (1<<4)  /* NTLM auth is supported */
+#define CURL_VERSION_GSSNEGOTIATE (1<<5)  /* Negotiate auth is supported
+                                             (deprecated) */
+#define CURL_VERSION_DEBUG        (1<<6)  /* Built with debug capabilities */
+#define CURL_VERSION_ASYNCHDNS    (1<<7)  /* Asynchronous DNS resolves */
+#define CURL_VERSION_SPNEGO       (1<<8)  /* SPNEGO auth is supported */
+#define CURL_VERSION_LARGEFILE    (1<<9)  /* Supports files larger than 2GB */
+#define CURL_VERSION_IDN          (1<<10) /* Internationized Domain Names are
+                                             supported */
+#define CURL_VERSION_SSPI         (1<<11) /* Built against Windows SSPI */
+#define CURL_VERSION_CONV         (1<<12) /* Character conversions supported */
+#define CURL_VERSION_CURLDEBUG    (1<<13) /* Debug memory tracking supported */
+#define CURL_VERSION_TLSAUTH_SRP  (1<<14) /* TLS-SRP auth is supported */
+#define CURL_VERSION_NTLM_WB      (1<<15) /* NTLM delegation to winbind helper
+                                             is supported */
+#define CURL_VERSION_HTTP2        (1<<16) /* HTTP2 support built-in */
+#define CURL_VERSION_GSSAPI       (1<<17) /* Built against a GSS-API library */
+#define CURL_VERSION_KERBEROS5    (1<<18) /* Kerberos V5 auth is supported */
+#define CURL_VERSION_UNIX_SOCKETS (1<<19) /* Unix domain sockets support */
+#define CURL_VERSION_PSL          (1<<20) /* Mozilla's Public Suffix List, used
+                                             for cookie domain verification */
+#define CURL_VERSION_HTTPS_PROXY  (1<<21) /* HTTPS-proxy support built-in */
+#define CURL_VERSION_MULTI_SSL    (1<<22) /* Multiple SSL backends available */
+#define CURL_VERSION_BROTLI       (1<<23) /* Brotli features are present. */
+
+ /*
+ * NAME curl_version_info()
+ *
+ * DESCRIPTION
+ *
+ * This function returns a pointer to a static copy of the version info
+ * struct. See above.
+ */
+CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion);
+
+/*
+ * NAME curl_easy_strerror()
+ *
+ * DESCRIPTION
+ *
+ * The curl_easy_strerror function may be used to turn a CURLcode value
+ * into the equivalent human readable error string.  This is useful
+ * for printing meaningful error messages.
+ */
+CURL_EXTERN const char *curl_easy_strerror(CURLcode);
+
+/*
+ * NAME curl_share_strerror()
+ *
+ * DESCRIPTION
+ *
+ * The curl_share_strerror function may be used to turn a CURLSHcode value
+ * into the equivalent human readable error string.  This is useful
+ * for printing meaningful error messages.
+ */
+CURL_EXTERN const char *curl_share_strerror(CURLSHcode);
+
+/*
+ * NAME curl_easy_pause()
+ *
+ * DESCRIPTION
+ *
+ * The curl_easy_pause function pauses or unpauses transfers. Select the new
+ * state by setting the bitmask, use the convenience defines below.
+ *
+ */
+CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);
+
+#define CURLPAUSE_RECV      (1<<0)
+#define CURLPAUSE_RECV_CONT (0)
+
+#define CURLPAUSE_SEND      (1<<2)
+#define CURLPAUSE_SEND_CONT (0)
+
+#define CURLPAUSE_ALL       (CURLPAUSE_RECV|CURLPAUSE_SEND)
+#define CURLPAUSE_CONT      (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT)
+
+#ifdef  __cplusplus
+}
+#endif
+
+/* unfortunately, the easy.h and multi.h include files need options and info
+  stuff before they can be included! */
+#include "easy.h" /* nothing in curl is fun without the easy stuff */
+#include "multi.h"
+
+/* the typechecker doesn't work in C++ (yet) */
+#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \
+    ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \
+    !defined(__cplusplus) && !defined(CURL_DISABLE_TYPECHECK)
+#include "typecheck-gcc.h"
+#else
+#if defined(__STDC__) && (__STDC__ >= 1)
+/* This preprocessor magic that replaces a call with the exact same call is
+   only done to make sure application authors pass exactly three arguments
+   to these functions. */
+#define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param)
+#define curl_easy_getinfo(handle,info,arg) curl_easy_getinfo(handle,info,arg)
+#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param)
+#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param)
+#endif /* __STDC__ >= 1 */
+#endif /* gcc >= 4.3 && !__cplusplus */
+
+#endif /* __CURL_CURL_H */

+ 77 - 0
ext/curl-7.58.0/Win32/include/curl/curlver.h

@@ -0,0 +1,77 @@
+#ifndef __CURL_CURLVER_H
+#define __CURL_CURLVER_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/* This header file contains nothing but libcurl version info, generated by
+   a script at release-time. This was made its own header file in 7.11.2 */
+
+/* This is the global package copyright */
+#define LIBCURL_COPYRIGHT "1996 - 2017 Daniel Stenberg, <[email protected]>."
+
+/* This is the version number of the libcurl package from which this header
+   file origins: */
+#define LIBCURL_VERSION "7.58.0"
+
+/* The numeric version number is also available "in parts" by using these
+   defines: */
+#define LIBCURL_VERSION_MAJOR 7
+#define LIBCURL_VERSION_MINOR 58
+#define LIBCURL_VERSION_PATCH 0
+
+/* This is the numeric version of the libcurl version number, meant for easier
+   parsing and comparions by programs. The LIBCURL_VERSION_NUM define will
+   always follow this syntax:
+
+         0xXXYYZZ
+
+   Where XX, YY and ZZ are the main version, release and patch numbers in
+   hexadecimal (using 8 bits each). All three numbers are always represented
+   using two digits.  1.2 would appear as "0x010200" while version 9.11.7
+   appears as "0x090b07".
+
+   This 6-digit (24 bits) hexadecimal number does not show pre-release number,
+   and it is always a greater number in a more recent release. It makes
+   comparisons with greater than and less than work.
+
+   Note: This define is the full hex number and _does not_ use the
+   CURL_VERSION_BITS() macro since curl's own configure script greps for it
+   and needs it to contain the full number.
+*/
+#define LIBCURL_VERSION_NUM 0x073a00
+
+/*
+ * This is the date and time when the full source package was created. The
+ * timestamp is not stored in git, as the timestamp is properly set in the
+ * tarballs by the maketgz script.
+ *
+ * The format of the date follows this template:
+ *
+ * "2007-11-23"
+ */
+#define LIBCURL_TIMESTAMP "2018-01-24"
+
+#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|z)
+#define CURL_AT_LEAST_VERSION(x,y,z) \
+  (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
+
+#endif /* __CURL_CURLVER_H */

+ 102 - 0
ext/curl-7.58.0/Win32/include/curl/easy.h

@@ -0,0 +1,102 @@
+#ifndef __CURL_EASY_H
+#define __CURL_EASY_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2016, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+CURL_EXTERN CURL *curl_easy_init(void);
+CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);
+CURL_EXTERN CURLcode curl_easy_perform(CURL *curl);
+CURL_EXTERN void curl_easy_cleanup(CURL *curl);
+
+/*
+ * NAME curl_easy_getinfo()
+ *
+ * DESCRIPTION
+ *
+ * Request internal information from the curl session with this function.  The
+ * third argument MUST be a pointer to a long, a pointer to a char * or a
+ * pointer to a double (as the documentation describes elsewhere).  The data
+ * pointed to will be filled in accordingly and can be relied upon only if the
+ * function returns CURLE_OK.  This function is intended to get used *AFTER* a
+ * performed transfer, all results from this function are undefined until the
+ * transfer is completed.
+ */
+CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...);
+
+
+/*
+ * NAME curl_easy_duphandle()
+ *
+ * DESCRIPTION
+ *
+ * Creates a new curl session handle with the same options set for the handle
+ * passed in. Duplicating a handle could only be a matter of cloning data and
+ * options, internal state info and things like persistent connections cannot
+ * be transferred. It is useful in multithreaded applications when you can run
+ * curl_easy_duphandle() for each new thread to avoid a series of identical
+ * curl_easy_setopt() invokes in every thread.
+ */
+CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl);
+
+/*
+ * NAME curl_easy_reset()
+ *
+ * DESCRIPTION
+ *
+ * Re-initializes a CURL handle to the default values. This puts back the
+ * handle to the same state as it was in when it was just created.
+ *
+ * It does keep: live connections, the Session ID cache, the DNS cache and the
+ * cookies.
+ */
+CURL_EXTERN void curl_easy_reset(CURL *curl);
+
+/*
+ * NAME curl_easy_recv()
+ *
+ * DESCRIPTION
+ *
+ * Receives data from the connected socket. Use after successful
+ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
+ */
+CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen,
+                                    size_t *n);
+
+/*
+ * NAME curl_easy_send()
+ *
+ * DESCRIPTION
+ *
+ * Sends data over the connected socket. Use after successful
+ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
+ */
+CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer,
+                                    size_t buflen, size_t *n);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif

+ 50 - 0
ext/curl-7.58.0/Win32/include/curl/mprintf.h

@@ -0,0 +1,50 @@
+#ifndef __CURL_MPRINTF_H
+#define __CURL_MPRINTF_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2016, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include <stdarg.h>
+#include <stdio.h> /* needed for FILE */
+#include "curl.h"  /* for CURL_EXTERN */
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+CURL_EXTERN int curl_mprintf(const char *format, ...);
+CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...);
+CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...);
+CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength,
+                               const char *format, ...);
+CURL_EXTERN int curl_mvprintf(const char *format, va_list args);
+CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args);
+CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args);
+CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength,
+                                const char *format, va_list args);
+CURL_EXTERN char *curl_maprintf(const char *format, ...);
+CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* __CURL_MPRINTF_H */

+ 439 - 0
ext/curl-7.58.0/Win32/include/curl/multi.h

@@ -0,0 +1,439 @@
+#ifndef __CURL_MULTI_H
+#define __CURL_MULTI_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+/*
+  This is an "external" header file. Don't give away any internals here!
+
+  GOALS
+
+  o Enable a "pull" interface. The application that uses libcurl decides where
+    and when to ask libcurl to get/send data.
+
+  o Enable multiple simultaneous transfers in the same thread without making it
+    complicated for the application.
+
+  o Enable the application to select() on its own file descriptors and curl's
+    file descriptors simultaneous easily.
+
+*/
+
+/*
+ * This header file should not really need to include "curl.h" since curl.h
+ * itself includes this file and we expect user applications to do #include
+ * <curl/curl.h> without the need for especially including multi.h.
+ *
+ * For some reason we added this include here at one point, and rather than to
+ * break existing (wrongly written) libcurl applications, we leave it as-is
+ * but with this warning attached.
+ */
+#include "curl.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER)
+typedef struct Curl_multi CURLM;
+#else
+typedef void CURLM;
+#endif
+
+typedef enum {
+  CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or
+                                    curl_multi_socket*() soon */
+  CURLM_OK,
+  CURLM_BAD_HANDLE,      /* the passed-in handle is not a valid CURLM handle */
+  CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */
+  CURLM_OUT_OF_MEMORY,   /* if you ever get this, you're in deep sh*t */
+  CURLM_INTERNAL_ERROR,  /* this is a libcurl bug */
+  CURLM_BAD_SOCKET,      /* the passed in socket argument did not match */
+  CURLM_UNKNOWN_OPTION,  /* curl_multi_setopt() with unsupported option */
+  CURLM_ADDED_ALREADY,   /* an easy handle already added to a multi handle was
+                            attempted to get added - again */
+  CURLM_LAST
+} CURLMcode;
+
+/* just to make code nicer when using curl_multi_socket() you can now check
+   for CURLM_CALL_MULTI_SOCKET too in the same style it works for
+   curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */
+#define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM
+
+/* bitmask bits for CURLMOPT_PIPELINING */
+#define CURLPIPE_NOTHING   0L
+#define CURLPIPE_HTTP1     1L
+#define CURLPIPE_MULTIPLEX 2L
+
+typedef enum {
+  CURLMSG_NONE, /* first, not used */
+  CURLMSG_DONE, /* This easy handle has completed. 'result' contains
+                   the CURLcode of the transfer */
+  CURLMSG_LAST /* last, not used */
+} CURLMSG;
+
+struct CURLMsg {
+  CURLMSG msg;       /* what this message means */
+  CURL *easy_handle; /* the handle it concerns */
+  union {
+    void *whatever;    /* message-specific data */
+    CURLcode result;   /* return code for transfer */
+  } data;
+};
+typedef struct CURLMsg CURLMsg;
+
+/* Based on poll(2) structure and values.
+ * We don't use pollfd and POLL* constants explicitly
+ * to cover platforms without poll(). */
+#define CURL_WAIT_POLLIN    0x0001
+#define CURL_WAIT_POLLPRI   0x0002
+#define CURL_WAIT_POLLOUT   0x0004
+
+struct curl_waitfd {
+  curl_socket_t fd;
+  short events;
+  short revents; /* not supported yet */
+};
+
+/*
+ * Name:    curl_multi_init()
+ *
+ * Desc:    inititalize multi-style curl usage
+ *
+ * Returns: a new CURLM handle to use in all 'curl_multi' functions.
+ */
+CURL_EXTERN CURLM *curl_multi_init(void);
+
+/*
+ * Name:    curl_multi_add_handle()
+ *
+ * Desc:    add a standard curl handle to the multi stack
+ *
+ * Returns: CURLMcode type, general multi error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle,
+                                            CURL *curl_handle);
+
+ /*
+  * Name:    curl_multi_remove_handle()
+  *
+  * Desc:    removes a curl handle from the multi stack again
+  *
+  * Returns: CURLMcode type, general multi error code.
+  */
+CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
+                                               CURL *curl_handle);
+
+ /*
+  * Name:    curl_multi_fdset()
+  *
+  * Desc:    Ask curl for its fd_set sets. The app can use these to select() or
+  *          poll() on. We want curl_multi_perform() called as soon as one of
+  *          them are ready.
+  *
+  * Returns: CURLMcode type, general multi error code.
+  */
+CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle,
+                                       fd_set *read_fd_set,
+                                       fd_set *write_fd_set,
+                                       fd_set *exc_fd_set,
+                                       int *max_fd);
+
+/*
+ * Name:     curl_multi_wait()
+ *
+ * Desc:     Poll on all fds within a CURLM set as well as any
+ *           additional fds passed to the function.
+ *
+ * Returns:  CURLMcode type, general multi error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle,
+                                      struct curl_waitfd extra_fds[],
+                                      unsigned int extra_nfds,
+                                      int timeout_ms,
+                                      int *ret);
+
+ /*
+  * Name:    curl_multi_perform()
+  *
+  * Desc:    When the app thinks there's data available for curl it calls this
+  *          function to read/write whatever there is right now. This returns
+  *          as soon as the reads and writes are done. This function does not
+  *          require that there actually is data available for reading or that
+  *          data can be written, it can be called just in case. It returns
+  *          the number of handles that still transfer data in the second
+  *          argument's integer-pointer.
+  *
+  * Returns: CURLMcode type, general multi error code. *NOTE* that this only
+  *          returns errors etc regarding the whole multi stack. There might
+  *          still have occurred problems on invidual transfers even when this
+  *          returns OK.
+  */
+CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle,
+                                         int *running_handles);
+
+ /*
+  * Name:    curl_multi_cleanup()
+  *
+  * Desc:    Cleans up and removes a whole multi stack. It does not free or
+  *          touch any individual easy handles in any way. We need to define
+  *          in what state those handles will be if this function is called
+  *          in the middle of a transfer.
+  *
+  * Returns: CURLMcode type, general multi error code.
+  */
+CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle);
+
+/*
+ * Name:    curl_multi_info_read()
+ *
+ * Desc:    Ask the multi handle if there's any messages/informationals from
+ *          the individual transfers. Messages include informationals such as
+ *          error code from the transfer or just the fact that a transfer is
+ *          completed. More details on these should be written down as well.
+ *
+ *          Repeated calls to this function will return a new struct each
+ *          time, until a special "end of msgs" struct is returned as a signal
+ *          that there is no more to get at this point.
+ *
+ *          The data the returned pointer points to will not survive calling
+ *          curl_multi_cleanup().
+ *
+ *          The 'CURLMsg' struct is meant to be very simple and only contain
+ *          very basic information. If more involved information is wanted,
+ *          we will provide the particular "transfer handle" in that struct
+ *          and that should/could/would be used in subsequent
+ *          curl_easy_getinfo() calls (or similar). The point being that we
+ *          must never expose complex structs to applications, as then we'll
+ *          undoubtably get backwards compatibility problems in the future.
+ *
+ * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out
+ *          of structs. It also writes the number of messages left in the
+ *          queue (after this read) in the integer the second argument points
+ *          to.
+ */
+CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle,
+                                          int *msgs_in_queue);
+
+/*
+ * Name:    curl_multi_strerror()
+ *
+ * Desc:    The curl_multi_strerror function may be used to turn a CURLMcode
+ *          value into the equivalent human readable error string.  This is
+ *          useful for printing meaningful error messages.
+ *
+ * Returns: A pointer to a zero-terminated error message.
+ */
+CURL_EXTERN const char *curl_multi_strerror(CURLMcode);
+
+/*
+ * Name:    curl_multi_socket() and
+ *          curl_multi_socket_all()
+ *
+ * Desc:    An alternative version of curl_multi_perform() that allows the
+ *          application to pass in one of the file descriptors that have been
+ *          detected to have "action" on them and let libcurl perform.
+ *          See man page for details.
+ */
+#define CURL_POLL_NONE   0
+#define CURL_POLL_IN     1
+#define CURL_POLL_OUT    2
+#define CURL_POLL_INOUT  3
+#define CURL_POLL_REMOVE 4
+
+#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD
+
+#define CURL_CSELECT_IN   0x01
+#define CURL_CSELECT_OUT  0x02
+#define CURL_CSELECT_ERR  0x04
+
+typedef int (*curl_socket_callback)(CURL *easy,      /* easy handle */
+                                    curl_socket_t s, /* socket */
+                                    int what,        /* see above */
+                                    void *userp,     /* private callback
+                                                        pointer */
+                                    void *socketp);  /* private socket
+                                                        pointer */
+/*
+ * Name:    curl_multi_timer_callback
+ *
+ * Desc:    Called by libcurl whenever the library detects a change in the
+ *          maximum number of milliseconds the app is allowed to wait before
+ *          curl_multi_socket() or curl_multi_perform() must be called
+ *          (to allow libcurl's timed events to take place).
+ *
+ * Returns: The callback should return zero.
+ */
+typedef int (*curl_multi_timer_callback)(CURLM *multi,    /* multi handle */
+                                         long timeout_ms, /* see above */
+                                         void *userp);    /* private callback
+                                                             pointer */
+
+CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,
+                                        int *running_handles);
+
+CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle,
+                                               curl_socket_t s,
+                                               int ev_bitmask,
+                                               int *running_handles);
+
+CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle,
+                                            int *running_handles);
+
+#ifndef CURL_ALLOW_OLD_MULTI_SOCKET
+/* This macro below was added in 7.16.3 to push users who recompile to use
+   the new curl_multi_socket_action() instead of the old curl_multi_socket()
+*/
+#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z)
+#endif
+
+/*
+ * Name:    curl_multi_timeout()
+ *
+ * Desc:    Returns the maximum number of milliseconds the app is allowed to
+ *          wait before curl_multi_socket() or curl_multi_perform() must be
+ *          called (to allow libcurl's timed events to take place).
+ *
+ * Returns: CURLM error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle,
+                                         long *milliseconds);
+
+#undef CINIT /* re-using the same name as in curl.h */
+
+#ifdef CURL_ISOCPP
+#define CINIT(name,type,num) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + num
+#else
+/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */
+#define LONG          CURLOPTTYPE_LONG
+#define OBJECTPOINT   CURLOPTTYPE_OBJECTPOINT
+#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT
+#define OFF_T         CURLOPTTYPE_OFF_T
+#define CINIT(name,type,number) CURLMOPT_/**/name = type + number
+#endif
+
+typedef enum {
+  /* This is the socket callback function pointer */
+  CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1),
+
+  /* This is the argument passed to the socket callback */
+  CINIT(SOCKETDATA, OBJECTPOINT, 2),
+
+    /* set to 1 to enable pipelining for this multi handle */
+  CINIT(PIPELINING, LONG, 3),
+
+   /* This is the timer callback function pointer */
+  CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4),
+
+  /* This is the argument passed to the timer callback */
+  CINIT(TIMERDATA, OBJECTPOINT, 5),
+
+  /* maximum number of entries in the connection cache */
+  CINIT(MAXCONNECTS, LONG, 6),
+
+  /* maximum number of (pipelining) connections to one host */
+  CINIT(MAX_HOST_CONNECTIONS, LONG, 7),
+
+  /* maximum number of requests in a pipeline */
+  CINIT(MAX_PIPELINE_LENGTH, LONG, 8),
+
+  /* a connection with a content-length longer than this
+     will not be considered for pipelining */
+  CINIT(CONTENT_LENGTH_PENALTY_SIZE, OFF_T, 9),
+
+  /* a connection with a chunk length longer than this
+     will not be considered for pipelining */
+  CINIT(CHUNK_LENGTH_PENALTY_SIZE, OFF_T, 10),
+
+  /* a list of site names(+port) that are blacklisted from
+     pipelining */
+  CINIT(PIPELINING_SITE_BL, OBJECTPOINT, 11),
+
+  /* a list of server types that are blacklisted from
+     pipelining */
+  CINIT(PIPELINING_SERVER_BL, OBJECTPOINT, 12),
+
+  /* maximum number of open connections in total */
+  CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13),
+
+   /* This is the server push callback function pointer */
+  CINIT(PUSHFUNCTION, FUNCTIONPOINT, 14),
+
+  /* This is the argument passed to the server push callback */
+  CINIT(PUSHDATA, OBJECTPOINT, 15),
+
+  CURLMOPT_LASTENTRY /* the last unused */
+} CURLMoption;
+
+
+/*
+ * Name:    curl_multi_setopt()
+ *
+ * Desc:    Sets options for the multi handle.
+ *
+ * Returns: CURLM error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle,
+                                        CURLMoption option, ...);
+
+
+/*
+ * Name:    curl_multi_assign()
+ *
+ * Desc:    This function sets an association in the multi handle between the
+ *          given socket and a private pointer of the application. This is
+ *          (only) useful for curl_multi_socket uses.
+ *
+ * Returns: CURLM error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle,
+                                        curl_socket_t sockfd, void *sockp);
+
+
+/*
+ * Name: curl_push_callback
+ *
+ * Desc: This callback gets called when a new stream is being pushed by the
+ *       server. It approves or denies the new stream.
+ *
+ * Returns: CURL_PUSH_OK or CURL_PUSH_DENY.
+ */
+#define CURL_PUSH_OK   0
+#define CURL_PUSH_DENY 1
+
+struct curl_pushheaders;  /* forward declaration only */
+
+CURL_EXTERN char *curl_pushheader_bynum(struct curl_pushheaders *h,
+                                        size_t num);
+CURL_EXTERN char *curl_pushheader_byname(struct curl_pushheaders *h,
+                                         const char *name);
+
+typedef int (*curl_push_callback)(CURL *parent,
+                                  CURL *easy,
+                                  size_t num_headers,
+                                  struct curl_pushheaders *headers,
+                                  void *userp);
+
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif
+
+#endif

+ 33 - 0
ext/curl-7.58.0/Win32/include/curl/stdcheaders.h

@@ -0,0 +1,33 @@
+#ifndef __STDC_HEADERS_H
+#define __STDC_HEADERS_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2016, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include <sys/types.h>
+
+size_t fread(void *, size_t, size_t, FILE *);
+size_t fwrite(const void *, size_t, size_t, FILE *);
+
+int strcasecmp(const char *, const char *);
+int strncasecmp(const char *, const char *, size_t);
+
+#endif /* __STDC_HEADERS_H */

+ 473 - 0
ext/curl-7.58.0/Win32/include/curl/system.h

@@ -0,0 +1,473 @@
+#ifndef __CURL_SYSTEM_H
+#define __CURL_SYSTEM_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * Try to keep one section per platform, compiler and architecture, otherwise,
+ * if an existing section is reused for a different one and later on the
+ * original is adjusted, probably the piggybacking one can be adversely
+ * changed.
+ *
+ * In order to differentiate between platforms/compilers/architectures use
+ * only compiler built in predefined preprocessor symbols.
+ *
+ * curl_off_t
+ * ----------
+ *
+ * For any given platform/compiler curl_off_t must be typedef'ed to a 64-bit
+ * wide signed integral data type. The width of this data type must remain
+ * constant and independent of any possible large file support settings.
+ *
+ * As an exception to the above, curl_off_t shall be typedef'ed to a 32-bit
+ * wide signed integral data type if there is no 64-bit type.
+ *
+ * As a general rule, curl_off_t shall not be mapped to off_t. This rule shall
+ * only be violated if off_t is the only 64-bit data type available and the
+ * size of off_t is independent of large file support settings. Keep your
+ * build on the safe side avoiding an off_t gating.  If you have a 64-bit
+ * off_t then take for sure that another 64-bit data type exists, dig deeper
+ * and you will find it.
+ *
+ */
+
+#if defined(__DJGPP__) || defined(__GO32__)
+#  if defined(__DJGPP__) && (__DJGPP__ > 1)
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__SALFORDC__)
+#  define CURL_TYPEOF_CURL_OFF_T     long
+#  define CURL_FORMAT_CURL_OFF_T     "ld"
+#  define CURL_FORMAT_CURL_OFF_TU    "lu"
+#  define CURL_SUFFIX_CURL_OFF_T     L
+#  define CURL_SUFFIX_CURL_OFF_TU    UL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__BORLANDC__)
+#  if (__BORLANDC__ < 0x520)
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     "I64d"
+#    define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#    define CURL_SUFFIX_CURL_OFF_T     i64
+#    define CURL_SUFFIX_CURL_OFF_TU    ui64
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__TURBOC__)
+#  define CURL_TYPEOF_CURL_OFF_T     long
+#  define CURL_FORMAT_CURL_OFF_T     "ld"
+#  define CURL_FORMAT_CURL_OFF_TU    "lu"
+#  define CURL_SUFFIX_CURL_OFF_T     L
+#  define CURL_SUFFIX_CURL_OFF_TU    UL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__WATCOMC__)
+#  if defined(__386__)
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     "I64d"
+#    define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#    define CURL_SUFFIX_CURL_OFF_T     i64
+#    define CURL_SUFFIX_CURL_OFF_TU    ui64
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__POCC__)
+#  if (__POCC__ < 280)
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  elif defined(_MSC_VER)
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     "I64d"
+#    define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#    define CURL_SUFFIX_CURL_OFF_T     i64
+#    define CURL_SUFFIX_CURL_OFF_TU    ui64
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__LCC__)
+#  define CURL_TYPEOF_CURL_OFF_T     long
+#  define CURL_FORMAT_CURL_OFF_T     "ld"
+#  define CURL_FORMAT_CURL_OFF_TU    "lu"
+#  define CURL_SUFFIX_CURL_OFF_T     L
+#  define CURL_SUFFIX_CURL_OFF_TU    UL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__SYMBIAN32__)
+#  if defined(__EABI__)  /* Treat all ARM compilers equally */
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  elif defined(__CW32__)
+#    pragma longlong on
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  elif defined(__VC32__)
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
+
+#elif defined(__MWERKS__)
+#  define CURL_TYPEOF_CURL_OFF_T     long long
+#  define CURL_FORMAT_CURL_OFF_T     "lld"
+#  define CURL_FORMAT_CURL_OFF_TU    "llu"
+#  define CURL_SUFFIX_CURL_OFF_T     LL
+#  define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(_WIN32_WCE)
+#  define CURL_TYPEOF_CURL_OFF_T     __int64
+#  define CURL_FORMAT_CURL_OFF_T     "I64d"
+#  define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#  define CURL_SUFFIX_CURL_OFF_T     i64
+#  define CURL_SUFFIX_CURL_OFF_TU    ui64
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__MINGW32__)
+#  define CURL_TYPEOF_CURL_OFF_T     long long
+#  define CURL_FORMAT_CURL_OFF_T     "I64d"
+#  define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#  define CURL_SUFFIX_CURL_OFF_T     LL
+#  define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#  define CURL_PULL_SYS_TYPES_H      1
+#  define CURL_PULL_WS2TCPIP_H       1
+
+#elif defined(__VMS)
+#  if defined(__VAX)
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
+
+#elif defined(__OS400__)
+#  if defined(__ILEC400__)
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#    define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#    define CURL_PULL_SYS_TYPES_H      1
+#    define CURL_PULL_SYS_SOCKET_H     1
+#  endif
+
+#elif defined(__MVS__)
+#  if defined(__IBMC__) || defined(__IBMCPP__)
+#    if defined(_ILP32)
+#    elif defined(_LP64)
+#    endif
+#    if defined(_LONG_LONG)
+#      define CURL_TYPEOF_CURL_OFF_T     long long
+#      define CURL_FORMAT_CURL_OFF_T     "lld"
+#      define CURL_FORMAT_CURL_OFF_TU    "llu"
+#      define CURL_SUFFIX_CURL_OFF_T     LL
+#      define CURL_SUFFIX_CURL_OFF_TU    ULL
+#    elif defined(_LP64)
+#      define CURL_TYPEOF_CURL_OFF_T     long
+#      define CURL_FORMAT_CURL_OFF_T     "ld"
+#      define CURL_FORMAT_CURL_OFF_TU    "lu"
+#      define CURL_SUFFIX_CURL_OFF_T     L
+#      define CURL_SUFFIX_CURL_OFF_TU    UL
+#    else
+#      define CURL_TYPEOF_CURL_OFF_T     long
+#      define CURL_FORMAT_CURL_OFF_T     "ld"
+#      define CURL_FORMAT_CURL_OFF_TU    "lu"
+#      define CURL_SUFFIX_CURL_OFF_T     L
+#      define CURL_SUFFIX_CURL_OFF_TU    UL
+#    endif
+#    define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#    define CURL_PULL_SYS_TYPES_H      1
+#    define CURL_PULL_SYS_SOCKET_H     1
+#  endif
+
+#elif defined(__370__)
+#  if defined(__IBMC__) || defined(__IBMCPP__)
+#    if defined(_ILP32)
+#    elif defined(_LP64)
+#    endif
+#    if defined(_LONG_LONG)
+#      define CURL_TYPEOF_CURL_OFF_T     long long
+#      define CURL_FORMAT_CURL_OFF_T     "lld"
+#      define CURL_FORMAT_CURL_OFF_TU    "llu"
+#      define CURL_SUFFIX_CURL_OFF_T     LL
+#      define CURL_SUFFIX_CURL_OFF_TU    ULL
+#    elif defined(_LP64)
+#      define CURL_TYPEOF_CURL_OFF_T     long
+#      define CURL_FORMAT_CURL_OFF_T     "ld"
+#      define CURL_FORMAT_CURL_OFF_TU    "lu"
+#      define CURL_SUFFIX_CURL_OFF_T     L
+#      define CURL_SUFFIX_CURL_OFF_TU    UL
+#    else
+#      define CURL_TYPEOF_CURL_OFF_T     long
+#      define CURL_FORMAT_CURL_OFF_T     "ld"
+#      define CURL_FORMAT_CURL_OFF_TU    "lu"
+#      define CURL_SUFFIX_CURL_OFF_T     L
+#      define CURL_SUFFIX_CURL_OFF_TU    UL
+#    endif
+#    define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#    define CURL_PULL_SYS_TYPES_H      1
+#    define CURL_PULL_SYS_SOCKET_H     1
+#  endif
+
+#elif defined(TPF)
+#  define CURL_TYPEOF_CURL_OFF_T     long
+#  define CURL_FORMAT_CURL_OFF_T     "ld"
+#  define CURL_FORMAT_CURL_OFF_TU    "lu"
+#  define CURL_SUFFIX_CURL_OFF_T     L
+#  define CURL_SUFFIX_CURL_OFF_TU    UL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__TINYC__) /* also known as tcc */
+
+#  define CURL_TYPEOF_CURL_OFF_T     long long
+#  define CURL_FORMAT_CURL_OFF_T     "lld"
+#  define CURL_FORMAT_CURL_OFF_TU    "llu"
+#  define CURL_SUFFIX_CURL_OFF_T     LL
+#  define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#  define CURL_PULL_SYS_TYPES_H      1
+#  define CURL_PULL_SYS_SOCKET_H     1
+
+#elif defined(__SUNPRO_C) /* Oracle Solaris Studio */
+#  if !defined(__LP64) && (defined(__ILP32) ||                          \
+                           defined(__i386) || defined(__sparcv8))
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  elif defined(__LP64) || \
+        defined(__amd64) || defined(__sparcv9)
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#  define CURL_PULL_SYS_TYPES_H      1
+#  define CURL_PULL_SYS_SOCKET_H     1
+
+/* ===================================== */
+/*    KEEP MSVC THE PENULTIMATE ENTRY    */
+/* ===================================== */
+
+#elif defined(_MSC_VER)
+#  if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     "I64d"
+#    define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#    define CURL_SUFFIX_CURL_OFF_T     i64
+#    define CURL_SUFFIX_CURL_OFF_TU    ui64
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+/* ===================================== */
+/*    KEEP GENERIC GCC THE LAST ENTRY    */
+/* ===================================== */
+
+#elif defined(__GNUC__)
+#  if !defined(__LP64__) &&                                             \
+  (defined(__ILP32__) || defined(__i386__) || defined(__hppa__) ||      \
+   defined(__ppc__) || defined(__powerpc__) || defined(__arm__) ||      \
+   defined(__sparc__) || defined(__mips__) || defined(__sh__) ||        \
+   defined(__XTENSA__) ||                                               \
+   (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4)  ||               \
+   (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L))
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  elif defined(__LP64__) || \
+        defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \
+        (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8) || \
+        (defined(__LONG_MAX__) && __LONG_MAX__ == 9223372036854775807L)
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#  define CURL_PULL_SYS_TYPES_H      1
+#  define CURL_PULL_SYS_SOCKET_H     1
+
+#else
+/* generic "safe guess" on old 32 bit style */
+# define CURL_TYPEOF_CURL_OFF_T     long
+# define CURL_FORMAT_CURL_OFF_T     "ld"
+# define CURL_FORMAT_CURL_OFF_TU    "lu"
+# define CURL_SUFFIX_CURL_OFF_T     L
+# define CURL_SUFFIX_CURL_OFF_TU    UL
+# define CURL_TYPEOF_CURL_SOCKLEN_T int
+#endif
+
+#ifdef _AIX
+/* AIX needs <sys/poll.h> */
+#define CURL_PULL_SYS_POLL_H
+#endif
+
+
+/* CURL_PULL_WS2TCPIP_H is defined above when inclusion of header file  */
+/* ws2tcpip.h is required here to properly make type definitions below. */
+#ifdef CURL_PULL_WS2TCPIP_H
+#  include <winsock2.h>
+#  include <windows.h>
+#  include <ws2tcpip.h>
+#endif
+
+/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file  */
+/* sys/types.h is required here to properly make type definitions below. */
+#ifdef CURL_PULL_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+
+/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file  */
+/* sys/socket.h is required here to properly make type definitions below. */
+#ifdef CURL_PULL_SYS_SOCKET_H
+#  include <sys/socket.h>
+#endif
+
+/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file    */
+/* sys/poll.h is required here to properly make type definitions below.   */
+#ifdef CURL_PULL_SYS_POLL_H
+#  include <sys/poll.h>
+#endif
+
+/* Data type definition of curl_socklen_t. */
+#ifdef CURL_TYPEOF_CURL_SOCKLEN_T
+  typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t;
+#endif
+
+/* Data type definition of curl_off_t. */
+
+#ifdef CURL_TYPEOF_CURL_OFF_T
+  typedef CURL_TYPEOF_CURL_OFF_T curl_off_t;
+#endif
+
+/*
+ * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow
+ * these to be visible and exported by the external libcurl interface API,
+ * while also making them visible to the library internals, simply including
+ * curl_setup.h, without actually needing to include curl.h internally.
+ * If some day this section would grow big enough, all this should be moved
+ * to its own header file.
+ */
+
+/*
+ * Figure out if we can use the ## preprocessor operator, which is supported
+ * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__
+ * or  __cplusplus so we need to carefully check for them too.
+ */
+
+#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \
+  defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \
+  defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \
+  defined(__ILEC400__)
+  /* This compiler is believed to have an ISO compatible preprocessor */
+#define CURL_ISOCPP
+#else
+  /* This compiler is believed NOT to have an ISO compatible preprocessor */
+#undef CURL_ISOCPP
+#endif
+
+/*
+ * Macros for minimum-width signed and unsigned curl_off_t integer constants.
+ */
+
+#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551)
+#  define __CURL_OFF_T_C_HLPR2(x) x
+#  define __CURL_OFF_T_C_HLPR1(x) __CURL_OFF_T_C_HLPR2(x)
+#  define CURL_OFF_T_C(Val)  __CURL_OFF_T_C_HLPR1(Val) ## \
+                             __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T)
+#  define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \
+                             __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU)
+#else
+#  ifdef CURL_ISOCPP
+#    define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix
+#  else
+#    define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix
+#  endif
+#  define __CURL_OFF_T_C_HLPR1(Val,Suffix) __CURL_OFF_T_C_HLPR2(Val,Suffix)
+#  define CURL_OFF_T_C(Val)  __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T)
+#  define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU)
+#endif
+
+#endif /* __CURL_SYSTEM_H */

+ 683 - 0
ext/curl-7.58.0/Win32/include/curl/typecheck-gcc.h

@@ -0,0 +1,683 @@
+#ifndef __CURL_TYPECHECK_GCC_H
+#define __CURL_TYPECHECK_GCC_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/* wraps curl_easy_setopt() with typechecking */
+
+/* To add a new kind of warning, add an
+ *   if(_curl_is_sometype_option(_curl_opt))
+ *     if(!_curl_is_sometype(value))
+ *       _curl_easy_setopt_err_sometype();
+ * block and define _curl_is_sometype_option, _curl_is_sometype and
+ * _curl_easy_setopt_err_sometype below
+ *
+ * NOTE: We use two nested 'if' statements here instead of the && operator, in
+ *       order to work around gcc bug #32061.  It affects only gcc 4.3.x/4.4.x
+ *       when compiling with -Wlogical-op.
+ *
+ * To add an option that uses the same type as an existing option, you'll just
+ * need to extend the appropriate _curl_*_option macro
+ */
+#define curl_easy_setopt(handle, option, value)                               \
+__extension__ ({                                                              \
+  __typeof__(option) _curl_opt = option;                                     \
+  if(__builtin_constant_p(_curl_opt)) {                                       \
+    if(_curl_is_long_option(_curl_opt))                                       \
+      if(!_curl_is_long(value))                                               \
+        _curl_easy_setopt_err_long();                                         \
+    if(_curl_is_off_t_option(_curl_opt))                                      \
+      if(!_curl_is_off_t(value))                                              \
+        _curl_easy_setopt_err_curl_off_t();                                   \
+    if(_curl_is_string_option(_curl_opt))                                     \
+      if(!_curl_is_string(value))                                             \
+        _curl_easy_setopt_err_string();                                       \
+    if(_curl_is_write_cb_option(_curl_opt))                                   \
+      if(!_curl_is_write_cb(value))                                           \
+        _curl_easy_setopt_err_write_callback();                               \
+    if((_curl_opt) == CURLOPT_READFUNCTION)                                   \
+      if(!_curl_is_read_cb(value))                                            \
+        _curl_easy_setopt_err_read_cb();                                      \
+    if((_curl_opt) == CURLOPT_IOCTLFUNCTION)                                  \
+      if(!_curl_is_ioctl_cb(value))                                           \
+        _curl_easy_setopt_err_ioctl_cb();                                     \
+    if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION)                                \
+      if(!_curl_is_sockopt_cb(value))                                         \
+        _curl_easy_setopt_err_sockopt_cb();                                   \
+    if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION)                             \
+      if(!_curl_is_opensocket_cb(value))                                      \
+        _curl_easy_setopt_err_opensocket_cb();                                \
+    if((_curl_opt) == CURLOPT_PROGRESSFUNCTION)                               \
+      if(!_curl_is_progress_cb(value))                                        \
+        _curl_easy_setopt_err_progress_cb();                                  \
+    if((_curl_opt) == CURLOPT_DEBUGFUNCTION)                                  \
+      if(!_curl_is_debug_cb(value))                                           \
+        _curl_easy_setopt_err_debug_cb();                                     \
+    if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION)                               \
+      if(!_curl_is_ssl_ctx_cb(value))                                         \
+        _curl_easy_setopt_err_ssl_ctx_cb();                                   \
+    if(_curl_is_conv_cb_option(_curl_opt))                                    \
+      if(!_curl_is_conv_cb(value))                                            \
+        _curl_easy_setopt_err_conv_cb();                                      \
+    if((_curl_opt) == CURLOPT_SEEKFUNCTION)                                   \
+      if(!_curl_is_seek_cb(value))                                            \
+        _curl_easy_setopt_err_seek_cb();                                      \
+    if(_curl_is_cb_data_option(_curl_opt))                                    \
+      if(!_curl_is_cb_data(value))                                            \
+        _curl_easy_setopt_err_cb_data();                                      \
+    if((_curl_opt) == CURLOPT_ERRORBUFFER)                                    \
+      if(!_curl_is_error_buffer(value))                                       \
+        _curl_easy_setopt_err_error_buffer();                                 \
+    if((_curl_opt) == CURLOPT_STDERR)                                         \
+      if(!_curl_is_FILE(value))                                               \
+        _curl_easy_setopt_err_FILE();                                         \
+    if(_curl_is_postfields_option(_curl_opt))                                 \
+      if(!_curl_is_postfields(value))                                         \
+        _curl_easy_setopt_err_postfields();                                   \
+    if((_curl_opt) == CURLOPT_HTTPPOST)                                       \
+      if(!_curl_is_arr((value), struct curl_httppost))                        \
+        _curl_easy_setopt_err_curl_httpost();                                 \
+    if((_curl_opt) == CURLOPT_MIMEPOST)                                       \
+      if(!_curl_is_ptr((value), curl_mime))                                   \
+        _curl_easy_setopt_err_curl_mimepost();                                \
+    if(_curl_is_slist_option(_curl_opt))                                      \
+      if(!_curl_is_arr((value), struct curl_slist))                           \
+        _curl_easy_setopt_err_curl_slist();                                   \
+    if((_curl_opt) == CURLOPT_SHARE)                                          \
+      if(!_curl_is_ptr((value), CURLSH))                                      \
+        _curl_easy_setopt_err_CURLSH();                                       \
+  }                                                                           \
+  curl_easy_setopt(handle, _curl_opt, value);                                 \
+})
+
+/* wraps curl_easy_getinfo() with typechecking */
+/* FIXME: don't allow const pointers */
+#define curl_easy_getinfo(handle, info, arg)                                  \
+__extension__ ({                                                              \
+  __typeof__(info) _curl_info = info;                                         \
+  if(__builtin_constant_p(_curl_info)) {                                      \
+    if(_curl_is_string_info(_curl_info))                                      \
+      if(!_curl_is_arr((arg), char *))                                        \
+        _curl_easy_getinfo_err_string();                                      \
+    if(_curl_is_long_info(_curl_info))                                        \
+      if(!_curl_is_arr((arg), long))                                          \
+        _curl_easy_getinfo_err_long();                                        \
+    if(_curl_is_double_info(_curl_info))                                      \
+      if(!_curl_is_arr((arg), double))                                        \
+        _curl_easy_getinfo_err_double();                                      \
+    if(_curl_is_slist_info(_curl_info))                                       \
+      if(!_curl_is_arr((arg), struct curl_slist *))                           \
+        _curl_easy_getinfo_err_curl_slist();                                  \
+    if(_curl_is_tlssessioninfo_info(_curl_info))                              \
+      if(!_curl_is_arr((arg), struct curl_tlssessioninfo *))                  \
+        _curl_easy_getinfo_err_curl_tlssesssioninfo();                        \
+    if(_curl_is_certinfo_info(_curl_info))                                    \
+      if(!_curl_is_arr((arg), struct curl_certinfo *))                        \
+        _curl_easy_getinfo_err_curl_certinfo();                               \
+    if(_curl_is_socket_info(_curl_info))                                      \
+      if(!_curl_is_arr((arg), curl_socket_t))                                 \
+        _curl_easy_getinfo_err_curl_socket();                                 \
+    if(_curl_is_off_t_info(_curl_info))                                       \
+      if(!_curl_is_arr((arg), curl_off_t))                                    \
+        _curl_easy_getinfo_err_curl_off_t();                                  \
+  }                                                                           \
+  curl_easy_getinfo(handle, _curl_info, arg);                                 \
+})
+
+/* TODO: typechecking for curl_share_setopt() and curl_multi_setopt(),
+ * for now just make sure that the functions are called with three
+ * arguments
+ */
+#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param)
+#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param)
+
+
+/* the actual warnings, triggered by calling the _curl_easy_setopt_err*
+ * functions */
+
+/* To define a new warning, use _CURL_WARNING(identifier, "message") */
+#define _CURL_WARNING(id, message)                                            \
+  static void __attribute__((__warning__(message)))                           \
+  __attribute__((__unused__)) __attribute__((__noinline__))                   \
+  id(void) { __asm__(""); }
+
+_CURL_WARNING(_curl_easy_setopt_err_long,
+  "curl_easy_setopt expects a long argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_curl_off_t,
+  "curl_easy_setopt expects a curl_off_t argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_string,
+              "curl_easy_setopt expects a "
+              "string ('char *' or char[]) argument for this option"
+  )
+_CURL_WARNING(_curl_easy_setopt_err_write_callback,
+  "curl_easy_setopt expects a curl_write_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_read_cb,
+  "curl_easy_setopt expects a curl_read_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_ioctl_cb,
+  "curl_easy_setopt expects a curl_ioctl_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_sockopt_cb,
+  "curl_easy_setopt expects a curl_sockopt_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_opensocket_cb,
+              "curl_easy_setopt expects a "
+              "curl_opensocket_callback argument for this option"
+  )
+_CURL_WARNING(_curl_easy_setopt_err_progress_cb,
+  "curl_easy_setopt expects a curl_progress_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_debug_cb,
+  "curl_easy_setopt expects a curl_debug_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_ssl_ctx_cb,
+  "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_conv_cb,
+  "curl_easy_setopt expects a curl_conv_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_seek_cb,
+  "curl_easy_setopt expects a curl_seek_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_cb_data,
+              "curl_easy_setopt expects a "
+              "private data pointer as argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_error_buffer,
+              "curl_easy_setopt expects a "
+              "char buffer of CURL_ERROR_SIZE as argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_FILE,
+  "curl_easy_setopt expects a 'FILE *' argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_postfields,
+  "curl_easy_setopt expects a 'void *' or 'char *' argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_curl_httpost,
+              "curl_easy_setopt expects a 'struct curl_httppost *' "
+              "argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_curl_mimepost,
+              "curl_easy_setopt expects a 'curl_mime *' "
+              "argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_curl_slist,
+  "curl_easy_setopt expects a 'struct curl_slist *' argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_CURLSH,
+  "curl_easy_setopt expects a CURLSH* argument for this option")
+
+_CURL_WARNING(_curl_easy_getinfo_err_string,
+  "curl_easy_getinfo expects a pointer to 'char *' for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_long,
+  "curl_easy_getinfo expects a pointer to long for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_double,
+  "curl_easy_getinfo expects a pointer to double for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_curl_slist,
+  "curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo,
+              "curl_easy_getinfo expects a pointer to "
+              "'struct curl_tlssessioninfo *' for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_curl_certinfo,
+              "curl_easy_getinfo expects a pointer to "
+              "'struct curl_certinfo *' for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_curl_socket,
+  "curl_easy_getinfo expects a pointer to curl_socket_t for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
+  "curl_easy_getinfo expects a pointer to curl_off_t for this info")
+
+/* groups of curl_easy_setops options that take the same type of argument */
+
+/* To add a new option to one of the groups, just add
+ *   (option) == CURLOPT_SOMETHING
+ * to the or-expression. If the option takes a long or curl_off_t, you don't
+ * have to do anything
+ */
+
+/* evaluates to true if option takes a long argument */
+#define _curl_is_long_option(option)                                          \
+  (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT)
+
+#define _curl_is_off_t_option(option)                                         \
+  ((option) > CURLOPTTYPE_OFF_T)
+
+/* evaluates to true if option takes a char* argument */
+#define _curl_is_string_option(option)                                        \
+  ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET ||                                \
+   (option) == CURLOPT_ACCEPT_ENCODING ||                                     \
+   (option) == CURLOPT_CAINFO ||                                              \
+   (option) == CURLOPT_CAPATH ||                                              \
+   (option) == CURLOPT_COOKIE ||                                              \
+   (option) == CURLOPT_COOKIEFILE ||                                          \
+   (option) == CURLOPT_COOKIEJAR ||                                           \
+   (option) == CURLOPT_COOKIELIST ||                                          \
+   (option) == CURLOPT_CRLFILE ||                                             \
+   (option) == CURLOPT_CUSTOMREQUEST ||                                       \
+   (option) == CURLOPT_DEFAULT_PROTOCOL ||                                    \
+   (option) == CURLOPT_DNS_INTERFACE ||                                       \
+   (option) == CURLOPT_DNS_LOCAL_IP4 ||                                       \
+   (option) == CURLOPT_DNS_LOCAL_IP6 ||                                       \
+   (option) == CURLOPT_DNS_SERVERS ||                                         \
+   (option) == CURLOPT_EGDSOCKET ||                                           \
+   (option) == CURLOPT_FTPPORT ||                                             \
+   (option) == CURLOPT_FTP_ACCOUNT ||                                         \
+   (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER ||                             \
+   (option) == CURLOPT_INTERFACE ||                                           \
+   (option) == CURLOPT_ISSUERCERT ||                                          \
+   (option) == CURLOPT_KEYPASSWD ||                                           \
+   (option) == CURLOPT_KRBLEVEL ||                                            \
+   (option) == CURLOPT_LOGIN_OPTIONS ||                                       \
+   (option) == CURLOPT_MAIL_AUTH ||                                           \
+   (option) == CURLOPT_MAIL_FROM ||                                           \
+   (option) == CURLOPT_NETRC_FILE ||                                          \
+   (option) == CURLOPT_NOPROXY ||                                             \
+   (option) == CURLOPT_PASSWORD ||                                            \
+   (option) == CURLOPT_PINNEDPUBLICKEY ||                                     \
+   (option) == CURLOPT_PRE_PROXY ||                                           \
+   (option) == CURLOPT_PROXY ||                                               \
+   (option) == CURLOPT_PROXYPASSWORD ||                                       \
+   (option) == CURLOPT_PROXYUSERNAME ||                                       \
+   (option) == CURLOPT_PROXYUSERPWD ||                                        \
+   (option) == CURLOPT_PROXY_CAINFO ||                                        \
+   (option) == CURLOPT_PROXY_CAPATH ||                                        \
+   (option) == CURLOPT_PROXY_CRLFILE ||                                       \
+   (option) == CURLOPT_PROXY_KEYPASSWD ||                                     \
+   (option) == CURLOPT_PROXY_PINNEDPUBLICKEY ||                               \
+   (option) == CURLOPT_PROXY_SERVICE_NAME ||                                  \
+   (option) == CURLOPT_PROXY_SSLCERT ||                                       \
+   (option) == CURLOPT_PROXY_SSLCERTTYPE ||                                   \
+   (option) == CURLOPT_PROXY_SSLKEY ||                                        \
+   (option) == CURLOPT_PROXY_SSLKEYTYPE ||                                    \
+   (option) == CURLOPT_PROXY_SSL_CIPHER_LIST ||                               \
+   (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD ||                              \
+   (option) == CURLOPT_PROXY_TLSAUTH_USERNAME ||                              \
+   (option) == CURLOPT_PROXY_TLSAUTH_TYPE ||                                  \
+   (option) == CURLOPT_RANDOM_FILE ||                                         \
+   (option) == CURLOPT_RANGE ||                                               \
+   (option) == CURLOPT_REFERER ||                                             \
+   (option) == CURLOPT_RTSP_SESSION_ID ||                                     \
+   (option) == CURLOPT_RTSP_STREAM_URI ||                                     \
+   (option) == CURLOPT_RTSP_TRANSPORT ||                                      \
+   (option) == CURLOPT_SERVICE_NAME ||                                        \
+   (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE ||                               \
+   (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 ||                             \
+   (option) == CURLOPT_SSH_KNOWNHOSTS ||                                      \
+   (option) == CURLOPT_SSH_PRIVATE_KEYFILE ||                                 \
+   (option) == CURLOPT_SSH_PUBLIC_KEYFILE ||                                  \
+   (option) == CURLOPT_SSLCERT ||                                             \
+   (option) == CURLOPT_SSLCERTTYPE ||                                         \
+   (option) == CURLOPT_SSLENGINE ||                                           \
+   (option) == CURLOPT_SSLKEY ||                                              \
+   (option) == CURLOPT_SSLKEYTYPE ||                                          \
+   (option) == CURLOPT_SSL_CIPHER_LIST ||                                     \
+   (option) == CURLOPT_TLSAUTH_PASSWORD ||                                    \
+   (option) == CURLOPT_TLSAUTH_TYPE ||                                        \
+   (option) == CURLOPT_TLSAUTH_USERNAME ||                                    \
+   (option) == CURLOPT_UNIX_SOCKET_PATH ||                                    \
+   (option) == CURLOPT_URL ||                                                 \
+   (option) == CURLOPT_USERAGENT ||                                           \
+   (option) == CURLOPT_USERNAME ||                                            \
+   (option) == CURLOPT_USERPWD ||                                             \
+   (option) == CURLOPT_XOAUTH2_BEARER ||                                      \
+   0)
+
+/* evaluates to true if option takes a curl_write_callback argument */
+#define _curl_is_write_cb_option(option)                                      \
+  ((option) == CURLOPT_HEADERFUNCTION ||                                      \
+   (option) == CURLOPT_WRITEFUNCTION)
+
+/* evaluates to true if option takes a curl_conv_callback argument */
+#define _curl_is_conv_cb_option(option)                                       \
+  ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION ||                            \
+   (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION ||                          \
+   (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION)
+
+/* evaluates to true if option takes a data argument to pass to a callback */
+#define _curl_is_cb_data_option(option)                                       \
+  ((option) == CURLOPT_CHUNK_DATA ||                                          \
+   (option) == CURLOPT_CLOSESOCKETDATA ||                                     \
+   (option) == CURLOPT_DEBUGDATA ||                                           \
+   (option) == CURLOPT_FNMATCH_DATA ||                                        \
+   (option) == CURLOPT_HEADERDATA ||                                          \
+   (option) == CURLOPT_INTERLEAVEDATA ||                                      \
+   (option) == CURLOPT_IOCTLDATA ||                                           \
+   (option) == CURLOPT_OPENSOCKETDATA ||                                      \
+   (option) == CURLOPT_PRIVATE ||                                             \
+   (option) == CURLOPT_PROGRESSDATA ||                                        \
+   (option) == CURLOPT_READDATA ||                                            \
+   (option) == CURLOPT_SEEKDATA ||                                            \
+   (option) == CURLOPT_SOCKOPTDATA ||                                         \
+   (option) == CURLOPT_SSH_KEYDATA ||                                         \
+   (option) == CURLOPT_SSL_CTX_DATA ||                                        \
+   (option) == CURLOPT_WRITEDATA ||                                           \
+   0)
+
+/* evaluates to true if option takes a POST data argument (void* or char*) */
+#define _curl_is_postfields_option(option)                                    \
+  ((option) == CURLOPT_POSTFIELDS ||                                          \
+   (option) == CURLOPT_COPYPOSTFIELDS ||                                      \
+   0)
+
+/* evaluates to true if option takes a struct curl_slist * argument */
+#define _curl_is_slist_option(option)                                         \
+  ((option) == CURLOPT_HTTP200ALIASES ||                                      \
+   (option) == CURLOPT_HTTPHEADER ||                                          \
+   (option) == CURLOPT_MAIL_RCPT ||                                           \
+   (option) == CURLOPT_POSTQUOTE ||                                           \
+   (option) == CURLOPT_PREQUOTE ||                                            \
+   (option) == CURLOPT_PROXYHEADER ||                                         \
+   (option) == CURLOPT_QUOTE ||                                               \
+   (option) == CURLOPT_RESOLVE ||                                             \
+   (option) == CURLOPT_TELNETOPTIONS ||                                       \
+   0)
+
+/* groups of curl_easy_getinfo infos that take the same type of argument */
+
+/* evaluates to true if info expects a pointer to char * argument */
+#define _curl_is_string_info(info)                                            \
+  (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG)
+
+/* evaluates to true if info expects a pointer to long argument */
+#define _curl_is_long_info(info)                                              \
+  (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE)
+
+/* evaluates to true if info expects a pointer to double argument */
+#define _curl_is_double_info(info)                                            \
+  (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST)
+
+/* true if info expects a pointer to struct curl_slist * argument */
+#define _curl_is_slist_info(info)                                       \
+  (((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST))
+
+/* true if info expects a pointer to struct curl_tlssessioninfo * argument */
+#define _curl_is_tlssessioninfo_info(info)                              \
+  (((info) == CURLINFO_TLS_SSL_PTR) || ((info) == CURLINFO_TLS_SESSION))
+
+/* true if info expects a pointer to struct curl_certinfo * argument */
+#define _curl_is_certinfo_info(info) ((info) == CURLINFO_CERTINFO)
+
+/* true if info expects a pointer to struct curl_socket_t argument */
+#define _curl_is_socket_info(info)                                            \
+  (CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T)
+
+/* true if info expects a pointer to curl_off_t argument */
+#define _curl_is_off_t_info(info)                                             \
+  (CURLINFO_OFF_T < (info))
+
+
+/* typecheck helpers -- check whether given expression has requested type*/
+
+/* For pointers, you can use the _curl_is_ptr/_curl_is_arr macros,
+ * otherwise define a new macro. Search for __builtin_types_compatible_p
+ * in the GCC manual.
+ * NOTE: these macros MUST NOT EVALUATE their arguments! The argument is
+ * the actual expression passed to the curl_easy_setopt macro. This
+ * means that you can only apply the sizeof and __typeof__ operators, no
+ * == or whatsoever.
+ */
+
+/* XXX: should evaluate to true iff expr is a pointer */
+#define _curl_is_any_ptr(expr)                                                \
+  (sizeof(expr) == sizeof(void *))
+
+/* evaluates to true if expr is NULL */
+/* XXX: must not evaluate expr, so this check is not accurate */
+#define _curl_is_NULL(expr)                                                   \
+  (__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL)))
+
+/* evaluates to true if expr is type*, const type* or NULL */
+#define _curl_is_ptr(expr, type)                                              \
+  (_curl_is_NULL(expr) ||                                                     \
+   __builtin_types_compatible_p(__typeof__(expr), type *) ||                  \
+   __builtin_types_compatible_p(__typeof__(expr), const type *))
+
+/* evaluates to true if expr is one of type[], type*, NULL or const type* */
+#define _curl_is_arr(expr, type)                                              \
+  (_curl_is_ptr((expr), type) ||                                              \
+   __builtin_types_compatible_p(__typeof__(expr), type []))
+
+/* evaluates to true if expr is a string */
+#define _curl_is_string(expr)                                                 \
+  (_curl_is_arr((expr), char) ||                                              \
+   _curl_is_arr((expr), signed char) ||                                       \
+   _curl_is_arr((expr), unsigned char))
+
+/* evaluates to true if expr is a long (no matter the signedness)
+ * XXX: for now, int is also accepted (and therefore short and char, which
+ * are promoted to int when passed to a variadic function) */
+#define _curl_is_long(expr)                                                   \
+  (__builtin_types_compatible_p(__typeof__(expr), long) ||                    \
+   __builtin_types_compatible_p(__typeof__(expr), signed long) ||             \
+   __builtin_types_compatible_p(__typeof__(expr), unsigned long) ||           \
+   __builtin_types_compatible_p(__typeof__(expr), int) ||                     \
+   __builtin_types_compatible_p(__typeof__(expr), signed int) ||              \
+   __builtin_types_compatible_p(__typeof__(expr), unsigned int) ||            \
+   __builtin_types_compatible_p(__typeof__(expr), short) ||                   \
+   __builtin_types_compatible_p(__typeof__(expr), signed short) ||            \
+   __builtin_types_compatible_p(__typeof__(expr), unsigned short) ||          \
+   __builtin_types_compatible_p(__typeof__(expr), char) ||                    \
+   __builtin_types_compatible_p(__typeof__(expr), signed char) ||             \
+   __builtin_types_compatible_p(__typeof__(expr), unsigned char))
+
+/* evaluates to true if expr is of type curl_off_t */
+#define _curl_is_off_t(expr)                                                  \
+  (__builtin_types_compatible_p(__typeof__(expr), curl_off_t))
+
+/* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */
+/* XXX: also check size of an char[] array? */
+#define _curl_is_error_buffer(expr)                                           \
+  (_curl_is_NULL(expr) ||                                                     \
+   __builtin_types_compatible_p(__typeof__(expr), char *) ||                  \
+   __builtin_types_compatible_p(__typeof__(expr), char[]))
+
+/* evaluates to true if expr is of type (const) void* or (const) FILE* */
+#if 0
+#define _curl_is_cb_data(expr)                                                \
+  (_curl_is_ptr((expr), void) ||                                              \
+   _curl_is_ptr((expr), FILE))
+#else /* be less strict */
+#define _curl_is_cb_data(expr)                                                \
+  _curl_is_any_ptr(expr)
+#endif
+
+/* evaluates to true if expr is of type FILE* */
+#define _curl_is_FILE(expr)                                             \
+  (_curl_is_NULL(expr) ||                                              \
+   (__builtin_types_compatible_p(__typeof__(expr), FILE *)))
+
+/* evaluates to true if expr can be passed as POST data (void* or char*) */
+#define _curl_is_postfields(expr)                                             \
+  (_curl_is_ptr((expr), void) ||                                              \
+   _curl_is_arr((expr), char))
+
+/* FIXME: the whole callback checking is messy...
+ * The idea is to tolerate char vs. void and const vs. not const
+ * pointers in arguments at least
+ */
+/* helper: __builtin_types_compatible_p distinguishes between functions and
+ * function pointers, hide it */
+#define _curl_callback_compatible(func, type)                                 \
+  (__builtin_types_compatible_p(__typeof__(func), type) ||                    \
+   __builtin_types_compatible_p(__typeof__(func) *, type))
+
+/* evaluates to true if expr is of type curl_read_callback or "similar" */
+#define _curl_is_read_cb(expr)                                          \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), __typeof__(fread) *) ||                  \
+   _curl_callback_compatible((expr), curl_read_callback) ||                   \
+   _curl_callback_compatible((expr), _curl_read_callback1) ||                 \
+   _curl_callback_compatible((expr), _curl_read_callback2) ||                 \
+   _curl_callback_compatible((expr), _curl_read_callback3) ||                 \
+   _curl_callback_compatible((expr), _curl_read_callback4) ||                 \
+   _curl_callback_compatible((expr), _curl_read_callback5) ||                 \
+   _curl_callback_compatible((expr), _curl_read_callback6))
+typedef size_t (*_curl_read_callback1)(char *, size_t, size_t, void *);
+typedef size_t (*_curl_read_callback2)(char *, size_t, size_t, const void *);
+typedef size_t (*_curl_read_callback3)(char *, size_t, size_t, FILE *);
+typedef size_t (*_curl_read_callback4)(void *, size_t, size_t, void *);
+typedef size_t (*_curl_read_callback5)(void *, size_t, size_t, const void *);
+typedef size_t (*_curl_read_callback6)(void *, size_t, size_t, FILE *);
+
+/* evaluates to true if expr is of type curl_write_callback or "similar" */
+#define _curl_is_write_cb(expr)                                               \
+  (_curl_is_read_cb(expr) ||                                            \
+   _curl_callback_compatible((expr), __typeof__(fwrite) *) ||                 \
+   _curl_callback_compatible((expr), curl_write_callback) ||                  \
+   _curl_callback_compatible((expr), _curl_write_callback1) ||                \
+   _curl_callback_compatible((expr), _curl_write_callback2) ||                \
+   _curl_callback_compatible((expr), _curl_write_callback3) ||                \
+   _curl_callback_compatible((expr), _curl_write_callback4) ||                \
+   _curl_callback_compatible((expr), _curl_write_callback5) ||                \
+   _curl_callback_compatible((expr), _curl_write_callback6))
+typedef size_t (*_curl_write_callback1)(const char *, size_t, size_t, void *);
+typedef size_t (*_curl_write_callback2)(const char *, size_t, size_t,
+                                       const void *);
+typedef size_t (*_curl_write_callback3)(const char *, size_t, size_t, FILE *);
+typedef size_t (*_curl_write_callback4)(const void *, size_t, size_t, void *);
+typedef size_t (*_curl_write_callback5)(const void *, size_t, size_t,
+                                       const void *);
+typedef size_t (*_curl_write_callback6)(const void *, size_t, size_t, FILE *);
+
+/* evaluates to true if expr is of type curl_ioctl_callback or "similar" */
+#define _curl_is_ioctl_cb(expr)                                         \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_ioctl_callback) ||                  \
+   _curl_callback_compatible((expr), _curl_ioctl_callback1) ||                \
+   _curl_callback_compatible((expr), _curl_ioctl_callback2) ||                \
+   _curl_callback_compatible((expr), _curl_ioctl_callback3) ||                \
+   _curl_callback_compatible((expr), _curl_ioctl_callback4))
+typedef curlioerr (*_curl_ioctl_callback1)(CURL *, int, void *);
+typedef curlioerr (*_curl_ioctl_callback2)(CURL *, int, const void *);
+typedef curlioerr (*_curl_ioctl_callback3)(CURL *, curliocmd, void *);
+typedef curlioerr (*_curl_ioctl_callback4)(CURL *, curliocmd, const void *);
+
+/* evaluates to true if expr is of type curl_sockopt_callback or "similar" */
+#define _curl_is_sockopt_cb(expr)                                       \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_sockopt_callback) ||                \
+   _curl_callback_compatible((expr), _curl_sockopt_callback1) ||              \
+   _curl_callback_compatible((expr), _curl_sockopt_callback2))
+typedef int (*_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype);
+typedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t,
+                                      curlsocktype);
+
+/* evaluates to true if expr is of type curl_opensocket_callback or
+   "similar" */
+#define _curl_is_opensocket_cb(expr)                                    \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_opensocket_callback) ||             \
+   _curl_callback_compatible((expr), _curl_opensocket_callback1) ||           \
+   _curl_callback_compatible((expr), _curl_opensocket_callback2) ||           \
+   _curl_callback_compatible((expr), _curl_opensocket_callback3) ||           \
+   _curl_callback_compatible((expr), _curl_opensocket_callback4))
+typedef curl_socket_t (*_curl_opensocket_callback1)
+  (void *, curlsocktype, struct curl_sockaddr *);
+typedef curl_socket_t (*_curl_opensocket_callback2)
+  (void *, curlsocktype, const struct curl_sockaddr *);
+typedef curl_socket_t (*_curl_opensocket_callback3)
+  (const void *, curlsocktype, struct curl_sockaddr *);
+typedef curl_socket_t (*_curl_opensocket_callback4)
+  (const void *, curlsocktype, const struct curl_sockaddr *);
+
+/* evaluates to true if expr is of type curl_progress_callback or "similar" */
+#define _curl_is_progress_cb(expr)                                      \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_progress_callback) ||               \
+   _curl_callback_compatible((expr), _curl_progress_callback1) ||             \
+   _curl_callback_compatible((expr), _curl_progress_callback2))
+typedef int (*_curl_progress_callback1)(void *,
+    double, double, double, double);
+typedef int (*_curl_progress_callback2)(const void *,
+    double, double, double, double);
+
+/* evaluates to true if expr is of type curl_debug_callback or "similar" */
+#define _curl_is_debug_cb(expr)                                         \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_debug_callback) ||                  \
+   _curl_callback_compatible((expr), _curl_debug_callback1) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback2) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback3) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback4) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback5) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback6) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback7) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback8))
+typedef int (*_curl_debug_callback1) (CURL *,
+    curl_infotype, char *, size_t, void *);
+typedef int (*_curl_debug_callback2) (CURL *,
+    curl_infotype, char *, size_t, const void *);
+typedef int (*_curl_debug_callback3) (CURL *,
+    curl_infotype, const char *, size_t, void *);
+typedef int (*_curl_debug_callback4) (CURL *,
+    curl_infotype, const char *, size_t, const void *);
+typedef int (*_curl_debug_callback5) (CURL *,
+    curl_infotype, unsigned char *, size_t, void *);
+typedef int (*_curl_debug_callback6) (CURL *,
+    curl_infotype, unsigned char *, size_t, const void *);
+typedef int (*_curl_debug_callback7) (CURL *,
+    curl_infotype, const unsigned char *, size_t, void *);
+typedef int (*_curl_debug_callback8) (CURL *,
+    curl_infotype, const unsigned char *, size_t, const void *);
+
+/* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */
+/* this is getting even messier... */
+#define _curl_is_ssl_ctx_cb(expr)                                       \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_ssl_ctx_callback) ||                \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback1) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback2) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback3) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback4) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback5) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback6) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback7) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback8))
+typedef CURLcode (*_curl_ssl_ctx_callback1)(CURL *, void *, void *);
+typedef CURLcode (*_curl_ssl_ctx_callback2)(CURL *, void *, const void *);
+typedef CURLcode (*_curl_ssl_ctx_callback3)(CURL *, const void *, void *);
+typedef CURLcode (*_curl_ssl_ctx_callback4)(CURL *, const void *,
+                                            const void *);
+#ifdef HEADER_SSL_H
+/* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX
+ * this will of course break if we're included before OpenSSL headers...
+ */
+typedef CURLcode (*_curl_ssl_ctx_callback5)(CURL *, SSL_CTX, void *);
+typedef CURLcode (*_curl_ssl_ctx_callback6)(CURL *, SSL_CTX, const void *);
+typedef CURLcode (*_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX, void *);
+typedef CURLcode (*_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX,
+                                           const void *);
+#else
+typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5;
+typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6;
+typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7;
+typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8;
+#endif
+
+/* evaluates to true if expr is of type curl_conv_callback or "similar" */
+#define _curl_is_conv_cb(expr)                                          \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_conv_callback) ||                   \
+   _curl_callback_compatible((expr), _curl_conv_callback1) ||                 \
+   _curl_callback_compatible((expr), _curl_conv_callback2) ||                 \
+   _curl_callback_compatible((expr), _curl_conv_callback3) ||                 \
+   _curl_callback_compatible((expr), _curl_conv_callback4))
+typedef CURLcode (*_curl_conv_callback1)(char *, size_t length);
+typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length);
+typedef CURLcode (*_curl_conv_callback3)(void *, size_t length);
+typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length);
+
+/* evaluates to true if expr is of type curl_seek_callback or "similar" */
+#define _curl_is_seek_cb(expr)                                          \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_seek_callback) ||                   \
+   _curl_callback_compatible((expr), _curl_seek_callback1) ||                 \
+   _curl_callback_compatible((expr), _curl_seek_callback2))
+typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int);
+typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int);
+
+
+#endif /* __CURL_TYPECHECK_GCC_H */

二进制
ext/curl-7.58.0/Win32/lib/libcurl_a.lib


二进制
ext/curl-7.58.0/Win32/lib/libcurl_a_debug.lib


+ 2751 - 0
ext/curl-7.58.0/x64/include/curl/curl.h

@@ -0,0 +1,2751 @@
+#ifndef __CURL_CURL_H
+#define __CURL_CURL_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * If you have libcurl problems, all docs and details are found here:
+ *   https://curl.haxx.se/libcurl/
+ *
+ * curl-library mailing list subscription and unsubscription web interface:
+ *   https://cool.haxx.se/mailman/listinfo/curl-library/
+ */
+
+#ifdef CURL_NO_OLDIES
+#define CURL_STRICTER
+#endif
+
+#include "curlver.h"         /* libcurl version defines   */
+#include "system.h"          /* determine things run-time */
+
+/*
+ * Define WIN32 when build target is Win32 API
+ */
+
+#if (defined(_WIN32) || defined(__WIN32__)) && \
+     !defined(WIN32) && !defined(__SYMBIAN32__)
+#define WIN32
+#endif
+
+#include <stdio.h>
+#include <limits.h>
+
+#if defined(__FreeBSD__) && (__FreeBSD__ >= 2)
+/* Needed for __FreeBSD_version symbol definition */
+#include <osreldate.h>
+#endif
+
+/* The include stuff here below is mainly for time_t! */
+#include <sys/types.h>
+#include <time.h>
+
+#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__)
+#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \
+      defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H))
+/* The check above prevents the winsock2 inclusion if winsock.h already was
+   included, since they can't co-exist without problems */
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#endif
+#endif
+
+/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish
+   libc5-based Linux systems. Only include it on systems that are known to
+   require it! */
+#if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \
+    defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \
+    defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \
+    defined(__CYGWIN__) || \
+   (defined(__FreeBSD_version) && (__FreeBSD_version < 800000))
+#include <sys/select.h>
+#endif
+
+#if !defined(WIN32) && !defined(_WIN32_WCE)
+#include <sys/socket.h>
+#endif
+
+#if !defined(WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__)
+#include <sys/time.h>
+#endif
+
+#ifdef __BEOS__
+#include <support/SupportDefs.h>
+#endif
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER)
+typedef struct Curl_easy CURL;
+typedef struct Curl_share CURLSH;
+#else
+typedef void CURL;
+typedef void CURLSH;
+#endif
+
+/*
+ * libcurl external API function linkage decorations.
+ */
+
+#ifdef CURL_STATICLIB
+#  define CURL_EXTERN
+#elif defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__)
+#  if defined(BUILDING_LIBCURL)
+#    define CURL_EXTERN  __declspec(dllexport)
+#  else
+#    define CURL_EXTERN  __declspec(dllimport)
+#  endif
+#elif defined(BUILDING_LIBCURL) && defined(CURL_HIDDEN_SYMBOLS)
+#  define CURL_EXTERN CURL_EXTERN_SYMBOL
+#else
+#  define CURL_EXTERN
+#endif
+
+#ifndef curl_socket_typedef
+/* socket typedef */
+#if defined(WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
+typedef SOCKET curl_socket_t;
+#define CURL_SOCKET_BAD INVALID_SOCKET
+#else
+typedef int curl_socket_t;
+#define CURL_SOCKET_BAD -1
+#endif
+#define curl_socket_typedef
+#endif /* curl_socket_typedef */
+
+/* enum for the different supported SSL backends */
+typedef enum {
+  CURLSSLBACKEND_NONE = 0,
+  CURLSSLBACKEND_OPENSSL = 1,
+  CURLSSLBACKEND_GNUTLS = 2,
+  CURLSSLBACKEND_NSS = 3,
+  CURLSSLBACKEND_OBSOLETE4 = 4,  /* Was QSOSSL. */
+  CURLSSLBACKEND_GSKIT = 5,
+  CURLSSLBACKEND_POLARSSL = 6,
+  CURLSSLBACKEND_WOLFSSL = 7,
+  CURLSSLBACKEND_SCHANNEL = 8,
+  CURLSSLBACKEND_DARWINSSL = 9,
+  CURLSSLBACKEND_AXTLS = 10,
+  CURLSSLBACKEND_MBEDTLS = 11
+} curl_sslbackend;
+
+/* aliases for library clones and renames */
+#define CURLSSLBACKEND_LIBRESSL CURLSSLBACKEND_OPENSSL
+#define CURLSSLBACKEND_BORINGSSL CURLSSLBACKEND_OPENSSL
+#define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL
+
+struct curl_httppost {
+  struct curl_httppost *next;       /* next entry in the list */
+  char *name;                       /* pointer to allocated name */
+  long namelength;                  /* length of name length */
+  char *contents;                   /* pointer to allocated data contents */
+  long contentslength;              /* length of contents field, see also
+                                       CURL_HTTPPOST_LARGE */
+  char *buffer;                     /* pointer to allocated buffer contents */
+  long bufferlength;                /* length of buffer field */
+  char *contenttype;                /* Content-Type */
+  struct curl_slist *contentheader; /* list of extra headers for this form */
+  struct curl_httppost *more;       /* if one field name has more than one
+                                       file, this link should link to following
+                                       files */
+  long flags;                       /* as defined below */
+
+/* specified content is a file name */
+#define CURL_HTTPPOST_FILENAME (1<<0)
+/* specified content is a file name */
+#define CURL_HTTPPOST_READFILE (1<<1)
+/* name is only stored pointer do not free in formfree */
+#define CURL_HTTPPOST_PTRNAME (1<<2)
+/* contents is only stored pointer do not free in formfree */
+#define CURL_HTTPPOST_PTRCONTENTS (1<<3)
+/* upload file from buffer */
+#define CURL_HTTPPOST_BUFFER (1<<4)
+/* upload file from pointer contents */
+#define CURL_HTTPPOST_PTRBUFFER (1<<5)
+/* upload file contents by using the regular read callback to get the data and
+   pass the given pointer as custom pointer */
+#define CURL_HTTPPOST_CALLBACK (1<<6)
+/* use size in 'contentlen', added in 7.46.0 */
+#define CURL_HTTPPOST_LARGE (1<<7)
+
+  char *showfilename;               /* The file name to show. If not set, the
+                                       actual file name will be used (if this
+                                       is a file part) */
+  void *userp;                      /* custom pointer used for
+                                       HTTPPOST_CALLBACK posts */
+  curl_off_t contentlen;            /* alternative length of contents
+                                       field. Used if CURL_HTTPPOST_LARGE is
+                                       set. Added in 7.46.0 */
+};
+
+/* This is the CURLOPT_PROGRESSFUNCTION callback proto. It is now considered
+   deprecated but was the only choice up until 7.31.0 */
+typedef int (*curl_progress_callback)(void *clientp,
+                                      double dltotal,
+                                      double dlnow,
+                                      double ultotal,
+                                      double ulnow);
+
+/* This is the CURLOPT_XFERINFOFUNCTION callback proto. It was introduced in
+   7.32.0, it avoids floating point and provides more detailed information. */
+typedef int (*curl_xferinfo_callback)(void *clientp,
+                                      curl_off_t dltotal,
+                                      curl_off_t dlnow,
+                                      curl_off_t ultotal,
+                                      curl_off_t ulnow);
+
+#ifndef CURL_MAX_READ_SIZE
+  /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */
+#define CURL_MAX_READ_SIZE 524288
+#endif
+
+#ifndef CURL_MAX_WRITE_SIZE
+  /* Tests have proven that 20K is a very bad buffer size for uploads on
+     Windows, while 16K for some odd reason performed a lot better.
+     We do the ifndef check to allow this value to easier be changed at build
+     time for those who feel adventurous. The practical minimum is about
+     400 bytes since libcurl uses a buffer of this size as a scratch area
+     (unrelated to network send operations). */
+#define CURL_MAX_WRITE_SIZE 16384
+#endif
+
+#ifndef CURL_MAX_HTTP_HEADER
+/* The only reason to have a max limit for this is to avoid the risk of a bad
+   server feeding libcurl with a never-ending header that will cause reallocs
+   infinitely */
+#define CURL_MAX_HTTP_HEADER (100*1024)
+#endif
+
+/* This is a magic return code for the write callback that, when returned,
+   will signal libcurl to pause receiving on the current transfer. */
+#define CURL_WRITEFUNC_PAUSE 0x10000001
+
+typedef size_t (*curl_write_callback)(char *buffer,
+                                      size_t size,
+                                      size_t nitems,
+                                      void *outstream);
+
+
+
+/* enumeration of file types */
+typedef enum {
+  CURLFILETYPE_FILE = 0,
+  CURLFILETYPE_DIRECTORY,
+  CURLFILETYPE_SYMLINK,
+  CURLFILETYPE_DEVICE_BLOCK,
+  CURLFILETYPE_DEVICE_CHAR,
+  CURLFILETYPE_NAMEDPIPE,
+  CURLFILETYPE_SOCKET,
+  CURLFILETYPE_DOOR, /* is possible only on Sun Solaris now */
+
+  CURLFILETYPE_UNKNOWN /* should never occur */
+} curlfiletype;
+
+#define CURLFINFOFLAG_KNOWN_FILENAME    (1<<0)
+#define CURLFINFOFLAG_KNOWN_FILETYPE    (1<<1)
+#define CURLFINFOFLAG_KNOWN_TIME        (1<<2)
+#define CURLFINFOFLAG_KNOWN_PERM        (1<<3)
+#define CURLFINFOFLAG_KNOWN_UID         (1<<4)
+#define CURLFINFOFLAG_KNOWN_GID         (1<<5)
+#define CURLFINFOFLAG_KNOWN_SIZE        (1<<6)
+#define CURLFINFOFLAG_KNOWN_HLINKCOUNT  (1<<7)
+
+/* Content of this structure depends on information which is known and is
+   achievable (e.g. by FTP LIST parsing). Please see the url_easy_setopt(3) man
+   page for callbacks returning this structure -- some fields are mandatory,
+   some others are optional. The FLAG field has special meaning. */
+struct curl_fileinfo {
+  char *filename;
+  curlfiletype filetype;
+  time_t time;
+  unsigned int perm;
+  int uid;
+  int gid;
+  curl_off_t size;
+  long int hardlinks;
+
+  struct {
+    /* If some of these fields is not NULL, it is a pointer to b_data. */
+    char *time;
+    char *perm;
+    char *user;
+    char *group;
+    char *target; /* pointer to the target filename of a symlink */
+  } strings;
+
+  unsigned int flags;
+
+  /* used internally */
+  char *b_data;
+  size_t b_size;
+  size_t b_used;
+};
+
+/* return codes for CURLOPT_CHUNK_BGN_FUNCTION */
+#define CURL_CHUNK_BGN_FUNC_OK      0
+#define CURL_CHUNK_BGN_FUNC_FAIL    1 /* tell the lib to end the task */
+#define CURL_CHUNK_BGN_FUNC_SKIP    2 /* skip this chunk over */
+
+/* if splitting of data transfer is enabled, this callback is called before
+   download of an individual chunk started. Note that parameter "remains" works
+   only for FTP wildcard downloading (for now), otherwise is not used */
+typedef long (*curl_chunk_bgn_callback)(const void *transfer_info,
+                                        void *ptr,
+                                        int remains);
+
+/* return codes for CURLOPT_CHUNK_END_FUNCTION */
+#define CURL_CHUNK_END_FUNC_OK      0
+#define CURL_CHUNK_END_FUNC_FAIL    1 /* tell the lib to end the task */
+
+/* If splitting of data transfer is enabled this callback is called after
+   download of an individual chunk finished.
+   Note! After this callback was set then it have to be called FOR ALL chunks.
+   Even if downloading of this chunk was skipped in CHUNK_BGN_FUNC.
+   This is the reason why we don't need "transfer_info" parameter in this
+   callback and we are not interested in "remains" parameter too. */
+typedef long (*curl_chunk_end_callback)(void *ptr);
+
+/* return codes for FNMATCHFUNCTION */
+#define CURL_FNMATCHFUNC_MATCH    0 /* string corresponds to the pattern */
+#define CURL_FNMATCHFUNC_NOMATCH  1 /* pattern doesn't match the string */
+#define CURL_FNMATCHFUNC_FAIL     2 /* an error occurred */
+
+/* callback type for wildcard downloading pattern matching. If the
+   string matches the pattern, return CURL_FNMATCHFUNC_MATCH value, etc. */
+typedef int (*curl_fnmatch_callback)(void *ptr,
+                                     const char *pattern,
+                                     const char *string);
+
+/* These are the return codes for the seek callbacks */
+#define CURL_SEEKFUNC_OK       0
+#define CURL_SEEKFUNC_FAIL     1 /* fail the entire transfer */
+#define CURL_SEEKFUNC_CANTSEEK 2 /* tell libcurl seeking can't be done, so
+                                    libcurl might try other means instead */
+typedef int (*curl_seek_callback)(void *instream,
+                                  curl_off_t offset,
+                                  int origin); /* 'whence' */
+
+/* This is a return code for the read callback that, when returned, will
+   signal libcurl to immediately abort the current transfer. */
+#define CURL_READFUNC_ABORT 0x10000000
+/* This is a return code for the read callback that, when returned, will
+   signal libcurl to pause sending data on the current transfer. */
+#define CURL_READFUNC_PAUSE 0x10000001
+
+typedef size_t (*curl_read_callback)(char *buffer,
+                                      size_t size,
+                                      size_t nitems,
+                                      void *instream);
+
+typedef enum {
+  CURLSOCKTYPE_IPCXN,  /* socket created for a specific IP connection */
+  CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */
+  CURLSOCKTYPE_LAST    /* never use */
+} curlsocktype;
+
+/* The return code from the sockopt_callback can signal information back
+   to libcurl: */
+#define CURL_SOCKOPT_OK 0
+#define CURL_SOCKOPT_ERROR 1 /* causes libcurl to abort and return
+                                CURLE_ABORTED_BY_CALLBACK */
+#define CURL_SOCKOPT_ALREADY_CONNECTED 2
+
+typedef int (*curl_sockopt_callback)(void *clientp,
+                                     curl_socket_t curlfd,
+                                     curlsocktype purpose);
+
+struct curl_sockaddr {
+  int family;
+  int socktype;
+  int protocol;
+  unsigned int addrlen; /* addrlen was a socklen_t type before 7.18.0 but it
+                           turned really ugly and painful on the systems that
+                           lack this type */
+  struct sockaddr addr;
+};
+
+typedef curl_socket_t
+(*curl_opensocket_callback)(void *clientp,
+                            curlsocktype purpose,
+                            struct curl_sockaddr *address);
+
+typedef int
+(*curl_closesocket_callback)(void *clientp, curl_socket_t item);
+
+typedef enum {
+  CURLIOE_OK,            /* I/O operation successful */
+  CURLIOE_UNKNOWNCMD,    /* command was unknown to callback */
+  CURLIOE_FAILRESTART,   /* failed to restart the read */
+  CURLIOE_LAST           /* never use */
+} curlioerr;
+
+typedef enum {
+  CURLIOCMD_NOP,         /* no operation */
+  CURLIOCMD_RESTARTREAD, /* restart the read stream from start */
+  CURLIOCMD_LAST         /* never use */
+} curliocmd;
+
+typedef curlioerr (*curl_ioctl_callback)(CURL *handle,
+                                         int cmd,
+                                         void *clientp);
+
+#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS
+/*
+ * The following typedef's are signatures of malloc, free, realloc, strdup and
+ * calloc respectively.  Function pointers of these types can be passed to the
+ * curl_global_init_mem() function to set user defined memory management
+ * callback routines.
+ */
+typedef void *(*curl_malloc_callback)(size_t size);
+typedef void (*curl_free_callback)(void *ptr);
+typedef void *(*curl_realloc_callback)(void *ptr, size_t size);
+typedef char *(*curl_strdup_callback)(const char *str);
+typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size);
+
+#define CURL_DID_MEMORY_FUNC_TYPEDEFS
+#endif
+
+/* the kind of data that is passed to information_callback*/
+typedef enum {
+  CURLINFO_TEXT = 0,
+  CURLINFO_HEADER_IN,    /* 1 */
+  CURLINFO_HEADER_OUT,   /* 2 */
+  CURLINFO_DATA_IN,      /* 3 */
+  CURLINFO_DATA_OUT,     /* 4 */
+  CURLINFO_SSL_DATA_IN,  /* 5 */
+  CURLINFO_SSL_DATA_OUT, /* 6 */
+  CURLINFO_END
+} curl_infotype;
+
+typedef int (*curl_debug_callback)
+       (CURL *handle,      /* the handle/transfer this concerns */
+        curl_infotype type, /* what kind of data */
+        char *data,        /* points to the data */
+        size_t size,       /* size of the data pointed to */
+        void *userptr);    /* whatever the user please */
+
+/* All possible error codes from all sorts of curl functions. Future versions
+   may return other values, stay prepared.
+
+   Always add new return codes last. Never *EVER* remove any. The return
+   codes must remain the same!
+ */
+
+typedef enum {
+  CURLE_OK = 0,
+  CURLE_UNSUPPORTED_PROTOCOL,    /* 1 */
+  CURLE_FAILED_INIT,             /* 2 */
+  CURLE_URL_MALFORMAT,           /* 3 */
+  CURLE_NOT_BUILT_IN,            /* 4 - [was obsoleted in August 2007 for
+                                    7.17.0, reused in April 2011 for 7.21.5] */
+  CURLE_COULDNT_RESOLVE_PROXY,   /* 5 */
+  CURLE_COULDNT_RESOLVE_HOST,    /* 6 */
+  CURLE_COULDNT_CONNECT,         /* 7 */
+  CURLE_WEIRD_SERVER_REPLY,      /* 8 */
+  CURLE_REMOTE_ACCESS_DENIED,    /* 9 a service was denied by the server
+                                    due to lack of access - when login fails
+                                    this is not returned. */
+  CURLE_FTP_ACCEPT_FAILED,       /* 10 - [was obsoleted in April 2006 for
+                                    7.15.4, reused in Dec 2011 for 7.24.0]*/
+  CURLE_FTP_WEIRD_PASS_REPLY,    /* 11 */
+  CURLE_FTP_ACCEPT_TIMEOUT,      /* 12 - timeout occurred accepting server
+                                    [was obsoleted in August 2007 for 7.17.0,
+                                    reused in Dec 2011 for 7.24.0]*/
+  CURLE_FTP_WEIRD_PASV_REPLY,    /* 13 */
+  CURLE_FTP_WEIRD_227_FORMAT,    /* 14 */
+  CURLE_FTP_CANT_GET_HOST,       /* 15 */
+  CURLE_HTTP2,                   /* 16 - A problem in the http2 framing layer.
+                                    [was obsoleted in August 2007 for 7.17.0,
+                                    reused in July 2014 for 7.38.0] */
+  CURLE_FTP_COULDNT_SET_TYPE,    /* 17 */
+  CURLE_PARTIAL_FILE,            /* 18 */
+  CURLE_FTP_COULDNT_RETR_FILE,   /* 19 */
+  CURLE_OBSOLETE20,              /* 20 - NOT USED */
+  CURLE_QUOTE_ERROR,             /* 21 - quote command failure */
+  CURLE_HTTP_RETURNED_ERROR,     /* 22 */
+  CURLE_WRITE_ERROR,             /* 23 */
+  CURLE_OBSOLETE24,              /* 24 - NOT USED */
+  CURLE_UPLOAD_FAILED,           /* 25 - failed upload "command" */
+  CURLE_READ_ERROR,              /* 26 - couldn't open/read from file */
+  CURLE_OUT_OF_MEMORY,           /* 27 */
+  /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error
+           instead of a memory allocation error if CURL_DOES_CONVERSIONS
+           is defined
+  */
+  CURLE_OPERATION_TIMEDOUT,      /* 28 - the timeout time was reached */
+  CURLE_OBSOLETE29,              /* 29 - NOT USED */
+  CURLE_FTP_PORT_FAILED,         /* 30 - FTP PORT operation failed */
+  CURLE_FTP_COULDNT_USE_REST,    /* 31 - the REST command failed */
+  CURLE_OBSOLETE32,              /* 32 - NOT USED */
+  CURLE_RANGE_ERROR,             /* 33 - RANGE "command" didn't work */
+  CURLE_HTTP_POST_ERROR,         /* 34 */
+  CURLE_SSL_CONNECT_ERROR,       /* 35 - wrong when connecting with SSL */
+  CURLE_BAD_DOWNLOAD_RESUME,     /* 36 - couldn't resume download */
+  CURLE_FILE_COULDNT_READ_FILE,  /* 37 */
+  CURLE_LDAP_CANNOT_BIND,        /* 38 */
+  CURLE_LDAP_SEARCH_FAILED,      /* 39 */
+  CURLE_OBSOLETE40,              /* 40 - NOT USED */
+  CURLE_FUNCTION_NOT_FOUND,      /* 41 - NOT USED starting with 7.53.0 */
+  CURLE_ABORTED_BY_CALLBACK,     /* 42 */
+  CURLE_BAD_FUNCTION_ARGUMENT,   /* 43 */
+  CURLE_OBSOLETE44,              /* 44 - NOT USED */
+  CURLE_INTERFACE_FAILED,        /* 45 - CURLOPT_INTERFACE failed */
+  CURLE_OBSOLETE46,              /* 46 - NOT USED */
+  CURLE_TOO_MANY_REDIRECTS,      /* 47 - catch endless re-direct loops */
+  CURLE_UNKNOWN_OPTION,          /* 48 - User specified an unknown option */
+  CURLE_TELNET_OPTION_SYNTAX,    /* 49 - Malformed telnet option */
+  CURLE_OBSOLETE50,              /* 50 - NOT USED */
+  CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint
+                                     wasn't verified fine */
+  CURLE_GOT_NOTHING,             /* 52 - when this is a specific error */
+  CURLE_SSL_ENGINE_NOTFOUND,     /* 53 - SSL crypto engine not found */
+  CURLE_SSL_ENGINE_SETFAILED,    /* 54 - can not set SSL crypto engine as
+                                    default */
+  CURLE_SEND_ERROR,              /* 55 - failed sending network data */
+  CURLE_RECV_ERROR,              /* 56 - failure in receiving network data */
+  CURLE_OBSOLETE57,              /* 57 - NOT IN USE */
+  CURLE_SSL_CERTPROBLEM,         /* 58 - problem with the local certificate */
+  CURLE_SSL_CIPHER,              /* 59 - couldn't use specified cipher */
+  CURLE_SSL_CACERT,              /* 60 - problem with the CA cert (path?) */
+  CURLE_BAD_CONTENT_ENCODING,    /* 61 - Unrecognized/bad encoding */
+  CURLE_LDAP_INVALID_URL,        /* 62 - Invalid LDAP URL */
+  CURLE_FILESIZE_EXCEEDED,       /* 63 - Maximum file size exceeded */
+  CURLE_USE_SSL_FAILED,          /* 64 - Requested FTP SSL level failed */
+  CURLE_SEND_FAIL_REWIND,        /* 65 - Sending the data requires a rewind
+                                    that failed */
+  CURLE_SSL_ENGINE_INITFAILED,   /* 66 - failed to initialise ENGINE */
+  CURLE_LOGIN_DENIED,            /* 67 - user, password or similar was not
+                                    accepted and we failed to login */
+  CURLE_TFTP_NOTFOUND,           /* 68 - file not found on server */
+  CURLE_TFTP_PERM,               /* 69 - permission problem on server */
+  CURLE_REMOTE_DISK_FULL,        /* 70 - out of disk space on server */
+  CURLE_TFTP_ILLEGAL,            /* 71 - Illegal TFTP operation */
+  CURLE_TFTP_UNKNOWNID,          /* 72 - Unknown transfer ID */
+  CURLE_REMOTE_FILE_EXISTS,      /* 73 - File already exists */
+  CURLE_TFTP_NOSUCHUSER,         /* 74 - No such user */
+  CURLE_CONV_FAILED,             /* 75 - conversion failed */
+  CURLE_CONV_REQD,               /* 76 - caller must register conversion
+                                    callbacks using curl_easy_setopt options
+                                    CURLOPT_CONV_FROM_NETWORK_FUNCTION,
+                                    CURLOPT_CONV_TO_NETWORK_FUNCTION, and
+                                    CURLOPT_CONV_FROM_UTF8_FUNCTION */
+  CURLE_SSL_CACERT_BADFILE,      /* 77 - could not load CACERT file, missing
+                                    or wrong format */
+  CURLE_REMOTE_FILE_NOT_FOUND,   /* 78 - remote file not found */
+  CURLE_SSH,                     /* 79 - error from the SSH layer, somewhat
+                                    generic so the error message will be of
+                                    interest when this has happened */
+
+  CURLE_SSL_SHUTDOWN_FAILED,     /* 80 - Failed to shut down the SSL
+                                    connection */
+  CURLE_AGAIN,                   /* 81 - socket is not ready for send/recv,
+                                    wait till it's ready and try again (Added
+                                    in 7.18.2) */
+  CURLE_SSL_CRL_BADFILE,         /* 82 - could not load CRL file, missing or
+                                    wrong format (Added in 7.19.0) */
+  CURLE_SSL_ISSUER_ERROR,        /* 83 - Issuer check failed.  (Added in
+                                    7.19.0) */
+  CURLE_FTP_PRET_FAILED,         /* 84 - a PRET command failed */
+  CURLE_RTSP_CSEQ_ERROR,         /* 85 - mismatch of RTSP CSeq numbers */
+  CURLE_RTSP_SESSION_ERROR,      /* 86 - mismatch of RTSP Session Ids */
+  CURLE_FTP_BAD_FILE_LIST,       /* 87 - unable to parse FTP file list */
+  CURLE_CHUNK_FAILED,            /* 88 - chunk callback reported error */
+  CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the
+                                    session will be queued */
+  CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not
+                                     match */
+  CURLE_SSL_INVALIDCERTSTATUS,   /* 91 - invalid certificate status */
+  CURLE_HTTP2_STREAM,            /* 92 - stream error in HTTP/2 framing layer
+                                    */
+  CURL_LAST /* never use! */
+} CURLcode;
+
+#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
+                          the obsolete stuff removed! */
+
+/* Previously obsolete error code re-used in 7.38.0 */
+#define CURLE_OBSOLETE16 CURLE_HTTP2
+
+/* Previously obsolete error codes re-used in 7.24.0 */
+#define CURLE_OBSOLETE10 CURLE_FTP_ACCEPT_FAILED
+#define CURLE_OBSOLETE12 CURLE_FTP_ACCEPT_TIMEOUT
+
+/*  compatibility with older names */
+#define CURLOPT_ENCODING CURLOPT_ACCEPT_ENCODING
+#define CURLE_FTP_WEIRD_SERVER_REPLY CURLE_WEIRD_SERVER_REPLY
+
+/* The following were added in 7.21.5, April 2011 */
+#define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION
+
+/* The following were added in 7.17.1 */
+/* These are scheduled to disappear by 2009 */
+#define CURLE_SSL_PEER_CERTIFICATE CURLE_PEER_FAILED_VERIFICATION
+
+/* The following were added in 7.17.0 */
+/* These are scheduled to disappear by 2009 */
+#define CURLE_OBSOLETE CURLE_OBSOLETE50 /* no one should be using this! */
+#define CURLE_BAD_PASSWORD_ENTERED CURLE_OBSOLETE46
+#define CURLE_BAD_CALLING_ORDER CURLE_OBSOLETE44
+#define CURLE_FTP_USER_PASSWORD_INCORRECT CURLE_OBSOLETE10
+#define CURLE_FTP_CANT_RECONNECT CURLE_OBSOLETE16
+#define CURLE_FTP_COULDNT_GET_SIZE CURLE_OBSOLETE32
+#define CURLE_FTP_COULDNT_SET_ASCII CURLE_OBSOLETE29
+#define CURLE_FTP_WEIRD_USER_REPLY CURLE_OBSOLETE12
+#define CURLE_FTP_WRITE_ERROR CURLE_OBSOLETE20
+#define CURLE_LIBRARY_NOT_FOUND CURLE_OBSOLETE40
+#define CURLE_MALFORMAT_USER CURLE_OBSOLETE24
+#define CURLE_SHARE_IN_USE CURLE_OBSOLETE57
+#define CURLE_URL_MALFORMAT_USER CURLE_NOT_BUILT_IN
+
+#define CURLE_FTP_ACCESS_DENIED CURLE_REMOTE_ACCESS_DENIED
+#define CURLE_FTP_COULDNT_SET_BINARY CURLE_FTP_COULDNT_SET_TYPE
+#define CURLE_FTP_QUOTE_ERROR CURLE_QUOTE_ERROR
+#define CURLE_TFTP_DISKFULL CURLE_REMOTE_DISK_FULL
+#define CURLE_TFTP_EXISTS CURLE_REMOTE_FILE_EXISTS
+#define CURLE_HTTP_RANGE_ERROR CURLE_RANGE_ERROR
+#define CURLE_FTP_SSL_FAILED CURLE_USE_SSL_FAILED
+
+/* The following were added earlier */
+
+#define CURLE_OPERATION_TIMEOUTED CURLE_OPERATION_TIMEDOUT
+
+#define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR
+#define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED
+#define CURLE_FTP_COULDNT_STOR_FILE CURLE_UPLOAD_FAILED
+
+#define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE
+#define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME
+
+/* This was the error code 50 in 7.7.3 and a few earlier versions, this
+   is no longer used by libcurl but is instead #defined here only to not
+   make programs break */
+#define CURLE_ALREADY_COMPLETE 99999
+
+/* Provide defines for really old option names */
+#define CURLOPT_FILE CURLOPT_WRITEDATA /* name changed in 7.9.7 */
+#define CURLOPT_INFILE CURLOPT_READDATA /* name changed in 7.9.7 */
+#define CURLOPT_WRITEHEADER CURLOPT_HEADERDATA
+
+/* Since long deprecated options with no code in the lib that does anything
+   with them. */
+#define CURLOPT_WRITEINFO CURLOPT_OBSOLETE40
+#define CURLOPT_CLOSEPOLICY CURLOPT_OBSOLETE72
+
+#endif /*!CURL_NO_OLDIES*/
+
+/* This prototype applies to all conversion callbacks */
+typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length);
+
+typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl,    /* easy handle */
+                                          void *ssl_ctx, /* actually an
+                                                            OpenSSL SSL_CTX */
+                                          void *userptr);
+
+typedef enum {
+  CURLPROXY_HTTP = 0,   /* added in 7.10, new in 7.19.4 default is to use
+                           CONNECT HTTP/1.1 */
+  CURLPROXY_HTTP_1_0 = 1,   /* added in 7.19.4, force to use CONNECT
+                               HTTP/1.0  */
+  CURLPROXY_HTTPS = 2, /* added in 7.52.0 */
+  CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already
+                           in 7.10 */
+  CURLPROXY_SOCKS5 = 5, /* added in 7.10 */
+  CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */
+  CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the
+                                   host name rather than the IP address. added
+                                   in 7.18.0 */
+} curl_proxytype;  /* this enum was added in 7.10 */
+
+/*
+ * Bitmasks for CURLOPT_HTTPAUTH and CURLOPT_PROXYAUTH options:
+ *
+ * CURLAUTH_NONE         - No HTTP authentication
+ * CURLAUTH_BASIC        - HTTP Basic authentication (default)
+ * CURLAUTH_DIGEST       - HTTP Digest authentication
+ * CURLAUTH_NEGOTIATE    - HTTP Negotiate (SPNEGO) authentication
+ * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated)
+ * CURLAUTH_NTLM         - HTTP NTLM authentication
+ * CURLAUTH_DIGEST_IE    - HTTP Digest authentication with IE flavour
+ * CURLAUTH_NTLM_WB      - HTTP NTLM authentication delegated to winbind helper
+ * CURLAUTH_ONLY         - Use together with a single other type to force no
+ *                         authentication or just that single type
+ * CURLAUTH_ANY          - All fine types set
+ * CURLAUTH_ANYSAFE      - All fine types except Basic
+ */
+
+#define CURLAUTH_NONE         ((unsigned long)0)
+#define CURLAUTH_BASIC        (((unsigned long)1)<<0)
+#define CURLAUTH_DIGEST       (((unsigned long)1)<<1)
+#define CURLAUTH_NEGOTIATE    (((unsigned long)1)<<2)
+/* Deprecated since the advent of CURLAUTH_NEGOTIATE */
+#define CURLAUTH_GSSNEGOTIATE CURLAUTH_NEGOTIATE
+/* Used for CURLOPT_SOCKS5_AUTH to stay terminologically correct */
+#define CURLAUTH_GSSAPI CURLAUTH_NEGOTIATE
+#define CURLAUTH_NTLM         (((unsigned long)1)<<3)
+#define CURLAUTH_DIGEST_IE    (((unsigned long)1)<<4)
+#define CURLAUTH_NTLM_WB      (((unsigned long)1)<<5)
+#define CURLAUTH_ONLY         (((unsigned long)1)<<31)
+#define CURLAUTH_ANY          (~CURLAUTH_DIGEST_IE)
+#define CURLAUTH_ANYSAFE      (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE))
+
+#define CURLSSH_AUTH_ANY       ~0     /* all types supported by the server */
+#define CURLSSH_AUTH_NONE      0      /* none allowed, silly but complete */
+#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */
+#define CURLSSH_AUTH_PASSWORD  (1<<1) /* password */
+#define CURLSSH_AUTH_HOST      (1<<2) /* host key files */
+#define CURLSSH_AUTH_KEYBOARD  (1<<3) /* keyboard interactive */
+#define CURLSSH_AUTH_AGENT     (1<<4) /* agent (ssh-agent, pageant...) */
+#define CURLSSH_AUTH_GSSAPI    (1<<5) /* gssapi (kerberos, ...) */
+#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY
+
+#define CURLGSSAPI_DELEGATION_NONE        0      /* no delegation (default) */
+#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */
+#define CURLGSSAPI_DELEGATION_FLAG        (1<<1) /* delegate always */
+
+#define CURL_ERROR_SIZE 256
+
+enum curl_khtype {
+  CURLKHTYPE_UNKNOWN,
+  CURLKHTYPE_RSA1,
+  CURLKHTYPE_RSA,
+  CURLKHTYPE_DSS,
+  CURLKHTYPE_ECDSA,
+  CURLKHTYPE_ED25519
+};
+
+struct curl_khkey {
+  const char *key; /* points to a zero-terminated string encoded with base64
+                      if len is zero, otherwise to the "raw" data */
+  size_t len;
+  enum curl_khtype keytype;
+};
+
+/* this is the set of return values expected from the curl_sshkeycallback
+   callback */
+enum curl_khstat {
+  CURLKHSTAT_FINE_ADD_TO_FILE,
+  CURLKHSTAT_FINE,
+  CURLKHSTAT_REJECT, /* reject the connection, return an error */
+  CURLKHSTAT_DEFER,  /* do not accept it, but we can't answer right now so
+                        this causes a CURLE_DEFER error but otherwise the
+                        connection will be left intact etc */
+  CURLKHSTAT_LAST    /* not for use, only a marker for last-in-list */
+};
+
+/* this is the set of status codes pass in to the callback */
+enum curl_khmatch {
+  CURLKHMATCH_OK,       /* match */
+  CURLKHMATCH_MISMATCH, /* host found, key mismatch! */
+  CURLKHMATCH_MISSING,  /* no matching host/key found */
+  CURLKHMATCH_LAST      /* not for use, only a marker for last-in-list */
+};
+
+typedef int
+  (*curl_sshkeycallback) (CURL *easy,     /* easy handle */
+                          const struct curl_khkey *knownkey, /* known */
+                          const struct curl_khkey *foundkey, /* found */
+                          enum curl_khmatch, /* libcurl's view on the keys */
+                          void *clientp); /* custom pointer passed from app */
+
+/* parameter for the CURLOPT_USE_SSL option */
+typedef enum {
+  CURLUSESSL_NONE,    /* do not attempt to use SSL */
+  CURLUSESSL_TRY,     /* try using SSL, proceed anyway otherwise */
+  CURLUSESSL_CONTROL, /* SSL for the control connection or fail */
+  CURLUSESSL_ALL,     /* SSL for all communication or fail */
+  CURLUSESSL_LAST     /* not an option, never use */
+} curl_usessl;
+
+/* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */
+
+/* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the
+   name of improving interoperability with older servers. Some SSL libraries
+   have introduced work-arounds for this flaw but those work-arounds sometimes
+   make the SSL communication fail. To regain functionality with those broken
+   servers, a user can this way allow the vulnerability back. */
+#define CURLSSLOPT_ALLOW_BEAST (1<<0)
+
+/* - NO_REVOKE tells libcurl to disable certificate revocation checks for those
+   SSL backends where such behavior is present. */
+#define CURLSSLOPT_NO_REVOKE (1<<1)
+
+#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
+                          the obsolete stuff removed! */
+
+/* Backwards compatibility with older names */
+/* These are scheduled to disappear by 2009 */
+
+#define CURLFTPSSL_NONE CURLUSESSL_NONE
+#define CURLFTPSSL_TRY CURLUSESSL_TRY
+#define CURLFTPSSL_CONTROL CURLUSESSL_CONTROL
+#define CURLFTPSSL_ALL CURLUSESSL_ALL
+#define CURLFTPSSL_LAST CURLUSESSL_LAST
+#define curl_ftpssl curl_usessl
+#endif /*!CURL_NO_OLDIES*/
+
+/* parameter for the CURLOPT_FTP_SSL_CCC option */
+typedef enum {
+  CURLFTPSSL_CCC_NONE,    /* do not send CCC */
+  CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */
+  CURLFTPSSL_CCC_ACTIVE,  /* Initiate the shutdown */
+  CURLFTPSSL_CCC_LAST     /* not an option, never use */
+} curl_ftpccc;
+
+/* parameter for the CURLOPT_FTPSSLAUTH option */
+typedef enum {
+  CURLFTPAUTH_DEFAULT, /* let libcurl decide */
+  CURLFTPAUTH_SSL,     /* use "AUTH SSL" */
+  CURLFTPAUTH_TLS,     /* use "AUTH TLS" */
+  CURLFTPAUTH_LAST /* not an option, never use */
+} curl_ftpauth;
+
+/* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */
+typedef enum {
+  CURLFTP_CREATE_DIR_NONE,  /* do NOT create missing dirs! */
+  CURLFTP_CREATE_DIR,       /* (FTP/SFTP) if CWD fails, try MKD and then CWD
+                               again if MKD succeeded, for SFTP this does
+                               similar magic */
+  CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD
+                               again even if MKD failed! */
+  CURLFTP_CREATE_DIR_LAST   /* not an option, never use */
+} curl_ftpcreatedir;
+
+/* parameter for the CURLOPT_FTP_FILEMETHOD option */
+typedef enum {
+  CURLFTPMETHOD_DEFAULT,   /* let libcurl pick */
+  CURLFTPMETHOD_MULTICWD,  /* single CWD operation for each path part */
+  CURLFTPMETHOD_NOCWD,     /* no CWD at all */
+  CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */
+  CURLFTPMETHOD_LAST       /* not an option, never use */
+} curl_ftpmethod;
+
+/* bitmask defines for CURLOPT_HEADEROPT */
+#define CURLHEADER_UNIFIED  0
+#define CURLHEADER_SEPARATE (1<<0)
+
+/* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */
+#define CURLPROTO_HTTP   (1<<0)
+#define CURLPROTO_HTTPS  (1<<1)
+#define CURLPROTO_FTP    (1<<2)
+#define CURLPROTO_FTPS   (1<<3)
+#define CURLPROTO_SCP    (1<<4)
+#define CURLPROTO_SFTP   (1<<5)
+#define CURLPROTO_TELNET (1<<6)
+#define CURLPROTO_LDAP   (1<<7)
+#define CURLPROTO_LDAPS  (1<<8)
+#define CURLPROTO_DICT   (1<<9)
+#define CURLPROTO_FILE   (1<<10)
+#define CURLPROTO_TFTP   (1<<11)
+#define CURLPROTO_IMAP   (1<<12)
+#define CURLPROTO_IMAPS  (1<<13)
+#define CURLPROTO_POP3   (1<<14)
+#define CURLPROTO_POP3S  (1<<15)
+#define CURLPROTO_SMTP   (1<<16)
+#define CURLPROTO_SMTPS  (1<<17)
+#define CURLPROTO_RTSP   (1<<18)
+#define CURLPROTO_RTMP   (1<<19)
+#define CURLPROTO_RTMPT  (1<<20)
+#define CURLPROTO_RTMPE  (1<<21)
+#define CURLPROTO_RTMPTE (1<<22)
+#define CURLPROTO_RTMPS  (1<<23)
+#define CURLPROTO_RTMPTS (1<<24)
+#define CURLPROTO_GOPHER (1<<25)
+#define CURLPROTO_SMB    (1<<26)
+#define CURLPROTO_SMBS   (1<<27)
+#define CURLPROTO_ALL    (~0) /* enable everything */
+
+/* long may be 32 or 64 bits, but we should never depend on anything else
+   but 32 */
+#define CURLOPTTYPE_LONG          0
+#define CURLOPTTYPE_OBJECTPOINT   10000
+#define CURLOPTTYPE_STRINGPOINT   10000
+#define CURLOPTTYPE_FUNCTIONPOINT 20000
+#define CURLOPTTYPE_OFF_T         30000
+
+/* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the
+   string options from the header file */
+
+/* name is uppercase CURLOPT_<name>,
+   type is one of the defined CURLOPTTYPE_<type>
+   number is unique identifier */
+#ifdef CINIT
+#undef CINIT
+#endif
+
+#ifdef CURL_ISOCPP
+#define CINIT(na,t,nu) CURLOPT_ ## na = CURLOPTTYPE_ ## t + nu
+#else
+/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */
+#define LONG          CURLOPTTYPE_LONG
+#define OBJECTPOINT   CURLOPTTYPE_OBJECTPOINT
+#define STRINGPOINT   CURLOPTTYPE_OBJECTPOINT
+#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT
+#define OFF_T         CURLOPTTYPE_OFF_T
+#define CINIT(name,type,number) CURLOPT_/**/name = type + number
+#endif
+
+/*
+ * This macro-mania below setups the CURLOPT_[what] enum, to be used with
+ * curl_easy_setopt(). The first argument in the CINIT() macro is the [what]
+ * word.
+ */
+
+typedef enum {
+  /* This is the FILE * or void * the regular output should be written to. */
+  CINIT(WRITEDATA, OBJECTPOINT, 1),
+
+  /* The full URL to get/put */
+  CINIT(URL, STRINGPOINT, 2),
+
+  /* Port number to connect to, if other than default. */
+  CINIT(PORT, LONG, 3),
+
+  /* Name of proxy to use. */
+  CINIT(PROXY, STRINGPOINT, 4),
+
+  /* "user:password;options" to use when fetching. */
+  CINIT(USERPWD, STRINGPOINT, 5),
+
+  /* "user:password" to use with proxy. */
+  CINIT(PROXYUSERPWD, STRINGPOINT, 6),
+
+  /* Range to get, specified as an ASCII string. */
+  CINIT(RANGE, STRINGPOINT, 7),
+
+  /* not used */
+
+  /* Specified file stream to upload from (use as input): */
+  CINIT(READDATA, OBJECTPOINT, 9),
+
+  /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE
+   * bytes big. */
+  CINIT(ERRORBUFFER, OBJECTPOINT, 10),
+
+  /* Function that will be called to store the output (instead of fwrite). The
+   * parameters will use fwrite() syntax, make sure to follow them. */
+  CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11),
+
+  /* Function that will be called to read the input (instead of fread). The
+   * parameters will use fread() syntax, make sure to follow them. */
+  CINIT(READFUNCTION, FUNCTIONPOINT, 12),
+
+  /* Time-out the read operation after this amount of seconds */
+  CINIT(TIMEOUT, LONG, 13),
+
+  /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about
+   * how large the file being sent really is. That allows better error
+   * checking and better verifies that the upload was successful. -1 means
+   * unknown size.
+   *
+   * For large file support, there is also a _LARGE version of the key
+   * which takes an off_t type, allowing platforms with larger off_t
+   * sizes to handle larger files.  See below for INFILESIZE_LARGE.
+   */
+  CINIT(INFILESIZE, LONG, 14),
+
+  /* POST static input fields. */
+  CINIT(POSTFIELDS, OBJECTPOINT, 15),
+
+  /* Set the referrer page (needed by some CGIs) */
+  CINIT(REFERER, STRINGPOINT, 16),
+
+  /* Set the FTP PORT string (interface name, named or numerical IP address)
+     Use i.e '-' to use default address. */
+  CINIT(FTPPORT, STRINGPOINT, 17),
+
+  /* Set the User-Agent string (examined by some CGIs) */
+  CINIT(USERAGENT, STRINGPOINT, 18),
+
+  /* If the download receives less than "low speed limit" bytes/second
+   * during "low speed time" seconds, the operations is aborted.
+   * You could i.e if you have a pretty high speed connection, abort if
+   * it is less than 2000 bytes/sec during 20 seconds.
+   */
+
+  /* Set the "low speed limit" */
+  CINIT(LOW_SPEED_LIMIT, LONG, 19),
+
+  /* Set the "low speed time" */
+  CINIT(LOW_SPEED_TIME, LONG, 20),
+
+  /* Set the continuation offset.
+   *
+   * Note there is also a _LARGE version of this key which uses
+   * off_t types, allowing for large file offsets on platforms which
+   * use larger-than-32-bit off_t's.  Look below for RESUME_FROM_LARGE.
+   */
+  CINIT(RESUME_FROM, LONG, 21),
+
+  /* Set cookie in request: */
+  CINIT(COOKIE, STRINGPOINT, 22),
+
+  /* This points to a linked list of headers, struct curl_slist kind. This
+     list is also used for RTSP (in spite of its name) */
+  CINIT(HTTPHEADER, OBJECTPOINT, 23),
+
+  /* This points to a linked list of post entries, struct curl_httppost */
+  CINIT(HTTPPOST, OBJECTPOINT, 24),
+
+  /* name of the file keeping your private SSL-certificate */
+  CINIT(SSLCERT, STRINGPOINT, 25),
+
+  /* password for the SSL or SSH private key */
+  CINIT(KEYPASSWD, STRINGPOINT, 26),
+
+  /* send TYPE parameter? */
+  CINIT(CRLF, LONG, 27),
+
+  /* send linked-list of QUOTE commands */
+  CINIT(QUOTE, OBJECTPOINT, 28),
+
+  /* send FILE * or void * to store headers to, if you use a callback it
+     is simply passed to the callback unmodified */
+  CINIT(HEADERDATA, OBJECTPOINT, 29),
+
+  /* point to a file to read the initial cookies from, also enables
+     "cookie awareness" */
+  CINIT(COOKIEFILE, STRINGPOINT, 31),
+
+  /* What version to specifically try to use.
+     See CURL_SSLVERSION defines below. */
+  CINIT(SSLVERSION, LONG, 32),
+
+  /* What kind of HTTP time condition to use, see defines */
+  CINIT(TIMECONDITION, LONG, 33),
+
+  /* Time to use with the above condition. Specified in number of seconds
+     since 1 Jan 1970 */
+  CINIT(TIMEVALUE, LONG, 34),
+
+  /* 35 = OBSOLETE */
+
+  /* Custom request, for customizing the get command like
+     HTTP: DELETE, TRACE and others
+     FTP: to use a different list command
+     */
+  CINIT(CUSTOMREQUEST, STRINGPOINT, 36),
+
+  /* FILE handle to use instead of stderr */
+  CINIT(STDERR, OBJECTPOINT, 37),
+
+  /* 38 is not used */
+
+  /* send linked-list of post-transfer QUOTE commands */
+  CINIT(POSTQUOTE, OBJECTPOINT, 39),
+
+  CINIT(OBSOLETE40, OBJECTPOINT, 40), /* OBSOLETE, do not use! */
+
+  CINIT(VERBOSE, LONG, 41),      /* talk a lot */
+  CINIT(HEADER, LONG, 42),       /* throw the header out too */
+  CINIT(NOPROGRESS, LONG, 43),   /* shut off the progress meter */
+  CINIT(NOBODY, LONG, 44),       /* use HEAD to get http document */
+  CINIT(FAILONERROR, LONG, 45),  /* no output on http error codes >= 400 */
+  CINIT(UPLOAD, LONG, 46),       /* this is an upload */
+  CINIT(POST, LONG, 47),         /* HTTP POST method */
+  CINIT(DIRLISTONLY, LONG, 48),  /* bare names when listing directories */
+
+  CINIT(APPEND, LONG, 50),       /* Append instead of overwrite on upload! */
+
+  /* Specify whether to read the user+password from the .netrc or the URL.
+   * This must be one of the CURL_NETRC_* enums below. */
+  CINIT(NETRC, LONG, 51),
+
+  CINIT(FOLLOWLOCATION, LONG, 52),  /* use Location: Luke! */
+
+  CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */
+  CINIT(PUT, LONG, 54),          /* HTTP PUT */
+
+  /* 55 = OBSOLETE */
+
+  /* DEPRECATED
+   * Function that will be called instead of the internal progress display
+   * function. This function should be defined as the curl_progress_callback
+   * prototype defines. */
+  CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56),
+
+  /* Data passed to the CURLOPT_PROGRESSFUNCTION and CURLOPT_XFERINFOFUNCTION
+     callbacks */
+  CINIT(PROGRESSDATA, OBJECTPOINT, 57),
+#define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA
+
+  /* We want the referrer field set automatically when following locations */
+  CINIT(AUTOREFERER, LONG, 58),
+
+  /* Port of the proxy, can be set in the proxy string as well with:
+     "[host]:[port]" */
+  CINIT(PROXYPORT, LONG, 59),
+
+  /* size of the POST input data, if strlen() is not good to use */
+  CINIT(POSTFIELDSIZE, LONG, 60),
+
+  /* tunnel non-http operations through a HTTP proxy */
+  CINIT(HTTPPROXYTUNNEL, LONG, 61),
+
+  /* Set the interface string to use as outgoing network interface */
+  CINIT(INTERFACE, STRINGPOINT, 62),
+
+  /* Set the krb4/5 security level, this also enables krb4/5 awareness.  This
+   * is a string, 'clear', 'safe', 'confidential' or 'private'.  If the string
+   * is set but doesn't match one of these, 'private' will be used.  */
+  CINIT(KRBLEVEL, STRINGPOINT, 63),
+
+  /* Set if we should verify the peer in ssl handshake, set 1 to verify. */
+  CINIT(SSL_VERIFYPEER, LONG, 64),
+
+  /* The CApath or CAfile used to validate the peer certificate
+     this option is used only if SSL_VERIFYPEER is true */
+  CINIT(CAINFO, STRINGPOINT, 65),
+
+  /* 66 = OBSOLETE */
+  /* 67 = OBSOLETE */
+
+  /* Maximum number of http redirects to follow */
+  CINIT(MAXREDIRS, LONG, 68),
+
+  /* Pass a long set to 1 to get the date of the requested document (if
+     possible)! Pass a zero to shut it off. */
+  CINIT(FILETIME, LONG, 69),
+
+  /* This points to a linked list of telnet options */
+  CINIT(TELNETOPTIONS, OBJECTPOINT, 70),
+
+  /* Max amount of cached alive connections */
+  CINIT(MAXCONNECTS, LONG, 71),
+
+  CINIT(OBSOLETE72, LONG, 72), /* OBSOLETE, do not use! */
+
+  /* 73 = OBSOLETE */
+
+  /* Set to explicitly use a new connection for the upcoming transfer.
+     Do not use this unless you're absolutely sure of this, as it makes the
+     operation slower and is less friendly for the network. */
+  CINIT(FRESH_CONNECT, LONG, 74),
+
+  /* Set to explicitly forbid the upcoming transfer's connection to be re-used
+     when done. Do not use this unless you're absolutely sure of this, as it
+     makes the operation slower and is less friendly for the network. */
+  CINIT(FORBID_REUSE, LONG, 75),
+
+  /* Set to a file name that contains random data for libcurl to use to
+     seed the random engine when doing SSL connects. */
+  CINIT(RANDOM_FILE, STRINGPOINT, 76),
+
+  /* Set to the Entropy Gathering Daemon socket pathname */
+  CINIT(EGDSOCKET, STRINGPOINT, 77),
+
+  /* Time-out connect operations after this amount of seconds, if connects are
+     OK within this time, then fine... This only aborts the connect phase. */
+  CINIT(CONNECTTIMEOUT, LONG, 78),
+
+  /* Function that will be called to store headers (instead of fwrite). The
+   * parameters will use fwrite() syntax, make sure to follow them. */
+  CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79),
+
+  /* Set this to force the HTTP request to get back to GET. Only really usable
+     if POST, PUT or a custom request have been used first.
+   */
+  CINIT(HTTPGET, LONG, 80),
+
+  /* Set if we should verify the Common name from the peer certificate in ssl
+   * handshake, set 1 to check existence, 2 to ensure that it matches the
+   * provided hostname. */
+  CINIT(SSL_VERIFYHOST, LONG, 81),
+
+  /* Specify which file name to write all known cookies in after completed
+     operation. Set file name to "-" (dash) to make it go to stdout. */
+  CINIT(COOKIEJAR, STRINGPOINT, 82),
+
+  /* Specify which SSL ciphers to use */
+  CINIT(SSL_CIPHER_LIST, STRINGPOINT, 83),
+
+  /* Specify which HTTP version to use! This must be set to one of the
+     CURL_HTTP_VERSION* enums set below. */
+  CINIT(HTTP_VERSION, LONG, 84),
+
+  /* Specifically switch on or off the FTP engine's use of the EPSV command. By
+     default, that one will always be attempted before the more traditional
+     PASV command. */
+  CINIT(FTP_USE_EPSV, LONG, 85),
+
+  /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */
+  CINIT(SSLCERTTYPE, STRINGPOINT, 86),
+
+  /* name of the file keeping your private SSL-key */
+  CINIT(SSLKEY, STRINGPOINT, 87),
+
+  /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */
+  CINIT(SSLKEYTYPE, STRINGPOINT, 88),
+
+  /* crypto engine for the SSL-sub system */
+  CINIT(SSLENGINE, STRINGPOINT, 89),
+
+  /* set the crypto engine for the SSL-sub system as default
+     the param has no meaning...
+   */
+  CINIT(SSLENGINE_DEFAULT, LONG, 90),
+
+  /* Non-zero value means to use the global dns cache */
+  CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* DEPRECATED, do not use! */
+
+  /* DNS cache timeout */
+  CINIT(DNS_CACHE_TIMEOUT, LONG, 92),
+
+  /* send linked-list of pre-transfer QUOTE commands */
+  CINIT(PREQUOTE, OBJECTPOINT, 93),
+
+  /* set the debug function */
+  CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94),
+
+  /* set the data for the debug function */
+  CINIT(DEBUGDATA, OBJECTPOINT, 95),
+
+  /* mark this as start of a cookie session */
+  CINIT(COOKIESESSION, LONG, 96),
+
+  /* The CApath directory used to validate the peer certificate
+     this option is used only if SSL_VERIFYPEER is true */
+  CINIT(CAPATH, STRINGPOINT, 97),
+
+  /* Instruct libcurl to use a smaller receive buffer */
+  CINIT(BUFFERSIZE, LONG, 98),
+
+  /* Instruct libcurl to not use any signal/alarm handlers, even when using
+     timeouts. This option is useful for multi-threaded applications.
+     See libcurl-the-guide for more background information. */
+  CINIT(NOSIGNAL, LONG, 99),
+
+  /* Provide a CURLShare for mutexing non-ts data */
+  CINIT(SHARE, OBJECTPOINT, 100),
+
+  /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default),
+     CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and
+     CURLPROXY_SOCKS5. */
+  CINIT(PROXYTYPE, LONG, 101),
+
+  /* Set the Accept-Encoding string. Use this to tell a server you would like
+     the response to be compressed. Before 7.21.6, this was known as
+     CURLOPT_ENCODING */
+  CINIT(ACCEPT_ENCODING, STRINGPOINT, 102),
+
+  /* Set pointer to private data */
+  CINIT(PRIVATE, OBJECTPOINT, 103),
+
+  /* Set aliases for HTTP 200 in the HTTP Response header */
+  CINIT(HTTP200ALIASES, OBJECTPOINT, 104),
+
+  /* Continue to send authentication (user+password) when following locations,
+     even when hostname changed. This can potentially send off the name
+     and password to whatever host the server decides. */
+  CINIT(UNRESTRICTED_AUTH, LONG, 105),
+
+  /* Specifically switch on or off the FTP engine's use of the EPRT command (
+     it also disables the LPRT attempt). By default, those ones will always be
+     attempted before the good old traditional PORT command. */
+  CINIT(FTP_USE_EPRT, LONG, 106),
+
+  /* Set this to a bitmask value to enable the particular authentications
+     methods you like. Use this in combination with CURLOPT_USERPWD.
+     Note that setting multiple bits may cause extra network round-trips. */
+  CINIT(HTTPAUTH, LONG, 107),
+
+  /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx
+     in second argument. The function must be matching the
+     curl_ssl_ctx_callback proto. */
+  CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108),
+
+  /* Set the userdata for the ssl context callback function's third
+     argument */
+  CINIT(SSL_CTX_DATA, OBJECTPOINT, 109),
+
+  /* FTP Option that causes missing dirs to be created on the remote server.
+     In 7.19.4 we introduced the convenience enums for this option using the
+     CURLFTP_CREATE_DIR prefix.
+  */
+  CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110),
+
+  /* Set this to a bitmask value to enable the particular authentications
+     methods you like. Use this in combination with CURLOPT_PROXYUSERPWD.
+     Note that setting multiple bits may cause extra network round-trips. */
+  CINIT(PROXYAUTH, LONG, 111),
+
+  /* FTP option that changes the timeout, in seconds, associated with
+     getting a response.  This is different from transfer timeout time and
+     essentially places a demand on the FTP server to acknowledge commands
+     in a timely manner. */
+  CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112),
+#define CURLOPT_SERVER_RESPONSE_TIMEOUT CURLOPT_FTP_RESPONSE_TIMEOUT
+
+  /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to
+     tell libcurl to resolve names to those IP versions only. This only has
+     affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */
+  CINIT(IPRESOLVE, LONG, 113),
+
+  /* Set this option to limit the size of a file that will be downloaded from
+     an HTTP or FTP server.
+
+     Note there is also _LARGE version which adds large file support for
+     platforms which have larger off_t sizes.  See MAXFILESIZE_LARGE below. */
+  CINIT(MAXFILESIZE, LONG, 114),
+
+  /* See the comment for INFILESIZE above, but in short, specifies
+   * the size of the file being uploaded.  -1 means unknown.
+   */
+  CINIT(INFILESIZE_LARGE, OFF_T, 115),
+
+  /* Sets the continuation offset.  There is also a LONG version of this;
+   * look above for RESUME_FROM.
+   */
+  CINIT(RESUME_FROM_LARGE, OFF_T, 116),
+
+  /* Sets the maximum size of data that will be downloaded from
+   * an HTTP or FTP server.  See MAXFILESIZE above for the LONG version.
+   */
+  CINIT(MAXFILESIZE_LARGE, OFF_T, 117),
+
+  /* Set this option to the file name of your .netrc file you want libcurl
+     to parse (using the CURLOPT_NETRC option). If not set, libcurl will do
+     a poor attempt to find the user's home directory and check for a .netrc
+     file in there. */
+  CINIT(NETRC_FILE, STRINGPOINT, 118),
+
+  /* Enable SSL/TLS for FTP, pick one of:
+     CURLUSESSL_TRY     - try using SSL, proceed anyway otherwise
+     CURLUSESSL_CONTROL - SSL for the control connection or fail
+     CURLUSESSL_ALL     - SSL for all communication or fail
+  */
+  CINIT(USE_SSL, LONG, 119),
+
+  /* The _LARGE version of the standard POSTFIELDSIZE option */
+  CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120),
+
+  /* Enable/disable the TCP Nagle algorithm */
+  CINIT(TCP_NODELAY, LONG, 121),
+
+  /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */
+  /* 123 OBSOLETE. Gone in 7.16.0 */
+  /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */
+  /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */
+  /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */
+  /* 127 OBSOLETE. Gone in 7.16.0 */
+  /* 128 OBSOLETE. Gone in 7.16.0 */
+
+  /* When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option
+     can be used to change libcurl's default action which is to first try
+     "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK
+     response has been received.
+
+     Available parameters are:
+     CURLFTPAUTH_DEFAULT - let libcurl decide
+     CURLFTPAUTH_SSL     - try "AUTH SSL" first, then TLS
+     CURLFTPAUTH_TLS     - try "AUTH TLS" first, then SSL
+  */
+  CINIT(FTPSSLAUTH, LONG, 129),
+
+  CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130),
+  CINIT(IOCTLDATA, OBJECTPOINT, 131),
+
+  /* 132 OBSOLETE. Gone in 7.16.0 */
+  /* 133 OBSOLETE. Gone in 7.16.0 */
+
+  /* zero terminated string for pass on to the FTP server when asked for
+     "account" info */
+  CINIT(FTP_ACCOUNT, STRINGPOINT, 134),
+
+  /* feed cookie into cookie engine */
+  CINIT(COOKIELIST, STRINGPOINT, 135),
+
+  /* ignore Content-Length */
+  CINIT(IGNORE_CONTENT_LENGTH, LONG, 136),
+
+  /* Set to non-zero to skip the IP address received in a 227 PASV FTP server
+     response. Typically used for FTP-SSL purposes but is not restricted to
+     that. libcurl will then instead use the same IP address it used for the
+     control connection. */
+  CINIT(FTP_SKIP_PASV_IP, LONG, 137),
+
+  /* Select "file method" to use when doing FTP, see the curl_ftpmethod
+     above. */
+  CINIT(FTP_FILEMETHOD, LONG, 138),
+
+  /* Local port number to bind the socket to */
+  CINIT(LOCALPORT, LONG, 139),
+
+  /* Number of ports to try, including the first one set with LOCALPORT.
+     Thus, setting it to 1 will make no additional attempts but the first.
+  */
+  CINIT(LOCALPORTRANGE, LONG, 140),
+
+  /* no transfer, set up connection and let application use the socket by
+     extracting it with CURLINFO_LASTSOCKET */
+  CINIT(CONNECT_ONLY, LONG, 141),
+
+  /* Function that will be called to convert from the
+     network encoding (instead of using the iconv calls in libcurl) */
+  CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142),
+
+  /* Function that will be called to convert to the
+     network encoding (instead of using the iconv calls in libcurl) */
+  CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143),
+
+  /* Function that will be called to convert from UTF8
+     (instead of using the iconv calls in libcurl)
+     Note that this is used only for SSL certificate processing */
+  CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144),
+
+  /* if the connection proceeds too quickly then need to slow it down */
+  /* limit-rate: maximum number of bytes per second to send or receive */
+  CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145),
+  CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146),
+
+  /* Pointer to command string to send if USER/PASS fails. */
+  CINIT(FTP_ALTERNATIVE_TO_USER, STRINGPOINT, 147),
+
+  /* callback function for setting socket options */
+  CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148),
+  CINIT(SOCKOPTDATA, OBJECTPOINT, 149),
+
+  /* set to 0 to disable session ID re-use for this transfer, default is
+     enabled (== 1) */
+  CINIT(SSL_SESSIONID_CACHE, LONG, 150),
+
+  /* allowed SSH authentication methods */
+  CINIT(SSH_AUTH_TYPES, LONG, 151),
+
+  /* Used by scp/sftp to do public/private key authentication */
+  CINIT(SSH_PUBLIC_KEYFILE, STRINGPOINT, 152),
+  CINIT(SSH_PRIVATE_KEYFILE, STRINGPOINT, 153),
+
+  /* Send CCC (Clear Command Channel) after authentication */
+  CINIT(FTP_SSL_CCC, LONG, 154),
+
+  /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */
+  CINIT(TIMEOUT_MS, LONG, 155),
+  CINIT(CONNECTTIMEOUT_MS, LONG, 156),
+
+  /* set to zero to disable the libcurl's decoding and thus pass the raw body
+     data to the application even when it is encoded/compressed */
+  CINIT(HTTP_TRANSFER_DECODING, LONG, 157),
+  CINIT(HTTP_CONTENT_DECODING, LONG, 158),
+
+  /* Permission used when creating new files and directories on the remote
+     server for protocols that support it, SFTP/SCP/FILE */
+  CINIT(NEW_FILE_PERMS, LONG, 159),
+  CINIT(NEW_DIRECTORY_PERMS, LONG, 160),
+
+  /* Set the behaviour of POST when redirecting. Values must be set to one
+     of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */
+  CINIT(POSTREDIR, LONG, 161),
+
+  /* used by scp/sftp to verify the host's public key */
+  CINIT(SSH_HOST_PUBLIC_KEY_MD5, STRINGPOINT, 162),
+
+  /* Callback function for opening socket (instead of socket(2)). Optionally,
+     callback is able change the address or refuse to connect returning
+     CURL_SOCKET_BAD.  The callback should have type
+     curl_opensocket_callback */
+  CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163),
+  CINIT(OPENSOCKETDATA, OBJECTPOINT, 164),
+
+  /* POST volatile input fields. */
+  CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165),
+
+  /* set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy */
+  CINIT(PROXY_TRANSFER_MODE, LONG, 166),
+
+  /* Callback function for seeking in the input stream */
+  CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167),
+  CINIT(SEEKDATA, OBJECTPOINT, 168),
+
+  /* CRL file */
+  CINIT(CRLFILE, STRINGPOINT, 169),
+
+  /* Issuer certificate */
+  CINIT(ISSUERCERT, STRINGPOINT, 170),
+
+  /* (IPv6) Address scope */
+  CINIT(ADDRESS_SCOPE, LONG, 171),
+
+  /* Collect certificate chain info and allow it to get retrievable with
+     CURLINFO_CERTINFO after the transfer is complete. */
+  CINIT(CERTINFO, LONG, 172),
+
+  /* "name" and "pwd" to use when fetching. */
+  CINIT(USERNAME, STRINGPOINT, 173),
+  CINIT(PASSWORD, STRINGPOINT, 174),
+
+    /* "name" and "pwd" to use with Proxy when fetching. */
+  CINIT(PROXYUSERNAME, STRINGPOINT, 175),
+  CINIT(PROXYPASSWORD, STRINGPOINT, 176),
+
+  /* Comma separated list of hostnames defining no-proxy zones. These should
+     match both hostnames directly, and hostnames within a domain. For
+     example, local.com will match local.com and www.local.com, but NOT
+     notlocal.com or www.notlocal.com. For compatibility with other
+     implementations of this, .local.com will be considered to be the same as
+     local.com. A single * is the only valid wildcard, and effectively
+     disables the use of proxy. */
+  CINIT(NOPROXY, STRINGPOINT, 177),
+
+  /* block size for TFTP transfers */
+  CINIT(TFTP_BLKSIZE, LONG, 178),
+
+  /* Socks Service */
+  CINIT(SOCKS5_GSSAPI_SERVICE, STRINGPOINT, 179), /* DEPRECATED, do not use! */
+
+  /* Socks Service */
+  CINIT(SOCKS5_GSSAPI_NEC, LONG, 180),
+
+  /* set the bitmask for the protocols that are allowed to be used for the
+     transfer, which thus helps the app which takes URLs from users or other
+     external inputs and want to restrict what protocol(s) to deal
+     with. Defaults to CURLPROTO_ALL. */
+  CINIT(PROTOCOLS, LONG, 181),
+
+  /* set the bitmask for the protocols that libcurl is allowed to follow to,
+     as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs
+     to be set in both bitmasks to be allowed to get redirected to. Defaults
+     to all protocols except FILE and SCP. */
+  CINIT(REDIR_PROTOCOLS, LONG, 182),
+
+  /* set the SSH knownhost file name to use */
+  CINIT(SSH_KNOWNHOSTS, STRINGPOINT, 183),
+
+  /* set the SSH host key callback, must point to a curl_sshkeycallback
+     function */
+  CINIT(SSH_KEYFUNCTION, FUNCTIONPOINT, 184),
+
+  /* set the SSH host key callback custom pointer */
+  CINIT(SSH_KEYDATA, OBJECTPOINT, 185),
+
+  /* set the SMTP mail originator */
+  CINIT(MAIL_FROM, STRINGPOINT, 186),
+
+  /* set the list of SMTP mail receiver(s) */
+  CINIT(MAIL_RCPT, OBJECTPOINT, 187),
+
+  /* FTP: send PRET before PASV */
+  CINIT(FTP_USE_PRET, LONG, 188),
+
+  /* RTSP request method (OPTIONS, SETUP, PLAY, etc...) */
+  CINIT(RTSP_REQUEST, LONG, 189),
+
+  /* The RTSP session identifier */
+  CINIT(RTSP_SESSION_ID, STRINGPOINT, 190),
+
+  /* The RTSP stream URI */
+  CINIT(RTSP_STREAM_URI, STRINGPOINT, 191),
+
+  /* The Transport: header to use in RTSP requests */
+  CINIT(RTSP_TRANSPORT, STRINGPOINT, 192),
+
+  /* Manually initialize the client RTSP CSeq for this handle */
+  CINIT(RTSP_CLIENT_CSEQ, LONG, 193),
+
+  /* Manually initialize the server RTSP CSeq for this handle */
+  CINIT(RTSP_SERVER_CSEQ, LONG, 194),
+
+  /* The stream to pass to INTERLEAVEFUNCTION. */
+  CINIT(INTERLEAVEDATA, OBJECTPOINT, 195),
+
+  /* Let the application define a custom write method for RTP data */
+  CINIT(INTERLEAVEFUNCTION, FUNCTIONPOINT, 196),
+
+  /* Turn on wildcard matching */
+  CINIT(WILDCARDMATCH, LONG, 197),
+
+  /* Directory matching callback called before downloading of an
+     individual file (chunk) started */
+  CINIT(CHUNK_BGN_FUNCTION, FUNCTIONPOINT, 198),
+
+  /* Directory matching callback called after the file (chunk)
+     was downloaded, or skipped */
+  CINIT(CHUNK_END_FUNCTION, FUNCTIONPOINT, 199),
+
+  /* Change match (fnmatch-like) callback for wildcard matching */
+  CINIT(FNMATCH_FUNCTION, FUNCTIONPOINT, 200),
+
+  /* Let the application define custom chunk data pointer */
+  CINIT(CHUNK_DATA, OBJECTPOINT, 201),
+
+  /* FNMATCH_FUNCTION user pointer */
+  CINIT(FNMATCH_DATA, OBJECTPOINT, 202),
+
+  /* send linked-list of name:port:address sets */
+  CINIT(RESOLVE, OBJECTPOINT, 203),
+
+  /* Set a username for authenticated TLS */
+  CINIT(TLSAUTH_USERNAME, STRINGPOINT, 204),
+
+  /* Set a password for authenticated TLS */
+  CINIT(TLSAUTH_PASSWORD, STRINGPOINT, 205),
+
+  /* Set authentication type for authenticated TLS */
+  CINIT(TLSAUTH_TYPE, STRINGPOINT, 206),
+
+  /* Set to 1 to enable the "TE:" header in HTTP requests to ask for
+     compressed transfer-encoded responses. Set to 0 to disable the use of TE:
+     in outgoing requests. The current default is 0, but it might change in a
+     future libcurl release.
+
+     libcurl will ask for the compressed methods it knows of, and if that
+     isn't any, it will not ask for transfer-encoding at all even if this
+     option is set to 1.
+
+  */
+  CINIT(TRANSFER_ENCODING, LONG, 207),
+
+  /* Callback function for closing socket (instead of close(2)). The callback
+     should have type curl_closesocket_callback */
+  CINIT(CLOSESOCKETFUNCTION, FUNCTIONPOINT, 208),
+  CINIT(CLOSESOCKETDATA, OBJECTPOINT, 209),
+
+  /* allow GSSAPI credential delegation */
+  CINIT(GSSAPI_DELEGATION, LONG, 210),
+
+  /* Set the name servers to use for DNS resolution */
+  CINIT(DNS_SERVERS, STRINGPOINT, 211),
+
+  /* Time-out accept operations (currently for FTP only) after this amount
+     of milliseconds. */
+  CINIT(ACCEPTTIMEOUT_MS, LONG, 212),
+
+  /* Set TCP keepalive */
+  CINIT(TCP_KEEPALIVE, LONG, 213),
+
+  /* non-universal keepalive knobs (Linux, AIX, HP-UX, more) */
+  CINIT(TCP_KEEPIDLE, LONG, 214),
+  CINIT(TCP_KEEPINTVL, LONG, 215),
+
+  /* Enable/disable specific SSL features with a bitmask, see CURLSSLOPT_* */
+  CINIT(SSL_OPTIONS, LONG, 216),
+
+  /* Set the SMTP auth originator */
+  CINIT(MAIL_AUTH, STRINGPOINT, 217),
+
+  /* Enable/disable SASL initial response */
+  CINIT(SASL_IR, LONG, 218),
+
+  /* Function that will be called instead of the internal progress display
+   * function. This function should be defined as the curl_xferinfo_callback
+   * prototype defines. (Deprecates CURLOPT_PROGRESSFUNCTION) */
+  CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219),
+
+  /* The XOAUTH2 bearer token */
+  CINIT(XOAUTH2_BEARER, STRINGPOINT, 220),
+
+  /* Set the interface string to use as outgoing network
+   * interface for DNS requests.
+   * Only supported by the c-ares DNS backend */
+  CINIT(DNS_INTERFACE, STRINGPOINT, 221),
+
+  /* Set the local IPv4 address to use for outgoing DNS requests.
+   * Only supported by the c-ares DNS backend */
+  CINIT(DNS_LOCAL_IP4, STRINGPOINT, 222),
+
+  /* Set the local IPv4 address to use for outgoing DNS requests.
+   * Only supported by the c-ares DNS backend */
+  CINIT(DNS_LOCAL_IP6, STRINGPOINT, 223),
+
+  /* Set authentication options directly */
+  CINIT(LOGIN_OPTIONS, STRINGPOINT, 224),
+
+  /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */
+  CINIT(SSL_ENABLE_NPN, LONG, 225),
+
+  /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */
+  CINIT(SSL_ENABLE_ALPN, LONG, 226),
+
+  /* Time to wait for a response to a HTTP request containing an
+   * Expect: 100-continue header before sending the data anyway. */
+  CINIT(EXPECT_100_TIMEOUT_MS, LONG, 227),
+
+  /* This points to a linked list of headers used for proxy requests only,
+     struct curl_slist kind */
+  CINIT(PROXYHEADER, OBJECTPOINT, 228),
+
+  /* Pass in a bitmask of "header options" */
+  CINIT(HEADEROPT, LONG, 229),
+
+  /* The public key in DER form used to validate the peer public key
+     this option is used only if SSL_VERIFYPEER is true */
+  CINIT(PINNEDPUBLICKEY, STRINGPOINT, 230),
+
+  /* Path to Unix domain socket */
+  CINIT(UNIX_SOCKET_PATH, STRINGPOINT, 231),
+
+  /* Set if we should verify the certificate status. */
+  CINIT(SSL_VERIFYSTATUS, LONG, 232),
+
+  /* Set if we should enable TLS false start. */
+  CINIT(SSL_FALSESTART, LONG, 233),
+
+  /* Do not squash dot-dot sequences */
+  CINIT(PATH_AS_IS, LONG, 234),
+
+  /* Proxy Service Name */
+  CINIT(PROXY_SERVICE_NAME, STRINGPOINT, 235),
+
+  /* Service Name */
+  CINIT(SERVICE_NAME, STRINGPOINT, 236),
+
+  /* Wait/don't wait for pipe/mutex to clarify */
+  CINIT(PIPEWAIT, LONG, 237),
+
+  /* Set the protocol used when curl is given a URL without a protocol */
+  CINIT(DEFAULT_PROTOCOL, STRINGPOINT, 238),
+
+  /* Set stream weight, 1 - 256 (default is 16) */
+  CINIT(STREAM_WEIGHT, LONG, 239),
+
+  /* Set stream dependency on another CURL handle */
+  CINIT(STREAM_DEPENDS, OBJECTPOINT, 240),
+
+  /* Set E-xclusive stream dependency on another CURL handle */
+  CINIT(STREAM_DEPENDS_E, OBJECTPOINT, 241),
+
+  /* Do not send any tftp option requests to the server */
+  CINIT(TFTP_NO_OPTIONS, LONG, 242),
+
+  /* Linked-list of host:port:connect-to-host:connect-to-port,
+     overrides the URL's host:port (only for the network layer) */
+  CINIT(CONNECT_TO, OBJECTPOINT, 243),
+
+  /* Set TCP Fast Open */
+  CINIT(TCP_FASTOPEN, LONG, 244),
+
+  /* Continue to send data if the server responds early with an
+   * HTTP status code >= 300 */
+  CINIT(KEEP_SENDING_ON_ERROR, LONG, 245),
+
+  /* The CApath or CAfile used to validate the proxy certificate
+     this option is used only if PROXY_SSL_VERIFYPEER is true */
+  CINIT(PROXY_CAINFO, STRINGPOINT, 246),
+
+  /* The CApath directory used to validate the proxy certificate
+     this option is used only if PROXY_SSL_VERIFYPEER is true */
+  CINIT(PROXY_CAPATH, STRINGPOINT, 247),
+
+  /* Set if we should verify the proxy in ssl handshake,
+     set 1 to verify. */
+  CINIT(PROXY_SSL_VERIFYPEER, LONG, 248),
+
+  /* Set if we should verify the Common name from the proxy certificate in ssl
+   * handshake, set 1 to check existence, 2 to ensure that it matches
+   * the provided hostname. */
+  CINIT(PROXY_SSL_VERIFYHOST, LONG, 249),
+
+  /* What version to specifically try to use for proxy.
+     See CURL_SSLVERSION defines below. */
+  CINIT(PROXY_SSLVERSION, LONG, 250),
+
+  /* Set a username for authenticated TLS for proxy */
+  CINIT(PROXY_TLSAUTH_USERNAME, STRINGPOINT, 251),
+
+  /* Set a password for authenticated TLS for proxy */
+  CINIT(PROXY_TLSAUTH_PASSWORD, STRINGPOINT, 252),
+
+  /* Set authentication type for authenticated TLS for proxy */
+  CINIT(PROXY_TLSAUTH_TYPE, STRINGPOINT, 253),
+
+  /* name of the file keeping your private SSL-certificate for proxy */
+  CINIT(PROXY_SSLCERT, STRINGPOINT, 254),
+
+  /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") for
+     proxy */
+  CINIT(PROXY_SSLCERTTYPE, STRINGPOINT, 255),
+
+  /* name of the file keeping your private SSL-key for proxy */
+  CINIT(PROXY_SSLKEY, STRINGPOINT, 256),
+
+  /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") for
+     proxy */
+  CINIT(PROXY_SSLKEYTYPE, STRINGPOINT, 257),
+
+  /* password for the SSL private key for proxy */
+  CINIT(PROXY_KEYPASSWD, STRINGPOINT, 258),
+
+  /* Specify which SSL ciphers to use for proxy */
+  CINIT(PROXY_SSL_CIPHER_LIST, STRINGPOINT, 259),
+
+  /* CRL file for proxy */
+  CINIT(PROXY_CRLFILE, STRINGPOINT, 260),
+
+  /* Enable/disable specific SSL features with a bitmask for proxy, see
+     CURLSSLOPT_* */
+  CINIT(PROXY_SSL_OPTIONS, LONG, 261),
+
+  /* Name of pre proxy to use. */
+  CINIT(PRE_PROXY, STRINGPOINT, 262),
+
+  /* The public key in DER form used to validate the proxy public key
+     this option is used only if PROXY_SSL_VERIFYPEER is true */
+  CINIT(PROXY_PINNEDPUBLICKEY, STRINGPOINT, 263),
+
+  /* Path to an abstract Unix domain socket */
+  CINIT(ABSTRACT_UNIX_SOCKET, STRINGPOINT, 264),
+
+  /* Suppress proxy CONNECT response headers from user callbacks */
+  CINIT(SUPPRESS_CONNECT_HEADERS, LONG, 265),
+
+  /* The request target, instead of extracted from the URL */
+  CINIT(REQUEST_TARGET, STRINGPOINT, 266),
+
+  /* bitmask of allowed auth methods for connections to SOCKS5 proxies */
+  CINIT(SOCKS5_AUTH, LONG, 267),
+
+  /* Enable/disable SSH compression */
+  CINIT(SSH_COMPRESSION, LONG, 268),
+
+  /* Post MIME data. */
+  CINIT(MIMEPOST, OBJECTPOINT, 269),
+
+  CURLOPT_LASTENTRY /* the last unused */
+} CURLoption;
+
+#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
+                          the obsolete stuff removed! */
+
+/* Backwards compatibility with older names */
+/* These are scheduled to disappear by 2011 */
+
+/* This was added in version 7.19.1 */
+#define CURLOPT_POST301 CURLOPT_POSTREDIR
+
+/* These are scheduled to disappear by 2009 */
+
+/* The following were added in 7.17.0 */
+#define CURLOPT_SSLKEYPASSWD CURLOPT_KEYPASSWD
+#define CURLOPT_FTPAPPEND CURLOPT_APPEND
+#define CURLOPT_FTPLISTONLY CURLOPT_DIRLISTONLY
+#define CURLOPT_FTP_SSL CURLOPT_USE_SSL
+
+/* The following were added earlier */
+
+#define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD
+#define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL
+
+#else
+/* This is set if CURL_NO_OLDIES is defined at compile-time */
+#undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */
+#endif
+
+
+  /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host
+     name resolves addresses using more than one IP protocol version, this
+     option might be handy to force libcurl to use a specific IP version. */
+#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP
+                                     versions that your system allows */
+#define CURL_IPRESOLVE_V4       1 /* resolve to IPv4 addresses */
+#define CURL_IPRESOLVE_V6       2 /* resolve to IPv6 addresses */
+
+  /* three convenient "aliases" that follow the name scheme better */
+#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER
+
+  /* These enums are for use with the CURLOPT_HTTP_VERSION option. */
+enum {
+  CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd
+                             like the library to choose the best possible
+                             for us! */
+  CURL_HTTP_VERSION_1_0,  /* please use HTTP 1.0 in the request */
+  CURL_HTTP_VERSION_1_1,  /* please use HTTP 1.1 in the request */
+  CURL_HTTP_VERSION_2_0,  /* please use HTTP 2 in the request */
+  CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */
+  CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE,  /* please use HTTP 2 without HTTP/1.1
+                                           Upgrade */
+
+  CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */
+};
+
+/* Convenience definition simple because the name of the version is HTTP/2 and
+   not 2.0. The 2_0 version of the enum name was set while the version was
+   still planned to be 2.0 and we stick to it for compatibility. */
+#define CURL_HTTP_VERSION_2 CURL_HTTP_VERSION_2_0
+
+/*
+ * Public API enums for RTSP requests
+ */
+enum {
+    CURL_RTSPREQ_NONE, /* first in list */
+    CURL_RTSPREQ_OPTIONS,
+    CURL_RTSPREQ_DESCRIBE,
+    CURL_RTSPREQ_ANNOUNCE,
+    CURL_RTSPREQ_SETUP,
+    CURL_RTSPREQ_PLAY,
+    CURL_RTSPREQ_PAUSE,
+    CURL_RTSPREQ_TEARDOWN,
+    CURL_RTSPREQ_GET_PARAMETER,
+    CURL_RTSPREQ_SET_PARAMETER,
+    CURL_RTSPREQ_RECORD,
+    CURL_RTSPREQ_RECEIVE,
+    CURL_RTSPREQ_LAST /* last in list */
+};
+
+  /* These enums are for use with the CURLOPT_NETRC option. */
+enum CURL_NETRC_OPTION {
+  CURL_NETRC_IGNORED,     /* The .netrc will never be read.
+                           * This is the default. */
+  CURL_NETRC_OPTIONAL,    /* A user:password in the URL will be preferred
+                           * to one in the .netrc. */
+  CURL_NETRC_REQUIRED,    /* A user:password in the URL will be ignored.
+                           * Unless one is set programmatically, the .netrc
+                           * will be queried. */
+  CURL_NETRC_LAST
+};
+
+enum {
+  CURL_SSLVERSION_DEFAULT,
+  CURL_SSLVERSION_TLSv1, /* TLS 1.x */
+  CURL_SSLVERSION_SSLv2,
+  CURL_SSLVERSION_SSLv3,
+  CURL_SSLVERSION_TLSv1_0,
+  CURL_SSLVERSION_TLSv1_1,
+  CURL_SSLVERSION_TLSv1_2,
+  CURL_SSLVERSION_TLSv1_3,
+
+  CURL_SSLVERSION_LAST /* never use, keep last */
+};
+
+enum {
+  CURL_SSLVERSION_MAX_NONE =     0,
+  CURL_SSLVERSION_MAX_DEFAULT =  (CURL_SSLVERSION_TLSv1   << 16),
+  CURL_SSLVERSION_MAX_TLSv1_0 =  (CURL_SSLVERSION_TLSv1_0 << 16),
+  CURL_SSLVERSION_MAX_TLSv1_1 =  (CURL_SSLVERSION_TLSv1_1 << 16),
+  CURL_SSLVERSION_MAX_TLSv1_2 =  (CURL_SSLVERSION_TLSv1_2 << 16),
+  CURL_SSLVERSION_MAX_TLSv1_3 =  (CURL_SSLVERSION_TLSv1_3 << 16),
+
+  /* never use, keep last */
+  CURL_SSLVERSION_MAX_LAST =     (CURL_SSLVERSION_LAST    << 16)
+};
+
+enum CURL_TLSAUTH {
+  CURL_TLSAUTH_NONE,
+  CURL_TLSAUTH_SRP,
+  CURL_TLSAUTH_LAST /* never use, keep last */
+};
+
+/* symbols to use with CURLOPT_POSTREDIR.
+   CURL_REDIR_POST_301, CURL_REDIR_POST_302 and CURL_REDIR_POST_303
+   can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302
+   | CURL_REDIR_POST_303 == CURL_REDIR_POST_ALL */
+
+#define CURL_REDIR_GET_ALL  0
+#define CURL_REDIR_POST_301 1
+#define CURL_REDIR_POST_302 2
+#define CURL_REDIR_POST_303 4
+#define CURL_REDIR_POST_ALL \
+    (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303)
+
+typedef enum {
+  CURL_TIMECOND_NONE,
+
+  CURL_TIMECOND_IFMODSINCE,
+  CURL_TIMECOND_IFUNMODSINCE,
+  CURL_TIMECOND_LASTMOD,
+
+  CURL_TIMECOND_LAST
+} curl_TimeCond;
+
+/* Special size_t value signaling a zero-terminated string. */
+#define CURL_ZERO_TERMINATED ((size_t) -1)
+
+/* curl_strequal() and curl_strnequal() are subject for removal in a future
+   release */
+CURL_EXTERN int curl_strequal(const char *s1, const char *s2);
+CURL_EXTERN int curl_strnequal(const char *s1, const char *s2, size_t n);
+
+/* Mime/form handling support. */
+typedef struct curl_mime_s      curl_mime;      /* Mime context. */
+typedef struct curl_mimepart_s  curl_mimepart;  /* Mime part context. */
+
+/*
+ * NAME curl_mime_init()
+ *
+ * DESCRIPTION
+ *
+ * Create a mime context and return its handle. The easy parameter is the
+ * target handle.
+ */
+CURL_EXTERN curl_mime *curl_mime_init(CURL *easy);
+
+/*
+ * NAME curl_mime_free()
+ *
+ * DESCRIPTION
+ *
+ * release a mime handle and its substructures.
+ */
+CURL_EXTERN void curl_mime_free(curl_mime *mime);
+
+/*
+ * NAME curl_mime_addpart()
+ *
+ * DESCRIPTION
+ *
+ * Append a new empty part to the given mime context and return a handle to
+ * the created part.
+ */
+CURL_EXTERN curl_mimepart *curl_mime_addpart(curl_mime *mime);
+
+/*
+ * NAME curl_mime_name()
+ *
+ * DESCRIPTION
+ *
+ * Set mime/form part name.
+ */
+CURL_EXTERN CURLcode curl_mime_name(curl_mimepart *part, const char *name);
+
+/*
+ * NAME curl_mime_filename()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part remote file name.
+ */
+CURL_EXTERN CURLcode curl_mime_filename(curl_mimepart *part,
+                                        const char *filename);
+
+/*
+ * NAME curl_mime_type()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part type.
+ */
+CURL_EXTERN CURLcode curl_mime_type(curl_mimepart *part, const char *mimetype);
+
+/*
+ * NAME curl_mime_encoder()
+ *
+ * DESCRIPTION
+ *
+ * Set mime data transfer encoder.
+ */
+CURL_EXTERN CURLcode curl_mime_encoder(curl_mimepart *part,
+                                       const char *encoding);
+
+/*
+ * NAME curl_mime_data()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part data source from memory data,
+ */
+CURL_EXTERN CURLcode curl_mime_data(curl_mimepart *part,
+                                    const char *data, size_t datasize);
+
+/*
+ * NAME curl_mime_filedata()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part data source from named file.
+ */
+CURL_EXTERN CURLcode curl_mime_filedata(curl_mimepart *part,
+                                        const char *filename);
+
+/*
+ * NAME curl_mime_data_cb()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part data source from callback function.
+ */
+CURL_EXTERN CURLcode curl_mime_data_cb(curl_mimepart *part,
+                                       curl_off_t datasize,
+                                       curl_read_callback readfunc,
+                                       curl_seek_callback seekfunc,
+                                       curl_free_callback freefunc,
+                                       void *arg);
+
+/*
+ * NAME curl_mime_subparts()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part data source from subparts.
+ */
+CURL_EXTERN CURLcode curl_mime_subparts(curl_mimepart *part,
+                                        curl_mime *subparts);
+/*
+ * NAME curl_mime_headers()
+ *
+ * DESCRIPTION
+ *
+ * Set mime part headers.
+ */
+CURL_EXTERN CURLcode curl_mime_headers(curl_mimepart *part,
+                                       struct curl_slist *headers,
+                                       int take_ownership);
+
+/* Old form API. */
+/* name is uppercase CURLFORM_<name> */
+#ifdef CFINIT
+#undef CFINIT
+#endif
+
+#ifdef CURL_ISOCPP
+#define CFINIT(name) CURLFORM_ ## name
+#else
+/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */
+#define CFINIT(name) CURLFORM_/**/name
+#endif
+
+typedef enum {
+  CFINIT(NOTHING),        /********* the first one is unused ************/
+
+  /*  */
+  CFINIT(COPYNAME),
+  CFINIT(PTRNAME),
+  CFINIT(NAMELENGTH),
+  CFINIT(COPYCONTENTS),
+  CFINIT(PTRCONTENTS),
+  CFINIT(CONTENTSLENGTH),
+  CFINIT(FILECONTENT),
+  CFINIT(ARRAY),
+  CFINIT(OBSOLETE),
+  CFINIT(FILE),
+
+  CFINIT(BUFFER),
+  CFINIT(BUFFERPTR),
+  CFINIT(BUFFERLENGTH),
+
+  CFINIT(CONTENTTYPE),
+  CFINIT(CONTENTHEADER),
+  CFINIT(FILENAME),
+  CFINIT(END),
+  CFINIT(OBSOLETE2),
+
+  CFINIT(STREAM),
+  CFINIT(CONTENTLEN), /* added in 7.46.0, provide a curl_off_t length */
+
+  CURLFORM_LASTENTRY /* the last unused */
+} CURLformoption;
+
+#undef CFINIT /* done */
+
+/* structure to be used as parameter for CURLFORM_ARRAY */
+struct curl_forms {
+  CURLformoption option;
+  const char     *value;
+};
+
+/* use this for multipart formpost building */
+/* Returns code for curl_formadd()
+ *
+ * Returns:
+ * CURL_FORMADD_OK             on success
+ * CURL_FORMADD_MEMORY         if the FormInfo allocation fails
+ * CURL_FORMADD_OPTION_TWICE   if one option is given twice for one Form
+ * CURL_FORMADD_NULL           if a null pointer was given for a char
+ * CURL_FORMADD_MEMORY         if the allocation of a FormInfo struct failed
+ * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used
+ * CURL_FORMADD_INCOMPLETE     if the some FormInfo is not complete (or error)
+ * CURL_FORMADD_MEMORY         if a curl_httppost struct cannot be allocated
+ * CURL_FORMADD_MEMORY         if some allocation for string copying failed.
+ * CURL_FORMADD_ILLEGAL_ARRAY  if an illegal option is used in an array
+ *
+ ***************************************************************************/
+typedef enum {
+  CURL_FORMADD_OK, /* first, no error */
+
+  CURL_FORMADD_MEMORY,
+  CURL_FORMADD_OPTION_TWICE,
+  CURL_FORMADD_NULL,
+  CURL_FORMADD_UNKNOWN_OPTION,
+  CURL_FORMADD_INCOMPLETE,
+  CURL_FORMADD_ILLEGAL_ARRAY,
+  CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */
+
+  CURL_FORMADD_LAST /* last */
+} CURLFORMcode;
+
+/*
+ * NAME curl_formadd()
+ *
+ * DESCRIPTION
+ *
+ * Pretty advanced function for building multi-part formposts. Each invoke
+ * adds one part that together construct a full post. Then use
+ * CURLOPT_HTTPPOST to send it off to libcurl.
+ */
+CURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost,
+                                      struct curl_httppost **last_post,
+                                      ...);
+
+/*
+ * callback function for curl_formget()
+ * The void *arg pointer will be the one passed as second argument to
+ *   curl_formget().
+ * The character buffer passed to it must not be freed.
+ * Should return the buffer length passed to it as the argument "len" on
+ *   success.
+ */
+typedef size_t (*curl_formget_callback)(void *arg, const char *buf,
+                                        size_t len);
+
+/*
+ * NAME curl_formget()
+ *
+ * DESCRIPTION
+ *
+ * Serialize a curl_httppost struct built with curl_formadd().
+ * Accepts a void pointer as second argument which will be passed to
+ * the curl_formget_callback function.
+ * Returns 0 on success.
+ */
+CURL_EXTERN int curl_formget(struct curl_httppost *form, void *arg,
+                             curl_formget_callback append);
+/*
+ * NAME curl_formfree()
+ *
+ * DESCRIPTION
+ *
+ * Free a multipart formpost previously built with curl_formadd().
+ */
+CURL_EXTERN void curl_formfree(struct curl_httppost *form);
+
+/*
+ * NAME curl_getenv()
+ *
+ * DESCRIPTION
+ *
+ * Returns a malloc()'ed string that MUST be curl_free()ed after usage is
+ * complete. DEPRECATED - see lib/README.curlx
+ */
+CURL_EXTERN char *curl_getenv(const char *variable);
+
+/*
+ * NAME curl_version()
+ *
+ * DESCRIPTION
+ *
+ * Returns a static ascii string of the libcurl version.
+ */
+CURL_EXTERN char *curl_version(void);
+
+/*
+ * NAME curl_easy_escape()
+ *
+ * DESCRIPTION
+ *
+ * Escapes URL strings (converts all letters consider illegal in URLs to their
+ * %XX versions). This function returns a new allocated string or NULL if an
+ * error occurred.
+ */
+CURL_EXTERN char *curl_easy_escape(CURL *handle,
+                                   const char *string,
+                                   int length);
+
+/* the previous version: */
+CURL_EXTERN char *curl_escape(const char *string,
+                              int length);
+
+
+/*
+ * NAME curl_easy_unescape()
+ *
+ * DESCRIPTION
+ *
+ * Unescapes URL encoding in strings (converts all %XX codes to their 8bit
+ * versions). This function returns a new allocated string or NULL if an error
+ * occurred.
+ * Conversion Note: On non-ASCII platforms the ASCII %XX codes are
+ * converted into the host encoding.
+ */
+CURL_EXTERN char *curl_easy_unescape(CURL *handle,
+                                     const char *string,
+                                     int length,
+                                     int *outlength);
+
+/* the previous version */
+CURL_EXTERN char *curl_unescape(const char *string,
+                                int length);
+
+/*
+ * NAME curl_free()
+ *
+ * DESCRIPTION
+ *
+ * Provided for de-allocation in the same translation unit that did the
+ * allocation. Added in libcurl 7.10
+ */
+CURL_EXTERN void curl_free(void *p);
+
+/*
+ * NAME curl_global_init()
+ *
+ * DESCRIPTION
+ *
+ * curl_global_init() should be invoked exactly once for each application that
+ * uses libcurl and before any call of other libcurl functions.
+ *
+ * This function is not thread-safe!
+ */
+CURL_EXTERN CURLcode curl_global_init(long flags);
+
+/*
+ * NAME curl_global_init_mem()
+ *
+ * DESCRIPTION
+ *
+ * curl_global_init() or curl_global_init_mem() should be invoked exactly once
+ * for each application that uses libcurl.  This function can be used to
+ * initialize libcurl and set user defined memory management callback
+ * functions.  Users can implement memory management routines to check for
+ * memory leaks, check for mis-use of the curl library etc.  User registered
+ * callback routines with be invoked by this library instead of the system
+ * memory management routines like malloc, free etc.
+ */
+CURL_EXTERN CURLcode curl_global_init_mem(long flags,
+                                          curl_malloc_callback m,
+                                          curl_free_callback f,
+                                          curl_realloc_callback r,
+                                          curl_strdup_callback s,
+                                          curl_calloc_callback c);
+
+/*
+ * NAME curl_global_cleanup()
+ *
+ * DESCRIPTION
+ *
+ * curl_global_cleanup() should be invoked exactly once for each application
+ * that uses libcurl
+ */
+CURL_EXTERN void curl_global_cleanup(void);
+
+/* linked-list structure for the CURLOPT_QUOTE option (and other) */
+struct curl_slist {
+  char *data;
+  struct curl_slist *next;
+};
+
+/*
+ * NAME curl_global_sslset()
+ *
+ * DESCRIPTION
+ *
+ * When built with multiple SSL backends, curl_global_sslset() allows to
+ * choose one. This function can only be called once, and it must be called
+ * *before* curl_global_init().
+ *
+ * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The
+ * backend can also be specified via the name parameter (passing -1 as id).
+ * If both id and name are specified, the name will be ignored. If neither id
+ * nor name are specified, the function will fail with
+ * CURLSSLSET_UNKNOWN_BACKEND and set the "avail" pointer to the
+ * NULL-terminated list of available backends.
+ *
+ * Upon success, the function returns CURLSSLSET_OK.
+ *
+ * If the specified SSL backend is not available, the function returns
+ * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a NULL-terminated
+ * list of available SSL backends.
+ *
+ * The SSL backend can be set only once. If it has already been set, a
+ * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE.
+ */
+
+typedef struct {
+  curl_sslbackend id;
+  const char *name;
+} curl_ssl_backend;
+
+typedef enum {
+  CURLSSLSET_OK = 0,
+  CURLSSLSET_UNKNOWN_BACKEND,
+  CURLSSLSET_TOO_LATE,
+  CURLSSLSET_NO_BACKENDS /* libcurl was built without any SSL support */
+} CURLsslset;
+
+CURL_EXTERN CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
+                                          const curl_ssl_backend ***avail);
+
+/*
+ * NAME curl_slist_append()
+ *
+ * DESCRIPTION
+ *
+ * Appends a string to a linked list. If no list exists, it will be created
+ * first. Returns the new list, after appending.
+ */
+CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *,
+                                                 const char *);
+
+/*
+ * NAME curl_slist_free_all()
+ *
+ * DESCRIPTION
+ *
+ * free a previously built curl_slist.
+ */
+CURL_EXTERN void curl_slist_free_all(struct curl_slist *);
+
+/*
+ * NAME curl_getdate()
+ *
+ * DESCRIPTION
+ *
+ * Returns the time, in seconds since 1 Jan 1970 of the time string given in
+ * the first argument. The time argument in the second parameter is unused
+ * and should be set to NULL.
+ */
+CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused);
+
+/* info about the certificate chain, only for OpenSSL builds. Asked
+   for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */
+struct curl_certinfo {
+  int num_of_certs;             /* number of certificates with information */
+  struct curl_slist **certinfo; /* for each index in this array, there's a
+                                   linked list with textual information in the
+                                   format "name: value" */
+};
+
+/* Information about the SSL library used and the respective internal SSL
+   handle, which can be used to obtain further information regarding the
+   connection. Asked for with CURLINFO_TLS_SSL_PTR or CURLINFO_TLS_SESSION. */
+struct curl_tlssessioninfo {
+  curl_sslbackend backend;
+  void *internals;
+};
+
+#define CURLINFO_STRING   0x100000
+#define CURLINFO_LONG     0x200000
+#define CURLINFO_DOUBLE   0x300000
+#define CURLINFO_SLIST    0x400000
+#define CURLINFO_PTR      0x400000 /* same as SLIST */
+#define CURLINFO_SOCKET   0x500000
+#define CURLINFO_OFF_T    0x600000
+#define CURLINFO_MASK     0x0fffff
+#define CURLINFO_TYPEMASK 0xf00000
+
+typedef enum {
+  CURLINFO_NONE, /* first, never use this */
+  CURLINFO_EFFECTIVE_URL    = CURLINFO_STRING + 1,
+  CURLINFO_RESPONSE_CODE    = CURLINFO_LONG   + 2,
+  CURLINFO_TOTAL_TIME       = CURLINFO_DOUBLE + 3,
+  CURLINFO_NAMELOOKUP_TIME  = CURLINFO_DOUBLE + 4,
+  CURLINFO_CONNECT_TIME     = CURLINFO_DOUBLE + 5,
+  CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6,
+  CURLINFO_SIZE_UPLOAD      = CURLINFO_DOUBLE + 7,
+  CURLINFO_SIZE_UPLOAD_T    = CURLINFO_OFF_T  + 7,
+  CURLINFO_SIZE_DOWNLOAD    = CURLINFO_DOUBLE + 8,
+  CURLINFO_SIZE_DOWNLOAD_T  = CURLINFO_OFF_T  + 8,
+  CURLINFO_SPEED_DOWNLOAD   = CURLINFO_DOUBLE + 9,
+  CURLINFO_SPEED_DOWNLOAD_T = CURLINFO_OFF_T  + 9,
+  CURLINFO_SPEED_UPLOAD     = CURLINFO_DOUBLE + 10,
+  CURLINFO_SPEED_UPLOAD_T   = CURLINFO_OFF_T  + 10,
+  CURLINFO_HEADER_SIZE      = CURLINFO_LONG   + 11,
+  CURLINFO_REQUEST_SIZE     = CURLINFO_LONG   + 12,
+  CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG   + 13,
+  CURLINFO_FILETIME         = CURLINFO_LONG   + 14,
+  CURLINFO_CONTENT_LENGTH_DOWNLOAD   = CURLINFO_DOUBLE + 15,
+  CURLINFO_CONTENT_LENGTH_DOWNLOAD_T = CURLINFO_OFF_T  + 15,
+  CURLINFO_CONTENT_LENGTH_UPLOAD     = CURLINFO_DOUBLE + 16,
+  CURLINFO_CONTENT_LENGTH_UPLOAD_T   = CURLINFO_OFF_T  + 16,
+  CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17,
+  CURLINFO_CONTENT_TYPE     = CURLINFO_STRING + 18,
+  CURLINFO_REDIRECT_TIME    = CURLINFO_DOUBLE + 19,
+  CURLINFO_REDIRECT_COUNT   = CURLINFO_LONG   + 20,
+  CURLINFO_PRIVATE          = CURLINFO_STRING + 21,
+  CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG   + 22,
+  CURLINFO_HTTPAUTH_AVAIL   = CURLINFO_LONG   + 23,
+  CURLINFO_PROXYAUTH_AVAIL  = CURLINFO_LONG   + 24,
+  CURLINFO_OS_ERRNO         = CURLINFO_LONG   + 25,
+  CURLINFO_NUM_CONNECTS     = CURLINFO_LONG   + 26,
+  CURLINFO_SSL_ENGINES      = CURLINFO_SLIST  + 27,
+  CURLINFO_COOKIELIST       = CURLINFO_SLIST  + 28,
+  CURLINFO_LASTSOCKET       = CURLINFO_LONG   + 29,
+  CURLINFO_FTP_ENTRY_PATH   = CURLINFO_STRING + 30,
+  CURLINFO_REDIRECT_URL     = CURLINFO_STRING + 31,
+  CURLINFO_PRIMARY_IP       = CURLINFO_STRING + 32,
+  CURLINFO_APPCONNECT_TIME  = CURLINFO_DOUBLE + 33,
+  CURLINFO_CERTINFO         = CURLINFO_PTR    + 34,
+  CURLINFO_CONDITION_UNMET  = CURLINFO_LONG   + 35,
+  CURLINFO_RTSP_SESSION_ID  = CURLINFO_STRING + 36,
+  CURLINFO_RTSP_CLIENT_CSEQ = CURLINFO_LONG   + 37,
+  CURLINFO_RTSP_SERVER_CSEQ = CURLINFO_LONG   + 38,
+  CURLINFO_RTSP_CSEQ_RECV   = CURLINFO_LONG   + 39,
+  CURLINFO_PRIMARY_PORT     = CURLINFO_LONG   + 40,
+  CURLINFO_LOCAL_IP         = CURLINFO_STRING + 41,
+  CURLINFO_LOCAL_PORT       = CURLINFO_LONG   + 42,
+  CURLINFO_TLS_SESSION      = CURLINFO_PTR    + 43,
+  CURLINFO_ACTIVESOCKET     = CURLINFO_SOCKET + 44,
+  CURLINFO_TLS_SSL_PTR      = CURLINFO_PTR    + 45,
+  CURLINFO_HTTP_VERSION     = CURLINFO_LONG   + 46,
+  CURLINFO_PROXY_SSL_VERIFYRESULT = CURLINFO_LONG + 47,
+  CURLINFO_PROTOCOL         = CURLINFO_LONG   + 48,
+  CURLINFO_SCHEME           = CURLINFO_STRING + 49,
+  /* Fill in new entries below here! */
+
+  CURLINFO_LASTONE          = 49
+} CURLINFO;
+
+/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
+   CURLINFO_HTTP_CODE */
+#define CURLINFO_HTTP_CODE CURLINFO_RESPONSE_CODE
+
+typedef enum {
+  CURLCLOSEPOLICY_NONE, /* first, never use this */
+
+  CURLCLOSEPOLICY_OLDEST,
+  CURLCLOSEPOLICY_LEAST_RECENTLY_USED,
+  CURLCLOSEPOLICY_LEAST_TRAFFIC,
+  CURLCLOSEPOLICY_SLOWEST,
+  CURLCLOSEPOLICY_CALLBACK,
+
+  CURLCLOSEPOLICY_LAST /* last, never use this */
+} curl_closepolicy;
+
+#define CURL_GLOBAL_SSL (1<<0) /* no purpose since since 7.57.0 */
+#define CURL_GLOBAL_WIN32 (1<<1)
+#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32)
+#define CURL_GLOBAL_NOTHING 0
+#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL
+#define CURL_GLOBAL_ACK_EINTR (1<<2)
+
+
+/*****************************************************************************
+ * Setup defines, protos etc for the sharing stuff.
+ */
+
+/* Different data locks for a single share */
+typedef enum {
+  CURL_LOCK_DATA_NONE = 0,
+  /*  CURL_LOCK_DATA_SHARE is used internally to say that
+   *  the locking is just made to change the internal state of the share
+   *  itself.
+   */
+  CURL_LOCK_DATA_SHARE,
+  CURL_LOCK_DATA_COOKIE,
+  CURL_LOCK_DATA_DNS,
+  CURL_LOCK_DATA_SSL_SESSION,
+  CURL_LOCK_DATA_CONNECT,
+  CURL_LOCK_DATA_LAST
+} curl_lock_data;
+
+/* Different lock access types */
+typedef enum {
+  CURL_LOCK_ACCESS_NONE = 0,   /* unspecified action */
+  CURL_LOCK_ACCESS_SHARED = 1, /* for read perhaps */
+  CURL_LOCK_ACCESS_SINGLE = 2, /* for write perhaps */
+  CURL_LOCK_ACCESS_LAST        /* never use */
+} curl_lock_access;
+
+typedef void (*curl_lock_function)(CURL *handle,
+                                   curl_lock_data data,
+                                   curl_lock_access locktype,
+                                   void *userptr);
+typedef void (*curl_unlock_function)(CURL *handle,
+                                     curl_lock_data data,
+                                     void *userptr);
+
+
+typedef enum {
+  CURLSHE_OK,  /* all is fine */
+  CURLSHE_BAD_OPTION, /* 1 */
+  CURLSHE_IN_USE,     /* 2 */
+  CURLSHE_INVALID,    /* 3 */
+  CURLSHE_NOMEM,      /* 4 out of memory */
+  CURLSHE_NOT_BUILT_IN, /* 5 feature not present in lib */
+  CURLSHE_LAST        /* never use */
+} CURLSHcode;
+
+typedef enum {
+  CURLSHOPT_NONE,  /* don't use */
+  CURLSHOPT_SHARE,   /* specify a data type to share */
+  CURLSHOPT_UNSHARE, /* specify which data type to stop sharing */
+  CURLSHOPT_LOCKFUNC,   /* pass in a 'curl_lock_function' pointer */
+  CURLSHOPT_UNLOCKFUNC, /* pass in a 'curl_unlock_function' pointer */
+  CURLSHOPT_USERDATA,   /* pass in a user data pointer used in the lock/unlock
+                           callback functions */
+  CURLSHOPT_LAST  /* never use */
+} CURLSHoption;
+
+CURL_EXTERN CURLSH *curl_share_init(void);
+CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...);
+CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *);
+
+/****************************************************************************
+ * Structures for querying information about the curl library at runtime.
+ */
+
+typedef enum {
+  CURLVERSION_FIRST,
+  CURLVERSION_SECOND,
+  CURLVERSION_THIRD,
+  CURLVERSION_FOURTH,
+  CURLVERSION_FIFTH,
+  CURLVERSION_LAST /* never actually use this */
+} CURLversion;
+
+/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by
+   basically all programs ever that want to get version information. It is
+   meant to be a built-in version number for what kind of struct the caller
+   expects. If the struct ever changes, we redefine the NOW to another enum
+   from above. */
+#define CURLVERSION_NOW CURLVERSION_FIFTH
+
+typedef struct {
+  CURLversion age;          /* age of the returned struct */
+  const char *version;      /* LIBCURL_VERSION */
+  unsigned int version_num; /* LIBCURL_VERSION_NUM */
+  const char *host;         /* OS/host/cpu/machine when configured */
+  int features;             /* bitmask, see defines below */
+  const char *ssl_version;  /* human readable string */
+  long ssl_version_num;     /* not used anymore, always 0 */
+  const char *libz_version; /* human readable string */
+  /* protocols is terminated by an entry with a NULL protoname */
+  const char * const *protocols;
+
+  /* The fields below this were added in CURLVERSION_SECOND */
+  const char *ares;
+  int ares_num;
+
+  /* This field was added in CURLVERSION_THIRD */
+  const char *libidn;
+
+  /* These field were added in CURLVERSION_FOURTH */
+
+  /* Same as '_libiconv_version' if built with HAVE_ICONV */
+  int iconv_ver_num;
+
+  const char *libssh_version; /* human readable string */
+
+  /* These fields were added in CURLVERSION_FIFTH */
+
+  unsigned int brotli_ver_num; /* Numeric Brotli version
+                                  (MAJOR << 24) | (MINOR << 12) | PATCH */
+  const char *brotli_version; /* human readable string. */
+
+} curl_version_info_data;
+
+#define CURL_VERSION_IPV6         (1<<0)  /* IPv6-enabled */
+#define CURL_VERSION_KERBEROS4    (1<<1)  /* Kerberos V4 auth is supported
+                                             (deprecated) */
+#define CURL_VERSION_SSL          (1<<2)  /* SSL options are present */
+#define CURL_VERSION_LIBZ         (1<<3)  /* libz features are present */
+#define CURL_VERSION_NTLM         (1<<4)  /* NTLM auth is supported */
+#define CURL_VERSION_GSSNEGOTIATE (1<<5)  /* Negotiate auth is supported
+                                             (deprecated) */
+#define CURL_VERSION_DEBUG        (1<<6)  /* Built with debug capabilities */
+#define CURL_VERSION_ASYNCHDNS    (1<<7)  /* Asynchronous DNS resolves */
+#define CURL_VERSION_SPNEGO       (1<<8)  /* SPNEGO auth is supported */
+#define CURL_VERSION_LARGEFILE    (1<<9)  /* Supports files larger than 2GB */
+#define CURL_VERSION_IDN          (1<<10) /* Internationized Domain Names are
+                                             supported */
+#define CURL_VERSION_SSPI         (1<<11) /* Built against Windows SSPI */
+#define CURL_VERSION_CONV         (1<<12) /* Character conversions supported */
+#define CURL_VERSION_CURLDEBUG    (1<<13) /* Debug memory tracking supported */
+#define CURL_VERSION_TLSAUTH_SRP  (1<<14) /* TLS-SRP auth is supported */
+#define CURL_VERSION_NTLM_WB      (1<<15) /* NTLM delegation to winbind helper
+                                             is supported */
+#define CURL_VERSION_HTTP2        (1<<16) /* HTTP2 support built-in */
+#define CURL_VERSION_GSSAPI       (1<<17) /* Built against a GSS-API library */
+#define CURL_VERSION_KERBEROS5    (1<<18) /* Kerberos V5 auth is supported */
+#define CURL_VERSION_UNIX_SOCKETS (1<<19) /* Unix domain sockets support */
+#define CURL_VERSION_PSL          (1<<20) /* Mozilla's Public Suffix List, used
+                                             for cookie domain verification */
+#define CURL_VERSION_HTTPS_PROXY  (1<<21) /* HTTPS-proxy support built-in */
+#define CURL_VERSION_MULTI_SSL    (1<<22) /* Multiple SSL backends available */
+#define CURL_VERSION_BROTLI       (1<<23) /* Brotli features are present. */
+
+ /*
+ * NAME curl_version_info()
+ *
+ * DESCRIPTION
+ *
+ * This function returns a pointer to a static copy of the version info
+ * struct. See above.
+ */
+CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion);
+
+/*
+ * NAME curl_easy_strerror()
+ *
+ * DESCRIPTION
+ *
+ * The curl_easy_strerror function may be used to turn a CURLcode value
+ * into the equivalent human readable error string.  This is useful
+ * for printing meaningful error messages.
+ */
+CURL_EXTERN const char *curl_easy_strerror(CURLcode);
+
+/*
+ * NAME curl_share_strerror()
+ *
+ * DESCRIPTION
+ *
+ * The curl_share_strerror function may be used to turn a CURLSHcode value
+ * into the equivalent human readable error string.  This is useful
+ * for printing meaningful error messages.
+ */
+CURL_EXTERN const char *curl_share_strerror(CURLSHcode);
+
+/*
+ * NAME curl_easy_pause()
+ *
+ * DESCRIPTION
+ *
+ * The curl_easy_pause function pauses or unpauses transfers. Select the new
+ * state by setting the bitmask, use the convenience defines below.
+ *
+ */
+CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);
+
+#define CURLPAUSE_RECV      (1<<0)
+#define CURLPAUSE_RECV_CONT (0)
+
+#define CURLPAUSE_SEND      (1<<2)
+#define CURLPAUSE_SEND_CONT (0)
+
+#define CURLPAUSE_ALL       (CURLPAUSE_RECV|CURLPAUSE_SEND)
+#define CURLPAUSE_CONT      (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT)
+
+#ifdef  __cplusplus
+}
+#endif
+
+/* unfortunately, the easy.h and multi.h include files need options and info
+  stuff before they can be included! */
+#include "easy.h" /* nothing in curl is fun without the easy stuff */
+#include "multi.h"
+
+/* the typechecker doesn't work in C++ (yet) */
+#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \
+    ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \
+    !defined(__cplusplus) && !defined(CURL_DISABLE_TYPECHECK)
+#include "typecheck-gcc.h"
+#else
+#if defined(__STDC__) && (__STDC__ >= 1)
+/* This preprocessor magic that replaces a call with the exact same call is
+   only done to make sure application authors pass exactly three arguments
+   to these functions. */
+#define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param)
+#define curl_easy_getinfo(handle,info,arg) curl_easy_getinfo(handle,info,arg)
+#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param)
+#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param)
+#endif /* __STDC__ >= 1 */
+#endif /* gcc >= 4.3 && !__cplusplus */
+
+#endif /* __CURL_CURL_H */

+ 77 - 0
ext/curl-7.58.0/x64/include/curl/curlver.h

@@ -0,0 +1,77 @@
+#ifndef __CURL_CURLVER_H
+#define __CURL_CURLVER_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/* This header file contains nothing but libcurl version info, generated by
+   a script at release-time. This was made its own header file in 7.11.2 */
+
+/* This is the global package copyright */
+#define LIBCURL_COPYRIGHT "1996 - 2017 Daniel Stenberg, <[email protected]>."
+
+/* This is the version number of the libcurl package from which this header
+   file origins: */
+#define LIBCURL_VERSION "7.58.0"
+
+/* The numeric version number is also available "in parts" by using these
+   defines: */
+#define LIBCURL_VERSION_MAJOR 7
+#define LIBCURL_VERSION_MINOR 58
+#define LIBCURL_VERSION_PATCH 0
+
+/* This is the numeric version of the libcurl version number, meant for easier
+   parsing and comparions by programs. The LIBCURL_VERSION_NUM define will
+   always follow this syntax:
+
+         0xXXYYZZ
+
+   Where XX, YY and ZZ are the main version, release and patch numbers in
+   hexadecimal (using 8 bits each). All three numbers are always represented
+   using two digits.  1.2 would appear as "0x010200" while version 9.11.7
+   appears as "0x090b07".
+
+   This 6-digit (24 bits) hexadecimal number does not show pre-release number,
+   and it is always a greater number in a more recent release. It makes
+   comparisons with greater than and less than work.
+
+   Note: This define is the full hex number and _does not_ use the
+   CURL_VERSION_BITS() macro since curl's own configure script greps for it
+   and needs it to contain the full number.
+*/
+#define LIBCURL_VERSION_NUM 0x073a00
+
+/*
+ * This is the date and time when the full source package was created. The
+ * timestamp is not stored in git, as the timestamp is properly set in the
+ * tarballs by the maketgz script.
+ *
+ * The format of the date follows this template:
+ *
+ * "2007-11-23"
+ */
+#define LIBCURL_TIMESTAMP "2018-01-24"
+
+#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|z)
+#define CURL_AT_LEAST_VERSION(x,y,z) \
+  (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
+
+#endif /* __CURL_CURLVER_H */

+ 102 - 0
ext/curl-7.58.0/x64/include/curl/easy.h

@@ -0,0 +1,102 @@
+#ifndef __CURL_EASY_H
+#define __CURL_EASY_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2016, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+CURL_EXTERN CURL *curl_easy_init(void);
+CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);
+CURL_EXTERN CURLcode curl_easy_perform(CURL *curl);
+CURL_EXTERN void curl_easy_cleanup(CURL *curl);
+
+/*
+ * NAME curl_easy_getinfo()
+ *
+ * DESCRIPTION
+ *
+ * Request internal information from the curl session with this function.  The
+ * third argument MUST be a pointer to a long, a pointer to a char * or a
+ * pointer to a double (as the documentation describes elsewhere).  The data
+ * pointed to will be filled in accordingly and can be relied upon only if the
+ * function returns CURLE_OK.  This function is intended to get used *AFTER* a
+ * performed transfer, all results from this function are undefined until the
+ * transfer is completed.
+ */
+CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...);
+
+
+/*
+ * NAME curl_easy_duphandle()
+ *
+ * DESCRIPTION
+ *
+ * Creates a new curl session handle with the same options set for the handle
+ * passed in. Duplicating a handle could only be a matter of cloning data and
+ * options, internal state info and things like persistent connections cannot
+ * be transferred. It is useful in multithreaded applications when you can run
+ * curl_easy_duphandle() for each new thread to avoid a series of identical
+ * curl_easy_setopt() invokes in every thread.
+ */
+CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl);
+
+/*
+ * NAME curl_easy_reset()
+ *
+ * DESCRIPTION
+ *
+ * Re-initializes a CURL handle to the default values. This puts back the
+ * handle to the same state as it was in when it was just created.
+ *
+ * It does keep: live connections, the Session ID cache, the DNS cache and the
+ * cookies.
+ */
+CURL_EXTERN void curl_easy_reset(CURL *curl);
+
+/*
+ * NAME curl_easy_recv()
+ *
+ * DESCRIPTION
+ *
+ * Receives data from the connected socket. Use after successful
+ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
+ */
+CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen,
+                                    size_t *n);
+
+/*
+ * NAME curl_easy_send()
+ *
+ * DESCRIPTION
+ *
+ * Sends data over the connected socket. Use after successful
+ * curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
+ */
+CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer,
+                                    size_t buflen, size_t *n);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif

+ 50 - 0
ext/curl-7.58.0/x64/include/curl/mprintf.h

@@ -0,0 +1,50 @@
+#ifndef __CURL_MPRINTF_H
+#define __CURL_MPRINTF_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2016, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include <stdarg.h>
+#include <stdio.h> /* needed for FILE */
+#include "curl.h"  /* for CURL_EXTERN */
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+CURL_EXTERN int curl_mprintf(const char *format, ...);
+CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...);
+CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...);
+CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength,
+                               const char *format, ...);
+CURL_EXTERN int curl_mvprintf(const char *format, va_list args);
+CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args);
+CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args);
+CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength,
+                                const char *format, va_list args);
+CURL_EXTERN char *curl_maprintf(const char *format, ...);
+CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* __CURL_MPRINTF_H */

+ 439 - 0
ext/curl-7.58.0/x64/include/curl/multi.h

@@ -0,0 +1,439 @@
+#ifndef __CURL_MULTI_H
+#define __CURL_MULTI_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+/*
+  This is an "external" header file. Don't give away any internals here!
+
+  GOALS
+
+  o Enable a "pull" interface. The application that uses libcurl decides where
+    and when to ask libcurl to get/send data.
+
+  o Enable multiple simultaneous transfers in the same thread without making it
+    complicated for the application.
+
+  o Enable the application to select() on its own file descriptors and curl's
+    file descriptors simultaneous easily.
+
+*/
+
+/*
+ * This header file should not really need to include "curl.h" since curl.h
+ * itself includes this file and we expect user applications to do #include
+ * <curl/curl.h> without the need for especially including multi.h.
+ *
+ * For some reason we added this include here at one point, and rather than to
+ * break existing (wrongly written) libcurl applications, we leave it as-is
+ * but with this warning attached.
+ */
+#include "curl.h"
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER)
+typedef struct Curl_multi CURLM;
+#else
+typedef void CURLM;
+#endif
+
+typedef enum {
+  CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or
+                                    curl_multi_socket*() soon */
+  CURLM_OK,
+  CURLM_BAD_HANDLE,      /* the passed-in handle is not a valid CURLM handle */
+  CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */
+  CURLM_OUT_OF_MEMORY,   /* if you ever get this, you're in deep sh*t */
+  CURLM_INTERNAL_ERROR,  /* this is a libcurl bug */
+  CURLM_BAD_SOCKET,      /* the passed in socket argument did not match */
+  CURLM_UNKNOWN_OPTION,  /* curl_multi_setopt() with unsupported option */
+  CURLM_ADDED_ALREADY,   /* an easy handle already added to a multi handle was
+                            attempted to get added - again */
+  CURLM_LAST
+} CURLMcode;
+
+/* just to make code nicer when using curl_multi_socket() you can now check
+   for CURLM_CALL_MULTI_SOCKET too in the same style it works for
+   curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */
+#define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM
+
+/* bitmask bits for CURLMOPT_PIPELINING */
+#define CURLPIPE_NOTHING   0L
+#define CURLPIPE_HTTP1     1L
+#define CURLPIPE_MULTIPLEX 2L
+
+typedef enum {
+  CURLMSG_NONE, /* first, not used */
+  CURLMSG_DONE, /* This easy handle has completed. 'result' contains
+                   the CURLcode of the transfer */
+  CURLMSG_LAST /* last, not used */
+} CURLMSG;
+
+struct CURLMsg {
+  CURLMSG msg;       /* what this message means */
+  CURL *easy_handle; /* the handle it concerns */
+  union {
+    void *whatever;    /* message-specific data */
+    CURLcode result;   /* return code for transfer */
+  } data;
+};
+typedef struct CURLMsg CURLMsg;
+
+/* Based on poll(2) structure and values.
+ * We don't use pollfd and POLL* constants explicitly
+ * to cover platforms without poll(). */
+#define CURL_WAIT_POLLIN    0x0001
+#define CURL_WAIT_POLLPRI   0x0002
+#define CURL_WAIT_POLLOUT   0x0004
+
+struct curl_waitfd {
+  curl_socket_t fd;
+  short events;
+  short revents; /* not supported yet */
+};
+
+/*
+ * Name:    curl_multi_init()
+ *
+ * Desc:    inititalize multi-style curl usage
+ *
+ * Returns: a new CURLM handle to use in all 'curl_multi' functions.
+ */
+CURL_EXTERN CURLM *curl_multi_init(void);
+
+/*
+ * Name:    curl_multi_add_handle()
+ *
+ * Desc:    add a standard curl handle to the multi stack
+ *
+ * Returns: CURLMcode type, general multi error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle,
+                                            CURL *curl_handle);
+
+ /*
+  * Name:    curl_multi_remove_handle()
+  *
+  * Desc:    removes a curl handle from the multi stack again
+  *
+  * Returns: CURLMcode type, general multi error code.
+  */
+CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
+                                               CURL *curl_handle);
+
+ /*
+  * Name:    curl_multi_fdset()
+  *
+  * Desc:    Ask curl for its fd_set sets. The app can use these to select() or
+  *          poll() on. We want curl_multi_perform() called as soon as one of
+  *          them are ready.
+  *
+  * Returns: CURLMcode type, general multi error code.
+  */
+CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle,
+                                       fd_set *read_fd_set,
+                                       fd_set *write_fd_set,
+                                       fd_set *exc_fd_set,
+                                       int *max_fd);
+
+/*
+ * Name:     curl_multi_wait()
+ *
+ * Desc:     Poll on all fds within a CURLM set as well as any
+ *           additional fds passed to the function.
+ *
+ * Returns:  CURLMcode type, general multi error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle,
+                                      struct curl_waitfd extra_fds[],
+                                      unsigned int extra_nfds,
+                                      int timeout_ms,
+                                      int *ret);
+
+ /*
+  * Name:    curl_multi_perform()
+  *
+  * Desc:    When the app thinks there's data available for curl it calls this
+  *          function to read/write whatever there is right now. This returns
+  *          as soon as the reads and writes are done. This function does not
+  *          require that there actually is data available for reading or that
+  *          data can be written, it can be called just in case. It returns
+  *          the number of handles that still transfer data in the second
+  *          argument's integer-pointer.
+  *
+  * Returns: CURLMcode type, general multi error code. *NOTE* that this only
+  *          returns errors etc regarding the whole multi stack. There might
+  *          still have occurred problems on invidual transfers even when this
+  *          returns OK.
+  */
+CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle,
+                                         int *running_handles);
+
+ /*
+  * Name:    curl_multi_cleanup()
+  *
+  * Desc:    Cleans up and removes a whole multi stack. It does not free or
+  *          touch any individual easy handles in any way. We need to define
+  *          in what state those handles will be if this function is called
+  *          in the middle of a transfer.
+  *
+  * Returns: CURLMcode type, general multi error code.
+  */
+CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle);
+
+/*
+ * Name:    curl_multi_info_read()
+ *
+ * Desc:    Ask the multi handle if there's any messages/informationals from
+ *          the individual transfers. Messages include informationals such as
+ *          error code from the transfer or just the fact that a transfer is
+ *          completed. More details on these should be written down as well.
+ *
+ *          Repeated calls to this function will return a new struct each
+ *          time, until a special "end of msgs" struct is returned as a signal
+ *          that there is no more to get at this point.
+ *
+ *          The data the returned pointer points to will not survive calling
+ *          curl_multi_cleanup().
+ *
+ *          The 'CURLMsg' struct is meant to be very simple and only contain
+ *          very basic information. If more involved information is wanted,
+ *          we will provide the particular "transfer handle" in that struct
+ *          and that should/could/would be used in subsequent
+ *          curl_easy_getinfo() calls (or similar). The point being that we
+ *          must never expose complex structs to applications, as then we'll
+ *          undoubtably get backwards compatibility problems in the future.
+ *
+ * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out
+ *          of structs. It also writes the number of messages left in the
+ *          queue (after this read) in the integer the second argument points
+ *          to.
+ */
+CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle,
+                                          int *msgs_in_queue);
+
+/*
+ * Name:    curl_multi_strerror()
+ *
+ * Desc:    The curl_multi_strerror function may be used to turn a CURLMcode
+ *          value into the equivalent human readable error string.  This is
+ *          useful for printing meaningful error messages.
+ *
+ * Returns: A pointer to a zero-terminated error message.
+ */
+CURL_EXTERN const char *curl_multi_strerror(CURLMcode);
+
+/*
+ * Name:    curl_multi_socket() and
+ *          curl_multi_socket_all()
+ *
+ * Desc:    An alternative version of curl_multi_perform() that allows the
+ *          application to pass in one of the file descriptors that have been
+ *          detected to have "action" on them and let libcurl perform.
+ *          See man page for details.
+ */
+#define CURL_POLL_NONE   0
+#define CURL_POLL_IN     1
+#define CURL_POLL_OUT    2
+#define CURL_POLL_INOUT  3
+#define CURL_POLL_REMOVE 4
+
+#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD
+
+#define CURL_CSELECT_IN   0x01
+#define CURL_CSELECT_OUT  0x02
+#define CURL_CSELECT_ERR  0x04
+
+typedef int (*curl_socket_callback)(CURL *easy,      /* easy handle */
+                                    curl_socket_t s, /* socket */
+                                    int what,        /* see above */
+                                    void *userp,     /* private callback
+                                                        pointer */
+                                    void *socketp);  /* private socket
+                                                        pointer */
+/*
+ * Name:    curl_multi_timer_callback
+ *
+ * Desc:    Called by libcurl whenever the library detects a change in the
+ *          maximum number of milliseconds the app is allowed to wait before
+ *          curl_multi_socket() or curl_multi_perform() must be called
+ *          (to allow libcurl's timed events to take place).
+ *
+ * Returns: The callback should return zero.
+ */
+typedef int (*curl_multi_timer_callback)(CURLM *multi,    /* multi handle */
+                                         long timeout_ms, /* see above */
+                                         void *userp);    /* private callback
+                                                             pointer */
+
+CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,
+                                        int *running_handles);
+
+CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle,
+                                               curl_socket_t s,
+                                               int ev_bitmask,
+                                               int *running_handles);
+
+CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle,
+                                            int *running_handles);
+
+#ifndef CURL_ALLOW_OLD_MULTI_SOCKET
+/* This macro below was added in 7.16.3 to push users who recompile to use
+   the new curl_multi_socket_action() instead of the old curl_multi_socket()
+*/
+#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z)
+#endif
+
+/*
+ * Name:    curl_multi_timeout()
+ *
+ * Desc:    Returns the maximum number of milliseconds the app is allowed to
+ *          wait before curl_multi_socket() or curl_multi_perform() must be
+ *          called (to allow libcurl's timed events to take place).
+ *
+ * Returns: CURLM error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle,
+                                         long *milliseconds);
+
+#undef CINIT /* re-using the same name as in curl.h */
+
+#ifdef CURL_ISOCPP
+#define CINIT(name,type,num) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + num
+#else
+/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */
+#define LONG          CURLOPTTYPE_LONG
+#define OBJECTPOINT   CURLOPTTYPE_OBJECTPOINT
+#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT
+#define OFF_T         CURLOPTTYPE_OFF_T
+#define CINIT(name,type,number) CURLMOPT_/**/name = type + number
+#endif
+
+typedef enum {
+  /* This is the socket callback function pointer */
+  CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1),
+
+  /* This is the argument passed to the socket callback */
+  CINIT(SOCKETDATA, OBJECTPOINT, 2),
+
+    /* set to 1 to enable pipelining for this multi handle */
+  CINIT(PIPELINING, LONG, 3),
+
+   /* This is the timer callback function pointer */
+  CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4),
+
+  /* This is the argument passed to the timer callback */
+  CINIT(TIMERDATA, OBJECTPOINT, 5),
+
+  /* maximum number of entries in the connection cache */
+  CINIT(MAXCONNECTS, LONG, 6),
+
+  /* maximum number of (pipelining) connections to one host */
+  CINIT(MAX_HOST_CONNECTIONS, LONG, 7),
+
+  /* maximum number of requests in a pipeline */
+  CINIT(MAX_PIPELINE_LENGTH, LONG, 8),
+
+  /* a connection with a content-length longer than this
+     will not be considered for pipelining */
+  CINIT(CONTENT_LENGTH_PENALTY_SIZE, OFF_T, 9),
+
+  /* a connection with a chunk length longer than this
+     will not be considered for pipelining */
+  CINIT(CHUNK_LENGTH_PENALTY_SIZE, OFF_T, 10),
+
+  /* a list of site names(+port) that are blacklisted from
+     pipelining */
+  CINIT(PIPELINING_SITE_BL, OBJECTPOINT, 11),
+
+  /* a list of server types that are blacklisted from
+     pipelining */
+  CINIT(PIPELINING_SERVER_BL, OBJECTPOINT, 12),
+
+  /* maximum number of open connections in total */
+  CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13),
+
+   /* This is the server push callback function pointer */
+  CINIT(PUSHFUNCTION, FUNCTIONPOINT, 14),
+
+  /* This is the argument passed to the server push callback */
+  CINIT(PUSHDATA, OBJECTPOINT, 15),
+
+  CURLMOPT_LASTENTRY /* the last unused */
+} CURLMoption;
+
+
+/*
+ * Name:    curl_multi_setopt()
+ *
+ * Desc:    Sets options for the multi handle.
+ *
+ * Returns: CURLM error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle,
+                                        CURLMoption option, ...);
+
+
+/*
+ * Name:    curl_multi_assign()
+ *
+ * Desc:    This function sets an association in the multi handle between the
+ *          given socket and a private pointer of the application. This is
+ *          (only) useful for curl_multi_socket uses.
+ *
+ * Returns: CURLM error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle,
+                                        curl_socket_t sockfd, void *sockp);
+
+
+/*
+ * Name: curl_push_callback
+ *
+ * Desc: This callback gets called when a new stream is being pushed by the
+ *       server. It approves or denies the new stream.
+ *
+ * Returns: CURL_PUSH_OK or CURL_PUSH_DENY.
+ */
+#define CURL_PUSH_OK   0
+#define CURL_PUSH_DENY 1
+
+struct curl_pushheaders;  /* forward declaration only */
+
+CURL_EXTERN char *curl_pushheader_bynum(struct curl_pushheaders *h,
+                                        size_t num);
+CURL_EXTERN char *curl_pushheader_byname(struct curl_pushheaders *h,
+                                         const char *name);
+
+typedef int (*curl_push_callback)(CURL *parent,
+                                  CURL *easy,
+                                  size_t num_headers,
+                                  struct curl_pushheaders *headers,
+                                  void *userp);
+
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif
+
+#endif

+ 33 - 0
ext/curl-7.58.0/x64/include/curl/stdcheaders.h

@@ -0,0 +1,33 @@
+#ifndef __STDC_HEADERS_H
+#define __STDC_HEADERS_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2016, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include <sys/types.h>
+
+size_t fread(void *, size_t, size_t, FILE *);
+size_t fwrite(const void *, size_t, size_t, FILE *);
+
+int strcasecmp(const char *, const char *);
+int strncasecmp(const char *, const char *, size_t);
+
+#endif /* __STDC_HEADERS_H */

+ 473 - 0
ext/curl-7.58.0/x64/include/curl/system.h

@@ -0,0 +1,473 @@
+#ifndef __CURL_SYSTEM_H
+#define __CURL_SYSTEM_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/*
+ * Try to keep one section per platform, compiler and architecture, otherwise,
+ * if an existing section is reused for a different one and later on the
+ * original is adjusted, probably the piggybacking one can be adversely
+ * changed.
+ *
+ * In order to differentiate between platforms/compilers/architectures use
+ * only compiler built in predefined preprocessor symbols.
+ *
+ * curl_off_t
+ * ----------
+ *
+ * For any given platform/compiler curl_off_t must be typedef'ed to a 64-bit
+ * wide signed integral data type. The width of this data type must remain
+ * constant and independent of any possible large file support settings.
+ *
+ * As an exception to the above, curl_off_t shall be typedef'ed to a 32-bit
+ * wide signed integral data type if there is no 64-bit type.
+ *
+ * As a general rule, curl_off_t shall not be mapped to off_t. This rule shall
+ * only be violated if off_t is the only 64-bit data type available and the
+ * size of off_t is independent of large file support settings. Keep your
+ * build on the safe side avoiding an off_t gating.  If you have a 64-bit
+ * off_t then take for sure that another 64-bit data type exists, dig deeper
+ * and you will find it.
+ *
+ */
+
+#if defined(__DJGPP__) || defined(__GO32__)
+#  if defined(__DJGPP__) && (__DJGPP__ > 1)
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__SALFORDC__)
+#  define CURL_TYPEOF_CURL_OFF_T     long
+#  define CURL_FORMAT_CURL_OFF_T     "ld"
+#  define CURL_FORMAT_CURL_OFF_TU    "lu"
+#  define CURL_SUFFIX_CURL_OFF_T     L
+#  define CURL_SUFFIX_CURL_OFF_TU    UL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__BORLANDC__)
+#  if (__BORLANDC__ < 0x520)
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     "I64d"
+#    define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#    define CURL_SUFFIX_CURL_OFF_T     i64
+#    define CURL_SUFFIX_CURL_OFF_TU    ui64
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__TURBOC__)
+#  define CURL_TYPEOF_CURL_OFF_T     long
+#  define CURL_FORMAT_CURL_OFF_T     "ld"
+#  define CURL_FORMAT_CURL_OFF_TU    "lu"
+#  define CURL_SUFFIX_CURL_OFF_T     L
+#  define CURL_SUFFIX_CURL_OFF_TU    UL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__WATCOMC__)
+#  if defined(__386__)
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     "I64d"
+#    define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#    define CURL_SUFFIX_CURL_OFF_T     i64
+#    define CURL_SUFFIX_CURL_OFF_TU    ui64
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__POCC__)
+#  if (__POCC__ < 280)
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  elif defined(_MSC_VER)
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     "I64d"
+#    define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#    define CURL_SUFFIX_CURL_OFF_T     i64
+#    define CURL_SUFFIX_CURL_OFF_TU    ui64
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__LCC__)
+#  define CURL_TYPEOF_CURL_OFF_T     long
+#  define CURL_FORMAT_CURL_OFF_T     "ld"
+#  define CURL_FORMAT_CURL_OFF_TU    "lu"
+#  define CURL_SUFFIX_CURL_OFF_T     L
+#  define CURL_SUFFIX_CURL_OFF_TU    UL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__SYMBIAN32__)
+#  if defined(__EABI__)  /* Treat all ARM compilers equally */
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  elif defined(__CW32__)
+#    pragma longlong on
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  elif defined(__VC32__)
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
+
+#elif defined(__MWERKS__)
+#  define CURL_TYPEOF_CURL_OFF_T     long long
+#  define CURL_FORMAT_CURL_OFF_T     "lld"
+#  define CURL_FORMAT_CURL_OFF_TU    "llu"
+#  define CURL_SUFFIX_CURL_OFF_T     LL
+#  define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(_WIN32_WCE)
+#  define CURL_TYPEOF_CURL_OFF_T     __int64
+#  define CURL_FORMAT_CURL_OFF_T     "I64d"
+#  define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#  define CURL_SUFFIX_CURL_OFF_T     i64
+#  define CURL_SUFFIX_CURL_OFF_TU    ui64
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__MINGW32__)
+#  define CURL_TYPEOF_CURL_OFF_T     long long
+#  define CURL_FORMAT_CURL_OFF_T     "I64d"
+#  define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#  define CURL_SUFFIX_CURL_OFF_T     LL
+#  define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#  define CURL_PULL_SYS_TYPES_H      1
+#  define CURL_PULL_WS2TCPIP_H       1
+
+#elif defined(__VMS)
+#  if defined(__VAX)
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
+
+#elif defined(__OS400__)
+#  if defined(__ILEC400__)
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#    define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#    define CURL_PULL_SYS_TYPES_H      1
+#    define CURL_PULL_SYS_SOCKET_H     1
+#  endif
+
+#elif defined(__MVS__)
+#  if defined(__IBMC__) || defined(__IBMCPP__)
+#    if defined(_ILP32)
+#    elif defined(_LP64)
+#    endif
+#    if defined(_LONG_LONG)
+#      define CURL_TYPEOF_CURL_OFF_T     long long
+#      define CURL_FORMAT_CURL_OFF_T     "lld"
+#      define CURL_FORMAT_CURL_OFF_TU    "llu"
+#      define CURL_SUFFIX_CURL_OFF_T     LL
+#      define CURL_SUFFIX_CURL_OFF_TU    ULL
+#    elif defined(_LP64)
+#      define CURL_TYPEOF_CURL_OFF_T     long
+#      define CURL_FORMAT_CURL_OFF_T     "ld"
+#      define CURL_FORMAT_CURL_OFF_TU    "lu"
+#      define CURL_SUFFIX_CURL_OFF_T     L
+#      define CURL_SUFFIX_CURL_OFF_TU    UL
+#    else
+#      define CURL_TYPEOF_CURL_OFF_T     long
+#      define CURL_FORMAT_CURL_OFF_T     "ld"
+#      define CURL_FORMAT_CURL_OFF_TU    "lu"
+#      define CURL_SUFFIX_CURL_OFF_T     L
+#      define CURL_SUFFIX_CURL_OFF_TU    UL
+#    endif
+#    define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#    define CURL_PULL_SYS_TYPES_H      1
+#    define CURL_PULL_SYS_SOCKET_H     1
+#  endif
+
+#elif defined(__370__)
+#  if defined(__IBMC__) || defined(__IBMCPP__)
+#    if defined(_ILP32)
+#    elif defined(_LP64)
+#    endif
+#    if defined(_LONG_LONG)
+#      define CURL_TYPEOF_CURL_OFF_T     long long
+#      define CURL_FORMAT_CURL_OFF_T     "lld"
+#      define CURL_FORMAT_CURL_OFF_TU    "llu"
+#      define CURL_SUFFIX_CURL_OFF_T     LL
+#      define CURL_SUFFIX_CURL_OFF_TU    ULL
+#    elif defined(_LP64)
+#      define CURL_TYPEOF_CURL_OFF_T     long
+#      define CURL_FORMAT_CURL_OFF_T     "ld"
+#      define CURL_FORMAT_CURL_OFF_TU    "lu"
+#      define CURL_SUFFIX_CURL_OFF_T     L
+#      define CURL_SUFFIX_CURL_OFF_TU    UL
+#    else
+#      define CURL_TYPEOF_CURL_OFF_T     long
+#      define CURL_FORMAT_CURL_OFF_T     "ld"
+#      define CURL_FORMAT_CURL_OFF_TU    "lu"
+#      define CURL_SUFFIX_CURL_OFF_T     L
+#      define CURL_SUFFIX_CURL_OFF_TU    UL
+#    endif
+#    define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#    define CURL_PULL_SYS_TYPES_H      1
+#    define CURL_PULL_SYS_SOCKET_H     1
+#  endif
+
+#elif defined(TPF)
+#  define CURL_TYPEOF_CURL_OFF_T     long
+#  define CURL_FORMAT_CURL_OFF_T     "ld"
+#  define CURL_FORMAT_CURL_OFF_TU    "lu"
+#  define CURL_SUFFIX_CURL_OFF_T     L
+#  define CURL_SUFFIX_CURL_OFF_TU    UL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+#elif defined(__TINYC__) /* also known as tcc */
+
+#  define CURL_TYPEOF_CURL_OFF_T     long long
+#  define CURL_FORMAT_CURL_OFF_T     "lld"
+#  define CURL_FORMAT_CURL_OFF_TU    "llu"
+#  define CURL_SUFFIX_CURL_OFF_T     LL
+#  define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#  define CURL_PULL_SYS_TYPES_H      1
+#  define CURL_PULL_SYS_SOCKET_H     1
+
+#elif defined(__SUNPRO_C) /* Oracle Solaris Studio */
+#  if !defined(__LP64) && (defined(__ILP32) ||                          \
+                           defined(__i386) || defined(__sparcv8))
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  elif defined(__LP64) || \
+        defined(__amd64) || defined(__sparcv9)
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#  define CURL_PULL_SYS_TYPES_H      1
+#  define CURL_PULL_SYS_SOCKET_H     1
+
+/* ===================================== */
+/*    KEEP MSVC THE PENULTIMATE ENTRY    */
+/* ===================================== */
+
+#elif defined(_MSC_VER)
+#  if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     "I64d"
+#    define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#    define CURL_SUFFIX_CURL_OFF_T     i64
+#    define CURL_SUFFIX_CURL_OFF_TU    ui64
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+
+/* ===================================== */
+/*    KEEP GENERIC GCC THE LAST ENTRY    */
+/* ===================================== */
+
+#elif defined(__GNUC__)
+#  if !defined(__LP64__) &&                                             \
+  (defined(__ILP32__) || defined(__i386__) || defined(__hppa__) ||      \
+   defined(__ppc__) || defined(__powerpc__) || defined(__arm__) ||      \
+   defined(__sparc__) || defined(__mips__) || defined(__sh__) ||        \
+   defined(__XTENSA__) ||                                               \
+   (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4)  ||               \
+   (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L))
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  elif defined(__LP64__) || \
+        defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \
+        (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8) || \
+        (defined(__LONG_MAX__) && __LONG_MAX__ == 9223372036854775807L)
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#  define CURL_PULL_SYS_TYPES_H      1
+#  define CURL_PULL_SYS_SOCKET_H     1
+
+#else
+/* generic "safe guess" on old 32 bit style */
+# define CURL_TYPEOF_CURL_OFF_T     long
+# define CURL_FORMAT_CURL_OFF_T     "ld"
+# define CURL_FORMAT_CURL_OFF_TU    "lu"
+# define CURL_SUFFIX_CURL_OFF_T     L
+# define CURL_SUFFIX_CURL_OFF_TU    UL
+# define CURL_TYPEOF_CURL_SOCKLEN_T int
+#endif
+
+#ifdef _AIX
+/* AIX needs <sys/poll.h> */
+#define CURL_PULL_SYS_POLL_H
+#endif
+
+
+/* CURL_PULL_WS2TCPIP_H is defined above when inclusion of header file  */
+/* ws2tcpip.h is required here to properly make type definitions below. */
+#ifdef CURL_PULL_WS2TCPIP_H
+#  include <winsock2.h>
+#  include <windows.h>
+#  include <ws2tcpip.h>
+#endif
+
+/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file  */
+/* sys/types.h is required here to properly make type definitions below. */
+#ifdef CURL_PULL_SYS_TYPES_H
+#  include <sys/types.h>
+#endif
+
+/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file  */
+/* sys/socket.h is required here to properly make type definitions below. */
+#ifdef CURL_PULL_SYS_SOCKET_H
+#  include <sys/socket.h>
+#endif
+
+/* CURL_PULL_SYS_POLL_H is defined above when inclusion of header file    */
+/* sys/poll.h is required here to properly make type definitions below.   */
+#ifdef CURL_PULL_SYS_POLL_H
+#  include <sys/poll.h>
+#endif
+
+/* Data type definition of curl_socklen_t. */
+#ifdef CURL_TYPEOF_CURL_SOCKLEN_T
+  typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t;
+#endif
+
+/* Data type definition of curl_off_t. */
+
+#ifdef CURL_TYPEOF_CURL_OFF_T
+  typedef CURL_TYPEOF_CURL_OFF_T curl_off_t;
+#endif
+
+/*
+ * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow
+ * these to be visible and exported by the external libcurl interface API,
+ * while also making them visible to the library internals, simply including
+ * curl_setup.h, without actually needing to include curl.h internally.
+ * If some day this section would grow big enough, all this should be moved
+ * to its own header file.
+ */
+
+/*
+ * Figure out if we can use the ## preprocessor operator, which is supported
+ * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__
+ * or  __cplusplus so we need to carefully check for them too.
+ */
+
+#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \
+  defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \
+  defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \
+  defined(__ILEC400__)
+  /* This compiler is believed to have an ISO compatible preprocessor */
+#define CURL_ISOCPP
+#else
+  /* This compiler is believed NOT to have an ISO compatible preprocessor */
+#undef CURL_ISOCPP
+#endif
+
+/*
+ * Macros for minimum-width signed and unsigned curl_off_t integer constants.
+ */
+
+#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551)
+#  define __CURL_OFF_T_C_HLPR2(x) x
+#  define __CURL_OFF_T_C_HLPR1(x) __CURL_OFF_T_C_HLPR2(x)
+#  define CURL_OFF_T_C(Val)  __CURL_OFF_T_C_HLPR1(Val) ## \
+                             __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T)
+#  define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \
+                             __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU)
+#else
+#  ifdef CURL_ISOCPP
+#    define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix
+#  else
+#    define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix
+#  endif
+#  define __CURL_OFF_T_C_HLPR1(Val,Suffix) __CURL_OFF_T_C_HLPR2(Val,Suffix)
+#  define CURL_OFF_T_C(Val)  __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T)
+#  define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU)
+#endif
+
+#endif /* __CURL_SYSTEM_H */

+ 683 - 0
ext/curl-7.58.0/x64/include/curl/typecheck-gcc.h

@@ -0,0 +1,683 @@
+#ifndef __CURL_TYPECHECK_GCC_H
+#define __CURL_TYPECHECK_GCC_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+/* wraps curl_easy_setopt() with typechecking */
+
+/* To add a new kind of warning, add an
+ *   if(_curl_is_sometype_option(_curl_opt))
+ *     if(!_curl_is_sometype(value))
+ *       _curl_easy_setopt_err_sometype();
+ * block and define _curl_is_sometype_option, _curl_is_sometype and
+ * _curl_easy_setopt_err_sometype below
+ *
+ * NOTE: We use two nested 'if' statements here instead of the && operator, in
+ *       order to work around gcc bug #32061.  It affects only gcc 4.3.x/4.4.x
+ *       when compiling with -Wlogical-op.
+ *
+ * To add an option that uses the same type as an existing option, you'll just
+ * need to extend the appropriate _curl_*_option macro
+ */
+#define curl_easy_setopt(handle, option, value)                               \
+__extension__ ({                                                              \
+  __typeof__(option) _curl_opt = option;                                     \
+  if(__builtin_constant_p(_curl_opt)) {                                       \
+    if(_curl_is_long_option(_curl_opt))                                       \
+      if(!_curl_is_long(value))                                               \
+        _curl_easy_setopt_err_long();                                         \
+    if(_curl_is_off_t_option(_curl_opt))                                      \
+      if(!_curl_is_off_t(value))                                              \
+        _curl_easy_setopt_err_curl_off_t();                                   \
+    if(_curl_is_string_option(_curl_opt))                                     \
+      if(!_curl_is_string(value))                                             \
+        _curl_easy_setopt_err_string();                                       \
+    if(_curl_is_write_cb_option(_curl_opt))                                   \
+      if(!_curl_is_write_cb(value))                                           \
+        _curl_easy_setopt_err_write_callback();                               \
+    if((_curl_opt) == CURLOPT_READFUNCTION)                                   \
+      if(!_curl_is_read_cb(value))                                            \
+        _curl_easy_setopt_err_read_cb();                                      \
+    if((_curl_opt) == CURLOPT_IOCTLFUNCTION)                                  \
+      if(!_curl_is_ioctl_cb(value))                                           \
+        _curl_easy_setopt_err_ioctl_cb();                                     \
+    if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION)                                \
+      if(!_curl_is_sockopt_cb(value))                                         \
+        _curl_easy_setopt_err_sockopt_cb();                                   \
+    if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION)                             \
+      if(!_curl_is_opensocket_cb(value))                                      \
+        _curl_easy_setopt_err_opensocket_cb();                                \
+    if((_curl_opt) == CURLOPT_PROGRESSFUNCTION)                               \
+      if(!_curl_is_progress_cb(value))                                        \
+        _curl_easy_setopt_err_progress_cb();                                  \
+    if((_curl_opt) == CURLOPT_DEBUGFUNCTION)                                  \
+      if(!_curl_is_debug_cb(value))                                           \
+        _curl_easy_setopt_err_debug_cb();                                     \
+    if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION)                               \
+      if(!_curl_is_ssl_ctx_cb(value))                                         \
+        _curl_easy_setopt_err_ssl_ctx_cb();                                   \
+    if(_curl_is_conv_cb_option(_curl_opt))                                    \
+      if(!_curl_is_conv_cb(value))                                            \
+        _curl_easy_setopt_err_conv_cb();                                      \
+    if((_curl_opt) == CURLOPT_SEEKFUNCTION)                                   \
+      if(!_curl_is_seek_cb(value))                                            \
+        _curl_easy_setopt_err_seek_cb();                                      \
+    if(_curl_is_cb_data_option(_curl_opt))                                    \
+      if(!_curl_is_cb_data(value))                                            \
+        _curl_easy_setopt_err_cb_data();                                      \
+    if((_curl_opt) == CURLOPT_ERRORBUFFER)                                    \
+      if(!_curl_is_error_buffer(value))                                       \
+        _curl_easy_setopt_err_error_buffer();                                 \
+    if((_curl_opt) == CURLOPT_STDERR)                                         \
+      if(!_curl_is_FILE(value))                                               \
+        _curl_easy_setopt_err_FILE();                                         \
+    if(_curl_is_postfields_option(_curl_opt))                                 \
+      if(!_curl_is_postfields(value))                                         \
+        _curl_easy_setopt_err_postfields();                                   \
+    if((_curl_opt) == CURLOPT_HTTPPOST)                                       \
+      if(!_curl_is_arr((value), struct curl_httppost))                        \
+        _curl_easy_setopt_err_curl_httpost();                                 \
+    if((_curl_opt) == CURLOPT_MIMEPOST)                                       \
+      if(!_curl_is_ptr((value), curl_mime))                                   \
+        _curl_easy_setopt_err_curl_mimepost();                                \
+    if(_curl_is_slist_option(_curl_opt))                                      \
+      if(!_curl_is_arr((value), struct curl_slist))                           \
+        _curl_easy_setopt_err_curl_slist();                                   \
+    if((_curl_opt) == CURLOPT_SHARE)                                          \
+      if(!_curl_is_ptr((value), CURLSH))                                      \
+        _curl_easy_setopt_err_CURLSH();                                       \
+  }                                                                           \
+  curl_easy_setopt(handle, _curl_opt, value);                                 \
+})
+
+/* wraps curl_easy_getinfo() with typechecking */
+/* FIXME: don't allow const pointers */
+#define curl_easy_getinfo(handle, info, arg)                                  \
+__extension__ ({                                                              \
+  __typeof__(info) _curl_info = info;                                         \
+  if(__builtin_constant_p(_curl_info)) {                                      \
+    if(_curl_is_string_info(_curl_info))                                      \
+      if(!_curl_is_arr((arg), char *))                                        \
+        _curl_easy_getinfo_err_string();                                      \
+    if(_curl_is_long_info(_curl_info))                                        \
+      if(!_curl_is_arr((arg), long))                                          \
+        _curl_easy_getinfo_err_long();                                        \
+    if(_curl_is_double_info(_curl_info))                                      \
+      if(!_curl_is_arr((arg), double))                                        \
+        _curl_easy_getinfo_err_double();                                      \
+    if(_curl_is_slist_info(_curl_info))                                       \
+      if(!_curl_is_arr((arg), struct curl_slist *))                           \
+        _curl_easy_getinfo_err_curl_slist();                                  \
+    if(_curl_is_tlssessioninfo_info(_curl_info))                              \
+      if(!_curl_is_arr((arg), struct curl_tlssessioninfo *))                  \
+        _curl_easy_getinfo_err_curl_tlssesssioninfo();                        \
+    if(_curl_is_certinfo_info(_curl_info))                                    \
+      if(!_curl_is_arr((arg), struct curl_certinfo *))                        \
+        _curl_easy_getinfo_err_curl_certinfo();                               \
+    if(_curl_is_socket_info(_curl_info))                                      \
+      if(!_curl_is_arr((arg), curl_socket_t))                                 \
+        _curl_easy_getinfo_err_curl_socket();                                 \
+    if(_curl_is_off_t_info(_curl_info))                                       \
+      if(!_curl_is_arr((arg), curl_off_t))                                    \
+        _curl_easy_getinfo_err_curl_off_t();                                  \
+  }                                                                           \
+  curl_easy_getinfo(handle, _curl_info, arg);                                 \
+})
+
+/* TODO: typechecking for curl_share_setopt() and curl_multi_setopt(),
+ * for now just make sure that the functions are called with three
+ * arguments
+ */
+#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param)
+#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param)
+
+
+/* the actual warnings, triggered by calling the _curl_easy_setopt_err*
+ * functions */
+
+/* To define a new warning, use _CURL_WARNING(identifier, "message") */
+#define _CURL_WARNING(id, message)                                            \
+  static void __attribute__((__warning__(message)))                           \
+  __attribute__((__unused__)) __attribute__((__noinline__))                   \
+  id(void) { __asm__(""); }
+
+_CURL_WARNING(_curl_easy_setopt_err_long,
+  "curl_easy_setopt expects a long argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_curl_off_t,
+  "curl_easy_setopt expects a curl_off_t argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_string,
+              "curl_easy_setopt expects a "
+              "string ('char *' or char[]) argument for this option"
+  )
+_CURL_WARNING(_curl_easy_setopt_err_write_callback,
+  "curl_easy_setopt expects a curl_write_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_read_cb,
+  "curl_easy_setopt expects a curl_read_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_ioctl_cb,
+  "curl_easy_setopt expects a curl_ioctl_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_sockopt_cb,
+  "curl_easy_setopt expects a curl_sockopt_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_opensocket_cb,
+              "curl_easy_setopt expects a "
+              "curl_opensocket_callback argument for this option"
+  )
+_CURL_WARNING(_curl_easy_setopt_err_progress_cb,
+  "curl_easy_setopt expects a curl_progress_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_debug_cb,
+  "curl_easy_setopt expects a curl_debug_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_ssl_ctx_cb,
+  "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_conv_cb,
+  "curl_easy_setopt expects a curl_conv_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_seek_cb,
+  "curl_easy_setopt expects a curl_seek_callback argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_cb_data,
+              "curl_easy_setopt expects a "
+              "private data pointer as argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_error_buffer,
+              "curl_easy_setopt expects a "
+              "char buffer of CURL_ERROR_SIZE as argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_FILE,
+  "curl_easy_setopt expects a 'FILE *' argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_postfields,
+  "curl_easy_setopt expects a 'void *' or 'char *' argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_curl_httpost,
+              "curl_easy_setopt expects a 'struct curl_httppost *' "
+              "argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_curl_mimepost,
+              "curl_easy_setopt expects a 'curl_mime *' "
+              "argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_curl_slist,
+  "curl_easy_setopt expects a 'struct curl_slist *' argument for this option")
+_CURL_WARNING(_curl_easy_setopt_err_CURLSH,
+  "curl_easy_setopt expects a CURLSH* argument for this option")
+
+_CURL_WARNING(_curl_easy_getinfo_err_string,
+  "curl_easy_getinfo expects a pointer to 'char *' for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_long,
+  "curl_easy_getinfo expects a pointer to long for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_double,
+  "curl_easy_getinfo expects a pointer to double for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_curl_slist,
+  "curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo,
+              "curl_easy_getinfo expects a pointer to "
+              "'struct curl_tlssessioninfo *' for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_curl_certinfo,
+              "curl_easy_getinfo expects a pointer to "
+              "'struct curl_certinfo *' for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_curl_socket,
+  "curl_easy_getinfo expects a pointer to curl_socket_t for this info")
+_CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
+  "curl_easy_getinfo expects a pointer to curl_off_t for this info")
+
+/* groups of curl_easy_setops options that take the same type of argument */
+
+/* To add a new option to one of the groups, just add
+ *   (option) == CURLOPT_SOMETHING
+ * to the or-expression. If the option takes a long or curl_off_t, you don't
+ * have to do anything
+ */
+
+/* evaluates to true if option takes a long argument */
+#define _curl_is_long_option(option)                                          \
+  (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT)
+
+#define _curl_is_off_t_option(option)                                         \
+  ((option) > CURLOPTTYPE_OFF_T)
+
+/* evaluates to true if option takes a char* argument */
+#define _curl_is_string_option(option)                                        \
+  ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET ||                                \
+   (option) == CURLOPT_ACCEPT_ENCODING ||                                     \
+   (option) == CURLOPT_CAINFO ||                                              \
+   (option) == CURLOPT_CAPATH ||                                              \
+   (option) == CURLOPT_COOKIE ||                                              \
+   (option) == CURLOPT_COOKIEFILE ||                                          \
+   (option) == CURLOPT_COOKIEJAR ||                                           \
+   (option) == CURLOPT_COOKIELIST ||                                          \
+   (option) == CURLOPT_CRLFILE ||                                             \
+   (option) == CURLOPT_CUSTOMREQUEST ||                                       \
+   (option) == CURLOPT_DEFAULT_PROTOCOL ||                                    \
+   (option) == CURLOPT_DNS_INTERFACE ||                                       \
+   (option) == CURLOPT_DNS_LOCAL_IP4 ||                                       \
+   (option) == CURLOPT_DNS_LOCAL_IP6 ||                                       \
+   (option) == CURLOPT_DNS_SERVERS ||                                         \
+   (option) == CURLOPT_EGDSOCKET ||                                           \
+   (option) == CURLOPT_FTPPORT ||                                             \
+   (option) == CURLOPT_FTP_ACCOUNT ||                                         \
+   (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER ||                             \
+   (option) == CURLOPT_INTERFACE ||                                           \
+   (option) == CURLOPT_ISSUERCERT ||                                          \
+   (option) == CURLOPT_KEYPASSWD ||                                           \
+   (option) == CURLOPT_KRBLEVEL ||                                            \
+   (option) == CURLOPT_LOGIN_OPTIONS ||                                       \
+   (option) == CURLOPT_MAIL_AUTH ||                                           \
+   (option) == CURLOPT_MAIL_FROM ||                                           \
+   (option) == CURLOPT_NETRC_FILE ||                                          \
+   (option) == CURLOPT_NOPROXY ||                                             \
+   (option) == CURLOPT_PASSWORD ||                                            \
+   (option) == CURLOPT_PINNEDPUBLICKEY ||                                     \
+   (option) == CURLOPT_PRE_PROXY ||                                           \
+   (option) == CURLOPT_PROXY ||                                               \
+   (option) == CURLOPT_PROXYPASSWORD ||                                       \
+   (option) == CURLOPT_PROXYUSERNAME ||                                       \
+   (option) == CURLOPT_PROXYUSERPWD ||                                        \
+   (option) == CURLOPT_PROXY_CAINFO ||                                        \
+   (option) == CURLOPT_PROXY_CAPATH ||                                        \
+   (option) == CURLOPT_PROXY_CRLFILE ||                                       \
+   (option) == CURLOPT_PROXY_KEYPASSWD ||                                     \
+   (option) == CURLOPT_PROXY_PINNEDPUBLICKEY ||                               \
+   (option) == CURLOPT_PROXY_SERVICE_NAME ||                                  \
+   (option) == CURLOPT_PROXY_SSLCERT ||                                       \
+   (option) == CURLOPT_PROXY_SSLCERTTYPE ||                                   \
+   (option) == CURLOPT_PROXY_SSLKEY ||                                        \
+   (option) == CURLOPT_PROXY_SSLKEYTYPE ||                                    \
+   (option) == CURLOPT_PROXY_SSL_CIPHER_LIST ||                               \
+   (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD ||                              \
+   (option) == CURLOPT_PROXY_TLSAUTH_USERNAME ||                              \
+   (option) == CURLOPT_PROXY_TLSAUTH_TYPE ||                                  \
+   (option) == CURLOPT_RANDOM_FILE ||                                         \
+   (option) == CURLOPT_RANGE ||                                               \
+   (option) == CURLOPT_REFERER ||                                             \
+   (option) == CURLOPT_RTSP_SESSION_ID ||                                     \
+   (option) == CURLOPT_RTSP_STREAM_URI ||                                     \
+   (option) == CURLOPT_RTSP_TRANSPORT ||                                      \
+   (option) == CURLOPT_SERVICE_NAME ||                                        \
+   (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE ||                               \
+   (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 ||                             \
+   (option) == CURLOPT_SSH_KNOWNHOSTS ||                                      \
+   (option) == CURLOPT_SSH_PRIVATE_KEYFILE ||                                 \
+   (option) == CURLOPT_SSH_PUBLIC_KEYFILE ||                                  \
+   (option) == CURLOPT_SSLCERT ||                                             \
+   (option) == CURLOPT_SSLCERTTYPE ||                                         \
+   (option) == CURLOPT_SSLENGINE ||                                           \
+   (option) == CURLOPT_SSLKEY ||                                              \
+   (option) == CURLOPT_SSLKEYTYPE ||                                          \
+   (option) == CURLOPT_SSL_CIPHER_LIST ||                                     \
+   (option) == CURLOPT_TLSAUTH_PASSWORD ||                                    \
+   (option) == CURLOPT_TLSAUTH_TYPE ||                                        \
+   (option) == CURLOPT_TLSAUTH_USERNAME ||                                    \
+   (option) == CURLOPT_UNIX_SOCKET_PATH ||                                    \
+   (option) == CURLOPT_URL ||                                                 \
+   (option) == CURLOPT_USERAGENT ||                                           \
+   (option) == CURLOPT_USERNAME ||                                            \
+   (option) == CURLOPT_USERPWD ||                                             \
+   (option) == CURLOPT_XOAUTH2_BEARER ||                                      \
+   0)
+
+/* evaluates to true if option takes a curl_write_callback argument */
+#define _curl_is_write_cb_option(option)                                      \
+  ((option) == CURLOPT_HEADERFUNCTION ||                                      \
+   (option) == CURLOPT_WRITEFUNCTION)
+
+/* evaluates to true if option takes a curl_conv_callback argument */
+#define _curl_is_conv_cb_option(option)                                       \
+  ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION ||                            \
+   (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION ||                          \
+   (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION)
+
+/* evaluates to true if option takes a data argument to pass to a callback */
+#define _curl_is_cb_data_option(option)                                       \
+  ((option) == CURLOPT_CHUNK_DATA ||                                          \
+   (option) == CURLOPT_CLOSESOCKETDATA ||                                     \
+   (option) == CURLOPT_DEBUGDATA ||                                           \
+   (option) == CURLOPT_FNMATCH_DATA ||                                        \
+   (option) == CURLOPT_HEADERDATA ||                                          \
+   (option) == CURLOPT_INTERLEAVEDATA ||                                      \
+   (option) == CURLOPT_IOCTLDATA ||                                           \
+   (option) == CURLOPT_OPENSOCKETDATA ||                                      \
+   (option) == CURLOPT_PRIVATE ||                                             \
+   (option) == CURLOPT_PROGRESSDATA ||                                        \
+   (option) == CURLOPT_READDATA ||                                            \
+   (option) == CURLOPT_SEEKDATA ||                                            \
+   (option) == CURLOPT_SOCKOPTDATA ||                                         \
+   (option) == CURLOPT_SSH_KEYDATA ||                                         \
+   (option) == CURLOPT_SSL_CTX_DATA ||                                        \
+   (option) == CURLOPT_WRITEDATA ||                                           \
+   0)
+
+/* evaluates to true if option takes a POST data argument (void* or char*) */
+#define _curl_is_postfields_option(option)                                    \
+  ((option) == CURLOPT_POSTFIELDS ||                                          \
+   (option) == CURLOPT_COPYPOSTFIELDS ||                                      \
+   0)
+
+/* evaluates to true if option takes a struct curl_slist * argument */
+#define _curl_is_slist_option(option)                                         \
+  ((option) == CURLOPT_HTTP200ALIASES ||                                      \
+   (option) == CURLOPT_HTTPHEADER ||                                          \
+   (option) == CURLOPT_MAIL_RCPT ||                                           \
+   (option) == CURLOPT_POSTQUOTE ||                                           \
+   (option) == CURLOPT_PREQUOTE ||                                            \
+   (option) == CURLOPT_PROXYHEADER ||                                         \
+   (option) == CURLOPT_QUOTE ||                                               \
+   (option) == CURLOPT_RESOLVE ||                                             \
+   (option) == CURLOPT_TELNETOPTIONS ||                                       \
+   0)
+
+/* groups of curl_easy_getinfo infos that take the same type of argument */
+
+/* evaluates to true if info expects a pointer to char * argument */
+#define _curl_is_string_info(info)                                            \
+  (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG)
+
+/* evaluates to true if info expects a pointer to long argument */
+#define _curl_is_long_info(info)                                              \
+  (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE)
+
+/* evaluates to true if info expects a pointer to double argument */
+#define _curl_is_double_info(info)                                            \
+  (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST)
+
+/* true if info expects a pointer to struct curl_slist * argument */
+#define _curl_is_slist_info(info)                                       \
+  (((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST))
+
+/* true if info expects a pointer to struct curl_tlssessioninfo * argument */
+#define _curl_is_tlssessioninfo_info(info)                              \
+  (((info) == CURLINFO_TLS_SSL_PTR) || ((info) == CURLINFO_TLS_SESSION))
+
+/* true if info expects a pointer to struct curl_certinfo * argument */
+#define _curl_is_certinfo_info(info) ((info) == CURLINFO_CERTINFO)
+
+/* true if info expects a pointer to struct curl_socket_t argument */
+#define _curl_is_socket_info(info)                                            \
+  (CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T)
+
+/* true if info expects a pointer to curl_off_t argument */
+#define _curl_is_off_t_info(info)                                             \
+  (CURLINFO_OFF_T < (info))
+
+
+/* typecheck helpers -- check whether given expression has requested type*/
+
+/* For pointers, you can use the _curl_is_ptr/_curl_is_arr macros,
+ * otherwise define a new macro. Search for __builtin_types_compatible_p
+ * in the GCC manual.
+ * NOTE: these macros MUST NOT EVALUATE their arguments! The argument is
+ * the actual expression passed to the curl_easy_setopt macro. This
+ * means that you can only apply the sizeof and __typeof__ operators, no
+ * == or whatsoever.
+ */
+
+/* XXX: should evaluate to true iff expr is a pointer */
+#define _curl_is_any_ptr(expr)                                                \
+  (sizeof(expr) == sizeof(void *))
+
+/* evaluates to true if expr is NULL */
+/* XXX: must not evaluate expr, so this check is not accurate */
+#define _curl_is_NULL(expr)                                                   \
+  (__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL)))
+
+/* evaluates to true if expr is type*, const type* or NULL */
+#define _curl_is_ptr(expr, type)                                              \
+  (_curl_is_NULL(expr) ||                                                     \
+   __builtin_types_compatible_p(__typeof__(expr), type *) ||                  \
+   __builtin_types_compatible_p(__typeof__(expr), const type *))
+
+/* evaluates to true if expr is one of type[], type*, NULL or const type* */
+#define _curl_is_arr(expr, type)                                              \
+  (_curl_is_ptr((expr), type) ||                                              \
+   __builtin_types_compatible_p(__typeof__(expr), type []))
+
+/* evaluates to true if expr is a string */
+#define _curl_is_string(expr)                                                 \
+  (_curl_is_arr((expr), char) ||                                              \
+   _curl_is_arr((expr), signed char) ||                                       \
+   _curl_is_arr((expr), unsigned char))
+
+/* evaluates to true if expr is a long (no matter the signedness)
+ * XXX: for now, int is also accepted (and therefore short and char, which
+ * are promoted to int when passed to a variadic function) */
+#define _curl_is_long(expr)                                                   \
+  (__builtin_types_compatible_p(__typeof__(expr), long) ||                    \
+   __builtin_types_compatible_p(__typeof__(expr), signed long) ||             \
+   __builtin_types_compatible_p(__typeof__(expr), unsigned long) ||           \
+   __builtin_types_compatible_p(__typeof__(expr), int) ||                     \
+   __builtin_types_compatible_p(__typeof__(expr), signed int) ||              \
+   __builtin_types_compatible_p(__typeof__(expr), unsigned int) ||            \
+   __builtin_types_compatible_p(__typeof__(expr), short) ||                   \
+   __builtin_types_compatible_p(__typeof__(expr), signed short) ||            \
+   __builtin_types_compatible_p(__typeof__(expr), unsigned short) ||          \
+   __builtin_types_compatible_p(__typeof__(expr), char) ||                    \
+   __builtin_types_compatible_p(__typeof__(expr), signed char) ||             \
+   __builtin_types_compatible_p(__typeof__(expr), unsigned char))
+
+/* evaluates to true if expr is of type curl_off_t */
+#define _curl_is_off_t(expr)                                                  \
+  (__builtin_types_compatible_p(__typeof__(expr), curl_off_t))
+
+/* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */
+/* XXX: also check size of an char[] array? */
+#define _curl_is_error_buffer(expr)                                           \
+  (_curl_is_NULL(expr) ||                                                     \
+   __builtin_types_compatible_p(__typeof__(expr), char *) ||                  \
+   __builtin_types_compatible_p(__typeof__(expr), char[]))
+
+/* evaluates to true if expr is of type (const) void* or (const) FILE* */
+#if 0
+#define _curl_is_cb_data(expr)                                                \
+  (_curl_is_ptr((expr), void) ||                                              \
+   _curl_is_ptr((expr), FILE))
+#else /* be less strict */
+#define _curl_is_cb_data(expr)                                                \
+  _curl_is_any_ptr(expr)
+#endif
+
+/* evaluates to true if expr is of type FILE* */
+#define _curl_is_FILE(expr)                                             \
+  (_curl_is_NULL(expr) ||                                              \
+   (__builtin_types_compatible_p(__typeof__(expr), FILE *)))
+
+/* evaluates to true if expr can be passed as POST data (void* or char*) */
+#define _curl_is_postfields(expr)                                             \
+  (_curl_is_ptr((expr), void) ||                                              \
+   _curl_is_arr((expr), char))
+
+/* FIXME: the whole callback checking is messy...
+ * The idea is to tolerate char vs. void and const vs. not const
+ * pointers in arguments at least
+ */
+/* helper: __builtin_types_compatible_p distinguishes between functions and
+ * function pointers, hide it */
+#define _curl_callback_compatible(func, type)                                 \
+  (__builtin_types_compatible_p(__typeof__(func), type) ||                    \
+   __builtin_types_compatible_p(__typeof__(func) *, type))
+
+/* evaluates to true if expr is of type curl_read_callback or "similar" */
+#define _curl_is_read_cb(expr)                                          \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), __typeof__(fread) *) ||                  \
+   _curl_callback_compatible((expr), curl_read_callback) ||                   \
+   _curl_callback_compatible((expr), _curl_read_callback1) ||                 \
+   _curl_callback_compatible((expr), _curl_read_callback2) ||                 \
+   _curl_callback_compatible((expr), _curl_read_callback3) ||                 \
+   _curl_callback_compatible((expr), _curl_read_callback4) ||                 \
+   _curl_callback_compatible((expr), _curl_read_callback5) ||                 \
+   _curl_callback_compatible((expr), _curl_read_callback6))
+typedef size_t (*_curl_read_callback1)(char *, size_t, size_t, void *);
+typedef size_t (*_curl_read_callback2)(char *, size_t, size_t, const void *);
+typedef size_t (*_curl_read_callback3)(char *, size_t, size_t, FILE *);
+typedef size_t (*_curl_read_callback4)(void *, size_t, size_t, void *);
+typedef size_t (*_curl_read_callback5)(void *, size_t, size_t, const void *);
+typedef size_t (*_curl_read_callback6)(void *, size_t, size_t, FILE *);
+
+/* evaluates to true if expr is of type curl_write_callback or "similar" */
+#define _curl_is_write_cb(expr)                                               \
+  (_curl_is_read_cb(expr) ||                                            \
+   _curl_callback_compatible((expr), __typeof__(fwrite) *) ||                 \
+   _curl_callback_compatible((expr), curl_write_callback) ||                  \
+   _curl_callback_compatible((expr), _curl_write_callback1) ||                \
+   _curl_callback_compatible((expr), _curl_write_callback2) ||                \
+   _curl_callback_compatible((expr), _curl_write_callback3) ||                \
+   _curl_callback_compatible((expr), _curl_write_callback4) ||                \
+   _curl_callback_compatible((expr), _curl_write_callback5) ||                \
+   _curl_callback_compatible((expr), _curl_write_callback6))
+typedef size_t (*_curl_write_callback1)(const char *, size_t, size_t, void *);
+typedef size_t (*_curl_write_callback2)(const char *, size_t, size_t,
+                                       const void *);
+typedef size_t (*_curl_write_callback3)(const char *, size_t, size_t, FILE *);
+typedef size_t (*_curl_write_callback4)(const void *, size_t, size_t, void *);
+typedef size_t (*_curl_write_callback5)(const void *, size_t, size_t,
+                                       const void *);
+typedef size_t (*_curl_write_callback6)(const void *, size_t, size_t, FILE *);
+
+/* evaluates to true if expr is of type curl_ioctl_callback or "similar" */
+#define _curl_is_ioctl_cb(expr)                                         \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_ioctl_callback) ||                  \
+   _curl_callback_compatible((expr), _curl_ioctl_callback1) ||                \
+   _curl_callback_compatible((expr), _curl_ioctl_callback2) ||                \
+   _curl_callback_compatible((expr), _curl_ioctl_callback3) ||                \
+   _curl_callback_compatible((expr), _curl_ioctl_callback4))
+typedef curlioerr (*_curl_ioctl_callback1)(CURL *, int, void *);
+typedef curlioerr (*_curl_ioctl_callback2)(CURL *, int, const void *);
+typedef curlioerr (*_curl_ioctl_callback3)(CURL *, curliocmd, void *);
+typedef curlioerr (*_curl_ioctl_callback4)(CURL *, curliocmd, const void *);
+
+/* evaluates to true if expr is of type curl_sockopt_callback or "similar" */
+#define _curl_is_sockopt_cb(expr)                                       \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_sockopt_callback) ||                \
+   _curl_callback_compatible((expr), _curl_sockopt_callback1) ||              \
+   _curl_callback_compatible((expr), _curl_sockopt_callback2))
+typedef int (*_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype);
+typedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t,
+                                      curlsocktype);
+
+/* evaluates to true if expr is of type curl_opensocket_callback or
+   "similar" */
+#define _curl_is_opensocket_cb(expr)                                    \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_opensocket_callback) ||             \
+   _curl_callback_compatible((expr), _curl_opensocket_callback1) ||           \
+   _curl_callback_compatible((expr), _curl_opensocket_callback2) ||           \
+   _curl_callback_compatible((expr), _curl_opensocket_callback3) ||           \
+   _curl_callback_compatible((expr), _curl_opensocket_callback4))
+typedef curl_socket_t (*_curl_opensocket_callback1)
+  (void *, curlsocktype, struct curl_sockaddr *);
+typedef curl_socket_t (*_curl_opensocket_callback2)
+  (void *, curlsocktype, const struct curl_sockaddr *);
+typedef curl_socket_t (*_curl_opensocket_callback3)
+  (const void *, curlsocktype, struct curl_sockaddr *);
+typedef curl_socket_t (*_curl_opensocket_callback4)
+  (const void *, curlsocktype, const struct curl_sockaddr *);
+
+/* evaluates to true if expr is of type curl_progress_callback or "similar" */
+#define _curl_is_progress_cb(expr)                                      \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_progress_callback) ||               \
+   _curl_callback_compatible((expr), _curl_progress_callback1) ||             \
+   _curl_callback_compatible((expr), _curl_progress_callback2))
+typedef int (*_curl_progress_callback1)(void *,
+    double, double, double, double);
+typedef int (*_curl_progress_callback2)(const void *,
+    double, double, double, double);
+
+/* evaluates to true if expr is of type curl_debug_callback or "similar" */
+#define _curl_is_debug_cb(expr)                                         \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_debug_callback) ||                  \
+   _curl_callback_compatible((expr), _curl_debug_callback1) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback2) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback3) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback4) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback5) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback6) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback7) ||                \
+   _curl_callback_compatible((expr), _curl_debug_callback8))
+typedef int (*_curl_debug_callback1) (CURL *,
+    curl_infotype, char *, size_t, void *);
+typedef int (*_curl_debug_callback2) (CURL *,
+    curl_infotype, char *, size_t, const void *);
+typedef int (*_curl_debug_callback3) (CURL *,
+    curl_infotype, const char *, size_t, void *);
+typedef int (*_curl_debug_callback4) (CURL *,
+    curl_infotype, const char *, size_t, const void *);
+typedef int (*_curl_debug_callback5) (CURL *,
+    curl_infotype, unsigned char *, size_t, void *);
+typedef int (*_curl_debug_callback6) (CURL *,
+    curl_infotype, unsigned char *, size_t, const void *);
+typedef int (*_curl_debug_callback7) (CURL *,
+    curl_infotype, const unsigned char *, size_t, void *);
+typedef int (*_curl_debug_callback8) (CURL *,
+    curl_infotype, const unsigned char *, size_t, const void *);
+
+/* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */
+/* this is getting even messier... */
+#define _curl_is_ssl_ctx_cb(expr)                                       \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_ssl_ctx_callback) ||                \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback1) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback2) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback3) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback4) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback5) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback6) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback7) ||              \
+   _curl_callback_compatible((expr), _curl_ssl_ctx_callback8))
+typedef CURLcode (*_curl_ssl_ctx_callback1)(CURL *, void *, void *);
+typedef CURLcode (*_curl_ssl_ctx_callback2)(CURL *, void *, const void *);
+typedef CURLcode (*_curl_ssl_ctx_callback3)(CURL *, const void *, void *);
+typedef CURLcode (*_curl_ssl_ctx_callback4)(CURL *, const void *,
+                                            const void *);
+#ifdef HEADER_SSL_H
+/* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX
+ * this will of course break if we're included before OpenSSL headers...
+ */
+typedef CURLcode (*_curl_ssl_ctx_callback5)(CURL *, SSL_CTX, void *);
+typedef CURLcode (*_curl_ssl_ctx_callback6)(CURL *, SSL_CTX, const void *);
+typedef CURLcode (*_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX, void *);
+typedef CURLcode (*_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX,
+                                           const void *);
+#else
+typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5;
+typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6;
+typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7;
+typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8;
+#endif
+
+/* evaluates to true if expr is of type curl_conv_callback or "similar" */
+#define _curl_is_conv_cb(expr)                                          \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_conv_callback) ||                   \
+   _curl_callback_compatible((expr), _curl_conv_callback1) ||                 \
+   _curl_callback_compatible((expr), _curl_conv_callback2) ||                 \
+   _curl_callback_compatible((expr), _curl_conv_callback3) ||                 \
+   _curl_callback_compatible((expr), _curl_conv_callback4))
+typedef CURLcode (*_curl_conv_callback1)(char *, size_t length);
+typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length);
+typedef CURLcode (*_curl_conv_callback3)(void *, size_t length);
+typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length);
+
+/* evaluates to true if expr is of type curl_seek_callback or "similar" */
+#define _curl_is_seek_cb(expr)                                          \
+  (_curl_is_NULL(expr) ||                                                     \
+   _curl_callback_compatible((expr), curl_seek_callback) ||                   \
+   _curl_callback_compatible((expr), _curl_seek_callback1) ||                 \
+   _curl_callback_compatible((expr), _curl_seek_callback2))
+typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int);
+typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int);
+
+
+#endif /* __CURL_TYPECHECK_GCC_H */

二进制
ext/curl-7.58.0/x64/lib/libcurl_a.lib


二进制
ext/curl-7.58.0/x64/lib/libcurl_a_debug.lib


+ 12 - 6
ext/installfiles/linux/zerotier-containerized/Dockerfile

@@ -1,15 +1,21 @@
-FROM alpine:latest
+FROM alpine:latest as builder
 MAINTAINER Adam Ierymenko <[email protected]>
 
-LABEL version="1.2.4"
-LABEL description="Containerized ZeroTier One for use on CoreOS or other Docker-only Linux hosts."
+ARG VERSION=1.2.12
+LABEL version=$VERSION
+LABEL description="Containerized ZeroTier One"
 
-# Uncomment to build in container
-#RUN apk add --update alpine-sdk linux-headers
+RUN apk add --update alpine-sdk linux-headers
+RUN git clone https://github.com/zerotier/ZeroTierOne.git
+WORKDIR ZeroTierOne
+RUN git checkout $VERSION
+RUN make
+
+FROM alpine:latest as final
 
 RUN apk add --update libgcc libstdc++
 
-ADD zerotier-one /
+COPY --from=builder /ZeroTierOne/zerotier-one /
 RUN chmod 0755 /zerotier-one
 RUN ln -sf /zerotier-one /zerotier-cli
 RUN mkdir -p /var/lib/zerotier-one

+ 220 - 81
ext/json/README.md

@@ -5,11 +5,11 @@
 [![Coverage Status](https://img.shields.io/coveralls/nlohmann/json.svg)](https://coveralls.io/r/nlohmann/json)
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/5550/badge.svg)](https://scan.coverity.com/projects/nlohmann-json)
 [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f3732b3327e34358a0e9d1fe9f661f08)](https://www.codacy.com/app/nlohmann/json?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=nlohmann/json&amp;utm_campaign=Badge_Grade)
-[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/Op57X0V7fTf2tdwl)
+[![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://wandbox.org/permlink/TarF5pPn9NtHQjhf)
 [![Documentation](https://img.shields.io/badge/docs-doxygen-blue.svg)](http://nlohmann.github.io/json)
 [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nlohmann/json/master/LICENSE.MIT)
-[![Github Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
-[![Github Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](http://github.com/nlohmann/json/issues)
+[![GitHub Releases](https://img.shields.io/github/release/nlohmann/json.svg)](https://github.com/nlohmann/json/releases)
+[![GitHub Issues](https://img.shields.io/github/issues/nlohmann/json.svg)](http://github.com/nlohmann/json/issues)
 [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/nlohmann/json.svg)](http://isitmaintained.com/project/nlohmann/json "Average time to resolve an issue")
 [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/289/badge)](https://bestpractices.coreinfrastructure.org/projects/289)
 
@@ -21,9 +21,10 @@
   - [STL-like access](#stl-like-access)
   - [Conversion from STL containers](#conversion-from-stl-containers)
   - [JSON Pointer and JSON Patch](#json-pointer-and-json-patch)
+  - [JSON Merge Patch](#json-merge-patch)
   - [Implicit conversions](#implicit-conversions)
   - [Conversions to/from arbitrary types](#arbitrary-types-conversions)
-  - [Binary formats (CBOR and MessagePack)](#binary-formats-cbor-and-messagepack)
+  - [Binary formats (CBOR, MessagePack, and UBJSON)](#binary-formats-cbor-messagepack-and-ubjson)
 - [Supported compilers](#supported-compilers)
 - [License](#license)
 - [Contact](#contact)
@@ -39,9 +40,9 @@ There are myriads of [JSON](http://json.org) libraries out there, and each may e
 
 - **Intuitive syntax**. In languages such as Python, JSON feels like a first class data type. We used all the operator magic of modern C++ to achieve the same feeling in your code. Check out the [examples below](#examples) and you'll know what I mean.
 
-- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/src/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings.
+- **Trivial integration**. Our whole code consists of a single header file [`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp). That's it. No library, no subproject, no dependencies, no complex build system. The class is written in vanilla C++11. All in all, everything should require no adjustment of your compiler flags or project settings.
 
-- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/blob/master/test/src/unit.cpp) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) that there are no memory leaks. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289).
+- **Serious testing**. Our class is heavily [unit-tested](https://github.com/nlohmann/json/tree/develop/test/src) and covers [100%](https://coveralls.io/r/nlohmann/json) of the code, including all exceptional behavior. Furthermore, we checked with [Valgrind](http://valgrind.org) and the [Clang Sanitizers](https://clang.llvm.org/docs/index.html) that there are no memory leaks. [Google OSS-Fuzz](https://github.com/google/oss-fuzz/tree/master/projects/json) additionally runs fuzz tests agains all parsers 24/7, effectively executing billions of tests so far. To maintain high quality, the project is following the [Core Infrastructure Initiative (CII) best practices](https://bestpractices.coreinfrastructure.org/projects/289).
 
 Other aspects were not so important to us:
 
@@ -54,31 +55,42 @@ See the [contribution guidelines](https://github.com/nlohmann/json/blob/master/.
 
 ## Integration
 
-The single required source, file `json.hpp` is in the `src` directory or [released here](https://github.com/nlohmann/json/releases). All you need to do is add
+[`json.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json.hpp) is the single required file in `single_include/nlohmann` or [released here](https://github.com/nlohmann/json/releases). You need to add
 
 ```cpp
-#include "json.hpp"
+#include <nlohmann/json.hpp>
 
 // for convenience
 using json = nlohmann::json;
 ```
 
-to the files you want to use JSON objects. That's it. Do not forget to set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and Clang).
+to the files you want to process JSON and set the necessary switches to enable C++11 (e.g., `-std=c++11` for GCC and Clang).
+
+You can further use file [`include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/json_fwd.hpp) for forward-declarations. The installation of json_fwd.hpp (as part of cmake's install step), can be achieved by setting `-DJSON_MultipleHeaders=ON`.
+
+### Package Managers
 
 :beer: If you are using OS X and [Homebrew](http://brew.sh), just type `brew tap nlohmann/json` and `brew install nlohmann_json` and you're set. If you want the bleeding edge rather than the latest release, use `brew install nlohmann_json --HEAD`.
 
-If you are using the [Meson Build System](http://mesonbuild.com), then you can wrap this repo as a subproject.
+If you are using the [Meson Build System](http://mesonbuild.com), then you can wrap this repository as a subproject.
 
 If you are using [Conan](https://www.conan.io/) to manage your dependencies, merely add `jsonformoderncpp/x.y.z@vthiery/stable` to your `conanfile.py`'s requires, where `x.y.z` is the release version you want to use. Please file issues [here](https://github.com/vthiery/conan-jsonformoderncpp/issues) if you experience problems with the packages.
 
+If you are using [Spack](https://www.spack.io/) to manage your dependencies, you can use the `nlohmann_json` package. Please see the [spack project](https://github.com/spack/spack) for any issues regarding the packaging.
+
 If you are using [hunter](https://github.com/ruslo/hunter/) on your project for external dependencies, then you can use the [nlohmann_json package](https://docs.hunter.sh/en/latest/packages/pkg/nlohmann_json.html). Please see the hunter project for any issues regarding the packaging.
 
+If you are using [Buckaroo](https://buckaroo.pm), you can install this library's module with `buckaroo install nlohmann/json`. Please file issues [here](https://github.com/LoopPerfect/buckaroo-recipes/issues/new?title=nlohmann/nlohmann/json).
+
 If you are using [vcpkg](https://github.com/Microsoft/vcpkg/) on your project for external dependencies, then you can use the [nlohmann-json package](https://github.com/Microsoft/vcpkg/tree/master/ports/nlohmann-json). Please see the vcpkg project for any issues regarding the packaging.
 
+If you are using [cget](http://cget.readthedocs.io/en/latest/), you can install the latest development version with `cget install nlohmann/json`. A specific version can be installed with `cget install nlohmann/[email protected]`. Also, the multiple header version can be installed by adding the `-DJSON_MultipleHeaders=ON` flag (i.e., `cget install nlohmann/json -DJSON_MultipleHeaders=ON`).
+
+If you are using [CocoaPods](https://cocoapods.org), you can use the library by adding pod `"nlohmann_json", '~>3.1.2'` to your podfile (see [an example](https://bitbucket.org/benman/nlohmann_json-cocoapod/src/master/)). Please file issues [here](https://bitbucket.org/benman/nlohmann_json-cocoapod/issues?status=new&status=open).
 
 ## Examples
 
-Beside the examples below, you may want to check the [documentation](https://nlohmann.github.io/json/) where each function contains a separate code example (e.g., check out [`emplace()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a602f275f0359ab181221384989810604.html#a602f275f0359ab181221384989810604)). All [example files](https://github.com/nlohmann/json/tree/develop/doc/examples) can be compiled and executed on their own (e.g., file [emplace.cpp](https://github.com/nlohmann/json/blob/develop/doc/examples/emplace.cpp)).
+Beside the examples below, you may want to check the [documentation](https://nlohmann.github.io/json/) where each function contains a separate code example (e.g., check out [`emplace()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a5338e282d1d02bed389d852dd670d98d.html#a5338e282d1d02bed389d852dd670d98d)). All [example files](https://github.com/nlohmann/json/tree/develop/doc/examples) can be compiled and executed on their own (e.g., file [emplace.cpp](https://github.com/nlohmann/json/blob/develop/doc/examples/emplace.cpp)).
 
 ### JSON as first-class data type
 
@@ -103,7 +115,7 @@ Assume you want to create the JSON object
 }
 ```
 
-With the JSON class, you could write:
+With this library, you could write:
 
 ```cpp
 // create an empty structure (null)
@@ -147,7 +159,7 @@ json j2 = {
 };
 ```
 
-Note that in all these cases, you never need to "tell" the compiler which JSON value you want to use. If you want to be explicit or express some edge cases, the functions `json::array` and `json::object` will help:
+Note that in all these cases, you never need to "tell" the compiler which JSON value type you want to use. If you want to be explicit or express some edge cases, the functions [`json::array`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_aa80485befaffcadaa39965494e0b4d2e.html#aa80485befaffcadaa39965494e0b4d2e) and [`json::object`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_aa13f7c0615867542ce80337cbcf13ada.html#aa13f7c0615867542ce80337cbcf13ada) will help:
 
 ```cpp
 // a way to express the empty array []
@@ -161,12 +173,11 @@ json empty_object_explicit = json::object();
 json array_not_object = json::array({ {"currency", "USD"}, {"value", 42.99} });
 ```
 
-
 ### Serialization / Deserialization
 
 #### To/from strings
 
-You can create an object (deserialization) by appending `_json` to a string literal:
+You can create a JSON value (deserialization) by appending `_json` to a string literal:
 
 ```cpp
 // create object from string literal
@@ -183,14 +194,14 @@ auto j2 = R"(
 
 Note that without appending the `_json` suffix, the passed string literal is not parsed, but just used as JSON string value. That is, `json j = "{ \"happy\": true, \"pi\": 3.141 }"` would just store the string `"{ "happy": true, "pi": 3.141 }"` rather than parsing the actual object.
 
-The above example can also be expressed explicitly using `json::parse()`:
+The above example can also be expressed explicitly using [`json::parse()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_aa9676414f2e36383c4b181fe856aa3c0.html#aa9676414f2e36383c4b181fe856aa3c0):
 
 ```cpp
 // parse explicitly
 auto j3 = json::parse("{ \"happy\": true, \"pi\": 3.141 }");
 ```
 
-You can also get a string representation (serialize):
+You can also get a string representation of a JSON value (serialize):
 
 ```cpp
 // explicit conversion to string
@@ -225,8 +236,9 @@ std::cout << cpp_string << " == " << cpp_string2 << " == " << j_string.get<std::
 std::cout << j_string << " == " << serialized_string << std::endl;
 ```
 
-`.dump()` always returns the serialized value, and `.get<std::string>()` returns the originally stored string value.
+[`.dump()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a5adea76fedba9898d404fef8598aa663.html#a5adea76fedba9898d404fef8598aa663) always returns the serialized value, and [`.get<std::string>()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a16f9445f7629f634221a42b967cdcd43.html#a16f9445f7629f634221a42b967cdcd43) returns the originally stored string value.
 
+Note the library only supports UTF-8. When you store strings with different encodings in the library, calling [`dump()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a5adea76fedba9898d404fef8598aa663.html#a5adea76fedba9898d404fef8598aa663) may throw an exception.
 
 #### To/from streams (e.g. files, string streams)
 
@@ -261,7 +273,7 @@ Please note that setting the exception bit for `failbit` is inappropriate for th
 
 #### Read from iterator range
 
-You can also read JSON from an iterator range; that is, from any container accessible by iterators whose content is stored as contiguous byte sequence, for instance a `std::vector<std::uint8_t>`:
+You can also parse JSON from an iterator range; that is, from any container accessible by iterators whose content is stored as contiguous byte sequence, for instance a `std::vector<std::uint8_t>`:
 
 ```cpp
 std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};
@@ -275,10 +287,53 @@ std::vector<std::uint8_t> v = {'t', 'r', 'u', 'e'};
 json j = json::parse(v);
 ```
 
+#### SAX interface
+
+The library uses a SAX-like interface with the following functions:
+
+```cpp
+// called when null is parsed
+bool null();
+
+// called when a boolean is parsed; value is passed
+bool boolean(bool val);
+
+// called when a signed or unsigned integer number is parsed; value is passed
+bool number_integer(number_integer_t val);
+bool number_unsigned(number_unsigned_t val);
+
+// called when a floating-point number is parsed; value and original string is passed
+bool number_float(number_float_t val, const string_t& s);
+
+// called when a string is parsed; value is passed and can be safely moved away
+bool string(string_t& val);
+
+// called when an object or array begins or ends, resp. The number of elements is passed (or -1 if not known)
+bool start_object(std::size_t elements);
+bool end_object();
+bool start_array(std::size_t elements);
+bool end_array();
+// called when an object key is parsed; value is passed and can be safely moved away
+bool key(string_t& val);
+
+// called when a parse error occurs; byte position, the last token, and an exception is passed
+bool parse_error(std::size_t position, const std::string& last_token, const detail::exception& ex);
+```
+
+The return value of each function determines whether parsing should proceed.
+
+To implement your own SAX handler, proceed as follows:
+
+1. Implement the SAX interface in a class. You can use class `nlohmann::json_sax<json>` as base class, but you can also use any class where the functions described above are implemented and public.
+2. Create an object of your SAX interface class, e.g. `my_sax`.
+3. Call `bool json::sax_parse(input, &my_sax)`; where the first parameter can be any input like a string or an input stream and the second parameter is a pointer to your SAX interface.
+
+Note the `sax_parse` function only returns a `bool` indicating the result of the last executed SAX event. It does not return a  `json` value - it is up to you to decide what to do with the SAX events. Furthermore, no exceptions are thrown in case of a parse error - it is up to you what to do with the exception object passed to your `parse_error` implementation. Internally, the SAX interface is used for the DOM parser (class `json_sax_dom_parser`) as well as the acceptor (`json_sax_acceptor`), see file [`json_sax.hpp`](https://github.com/nlohmann/json/blob/develop/include/nlohmann/detail/input/json_sax.hpp).
+
 
 ### STL-like access
 
-We designed the JSON class to behave just like an STL container. In fact, it satisfies the [**ReversibleContainer**](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) requirement.
+We designed the JSON class to behave just like an STL container. In fact, it satisfies the [**ReversibleContainer**](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) requirement.
 
 ```cpp
 // create an array using push_back
@@ -352,7 +407,7 @@ o.erase("foo");
 
 ### Conversion from STL containers
 
-Any sequence container (`std::array`, `std::vector`, `std::deque`, `std::forward_list`, `std::list`) whose values can be used to construct JSON types (e.g., integers, floating point numbers, Booleans, string types, or again STL containers described in this section) can be used to create a JSON array. The same holds for similar associative containers (`std::set`, `std::multiset`, `std::unordered_set`, `std::unordered_multiset`), but in these cases the order of the elements of the array depends on how the elements are ordered in the respective STL container.
+Any sequence container (`std::array`, `std::vector`, `std::deque`, `std::forward_list`, `std::list`) whose values can be used to construct JSON values (e.g., integers, floating point numbers, Booleans, string types, or again STL containers described in this section) can be used to create a JSON array. The same holds for similar associative containers (`std::set`, `std::multiset`, `std::unordered_set`, `std::unordered_multiset`), but in these cases the order of the elements of the array depends on how the elements are ordered in the respective STL container.
 
 ```cpp
 std::vector<int> c_vector {1, 2, 3, 4};
@@ -392,7 +447,7 @@ json j_umset(c_umset); // both entries for "one" are used
 // maybe ["one", "two", "one", "four"]
 ```
 
-Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys can construct an `std::string` and whose values can be used to construct JSON types (see examples above) can be used to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container.
+Likewise, any associative key-value containers (`std::map`, `std::multimap`, `std::unordered_map`, `std::unordered_multimap`) whose keys can construct an `std::string` and whose values can be used to construct JSON values (see examples above) can be used to create a JSON object. Note that in case of multimaps only one key is used in the JSON object and the value depends on the internal order of the STL container.
 
 ```cpp
 std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
@@ -450,6 +505,37 @@ json::diff(j_result, j_original);
 // ]
 ```
 
+### JSON Merge Patch
+
+The library supports **JSON Merge Patch** ([RFC 7386](https://tools.ietf.org/html/rfc7386)) as a patch format. Instead of using JSON Pointer (see above) to specify values to be manipulated, it describes the changes using a syntax that closely mimics the document being modified.
+
+```cpp
+// a JSON value
+json j_document = R"({
+  "a": "b",
+  "c": {
+    "d": "e",
+    "f": "g"
+  }
+})"_json;
+
+// a patch
+json j_patch = R"({
+  "a":"z",
+  "c": {
+    "f": null
+  }
+})"_json;
+
+// apply the patch
+j_original.merge_patch(j_patch);
+// {
+//  "a": "z",
+//  "c": {
+//    "d": "e"
+//  }
+// }
+```
 
 ### Implicit conversions
 
@@ -484,9 +570,17 @@ int vi = jn.get<int>();
 // etc.
 ```
 
+Note that `char` types are not automatically converted to JSON strings, but to integer numbers. A conversion to a string must be specified explicitly:
+
+```cpp
+char ch = 'A';                       // ASCII value 65
+json j_default = ch;                 // stores integer number 65
+json j_string = std::string(1, ch);  // stores string "A"
+```
+
 ### Arbitrary types conversions
 
-Every type can be serialized in JSON, not just STL-containers and scalar types. Usually, you would do something along those lines:
+Every type can be serialized in JSON, not just STL containers and scalar types. Usually, you would do something along those lines:
 
 ```cpp
 namespace ns {
@@ -561,7 +655,8 @@ Likewise, when calling `get<your_type>()`, the `from_json` method will be called
 Some important things:
 
 * Those methods **MUST** be in your type's namespace (which can be the global namespace), or the library will not be able to locate them (in this example, they are in namespace `ns`, where `person` is defined).
-* When using `get<your_type>()`, `your_type` **MUST** be [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). (There is a way to bypass this requirement described later.)
+* Those methods **MUST** be available (e.g., properly headers must be included) everywhere you use the implicit conversions. Look at [issue 1108](https://github.com/nlohmann/json/issues/1108) for errors that may occur otherwise.
+* When using `get<your_type>()`, `your_type` **MUST** be [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). (There is a way to bypass this requirement described later.)
 * In function `from_json`, use function [`at()`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a93403e803947b86f4da2d1fb3345cf2c.html#a93403e803947b86f4da2d1fb3345cf2c) to access the object values rather than `operator[]`. In case a key does not exist, `at` throws an exception that you can handle, whereas `operator[]` exhibits undefined behavior.
 * In case your type contains several `operator=` definitions, code like `your_variable = your_json;` [may not compile](https://github.com/nlohmann/json/issues/667). You need to write `your_variable = your_json.get<decltype your_variable>();` instead.
 * You do not need to add serializers or deserializers for STL types like `std::vector`: the library already implements these.
@@ -573,7 +668,7 @@ Some important things:
 This requires a bit more advanced technique. But first, let's see how this conversion mechanism works:
 
 The library uses **JSON Serializers** to convert types to json.
-The default serializer for `nlohmann::json` is `nlohmann::adl_serializer` (ADL means [Argument-Dependent Lookup](http://en.cppreference.com/w/cpp/language/adl)).
+The default serializer for `nlohmann::json` is `nlohmann::adl_serializer` (ADL means [Argument-Dependent Lookup](https://en.cppreference.com/w/cpp/language/adl)).
 
 It is implemented like this (simplified):
 
@@ -590,7 +685,7 @@ struct adl_serializer {
 };
 ```
 
-This serializer works fine when you have control over the type's namespace. However, what about `boost::optional`, or `std::filesystem::path` (C++17)? Hijacking the `boost` namespace is pretty bad, and it's illegal to add something other than template specializations to `std`...
+This serializer works fine when you have control over the type's namespace. However, what about `boost::optional` or `std::filesystem::path` (C++17)? Hijacking the `boost` namespace is pretty bad, and it's illegal to add something other than template specializations to `std`...
 
 To solve this, you need to add a specialization of `adl_serializer` to the `nlohmann` namespace, here's an example:
 
@@ -612,7 +707,7 @@ namespace nlohmann {
             if (j.is_null()) {
                 opt = boost::none;
             } else {
-                opt = j.get<T>(); // same as above, but with 
+                opt = j.get<T>(); // same as above, but with
                                   // adl_serializer<T>::from_json
             }
         }
@@ -622,7 +717,7 @@ namespace nlohmann {
 
 #### How can I use `get()` for non-default constructible/non-copyable types?
 
-There is a way, if your type is [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible). You will need to specialize the `adl_serializer` as well, but with a special `from_json` overload:
+There is a way, if your type is [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible). You will need to specialize the `adl_serializer` as well, but with a special `from_json` overload:
 
 ```cpp
 struct move_only_type {
@@ -630,7 +725,7 @@ struct move_only_type {
     move_only_type(int ii): i(ii) {}
     move_only_type(const move_only_type&) = delete;
     move_only_type(move_only_type&&) = default;
-    
+
     int i;
 };
 
@@ -642,7 +737,7 @@ namespace nlohmann {
         static move_only_type from_json(const json& j) {
             return {j.get<int>()};
         }
-        
+
         // Here's the catch! You must provide a to_json method! Otherwise you
         // will not be able to convert move_only_type to json, since you fully
         // specialized adl_serializer on that type
@@ -659,9 +754,9 @@ Yes. You might want to take a look at [`unit-udt.cpp`](https://github.com/nlohma
 
 If you write your own serializer, you'll need to do a few things:
 
-* use a different `basic_json` alias than `nlohmann::json` (the last template parameter of `basic_json` is the `JSONSerializer`)
-* use your `basic_json` alias (or a template parameter) in all your `to_json`/`from_json` methods
-* use `nlohmann::to_json` and `nlohmann::from_json` when you need ADL
+- use a different `basic_json` alias than `nlohmann::json` (the last template parameter of `basic_json` is the `JSONSerializer`)
+- use your `basic_json` alias (or a template parameter) in all your `to_json`/`from_json` methods
+- use `nlohmann::to_json` and `nlohmann::from_json` when you need ADL
 
 Here is an example, without simplifications, that only accepts types with a size <= 32, and uses ADL.
 
@@ -677,7 +772,7 @@ struct less_than_32_serializer {
                                  // this is where the magic happens
         to_json(j, value);
     }
-    
+
     template <typename BasicJsonType>
     static void from_json(const BasicJsonType& j, T& value) {
         // same thing here
@@ -699,7 +794,7 @@ struct bad_serializer
       // if BasicJsonType::json_serializer == bad_serializer ... oops!
       j = value;
     }
-    
+
     template <typename BasicJsonType>
     static void to_json(const BasicJsonType& j, T& value) {
       // this calls BasicJsonType::json_serializer<T>::from_json(j, value);
@@ -709,9 +804,9 @@ struct bad_serializer
 };
 ```
 
-### Binary formats (CBOR and MessagePack)
+### Binary formats (CBOR, MessagePack, and UBJSON)
 
-Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [CBOR](http://cbor.io) (Concise Binary Object Representation) and [MessagePack](http://msgpack.org) to efficiently encode JSON values to byte vectors and to decode such vectors.
+Though JSON is a ubiquitous data format, it is not a very compact format suitable for data exchange, for instance over a network. Hence, the library supports [CBOR](http://cbor.io) (Concise Binary Object Representation), [MessagePack](http://msgpack.org), and [UBJSON](http://ubjson.org) (Universal Binary JSON Specification) to efficiently encode JSON values to byte vectors and to decode such vectors.
 
 ```cpp
 // create a JSON value
@@ -720,7 +815,7 @@ json j = R"({"compact": true, "schema": 0})"_json;
 // serialize to CBOR
 std::vector<std::uint8_t> v_cbor = json::to_cbor(j);
 
-// 0xa2, 0x67, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xf5, 0x66, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x00
+// 0xA2, 0x67, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0xF5, 0x66, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00
 
 // roundtrip
 json j_from_cbor = json::from_cbor(v_cbor);
@@ -728,19 +823,27 @@ json j_from_cbor = json::from_cbor(v_cbor);
 // serialize to MessagePack
 std::vector<std::uint8_t> v_msgpack = json::to_msgpack(j);
 
-// 0x82, 0xa7, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0xc3, 0xa6, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x00
+// 0x82, 0xA7, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0xC3, 0xA6, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x00
 
 // roundtrip
 json j_from_msgpack = json::from_msgpack(v_msgpack);
+
+// serialize to UBJSON
+std::vector<std::uint8_t> v_ubjson = json::to_ubjson(j);
+
+// 0x7B, 0x69, 0x07, 0x63, 0x6F, 0x6D, 0x70, 0x61, 0x63, 0x74, 0x54, 0x69, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x61, 0x69, 0x00, 0x7D
+
+// roundtrip
+json j_from_ubjson = json::from_ubjson(v_ubjson);
 ```
 
 
 ## Supported compilers
 
-Though it's 2017 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
+Though it's 2018 already, the support for C++11 is still a bit sparse. Currently, the following compilers are known to work:
 
-- GCC 4.9 - 7.2 (and possibly later)
-- Clang 3.4 - 5.0 (and possibly later)
+- GCC 4.9 - 8.2 (and possibly later)
+- Clang 3.4 - 6.1 (and possibly later)
 - Intel C++ Compiler 17.0.2 (and possibly later)
 - Microsoft Visual C++ 2015 / Build Tools 14.0.25123.0 (and possibly later)
 - Microsoft Visual C++ 2017 / Build Tools 15.5.180.51428 (and possibly later)
@@ -751,43 +854,49 @@ Please note:
 
 - GCC 4.8 does not work because of two bugs ([55817](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55817) and [57824](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824)) in the C++11 support. Note there is a [pull request](https://github.com/nlohmann/json/pull/212) to fix some of the issues.
 - Android defaults to using very old compilers and C++ libraries. To fix this, add the following to your `Application.mk`. This will switch to the LLVM C++ library, the Clang compiler, and enable C++11 and other features disabled by default.
- 
+
     ```
     APP_STL := c++_shared
     NDK_TOOLCHAIN_VERSION := clang3.6
     APP_CPPFLAGS += -frtti -fexceptions
     ```
- 
+
     The code compiles successfully with [Android NDK](https://developer.android.com/ndk/index.html?hl=ml), Revision 9 - 11 (and possibly later) and [CrystaX's Android NDK](https://www.crystax.net/en/android/ndk) version 10.
 
 - For GCC running on MinGW or Android SDK, the error `'to_string' is not a member of 'std'` (or similarly, for `strtod`) may occur. Note this is not an issue with the code,  but rather with the compiler itself. On Android, see above to build with a newer environment.  For MinGW, please refer to [this site](http://tehsausage.com/mingw-to-string) and [this discussion](https://github.com/nlohmann/json/issues/136) for information on how to fix this bug. For Android NDK using `APP_STL := gnustl_static`, please refer to [this discussion](https://github.com/nlohmann/json/issues/219).
 
+- Unsupported versions of GCC and Clang are rejected by `#error` directives. This can be switched off by defining `JSON_SKIP_UNSUPPORTED_COMPILER_CHECK`. Note that you can expect no support in this case.
+
 The following compilers are currently used in continuous integration at [Travis](https://travis-ci.org/nlohmann/json) and [AppVeyor](https://ci.appveyor.com/project/nlohmann/json):
 
 | Compiler        | Operating System             | Version String |
 |-----------------|------------------------------|----------------|
-| GCC 4.9.4       | Ubuntu 14.04.5 LTS           | g++-4.9 (Ubuntu 4.9.4-2ubuntu1~14.04.1) 4.9.4 |
-| GCC 5.4.1       | Ubuntu 14.04.5 LTS           | g++-5 (Ubuntu 5.4.1-2ubuntu1~14.04) 5.4.1 20160904 |
-| GCC 6.3.0       | Ubuntu 14.04.5 LTS           | g++-6 (Ubuntu/Linaro 6.3.0-18ubuntu2~14.04) 6.3.0 20170519 |
-| GCC 7.1.0       | Ubuntu 14.04.5 LTS           | g++-7 (Ubuntu 7.1.0-5ubuntu2~14.04) 7.1.0
-| Clang 3.5.0     | Ubuntu 14.04.5 LTS           | clang version 3.5.0-4ubuntu2~trusty2 (tags/RELEASE_350/final) |
-| Clang 3.6.2     | Ubuntu 14.04.5 LTS           | clang version 3.6.2-svn240577-1~exp1 (branches/release_36) |
-| Clang 3.7.1     | Ubuntu 14.04.5 LTS           | clang version 3.7.1-svn253571-1~exp1 (branches/release_37) |
-| Clang 3.8.0     | Ubuntu 14.04.5 LTS           | clang version 3.8.0-2ubuntu3~trusty5 (tags/RELEASE_380/final) |
-| Clang 3.9.1     | Ubuntu 14.04.5 LTS           | clang version 3.9.1-4ubuntu3~14.04.2 (tags/RELEASE_391/rc2) |
-| Clang 4.0.1     | Ubuntu 14.04.5 LTS           | clang version 4.0.1-svn305264-1~exp1 (branches/release_40) |
-| Clang 5.0.0     | Ubuntu 14.04.5 LTS           | clang version 5.0.0-svn310902-1~exp1 (branches/release_50) |
-| Clang Xcode 6.4 | Darwin Kernel Version 14.3.0 (OSX 10.10.3) | Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn) |
-| Clang Xcode 7.3 | Darwin Kernel Version 15.0.0 (OSX 10.10.5) | Apple LLVM version 7.3.0 (clang-703.0.29) |
-| Clang Xcode 8.0 | Darwin Kernel Version 15.6.0 | Apple LLVM version 8.0.0 (clang-800.0.38) |
-| Clang Xcode 8.1 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
-| Clang Xcode 8.2 | Darwin Kernel Version 16.1.0 (macOS 10.12.1) | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
-| Clang Xcode 8.3 | Darwin Kernel Version 16.5.0 (macOS 10.12.4) | Apple LLVM version 8.1.0 (clang-802.0.38) |
-| Clang Xcode 9.0 | Darwin Kernel Version 16.7.0 (macOS 10.12.6) | Apple LLVM version 9.0.0 (clang-900.0.37) |
-| Clang Xcode 9.1 | Darwin Kernel Version 16.7.0 (macOS 10.12.6) | Apple LLVM version 9.0.0 (clang-900.0.38) |
-| Clang Xcode 9.2 | Darwin Kernel Version 16.7.0 (macOS 10.12.6) | Apple LLVM version 8.1.0 (clang-900.0.39.2) |
-| Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25420.1, MSVC 19.0.24215.1 | 
-| Visual Studio 2017 | Windows Server 2016 | Microsoft (R) Build Engine version 15.5.180.51428, MSVC 19.12.25830.2 |
+| GCC 4.9.4       | Ubuntu 14.04.1 LTS           | g++-4.9 (Ubuntu 4.9.4-2ubuntu1~14.04.1) 4.9.4 |
+| GCC 5.5.0       | Ubuntu 14.04.1 LTS           | g++-5 (Ubuntu 5.5.0-12ubuntu1~14.04) 5.5.0 20171010 |
+| GCC 6.4.0       | Ubuntu 14.04.1 LTS           | g++-6 (Ubuntu 6.4.0-17ubuntu1~14.04) 6.4.0 20180424 |
+| GCC 7.3.0       | Ubuntu 14.04.1 LTS           | g++-7 (Ubuntu 7.3.0-21ubuntu1~14.04) 7.3.0 |
+| GCC 7.3.0       | Windows Server 2012 R2 (x64) | g++ (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.3.0 |
+| GCC 8.1.0       | Ubuntu 14.04.1 LTS           | g++-8 (Ubuntu 8.1.0-5ubuntu1~14.04) 8.1.0 |
+| Clang 3.5.0     | Ubuntu 14.04.1 LTS           | clang version 3.5.0-4ubuntu2~trusty2 (tags/RELEASE_350/final) (based on LLVM 3.5.0) |
+| Clang 3.6.2     | Ubuntu 14.04.1 LTS           | clang version 3.6.2-svn240577-1~exp1 (branches/release_36) (based on LLVM 3.6.2) |
+| Clang 3.7.1     | Ubuntu 14.04.1 LTS           | clang version 3.7.1-svn253571-1~exp1 (branches/release_37) (based on LLVM 3.7.1) |
+| Clang 3.8.0     | Ubuntu 14.04.1 LTS           | clang version 3.8.0-2ubuntu3~trusty5 (tags/RELEASE_380/final) |
+| Clang 3.9.1     | Ubuntu 14.04.1 LTS           | clang version 3.9.1-4ubuntu3~14.04.3 (tags/RELEASE_391/rc2) |
+| Clang 4.0.1     | Ubuntu 14.04.1 LTS           | clang version 4.0.1-svn305264-1~exp1 (branches/release_40) |
+| Clang 5.0.2     | Ubuntu 14.04.1 LTS           | clang version 5.0.2-svn328729-1~exp1~20180509123505.100 (branches/release_50) |
+| Clang 6.0.1     | Ubuntu 14.04.1 LTS           | clang version 6.0.1-svn334776-1~exp1~20180726133705.85 (branches/release_60) |
+| Clang Xcode 6.4 | OSX 10.10.5 | Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn) |
+| Clang Xcode 7.3 | OSX 10.11.6 | Apple LLVM version 7.3.0 (clang-703.0.31) |
+| Clang Xcode 8.0 | OSX 10.11.6 | Apple LLVM version 8.0.0 (clang-800.0.38) |
+| Clang Xcode 8.1 | OSX 10.12.6 | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
+| Clang Xcode 8.2 | OSX 10.12.6 | Apple LLVM version 8.0.0 (clang-800.0.42.1) |
+| Clang Xcode 8.3 | OSX 10.11.6 | Apple LLVM version 8.1.0 (clang-802.0.38) |
+| Clang Xcode 9.0 | OSX 10.12.6 | Apple LLVM version 9.0.0 (clang-900.0.37) |
+| Clang Xcode 9.1 | OSX 10.12.6 | Apple LLVM version 9.0.0 (clang-900.0.38) |
+| Clang Xcode 9.2 | OSX 10.13.3 | Apple LLVM version 9.1.0 (clang-902.0.39.1) |
+| Clang Xcode 9.3 | OSX 10.13.3 | Apple LLVM version 9.1.0 (clang-902.0.39.2) |
+| Visual Studio 14 2015 | Windows Server 2012 R2 (x64) | Microsoft (R) Build Engine version 14.0.25420.1, MSVC 19.0.24215.1 |
+| Visual Studio 2017 | Windows Server 2016 | Microsoft (R) Build Engine version 15.7.180.61344, MSVC 19.14.26433.0 |
 
 ## License
 
@@ -795,7 +904,7 @@ The following compilers are currently used in continuous integration at [Travis]
 
 The class is licensed under the [MIT License](http://opensource.org/licenses/MIT):
 
-Copyright &copy; 2013-2017 [Niels Lohmann](http://nlohmann.me)
+Copyright &copy; 2013-2018 [Niels Lohmann](http://nlohmann.me)
 
 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:
 
@@ -807,12 +916,17 @@ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR I
 
 The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright &copy; 2008-2009 [Björn Hoehrmann](http://bjoern.hoehrmann.de/) <[email protected]>
 
+The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the [MIT License](http://opensource.org/licenses/MIT) (see above). Copyright &copy; 2009 [Florian Loitsch](http://florian.loitsch.com/)
+
 ## Contact
 
-If you have questions regarding the library, I would like to invite you to [open an issue at Github](https://github.com/nlohmann/json/issues/new). Please describe your request, problem, or question as detailed as possible, and also mention the version of the library you are using as well as the version of your compiler and operating system. Opening an issue at Github allows other users and contributors to this library to collaborate. For instance, I have little experience with MSVC, and most issues in this regard have been solved by a growing community. If you have a look at the [closed issues](https://github.com/nlohmann/json/issues?q=is%3Aissue+is%3Aclosed), you will see that we react quite timely in most cases.
+If you have questions regarding the library, I would like to invite you to [open an issue at GitHub](https://github.com/nlohmann/json/issues/new). Please describe your request, problem, or question as detailed as possible, and also mention the version of the library you are using as well as the version of your compiler and operating system. Opening an issue at GitHub allows other users and contributors to this library to collaborate. For instance, I have little experience with MSVC, and most issues in this regard have been solved by a growing community. If you have a look at the [closed issues](https://github.com/nlohmann/json/issues?q=is%3Aissue+is%3Aclosed), you will see that we react quite timely in most cases.
 
 Only if your request would contain confidential information, please [send me an email](mailto:[email protected]). For encrypted messages, please use [this key](https://keybase.io/nlohmann/pgp_keys.asc).
 
+## Security
+
+[Commits by Niels Lohmann](https://github.com/nlohmann/json/commits) and [releases](https://github.com/nlohmann/json/releases) are signed with this [PGP Key](https://keybase.io/nlohmann/pgp_keys.asc?fingerprint=797167ae41c0a6d9232e48457f3cea63ae251b69).
 
 ## Thanks
 
@@ -847,7 +961,7 @@ I deeply appreciate the help of the following people.
 - [Corbin Hughes](https://github.com/nibroc) fixed some typos in the contribution guidelines.
 - [twelsby](https://github.com/twelsby) fixed the array subscript operator, an issue that failed the MSVC build, and floating-point parsing/dumping. He further added support for unsigned integer numbers and implemented better roundtrip support for parsed numbers.
 - [Volker Diels-Grabsch](https://github.com/vog) fixed a link in the README file.
-- [msm-](https://github.com/msm-) added support for american fuzzy lop. 
+- [msm-](https://github.com/msm-) added support for American Fuzzy Lop.
 - [Annihil](https://github.com/Annihil) fixed an example in the README file.
 - [Themercee](https://github.com/Themercee) noted a wrong URL in the README file.
 - [Lv Zheng](https://github.com/lv-zheng) fixed a namespace issue with `int64_t` and `uint64_t`.
@@ -860,14 +974,14 @@ I deeply appreciate the help of the following people.
 - [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release.
 - [Damien](https://github.com/dtoma) fixed one of the last conversion warnings.
 - [Thomas Braun](https://github.com/t-b) fixed a warning in a test case.
-- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). He also implemented the magic behind the serialization/deserialization of user-defined types.
+- [Théo DELRIEU](https://github.com/theodelrieu) patiently and constructively oversaw the long way toward [iterator-range parsing](https://github.com/nlohmann/json/issues/290). He also implemented the magic behind the serialization/deserialization of user-defined types and split the single header file into smaller chunks.
 - [Stefan](https://github.com/5tefan) fixed a minor issue in the documentation.
 - [Vasil Dimov](https://github.com/vasild) fixed the documentation regarding conversions from `std::multiset`.
 - [ChristophJud](https://github.com/ChristophJud) overworked the CMake files to ease project inclusion.
 - [Vladimir Petrigo](https://github.com/vpetrigo) made a SFINAE hack more readable and added Visual Studio 17 to the build matrix.
 - [Denis Andrejew](https://github.com/seeekr) fixed a grammar issue in the README file.
 - [Pierre-Antoine Lacaze](https://github.com/palacaze) found a subtle bug in the `dump()` function.
-- [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](http://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing.
+- [TurpentineDistillery](https://github.com/TurpentineDistillery) pointed to [`std::locale::classic()`](https://en.cppreference.com/w/cpp/locale/locale/classic) to avoid too much locale joggling, found some nice performance improvements in the parser, improved the benchmarking code, and realized locale-independent number parsing and printing.
 - [cgzones](https://github.com/cgzones) had an idea how to fix the Coverity scan.
 - [Jared Grubb](https://github.com/jaredgrubb) silenced a nasty documentation warning.
 - [Yixin Zhang](https://github.com/qwename) fixed an integer overflow check.
@@ -893,7 +1007,7 @@ I deeply appreciate the help of the following people.
 - [Vincent Thiery](https://github.com/vthiery) maintains a package for the Conan package manager.
 - [Steffen](https://github.com/koemeet) fixed a potential issue with MSVC and `std::min`.
 - [Mike Tzou](https://github.com/Chocobo1) fixed some typos.
-- [amrcode](https://github.com/amrcode) noted a missleading documentation about comparison of floats.
+- [amrcode](https://github.com/amrcode) noted a misleading documentation about comparison of floats.
 - [Oleg Endo](https://github.com/olegendo) reduced the memory consumption by replacing `<iostream>` with `<iosfwd>`.
 - [dan-42](https://github.com/dan-42) cleaned up the CMake files to simplify including/reusing of the library.
 - [Nikita Ofitserov](https://github.com/himikof) allowed for moving values from initializer lists.
@@ -910,25 +1024,50 @@ I deeply appreciate the help of the following people.
 - [Nate Vargas](https://github.com/eld00d) added a Doxygen tag file.
 - [pvleuven](https://github.com/pvleuven) helped fixing a warning in ICC.
 - [Pavel](https://github.com/crea7or) helped fixing some warnings in MSVC.
-- [Jamie Seward](https://github.com/jseward) avoided unneccessary string copies in `find()` and `count()`.
+- [Jamie Seward](https://github.com/jseward) avoided unnecessary string copies in `find()` and `count()`.
 - [Mitja](https://github.com/Itja) fixed some typos.
 - [Jorrit Wronski](https://github.com/jowr) updated the Hunter package links.
 - [Matthias Möller](https://github.com/TinyTinni) added a `.natvis` for the MSVC debug view.
 - [bogemic](https://github.com/bogemic) fixed some C++17 deprecation warnings.
 - [Eren Okka](https://github.com/erengy) fixed some MSVC warnings.
-
+- [abolz](https://github.com/abolz) integrated the Grisu2 algorithm for proper floating-point formatting, allowing more roundtrip checks to succeed.
+- [Vadim Evard](https://github.com/Pipeliner) fixed a Markdown issue in the README.
+- [zerodefect](https://github.com/zerodefect) fixed a compiler warning.
+- [Kert](https://github.com/kaidokert) allowed to template the string type in the serialization and added the possibility to override the exceptional behavior.
+- [mark-99](https://github.com/mark-99) helped fixing an ICC error.
+- [Patrik Huber](https://github.com/patrikhuber) fixed links in the README file.
+- [johnfb](https://github.com/johnfb) found a bug in the implementation of CBOR's indefinite length strings.
+- [Paul Fultz II](https://github.com/pfultz2) added a note on the cget package manager.
+- [Wilson Lin](https://github.com/wla80) made the integration section of the README more concise.
+- [RalfBielig](https://github.com/ralfbielig) detected and fixed a memory leak in the parser callback.
+- [agrianius](https://github.com/agrianius) allowed to dump JSON to an alternative string type.
+- [Kevin Tonon](https://github.com/ktonon) overworked the C++11 compiler checks in CMake.
+- [Axel Huebl](https://github.com/ax3l) simplified a CMake check and added support for the [Spack package manager](https://spack.io).
+- [Carlos O'Ryan](https://github.com/coryan) fixed a typo.
+- [James Upjohn](https://github.com/jammehcow) fixed a version number in the compilers section.
+- [Chuck Atkins](https://github.com/chuckatkins) adjusted the CMake files to the CMake packaging guidelines
+- [Jan Schöppach](https://github.com/dns13) fixed a typo.
+- [martin-mfg](https://github.com/martin-mfg) fixed a typo.
+- [Matthias Möller](https://github.com/TinyTinni) removed the dependency from `std::stringstream`.
+- [agrianius](https://github.com/agrianius) added code to use alternative string implementations.
+- [Daniel599](https://github.com/Daniel599) allowed to use more algorithms with the `items()` function.
+- [Julius Rakow](https://github.com/jrakow) fixed the Meson include directory and fixed the links to [cppreference.com](cppreference.com).
+- [Sonu Lohani](https://github.com/sonulohani) fixed the compilation with MSVC 2015 in debug mode.
+- [grembo](https://github.com/grembo) fixed the test suite and re-enabled several test cases.
+- [Hyeon Kim](https://github.com/simnalamburt) introduced the macro `JSON_INTERNAL_CATCH` to control the exception handling inside the library.
+- [thyu](https://github.com/thyu) fixed a compiler warning.
 
 Thanks a lot for helping out! Please [let me know](mailto:[email protected]) if I forgot someone.
 
 
 ## Used third-party tools
 
-The library itself contains of a single header file licensed under the MIT license. However, it is built, tested, documented, and whatnot using a lot of third-party tools and services. Thanks a lot!
+The library itself consists of a single header file licensed under the MIT license. However, it is built, tested, documented, and whatnot using a lot of third-party tools and services. Thanks a lot!
 
+- [**amalgamate.py - Amalgamate C source and header files**](https://github.com/edlund/amalgamate) to create a single header file
 - [**American fuzzy lop**](http://lcamtuf.coredump.cx/afl/) for fuzz testing
 - [**AppVeyor**](https://www.appveyor.com) for [continuous integration](https://ci.appveyor.com/project/nlohmann/json) on Windows
 - [**Artistic Style**](http://astyle.sourceforge.net) for automatic source code identation
-- [**benchpress**](https://github.com/sbs-ableton/benchpress) to benchmark the code
 - [**Catch**](https://github.com/philsquared/Catch) for the unit tests
 - [**Clang**](http://clang.llvm.org) for compilation with code sanitizers
 - [**Cmake**](https://cmake.org) for build automation
@@ -936,17 +1075,17 @@ The library itself contains of a single header file licensed under the MIT licen
 - [**Coveralls**](https://coveralls.io) to measure [code coverage](https://coveralls.io/github/nlohmann/json)
 - [**Coverity Scan**](https://scan.coverity.com) for [static analysis](https://scan.coverity.com/projects/nlohmann-json)
 - [**cppcheck**](http://cppcheck.sourceforge.net) for static analysis
-- [**cxxopts**](https://github.com/jarro2783/cxxopts) to let benchpress parse command-line parameters
 - [**Doxygen**](http://www.stack.nl/~dimitri/doxygen/) to generate [documentation](https://nlohmann.github.io/json/)
 - [**git-update-ghpages**](https://github.com/rstacruz/git-update-ghpages) to upload the documentation to gh-pages
-- [**Github Changelog Generator**](https://github.com/skywinder/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md)
+- [**GitHub Changelog Generator**](https://github.com/skywinder/github-changelog-generator) to generate the [ChangeLog](https://github.com/nlohmann/json/blob/develop/ChangeLog.md)
+- [**Google Benchmark**](https://github.com/google/benchmark) to implement the benchmarks
 - [**libFuzzer**](http://llvm.org/docs/LibFuzzer.html) to implement fuzz testing for OSS-Fuzz
-- [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library
+- [**OSS-Fuzz**](https://github.com/google/oss-fuzz) for continuous fuzz testing of the library ([project repository](https://github.com/google/oss-fuzz/tree/master/projects/json))
 - [**Probot**](https://probot.github.io) for automating maintainer tasks such as closing stale issues, requesting missing information, or detecting toxic comments.
 - [**send_to_wandbox**](https://github.com/nlohmann/json/blob/develop/doc/scripts/send_to_wandbox.py) to send code examples to [Wandbox](http://melpon.org/wandbox)
 - [**Travis**](https://travis-ci.org) for [continuous integration](https://travis-ci.org/nlohmann/json) on Linux and macOS
 - [**Valgrind**](http://valgrind.org) to check for correct memory management
-- [**Wandbox**](http://melpon.org/wandbox) for [online examples](https://wandbox.org/permlink/Op57X0V7fTf2tdwl)
+- [**Wandbox**](http://melpon.org/wandbox) for [online examples](https://wandbox.org/permlink/TarF5pPn9NtHQjhf)
 
 
 ## Projects using JSON for Modern C++
@@ -956,7 +1095,7 @@ The library is currently used in Apple macOS Sierra and iOS 10. I am not sure wh
 
 ## Notes
 
-- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](http://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a2e26bd0b0168abb61f67ad5bcd5b9fa1.html#a2e26bd0b0168abb61f67ad5bcd5b9fa1) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a674de1ee73e6bf4843fc5dc1351fb726.html#a674de1ee73e6bf4843fc5dc1351fb726).
+- The code contains numerous debug **assertions** which can be switched off by defining the preprocessor macro `NDEBUG`, see the [documentation of `assert`](https://en.cppreference.com/w/cpp/error/assert). In particular, note [`operator[]`](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a2e26bd0b0168abb61f67ad5bcd5b9fa1.html#a2e26bd0b0168abb61f67ad5bcd5b9fa1) implements **unchecked access** for const objects: If the given key is not present, the behavior is undefined (think of a dereferenced null pointer) and yields an [assertion failure](https://github.com/nlohmann/json/issues/289) if assertions are switched on. If you are not sure whether an element in an object exists, use checked access with the [`at()` function](https://nlohmann.github.io/json/classnlohmann_1_1basic__json_a674de1ee73e6bf4843fc5dc1351fb726.html#a674de1ee73e6bf4843fc5dc1351fb726).
 - As the exact type of a number is not defined in the [JSON specification](http://rfc7159.net/rfc7159), this library tries to choose the best fitting C++ number type automatically. As a result, the type `double` may be used to store numbers which may yield [**floating-point exceptions**](https://github.com/nlohmann/json/issues/181) in certain rare situations if floating-point exceptions have been unmasked in the calling code. These exceptions are not caused by the library and need to be fixed in the calling code, such as by re-masking the exceptions prior to calling library functions.
 - The library supports **Unicode input** as follows:
   - Only **UTF-8** encoded input is supported which is the default encoding for JSON according to [RFC 7159](http://rfc7159.net/rfc7159#rfc.section.8.1).

文件差异内容过多而无法显示
+ 766 - 823
ext/json/json.hpp


+ 5 - 2
include/ZeroTierDebug.h

@@ -76,6 +76,9 @@
 #ifdef __APPLE__
   #define ZT_THREAD_ID (long)0 // (long)gettid()
 #endif
+#ifdef __FreeBSD__
+  #define ZT_THREAD_ID (long)0 // (long)gettid()
+#endif
 #ifdef _WIN32
   #define ZT_THREAD_ID (long)0 // 
 #endif
@@ -86,7 +89,7 @@
 		#include <android/log.h>
 		#define ZT_LOG_TAG "ZTSDK"
 #endif
-#if defined(ZT_TRACE)
+#if defined(ZT_DEBUG_TRACE)
 	#if ZT_MSG_INFO == true
 		#if defined(__ANDROID__)
 			#define DEBUG_INFO(fmt, args...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, ZT_LOG_TAG, \
@@ -96,7 +99,7 @@
 			#define DEBUG_INFO(fmt, ...) fprintf(stderr, ZT_GRN "INFO [%ld]: %17s:%5d:%25s: " fmt "\n" \
 					ZT_RESET, ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, __VA_ARGS__)
 		#endif
-		#if defined(__linux__) or defined(__APPLE__)
+		#if defined(__linux__) or defined(__APPLE__) or defined(__FreeBSD__)
 			#define DEBUG_INFO(fmt, args ...) fprintf(stderr, ZT_GRN "INFO [%ld]: %17s:%5d:%25s: " fmt "\n" \
 					ZT_RESET, ZT_THREAD_ID, ZT_FILENAME, __LINE__, __FUNCTION__, ##args)
 		#endif

+ 98 - 4
include/ZeroTierOne.h

@@ -411,7 +411,7 @@ enum ZT_ResultCode
 	ZT_RESULT_ERROR_UNSUPPORTED_OPERATION = 1001,
 
 	/**
-	 * The requestion operation was given a bad parameter or was called in an invalid state
+	 * The requested operation was given a bad parameter or was called in an invalid state
 	 */
 	ZT_RESULT_ERROR_BAD_PARAMETER = 1002
 };
@@ -422,6 +422,35 @@ enum ZT_ResultCode
  */
 #define ZT_ResultCode_isFatal(x) ((((int)(x)) >= 100)&&(((int)(x)) < 1000))
 
+/**
+ * The multipath algorithm in use by this node.
+ */
+enum ZT_MultipathMode
+{
+	/**
+	 * No active multipath.
+	 *
+	 * Traffic is merely sent over the strongest path. That being
+	 * said, this mode will automatically failover in the event that a link goes down.
+	 */
+	ZT_MULTIPATH_NONE = 0,
+
+	/**
+	 * Traffic is randomly distributed among all active paths.
+	 *
+	 * Will cease sending traffic over links that appear to be stale.
+	 */
+	ZT_MULTIPATH_RANDOM = 1,
+
+	/**
+	 * Traffic is allocated across all active paths in proportion to their strength and
+	 * reliability.
+	 *
+	 * Will cease sending traffic over links that appear to be stale.
+	 */
+	ZT_MULTIPATH_PROPORTIONALLY_BALANCED = 2,
+};
+
 /**
  * Status codes sent to status update callback when things happen
  */
@@ -715,6 +744,11 @@ enum ZT_VirtualNetworkRuleType
 	 */
 	ZT_NETWORK_RULE_ACTION_BREAK = 5,
 
+	/**
+	 * Place a matching frame in the specified QoS bucket
+	 */
+	ZT_NETWORK_RULE_ACTION_PRIORITY = 6,
+
 	/**
 	 * Maximum ID for an ACTION, anything higher is a MATCH
 	 */
@@ -905,6 +939,11 @@ typedef struct
 			uint32_t flags;
 			uint16_t length;
 		} fwd;
+
+		/**
+		 * Quality of Service (QoS) bucket we want a frame to be placed in
+		 */
+		uint8_t qosBucket;
 	} v;
 } ZT_VirtualNetworkRule;
 
@@ -1185,6 +1224,56 @@ typedef struct
 	 */
 	uint64_t trustedPathId;
 
+	/**
+	 * One-way latency
+	 */
+	float latency;
+
+	/**
+	 * How much latency varies over time
+	 */
+	float packetDelayVariance;
+
+	/**
+	 * How much observed throughput varies over time
+	 */
+	float throughputDisturbCoeff;
+
+	/**
+	 * Packet Error Ratio (PER)
+	 */
+	float packetErrorRatio;
+
+	/**
+	 * Packet Loss Ratio (PLR)
+	 */
+	float packetLossRatio;
+
+	/**
+	 * Stability of the path
+	 */
+	float stability;
+
+	/**
+	 * Current throughput (moving average)
+	 */
+	uint64_t throughput;
+
+	/**
+	 * Maximum observed throughput for this path
+	 */
+	uint64_t maxThroughput;
+
+	/**
+	 * Percentage of traffic allocated to this path
+	 */
+	float allocation;
+
+	/**
+	 * Name of physical interface (for monitoring)
+	 */
+	char *ifname;
+
 	/**
 	 * Is path expired?
 	 */
@@ -1236,6 +1325,11 @@ typedef struct
 	 */
 	unsigned int pathCount;
 
+	/**
+	 * Whether this peer was ever reachable via an aggregate link
+	 */
+	bool hadAggregateLink;
+
 	/**
 	 * Known network paths to peer
 	 */
@@ -1462,7 +1556,7 @@ typedef int (*ZT_WirePacketSendFunction)(
 /**
  * Function to check whether a path should be used for ZeroTier traffic
  *
- * Paramters:
+ * Parameters:
  *  (1) Node
  *  (2) User pointer
  *  (3) ZeroTier address or 0 for none/any
@@ -1495,7 +1589,7 @@ typedef int (*ZT_PathCheckFunction)(
  *  (1) Node
  *  (2) User pointer
  *  (3) ZeroTier address (least significant 40 bits)
- *  (4) Desried address family or -1 for any
+ *  (4) Desired address family or -1 for any
  *  (5) Buffer to fill with result
  *
  * If provided this function will be occasionally called to get physical
@@ -1660,7 +1754,7 @@ ZT_SDK_API enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void
  * Join a network
  *
  * This may generate calls to the port config callback before it returns,
- * or these may be deffered if a netconf is not available yet.
+ * or these may be differed if a netconf is not available yet.
  *
  * If we are already a member of the network, nothing is done and OK is
  * returned.

+ 2 - 1
make-bsd.mk

@@ -123,6 +123,7 @@ endif
 ifeq ($(ZT_USE_ARM32_NEON_ASM_SALSA2012),1)
 	override DEFS+=-DZT_USE_ARM32_NEON_ASM_SALSA2012
 	override CORE_OBJS+=ext/arm32-neon-salsa2012-asm/salsa2012.o
+	override ASFLAGS+=-meabi=5
 endif
 
 override DEFS+=-DZT_BUILD_PLATFORM=$(ZT_BUILD_PLATFORM) -DZT_BUILD_ARCHITECTURE=$(ZT_ARCHITECTURE) -DZT_SOFTWARE_UPDATE_DEFAULT="\"disable\""
@@ -159,7 +160,7 @@ clean:
 	rm -rf *.a *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o build-* zerotier-one zerotier-idtool zerotier-selftest zerotier-cli $(ONE_OBJS) $(CORE_OBJS)
 
 debug:	FORCE
-	gmake -j 4 ZT_DEBUG=1
+	$(MAKE) -j ZT_DEBUG=1
 
 install:	one
 	rm -f /usr/local/sbin/zerotier-one

+ 20 - 1
make-linux.mk

@@ -17,6 +17,10 @@ DESTDIR?=
 
 include objects.mk
 ONE_OBJS+=osdep/LinuxEthernetTap.o
+ONE_OBJS+=osdep/LinuxNetLink.o
+
+NLTEST_OBJS+=osdep/LinuxNetLink.o node/InetAddress.o node/Utils.o node/Salsa20.o
+NLTEST_OBJS+=nltest.o
 
 # Auto-detect miniupnpc and nat-pmp as well and use system libs if present,
 # otherwise build into binary as done on Mac and Windows.
@@ -42,8 +46,15 @@ endif
 # Trying to use dynamically linked libhttp-parser causes tons of compatibility problems.
 ONE_OBJS+=ext/http-parser/http_parser.o
 
+# Build with address sanitization library for advanced debugging (clang)
+ifeq ($(ZT_SANITIZE),1)
+	DEFS+=-fsanitize=address -DASAN_OPTIONS=symbolize=1
+endif
+ifeq ($(ZT_DEBUG_TRACE),1)
+	DEFS+=-DZT_DEBUG_TRACE
+endif
 ifeq ($(ZT_TRACE),1)
-	override DEFS+=-DZT_TRACE
+	DEFS+=-DZT_TRACE
 endif
 
 ifeq ($(ZT_RULES_ENGINE_DEBUGGING),1)
@@ -90,6 +101,11 @@ ifeq ($(ZT_USE_TEST_TAP),1)
 	override DEFS+=-DZT_USE_TEST_TAP
 endif
 
+ifeq ($(ZT_VAULT_SUPPORT),1)
+	override DEFS+=-DZT_VAULT_SUPPORT=1
+	override LDLIBS+=-lcurl
+endif
+
 # Uncomment for gprof profile build
 #CFLAGS=-Wall -g -pg -pthread $(INCLUDES) $(DEFS)
 #CXXFLAGS=-Wall -g -pg -pthread $(INCLUDES) $(DEFS)
@@ -306,6 +322,9 @@ debug:	FORCE
 	make ZT_DEBUG=1 one
 	make ZT_DEBUG=1 selftest
 
+nltest: $(NLTEST_OBJS)
+	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o nltest $(NLTEST_OBJS) $(LDLIBS)
+
 # Note: keep the symlinks in /var/lib/zerotier-one to the binaries since these
 # provide backward compatibility with old releases where the binaries actually
 # lived here. Folks got scripts.

+ 10 - 2
make-mac.mk

@@ -43,7 +43,10 @@ ONE_OBJS+=ext/libnatpmp/natpmp.o ext/libnatpmp/getgateway.o ext/miniupnpc/connec
 
 # Build with address sanitization library for advanced debugging (clang)
 ifeq ($(ZT_SANITIZE),1)
-	SANFLAGS+=-fsanitize=address -DASAN_OPTIONS=symbolize=1
+	DEFS+=-fsanitize=address -DASAN_OPTIONS=symbolize=1
+endif
+ifeq ($(ZT_DEBUG_TRACE),1)
+	DEFS+=-DZT_DEBUG_TRACE
 endif
 # Debug mode -- dump trace output, build binary with -g
 ifeq ($(ZT_DEBUG),1)
@@ -63,6 +66,11 @@ ifeq ($(ZT_TRACE),1)
 	DEFS+=-DZT_TRACE
 endif
 
+ifeq ($(ZT_VAULT_SUPPORT),1)
+	DEFS+=-DZT_VAULT_SUPPORT=1
+	LIBS+=-lcurl
+endif
+
 CXXFLAGS=$(CFLAGS) -std=c++11 -stdlib=libc++ 
 
 all: one macui
@@ -70,7 +78,7 @@ all: one macui
 ext/x64-salsa2012-asm/salsa2012.o:
 	$(CC) $(CFLAGS) -c ext/x64-salsa2012-asm/salsa2012.s -o ext/x64-salsa2012-asm/salsa2012.o
 
-one:	$(CORE_OBJS) $(ONE_OBJS) one.o
+one:	$(CORE_OBJS) $(ONE_OBJS) one.o 
 	$(CXX) $(CXXFLAGS) -o zerotier-one $(CORE_OBJS) $(ONE_OBJS) one.o $(LIBS)
 	$(STRIP) zerotier-one
 	ln -sf zerotier-one zerotier-idtool

+ 13 - 0
nltest.cpp

@@ -0,0 +1,13 @@
+#include "osdep/LinuxNetLink.hpp"
+
+using namespace ZeroTier;
+
+int main(int argc, char **argv)
+{
+    LinuxNetLink &nl = LinuxNetLink::getInstance();
+
+
+    while(true) {
+        Thread::sleep(1000);
+    }
+}

+ 3 - 3
node/Capability.hpp

@@ -52,7 +52,7 @@ class RuntimeEnvironment;
  * (1) Evaluates its capabilities in ascending order of ID to determine
  *     which capability allows it to transmit this packet.
  * (2) If it has not done so lately, it then sends this capability to the
- *     receving peer ("presents" it).
+ *     receiving peer ("presents" it).
  * (3) The sender then sends the packet.
  *
  * On the receiving side the receiver evaluates the capabilities presented
@@ -64,7 +64,7 @@ class RuntimeEnvironment;
  *
  * Capabilities support a chain of custody. This is currently unused but
  * in the future would allow the publication of capabilities that can be
- * handed off between nodes. Limited transferrability of capabilities is
+ * handed off between nodes. Limited transferability of capabilities is
  * a feature of true capability based security.
  */
 class Capability : public Credential
@@ -81,7 +81,7 @@ public:
 	 * @param id Capability ID
 	 * @param nwid Network ID
 	 * @param ts Timestamp (at controller)
-	 * @param mccl Maximum custody chain length (1 to create non-transferrable capability)
+	 * @param mccl Maximum custody chain length (1 to create non-transferable capability)
 	 * @param rules Network flow rules for this capability
 	 * @param ruleCount Number of flow rules
 	 */

+ 1 - 1
node/CertificateOfMembership.hpp

@@ -243,7 +243,7 @@ public:
 	 * Compare two certificates for parameter agreement
 	 *
 	 * This compares this certificate with the other and returns true if all
-	 * paramters in this cert are present in the other and if they agree to
+	 * parameters in this cert are present in the other and if they agree to
 	 * within this cert's max delta value for each given parameter.
 	 *
 	 * Tuples present in other but not in this cert are ignored, but any

+ 190 - 0
node/Constants.hpp

@@ -275,6 +275,189 @@
  */
 #define ZT_PING_CHECK_INVERVAL 5000
 
+/**
+ * How often the local.conf file is checked for changes
+ */
+#define ZT_LOCAL_CONF_FILE_CHECK_INTERVAL 10000
+
+/**
+ * How frequently to check for changes to the system's network interfaces. When
+ * the service decides to use this constant it's because we want to react more
+ * quickly to new interfaces that pop up or go down.
+ */
+#define ZT_MULTIPATH_BINDER_REFRESH_PERIOD 5000
+
+/**
+ * Packets are only used for QoS/ACK statistical sampling if their packet ID is divisible by
+ * this integer. This is to provide a mechanism for both peers to agree on which packets need
+ * special treatment without having to exchange information. Changing this value would be
+ * a breaking change and would necessitate a protocol version upgrade. Since each incoming and
+ * outgoing packet ID is checked against this value its evaluation is of the form:
+ * (id & (divisor - 1)) == 0, thus the divisor must be a power of 2.
+ *
+ * This value is set at (16) so that given a normally-distributed RNG output we will sample
+ * 1/16th (or ~6.25%) of packets.
+ */
+#define ZT_PATH_QOS_ACK_PROTOCOL_DIVISOR 0x10
+
+/**
+ * Time horizon for VERB_QOS_MEASUREMENT and VERB_ACK packet processing cutoff
+ */
+#define ZT_PATH_QOS_ACK_CUTOFF_TIME 30000
+
+/**
+ * Maximum number of VERB_QOS_MEASUREMENT and VERB_ACK packets allowed to be
+ * processed within cutoff time. Separate totals are kept for each type but
+ * the limit is the same for both.
+ *
+ * This limits how often this peer will compute statistical estimates
+ * of various QoS measures from a VERB_QOS_MEASUREMENT or VERB_ACK packets to
+ * CUTOFF_LIMIT times per CUTOFF_TIME milliseconds per peer to prevent
+ * this from being useful for DOS amplification attacks.
+ */
+#define ZT_PATH_QOS_ACK_CUTOFF_LIMIT 128
+
+/**
+ * Path choice history window size. This is used to keep track of which paths were
+ * previously selected so that we can maintain a target allocation over time.
+ */
+#define ZT_MULTIPATH_PROPORTION_WIN_SZ 128
+
+/**
+ * How often we will sample packet latency. Should be at least greater than ZT_PING_CHECK_INVERVAL
+ * since we will record a 0 bit/s measurement if no valid latency measurement was made within this
+ * window of time.
+ */
+#define ZT_PATH_LATENCY_SAMPLE_INTERVAL ZT_MULTIPATH_PEER_PING_PERIOD * 2
+
+/**
+ * Interval used for rate-limiting the computation of path quality estimates.
+ */
+#define ZT_PATH_QUALITY_COMPUTE_INTERVAL 1000
+
+/**
+ * Number of samples to consider when computing real-time path statistics
+ */
+#define ZT_PATH_QUALITY_METRIC_REALTIME_CONSIDERATION_WIN_SZ 128
+
+/**
+ * Number of samples to consider when computing performing long-term path quality analysis.
+ * By default this value is set to ZT_PATH_QUALITY_METRIC_REALTIME_CONSIDERATION_WIN_SZ but can
+ * be set to any value greater than that to observe longer-term path quality behavior.
+ */
+#define ZT_PATH_QUALITY_METRIC_WIN_SZ ZT_PATH_QUALITY_METRIC_REALTIME_CONSIDERATION_WIN_SZ
+
+/**
+ * Maximum acceptable Packet Delay Variance (PDV) over a path
+ */
+#define ZT_PATH_MAX_PDV 1000
+
+/**
+ * Maximum acceptable time interval between expectation and receipt of at least one ACK over a path
+ */
+#define ZT_PATH_MAX_AGE 30000
+
+/**
+ * Maximum acceptable mean latency over a path
+ */
+#define ZT_PATH_MAX_MEAN_LATENCY 1000
+
+/**
+ * How much each factor contributes to the "stability" score of a path
+ */
+#define ZT_PATH_CONTRIB_PDV                    1.0 / 3.0
+#define ZT_PATH_CONTRIB_LATENCY                1.0 / 3.0
+#define ZT_PATH_CONTRIB_THROUGHPUT_DISTURBANCE 1.0 / 3.0
+
+/**
+ * How much each factor contributes to the "quality" score of a path
+ */
+#define ZT_PATH_CONTRIB_STABILITY  0.75 / 3.0
+#define ZT_PATH_CONTRIB_THROUGHPUT 1.50 / 3.0
+#define ZT_PATH_CONTRIB_SCOPE      0.75 / 3.0
+
+/**
+ * How often a QoS packet is sent
+ */
+#define ZT_PATH_QOS_INTERVAL 3000
+
+/**
+ * Min and max acceptable sizes for a VERB_QOS_MEASUREMENT packet
+ */
+#define ZT_PATH_MIN_QOS_PACKET_SZ 8 + 1
+#define ZT_PATH_MAX_QOS_PACKET_SZ 1400
+
+/**
+ * How many ID:sojourn time pairs in a single QoS packet
+ */
+#define ZT_PATH_QOS_TABLE_SIZE (ZT_PATH_MAX_QOS_PACKET_SZ * 8) / (64 + 16)
+
+/**
+ * Maximum number of outgoing packets we monitor for QoS information
+ */
+#define ZT_PATH_MAX_OUTSTANDING_QOS_RECORDS 128
+
+/**
+ * Timeout for QoS records
+ */
+#define ZT_PATH_QOS_TIMEOUT ZT_PATH_QOS_INTERVAL * 2
+
+/**
+ * How often the service tests the path throughput
+ */
+#define ZT_PATH_THROUGHPUT_MEASUREMENT_INTERVAL ZT_PATH_ACK_INTERVAL * 8
+
+/**
+ * Minimum amount of time between each ACK packet
+ */
+#define ZT_PATH_ACK_INTERVAL 1000
+
+/**
+ * How often an aggregate link statistics report is emitted into this tracing system
+ */
+#define ZT_PATH_AGGREGATE_STATS_REPORT_INTERVAL 60000
+
+/**
+ * How much an aggregate link's component paths can vary from their target allocation
+ * before the link is considered to be in a state of imbalance.
+ */
+#define ZT_PATH_IMBALANCE_THRESHOLD 0.20
+
+/**
+ * Max allowable time spent in any queue
+ */
+#define ZT_QOS_TARGET 5 // ms
+
+/**
+ * Time period where the time spent in the queue by a packet should fall below
+ * target at least once
+ */
+#define ZT_QOS_INTERVAL 100 // ms
+
+/**
+ * The number of bytes that each queue is allowed to send during each DRR cycle.
+ * This approximates a single-byte-based fairness queuing scheme
+ */
+#define ZT_QOS_QUANTUM ZT_DEFAULT_MTU
+
+/**
+ * The maximum total number of packets that can be queued among all
+ * active/inactive, old/new queues
+ */
+#define ZT_QOS_MAX_ENQUEUED_PACKETS 1024
+
+/**
+ * Number of QoS queues (buckets)
+ */
+#define ZT_QOS_NUM_BUCKETS 9
+
+/**
+ * All unspecified traffic is put in this bucket. Anything in a bucket with a smaller
+ * value is de-prioritized. Anything in a bucket with a higher value is prioritized over
+ * other traffic.
+ */
+#define ZT_QOS_DEFAULT_BUCKET 0
+
 /**
  * How frequently to send heartbeats over in-use paths
  */
@@ -290,6 +473,13 @@
  */
 #define ZT_PEER_PING_PERIOD 60000
 
+/**
+ * Delay between full-fledge pings of directly connected peers.
+ * With multipath bonding enabled ping peers more often to measure
+ * packet loss and latency.
+ */
+#define ZT_MULTIPATH_PEER_PING_PERIOD 5000
+
 /**
  * Paths are considered expired if they have not sent us a real packet in this long
  */

+ 4 - 0
node/Hashtable.hpp

@@ -399,6 +399,10 @@ private:
 	{
 		return ((unsigned long)i * (unsigned long)0x9e3779b1);
 	}
+	static inline unsigned long _hc(const int i)
+	{
+		return ((unsigned long)i * (unsigned long)0x9e3379b1);
+	}
 
 	inline void _grow()
 	{

+ 2 - 2
node/Identity.cpp

@@ -50,8 +50,8 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
 	SHA512::hash(digest,publicKey,publicKeyBytes);
 
 	// Initialize genmem[] using Salsa20 in a CBC-like configuration since
-	// ordinary Salsa20 is randomly seekable. This is good for a cipher
-	// but is not what we want for sequential memory-harndess.
+	// ordinary Salsa20 is randomly seek-able. This is good for a cipher
+	// but is not what we want for sequential memory-hardness.
 	memset(genmem,0,ZT_IDENTITY_GEN_MEMORY);
 	Salsa20 s20(digest,(char *)digest + 32);
 	s20.crypt20((char *)genmem,(char *)genmem,64);

+ 89 - 35
node/IncomingPacket.cpp

@@ -80,6 +80,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
 			if (!trusted) {
 				if (!dearmor(peer->key())) {
 					RR->t->incomingPacketMessageAuthenticationFailure(tPtr,_path,packetId(),sourceAddress,hops(),"invalid MAC");
+					_path->recordInvalidPacket();
 					return true;
 				}
 			}
@@ -93,9 +94,11 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
 			switch(v) {
 				//case Packet::VERB_NOP:
 				default: // ignore unknown verbs, but if they pass auth check they are "received"
-					peer->received(tPtr,_path,hops(),packetId(),v,0,Packet::VERB_NOP,false,0);
+					peer->received(tPtr,_path,hops(),packetId(),payloadLength(),v,0,Packet::VERB_NOP,false,0);
 					return true;
 				case Packet::VERB_HELLO:                      return _doHELLO(RR,tPtr,true);
+				case Packet::VERB_ACK:                        return _doACK(RR,tPtr,peer);
+				case Packet::VERB_QOS_MEASUREMENT:            return _doQOS_MEASUREMENT(RR,tPtr,peer);
 				case Packet::VERB_ERROR:                      return _doERROR(RR,tPtr,peer);
 				case Packet::VERB_OK:                         return _doOK(RR,tPtr,peer);
 				case Packet::VERB_WHOIS:                      return _doWHOIS(RR,tPtr,peer);
@@ -194,7 +197,57 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar
 		default: break;
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb,false,networkId);
+	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_ERROR,inRePacketId,inReVerb,false,networkId);
+
+	return true;
+}
+
+bool IncomingPacket::_doACK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+{
+	if (!peer->rateGateACK(RR->node->now()))
+		return true;
+	/* Dissect incoming ACK packet. From this we can estimate current throughput of the path, establish known
+	 * maximums and detect packet loss. */
+	if (peer->localMultipathSupport()) {
+		int32_t ackedBytes;
+		if (payloadLength() != sizeof(ackedBytes)) {
+			return true; // ignore
+		}
+		memcpy(&ackedBytes, payload(), sizeof(ackedBytes));
+		_path->receivedAck(RR->node->now(), Utils::ntoh(ackedBytes));
+		peer->inferRemoteMultipathEnabled();
+	}
+
+	return true;
+}
+bool IncomingPacket::_doQOS_MEASUREMENT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
+{
+	if (!peer->rateGateQoS(RR->node->now()))
+		return true;
+	/* Dissect incoming QoS packet. From this we can compute latency values and their variance.
+	 * The latency variance is used as a measure of "jitter". */
+	if (peer->localMultipathSupport()) {
+		if (payloadLength() > ZT_PATH_MAX_QOS_PACKET_SZ || payloadLength() < ZT_PATH_MIN_QOS_PACKET_SZ) {
+			return true; // ignore
+		}
+		const int64_t now = RR->node->now();
+		uint64_t rx_id[ZT_PATH_QOS_TABLE_SIZE];
+		uint16_t rx_ts[ZT_PATH_QOS_TABLE_SIZE];
+		char *begin = (char *)payload();
+		char *ptr = begin;
+		int count = 0;
+		int len = payloadLength();
+		// Read packet IDs and latency compensation intervals for each packet tracked by this QoS packet
+		while (ptr < (begin + len) && (count < ZT_PATH_QOS_TABLE_SIZE)) {
+			memcpy((void*)&rx_id[count], ptr, sizeof(uint64_t));
+			ptr+=sizeof(uint64_t);
+			memcpy((void*)&rx_ts[count], ptr, sizeof(uint16_t));
+			ptr+=sizeof(uint16_t);
+			count++;
+		}
+		_path->receivedQoS(now, count, rx_id, rx_ts);
+		peer->inferRemoteMultipathEnabled();
+	}
 
 	return true;
 }
@@ -395,7 +448,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
 	_path->send(RR,tPtr,outp.data(),outp.size(),now);
 
 	peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
-	peer->received(tPtr,_path,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP,false,0);
+	peer->received(tPtr,_path,hops(),pid,payloadLength(),Packet::VERB_HELLO,0,Packet::VERB_NOP,false,0);
 
 	return true;
 }
@@ -445,8 +498,9 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
 				}
 			}
 
-			if (!hops())
-				_path->updateLatency((unsigned int)latency);
+			if (!hops() && (RR->node->getMultipathMode() != ZT_MULTIPATH_NONE)) {
+				_path->updateLatency((unsigned int)latency, RR->node->now());
+			}
 
 			peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
 
@@ -507,7 +561,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
 		default: break;
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,false,networkId);
+	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_OK,inRePacketId,inReVerb,false,networkId);
 
 	return true;
 }
@@ -542,7 +596,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const Shar
 		_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,false,0);
+	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,false,0);
 
 	return true;
 }
@@ -566,7 +620,7 @@ bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const
 		}
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,false,0);
+	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,false,0);
 
 	return true;
 }
@@ -595,7 +649,7 @@ bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,void *tPtr,const Shar
 		_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,trustEstablished,nwid);
+	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_FRAME,0,Packet::VERB_NOP,trustEstablished,nwid);
 
 	return true;
 }
@@ -618,7 +672,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const
 		if (!network->gate(tPtr,peer)) {
 			RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,true);
 			_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
-			peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false,nwid);
+			peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false,nwid);
 			return true;
 		}
 
@@ -630,7 +684,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const
 			const uint8_t *const frameData = (const uint8_t *)field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,frameLen);
 
 			if ((!from)||(from == network->mac())) {
-				peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+				peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
 				return true;
 			}
 
@@ -641,19 +695,19 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const
 							network->learnBridgeRoute(from,peer->address());
 						} else {
 							RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (remote)");
-							peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+							peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
 							return true;
 						}
 					} else if (to != network->mac()) {
 						if (to.isMulticast()) {
 							if (network->config().multicastLimit == 0) {
 								RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"multicast disabled");
-								peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+								peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
 								return true;
 							}
 						} else if (!network->config().permitsBridging(RR->identity.address())) {
 							RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_EXT_FRAME,from,to,"bridging not allowed (local)");
-							peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+							peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
 							return true;
 						}
 					}
@@ -673,9 +727,9 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const
 			_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 		}
 
-		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid);
+		peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true,nwid);
 	} else {
-		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false,nwid);
+		peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false,nwid);
 	}
 
 	return true;
@@ -695,7 +749,7 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,void *tPtr,const Share
 	outp.armor(peer->key(),true);
 	_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 
-	peer->received(tPtr,_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false,0);
+	peer->received(tPtr,_path,hops(),pid,payloadLength(),Packet::VERB_ECHO,0,Packet::VERB_NOP,false,0);
 
 	return true;
 }
@@ -740,7 +794,7 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,c
 		}
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,trustEstablished,(network) ? network->id() : 0);
+	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,trustEstablished,(network) ? network->id() : 0);
 
 	return true;
 }
@@ -863,7 +917,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *t
 		}
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,trustEstablished,(network) ? network->id() : 0);
+	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,trustEstablished,(network) ? network->id() : 0);
 
 	return true;
 }
@@ -889,7 +943,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void
 		_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 	}
 
-	peer->received(tPtr,_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,false,nwid);
+	peer->received(tPtr,_path,hopCount,requestPacketId,payloadLength(),Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,false,nwid);
 
 	return true;
 }
@@ -910,7 +964,7 @@ bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,c
 		}
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,false,(network) ? network->id() : 0);
+	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,false,(network) ? network->id() : 0);
 
 	return true;
 }
@@ -953,7 +1007,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr
 		}
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,trustEstablished,nwid);
+	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,trustEstablished,nwid);
 
 	return true;
 }
@@ -979,7 +1033,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,
 		if (!network->gate(tPtr,peer)) {
 			RR->t->incomingNetworkAccessDenied(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,true);
 			_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
-			peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
+			peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
 			return true;
 		}
 
@@ -1003,19 +1057,19 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,
 
 		if (network->config().multicastLimit == 0) {
 			RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,from,to.mac(),"multicast disabled");
-			peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
+			peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
 			return true;
 		}
 
 		if ((frameLen > 0)&&(frameLen <= ZT_MAX_MTU)) {
 			if (!to.mac().isMulticast()) {
 				RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"destination not multicast");
-				peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+				peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
 				return true;
 			}
 			if ((!from)||(from.isMulticast())||(from == network->mac())) {
 				RR->t->incomingPacketInvalid(tPtr,_path,packetId(),source(),hops(),Packet::VERB_MULTICAST_FRAME,"invalid source MAC");
-				peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+				peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
 				return true;
 			}
 
@@ -1029,7 +1083,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,
 					network->learnBridgeRoute(from,peer->address());
 				} else {
 					RR->t->incomingNetworkFrameDropped(tPtr,network,_path,packetId(),size(),peer->address(),Packet::VERB_MULTICAST_FRAME,from,to.mac(),"bridging not allowed (remote)");
-					peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
+					peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid); // trustEstablished because COM is okay
 					return true;
 				}
 			}
@@ -1052,10 +1106,10 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,
 			}
 		}
 
-		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid);
+		peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true,nwid);
 	} else {
 		_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
-		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
+		peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false,nwid);
 	}
 
 	return true;
@@ -1067,7 +1121,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPt
 
 	// First, subject this to a rate limit
 	if (!peer->rateGatePushDirectPaths(now)) {
-		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false,0);
+		peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false,0);
 		return true;
 	}
 
@@ -1091,7 +1145,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPt
 			case 4: {
 				const InetAddress a(field(ptr,4),4,at<uint16_t>(ptr + 4));
 				if (
-				    ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
+					((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
 						(!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
 						(RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localSocket(),a)) ) // should use path
 				{
@@ -1105,7 +1159,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPt
 			case 6: {
 				const InetAddress a(field(ptr,16),16,at<uint16_t>(ptr + 16));
 				if (
-				    ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
+					((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
 						(!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
 						(RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localSocket(),a)) ) // should use path
 				{
@@ -1120,7 +1174,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPt
 		ptr += addrLen;
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false,0);
+	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false,0);
 
 	return true;
 }
@@ -1136,7 +1190,7 @@ bool IncomingPacket::_doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,con
 		RR->node->postEvent(tPtr,ZT_EVENT_USER_MESSAGE,reinterpret_cast<const void *>(&um));
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_USER_MESSAGE,0,Packet::VERB_NOP,false,0);
+	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_USER_MESSAGE,0,Packet::VERB_NOP,false,0);
 
 	return true;
 }
@@ -1160,7 +1214,7 @@ bool IncomingPacket::_doREMOTE_TRACE(const RuntimeEnvironment *RR,void *tPtr,con
 		}
 	}
 
-	peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_REMOTE_TRACE,0,Packet::VERB_NOP,false,0);
+	peer->received(tPtr,_path,hops(),packetId(),payloadLength(),Packet::VERB_REMOTE_TRACE,0,Packet::VERB_NOP,false,0);
 
 	return true;
 }

+ 2 - 0
node/IncomingPacket.hpp

@@ -125,6 +125,8 @@ private:
 	// been authenticated, decrypted, decompressed, and classified.
 	bool _doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
 	bool _doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated);
+	bool _doACK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doQOS_MEASUREMENT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
 	bool _doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
 	bool _doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
 	bool _doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);

+ 13 - 13
node/InetAddress.cpp

@@ -5,7 +5,7 @@
  * 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 oion) any later version.
+ * (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
@@ -62,23 +62,23 @@ InetAddress::IpScope InetAddress::ipScope() const
 				case 0x37: return IP_SCOPE_PSEUDOPRIVATE;                             // 55.0.0.0/8 (US DoD)
 				case 0x38: return IP_SCOPE_PSEUDOPRIVATE;                             // 56.0.0.0/8 (US Postal Service)
 				case 0x64:
-					if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_PRIVATE;       // 100.64.0.0/10
+					if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_PRIVATE;     // 100.64.0.0/10
 					break;
 				case 0x7f: return IP_SCOPE_LOOPBACK;                                  // 127.0.0.0/8
 				case 0xa9:
-					if ((ip & 0xffff0000) == 0xa9fe0000) return IP_SCOPE_LINK_LOCAL;    // 169.254.0.0/16
+					if ((ip & 0xffff0000) == 0xa9fe0000) return IP_SCOPE_LINK_LOCAL;  // 169.254.0.0/16
 					break;
 				case 0xac:
-					if ((ip & 0xfff00000) == 0xac100000) return IP_SCOPE_PRIVATE;       // 172.16.0.0/12
+					if ((ip & 0xfff00000) == 0xac100000) return IP_SCOPE_PRIVATE;     // 172.16.0.0/12
 					break;
 				case 0xc0:
-					if ((ip & 0xffff0000) == 0xc0a80000) return IP_SCOPE_PRIVATE;				// 192.168.0.0/16
+					if ((ip & 0xffff0000) == 0xc0a80000) return IP_SCOPE_PRIVATE;     // 192.168.0.0/16
 					break;
 				case 0xff: return IP_SCOPE_NONE;                                      // 255.0.0.0/8 (broadcast, or unused/unusable)
 			}
 			switch(ip >> 28) {
-				case 0xe: return IP_SCOPE_MULTICAST;                              // 224.0.0.0/4
-				case 0xf: return IP_SCOPE_PSEUDOPRIVATE;                          // 240.0.0.0/4 ("reserved," usually unusable)
+				case 0xe: return IP_SCOPE_MULTICAST;                                  // 224.0.0.0/4
+				case 0xf: return IP_SCOPE_PSEUDOPRIVATE;                              // 240.0.0.0/4 ("reserved," usually unusable)
 			}
 			return IP_SCOPE_GLOBAL;
 		}	break;
@@ -86,21 +86,21 @@ InetAddress::IpScope InetAddress::ipScope() const
 		case AF_INET6: {
 			const unsigned char *ip = reinterpret_cast<const unsigned char *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
 			if ((ip[0] & 0xf0) == 0xf0) {
-				if (ip[0] == 0xff) return IP_SCOPE_MULTICAST;                              // ff00::/8
+				if (ip[0] == 0xff) return IP_SCOPE_MULTICAST;                        // ff00::/8
 				if ((ip[0] == 0xfe)&&((ip[1] & 0xc0) == 0x80)) {
 					unsigned int k = 2;
 					while ((!ip[k])&&(k < 15)) ++k;
 					if ((k == 15)&&(ip[15] == 0x01))
-						return IP_SCOPE_LOOPBACK;                                              // fe80::1/128
-					else return IP_SCOPE_LINK_LOCAL;                                         // fe80::/10
+						return IP_SCOPE_LOOPBACK;                                    // fe80::1/128
+					else return IP_SCOPE_LINK_LOCAL;                                 // fe80::/10
 				}
-				if ((ip[0] & 0xfe) == 0xfc) return IP_SCOPE_PRIVATE;                       // fc00::/7
+				if ((ip[0] & 0xfe) == 0xfc) return IP_SCOPE_PRIVATE;                 // fc00::/7
 			}
 			unsigned int k = 0;
 			while ((!ip[k])&&(k < 15)) ++k;
 			if (k == 15) { // all 0's except last byte
-				if (ip[15] == 0x01) return IP_SCOPE_LOOPBACK;                              // ::1/128
-				if (ip[15] == 0x00) return IP_SCOPE_NONE;                                  // ::/128
+				if (ip[15] == 0x01) return IP_SCOPE_LOOPBACK;                        // ::1/128
+				if (ip[15] == 0x00) return IP_SCOPE_NONE;                            // ::/128
 			}
 			return IP_SCOPE_GLOBAL;
 		}	break;

+ 1 - 1
node/MulticastGroup.hpp

@@ -68,7 +68,7 @@ public:
 	 * Derive the multicast group used for address resolution (ARP/NDP) for an IP
 	 *
 	 * @param ip IP address (port field is ignored)
-	 * @return Multicat group for ARP/NDP
+	 * @return Multicast group for ARP/NDP
 	 */
 	static inline MulticastGroup deriveMulticastGroupForAddressResolution(const InetAddress &ip)
 	{

+ 1 - 1
node/Multicaster.cpp

@@ -190,7 +190,7 @@ void Multicaster::send(
 				for(unsigned int i=0;i<multicastReplicatorCount;++i) {
 					const SharedPtr<Peer> p(RR->topology->getPeerNoCache(multicastReplicators[i]));
 					if ((p)&&(p->isAlive(now))) {
-						const SharedPtr<Path> pp(p->getBestPath(now,false));
+						const SharedPtr<Path> pp(p->getAppropriatePath(now,false));
 						if ((pp)&&(pp->latency() < bestMulticastReplicatorLatency)) {
 							bestMulticastReplicatorLatency = pp->latency();
 							bestMulticastReplicatorPath = pp;

+ 16 - 6
node/Network.cpp

@@ -29,6 +29,8 @@
 #include <stdlib.h>
 #include <math.h>
 
+#include "../include/ZeroTierDebug.h"
+
 #include "Constants.hpp"
 #include "../version.h"
 #include "Network.hpp"
@@ -106,7 +108,8 @@ static _doZtFilterResult _doZtFilter(
 	const unsigned int ruleCount,
 	Address &cc, // MUTABLE -- set to TEE destination if TEE action is taken or left alone otherwise
 	unsigned int &ccLength, // MUTABLE -- set to length of packet payload to TEE
-	bool &ccWatch) // MUTABLE -- set to true for WATCH target as opposed to normal TEE
+	bool &ccWatch, // MUTABLE -- set to true for WATCH target as opposed to normal TEE
+	uint8_t &qosBucket) // MUTABLE -- set to the value of the argument provided to PRIORITY
 {
 	// Set to true if we are a TEE/REDIRECT/WATCH target
 	bool superAccept = false;
@@ -124,6 +127,10 @@ static _doZtFilterResult _doZtFilter(
 		if ((unsigned int)rt <= (unsigned int)ZT_NETWORK_RULE_ACTION__MAX_ID) {
 			if (thisSetMatches) {
 				switch(rt) {
+					case ZT_NETWORK_RULE_ACTION_PRIORITY:
+						qosBucket = (rules[rn].v.qosBucket >= 0 || rules[rn].v.qosBucket <= 8) ? rules[rn].v.qosBucket : 4; // 4 = default bucket (no priority)
+						return DOZTFILTER_ACCEPT;
+
 					case ZT_NETWORK_RULE_ACTION_DROP:
 						return DOZTFILTER_DROP;
 
@@ -621,7 +628,8 @@ bool Network::filterOutgoingPacket(
 	const uint8_t *frameData,
 	const unsigned int frameLen,
 	const unsigned int etherType,
-	const unsigned int vlanId)
+	const unsigned int vlanId,
+	uint8_t &qosBucket)
 {
 	const int64_t now = RR->node->now();
 	Address ztFinalDest(ztDest);
@@ -636,7 +644,7 @@ bool Network::filterOutgoingPacket(
 
 	Membership *const membership = (ztDest) ? _memberships.get(ztDest) : (Membership *)0;
 
-	switch(_doZtFilter(RR,rrl,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch)) {
+	switch(_doZtFilter(RR,rrl,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch,qosBucket)) {
 
 		case DOZTFILTER_NO_MATCH: {
 			for(unsigned int c=0;c<_config.capabilityCount;++c) {
@@ -644,7 +652,7 @@ bool Network::filterOutgoingPacket(
 				Address cc2;
 				unsigned int ccLength2 = 0;
 				bool ccWatch2 = false;
-				switch (_doZtFilter(RR,crrl,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.capabilities[c].rules(),_config.capabilities[c].ruleCount(),cc2,ccLength2,ccWatch2)) {
+				switch (_doZtFilter(RR,crrl,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.capabilities[c].rules(),_config.capabilities[c].ruleCount(),cc2,ccLength2,ccWatch2,qosBucket)) {
 					case DOZTFILTER_NO_MATCH:
 					case DOZTFILTER_DROP: // explicit DROP in a capability just terminates its evaluation and is an anti-pattern
 						break;
@@ -759,11 +767,13 @@ int Network::filterIncomingPacket(
 	bool ccWatch = false;
 	const Capability *c = (Capability *)0;
 
+	uint8_t qosBucket = 255; // For incoming packets this is a dummy value
+
 	Mutex::Lock _l(_lock);
 
 	Membership &membership = _membership(sourcePeer->address());
 
-	switch (_doZtFilter(RR,rrl,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch)) {
+	switch (_doZtFilter(RR,rrl,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch,qosBucket)) {
 
 		case DOZTFILTER_NO_MATCH: {
 			Membership::CapabilityIterator mci(membership,_config);
@@ -772,7 +782,7 @@ int Network::filterIncomingPacket(
 				Address cc2;
 				unsigned int ccLength2 = 0;
 				bool ccWatch2 = false;
-				switch(_doZtFilter(RR,crrl,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,c->rules(),c->ruleCount(),cc2,ccLength2,ccWatch2)) {
+				switch(_doZtFilter(RR,crrl,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,c->rules(),c->ruleCount(),cc2,ccLength2,ccWatch2,qosBucket)) {
 					case DOZTFILTER_NO_MATCH:
 					case DOZTFILTER_DROP: // explicit DROP in a capability just terminates its evaluation and is an anti-pattern
 						break;

+ 10 - 2
node/Network.hpp

@@ -132,7 +132,8 @@ public:
 		const uint8_t *frameData,
 		const unsigned int frameLen,
 		const unsigned int etherType,
-		const unsigned int vlanId);
+		const unsigned int vlanId,
+		uint8_t &qosBucket);
 
 	/**
 	 * Apply filters to an incoming packet
@@ -297,6 +298,13 @@ public:
 	 */
 	void learnBridgeRoute(const MAC &mac,const Address &addr);
 
+	/**
+	 * Whether QoS is in effect for this network
+	 */
+	bool QoSEnabled() {
+		return false;
+	}
+
 	/**
 	 * Learn a multicast group that is bridged to our tap device
 	 *
@@ -438,6 +446,6 @@ private:
 	AtomicCounter __refCount;
 };
 
-} // naemspace ZeroTier
+} // namespace ZeroTier
 
 #endif

+ 1 - 1
node/NetworkConfig.hpp

@@ -562,7 +562,7 @@ public:
 	char name[ZT_MAX_NETWORK_SHORT_NAME_LENGTH + 1];
 
 	/**
-	 * Certficiate of membership (for private networks)
+	 * Certificate of membership (for private networks)
 	 */
 	CertificateOfMembership com;
 };

+ 16 - 2
node/Node.cpp

@@ -234,7 +234,7 @@ public:
 			}
 
 			if ((!contacted)&&(_bestCurrentUpstream)) {
-				const SharedPtr<Path> up(_bestCurrentUpstream->getBestPath(_now,true));
+				const SharedPtr<Path> up(_bestCurrentUpstream->getAppropriatePath(_now,true));
 				if (up)
 					p->sendHELLO(_tPtr,up->localSocket(),up->address(),_now);
 			}
@@ -368,6 +368,7 @@ ZT_ResultCode Node::leave(uint64_t nwid,void **uptr,void *tptr)
 	{
 		Mutex::Lock _l(_networks_m);
 		SharedPtr<Network> *nw = _networks.get(nwid);
+		RR->sw->removeNetworkQoSControlBlock(nwid);
 		if (!nw)
 			return ZT_RESULT_OK;
 		if (uptr)
@@ -450,6 +451,7 @@ ZT_PeerList *Node::peers() const
 	for(std::vector< std::pair< Address,SharedPtr<Peer> > >::iterator pi(peers.begin());pi!=peers.end();++pi) {
 		ZT_Peer *p = &(pl->peers[pl->peerCount++]);
 		p->address = pi->second->address().toInt();
+		p->hadAggregateLink = 0;
 		if (pi->second->remoteVersionKnown()) {
 			p->versionMajor = pi->second->remoteVersionMajor();
 			p->versionMinor = pi->second->remoteVersionMinor();
@@ -465,7 +467,8 @@ ZT_PeerList *Node::peers() const
 		p->role = RR->topology->role(pi->second->identity().address());
 
 		std::vector< SharedPtr<Path> > paths(pi->second->paths(_now));
-		SharedPtr<Path> bestp(pi->second->getBestPath(_now,false));
+		SharedPtr<Path> bestp(pi->second->getAppropriatePath(_now,false));
+		p->hadAggregateLink |= pi->second->hasAggregateLink();
 		p->pathCount = 0;
 		for(std::vector< SharedPtr<Path> >::iterator path(paths.begin());path!=paths.end();++path) {
 			ZT_FAST_MEMCPY(&(p->paths[p->pathCount].address),&((*path)->address()),sizeof(struct sockaddr_storage));
@@ -474,6 +477,17 @@ ZT_PeerList *Node::peers() const
 			p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust((*path)->address());
 			p->paths[p->pathCount].expired = 0;
 			p->paths[p->pathCount].preferred = ((*path) == bestp) ? 1 : 0;
+			p->paths[p->pathCount].latency = (*path)->latency();
+			p->paths[p->pathCount].packetDelayVariance = (*path)->packetDelayVariance(); 
+			p->paths[p->pathCount].throughputDisturbCoeff = (*path)->throughputDisturbanceCoefficient();
+			p->paths[p->pathCount].packetErrorRatio = (*path)->packetErrorRatio();
+			p->paths[p->pathCount].packetLossRatio = (*path)->packetLossRatio();
+			p->paths[p->pathCount].stability = (*path)->lastComputedStability();
+			p->paths[p->pathCount].throughput = (*path)->meanThroughput();
+			p->paths[p->pathCount].maxThroughput = (*path)->maxLifetimeThroughput();
+			p->paths[p->pathCount].allocation = (float)(*path)->allocation() / (float)255;
+			p->paths[p->pathCount].ifname = (*path)->getName();
+
 			++p->pathCount;
 		}
 	}

+ 5 - 0
node/Node.hpp

@@ -260,6 +260,9 @@ public:
 	inline const Address &remoteTraceTarget() const { return _remoteTraceTarget; }
 	inline Trace::Level remoteTraceLevel() const { return _remoteTraceLevel; }
 
+	inline void setMultipathMode(uint8_t mode) { _multipathMode = mode; }
+	inline uint8_t getMultipathMode() { return _multipathMode; }
+
 private:
 	RuntimeEnvironment _RR;
 	RuntimeEnvironment *RR;
@@ -284,6 +287,8 @@ private:
 	Address _remoteTraceTarget;
 	enum Trace::Level _remoteTraceLevel;
 
+	uint8_t _multipathMode;
+
 	volatile int64_t _now;
 	int64_t _lastPingCheck;
 	int64_t _lastHousekeepingRun;

+ 2 - 1
node/OutboundMulticast.cpp

@@ -85,7 +85,8 @@ void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,void *tPtr,const A
 {
 	const SharedPtr<Network> nw(RR->node->network(_nwid));
 	const Address toAddr2(toAddr);
-	if ((nw)&&(nw->filterOutgoingPacket(tPtr,true,RR->identity.address(),toAddr2,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) {
+	uint8_t QoSBucket = 255; // Dummy value
+	if ((nw)&&(nw->filterOutgoingPacket(tPtr,true,RR->identity.address(),toAddr2,_macSrc,_macDest,_frameData,_frameLen,_etherType,0,QoSBucket))) {
 		_packet.newInitializationVector();
 		_packet.setDestination(toAddr2);
 		RR->node->expectReplyTo(_packet.packetId());

+ 56 - 10
node/Packet.hpp

@@ -150,7 +150,7 @@
  *
  * In cryptography, a "break" means something different from what it means in
  * common discussion. If a cipher is 256 bits strong and someone finds a way
- * to reduce key search to 254 bits, this constitues a "break" in the academic
+ * to reduce key search to 254 bits, this constitutes a "break" in the academic
  * literature. 254 bits is still far beyond what can be leveraged to accomplish
  * a "break" as most people would understand it -- the actual decryption and
  * reading of traffic.
@@ -249,7 +249,7 @@
  */
 #define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD
 
-// Field incides for parsing verbs -------------------------------------------
+// Field indices for parsing verbs -------------------------------------------
 
 // Some verbs have variable-length fields. Those aren't fully defined here
 // yet-- instead they are parsed using relative indexes in IncomingPacket.
@@ -419,7 +419,7 @@ public:
 
 		template<unsigned int C2>
 		Fragment(const Buffer<C2> &b) :
-	 		Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
+			Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
 		{
 		}
 
@@ -734,7 +734,7 @@ public:
 		 * Credentials can be for any number of networks.
 		 *
 		 * The use of a zero byte to terminate the COM section is for legacy
-		 * backward compatiblity. Newer fields are prefixed with a length.
+		 * backward compatibility. Newer fields are prefixed with a length.
 		 *
 		 * OK/ERROR are not generated.
 		 */
@@ -751,7 +751,7 @@ public:
 		 * This message requests network configuration from a node capable of
 		 * providing it.
 		 *
-		 * Respones to this are always whole configs intended for the recipient.
+		 * Responses to this are always whole configs intended for the recipient.
 		 * For patches and other updates a NETWORK_CONFIG is sent instead.
 		 *
 		 * It would be valid and correct as of 1.2.0 to use NETWORK_CONFIG always,
@@ -884,7 +884,7 @@ public:
 		 *   <[6] MAC address of multicast group>
 		 *   <[4] 32-bit ADI for multicast group>
 		 *   <[1] flags>
-		 *  [<[...] network certficate of membership (DEPRECATED)>]
+		 *  [<[...] network certificate of membership (DEPRECATED)>]
 		 *  [<[...] implicit gather results if flag 0x01 is set>]
 		 *
 		 * OK flags (same bits as request flags):
@@ -930,7 +930,53 @@ public:
 		 */
 		VERB_PUSH_DIRECT_PATHS = 0x10,
 
-		// 0x11, 0x12 -- deprecated
+		// 0x11 -- deprecated
+
+		/**
+		 * An acknowledgment of receipt of a series of recent packets from another
+		 * peer. This is used to calculate relative throughput values and to detect
+		 * packet loss. Only VERB_FRAME and VERB_EXT_FRAME packets are counted.
+		 *
+		 * ACK response format:
+		 *  <[4] 32-bit number of bytes received since last ACK>
+		 *
+		 * Upon receipt of this packet, the local peer will verify that the correct
+		 * number of bytes were received by the remote peer. If these values do
+		 * not agree that could be an indicator of packet loss.
+		 *
+		 * Additionally, the local peer knows the interval of time that has
+		 * elapsed since the last received ACK. With this information it can compute
+		 * a rough estimate of the current throughput.
+		 *
+		 * This is sent at a maximum rate of once per every ZT_PATH_ACK_INTERVAL
+		 */
+		VERB_ACK = 0x12,
+
+		/**
+		 * A packet containing timing measurements useful for estimating path quality.
+		 * Composed of a list of <packet ID:internal sojourn time> pairs for an
+		 * arbitrary set of recent packets. This is used to sample for latency and
+		 * packet delay variance (PDV, "jitter").
+		 *
+		 * QoS record format:
+		 *
+		 *  <[8] 64-bit packet ID of previously-received packet>
+		 *  <[1] 8-bit packet sojourn time>
+		 *  <...repeat until end of max 1400 byte packet...>
+		 *
+		 * The number of possible records per QoS packet is: (1400 * 8) / 72 = 155
+		 * This packet should be sent very rarely (every few seconds) as it can be
+		 * somewhat large if the connection is saturated. Future versions might use
+		 * a bloom table to probabilistically determine these values in a vastly
+		 * more space-efficient manner.
+		 *
+		 * Note: The 'internal packet sojourn time' is a slight misnomer as it is a
+		 * measure of the amount of time between when a packet was received and the
+		 * egress time of its tracking QoS packet.
+		 *
+		 * This is sent at a maximum rate of once per every ZT_PATH_QOS_INTERVAL
+		 */
+		VERB_QOS_MEASUREMENT = 0x13,
 
 		/**
 		 * A message with arbitrary user-definable content:
@@ -954,7 +1000,7 @@ public:
 		 *
 		 * This message contains a remote trace event. Remote trace events can
 		 * be sent to observers configured at the network level for those that
-		 * pertain directly to actiity on a network, or to global observers if
+		 * pertain directly to activity on a network, or to global observers if
 		 * locally configured.
 		 *
 		 * The instance ID is a random 64-bit value generated by each ZeroTier
@@ -999,7 +1045,7 @@ public:
 
 	template<unsigned int C2>
 	Packet(const Buffer<C2> &b) :
- 		Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
+		Buffer<ZT_PROTO_MAX_PACKET_LENGTH>(b)
 	{
 	}
 
@@ -1251,7 +1297,7 @@ public:
 	 * Encrypt/decrypt a separately armored portion of a packet
 	 *
 	 * This is currently only used to mask portions of HELLO as an extra
-	 * security precation since most of that message is sent in the clear.
+	 * security precaution since most of that message is sent in the clear.
 	 *
 	 * This must NEVER be used more than once in the same packet, as doing
 	 * so will result in re-use of the same key stream.

+ 443 - 5
node/Path.hpp

@@ -39,6 +39,10 @@
 #include "SharedPtr.hpp"
 #include "AtomicCounter.hpp"
 #include "Utils.hpp"
+#include "RingBuffer.hpp"
+#include "Packet.hpp"
+
+#include "../osdep/Phy.hpp"
 
 /**
  * Maximum return value of preferenceRank()
@@ -55,6 +59,7 @@ class RuntimeEnvironment;
 class Path
 {
 	friend class SharedPtr<Path>;
+	Phy<Path *> *_phy;
 
 public:
 	/**
@@ -93,22 +98,77 @@ public:
 		_lastOut(0),
 		_lastIn(0),
 		_lastTrustEstablishedPacketReceived(0),
+		_lastPathQualityComputeTime(0),
 		_localSocket(-1),
 		_latency(0xffff),
 		_addr(),
-		_ipScope(InetAddress::IP_SCOPE_NONE)
+		_ipScope(InetAddress::IP_SCOPE_NONE),
+		_lastAck(0),
+		_lastThroughputEstimation(0),
+		_lastQoSMeasurement(0),
+		_lastQoSRecordPurge(0),
+		_unackedBytes(0),
+		_expectingAckAsOf(0),
+		_packetsReceivedSinceLastAck(0),
+		_packetsReceivedSinceLastQoS(0),
+		_maxLifetimeThroughput(0),
+		_lastComputedMeanThroughput(0),
+		_bytesAckedSinceLastThroughputEstimation(0),
+		_lastComputedMeanLatency(0.0),
+		_lastComputedPacketDelayVariance(0.0),
+		_lastComputedPacketErrorRatio(0.0),
+		_lastComputedPacketLossRatio(0),
+		_lastComputedStability(0.0),
+		_lastComputedRelativeQuality(0),
+		_lastComputedThroughputDistCoeff(0.0),
+		_lastAllocation(0)
 	{
+		prepareBuffers();
 	}
 
 	Path(const int64_t localSocket,const InetAddress &addr) :
 		_lastOut(0),
 		_lastIn(0),
 		_lastTrustEstablishedPacketReceived(0),
+		_lastPathQualityComputeTime(0),
 		_localSocket(localSocket),
 		_latency(0xffff),
 		_addr(addr),
-		_ipScope(addr.ipScope())
+		_ipScope(addr.ipScope()),
+		_lastAck(0),
+		_lastThroughputEstimation(0),
+		_lastQoSMeasurement(0),
+		_lastQoSRecordPurge(0),
+		_unackedBytes(0),
+		_expectingAckAsOf(0),
+		_packetsReceivedSinceLastAck(0),
+		_packetsReceivedSinceLastQoS(0),
+		_maxLifetimeThroughput(0),
+		_lastComputedMeanThroughput(0),
+		_bytesAckedSinceLastThroughputEstimation(0),
+		_lastComputedMeanLatency(0.0),
+		_lastComputedPacketDelayVariance(0.0),
+		_lastComputedPacketErrorRatio(0.0),
+		_lastComputedPacketLossRatio(0),
+		_lastComputedStability(0.0),
+		_lastComputedRelativeQuality(0),
+		_lastComputedThroughputDistCoeff(0.0),
+		_lastAllocation(0)
 	{
+		prepareBuffers();
+		_phy->getIfName((PhySocket *)((uintptr_t)_localSocket), _ifname, 16);
+	}
+
+	~Path()
+	{
+		delete _throughputSamples;
+		delete _latencySamples;
+		delete _packetValiditySamples;
+		delete _throughputDisturbanceSamples;
+		_throughputSamples = NULL;
+		_latencySamples = NULL;
+		_packetValiditySamples = NULL;
+		_throughputDisturbanceSamples = NULL;
 	}
 
 	/**
@@ -147,12 +207,16 @@ public:
 	 *
 	 * @param l Measured latency
 	 */
-	inline void updateLatency(const unsigned int l)
+	inline void updateLatency(const unsigned int l, int64_t now)
 	{
 		unsigned int pl = _latency;
-		if (pl < 0xffff)
+		if (pl < 0xffff) {
 			_latency = (pl + l) / 2;
-		else _latency = l;
+		}
+		else {
+			_latency = l;
+		}
+		_latencySamples->push(l);
 	}
 
 	/**
@@ -240,6 +304,324 @@ public:
 		return (((age < (ZT_PATH_HEARTBEAT_PERIOD + 5000)) ? l : (l + 0xffff + age)) * (long)((ZT_INETADDRESS_MAX_SCOPE - _ipScope) + 1));
 	}
 
+	/**
+	 * Record statistics on outgoing packets. Used later to estimate QoS metrics.
+	 *
+	 * @param now Current time
+	 * @param packetId ID of packet
+	 * @param payloadLength Length of payload
+	 * @param verb Packet verb
+	 */
+	inline void recordOutgoingPacket(int64_t now, int64_t packetId, uint16_t payloadLength, Packet::Verb verb)
+	{
+		Mutex::Lock _l(_statistics_m);
+		if (verb != Packet::VERB_ACK && verb != Packet::VERB_QOS_MEASUREMENT) {
+			if ((packetId & (ZT_PATH_QOS_ACK_PROTOCOL_DIVISOR - 1)) == 0) {
+				_unackedBytes += payloadLength;
+				// Take note that we're expecting a VERB_ACK on this path as of a specific time
+				_expectingAckAsOf = ackAge(now) > ZT_PATH_ACK_INTERVAL ? _expectingAckAsOf : now;
+				if (_outQoSRecords.size() < ZT_PATH_MAX_OUTSTANDING_QOS_RECORDS) {
+					_outQoSRecords[packetId] = now;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Record statistics on incoming packets. Used later to estimate QoS metrics.
+	 *
+	 * @param now Current time
+	 * @param packetId ID of packet
+	 * @param payloadLength Length of payload
+	 * @param verb Packet verb
+	 */
+	inline void recordIncomingPacket(int64_t now, int64_t packetId, uint16_t payloadLength, Packet::Verb verb)
+	{
+		Mutex::Lock _l(_statistics_m);
+		if (verb != Packet::VERB_ACK && verb != Packet::VERB_QOS_MEASUREMENT) {
+			if ((packetId & (ZT_PATH_QOS_ACK_PROTOCOL_DIVISOR - 1)) == 0) {
+				_inACKRecords[packetId] = payloadLength;
+				_packetsReceivedSinceLastAck++;
+				_inQoSRecords[packetId] = now;
+				_packetsReceivedSinceLastQoS++;
+			}
+			_packetValiditySamples->push(true);
+		}
+	}
+
+	/**
+	 * Record that we've received a VERB_ACK on this path, also compute throughput if required.
+	 *
+	 * @param now Current time
+	 * @param ackedBytes Number of bytes acknowledged by other peer
+	 */
+	inline void receivedAck(int64_t now, int32_t ackedBytes)
+	{
+		_expectingAckAsOf = 0;
+		_unackedBytes = (ackedBytes > _unackedBytes) ? 0 : _unackedBytes - ackedBytes;
+		int64_t timeSinceThroughputEstimate = (now - _lastThroughputEstimation);
+		if (timeSinceThroughputEstimate >= ZT_PATH_THROUGHPUT_MEASUREMENT_INTERVAL) {
+			uint64_t throughput = (float)(_bytesAckedSinceLastThroughputEstimation * 8) / ((float)timeSinceThroughputEstimate / (float)1000);
+			_throughputSamples->push(throughput);
+			_maxLifetimeThroughput = throughput > _maxLifetimeThroughput ? throughput : _maxLifetimeThroughput;
+			_lastThroughputEstimation = now;
+			_bytesAckedSinceLastThroughputEstimation = 0;
+		} else {
+			_bytesAckedSinceLastThroughputEstimation += ackedBytes;
+		}
+	}
+
+	/**
+	 * @return Number of bytes this peer is responsible for ACKing since last ACK
+	 */
+	inline int32_t bytesToAck()
+	{
+		Mutex::Lock _l(_statistics_m);
+		int32_t bytesToAck = 0;
+		std::map<uint64_t,uint16_t>::iterator it = _inACKRecords.begin();
+		while (it != _inACKRecords.end()) {
+			bytesToAck += it->second;
+			it++;
+		}
+		return bytesToAck;
+	}
+
+	/**
+	 * @return Number of bytes thus far sent that have not been acknowledged by the remote peer
+	 */
+	inline int64_t unackedSentBytes()
+	{
+		return _unackedBytes;
+	}
+
+	/**
+	 * Account for the fact that an ACK was just sent. Reset counters, timers, and clear statistics buffers
+	 *
+	 * @param Current time
+	 */
+	inline void sentAck(int64_t now)
+	{
+		Mutex::Lock _l(_statistics_m);
+		_inACKRecords.clear();
+		_packetsReceivedSinceLastAck = 0;
+		_lastAck = now;
+	}
+
+	/**
+	 * Receive QoS data, match with recorded egress times from this peer, compute latency
+	 * estimates.
+	 *
+	 * @param now Current time
+	 * @param count Number of records
+	 * @param rx_id table of packet IDs
+	 * @param rx_ts table of holding times
+	 */
+	inline void receivedQoS(int64_t now, int count, uint64_t *rx_id, uint16_t *rx_ts)
+	{
+		Mutex::Lock _l(_statistics_m);
+		// Look up egress times and compute latency values for each record
+		std::map<uint64_t,uint64_t>::iterator it;
+		for (int j=0; j<count; j++) {
+			it = _outQoSRecords.find(rx_id[j]);
+			if (it != _outQoSRecords.end()) {
+				uint16_t rtt = (uint16_t)(now - it->second);
+				uint16_t rtt_compensated = rtt - rx_ts[j];
+				float latency = rtt_compensated / 2.0;
+				updateLatency(latency, now);
+				_outQoSRecords.erase(it);
+			}
+		}
+	}
+
+	/**
+	 * Generate the contents of a VERB_QOS_MEASUREMENT packet.
+	 *
+	 * @param now Current time
+	 * @param qosBuffer destination buffer
+	 * @return Size of payload
+	 */
+	inline int32_t generateQoSPacket(int64_t now, char *qosBuffer)
+	{
+		Mutex::Lock _l(_statistics_m);
+		int32_t len = 0;
+		std::map<uint64_t,uint64_t>::iterator it = _inQoSRecords.begin();
+		int i=0;
+		while (i<_packetsReceivedSinceLastQoS && it != _inQoSRecords.end()) {
+			uint64_t id = it->first;
+			memcpy(qosBuffer, &id, sizeof(uint64_t));
+			qosBuffer+=sizeof(uint64_t);
+			uint16_t holdingTime = (now - it->second);
+			memcpy(qosBuffer, &holdingTime, sizeof(uint16_t));
+			qosBuffer+=sizeof(uint16_t);
+			len+=sizeof(uint64_t)+sizeof(uint16_t);
+			_inQoSRecords.erase(it++);
+			i++;
+		}
+		return len;
+	}
+
+	/**
+	 * Account for the fact that a VERB_QOS_MEASUREMENT was just sent. Reset timers.
+	 *
+	 * @param Current time
+	 */
+	inline void sentQoS(int64_t now) {
+		_packetsReceivedSinceLastQoS = 0;
+		_lastQoSMeasurement = now;
+	}
+
+	/**
+	 * @param now Current time
+	 * @return Whether an ACK (VERB_ACK) packet needs to be emitted at this time
+	 */
+	inline bool needsToSendAck(int64_t now) {
+		return ((now - _lastAck) >= ZT_PATH_ACK_INTERVAL ||
+			(_packetsReceivedSinceLastAck == ZT_PATH_QOS_TABLE_SIZE)) && _packetsReceivedSinceLastAck;
+	}
+
+	/**
+	 * @param now Current time
+	 * @return Whether a QoS (VERB_QOS_MEASUREMENT) packet needs to be emitted at this time
+	 */
+	inline bool needsToSendQoS(int64_t now) {
+		return ((_packetsReceivedSinceLastQoS >= ZT_PATH_QOS_TABLE_SIZE) ||
+			((now - _lastQoSMeasurement) > ZT_PATH_QOS_INTERVAL)) && _packetsReceivedSinceLastQoS;
+	}
+
+	/**
+	 * How much time has elapsed since we've been expecting a VERB_ACK on this path. This value
+	 * is used to determine a more relevant path "age". This lets us penalize paths which are no
+	 * longer ACKing, but not those that simple aren't being used to carry traffic at the
+	 * current time.
+	 */
+	inline int64_t ackAge(int64_t now) { return _expectingAckAsOf ? now - _expectingAckAsOf : 0; }
+
+	/**
+	 * The maximum observed throughput (in bits/s) for this path
+	 */
+	inline uint64_t maxLifetimeThroughput() { return _maxLifetimeThroughput; }
+
+	/**
+	 * @return The mean throughput (in bits/s) of this link
+	 */
+	inline uint64_t meanThroughput() { return _lastComputedMeanThroughput; }
+
+	/**
+	 * Assign a new relative quality value for this path in the aggregate link
+	 *
+	 * @param rq Quality of this path in comparison to other paths available to this peer
+	 */
+	inline void updateRelativeQuality(float rq) { _lastComputedRelativeQuality = rq; }
+
+	/**
+	 * @return Quality of this path compared to others in the aggregate link
+	 */
+	inline float relativeQuality() { return _lastComputedRelativeQuality; }
+
+	/**
+	 * Assign a new allocation value for this path in the aggregate link
+	 *
+	 * @param allocation Percentage of traffic to be sent over this path to a peer
+	 */
+	inline void updateComponentAllocationOfAggregateLink(unsigned char allocation) { _lastAllocation = allocation; }
+
+	/**
+	 * @return Percentage of traffic allocated to this path in the aggregate link
+	 */
+	inline unsigned char allocation() { return _lastAllocation; }
+
+	/**
+	 * @return Stability estimates can become expensive to compute, we cache the most recent result.
+	 */
+	inline float lastComputedStability() { return _lastComputedStability; }
+
+	/**
+	 * @return A pointer to a cached copy of the human-readable name of the interface this Path's localSocket is bound to
+	 */
+	inline char *getName() { return _ifname; }
+
+	/**
+	 * @return Packet delay variance
+	 */
+	inline float packetDelayVariance() { return _lastComputedPacketDelayVariance; }
+
+	/**
+	 * @return Previously-computed mean latency
+	 */
+	inline float meanLatency() { return _lastComputedMeanLatency; }
+
+	/**
+	 * @return Packet loss rate (PLR)
+	 */
+	inline float packetLossRatio() { return _lastComputedPacketLossRatio; }
+
+	/**
+	 * @return Packet error ratio (PER)
+	 */
+	inline float packetErrorRatio() { return _lastComputedPacketErrorRatio; }
+
+	/**
+	 * Record an invalid incoming packet. This packet failed MAC/compression/cipher checks and will now
+	 * contribute to a Packet Error Ratio (PER).
+	 */
+	inline void recordInvalidPacket() { _packetValiditySamples->push(false); }
+
+	/**
+	 * @return A pointer to a cached copy of the address string for this Path (For debugging only)
+	 */
+	inline char *getAddressString() { return _addrString; }
+
+	/**
+	 * @return The current throughput disturbance coefficient
+	 */
+	inline float throughputDisturbanceCoefficient() { return _lastComputedThroughputDistCoeff; }
+
+	/**
+	 * Compute and cache stability and performance metrics. The resultant stability coefficient is a measure of how "well behaved"
+	 * this path is. This figure is substantially different from (but required for the estimation of the path's overall "quality".
+	 *
+	 * @param now Current time
+	 */
+	inline void processBackgroundPathMeasurements(int64_t now) {
+		if (now - _lastPathQualityComputeTime > ZT_PATH_QUALITY_COMPUTE_INTERVAL) {
+			Mutex::Lock _l(_statistics_m);
+			_lastPathQualityComputeTime = now;
+			address().toString(_addrString);
+			_lastComputedMeanLatency = _latencySamples->mean();
+			_lastComputedPacketDelayVariance = _latencySamples->stddev(); // Similar to "jitter" (SEE: RFC 3393, RFC 4689)
+			_lastComputedMeanThroughput = (uint64_t)_throughputSamples->mean();
+			// If no packet validity samples, assume PER==0
+			_lastComputedPacketErrorRatio = 1 - (_packetValiditySamples->count() ? _packetValiditySamples->mean() : 1);
+			// Compute path stability
+			// Normalize measurements with wildly different ranges into a reasonable range
+			float normalized_pdv = Utils::normalize(_lastComputedPacketDelayVariance, 0, ZT_PATH_MAX_PDV, 0, 10);
+			float normalized_la = Utils::normalize(_lastComputedMeanLatency, 0, ZT_PATH_MAX_MEAN_LATENCY, 0, 10);
+			float throughput_cv = _throughputSamples->mean() > 0 ? _throughputSamples->stddev() / _throughputSamples->mean() : 1;
+			// Form an exponential cutoff and apply contribution weights
+			float pdv_contrib = exp((-1)*normalized_pdv) * ZT_PATH_CONTRIB_PDV;
+			float latency_contrib = exp((-1)*normalized_la) * ZT_PATH_CONTRIB_LATENCY;
+			// Throughput Disturbance Coefficient
+			float throughput_disturbance_contrib = exp((-1)*throughput_cv) * ZT_PATH_CONTRIB_THROUGHPUT_DISTURBANCE;
+			_throughputDisturbanceSamples->push(throughput_cv);
+			_lastComputedThroughputDistCoeff = _throughputDisturbanceSamples->mean();
+			// Obey user-defined ignored contributions
+			pdv_contrib = ZT_PATH_CONTRIB_PDV > 0.0 ? pdv_contrib : 1;
+			latency_contrib = ZT_PATH_CONTRIB_LATENCY > 0.0 ? latency_contrib : 1;
+			throughput_disturbance_contrib = ZT_PATH_CONTRIB_THROUGHPUT_DISTURBANCE > 0.0 ? throughput_disturbance_contrib : 1;
+			// Stability
+			_lastComputedStability = pdv_contrib + latency_contrib + throughput_disturbance_contrib;
+			_lastComputedStability *= 1 - _lastComputedPacketErrorRatio;
+			// Prevent QoS records from sticking around for too long
+			std::map<uint64_t,uint64_t>::iterator it = _outQoSRecords.begin();
+			while (it != _outQoSRecords.end()) {
+				// Time since egress of tracked packet
+				if ((now - it->second) >= ZT_PATH_QOS_TIMEOUT) {
+					_outQoSRecords.erase(it++);
+				} else { it++; }
+			}
+		}
+	}
+
 	/**
 	 * @return True if this path is alive (receiving heartbeats)
 	 */
@@ -265,15 +647,71 @@ public:
 	 */
 	inline int64_t lastTrustEstablishedPacketReceived() const { return _lastTrustEstablishedPacketReceived; }
 
+	/**
+	 * Initialize statistical buffers
+	 */
+	inline void prepareBuffers() {
+		_throughputSamples = new RingBuffer<uint64_t>(ZT_PATH_QUALITY_METRIC_WIN_SZ);
+		_latencySamples = new RingBuffer<uint32_t>(ZT_PATH_QUALITY_METRIC_WIN_SZ);
+		_packetValiditySamples = new RingBuffer<bool>(ZT_PATH_QUALITY_METRIC_WIN_SZ);
+		_throughputDisturbanceSamples = new RingBuffer<float>(ZT_PATH_QUALITY_METRIC_WIN_SZ);
+		memset(_ifname, 0, 16);
+		memset(_addrString, 0, sizeof(_addrString));
+	}
+
 private:
+	Mutex _statistics_m;
+
 	volatile int64_t _lastOut;
 	volatile int64_t _lastIn;
 	volatile int64_t _lastTrustEstablishedPacketReceived;
+	volatile int64_t _lastPathQualityComputeTime;
 	int64_t _localSocket;
 	volatile unsigned int _latency;
 	InetAddress _addr;
 	InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
 	AtomicCounter __refCount;
+
+	std::map<uint64_t, uint64_t> _outQoSRecords; // id:egress_time
+	std::map<uint64_t, uint64_t> _inQoSRecords; // id:now
+	std::map<uint64_t, uint16_t> _inACKRecords; // id:len
+
+	int64_t _lastAck;
+	int64_t _lastThroughputEstimation;
+	int64_t _lastQoSMeasurement;
+	int64_t _lastQoSRecordPurge;
+
+	int64_t _unackedBytes;
+	int64_t _expectingAckAsOf;
+	int16_t _packetsReceivedSinceLastAck;
+	int16_t _packetsReceivedSinceLastQoS;
+
+	uint64_t _maxLifetimeThroughput;
+	uint64_t _lastComputedMeanThroughput;
+	uint64_t _bytesAckedSinceLastThroughputEstimation;
+
+	float _lastComputedMeanLatency;
+	float _lastComputedPacketDelayVariance;
+
+	float _lastComputedPacketErrorRatio;
+	float _lastComputedPacketLossRatio;
+
+	// cached estimates
+	float _lastComputedStability;
+	float _lastComputedRelativeQuality;
+	float _lastComputedThroughputDistCoeff;
+	unsigned char _lastAllocation;
+
+
+
+	// cached human-readable strings for tracing purposes
+	char _ifname[16];
+	char _addrString[256];
+
+	RingBuffer<uint64_t> *_throughputSamples;
+	RingBuffer<uint32_t> *_latencySamples;
+	RingBuffer<bool> *_packetValiditySamples;
+	RingBuffer<float> *_throughputDisturbanceSamples;
 };
 
 } // namespace ZeroTier

+ 394 - 36
node/Peer.cpp

@@ -25,7 +25,6 @@
  */
 
 #include "../version.h"
-
 #include "Constants.hpp"
 #include "Peer.hpp"
 #include "Node.hpp"
@@ -35,6 +34,8 @@
 #include "Packet.hpp"
 #include "Trace.hpp"
 #include "InetAddress.hpp"
+#include "RingBuffer.hpp"
+#include "Utils.hpp"
 
 namespace ZeroTier {
 
@@ -53,16 +54,31 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
 	_lastCredentialsReceived(0),
 	_lastTrustEstablishedPacketReceived(0),
 	_lastSentFullHello(0),
+	_lastACKWindowReset(0),
+	_lastQoSWindowReset(0),
+	_lastMultipathCompatibilityCheck(0),
+	_freeRandomByte(0),
+	_uniqueAlivePathCount(0),
+	_localMultipathSupported(false),
+	_remoteMultipathSupported(false),
+	_canUseMultipath(false),
 	_vProto(0),
 	_vMajor(0),
 	_vMinor(0),
 	_vRevision(0),
 	_id(peerIdentity),
 	_directPathPushCutoffCount(0),
-	_credentialsCutoffCount(0)
+	_credentialsCutoffCount(0),
+	_linkIsBalanced(false),
+	_linkIsRedundant(false),
+	_remotePeerMultipathEnabled(false),
+	_lastAggregateStatsReport(0),
+	_lastAggregateAllocation(0)
 {
+	Utils::getSecureRandom(&_freeRandomByte, 1);
 	if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
 		throw ZT_EXCEPTION_INVALID_ARGUMENT;
+	_pathChoiceHist = new RingBuffer<int>(ZT_MULTIPATH_PROPORTION_WIN_SZ);
 }
 
 void Peer::received(
@@ -70,6 +86,7 @@ void Peer::received(
 	const SharedPtr<Path> &path,
 	const unsigned int hops,
 	const uint64_t packetId,
+	const unsigned int payloadLength,
 	const Packet::Verb verb,
 	const uint64_t inRePacketId,
 	const Packet::Verb inReVerb,
@@ -95,9 +112,25 @@ void Peer::received(
 		path->trustedPacketReceived(now);
 	}
 
+	{
+		Mutex::Lock _l(_paths_m);
+
+		recordIncomingPacket(tPtr, path, packetId, payloadLength, verb, now);
+
+		if (_canUseMultipath) {
+			if (path->needsToSendQoS(now)) {
+				sendQOS_MEASUREMENT(tPtr, path, path->localSocket(), path->address(), now);
+			}
+			for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+				if (_paths[i].p) {
+					_paths[i].p->processBackgroundPathMeasurements(now);
+				}
+			}
+		}
+	}
+
 	if (hops == 0) {
 		// If this is a direct packet (no hops), update existing paths or learn new ones
-
 		bool havePath = false;
 		{
 			Mutex::Lock _l(_paths_m);
@@ -116,20 +149,26 @@ void Peer::received(
 		if ((!havePath)&&(RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address()))) {
 			Mutex::Lock _l(_paths_m);
 
-			// Paths are redunant if they duplicate an alive path to the same IP or
+			// Paths are redundant if they duplicate an alive path to the same IP or
 			// with the same local socket and address family.
 			bool redundant = false;
+			unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS;
 			for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
 				if (_paths[i].p) {
-					if ( (_paths[i].p->alive(now)) && ( ((_paths[i].p->localSocket() == path->localSocket())&&(_paths[i].p->address().ss_family == path->address().ss_family)) || (_paths[i].p->address().ipsEqual2(path->address())) ) )  {
+					if ( (_paths[i].p->alive(now)) && ( ((_paths[i].p->localSocket() == path->localSocket())&&(_paths[i].p->address().ss_family == path->address().ss_family)) || (_paths[i].p->address().ipsEqual2(path->address())) ) ) {
 						redundant = true;
 						break;
 					}
+					// If the path is the same address and port, simply assume this is a replacement
+					if ( (_paths[i].p->address().ipsEqual2(path->address()) && (_paths[i].p->address().port() == path->address().port()))) {
+						replacePath = i;
+						break;
+					}
 				} else break;
 			}
-
-			if (!redundant) {
-				unsigned int replacePath = ZT_MAX_PEER_NETWORK_PATHS;
+			// If the path isn't a duplicate of the same localSocket AND we haven't already determined a replacePath,
+			// then find the worst path and replace it.
+			if (!redundant && replacePath == ZT_MAX_PEER_NETWORK_PATHS) {
 				int replacePathQuality = 0;
 				for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
 					if (_paths[i].p) {
@@ -143,16 +182,15 @@ void Peer::received(
 						break;
 					}
 				}
-
-				if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) {
-					if (verb == Packet::VERB_OK) {
-						RR->t->peerLearnedNewPath(tPtr,networkId,*this,path,packetId);
-						_paths[replacePath].lr = now;
-						_paths[replacePath].p = path;
-						_paths[replacePath].priority = 1;
-					} else {
-						attemptToContact = true;
-					}
+			}
+			if (replacePath != ZT_MAX_PEER_NETWORK_PATHS) {
+				if (verb == Packet::VERB_OK) {
+					RR->t->peerLearnedNewPath(tPtr,networkId,*this,path,packetId);
+					_paths[replacePath].lr = now;
+					_paths[replacePath].p = path;
+					_paths[replacePath].priority = 1;
+				} else {
+					attemptToContact = true;
 				}
 			}
 		}
@@ -232,29 +270,265 @@ void Peer::received(
 	}
 }
 
-SharedPtr<Path> Peer::getBestPath(int64_t now,bool includeExpired) const
+void Peer::recordOutgoingPacket(const SharedPtr<Path> &path, const uint64_t packetId,
+	uint16_t payloadLength, const Packet::Verb verb, int64_t now)
 {
-	Mutex::Lock _l(_paths_m);
+	// Grab second byte from packetId to use as a source of entropy in the next path selection
+	_freeRandomByte = (packetId & 0xFF00) >> 8;
+	if (_canUseMultipath) {
+		path->recordOutgoingPacket(now, packetId, payloadLength, verb);
+	}
+}
 
+void Peer::recordIncomingPacket(void *tPtr, const SharedPtr<Path> &path, const uint64_t packetId,
+	uint16_t payloadLength, const Packet::Verb verb, int64_t now)
+{
+	if (_canUseMultipath) {
+		if (path->needsToSendAck(now)) {
+			sendACK(tPtr, path, path->localSocket(), path->address(), now);
+		}
+		path->recordIncomingPacket(now, packetId, payloadLength, verb);
+	}
+}
+
+void Peer::computeAggregateProportionalAllocation(int64_t now)
+{
+	float maxStability = 0;
+	float totalRelativeQuality = 0;
+	float maxThroughput = 1;
+	float maxScope = 0;
+	float relStability[ZT_MAX_PEER_NETWORK_PATHS];
+	float relThroughput[ZT_MAX_PEER_NETWORK_PATHS];
+	memset(&relStability, 0, sizeof(relStability));
+	memset(&relThroughput, 0, sizeof(relThroughput));
+	// Survey all paths
+	for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+		if (_paths[i].p) {
+			relStability[i] = _paths[i].p->lastComputedStability();
+			relThroughput[i] = _paths[i].p->maxLifetimeThroughput();
+			maxStability = relStability[i] > maxStability ? relStability[i] : maxStability;
+			maxThroughput = relThroughput[i] > maxThroughput ? relThroughput[i] : maxThroughput;
+			maxScope = _paths[i].p->ipScope() > maxScope ? _paths[i].p->ipScope() : maxScope;
+		}
+	}
+	// Convert to relative values
+	for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+		if (_paths[i].p) {
+			relStability[i] /= maxStability ? maxStability : 1;
+			relThroughput[i] /= maxThroughput ? maxThroughput : 1;
+			float normalized_ma = Utils::normalize(_paths[i].p->ackAge(now), 0, ZT_PATH_MAX_AGE, 0, 10);
+			float age_contrib = exp((-1)*normalized_ma);
+			float relScope = ((float)(_paths[i].p->ipScope()+1) / (maxScope + 1));
+			float relQuality =
+				(relStability[i] * ZT_PATH_CONTRIB_STABILITY)
+				+ (fmax(1, relThroughput[i]) * ZT_PATH_CONTRIB_THROUGHPUT)
+				+ relScope * ZT_PATH_CONTRIB_SCOPE;
+			relQuality *= age_contrib;
+			// Arbitrary cutoffs
+			relQuality = relQuality > (1.00 / 100.0) ? relQuality : 0.0;
+			relQuality = relQuality < (99.0 / 100.0) ? relQuality : 1.0;
+			totalRelativeQuality += relQuality;
+			_paths[i].p->updateRelativeQuality(relQuality);
+		}
+	}
+	// Convert set of relative performances into an allocation set
+	for(uint16_t i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+		if (_paths[i].p) {
+			_paths[i].p->updateComponentAllocationOfAggregateLink((_paths[i].p->relativeQuality() / totalRelativeQuality) * 255);
+		}
+	}
+}
+
+int Peer::computeAggregateLinkPacketDelayVariance()
+{
+	float pdv = 0.0;
+	for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+		if (_paths[i].p) {
+			pdv += _paths[i].p->relativeQuality() * _paths[i].p->packetDelayVariance();
+		}
+	}
+	return pdv;
+}
+
+int Peer::computeAggregateLinkMeanLatency()
+{
+	int ml = 0;
+	int pathCount = 0;
+	for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+		if (_paths[i].p) {
+			pathCount++;
+			ml += _paths[i].p->relativeQuality() * _paths[i].p->meanLatency();
+		}
+	}
+	return ml / pathCount;
+}
+
+int Peer::aggregateLinkPhysicalPathCount()
+{
+	std::map<std::string, bool> ifnamemap;
+	int pathCount = 0;
+	int64_t now = RR->node->now();
+	for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+		if (_paths[i].p && _paths[i].p->alive(now)) {
+			if (!ifnamemap[_paths[i].p->getName()]) {
+				ifnamemap[_paths[i].p->getName()] = true;
+				pathCount++;
+			}
+		}
+	}
+	return pathCount;
+}
+
+int Peer::aggregateLinkLogicalPathCount()
+{
+	int pathCount = 0;
+	int64_t now = RR->node->now();
+	for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+		if (_paths[i].p && _paths[i].p->alive(now)) {
+			pathCount++;
+		}
+	}
+	return pathCount;
+}
+
+SharedPtr<Path> Peer::getAppropriatePath(int64_t now, bool includeExpired)
+{
+	Mutex::Lock _l(_paths_m);
 	unsigned int bestPath = ZT_MAX_PEER_NETWORK_PATHS;
-	long bestPathQuality = 2147483647;
+
+	/**
+	 * Send traffic across the highest quality path only. This algorithm will still
+	 * use the old path quality metric from protocol version 9.
+	 */
+	if (!_canUseMultipath) {
+		long bestPathQuality = 2147483647;
+		for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+			if (_paths[i].p) {
+				if ((includeExpired)||((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION)) {
+					const long q = _paths[i].p->quality(now) / _paths[i].priority;
+					if (q <= bestPathQuality) {
+						bestPathQuality = q;
+						bestPath = i;
+					}
+				}
+			} else break;
+		}
+		if (bestPath != ZT_MAX_PEER_NETWORK_PATHS) {
+			return _paths[bestPath].p;
+		}
+		return SharedPtr<Path>();
+	}
+
 	for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
 		if (_paths[i].p) {
-			if ((includeExpired)||((now - _paths[i].lr) < ZT_PEER_PATH_EXPIRATION)) {
-				const long q = _paths[i].p->quality(now) / _paths[i].priority;
-				if (q <= bestPathQuality) {
-					bestPathQuality = q;
-					bestPath = i;
+			_paths[i].p->processBackgroundPathMeasurements(now);
+		}
+	}
+
+	/**
+	 * Randomly distribute traffic across all paths
+	 */
+	int numAlivePaths = 0;
+	int numStalePaths = 0;
+	if (RR->node->getMultipathMode() == ZT_MULTIPATH_RANDOM) {
+		int alivePaths[ZT_MAX_PEER_NETWORK_PATHS];
+		int stalePaths[ZT_MAX_PEER_NETWORK_PATHS];
+		memset(&alivePaths, -1, sizeof(alivePaths));
+		memset(&stalePaths, -1, sizeof(stalePaths));
+		for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+			if (_paths[i].p) {
+				if (_paths[i].p->alive(now)) {
+					alivePaths[numAlivePaths] = i;
+					numAlivePaths++;
+				}
+				else {
+					stalePaths[numStalePaths] = i;
+					numStalePaths++;
 				}
 			}
-		} else break;
+		}
+		unsigned int r = _freeRandomByte;
+		if (numAlivePaths > 0) {
+			int rf = r % numAlivePaths;
+			return _paths[alivePaths[rf]].p;
+		}
+		else if(numStalePaths > 0) {
+			// Resort to trying any non-expired path
+			int rf = r % numStalePaths;
+			return _paths[stalePaths[rf]].p;
+		}
 	}
 
-	if (bestPath != ZT_MAX_PEER_NETWORK_PATHS)
-		return _paths[bestPath].p;
+	/**
+	 * Proportionally allocate traffic according to dynamic path quality measurements
+	 */
+	if (RR->node->getMultipathMode() == ZT_MULTIPATH_PROPORTIONALLY_BALANCED) {
+		if ((now - _lastAggregateAllocation) >= ZT_PATH_QUALITY_COMPUTE_INTERVAL) {
+			_lastAggregateAllocation = now;
+			computeAggregateProportionalAllocation(now);
+		}
+		// Randomly choose path according to their allocations
+		float rf = _freeRandomByte;
+		for(int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+			if (_paths[i].p) {
+				if (rf < _paths[i].p->allocation()) {
+					bestPath = i;
+					_pathChoiceHist->push(bestPath); // Record which path we chose
+					break;
+				}
+				rf -= _paths[i].p->allocation();
+			}
+		}
+		if (bestPath < ZT_MAX_PEER_NETWORK_PATHS) {
+			return _paths[bestPath].p;
+		}
+	}
 	return SharedPtr<Path>();
 }
 
+char *Peer::interfaceListStr()
+{
+	std::map<std::string, int> ifnamemap;
+	char tmp[32];
+	const int64_t now = RR->node->now();
+	char *ptr = _interfaceListStr;
+	bool imbalanced = false;
+	memset(_interfaceListStr, 0, sizeof(_interfaceListStr));
+	int alivePathCount = aggregateLinkLogicalPathCount();
+	for(unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+		if (_paths[i].p && _paths[i].p->alive(now)) {
+			int ipv = _paths[i].p->address().isV4();
+			// If this is acting as an aggregate link, check allocations
+			float targetAllocation = 1.0 / alivePathCount;
+			float currentAllocation = 1.0;
+			if (alivePathCount > 1) {
+				currentAllocation = (float)_pathChoiceHist->countValue(i) / (float)_pathChoiceHist->count();
+				if (fabs(targetAllocation - currentAllocation) > ZT_PATH_IMBALANCE_THRESHOLD) {
+					imbalanced = true;
+				}
+			}
+			char *ipvStr = ipv ? (char*)"ipv4" : (char*)"ipv6";
+			sprintf(tmp, "(%s, %s, %.3f)", _paths[i].p->getName(), ipvStr, currentAllocation);
+			// Prevent duplicates
+			if(ifnamemap[_paths[i].p->getName()] != ipv) {
+				memcpy(ptr, tmp, strlen(tmp));
+				ptr += strlen(tmp);
+				*ptr = ' ';
+				ptr++;
+				ifnamemap[_paths[i].p->getName()] = ipv;
+			}
+		}
+	}
+	ptr--; // Overwrite trailing space
+	if (imbalanced) {
+		sprintf(tmp, ", is asymmetrical");
+		memcpy(ptr, tmp, sizeof(tmp));
+	} else {
+		*ptr = '\0';
+	}
+	return _interfaceListStr;
+}
+
 void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &other) const
 {
 	unsigned int myBestV4ByScope[ZT_INETADDRESS_MAX_SCOPE+1];
@@ -376,6 +650,71 @@ void Peer::introduce(void *const tPtr,const int64_t now,const SharedPtr<Peer> &o
 	}
 }
 
+inline void Peer::processBackgroundPeerTasks(int64_t now)
+{
+	// Determine current multipath compatibility with other peer
+	if ((now - _lastMultipathCompatibilityCheck) >= ZT_PATH_QUALITY_COMPUTE_INTERVAL) {
+		// Cache number of available paths so that we can short-circuit multipath logic elsewhere
+		//
+		// We also take notice of duplicate paths (same IP only) because we may have
+		// recently received a direct path push from a peer and our list might contain
+		// a dead path which hasn't been fully recognized as such. In this case we
+		// don't want the duplicate to trigger execution of multipath code prematurely.
+		//
+		// This is done to support the behavior of auto multipath enable/disable
+		// without user intervention.
+		int currAlivePathCount = 0;
+		int duplicatePathsFound = 0;
+		for (unsigned int i=0;i<ZT_MAX_PEER_NETWORK_PATHS;++i) {
+			if (_paths[i].p) {
+				currAlivePathCount++;
+				for (unsigned int j=0;j<ZT_MAX_PEER_NETWORK_PATHS;++j) {
+					if (_paths[i].p && _paths[j].p && _paths[i].p->address().ipsEqual2(_paths[j].p->address()) && i != j) {
+						duplicatePathsFound+=1;
+						break;
+					}
+				}
+			}
+		}
+		_uniqueAlivePathCount = (currAlivePathCount - (duplicatePathsFound / 2));
+		_lastMultipathCompatibilityCheck = now;
+		_localMultipathSupported = ((RR->node->getMultipathMode() != ZT_MULTIPATH_NONE) && (ZT_PROTO_VERSION > 9));
+		_remoteMultipathSupported = _vProto > 9;
+		// If both peers support multipath and more than one path exist, we can use multipath logic
+		_canUseMultipath = _localMultipathSupported && _remoteMultipathSupported && (_uniqueAlivePathCount > 1);
+	}
+}
+
+void Peer::sendACK(void *tPtr,const SharedPtr<Path> &path,const int64_t localSocket,const InetAddress &atAddress,int64_t now)
+{
+	Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ACK);
+	uint32_t bytesToAck = path->bytesToAck();
+	outp.append<uint32_t>(bytesToAck);
+	if (atAddress) {
+		outp.armor(_key,false);
+		RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
+	} else {
+		RR->sw->send(tPtr,outp,false);
+	}
+	path->sentAck(now);
+}
+
+void Peer::sendQOS_MEASUREMENT(void *tPtr,const SharedPtr<Path> &path,const int64_t localSocket,const InetAddress &atAddress,int64_t now)
+{
+	const int64_t _now = RR->node->now();
+	Packet outp(_id.address(),RR->identity.address(),Packet::VERB_QOS_MEASUREMENT);
+	char qosData[ZT_PATH_MAX_QOS_PACKET_SZ];
+	int16_t len = path->generateQoSPacket(_now,qosData);
+	outp.append(qosData,len);
+	if (atAddress) {
+		outp.armor(_key,false);
+		RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
+	} else {
+		RR->sw->send(tPtr,outp,false);
+	}
+	path->sentQoS(now);
+}
+
 void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,int64_t now)
 {
 	Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
@@ -444,12 +783,30 @@ void Peer::tryMemorizedPath(void *tPtr,int64_t now)
 unsigned int Peer::doPingAndKeepalive(void *tPtr,int64_t now)
 {
 	unsigned int sent = 0;
-
 	Mutex::Lock _l(_paths_m);
 
 	const bool sendFullHello = ((now - _lastSentFullHello) >= ZT_PEER_PING_PERIOD);
 	_lastSentFullHello = now;
 
+	processBackgroundPeerTasks(now);
+
+	// Emit traces regarding aggregate link status
+	if (_canUseMultipath) {
+		int alivePathCount = aggregateLinkPhysicalPathCount();
+		if ((now - _lastAggregateStatsReport) > ZT_PATH_AGGREGATE_STATS_REPORT_INTERVAL) {
+			_lastAggregateStatsReport = now;
+			if (alivePathCount) {
+				RR->t->peerLinkAggregateStatistics(NULL,*this);
+			}
+		} if (alivePathCount < 2 && _linkIsRedundant) {
+			_linkIsRedundant = !_linkIsRedundant;
+			RR->t->peerLinkNoLongerRedundant(NULL,*this);
+		} if (alivePathCount > 1 && !_linkIsRedundant) {
+			_linkIsRedundant = !_linkIsRedundant;
+			RR->t->peerLinkNowRedundant(NULL,*this);
+		}
+	}
+
 	// Right now we only keep pinging links that have the maximum priority. The
 	// priority is used to track cluster redirections, meaning that when a cluster
 	// redirects us its redirect target links override all other links and we
@@ -477,13 +834,14 @@ unsigned int Peer::doPingAndKeepalive(void *tPtr,int64_t now)
 			}
 		} else break;
 	}
-	while(j < ZT_MAX_PEER_NETWORK_PATHS) {
-		_paths[j].lr = 0;
-		_paths[j].p.zero();
-		_paths[j].priority = 1;
-		++j;
+	if (canUseMultipath()) {
+		while(j < ZT_MAX_PEER_NETWORK_PATHS) {
+			_paths[j].lr = 0;
+			_paths[j].p.zero();
+			_paths[j].priority = 1;
+			++j;
+		}
 	}
-
 	return sent;
 }
 

+ 198 - 18
node/Peer.hpp

@@ -27,18 +27,13 @@
 #ifndef ZT_PEER_HPP
 #define ZT_PEER_HPP
 
-#include <stdint.h>
-
-#include "Constants.hpp"
-
-#include <algorithm>
-#include <utility>
 #include <vector>
-#include <stdexcept>
 
 #include "../include/ZeroTierOne.h"
 
+#include "Constants.hpp"
 #include "RuntimeEnvironment.hpp"
+#include "Node.hpp"
 #include "Path.hpp"
 #include "Address.hpp"
 #include "Utils.hpp"
@@ -65,7 +60,11 @@ private:
 	Peer() {} // disabled to prevent bugs -- should not be constructed uninitialized
 
 public:
-	~Peer() { Utils::burn(_key,sizeof(_key)); }
+	~Peer() {
+		Utils::burn(_key,sizeof(_key));
+		delete _pathChoiceHist;
+		_pathChoiceHist = NULL;
+	}
 
 	/**
 	 * Construct a new peer
@@ -108,6 +107,7 @@ public:
 		const SharedPtr<Path> &path,
 		const unsigned int hops,
 		const uint64_t packetId,
+		const unsigned int payloadLength,
 		const Packet::Verb verb,
 		const uint64_t inRePacketId,
 		const Packet::Verb inReVerb,
@@ -145,20 +145,95 @@ public:
 	 */
 	inline bool sendDirect(void *tPtr,const void *data,unsigned int len,int64_t now,bool force)
 	{
-		SharedPtr<Path> bp(getBestPath(now,force));
+		SharedPtr<Path> bp(getAppropriatePath(now,force));
 		if (bp)
 			return bp->send(RR,tPtr,data,len,now);
 		return false;
 	}
 
 	/**
-	 * Get the best current direct path
+	 * Record statistics on outgoing packets
+	 *
+	 * @param path Path over which packet was sent
+	 * @param id Packet ID
+	 * @param len Length of packet payload
+	 * @param verb Packet verb
+	 * @param now Current time
+	 */
+	void recordOutgoingPacket(const SharedPtr<Path> &path, const uint64_t packetId, uint16_t payloadLength, const Packet::Verb verb, int64_t now);
+
+	/**
+	 * Record statistics on incoming packets
+	 *
+	 * @param path Path over which packet was sent
+	 * @param id Packet ID
+	 * @param len Length of packet payload
+	 * @param verb Packet verb
+	 * @param now Current time
+	 */
+	void recordIncomingPacket(void *tPtr, const SharedPtr<Path> &path, const uint64_t packetId, uint16_t payloadLength, const Packet::Verb verb, int64_t now);
+
+	/**
+	 * Send an ACK to peer for the most recent packets received
+	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+	 * @param localSocket Raw socket the ACK packet will be sent over
+	 * @param atAddress Destination for the ACK packet
+	 * @param now Current time
+	 */
+	void sendACK(void *tPtr, const SharedPtr<Path> &path, const int64_t localSocket,const InetAddress &atAddress,int64_t now);
+
+	/**
+	 * Send a QoS packet to peer so that it can evaluate the quality of this link
+	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+	 * @param localSocket Raw socket the QoS packet will be sent over
+	 * @param atAddress Destination for the QoS packet
+	 * @param now Current time
+	 */
+	void sendQOS_MEASUREMENT(void *tPtr, const SharedPtr<Path> &path, const int64_t localSocket,const InetAddress &atAddress,int64_t now);
+
+	/**
+	 * Compute relative quality values and allocations for the components of the aggregate link
+	 *
+	 * @param now Current time
+	 */
+	void computeAggregateProportionalAllocation(int64_t now);
+
+	/**
+	 * @return The aggregate link Packet Delay Variance (PDV)
+	 */
+	int computeAggregateLinkPacketDelayVariance();
+
+	/**
+	 * @return The aggregate link mean latency
+	 */
+	int computeAggregateLinkMeanLatency();
+
+	/**
+	 * @return The number of currently alive "physical" paths in the aggregate link
+	 */
+	int aggregateLinkPhysicalPathCount();
+
+	/**
+	 * @return The number of currently alive "logical" paths in the aggregate link
+	 */
+	int aggregateLinkLogicalPathCount();
+
+	/**
+	 * Get the most appropriate direct path based on current multipath and QoS configuration
 	 *
 	 * @param now Current time
 	 * @param includeExpired If true, include even expired paths
 	 * @return Best current path or NULL if none
 	 */
-	SharedPtr<Path> getBestPath(int64_t now,bool includeExpired) const;
+	SharedPtr<Path> getAppropriatePath(int64_t now, bool includeExpired);
+
+	/**
+	 * Generate a human-readable string of interface names making up the aggregate link, also include
+	 * moving allocation and IP version number for each (for tracing)
+	 */
+	char *interfaceListStr();
 
 	/**
 	 * Send VERB_RENDEZVOUS to this and another peer via the best common IP scope and path
@@ -212,6 +287,16 @@ public:
 	 */
 	unsigned int doPingAndKeepalive(void *tPtr,int64_t now);
 
+	/**
+	 * Clear paths whose localSocket(s) are in a CLOSED state or have an otherwise INVALID state.
+	 * This should be called frequently so that we can detect and remove unproductive or invalid paths.
+	 *
+	 * Under the hood this is done periodically based on ZT_CLOSED_PATH_PRUNING_INTERVAL.
+	 *
+	 * @return Number of paths that were pruned this round
+	 */
+	unsigned int prunePaths();
+
 	/**
 	 * Process a cluster redirect sent by this peer
 	 *
@@ -268,14 +353,18 @@ public:
 	inline int64_t isActive(int64_t now) const { return ((now - _lastNontrivialReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
 
 	/**
-	 * @return Latency in milliseconds of best path or 0xffff if unknown / no paths
+	 * @return Latency in milliseconds of best/aggregate path or 0xffff if unknown / no paths
 	 */
-	inline unsigned int latency(const int64_t now) const
+	inline unsigned int latency(const int64_t now)
 	{
-		SharedPtr<Path> bp(getBestPath(now,false));
-		if (bp)
-			return bp->latency();
-		return 0xffff;
+		if (_canUseMultipath) {
+			return (int)computeAggregateLinkMeanLatency();
+		} else {
+			SharedPtr<Path> bp(getAppropriatePath(now,false));
+			if (bp)
+				return bp->latency();
+			return 0xffff;
+		}
 	}
 
 	/**
@@ -289,7 +378,7 @@ public:
 	 *
 	 * @return Relay quality score computed from latency and other factors, lower is better
 	 */
-	inline unsigned int relayQuality(const int64_t now) const
+	inline unsigned int relayQuality(const int64_t now)
 	{
 		const uint64_t tsr = now - _lastReceive;
 		if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT)
@@ -328,6 +417,37 @@ public:
 
 	inline bool remoteVersionKnown() const { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
 
+	/**
+	 * Periodically update known multipath activation constraints. This is done so that we know when and when
+	 * not to use multipath logic. Doing this once every few seconds is sufficient.
+	 *
+	 * @param now Current time
+	 */
+	inline void processBackgroundPeerTasks(int64_t now);
+
+	/**
+	 * Record that the remote peer does have multipath enabled. As is evident by the receipt of a VERB_ACK
+	 * or a VERB_QOS_MEASUREMENT packet at some point in the past. Until this flag is set, the local client
+	 * shall assume that multipath is not enabled and should only use classical Protocol 9 logic.
+	 */
+	inline void inferRemoteMultipathEnabled() { _remotePeerMultipathEnabled = true; }
+
+	/**
+	 * @return Whether the local client supports and is configured to use multipath
+	 */
+	inline bool localMultipathSupport() { return _localMultipathSupported; }
+
+	/**
+	 * @return Whether the remote peer supports and is configured to use multipath
+	 */
+	inline bool remoteMultipathSupport() { return _remoteMultipathSupported; }
+
+	/**
+	 * @return Whether this client can use multipath to communicate with this peer. True if both peers are using
+	 * the correct protocol and if both peers have multipath enabled. False if otherwise.
+	 */
+	inline bool canUseMultipath() { return _canUseMultipath; }
+
 	/**
 	 * @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
 	 */
@@ -417,6 +537,41 @@ public:
 		return false;
 	}
 
+	/**
+	 * Rate limit gate for VERB_ACK
+	 */
+	inline bool rateGateACK(const int64_t now)
+	{
+		if ((now - _lastACKWindowReset) >= ZT_PATH_QOS_ACK_CUTOFF_TIME) {
+			_lastACKWindowReset = now;
+			_ACKCutoffCount = 0;
+		} else {
+			++_ACKCutoffCount;
+		}
+		return (_ACKCutoffCount < ZT_PATH_QOS_ACK_CUTOFF_LIMIT);
+	}
+
+	/**
+	 * Rate limit gate for VERB_QOS_MEASUREMENT
+	 */
+	inline bool rateGateQoS(const int64_t now)
+	{
+		if ((now - _lastQoSWindowReset) >= ZT_PATH_QOS_ACK_CUTOFF_TIME) {
+			_lastQoSWindowReset = now;
+			_QoSCutoffCount = 0;
+		} else {
+			++_QoSCutoffCount;
+		}
+		return (_QoSCutoffCount < ZT_PATH_QOS_ACK_CUTOFF_LIMIT);
+	}
+
+	/**
+	 * @return Whether this peer is reachable via an aggregate link
+	 */
+	inline bool hasAggregateLink() {
+		return _localMultipathSupported && _remoteMultipathSupported && _remotePeerMultipathEnabled;
+	}
+
 	/**
 	 * Serialize a peer for storage in local cache
 	 *
@@ -515,6 +670,18 @@ private:
 	int64_t _lastCredentialsReceived;
 	int64_t _lastTrustEstablishedPacketReceived;
 	int64_t _lastSentFullHello;
+	int64_t _lastPathPrune;
+	int64_t _lastACKWindowReset;
+	int64_t _lastQoSWindowReset;
+	int64_t _lastMultipathCompatibilityCheck;
+
+	unsigned char _freeRandomByte;
+
+	int _uniqueAlivePathCount;
+
+	bool _localMultipathSupported;
+	bool _remoteMultipathSupported;
+	bool _canUseMultipath;
 
 	uint16_t _vProto;
 	uint16_t _vMajor;
@@ -528,8 +695,21 @@ private:
 
 	unsigned int _directPathPushCutoffCount;
 	unsigned int _credentialsCutoffCount;
+	unsigned int _QoSCutoffCount;
+	unsigned int _ACKCutoffCount;
 
 	AtomicCounter __refCount;
+
+	RingBuffer<int> *_pathChoiceHist;
+
+	bool _linkIsBalanced;
+	bool _linkIsRedundant;
+	bool _remotePeerMultipathEnabled;
+
+	int64_t _lastAggregateStatsReport;
+	int64_t _lastAggregateAllocation;
+
+	char _interfaceListStr[256]; // 16 characters * 16 paths in a link
 };
 
 } // namespace ZeroTier

+ 345 - 0
node/RingBuffer.hpp

@@ -0,0 +1,345 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018  ZeroTier, Inc.  https://www.zerotier.com/
+ *
+ * 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/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_RINGBUFFER_H
+#define ZT_RINGBUFFER_H
+
+#include <typeinfo>
+#include <cstdint>
+#include <stdlib.h>
+#include <memory.h>
+#include <algorithm>
+#include <math.h>
+
+namespace ZeroTier {
+
+/**
+ * A circular buffer
+ *
+ * For fast handling of continuously-evolving variables (such as path quality metrics).
+ * Using this, we can maintain longer sliding historical windows for important path 
+ * metrics without the need for potentially expensive calls to memcpy/memmove.
+ *
+ * Some basic statistical functionality is implemented here in an attempt
+ * to reduce the complexity of code needed to interact with this type of buffer.
+ */
+
+template <class T>
+class RingBuffer
+{
+private:
+	T * buf;
+	size_t size;
+	size_t begin;
+	size_t end;
+	bool wrap;
+
+public:
+
+	/**
+	 * create a RingBuffer with space for up to size elements.
+	 */
+	explicit RingBuffer(size_t size)
+		: size(size),
+		begin(0),
+		end(0),
+		wrap(false)
+	{
+		buf = new T[size];
+		memset(buf, 0, sizeof(T) * size);
+	}
+
+	/**
+	 * @return A pointer to the underlying buffer
+	 */
+	T* get_buf()
+	{
+		return buf + begin;
+	}
+
+	/** 
+	 * Adjust buffer index pointer as if we copied data in
+	 * @param n Number of elements to copy in
+	 * @return Number of elements we copied in
+	 */
+	size_t produce(size_t n)
+	{
+		n = std::min(n, getFree());
+		if (n == 0) {
+			return n;
+		}
+		const size_t first_chunk = std::min(n, size - end);
+		end = (end + first_chunk) % size;
+		if (first_chunk < n) {
+			const size_t second_chunk = n - first_chunk;
+			end = (end + second_chunk) % size;
+		}
+		if (begin == end) {
+			wrap = true;
+		}
+		return n;
+	}
+
+	/** 
+	 * Fast erase, O(1). 
+	 * Merely reset the buffer pointer, doesn't erase contents
+	 */
+	void reset()
+	{
+		consume(count());
+	}
+
+	/** 
+	 * adjust buffer index pointer as if we copied data out
+	 * @param n Number of elements we copied from the buffer
+	 * @return Number of elements actually available from the buffer
+	 */
+	size_t consume(size_t n)
+	{
+		n = std::min(n, count());
+		if (n == 0) {
+			return n;
+		}
+		if (wrap) {
+			wrap = false;
+		}
+		const size_t first_chunk = std::min(n, size - begin);
+		begin = (begin + first_chunk) % size;
+		if (first_chunk < n) {
+			const size_t second_chunk = n - first_chunk;
+			begin = (begin + second_chunk) % size;
+		}
+		return n;
+	}
+
+	/**
+	 * @param data Buffer that is to be written to the ring
+	 * @param n Number of elements to write to the buffer
+	 */
+	size_t write(const T * data, size_t n)
+	{
+		n = std::min(n, getFree());
+		if (n == 0) {
+			return n;
+		}
+		const size_t first_chunk = std::min(n, size - end);
+		memcpy(buf + end, data, first_chunk * sizeof(T));
+		end = (end + first_chunk) % size;
+		if (first_chunk < n) {
+			const size_t second_chunk = n - first_chunk;
+			memcpy(buf + end, data + first_chunk, second_chunk * sizeof(T));
+			end = (end + second_chunk) % size;
+		}
+		if (begin == end) {
+			wrap = true;
+		}
+		return n;
+	}
+
+	/**
+	 * Place a single value on the buffer. If the buffer is full, consume a value first.
+	 *
+	 * @param value A single value to be placed in the buffer
+	 */
+	void push(const T value)
+	{
+		if (count() == size) {
+			consume(1);
+		}
+		const size_t first_chunk = std::min((size_t)1, size - end);
+		*(buf + end) = value;
+		end = (end + first_chunk) % size;
+		if (begin == end) {
+			wrap = true;
+		}
+	}
+
+	/**
+	 * @return The most recently pushed element on the buffer
+	 */
+	T get_most_recent() { return *(buf + end); }
+
+	/**
+	 * @param dest Destination buffer
+	 * @param n Size (in terms of number of elements) of the destination buffer
+	 * @return Number of elements read from the buffer
+	 */
+	size_t read(T * dest, size_t n)
+	{
+		n = std::min(n, count());
+		if (n == 0) {
+			return n;
+		}
+		if (wrap) {
+			wrap = false;
+		}
+		const size_t first_chunk = std::min(n, size - begin);
+		memcpy(dest, buf + begin, first_chunk * sizeof(T));
+		begin = (begin + first_chunk) % size;
+		if (first_chunk < n) {
+			const size_t second_chunk = n - first_chunk;
+			memcpy(dest + first_chunk, buf + begin, second_chunk * sizeof(T));
+			begin = (begin + second_chunk) % size;
+		}
+		return n;
+	}
+
+	/**
+	 * Return how many elements are in the buffer, O(1).
+	 *
+	 * @return The number of elements in the buffer
+	 */
+	size_t count()
+	{
+		if (end == begin) {
+			return wrap ? size : 0;
+		}
+		else if (end > begin) {
+			return end - begin;
+		}
+		else {
+			return size + end - begin;
+		}
+	}
+
+	/**
+	 * @return The number of slots that are unused in the buffer
+	 */
+	size_t getFree() { return size - count(); }
+
+	/**
+	 * @return The arithmetic mean of the contents of the buffer
+	 */
+	float mean()
+	{
+		size_t iterator = begin;
+		float subtotal = 0;
+		size_t curr_cnt = count();
+		for (size_t i=0; i<curr_cnt; i++) {
+			iterator = (iterator + size - 1) % curr_cnt;
+			subtotal += (float)*(buf + iterator);
+		}
+		return curr_cnt ? subtotal / (float)curr_cnt : 0;
+	}
+
+	/**
+	 * @return The arithmetic mean of the most recent 'n' elements of the buffer
+	 */
+	float mean(size_t n)
+	{
+		n = n < size ? n : size;
+		size_t iterator = begin;
+		float subtotal = 0;
+		size_t curr_cnt = count();
+		for (size_t i=0; i<n; i++) {
+			iterator = (iterator + size - 1) % curr_cnt;
+			subtotal += (float)*(buf + iterator);
+		}
+		return curr_cnt ? subtotal / (float)curr_cnt : 0;
+	}
+
+	/**
+	 * @return The sample standard deviation of element values
+	 */
+	float stddev() { return sqrt(variance()); }
+
+	/**
+	 * @return The variance of element values
+	 */
+	float variance()
+	{
+		size_t iterator = begin;
+		float cached_mean = mean();
+		size_t curr_cnt = count();
+		if (size) {
+			T sum_of_squared_deviations = 0;
+			for (size_t i=0; i<curr_cnt; i++) {
+				iterator = (iterator + size - 1) % curr_cnt;
+				float deviation = (buf[i] - cached_mean);
+				sum_of_squared_deviations += (deviation*deviation);
+			}
+			float variance = (float)sum_of_squared_deviations / (float)(size - 1);
+			return variance;
+		}
+		return 0;
+	}
+
+	/**
+	 * @return The number of elements of zero value
+	 */
+	size_t zeroCount()
+	{
+		size_t iterator = begin;
+		size_t zeros = 0;
+		size_t curr_cnt = count();
+		for (size_t i=0; i<curr_cnt; i++) {
+			iterator = (iterator + size - 1) % curr_cnt;
+			if (*(buf + iterator) == 0) {
+				zeros++;
+			}
+		}
+		return zeros;
+	}
+
+	/**
+	 * @param value Value to match against in buffer
+	 * @return The number of values held in the ring buffer which match a given value
+	 */
+	size_t countValue(T value)
+	{
+		size_t iterator = begin;
+		size_t cnt = 0;
+		size_t curr_cnt = count();
+		for (size_t i=0; i<curr_cnt; i++) {
+			iterator = (iterator + size - 1) % curr_cnt;
+			if (*(buf + iterator) == value) {
+				cnt++;
+			}
+		}
+		return cnt;
+	}
+
+	/**
+	 * Print the contents of the buffer
+	 */
+	void dump()
+	{
+		size_t iterator = begin;
+		for (size_t i=0; i<size; i++) {
+			iterator = (iterator + size - 1) % size;
+			if (typeid(T) == typeid(int)) {
+				 //DEBUG_INFO("buf[%2zu]=%2d", iterator, (int)*(buf + iterator));
+			}
+			else {
+				 //DEBUG_INFO("buf[%2zu]=%2f", iterator, (float)*(buf + iterator));
+			}
+		}
+	}
+};
+
+} // namespace ZeroTier
+
+#endif

+ 1 - 1
node/Salsa20.cpp

@@ -14,7 +14,7 @@
 #define XOR(v,w) ((v) ^ (w))
 #define PLUS(v,w) ((uint32_t)((v) + (w)))
 
-// Set up laod/store macros with appropriate endianness (we don't use these in SSE mode)
+// Set up load/store macros with appropriate endianness (we don't use these in SSE mode)
 #ifndef ZT_SALSA20_SSE
 
 #if __BYTE_ORDER == __LITTLE_ENDIAN

+ 1 - 1
node/SelfAwareness.cpp

@@ -145,7 +145,7 @@ std::vector<InetAddress> SelfAwareness::getSymmetricNatPredictions()
 	 *
 	 * Since flows are encrypted and authenticated they could not actually
 	 * read or modify traffic, but they could gather meta-data for forensics
-	 * purpsoes or use this as a DOS attack vector. */
+	 * purposes or use this as a DOS attack vector. */
 
 	std::map< uint32_t,unsigned int > maxPortByIp;
 	InetAddress theOneTrueSurface;

+ 273 - 10
node/Switch.cpp

@@ -269,6 +269,8 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 		}
 	}
 
+	uint8_t qosBucket = ZT_QOS_DEFAULT_BUCKET;
+
 	if (to.isMulticast()) {
 		MulticastGroup multicastGroup(to,0);
 
@@ -386,7 +388,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 			network->learnBridgedMulticastGroup(tPtr,multicastGroup,RR->node->now());
 
 		// First pass sets noTee to false, but noTee is set to true in OutboundMulticast to prevent duplicates.
-		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId,qosBucket)) {
 			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
 			return;
 		}
@@ -410,7 +412,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 		Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this
 		SharedPtr<Peer> toPeer(RR->topology->getPeer(tPtr,toZT));
 
-		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId,qosBucket)) {
 			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
 			return;
 		}
@@ -425,7 +427,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 			outp.append(data,len);
 			if (!network->config().disableCompression())
 				outp.compress();
-			send(tPtr,outp,true);
+			aqm_enqueue(tPtr,network,outp,true,qosBucket);
 		} else {
 			Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME);
 			outp.append(network->id());
@@ -433,7 +435,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 			outp.append(data,len);
 			if (!network->config().disableCompression())
 				outp.compress();
-			send(tPtr,outp,true);
+			aqm_enqueue(tPtr,network,outp,true,qosBucket);
 		}
 
 	} else {
@@ -442,7 +444,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 		// We filter with a NULL destination ZeroTier address first. Filtrations
 		// for each ZT destination are also done below. This is the same rationale
 		// and design as for multicast.
-		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId,qosBucket)) {
 			RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked");
 			return;
 		}
@@ -480,7 +482,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 		}
 
 		for(unsigned int b=0;b<numBridges;++b) {
-			if (network->filterOutgoingPacket(tPtr,true,RR->identity.address(),bridges[b],from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+			if (network->filterOutgoingPacket(tPtr,true,RR->identity.address(),bridges[b],from,to,(const uint8_t *)data,len,etherType,vlanId,qosBucket)) {
 				Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
 				outp.append(network->id());
 				outp.append((uint8_t)0x00);
@@ -490,7 +492,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 				outp.append(data,len);
 				if (!network->config().disableCompression())
 					outp.compress();
-				send(tPtr,outp,true);
+				aqm_enqueue(tPtr,network,outp,true,qosBucket);
 			} else {
 				RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked (bridge replication)");
 			}
@@ -498,6 +500,263 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const
 	}
 }
 
+void Switch::aqm_enqueue(void *tPtr, const SharedPtr<Network> &network, Packet &packet,bool encrypt,int qosBucket)
+{
+	if(!network->QoSEnabled()) {
+		send(tPtr, packet, encrypt);
+		return;
+	}
+	NetworkQoSControlBlock *nqcb = _netQueueControlBlock[network->id()];
+	if (!nqcb) {
+		// DEBUG_INFO("creating network QoS control block (NQCB) for network %llx", network->id());
+		nqcb = new NetworkQoSControlBlock();
+		_netQueueControlBlock[network->id()] = nqcb;
+		// Initialize ZT_QOS_NUM_BUCKETS queues and place them in the INACTIVE list
+		// These queues will be shuffled between the new/old/inactive lists by the enqueue/dequeue algorithm
+		for (int i=0; i<ZT_QOS_NUM_BUCKETS; i++) {
+			nqcb->inactiveQueues.push_back(new ManagedQueue(i));
+		}
+	}
+
+	if (packet.verb() != Packet::VERB_FRAME && packet.verb() != Packet::VERB_EXT_FRAME) {
+		// DEBUG_INFO("skipping, no QoS for this packet, verb=%x", packet.verb());
+		// just send packet normally, no QoS for ZT protocol traffic
+		send(tPtr, packet, encrypt);
+	} 
+
+	_aqm_m.lock();
+
+	// Enqueue packet and move queue to appropriate list
+
+	const Address dest(packet.destination());
+	TXQueueEntry *txEntry = new TXQueueEntry(dest,RR->node->now(),packet,encrypt);
+	
+	ManagedQueue *selectedQueue = nullptr;
+	for (int i=0; i<ZT_QOS_NUM_BUCKETS; i++) {
+		if (i < nqcb->oldQueues.size()) { // search old queues first (I think this is best since old would imply most recent usage of the queue)
+			if (nqcb->oldQueues[i]->id == qosBucket) {
+				selectedQueue = nqcb->oldQueues[i];
+			}
+		} if (i < nqcb->newQueues.size()) { // search new queues (this would imply not often-used queues)
+			if (nqcb->newQueues[i]->id == qosBucket) {
+				selectedQueue = nqcb->newQueues[i];
+			}
+		} if (i < nqcb->inactiveQueues.size()) { // search inactive queues
+			if (nqcb->inactiveQueues[i]->id == qosBucket) {
+				selectedQueue = nqcb->inactiveQueues[i];
+				// move queue to end of NEW queue list
+				selectedQueue->byteCredit = ZT_QOS_QUANTUM;
+				// DEBUG_INFO("moving q=%p from INACTIVE to NEW list", selectedQueue);
+				nqcb->newQueues.push_back(selectedQueue);
+				nqcb->inactiveQueues.erase(nqcb->inactiveQueues.begin() + i);
+			}
+		}
+	}
+	if (!selectedQueue) {
+		return;
+	}
+
+	selectedQueue->q.push_back(txEntry);
+	selectedQueue->byteLength+=txEntry->packet.payloadLength();
+	nqcb->_currEnqueuedPackets++;
+
+	// DEBUG_INFO("nq=%2lu, oq=%2lu, iq=%2lu, nqcb.size()=%3d, bucket=%2d, q=%p", nqcb->newQueues.size(), nqcb->oldQueues.size(), nqcb->inactiveQueues.size(), nqcb->_currEnqueuedPackets, qosBucket, selectedQueue);
+
+	// Drop a packet if necessary
+	ManagedQueue *selectedQueueToDropFrom = nullptr;
+	if (nqcb->_currEnqueuedPackets > ZT_QOS_MAX_ENQUEUED_PACKETS)
+	{
+		// DEBUG_INFO("too many enqueued packets (%d), finding packet to drop", nqcb->_currEnqueuedPackets);
+		int maxQueueLength = 0;
+		for (int i=0; i<ZT_QOS_NUM_BUCKETS; i++) {
+			if (i < nqcb->oldQueues.size()) {
+				if (nqcb->oldQueues[i]->byteLength > maxQueueLength) {
+					maxQueueLength = nqcb->oldQueues[i]->byteLength;
+					selectedQueueToDropFrom = nqcb->oldQueues[i];
+				}
+			} if (i < nqcb->newQueues.size()) {
+				if (nqcb->newQueues[i]->byteLength > maxQueueLength) {
+					maxQueueLength = nqcb->newQueues[i]->byteLength;
+					selectedQueueToDropFrom = nqcb->newQueues[i];
+				}
+			} if (i < nqcb->inactiveQueues.size()) {
+				if (nqcb->inactiveQueues[i]->byteLength > maxQueueLength) {
+					maxQueueLength = nqcb->inactiveQueues[i]->byteLength;
+					selectedQueueToDropFrom = nqcb->inactiveQueues[i];
+				}
+			}
+		}
+		if (selectedQueueToDropFrom) {
+			// DEBUG_INFO("dropping packet from head of largest queue (%d payload bytes)", maxQueueLength);
+			int sizeOfDroppedPacket = selectedQueueToDropFrom->q.front()->packet.payloadLength();
+			delete selectedQueueToDropFrom->q.front();
+			selectedQueueToDropFrom->q.pop_front();
+			selectedQueueToDropFrom->byteLength-=sizeOfDroppedPacket;
+			nqcb->_currEnqueuedPackets--;
+		}
+	}
+	_aqm_m.unlock();
+	aqm_dequeue(tPtr);
+}
+
+uint64_t Switch::control_law(uint64_t t, int count)
+{
+	return t + ZT_QOS_INTERVAL / sqrt(count);
+}
+
+Switch::dqr Switch::dodequeue(ManagedQueue *q, uint64_t now) 
+{
+	dqr r;
+	r.ok_to_drop = false;
+	r.p = q->q.front();
+
+	if (r.p == NULL) {
+		q->first_above_time = 0;
+		return r;
+	}
+	uint64_t sojourn_time = now - r.p->creationTime;
+	if (sojourn_time < ZT_QOS_TARGET || q->byteLength <= ZT_DEFAULT_MTU) {
+		// went below - stay below for at least interval
+		q->first_above_time = 0;
+	} else {
+		if (q->first_above_time == 0) {
+			// just went above from below. if still above at
+			// first_above_time, will say it's ok to drop.
+			q->first_above_time = now + ZT_QOS_INTERVAL;
+		} else if (now >= q->first_above_time) {
+			r.ok_to_drop = true;
+		}
+	}
+	return r;
+}
+
+Switch::TXQueueEntry * Switch::CoDelDequeue(ManagedQueue *q, bool isNew, uint64_t now)
+{
+	dqr r = dodequeue(q, now);
+
+	if (q->dropping) {
+		if (!r.ok_to_drop) {
+			q->dropping = false;
+		}
+		while (now >= q->drop_next && q->dropping) {
+			q->q.pop_front(); // drop
+			r = dodequeue(q, now);
+			if (!r.ok_to_drop) {
+				// leave dropping state
+				q->dropping = false;
+			} else {
+				++(q->count);
+				// schedule the next drop.
+				q->drop_next = control_law(q->drop_next, q->count);
+			}
+		}
+	} else if (r.ok_to_drop) {
+		q->q.pop_front(); // drop
+		r = dodequeue(q, now);
+		q->dropping = true;
+		q->count = (q->count > 2 && now - q->drop_next < 8*ZT_QOS_INTERVAL)?
+		q->count - 2 : 1;
+		q->drop_next = control_law(now, q->count);
+	}
+	return r.p;
+}
+
+void Switch::aqm_dequeue(void *tPtr)
+{
+	// Cycle through network-specific QoS control blocks
+	for(std::map<uint64_t,NetworkQoSControlBlock*>::iterator nqcb(_netQueueControlBlock.begin());nqcb!=_netQueueControlBlock.end();) {
+		if (!(*nqcb).second->_currEnqueuedPackets) {
+			return;
+		}
+
+		uint64_t now = RR->node->now();
+		TXQueueEntry *entryToEmit = nullptr;
+		std::vector<ManagedQueue*> *currQueues = &((*nqcb).second->newQueues);
+		std::vector<ManagedQueue*> *oldQueues = &((*nqcb).second->oldQueues);
+		std::vector<ManagedQueue*> *inactiveQueues = &((*nqcb).second->inactiveQueues);
+
+		_aqm_m.lock();
+
+		// Attempt dequeue from queues in NEW list
+		bool examiningNewQueues = true;
+		while (currQueues->size()) {
+			ManagedQueue *queueAtFrontOfList = currQueues->front();
+			if (queueAtFrontOfList->byteCredit < 0) {
+				queueAtFrontOfList->byteCredit += ZT_QOS_QUANTUM;
+				// Move to list of OLD queues
+				// DEBUG_INFO("moving q=%p from NEW to OLD list", queueAtFrontOfList);
+				oldQueues->push_back(queueAtFrontOfList);
+				currQueues->erase(currQueues->begin());
+			} else {
+				entryToEmit = CoDelDequeue(queueAtFrontOfList, examiningNewQueues, now);
+				if (!entryToEmit) {
+					// Move to end of list of OLD queues
+					// DEBUG_INFO("moving q=%p from NEW to OLD list", queueAtFrontOfList);
+					oldQueues->push_back(queueAtFrontOfList);
+					currQueues->erase(currQueues->begin());
+				}
+				else {
+					int len = entryToEmit->packet.payloadLength();
+					queueAtFrontOfList->byteLength -= len;
+					queueAtFrontOfList->byteCredit -= len;
+					// Send the packet!
+					queueAtFrontOfList->q.pop_front();
+					send(tPtr, entryToEmit->packet, entryToEmit->encrypt);
+					(*nqcb).second->_currEnqueuedPackets--;
+				}
+				if (queueAtFrontOfList) {
+					//DEBUG_INFO("dequeuing from q=%p, len=%lu in NEW list (byteCredit=%d)", queueAtFrontOfList, queueAtFrontOfList->q.size(), queueAtFrontOfList->byteCredit);
+				}
+				break;
+			}
+		}
+
+		// Attempt dequeue from queues in OLD list
+		examiningNewQueues = false;
+		currQueues = &((*nqcb).second->oldQueues);
+		while (currQueues->size()) {
+			ManagedQueue *queueAtFrontOfList = currQueues->front();
+			if (queueAtFrontOfList->byteCredit < 0) {
+				queueAtFrontOfList->byteCredit += ZT_QOS_QUANTUM;
+				oldQueues->push_back(queueAtFrontOfList);
+				currQueues->erase(currQueues->begin());
+			} else {
+				entryToEmit = CoDelDequeue(queueAtFrontOfList, examiningNewQueues, now);
+				if (!entryToEmit) {
+					//DEBUG_INFO("moving q=%p from OLD to INACTIVE list", queueAtFrontOfList);
+					// Move to inactive list of queues
+					inactiveQueues->push_back(queueAtFrontOfList);
+					currQueues->erase(currQueues->begin());
+				}
+				else {
+					int len = entryToEmit->packet.payloadLength();
+					queueAtFrontOfList->byteLength -= len;
+					queueAtFrontOfList->byteCredit -= len;
+					queueAtFrontOfList->q.pop_front();
+					send(tPtr, entryToEmit->packet, entryToEmit->encrypt);
+					(*nqcb).second->_currEnqueuedPackets--;
+				}
+				if (queueAtFrontOfList) {
+					//DEBUG_INFO("dequeuing from q=%p, len=%lu in OLD list (byteCredit=%d)", queueAtFrontOfList, queueAtFrontOfList->q.size(), queueAtFrontOfList->byteCredit);
+				}
+				break;
+			}
+		}
+		nqcb++;
+		_aqm_m.unlock();
+	}
+}
+
+void Switch::removeNetworkQoSControlBlock(uint64_t nwid)
+{
+	NetworkQoSControlBlock *nq = _netQueueControlBlock[nwid];
+	if (nq) {
+		_netQueueControlBlock.erase(nwid);
+		delete nq;
+		nq = NULL;
+	}
+}
+
 void Switch::send(void *tPtr,Packet &packet,bool encrypt)
 {
 	const Address dest(packet.destination());
@@ -557,6 +816,7 @@ void Switch::doAnythingWaitingForPeer(void *tPtr,const SharedPtr<Peer> &peer)
 
 	{
 		Mutex::Lock _l(_txQueue_m);
+
 		for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
 			if (txi->dest == peer->address()) {
 				if (_trySend(tPtr,txi->packet,txi->encrypt)) {
@@ -581,6 +841,7 @@ unsigned long Switch::doTimerTasks(void *tPtr,int64_t now)
 	std::vector<Address> needWhois;
 	{
 		Mutex::Lock _l(_txQueue_m);
+
 		for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
 			if (_trySend(tPtr,txi->packet,txi->encrypt)) {
 				_txQueue.erase(txi++);
@@ -654,12 +915,12 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt)
 
 	const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,destination));
 	if (peer) {
-		viaPath = peer->getBestPath(now,false);
+		viaPath = peer->getAppropriatePath(now,false);
 		if (!viaPath) {
 			peer->tryMemorizedPath(tPtr,now); // periodically attempt memorized or statically defined paths, if any are known
 			const SharedPtr<Peer> relay(RR->topology->getUpstreamPeer());
-			if ( (!relay) || (!(viaPath = relay->getBestPath(now,false))) ) {
-				if (!(viaPath = peer->getBestPath(now,true)))
+			if ( (!relay) || (!(viaPath = relay->getAppropriatePath(now,false))) ) {
+				if (!(viaPath = peer->getAppropriatePath(now,true)))
 					return false;
 			}
 		}
@@ -674,6 +935,8 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt)
 	unsigned int chunkSize = std::min(packet.size(),mtu);
 	packet.setFragmented(chunkSize < packet.size());
 
+	peer->recordOutgoingPacket(viaPath, packet.packetId(), packet.payloadLength(), packet.verb(), now);
+
 	if (trustedPathId) {
 		packet.setTrusted(trustedPathId);
 	} else {

+ 94 - 0
node/Switch.hpp

@@ -59,6 +59,14 @@ class Peer;
  */
 class Switch
 {
+	struct ManagedQueue;
+	struct TXQueueEntry;
+
+	typedef struct {
+		TXQueueEntry *p;
+		bool ok_to_drop;
+	} dqr;
+
 public:
 	Switch(const RuntimeEnvironment *renv);
 
@@ -87,6 +95,62 @@ public:
 	 */
 	void onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
 
+	/**
+	 * Determines the next drop schedule for packets in the TX queue
+	 *
+	 * @param t Current time
+	 * @param count Number of packets dropped this round
+	 */
+	uint64_t control_law(uint64_t t, int count);
+
+	/**
+	 * Selects a packet eligible for transmission from a TX queue. According to the control law, multiple packets
+	 * may be intentionally dropped before a packet is returned to the AQM scheduler.
+	 *
+	 * @param q The TX queue that is being dequeued from
+	 * @param now Current time
+	 */
+	dqr dodequeue(ManagedQueue *q, uint64_t now);
+
+	/**
+	 * Presents a packet to the AQM scheduler.
+	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+	 * @param network Network that the packet shall be sent over
+	 * @param packet Packet to be sent
+	 * @param encrypt Encrypt packet payload? (always true except for HELLO)
+	 * @param qosBucket Which bucket the rule-system determined this packet should fall into
+	 */
+	void aqm_enqueue(void *tPtr, const SharedPtr<Network> &network, Packet &packet,bool encrypt,int qosBucket);
+
+	/**
+	 * Performs a single AQM cycle and dequeues and transmits all eligible packets on all networks
+	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+	 */
+	void aqm_dequeue(void *tPtr);
+
+	/**
+	 * Calls the dequeue mechanism and adjust queue state variables
+	 *
+	 * @param q The TX queue that is being dequeued from
+	 * @param isNew Whether or not this queue is in the NEW list
+	 * @param now Current time
+	 */
+	Switch::TXQueueEntry * CoDelDequeue(ManagedQueue *q, bool isNew, uint64_t now);
+
+	/**
+	 * Removes QoS Queues and flow state variables for a specific network. These queues are created
+	 * automatically upon the transmission of the first packet from this peer to another peer on the
+	 * given network.
+	 *
+	 * The reason for existence of queues and flow state variables specific to each network is so that
+	 * each network's QoS rules function independently.
+	 *
+	 * @param nwid Network ID
+	 */
+	void removeNetworkQoSControlBlock(uint64_t nwid);
+
 	/**
 	 * Send a packet to a ZeroTier address (destination in packet)
 	 *
@@ -200,6 +264,7 @@ private:
 	};
 	std::list< TXQueueEntry > _txQueue;
 	Mutex _txQueue_m;
+	Mutex _aqm_m;
 
 	// Tracks sending of VERB_RENDEZVOUS to relaying peers
 	struct _LastUniteKey
@@ -221,6 +286,35 @@ private:
 	};
 	Hashtable< _LastUniteKey,uint64_t > _lastUniteAttempt; // key is always sorted in ascending order, for set-like behavior
 	Mutex _lastUniteAttempt_m;
+
+	// Queue with additional flow state variables
+	struct ManagedQueue
+	{
+		ManagedQueue(int id) :
+			id(id),
+			byteCredit(ZT_QOS_QUANTUM),
+			byteLength(0),
+			dropping(false)
+		{}
+		int id;
+		int byteCredit;
+		int byteLength;
+		uint64_t first_above_time;
+		uint32_t count;
+		uint64_t drop_next;
+		bool dropping;
+		uint64_t drop_next_time;
+		std::list< TXQueueEntry *> q;
+	};
+	// To implement fq_codel we need to maintain a queue of queues
+	struct NetworkQoSControlBlock
+	{
+		int _currEnqueuedPackets;
+		std::vector<ManagedQueue *> newQueues;
+		std::vector<ManagedQueue *> oldQueues;
+		std::vector<ManagedQueue *> inactiveQueues;
+	};
+	std::map<uint64_t,NetworkQoSControlBlock*> _netQueueControlBlock;
 };
 
 } // namespace ZeroTier

+ 1 - 1
node/Tag.hpp

@@ -58,7 +58,7 @@ class RuntimeEnvironment;
  * values.
  *
  * Unlike capabilities tags are signed only by the issuer and are never
- * transferrable.
+ * transferable.
  */
 class Tag : public Credential
 {

+ 1 - 1
node/Topology.cpp

@@ -138,7 +138,7 @@ SharedPtr<Peer> Topology::getPeer(void *tPtr,const Address &zta)
 			}
 			return SharedPtr<Peer>();
 		}
-	} catch ( ... ) {} // ignore invalid identities or other strage failures
+	} catch ( ... ) {} // ignore invalid identities or other strange failures
 
 	return SharedPtr<Peer>();
 }

+ 1 - 1
node/Topology.hpp

@@ -299,7 +299,7 @@ public:
 		Address *a = (Address *)0;
 		SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
 		while (i.next(a,p)) {
-			const SharedPtr<Path> pp((*p)->getBestPath(now,false));
+			const SharedPtr<Path> pp((*p)->getAppropriatePath(now,false));
 			if (pp)
 				++cnt;
 		}

+ 21 - 0
node/Trace.cpp

@@ -40,6 +40,7 @@
 #include "Tag.hpp"
 #include "Capability.hpp"
 #include "Revocation.hpp"
+#include "../include/ZeroTierDebug.h"
 
 namespace ZeroTier {
 
@@ -106,6 +107,26 @@ void Trace::peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,
 	}
 }
 
+void Trace::peerLinkNowRedundant(void *const tPtr,Peer &peer)
+{
+	ZT_LOCAL_TRACE(tPtr,RR,"link to peer %.10llx is fully redundant",peer.address().toInt());
+}
+
+void Trace::peerLinkNoLongerRedundant(void *const tPtr,Peer &peer)
+{
+	ZT_LOCAL_TRACE(tPtr,RR,"link to peer %.10llx is no longer redundant",peer.address().toInt());
+}
+
+void Trace::peerLinkAggregateStatistics(void *const tPtr,Peer &peer)
+{
+	ZT_LOCAL_TRACE(tPtr,RR,"link to peer %.10llx is composed of (%d) physical paths %s, has packet delay variance (%.0f ms), mean latency (%.0f ms)",
+		peer.address().toInt(),
+		peer.aggregateLinkPhysicalPathCount(),
+		peer.interfaceListStr(),
+		peer.computeAggregateLinkPacketDelayVariance(),
+		peer.computeAggregateLinkMeanLatency());
+}
+
 void Trace::peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath,const uint64_t packetId)
 {
 	char tmp[128];

+ 6 - 0
node/Trace.hpp

@@ -121,6 +121,12 @@ public:
 	void resettingPathsInScope(void *const tPtr,const Address &reporter,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,const InetAddress::IpScope scope);
 
 	void peerConfirmingUnknownPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &path,const uint64_t packetId,const Packet::Verb verb);
+
+	void peerLinkNowRedundant(void *const tPtr,Peer &peer);
+	void peerLinkNoLongerRedundant(void *const tPtr,Peer &peer);
+
+	void peerLinkAggregateStatistics(void *const tPtr,Peer &peer);
+
 	void peerLearnedNewPath(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath,const uint64_t packetId);
 	void peerRedirected(void *const tPtr,const uint64_t networkId,Peer &peer,const SharedPtr<Path> &newPath);
 

+ 22 - 2
node/Utils.hpp

@@ -38,6 +38,10 @@
 #include <vector>
 #include <map>
 
+#if defined(__FreeBSD__)
+#include <sys/endian.h>
+#endif
+
 #include "Constants.hpp"
 
 #ifdef __LINUX__
@@ -261,6 +265,14 @@ public:
 		return l;
 	}
 
+	static inline float normalize(float value, int64_t bigMin, int64_t bigMax, int32_t targetMin, int32_t targetMax)
+	{
+	    int64_t bigSpan = bigMax - bigMin;
+	    int64_t smallSpan = targetMax - targetMin;
+	    float valueScaled = (value - (float)bigMin) / (float)bigSpan;
+	    return (float)targetMin + valueScaled * (float)smallSpan;
+	}
+
 	/**
 	 * Generate secure random bytes
 	 *
@@ -411,8 +423,12 @@ public:
 	static inline uint64_t hton(uint64_t n)
 	{
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-#if defined(__GNUC__) && (!defined(__OpenBSD__))
+#if defined(__GNUC__)
+#if defined(__FreeBSD__)
+		return bswap64(n);
+#elif (!defined(__OpenBSD__))
 		return __builtin_bswap64(n);
+#endif
 #else
 		return (
 			((n & 0x00000000000000FFULL) << 56) |
@@ -440,8 +456,12 @@ public:
 	static inline uint64_t ntoh(uint64_t n)
 	{
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-#if defined(__GNUC__) && !defined(__OpenBSD__)
+#if defined(__GNUC__)
+#if defined(__FreeBSD__)
+		return bswap64(n);
+#elif (!defined(__OpenBSD__))
 		return __builtin_bswap64(n);
+#endif
 #else
 		return (
 			((n & 0x00000000000000FFULL) << 56) |

+ 1 - 0
osdep/Binder.hpp

@@ -389,6 +389,7 @@ public:
 						_bindings[_bindingCount].udpSock = udps;
 						_bindings[_bindingCount].tcpListenSock = tcps;
 						_bindings[_bindingCount].address = ii->first;
+						phy.setIfName(udps, (char*)ii->second.c_str(), ii->second.length());
 						++_bindingCount;
 					}
 				} else {

+ 33 - 71
osdep/LinuxEthernetTap.cpp

@@ -56,6 +56,7 @@
 #include "../node/Dictionary.hpp"
 #include "OSUtils.hpp"
 #include "LinuxEthernetTap.hpp"
+#include "LinuxNetLink.hpp"
 
 // ff:ff:ff:ff:ff:ff with no ADI
 static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
@@ -97,6 +98,9 @@ LinuxEthernetTap::LinuxEthernetTap(
 	char procpath[128],nwids[32];
 	struct stat sbuf;
 
+	// ensure netlink connection is started
+	(void)LinuxNetLink::getInstance();
+	
 	OSUtils::ztsnprintf(nwids,sizeof(nwids),"%.16llx",nwid);
 
 	Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally
@@ -263,18 +267,8 @@ bool LinuxEthernetTap::enabled() const
 
 static bool ___removeIp(const std::string &_dev,const InetAddress &ip)
 {
-	long cpid = (long)vfork();
-	if (cpid == 0) {
-		OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
-		setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
-		char iptmp[128];
-		::execlp("ip","ip","addr","del",ip.toString(iptmp),"dev",_dev.c_str(),(const char *)0);
-		::_exit(-1);
-	} else {
-		int exitcode = -1;
-		::waitpid(cpid,&exitcode,0);
-		return (exitcode == 0);
-	}
+	LinuxNetLink::getInstance().removeAddress(ip, _dev.c_str());
+	return true;
 }
 
 #ifdef __SYNOLOGY__
@@ -285,49 +279,32 @@ bool LinuxEthernetTap::addIpSyn(std::vector<InetAddress> ips)
 	std::string cfg_contents = "DEVICE="+_dev+"\nBOOTPROTO=static";
 	int ip4=0,ip6=0,ip4_tot=0,ip6_tot=0;
 
-	long cpid = (long)vfork();
-	if (cpid == 0) {
-		OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
-		setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
-		// We must know if there is at least (one) of each protocol version so we
-		// can properly enumerate address/netmask combinations in the ifcfg-dev file
-		for(int i=0; i<(int)ips.size(); i++) {
-			if (ips[i].isV4())
-				ip4_tot++;
-			else
-				ip6_tot++;
-		}
-		// Assemble and write contents of ifcfg-dev file
-		for(int i=0; i<(int)ips.size(); i++) {
-			if (ips[i].isV4()) {
-				char iptmp[64],iptmp2[64];
-				std::string numstr4 = ip4_tot > 1 ? std::to_string(ip4) : "";
-				cfg_contents += "\nIPADDR"+numstr4+"="+ips[i].toIpString(iptmp)
-					+ "\nNETMASK"+numstr4+"="+ips[i].netmask().toIpString(iptmp2)+"\n";
-				ip4++;
-			}
-			else {
-				char iptmp[64],iptmp2[64];
-				std::string numstr6 = ip6_tot > 1 ? std::to_string(ip6) : "";
-				cfg_contents += "\nIPV6ADDR"+numstr6+"="+ips[i].toIpString(iptmp)
-					+ "\nNETMASK"+numstr6+"="+ips[i].netmask().toIpString(iptmp2)+"\n";
-				ip6++;
-			}
-		}
-		OSUtils::writeFile(filepath.c_str(), cfg_contents.c_str(), cfg_contents.length());
-		// Finaly, add IPs
-		for(int i=0; i<(int)ips.size(); i++){
-			char iptmp[128],iptmp2[128];
-			if (ips[i].isV4())
-				::execlp("ip","ip","addr","add",ips[i].toString(iptmp),"broadcast",ips[i].broadcast().toIpString(iptmp2),"dev",_dev.c_str(),(const char *)0);
-			else
-				::execlp("ip","ip","addr","add",ips[i].toString(iptmp),"dev",_dev.c_str(),(const char *)0);
+	for(int i=0; i<(int)ips.size(); i++) {
+		if (ips[i].isV4())
+			ip4_tot++;
+		else
+			ip6_tot++;
+	}
+	// Assemble and write contents of ifcfg-dev file
+	for(int i=0; i<(int)ips.size(); i++) {
+		if (ips[i].isV4()) {
+			char iptmp[64],iptmp2[64];
+			std::string numstr4 = ip4_tot > 1 ? std::to_string(ip4) : "";
+			cfg_contents += "\nIPADDR"+numstr4+"="+ips[i].toIpString(iptmp)
+				+ "\nNETMASK"+numstr4+"="+ips[i].netmask().toIpString(iptmp2)+"\n";
+			ip4++;
+		} else {
+			char iptmp[64],iptmp2[64];
+			std::string numstr6 = ip6_tot > 1 ? std::to_string(ip6) : "";
+			cfg_contents += "\nIPV6ADDR"+numstr6+"="+ips[i].toIpString(iptmp)
+				+ "\nNETMASK"+numstr6+"="+ips[i].netmask().toIpString(iptmp2)+"\n";
+			ip6++;
 		}
-		::_exit(-1);
-	} else if (cpid > 0) {
-		int exitcode = -1;
-		::waitpid(cpid,&exitcode,0);
-		return (exitcode == 0);
+	}
+	OSUtils::writeFile(filepath.c_str(), cfg_contents.c_str(), cfg_contents.length());
+	// Finaly, add IPs
+	for(int i=0; i<(int)ips.size(); i++){
+		LinuxNetLink::getInstance().addAddress(ips[i], _dev.c_str());
 	}
 	return true;
 }
@@ -348,24 +325,9 @@ bool LinuxEthernetTap::addIp(const InetAddress &ip)
 			___removeIp(_dev,*i);
 	}
 
-	long cpid = (long)vfork();
-	if (cpid == 0) {
-		OSUtils::redirectUnixOutputs("/dev/null",(const char *)0);
-		setenv("PATH", "/sbin:/bin:/usr/sbin:/usr/bin", 1);
-		char iptmp[128],iptmp2[128];
-		if (ip.isV4()) {
-			::execlp("ip","ip","addr","add",ip.toString(iptmp),"broadcast",ip.broadcast().toIpString(iptmp2),"dev",_dev.c_str(),(const char *)0);
-		} else {
-			::execlp("ip","ip","addr","add",ip.toString(iptmp),"dev",_dev.c_str(),(const char *)0);
-		}
-		::_exit(-1);
-	} else if (cpid > 0) {
-		int exitcode = -1;
-		::waitpid(cpid,&exitcode,0);
-		return (exitcode == 0);
-	}
+	LinuxNetLink::getInstance().addAddress(ip, _dev.c_str());
 
-	return false;
+	return true;
 }
 
 bool LinuxEthernetTap::removeIp(const InetAddress &ip)

+ 1080 - 0
osdep/LinuxNetLink.cpp

@@ -0,0 +1,1080 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018  ZeroTier, Inc.  https://www.zerotier.com/
+ *
+ * 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/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#include "LinuxNetLink.hpp"
+
+#include <unistd.h>
+#include <linux/if_tun.h>
+
+
+namespace ZeroTier {
+
+struct nl_route_req {
+    struct nlmsghdr nl;
+    struct rtmsg rt;
+    char buf[8192];
+};
+
+struct nl_if_req {
+    struct nlmsghdr nl;
+    struct ifinfomsg ifa;
+    char buf[8192];
+};
+
+struct nl_adr_req {
+	struct nlmsghdr nl;
+	struct ifaddrmsg ifa;
+	char buf[8192];
+};
+
+LinuxNetLink::LinuxNetLink()
+	: _t()
+	, _running(false)
+	, _routes_ipv4()
+	, _rv4_m()
+	, _routes_ipv6()
+	, _rv6_m()
+	, _seq(0)
+	, _interfaces()
+	, _if_m()
+	, _fd(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE))
+	, _la({0})
+{
+
+	// set socket timeout to 1 sec so we're not permablocking recv() calls
+	_setSocketTimeout(_fd, 1);
+
+	_la.nl_family = AF_NETLINK;
+	_la.nl_pid = getpid()+1;
+	_la.nl_groups = RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR|RTMGRP_IPV4_ROUTE|RTMGRP_IPV6_ROUTE|RTMGRP_NOTIFY;
+	if (bind(_fd, (struct sockaddr*)&_la, sizeof(_la))) {
+		fprintf(stderr, "Error connecting to RTNETLINK: %s\n", strerror(errno));
+		::exit(1);
+	}
+
+	fprintf(stderr, "Requesting IPV4 Routes\n");
+	_requestIPv4Routes();
+	fprintf(stderr, "Requesting IPV6 Routes\n");
+	_requestIPv6Routes();
+	fprintf(stderr, "Requesting Interface List\n");
+	_requestInterfaceList();
+
+	_running = true;
+	_t = Thread::start(this);
+}
+
+LinuxNetLink::~LinuxNetLink()
+{
+	_running = false;
+	Thread::join(_t);
+
+	::close(_fd);
+}
+
+void LinuxNetLink::_setSocketTimeout(int fd, int seconds) 
+{
+	struct timeval tv;
+	tv.tv_sec = seconds;
+	tv.tv_usec = 0;
+	if(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)) != 0) {
+#ifdef ZT_TRACE
+		fprintf(stderr, "setsockopt failed: %s\n", strerror(errno));
+#endif
+	}
+}
+
+int LinuxNetLink::_doRecv(int fd)
+{
+	char buf[8192];
+	char *p = NULL;
+	struct nlmsghdr *nlp;
+	int nll = 0;
+	int rtn = 0;
+	p = buf;
+
+	while(true) {
+		rtn = recv(fd, p, sizeof(buf) - nll, 0);
+
+		if (rtn > 0) {
+			nlp = (struct nlmsghdr *)p;
+
+			if(nlp->nlmsg_type == NLMSG_ERROR && (nlp->nlmsg_flags & NLM_F_ACK) != NLM_F_ACK) {
+				struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(nlp);
+				if (err->error != 0) {
+#ifdef ZT_TRACE
+					fprintf(stderr, "rtnetlink error: %s\n", strerror(-(err->error)));
+#endif
+				}
+				p = buf;
+				nll = 0;
+				break;
+			}
+
+			if (nlp->nlmsg_type == NLMSG_NOOP) {
+				break;
+			}
+
+			if( (nlp->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI || (nlp->nlmsg_type == NLMSG_DONE))
+			{
+				if (nlp->nlmsg_type == NLMSG_DONE) {
+					_processMessage(nlp, nll);
+					p = buf;
+					nll = 0;
+					break;
+				}
+				p += rtn;
+				nll += rtn;
+			}
+
+			if (nlp->nlmsg_type == NLMSG_OVERRUN) {
+#ifdef ZT_TRACE
+				fprintf(stderr, "NLMSG_OVERRUN: Data lost\n");
+#endif
+				p = buf;
+				nll = 0;
+				break;
+			}
+			
+			nll += rtn;
+
+			_processMessage(nlp, nll);
+			p = buf;
+			nll = 0;
+			break;
+		} else {
+			break;
+		}
+	}
+	return rtn;
+}
+
+void LinuxNetLink::threadMain() throw()
+{
+	int rtn = 0;
+
+	while(_running) {
+		rtn = _doRecv(_fd);
+		if (rtn <= 0) {
+			Thread::sleep(100);
+			continue;
+		}
+	}
+}
+
+void LinuxNetLink::_processMessage(struct nlmsghdr *nlp, int nll)
+{
+	for(; NLMSG_OK(nlp, nll); nlp=NLMSG_NEXT(nlp, nll))
+	{
+		switch(nlp->nlmsg_type) 
+		{
+		case RTM_NEWLINK:
+			_linkAdded(nlp);
+			break;
+		case RTM_DELLINK:
+			_linkDeleted(nlp);
+			break;
+		case RTM_NEWADDR:
+			_ipAddressAdded(nlp);
+			break;
+		case RTM_DELADDR:
+			_ipAddressDeleted(nlp);
+			break;
+		case RTM_NEWROUTE:
+			_routeAdded(nlp);
+			break;
+		case RTM_DELROUTE:
+			_routeDeleted(nlp);
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+void LinuxNetLink::_ipAddressAdded(struct nlmsghdr *nlp)
+{
+	struct ifaddrmsg *ifap = (struct ifaddrmsg *)NLMSG_DATA(nlp);
+	struct rtattr *rtap = (struct rtattr *)IFA_RTA(ifap);
+	int ifal = IFA_PAYLOAD(nlp);
+
+	char addr[40] = {0};
+	char local[40] = {0};
+	char label[40] = {0};
+	char bcast[40] = {0};
+	
+	for(;RTA_OK(rtap, ifal); rtap=RTA_NEXT(rtap,ifal))
+	{
+		switch(rtap->rta_type) {
+		case IFA_ADDRESS:
+			inet_ntop(ifap->ifa_family, RTA_DATA(rtap), addr, 40);
+			break;
+		case IFA_LOCAL:
+			inet_ntop(ifap->ifa_family, RTA_DATA(rtap), local, 40);
+			break;
+		case IFA_LABEL:
+			memcpy(label, RTA_DATA(rtap), 40);
+			break;
+		case IFA_BROADCAST:
+			inet_ntop(ifap->ifa_family, RTA_DATA(rtap), bcast, 40);
+			break;
+		}
+	}
+#ifdef ZT_TRACE
+	fprintf(stderr,"Added IP Address %s local: %s label: %s broadcast: %s\n", addr, local, label, bcast);
+#endif
+}
+
+void LinuxNetLink::_ipAddressDeleted(struct nlmsghdr *nlp)
+{
+	struct ifaddrmsg *ifap = (struct ifaddrmsg *)NLMSG_DATA(nlp);
+	struct rtattr *rtap = (struct rtattr *)IFA_RTA(ifap);
+	int ifal = IFA_PAYLOAD(nlp);
+
+	char addr[40] = {0};
+	char local[40] = {0};
+	char label[40] = {0};
+	char bcast[40] = {0};
+	
+	for(;RTA_OK(rtap, ifal); rtap=RTA_NEXT(rtap,ifal))
+	{
+		switch(rtap->rta_type) {
+		case IFA_ADDRESS:
+			inet_ntop(ifap->ifa_family, RTA_DATA(rtap), addr, 40);
+			break;
+		case IFA_LOCAL:
+			inet_ntop(ifap->ifa_family, RTA_DATA(rtap), local, 40);
+			break;
+		case IFA_LABEL:
+			memcpy(label, RTA_DATA(rtap), 40);
+			break;
+		case IFA_BROADCAST:
+			inet_ntop(ifap->ifa_family, RTA_DATA(rtap), bcast, 40);
+			break;
+		}
+	}
+#ifdef ZT_TRACE
+	fprintf(stderr, "Removed IP Address %s local: %s label: %s broadcast: %s\n", addr, local, label, bcast);
+#endif
+}
+
+void LinuxNetLink::_routeAdded(struct nlmsghdr *nlp)
+{
+	char dsts[40] = {0};
+	char gws[40] = {0};
+	char srcs[40] = {0};
+	char ifs[16] = {0};
+	char ms[24] = {0};
+
+	struct rtmsg *rtp = (struct rtmsg *) NLMSG_DATA(nlp);
+	struct rtattr *rtap = (struct rtattr *)RTM_RTA(rtp);
+	int rtl = RTM_PAYLOAD(nlp);
+
+	for(;RTA_OK(rtap, rtl); rtap=RTA_NEXT(rtap, rtl))
+	{
+		switch(rtap->rta_type)
+		{
+		case RTA_DST:
+			inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, rtp->rtm_family == AF_INET ? 24 : 40);
+			break;
+		case RTA_SRC:
+			inet_ntop(rtp->rtm_family, RTA_DATA(rtap), srcs, rtp->rtm_family == AF_INET ? 24: 40);
+			break;
+		case RTA_GATEWAY:
+			inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, rtp->rtm_family == AF_INET ? 24 : 40);
+			break;
+		case RTA_OIF:
+			sprintf(ifs, "%d", *((int*)RTA_DATA(rtap)));
+			break;
+		}
+	}
+	sprintf(ms, "%d", rtp->rtm_dst_len);
+#ifdef ZT_TRACE
+	fprintf(stderr, "Route Added: dst %s/%s gw %s src %s if %s\n", dsts, ms, gws, srcs, ifs);
+#endif
+}
+
+void LinuxNetLink::_routeDeleted(struct nlmsghdr *nlp)
+{
+	char dsts[40] = {0};
+	char gws[40] = {0};
+	char srcs[40] = {0};
+	char ifs[16] = {0};
+	char ms[24] = {0};
+
+	struct rtmsg *rtp = (struct rtmsg *) NLMSG_DATA(nlp);
+	struct rtattr *rtap = (struct rtattr *)RTM_RTA(rtp);
+	int rtl = RTM_PAYLOAD(nlp);
+
+	for(;RTA_OK(rtap, rtl); rtap=RTA_NEXT(rtap, rtl))
+	{
+		switch(rtap->rta_type)
+		{
+		case RTA_DST:
+			inet_ntop(rtp->rtm_family, RTA_DATA(rtap), dsts, rtp->rtm_family == AF_INET ? 24 : 40);
+			break;
+		case RTA_SRC:
+			inet_ntop(rtp->rtm_family, RTA_DATA(rtap), srcs, rtp->rtm_family == AF_INET ? 24 : 40);
+			break;
+		case RTA_GATEWAY:
+			inet_ntop(rtp->rtm_family, RTA_DATA(rtap), gws, rtp->rtm_family == AF_INET ? 24 : 40);
+			break;
+		case RTA_OIF:
+			sprintf(ifs, "%d", *((int*)RTA_DATA(rtap)));
+			break;
+		}
+	}
+	sprintf(ms, "%d", rtp->rtm_dst_len);
+
+#ifdef ZT_TRACE
+	fprintf(stderr, "Route Deleted: dst %s/%s gw %s src %s if %s\n", dsts, ms, gws, srcs, ifs);
+#endif
+}
+
+void LinuxNetLink::_linkAdded(struct nlmsghdr *nlp)
+{
+	char mac[18] = {0};
+	char mac_bin[6] = {0};
+	unsigned int mtu = 0;
+	char ifname[IFNAMSIZ] = {0};
+
+	struct ifinfomsg *ifip = (struct ifinfomsg *)NLMSG_DATA(nlp);
+	struct rtattr *rtap = (struct rtattr *)IFLA_RTA(ifip);
+	int ifil = RTM_PAYLOAD(nlp);
+
+	const char *ptr;
+	unsigned char *ptr2;
+	for(;RTA_OK(rtap, ifil);rtap=RTA_NEXT(rtap, ifil))
+	{
+		switch(rtap->rta_type) {
+		case IFLA_ADDRESS:
+			ptr2 = (unsigned char*)RTA_DATA(rtap);
+			snprintf(mac, 20, "%02x:%02x:%02x:%02x:%02x:%02x",
+				ptr2[0], ptr2[1], ptr2[2], ptr2[3], ptr2[4], ptr2[5]);
+			memcpy(mac_bin, ptr, 6);
+			break;
+		case IFLA_IFNAME:
+			ptr = (const char*)RTA_DATA(rtap);
+			memcpy(ifname, ptr, strlen(ptr));
+			break;
+		case IFLA_MTU:
+			memcpy(&mtu, RTA_DATA(rtap), sizeof(unsigned int));
+			break;
+		}
+	}
+
+	{
+		Mutex::Lock l(_if_m);
+		struct iface_entry &entry = _interfaces[ifip->ifi_index];
+		entry.index = ifip->ifi_index;
+		memcpy(entry.ifacename, ifname, sizeof(ifname));
+		memcpy(entry.mac, mac, sizeof(mac));
+		memcpy(entry.mac_bin, mac_bin, 6);
+		entry.mtu = mtu;
+	}
+#ifdef ZT_TRACE
+	fprintf(stderr, "Link Added: %s mac: %s, mtu: %d\n", ifname, mac, mtu);
+#endif
+}
+
+void LinuxNetLink::_linkDeleted(struct nlmsghdr *nlp)
+{
+	char mac[18] = {0};
+	unsigned int mtu = 0;
+	char ifname[40] = {0};
+
+	struct ifinfomsg *ifip = (struct ifinfomsg *)NLMSG_DATA(nlp);
+	struct rtattr *rtap = (struct rtattr *)IFLA_RTA(ifip);
+	int ifil = RTM_PAYLOAD(nlp);
+
+	const char *ptr;
+	unsigned char *ptr2;
+	for(;RTA_OK(rtap, ifil);rtap=RTA_NEXT(rtap, ifil))
+	{
+		switch(rtap->rta_type) {
+		case IFLA_ADDRESS:
+			ptr2 = (unsigned char*)RTA_DATA(rtap);
+			snprintf(mac, 20, "%02x:%02x:%02x:%02x:%02x:%02x",
+				ptr2[0], ptr2[1], ptr2[2], ptr2[3], ptr2[4], ptr2[5]);
+			break;
+		case IFLA_IFNAME:
+			ptr = (const char*)RTA_DATA(rtap);
+			memcpy(ifname, ptr, strlen(ptr));
+			break;
+		case IFLA_MTU:
+			memcpy(&mtu, RTA_DATA(rtap), sizeof(unsigned int));
+			break;
+		}
+	}
+#ifdef ZT_TRACE
+	fprintf(stderr, "Link Deleted: %s mac: %s, mtu: %d\n", ifname, mac, mtu);
+#endif
+	{
+		Mutex::Lock l(_if_m);
+		if(_interfaces.contains(ifip->ifi_index)) {
+			_interfaces.erase(ifip->ifi_index);
+		}
+	}
+}
+
+void LinuxNetLink::_requestIPv4Routes()
+{
+	int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (fd == -1) {
+		fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno));
+		return;
+	}
+
+	_setSocketTimeout(fd);
+
+	struct sockaddr_nl la;
+	la.nl_family = AF_NETLINK;
+	la.nl_pid = getpid();
+	la.nl_groups = RTMGRP_IPV4_ROUTE;
+	if(bind(fd, (struct sockaddr*)&la, sizeof(la))) {
+		fprintf(stderr, "Error binding RTNETLINK: %s\n", strerror(errno));
+		return;
+	}
+
+	struct nl_route_req req;
+	bzero(&req, sizeof(req));
+	req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+	req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	req.nl.nlmsg_type = RTM_GETROUTE;
+	req.nl.nlmsg_pid = 0;
+	req.nl.nlmsg_seq = ++_seq;
+	req.rt.rtm_family = AF_INET;
+	req.rt.rtm_table = RT_TABLE_MAIN;
+
+	struct sockaddr_nl pa;
+	bzero(&pa, sizeof(pa));
+	pa.nl_family = AF_NETLINK;
+
+	struct msghdr msg;
+	bzero(&msg, sizeof(msg));
+	msg.msg_name = (void*)&pa;
+	msg.msg_namelen = sizeof(pa);
+
+	struct iovec iov;
+	bzero(&iov, sizeof(iov));
+	iov.iov_base = (void*)&req.nl;
+	iov.iov_len = req.nl.nlmsg_len;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	sendmsg(fd, &msg, 0);
+
+	_doRecv(fd);
+
+	close(fd);
+}
+
+void LinuxNetLink::_requestIPv6Routes()
+{
+	int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (fd == -1) {
+		fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno));
+		return;
+	}
+
+	_setSocketTimeout(fd);
+
+	struct sockaddr_nl la;
+	la.nl_family = AF_NETLINK;
+	la.nl_pid = getpid();
+	la.nl_groups = RTMGRP_IPV6_ROUTE;
+	if(bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) {
+		fprintf(stderr, "Error binding RTNETLINK: %s\n", strerror(errno));
+		return;
+	}
+
+	struct nl_route_req req;
+	bzero(&req, sizeof(req));
+	req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+	req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	req.nl.nlmsg_type = RTM_GETROUTE;
+	req.nl.nlmsg_pid = 0;
+	req.nl.nlmsg_seq = ++_seq;
+	req.rt.rtm_family = AF_INET6;
+	req.rt.rtm_table = RT_TABLE_MAIN;
+
+	struct sockaddr_nl pa;
+	bzero(&pa, sizeof(pa));
+	pa.nl_family = AF_NETLINK;
+
+	struct msghdr msg;
+	bzero(&msg, sizeof(msg));
+	msg.msg_name = (void*)&pa;
+	msg.msg_namelen = sizeof(pa);
+
+	struct iovec iov;
+	bzero(&iov, sizeof(iov));
+	iov.iov_base = (void*)&req.nl;
+	iov.iov_len = req.nl.nlmsg_len;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	sendmsg(fd, &msg, 0);
+
+	_doRecv(fd);
+
+	close(fd);
+}
+
+void LinuxNetLink::_requestInterfaceList()
+{
+	int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (fd == -1) {
+		fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno));
+		return;
+	}
+
+	_setSocketTimeout(fd);
+
+	struct sockaddr_nl la;
+	la.nl_family = AF_NETLINK;
+	la.nl_pid = getpid();
+	la.nl_groups = RTMGRP_LINK;
+	if(bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) {
+		fprintf(stderr, "Error binding RTNETLINK: %s\n", strerror(errno));
+		return;
+	}
+
+	struct nl_if_req req;
+	bzero(&req, sizeof(req));
+	req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+	req.nl.nlmsg_type = RTM_GETLINK;
+	req.nl.nlmsg_pid = 0;
+	req.nl.nlmsg_seq = ++_seq;
+	req.ifa.ifi_family = AF_UNSPEC;
+
+	struct sockaddr_nl pa;
+	bzero(&pa, sizeof(pa));
+	pa.nl_family = AF_NETLINK;
+
+	struct msghdr msg;
+	bzero(&msg, sizeof(msg));
+	msg.msg_name = (void*)&pa;
+	msg.msg_namelen = sizeof(pa);
+
+	struct iovec iov;
+	bzero(&iov, sizeof(iov));
+	iov.iov_base = (void*)&req.nl;
+	iov.iov_len = req.nl.nlmsg_len;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	sendmsg(fd, &msg, 0);
+
+	_doRecv(fd);
+
+	close(fd);
+}
+
+void LinuxNetLink::addRoute(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifaceName)
+{
+	int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (fd == -1) {
+		fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno));
+		return;
+	}
+
+	_setSocketTimeout(fd);
+
+	struct sockaddr_nl la;
+	bzero(&la, sizeof(la));
+	la.nl_family = AF_NETLINK;
+	la.nl_pid = getpid();
+
+	if(bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) {
+		fprintf(stderr, "Error binding RTNETLINK: %s\n", strerror(errno));
+		return;
+	}
+
+#ifdef ZT_TRACE
+	char  tmp[64];
+	char tmp2[64];
+	char tmp3[64];
+	fprintf(stderr, "Adding Route. target: %s via: %s src: %s iface: %s\n", target.toString(tmp), via.toString(tmp2), src.toString(tmp3), ifaceName);
+#endif
+
+	if(!target) {
+#ifdef ZT_TRACE
+		fprintf(stderr, "Uhhhh adding an empty route?!?!?");
+#endif
+		return;
+	}
+
+	int rtl = sizeof(struct rtmsg);
+	struct nl_route_req req;
+	bzero(&req, sizeof(req));
+
+	struct rtattr *rtap = (struct rtattr *)req.buf;
+	rtap->rta_type = RTA_DST;
+	if (target.isV4()) {
+		rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr));
+		memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&target)->sin_addr, sizeof(struct in_addr));
+	} else {
+		rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr));
+		memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&target)->sin6_addr, sizeof(struct in6_addr));
+	}
+	rtl += rtap->rta_len;
+
+	if(via) {
+		rtap = (struct rtattr *)(((char*)rtap)+rtap->rta_len);
+		rtap->rta_type = RTA_GATEWAY;
+		if(via.isV4()) {
+			rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr));
+			memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&via)->sin_addr, sizeof(struct in_addr));
+		} else {
+			rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr));
+			memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&via)->sin6_addr, sizeof(struct in6_addr));
+		}
+		rtl += rtap->rta_len;
+	} else if (src) {
+		rtap = (struct rtattr *)(((char*)rtap)+rtap->rta_len);
+		rtap->rta_type = RTA_SRC;
+		if(src.isV4()) {
+			rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr));
+			memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&src)->sin_addr, sizeof(struct in_addr));
+			
+		} else {
+			rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr));
+			memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&src)->sin6_addr, sizeof(struct in6_addr));
+		}
+		req.rt.rtm_src_len = src.netmaskBits();
+	}
+
+	if (ifaceName != NULL) {
+		int interface_index = _indexForInterface(ifaceName);
+		if (interface_index != -1) {
+			rtap = (struct rtattr *) (((char*)rtap) + rtap->rta_len);
+			rtap->rta_type = RTA_OIF;
+			rtap->rta_len = RTA_LENGTH(sizeof(int));
+			memcpy(RTA_DATA(rtap), &interface_index, sizeof(int));
+			rtl += rtap->rta_len;
+		}
+	}
+
+	
+
+	req.nl.nlmsg_len = NLMSG_LENGTH(rtl);
+	req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE | NLM_F_ACK;
+	req.nl.nlmsg_type = RTM_NEWROUTE;
+	req.nl.nlmsg_pid = 0;
+	req.nl.nlmsg_seq = ++_seq;
+	req.rt.rtm_family = target.ss_family;
+	req.rt.rtm_table = RT_TABLE_MAIN;
+	req.rt.rtm_protocol = RTPROT_STATIC;
+	req.rt.rtm_scope = RT_SCOPE_UNIVERSE;
+	req.rt.rtm_type = RTN_UNICAST;
+	req.rt.rtm_dst_len = target.netmaskBits();
+	req.rt.rtm_flags = 0;
+
+	struct sockaddr_nl pa;
+	bzero(&pa, sizeof(pa));
+	pa.nl_family = AF_NETLINK;
+
+	struct msghdr msg;
+	bzero(&msg, sizeof(msg));
+	msg.msg_name = (void*)&pa;
+	msg.msg_namelen = sizeof(pa);
+
+	struct iovec iov;
+	bzero(&iov, sizeof(iov));
+	iov.iov_base = (void*)&req.nl;
+	iov.iov_len = req.nl.nlmsg_len;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	sendmsg(fd, &msg, 0);
+
+	_doRecv(fd);
+
+	close(fd);
+}
+
+void LinuxNetLink::delRoute(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifaceName)
+{
+	int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (fd == -1) {
+		fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno));
+		return;
+	}
+
+	_setSocketTimeout(fd);
+
+	struct sockaddr_nl la;
+	la.nl_family = AF_NETLINK;
+	la.nl_pid = getpid();
+
+	if(bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) {
+		fprintf(stderr, "Error binding RTNETLINK: %s\n", strerror(errno));
+		return;
+	}
+#ifdef ZT_TRACE
+	char  tmp[64];
+	char tmp2[64];
+	char tmp3[64];
+	fprintf(stderr, "Removing Route. target: %s via: %s src: %s iface: %s\n", target.toString(tmp), via.toString(tmp2), src.toString(tmp3), ifaceName);
+#endif
+
+	if(!target) {
+#ifdef ZT_TRACE
+		fprintf(stderr, "Uhhhh deleting an empty route?!?!?");
+#endif
+		return;
+	}
+
+	int rtl = sizeof(struct rtmsg);
+	struct nl_route_req req;
+	bzero(&req, sizeof(req));
+
+	struct rtattr *rtap = (struct rtattr *)req.buf;
+	rtap->rta_type = RTA_DST;
+	if (target.isV4()) {
+		rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr));
+		memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&target)->sin_addr, sizeof(struct in_addr));
+	} else {
+		rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr));
+		memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&target)->sin6_addr, sizeof(struct in6_addr));
+	}
+	rtl += rtap->rta_len;
+
+	if(via) {
+		rtap = (struct rtattr *)(((char*)rtap)+rtap->rta_len);
+		rtap->rta_type = RTA_GATEWAY;
+		if(via.isV4()) {
+			rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr));
+			memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&via)->sin_addr, sizeof(struct in_addr));
+		} else {
+			rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr));
+			memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&via)->sin6_addr, sizeof(struct in6_addr));
+		}
+		rtl += rtap->rta_len;
+	} else if (src) {
+		rtap = (struct rtattr *)(((char*)rtap)+rtap->rta_len);
+		rtap->rta_type = RTA_SRC;
+		if(src.isV4()) {
+			rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr));
+			memcpy(RTA_DATA(rtap), &((struct sockaddr_in*)&src)->sin_addr, sizeof(struct in_addr));
+			
+		} else {
+			rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr));
+			memcpy(RTA_DATA(rtap), &((struct sockaddr_in6*)&src)->sin6_addr, sizeof(struct in6_addr));
+		}
+		req.rt.rtm_src_len = src.netmaskBits();
+	}
+
+	if (ifaceName != NULL) {
+		int interface_index = _indexForInterface(ifaceName);
+		if (interface_index != -1) {
+			rtap = (struct rtattr *) (((char*)rtap) + rtap->rta_len);
+			rtap->rta_type = RTA_OIF;
+			rtap->rta_len = RTA_LENGTH(sizeof(int));
+			memcpy(RTA_DATA(rtap), &interface_index, sizeof(int));
+			rtl += rtap->rta_len;
+		}
+	}
+
+	
+
+	req.nl.nlmsg_len = NLMSG_LENGTH(rtl);
+	req.nl.nlmsg_flags = NLM_F_REQUEST;
+	req.nl.nlmsg_type = RTM_DELROUTE;
+	req.nl.nlmsg_pid = 0;
+	req.nl.nlmsg_seq = ++_seq;
+	req.rt.rtm_family = target.ss_family;
+	req.rt.rtm_table = RT_TABLE_MAIN;
+	req.rt.rtm_protocol = RTPROT_STATIC;
+	req.rt.rtm_scope = RT_SCOPE_UNIVERSE;
+	req.rt.rtm_type = RTN_UNICAST;
+	req.rt.rtm_dst_len = target.netmaskBits();
+	req.rt.rtm_flags = 0;
+
+	struct sockaddr_nl pa;
+	bzero(&pa, sizeof(pa));
+	pa.nl_family = AF_NETLINK;
+
+	struct msghdr msg;
+	bzero(&msg, sizeof(msg));
+	msg.msg_name = (void*)&pa;
+	msg.msg_namelen = sizeof(pa);
+
+	struct iovec iov;
+	bzero(&iov, sizeof(iov));
+	iov.iov_base = (void*)&req.nl;
+	iov.iov_len = req.nl.nlmsg_len;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	sendmsg(fd, &msg, 0);
+
+	_doRecv(fd);
+
+	close(fd);
+}
+
+void LinuxNetLink::addAddress(const InetAddress &addr, const char *iface)
+{
+	int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (fd == -1) {
+		fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno));
+		return;
+	}
+
+	_setSocketTimeout(fd);
+
+	struct sockaddr_nl la;
+	la.nl_family = AF_NETLINK;
+	la.nl_pid = getpid();
+	if (addr.isV4()) {
+		la.nl_groups = RTMGRP_IPV4_IFADDR;
+	} else {
+		la.nl_groups = RTMGRP_IPV6_IFADDR;
+	}
+	if(bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) {
+		fprintf(stderr, "Error binding RTNETLINK: %s\n", strerror(errno));
+		return;
+	}
+#ifdef ZT_TRACE
+	char tmp[128];
+	fprintf(stderr, "Adding IP address %s to interface %s", addr.toString(tmp), iface);
+#endif
+	int interface_index = _indexForInterface(iface);
+
+	if (interface_index == -1) {
+		fprintf(stderr, "Unable to find index for interface %s\n", iface);
+		return;
+	}
+	
+	int rtl = sizeof(struct ifaddrmsg);
+	struct nl_adr_req req;
+	bzero(&req, sizeof(struct nl_adr_req));
+
+	struct rtattr *rtap = (struct rtattr *)req.buf;;
+	if(addr.isV4()) {
+		struct sockaddr_in *addr_v4 = (struct sockaddr_in*)&addr;
+		rtap->rta_type = IFA_ADDRESS;
+		rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr));
+		memcpy(RTA_DATA(rtap), &addr_v4->sin_addr, sizeof(struct in_addr));
+		rtl += rtap->rta_len;
+
+		rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len);
+		rtap->rta_type = IFA_LOCAL;
+		rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr));
+		memcpy(RTA_DATA(rtap), &addr_v4->sin_addr, sizeof(struct in_addr));
+		rtl += rtap->rta_len;
+
+		InetAddress broadcast = addr.broadcast();
+		if(broadcast) {
+			rtap = (struct rtattr*)(((char*)rtap)+rtap->rta_len);
+			struct sockaddr_in *bcast = (struct sockaddr_in*)&broadcast;
+			rtap->rta_type = IFA_BROADCAST;
+			rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr));
+			memcpy(RTA_DATA(rtap), &bcast->sin_addr, sizeof(struct in_addr));
+			rtl += rtap->rta_len;
+		}
+	} else { //V6
+		rtap->rta_type = IFA_ADDRESS;
+		struct sockaddr_in6 *addr_v6 = (struct sockaddr_in6*)&addr;
+		rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr));
+		memcpy(RTA_DATA(rtap), &addr_v6->sin6_addr, sizeof(struct in6_addr));
+		rtl += rtap->rta_len;
+	}
+
+	if (iface) {
+		rtap = (struct rtattr*)(((char*)rtap)+rtap->rta_len);
+		rtap->rta_type = IFA_LABEL;
+		rtap->rta_len = RTA_LENGTH(strlen(iface));
+		memcpy(RTA_DATA(rtap), iface, strlen(iface));
+		rtl += rtap->rta_len;
+	}
+
+	req.nl.nlmsg_len = NLMSG_LENGTH(rtl);
+	req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
+	req.nl.nlmsg_type = RTM_NEWADDR;
+	req.nl.nlmsg_pid = 0;
+	req.nl.nlmsg_seq = ++_seq;
+	req.ifa.ifa_family = addr.ss_family;
+	req.ifa.ifa_prefixlen = addr.port();
+	req.ifa.ifa_flags = IFA_F_PERMANENT;
+	req.ifa.ifa_scope = 0;
+	req.ifa.ifa_index = interface_index;
+
+	struct sockaddr_nl pa;
+	bzero(&pa, sizeof(sockaddr_nl));
+	pa.nl_family = AF_NETLINK;
+
+	struct msghdr msg;
+	bzero(&msg, sizeof(msg));
+	msg.msg_name = (void*)&pa;
+	msg.msg_namelen = sizeof(pa);
+
+	struct iovec iov;
+	iov.iov_base = (void*)&req.nl;
+	iov.iov_len = req.nl.nlmsg_len;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	sendmsg(fd, &msg, 0);
+
+	_doRecv(fd);
+
+	close(fd);
+}
+
+void LinuxNetLink::removeAddress(const InetAddress &addr, const char *iface)
+{
+	int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (fd == -1) {
+		fprintf(stderr, "Error opening RTNETLINK socket: %s\n", strerror(errno));
+		return;
+	}
+
+	_setSocketTimeout(fd);
+
+	struct sockaddr_nl la;
+	la.nl_family = AF_NETLINK;
+	la.nl_pid = getpid();
+	if (addr.isV4()) {
+		la.nl_groups = RTMGRP_IPV4_IFADDR;
+	} else {
+		la.nl_groups = RTMGRP_IPV6_IFADDR;
+	}
+	if(bind(fd, (struct sockaddr*)&la, sizeof(struct sockaddr_nl))) {
+		fprintf(stderr, "Error binding RTNETLINK: %s\n", strerror(errno));
+		return;
+	}
+#ifdef ZT_TRACE
+	char tmp[128];
+	fprintf(stderr, "Removing IP address %s from interface %s", addr.toString(tmp), iface);
+#endif
+	int interface_index = _indexForInterface(iface);
+
+	if (interface_index == -1) {
+		fprintf(stderr, "Unable to find index for interface %s\n", iface);
+		return;
+	}
+	
+	int rtl = sizeof(struct ifaddrmsg);
+	struct nl_adr_req req;
+	bzero(&req, sizeof(struct nl_adr_req));
+
+	struct rtattr *rtap = (struct rtattr *)req.buf;
+	if(addr.isV4()) {
+		struct sockaddr_in *addr_v4 = (struct sockaddr_in*)&addr;
+		rtap->rta_type = IFA_ADDRESS;
+		rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr));
+		memcpy(RTA_DATA(rtap), &addr_v4->sin_addr, sizeof(struct in_addr));
+		rtl += rtap->rta_len;
+
+		rtap = (struct rtattr*)(((char*)rtap) + rtap->rta_len);
+		rtap->rta_type = IFA_LOCAL;
+		rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr));
+		memcpy(RTA_DATA(rtap), &addr_v4->sin_addr, sizeof(struct in_addr));
+		rtl += rtap->rta_len;
+
+		InetAddress broadcast = addr.broadcast();
+		if(broadcast) {
+			rtap = (struct rtattr*)(((char*)rtap)+rtap->rta_len);
+			struct sockaddr_in *bcast = (struct sockaddr_in*)&broadcast;
+			rtap->rta_type = IFA_BROADCAST;
+			rtap->rta_len = RTA_LENGTH(sizeof(struct in_addr));
+			memcpy(RTA_DATA(rtap), &bcast->sin_addr, sizeof(struct in_addr));
+			rtl += rtap->rta_len;
+		}
+	} else { //V6
+		rtap->rta_type = IFA_ADDRESS;
+		struct sockaddr_in6 *addr_v6 = (struct sockaddr_in6*)&addr;
+		rtap->rta_len = RTA_LENGTH(sizeof(struct in6_addr));
+		memcpy(RTA_DATA(rtap), &addr_v6->sin6_addr, sizeof(struct in6_addr));
+		rtl += rtap->rta_len;
+	}
+
+	if (iface) {
+		rtap = (struct rtattr*)(((char*)rtap)+rtap->rta_len);
+		rtap->rta_type = IFA_LABEL;
+		rtap->rta_len = RTA_LENGTH(strlen(iface));
+		memcpy(RTA_DATA(rtap), iface, strlen(iface));
+		rtl += rtap->rta_len;
+	}
+
+	req.nl.nlmsg_len = NLMSG_LENGTH(rtl);
+	req.nl.nlmsg_flags = NLM_F_REQUEST;
+	req.nl.nlmsg_type = RTM_DELADDR;
+	req.nl.nlmsg_pid = 0;
+	req.nl.nlmsg_seq = ++_seq;
+	req.ifa.ifa_family = addr.ss_family;
+	req.ifa.ifa_prefixlen = addr.port();
+	req.ifa.ifa_flags = IFA_F_PERMANENT;
+	req.ifa.ifa_scope = 0;
+	req.ifa.ifa_index = interface_index;
+
+	struct sockaddr_nl pa;
+	bzero(&pa, sizeof(sockaddr_nl));
+	pa.nl_family = AF_NETLINK;
+
+	struct msghdr msg;
+	bzero(&msg, sizeof(msg));
+	msg.msg_name = (void*)&pa;
+	msg.msg_namelen = sizeof(pa);
+
+	struct iovec iov;
+	iov.iov_base = (void*)&req.nl;
+	iov.iov_len = req.nl.nlmsg_len;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	sendmsg(fd, &msg, 0);
+
+	_doRecv(fd);
+
+	close(fd);
+}
+
+RouteList LinuxNetLink::getIPV4Routes() const 
+{
+	return _routes_ipv4;
+}
+
+RouteList LinuxNetLink::getIPV6Routes() const
+{
+	return _routes_ipv6;
+}
+
+int LinuxNetLink::_indexForInterface(const char *iface)
+{
+	Mutex::Lock l(_if_m);
+	int interface_index = -1;
+	Hashtable<int, iface_entry>::Iterator iter(_interfaces);
+	int *k = NULL;
+	iface_entry *v = NULL;
+	while(iter.next(k,v)) {
+		if(strcmp(iface, v->ifacename) == 0) {
+			interface_index = v->index;
+			break;
+		}
+	}
+	return interface_index;
+}
+
+} // namespace ZeroTier

+ 129 - 0
osdep/LinuxNetLink.hpp

@@ -0,0 +1,129 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2018  ZeroTier, Inc.  https://www.zerotier.com/
+ *
+ * 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/>.
+ *
+ * --
+ *
+ * You can be released from the requirements of the license by purchasing
+ * a commercial license. Buying such a license is mandatory as soon as you
+ * develop commercial closed-source software that incorporates or links
+ * directly against ZeroTier software without disclosing the source code
+ * of your own application.
+ */
+
+#ifndef ZT_LINUX_NETLINK_HPP
+#define ZT_LINUX_NETLINK_HPP
+
+#include <vector>
+
+#include <sys/socket.h>
+#include <asm/types.h>
+#include <linux/rtnetlink.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+
+#include "../node/InetAddress.hpp"
+#include "../node/MAC.hpp"
+#include "Thread.hpp"
+#include "../node/Hashtable.hpp"
+#include "../node/Mutex.hpp"
+
+
+namespace ZeroTier {
+
+struct route_entry {
+	InetAddress target;
+	InetAddress via;
+	int if_index;
+	char iface[IFNAMSIZ];
+};
+typedef std::vector<route_entry> RouteList;
+
+/**
+ * Interface with Linux's RTNETLINK 
+ */
+class LinuxNetLink
+{
+private:
+    LinuxNetLink();
+    ~LinuxNetLink();
+
+public:
+    static LinuxNetLink& getInstance()
+    {
+        static LinuxNetLink instance;
+        return instance;
+    }
+
+    LinuxNetLink(LinuxNetLink const&) = delete;
+    void operator=(LinuxNetLink const&) = delete;
+
+    void addRoute(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifaceName);
+    void delRoute(const InetAddress &target, const InetAddress &via, const InetAddress &src, const char *ifaceName);
+    RouteList getIPV4Routes() const;
+    RouteList getIPV6Routes() const;
+
+    void addAddress(const InetAddress &addr, const char *iface);
+    void removeAddress(const InetAddress &addr, const char *iface);
+
+    void threadMain() throw();
+private:
+    int _doRecv(int fd);
+
+    void _processMessage(struct nlmsghdr *nlp, int nll);
+    void _routeAdded(struct nlmsghdr *nlp);
+    void _routeDeleted(struct nlmsghdr *nlp);
+    void _linkAdded(struct nlmsghdr *nlp);
+    void _linkDeleted(struct nlmsghdr *nlp);
+    void _ipAddressAdded(struct nlmsghdr *nlp);
+    void _ipAddressDeleted(struct nlmsghdr *nlp);
+
+    void _requestInterfaceList();
+    void _requestIPv4Routes();
+    void _requestIPv6Routes();
+
+    int _indexForInterface(const char *iface);
+
+    void _setSocketTimeout(int fd, int seconds = 1);
+
+    Thread _t;
+    bool _running;
+
+    RouteList _routes_ipv4;
+    Mutex _rv4_m;
+    RouteList _routes_ipv6;
+    Mutex _rv6_m;
+
+    uint32_t _seq;
+
+    struct iface_entry {
+        int index;
+        char ifacename[IFNAMSIZ];
+        char mac[18];
+        char mac_bin[6];
+        unsigned int mtu;
+    };
+    Hashtable<int, iface_entry> _interfaces;
+    Mutex _if_m;
+
+    // socket communication vars;
+    int _fd;
+    struct sockaddr_nl _la;
+};    
+
+}
+
+#endif // ZT_LINUX_NETLINK_HPPS

+ 9 - 1
osdep/ManagedRoute.hpp

@@ -49,14 +49,20 @@ class ManagedRoute
 	friend class SharedPtr<ManagedRoute>;
 
 public:
-	ManagedRoute(const InetAddress &target,const InetAddress &via,const char *device)
+	ManagedRoute(const InetAddress &target,const InetAddress &via,const InetAddress &src,const char *device)
 	{
 		_target = target;
 		_via = via;
+		_src = src;
 		if (via.ss_family == AF_INET)
 			_via.setPort(32);
 		else if (via.ss_family == AF_INET6)
 			_via.setPort(128);
+		if (src.ss_family == AF_INET) {
+			_src.setPort(32);
+		} else if (src.ss_family == AF_INET6) {
+			_src.setPort(128);
+		}
 		Utils::scopy(_device,sizeof(_device),device);
 		_systemDevice[0] = (char)0;
 	}
@@ -87,6 +93,7 @@ public:
 
 	inline const InetAddress &target() const { return _target; }
 	inline const InetAddress &via() const { return _via; }
+	inline const InetAddress &src() const { return _src; }
 	inline const char *device() const { return _device; }
 
 private:
@@ -95,6 +102,7 @@ private:
 
 	InetAddress _target;
 	InetAddress _via;
+	InetAddress _src;
 	InetAddress _systemVia; // for route overrides
 	std::map<InetAddress,bool> _applied; // routes currently applied
 	char _device[128];

+ 21 - 1
osdep/OSUtils.cpp

@@ -29,6 +29,7 @@
 #include <stdlib.h>
 #include <stdarg.h>
 #include <sys/stat.h>
+#include <stdlib.h>
 
 #include "../node/Constants.hpp"
 #include "../node/Utils.hpp"
@@ -366,7 +367,7 @@ std::vector<std::string> OSUtils::split(const char *s,const char *const sep,cons
 				if (buf.size() > 0) {
 					fields.push_back(buf);
 					buf.clear();
-				} // else skip runs of seperators
+				} // else skip runs of separators
 			} else buf.push_back(*s);
 		}
 		++s;
@@ -398,6 +399,25 @@ std::string OSUtils::platformDefaultHomePath()
     return homeDir;
 #endif
 
+#ifdef __SYNOLOGY__
+	return std::string("/var/packages/zerotier/target/var");
+#endif
+
+    // Check for user-defined environment variable before using defaults
+#ifdef __WINDOWS__
+	DWORD bufferSize = 65535;
+	std::string userDefinedPath;
+	bufferSize = GetEnvironmentVariable("ZEROTIER_HOME", &userDefinedPath[0], bufferSize);
+	if (bufferSize) {
+		return userDefinedPath;
+	}
+#else
+	if(const char* userDefinedPath = getenv("ZEROTIER_HOME")) {
+		return std::string(userDefinedPath);
+	}
+#endif
+
+	// Finally, resort to using default paths if no user-defined path was provided
 #ifdef __UNIX_LIKE__
 
 #ifdef __APPLE__

+ 68 - 3
osdep/Phy.hpp

@@ -152,12 +152,13 @@ private:
 		ZT_PHY_SOCKET_UNIX_LISTEN = 0x08
 	};
 
-	struct PhySocketImpl
-	{
+	struct PhySocketImpl {
+		PhySocketImpl() { memset(ifname, 0, sizeof(ifname)); }
 		PhySocketType type;
 		ZT_PHY_SOCKFD_TYPE sock;
 		void *uptr; // user-settable pointer
 		ZT_PHY_SOCKADDR_STORAGE_TYPE saddr; // remote for TCP_OUT and TCP_IN, local for TCP_LISTEN, RAW, and UDP
+		char ifname[16];
 	};
 
 	std::list<PhySocketImpl> _socks;
@@ -249,6 +250,70 @@ public:
 	 */
 	static inline void** getuptr(PhySocket *s) throw() { return &(reinterpret_cast<PhySocketImpl *>(s)->uptr); }
 
+	/**
+	 * @param s Socket object
+	 * @param nameBuf Buffer to store name of interface which this Socket object is bound to
+	 * @param buflen Length of buffer to copy name into
+	 */
+	static inline void getIfName(PhySocket *s, char *nameBuf, int buflen)
+	{
+		if (s) {
+			memcpy(nameBuf, reinterpret_cast<PhySocketImpl *>(s)->ifname, buflen);
+		}
+	}
+
+	/**
+	 * @param s Socket object
+	 * @param ifname Buffer containing name of interface that this Socket object is bound to
+	 * @param len Length of name of interface
+	 */
+	static inline void setIfName(PhySocket *s, char *ifname, int len)
+	{
+		if (s) {
+			memcpy(&(reinterpret_cast<PhySocketImpl *>(s)->ifname), ifname, len);
+		}
+	}
+
+	/**
+	 * Whether or not the socket object is in a closed state
+	 *
+	 * @param s Socket object
+	 * @return true if socket is closed, false if otherwise
+	 */
+	inline bool isClosed(PhySocket *s)
+	{
+		PhySocketImpl *sws = (reinterpret_cast<PhySocketImpl *>(s));
+		return sws->type == ZT_PHY_SOCKET_CLOSED;
+	}
+
+	/**
+	 * Get state of socket object
+	 *
+	 * @param s Socket object
+	 * @return State of socket
+	 */
+	inline int getState(PhySocket *s)
+	{
+		PhySocketImpl *sws = (reinterpret_cast<PhySocketImpl *>(s));
+		return sws->type;
+	}
+
+	/**
+	 * In the event that this socket is erased, we need a way to convey to the multipath logic
+	 * that this path is no longer valid.
+	 *
+	 * @param s Socket object
+	 * @return Whether the state of this socket is within an acceptable range of values
+	 */
+	inline bool isValidState(PhySocket *s)
+	{
+		if (s) {
+			PhySocketImpl *sws = (reinterpret_cast<PhySocketImpl *>(s));
+			return sws->type >= ZT_PHY_SOCKET_CLOSED && sws->type <= ZT_PHY_SOCKET_UNIX_LISTEN;
+		}
+		return false;
+	}
+
 	/**
 	 * Cause poll() to stop waiting immediately
 	 *
@@ -985,7 +1050,7 @@ public:
 					ZT_PHY_SOCKFD_TYPE sock = s->sock; // if closed, s->sock becomes invalid as s is no longer dereferencable
 					if ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))) {
 						try {
-							_handler->phyOnUnixWritable((PhySocket *)&(*s),&(s->uptr),false);
+							_handler->phyOnUnixWritable((PhySocket *)&(*s),&(s->uptr));
 						} catch ( ... ) {}
 					}
 					if (FD_ISSET(sock,&rfds)) {

+ 1 - 1
osdep/WindowsEthernetTap.hpp

@@ -71,7 +71,7 @@ public:
 	static std::string destroyAllPersistentTapDevices();
 
 	/**
-	 * Uninstall a specific persistent tap device by instance ID
+	 * Uninstalls a specific persistent tap device by instance ID
 	 *
 	 * @param instanceId Device instance ID
 	 * @return Empty string on success, otherwise an error message

+ 4 - 1
rule-compiler/rule-compiler.js

@@ -65,7 +65,8 @@ const OPEN_BLOCK_KEYWORDS = {
 	'tee': true,
 	'watch': true,
 	'redirect': true,
-	'break': true
+	'break': true,
+	'priority': true
 };
 
 // Reserved words that can't be used as tag, capability, or rule set names
@@ -81,6 +82,7 @@ const RESERVED_WORDS = {
 	'watch': true,
 	'redirect': true,
 	'break': true,
+	'priority': true,
 
 	'ztsrc': true,
 	'ztdest': true,
@@ -131,6 +133,7 @@ const KEYWORD_TO_API_MAP = {
 	'watch': 'ACTION_WATCH',
 	'redirect': 'ACTION_REDIRECT',
 	'break': 'ACTION_BREAK',
+	'priority': 'ACTION_PRIORITY',
 
 	'ztsrc': 'MATCH_SOURCE_ZEROTIER_ADDRESS',
 	'ztdest': 'MATCH_DEST_ZEROTIER_ADDRESS',

+ 452 - 113
service/OneService.cpp

@@ -67,9 +67,12 @@
 #include <ShlObj.h>
 #include <netioapi.h>
 #include <iphlpapi.h>
+#include <unistd.h>
+#define stat _stat
 #else
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
 #include <sys/wait.h>
 #include <unistd.h>
 #include <ifaddrs.h>
@@ -81,6 +84,12 @@
 #include "../ext/http-parser/http_parser.h"
 #endif
 
+#if ZT_VAULT_SUPPORT
+extern "C" {
+#include <curl/curl.h>
+}
+#endif
+
 #include "../ext/json/json.hpp"
 
 using json = nlohmann::json;
@@ -162,6 +171,14 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
 // TCP activity timeout
 #define ZT_TCP_ACTIVITY_TIMEOUT 60000
 
+#if ZT_VAULT_SUPPORT
+size_t curlResponseWrite(void *ptr, size_t size, size_t nmemb, std::string *data)
+{
+	data->append((char*)ptr, size * nmemb);
+	return size * nmemb;
+}
+#endif
+
 namespace ZeroTier {
 
 namespace {
@@ -283,6 +300,39 @@ static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer)
 	pj["paths"] = pa;
 }
 
+static void _peerAggregateLinkToJson(nlohmann::json &pj,const ZT_Peer *peer)
+{
+	char tmp[256];
+	OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.10llx",peer->address);
+	pj["aggregateLinkLatency"] = peer->latency;
+
+	nlohmann::json pa = nlohmann::json::array();
+	for(unsigned int i=0;i<peer->pathCount;++i) {
+		int64_t lastSend = peer->paths[i].lastSend;
+		int64_t lastReceive = peer->paths[i].lastReceive;
+		nlohmann::json j;
+		j["address"] = reinterpret_cast<const InetAddress *>(&(peer->paths[i].address))->toString(tmp);
+		j["lastSend"] = (lastSend < 0) ? 0 : lastSend;
+		j["lastReceive"] = (lastReceive < 0) ? 0 : lastReceive;
+		//j["trustedPathId"] = peer->paths[i].trustedPathId;
+		//j["active"] = (bool)(peer->paths[i].expired == 0);
+		//j["expired"] = (bool)(peer->paths[i].expired != 0);
+		//j["preferred"] = (bool)(peer->paths[i].preferred != 0);
+		j["latency"] = peer->paths[i].latency;
+		j["pdv"] = peer->paths[i].packetDelayVariance;
+		//j["throughputDisturbCoeff"] = peer->paths[i].throughputDisturbCoeff;
+		//j["packetErrorRatio"] = peer->paths[i].packetErrorRatio;
+		//j["packetLossRatio"] = peer->paths[i].packetLossRatio;
+		j["stability"] = peer->paths[i].stability;
+		j["throughput"] = peer->paths[i].throughput;
+		//j["maxThroughput"] = peer->paths[i].maxThroughput;
+		j["allocation"] = peer->paths[i].allocation;
+		j["ifname"] = peer->paths[i].ifname;
+		pa.push_back(j);
+	}
+	pj["paths"] = pa;
+}
+
 static void _moonToJson(nlohmann::json &mj,const World &world)
 {
 	char tmp[4096];
@@ -403,7 +453,10 @@ public:
 	PhySocket *_localControlSocket6;
 	bool _updateAutoApply;
 	bool _allowTcpFallbackRelay;
+	unsigned int _multipathMode;
 	unsigned int _primaryPort;
+	unsigned int _secondaryPort;
+	unsigned int _tertiaryPort;
 	volatile unsigned int _udpPortPickerCounter;
 
 	// Local configuration and memo-ized information from it
@@ -418,11 +471,13 @@ public:
 	std::vector< std::string > _interfacePrefixBlacklist;
 	Mutex _localConfig_m;
 
+	std::vector<InetAddress> explicitBind;
+
 	/*
 	 * To attempt to handle NAT/gateway craziness we use three local UDP ports:
 	 *
 	 * [0] is the normal/default port, usually 9993
-	 * [1] is a port dervied from our ZeroTier address
+	 * [1] is a port derived from our ZeroTier address
 	 * [2] is a port computed from the normal/default for use with uPnP/NAT-PMP mappings
 	 *
 	 * [2] exists because on some gateways trying to do regular NAT-t interferes
@@ -481,6 +536,14 @@ public:
 	PortMapper *_portMapper;
 #endif
 
+	// HashiCorp Vault Settings
+#if ZT_VAULT_SUPPORT
+	bool _vaultEnabled;
+	std::string _vaultURL;
+	std::string _vaultToken;
+	std::string _vaultPath; // defaults to cubbyhole/zerotier/identity.secret for per-access key storage
+#endif
+
 	// Set to false to force service to stop
 	volatile bool _run;
 	Mutex _run_m;
@@ -512,12 +575,21 @@ public:
 		,_portMappingEnabled(true)
 #ifdef ZT_USE_MINIUPNPC
 		,_portMapper((PortMapper *)0)
+#endif
+#ifdef ZT_VAULT_SUPPORT
+		,_vaultEnabled(false)
+		,_vaultURL()
+		,_vaultToken()
+		,_vaultPath("cubbyhole/zerotier")
 #endif
 		,_run(true)
 	{
 		_ports[0] = 0;
 		_ports[1] = 0;
 		_ports[2] = 0;
+#if ZT_VAULT_SUPPORT
+		curl_global_init(CURL_GLOBAL_DEFAULT);
+#endif
 	}
 
 	virtual ~OneServiceImpl()
@@ -525,6 +597,10 @@ public:
 		_binder.closeAll(_phy);
 		_phy.close(_localControlSocket4);
 		_phy.close(_localControlSocket6);
+#if ZT_VAULT_SUPPORT
+		curl_global_cleanup();
+#endif
+
 #ifdef ZT_USE_MINIUPNPC
 		delete _portMapper;
 #endif
@@ -568,98 +644,8 @@ public:
 				_node = new Node(this,(void *)0,&cb,OSUtils::now());
 			}
 
-			// Read local configuration
-			std::vector<InetAddress> explicitBind;
-			{
-				std::map<InetAddress,ZT_PhysicalPathConfiguration> ppc;
-
-				// LEGACY: support old "trustedpaths" flat file
-				FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S "trustedpaths").c_str(),"r");
-				if (trustpaths) {
-					fprintf(stderr,"WARNING: 'trustedpaths' flat file format is deprecated in favor of path definitions in local.conf" ZT_EOL_S);
-					char buf[1024];
-					while (fgets(buf,sizeof(buf),trustpaths)) {
-						int fno = 0;
-						char *saveptr = (char *)0;
-						uint64_t trustedPathId = 0;
-						InetAddress trustedPathNetwork;
-						for(char *f=Utils::stok(buf,"=\r\n \t",&saveptr);(f);f=Utils::stok((char *)0,"=\r\n \t",&saveptr)) {
-							if (fno == 0) {
-								trustedPathId = Utils::hexStrToU64(f);
-							} else if (fno == 1) {
-								trustedPathNetwork = InetAddress(f);
-							} else break;
-							++fno;
-						}
-						if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.netmaskBits() > 0) ) {
-							ppc[trustedPathNetwork].trustedPathId = trustedPathId;
-							ppc[trustedPathNetwork].mtu = 0; // use default
-						}
-					}
-					fclose(trustpaths);
-				}
-
-				// Read local config file
-				Mutex::Lock _l2(_localConfig_m);
-				std::string lcbuf;
-				if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S "local.conf").c_str(),lcbuf)) {
-					try {
-						_localConfig = OSUtils::jsonParse(lcbuf);
-						if (!_localConfig.is_object()) {
-							fprintf(stderr,"WARNING: unable to parse local.conf (root element is not a JSON object)" ZT_EOL_S);
-						}
-					} catch ( ... ) {
-						fprintf(stderr,"WARNING: unable to parse local.conf (invalid JSON)" ZT_EOL_S);
-					}
-				}
-
-				// Get any trusted paths in local.conf (we'll parse the rest of physical[] elsewhere)
-				json &physical = _localConfig["physical"];
-				if (physical.is_object()) {
-					for(json::iterator phy(physical.begin());phy!=physical.end();++phy) {
-						InetAddress net(OSUtils::jsonString(phy.key(),"").c_str());
-						if (net) {
-							if (phy.value().is_object()) {
-								uint64_t tpid;
-								if ((tpid = OSUtils::jsonInt(phy.value()["trustedPathId"],0ULL)) != 0ULL) {
-									if ((net.ss_family == AF_INET)||(net.ss_family == AF_INET6))
-										ppc[net].trustedPathId = tpid;
-								}
-								ppc[net].mtu = (int)OSUtils::jsonInt(phy.value()["mtu"],0ULL); // 0 means use default
-							}
-						}
-					}
-				}
-
-				json &settings = _localConfig["settings"];
-				if (settings.is_object()) {
-					// Allow controller DB path to be put somewhere else
-					const std::string cdbp(OSUtils::jsonString(settings["controllerDbPath"],""));
-					if (cdbp.length() > 0)
-						_controllerDbPath = cdbp;
-
-					// Bind to wildcard instead of to specific interfaces (disables full tunnel capability)
-					json &bind = settings["bind"];
-					if (bind.is_array()) {
-						for(unsigned long i=0;i<bind.size();++i) {
-							const std::string ips(OSUtils::jsonString(bind[i],""));
-							if (ips.length() > 0) {
-								InetAddress ip(ips.c_str());
-								if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6))
-									explicitBind.push_back(ip);
-							}
-						}
-					}
-				}
-
-				// Set trusted paths if there are any
-				if (ppc.size() > 0) {
-					for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::iterator i(ppc.begin());i!=ppc.end();++i)
-						_node->setPhysicalPathConfiguration(reinterpret_cast<const struct sockaddr_storage *>(&(i->first)),&(i->second));
-				}
-			}
-
-			// Apply other runtime configuration from local.conf
+			// local.conf
+			readLocalSettings();
 			applyLocalConfig();
 
 			// Make sure we can use the primary port, and hunt for one if configured to do so
@@ -708,7 +694,7 @@ public:
 			// This exists because there are buggy NATs out there that fail if more
 			// than one device behind the same NAT tries to use the same internal
 			// private address port number. Buggy NATs are a running theme.
-			_ports[1] = 20000 + ((unsigned int)_node->address() % 45500);
+			_ports[1] = (_secondaryPort == 0) ? 20000 + ((unsigned int)_node->address() % 45500) : _secondaryPort;
 			for(int i=0;;++i) {
 				if (i > 1000) {
 					_ports[1] = 0;
@@ -726,7 +712,7 @@ public:
 				// use the other two ports for that because some NATs do really funky
 				// stuff with ports that are explicitly mapped that breaks things.
 				if (_ports[1]) {
-					_ports[2] = _ports[1];
+					_ports[2] = (_tertiaryPort == 0) ? _ports[1] : _tertiaryPort;
 					for(int i=0;;++i) {
 						if (i > 1000) {
 							_ports[2] = 0;
@@ -780,8 +766,10 @@ public:
 			int64_t lastTapMulticastGroupCheck = 0;
 			int64_t lastBindRefresh = 0;
 			int64_t lastUpdateCheck = clockShouldBe;
+			int64_t lastMultipathModeUpdate = 0;
 			int64_t lastCleanedPeersDb = 0;
 			int64_t lastLocalInterfaceAddressCheck = (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle
+			int64_t lastLocalConfFileCheck = OSUtils::now();
 			for(;;) {
 				_run_m.lock();
 				if (!_run) {
@@ -810,8 +798,21 @@ public:
 						_updater->apply();
 				}
 
+				// Reload local.conf if anything changed recently
+				if ((now - lastLocalConfFileCheck) >= ZT_LOCAL_CONF_FILE_CHECK_INTERVAL) {
+					lastLocalConfFileCheck = now;
+					struct stat result;
+					if(stat((_homePath + ZT_PATH_SEPARATOR_S "local.conf").c_str(), &result)==0) {
+						int64_t mod_time = result.st_mtime * 1000;
+						if ((now - mod_time) <= ZT_LOCAL_CONF_FILE_CHECK_INTERVAL) {
+							readLocalSettings();
+							applyLocalConfig();
+						}
+					}
+				}
+
 				// Refresh bindings in case device's interfaces have changed, and also sync routes to update any shadow routes (e.g. shadow default)
-				if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD)||(restarted)) {
+				if (((now - lastBindRefresh) >= (_multipathMode ? ZT_BINDER_REFRESH_PERIOD / 8 : ZT_BINDER_REFRESH_PERIOD))||(restarted)) {
 					lastBindRefresh = now;
 					unsigned int p[3];
 					unsigned int pc = 0;
@@ -828,6 +829,11 @@ public:
 						}
 					}
 				}
+				// Update multipath mode (if needed)
+				if (((now - lastMultipathModeUpdate) >= ZT_BINDER_REFRESH_PERIOD / 8)||(restarted)) {
+					lastMultipathModeUpdate = now;
+					_node->setMultipathMode(_multipathMode);
+				}
 
 				// Run background task processor in core if it's time to do so
 				int64_t dl = _nextBackgroundTaskDeadline;
@@ -863,7 +869,7 @@ public:
 				}
 
 				// Sync information about physical network interfaces
-				if ((now - lastLocalInterfaceAddressCheck) >= ZT_LOCAL_INTERFACE_CHECK_INTERVAL) {
+				if ((now - lastLocalInterfaceAddressCheck) >= (_multipathMode ? ZT_LOCAL_INTERFACE_CHECK_INTERVAL / 8 : ZT_LOCAL_INTERFACE_CHECK_INTERVAL)) {
 					lastLocalInterfaceAddressCheck = now;
 
 					_node->clearLocalInterfaceAddresses();
@@ -922,6 +928,97 @@ public:
 		return _termReason;
 	}
 
+	void readLocalSettings()
+	{		
+		// Read local configuration
+		std::map<InetAddress,ZT_PhysicalPathConfiguration> ppc;
+
+		// LEGACY: support old "trustedpaths" flat file
+		FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S "trustedpaths").c_str(),"r");
+		if (trustpaths) {
+			fprintf(stderr,"WARNING: 'trustedpaths' flat file format is deprecated in favor of path definitions in local.conf" ZT_EOL_S);
+			char buf[1024];
+			while (fgets(buf,sizeof(buf),trustpaths)) {
+				int fno = 0;
+				char *saveptr = (char *)0;
+				uint64_t trustedPathId = 0;
+				InetAddress trustedPathNetwork;
+				for(char *f=Utils::stok(buf,"=\r\n \t",&saveptr);(f);f=Utils::stok((char *)0,"=\r\n \t",&saveptr)) {
+					if (fno == 0) {
+						trustedPathId = Utils::hexStrToU64(f);
+					} else if (fno == 1) {
+						trustedPathNetwork = InetAddress(f);
+					} else break;
+					++fno;
+				}
+				if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.netmaskBits() > 0) ) {
+					ppc[trustedPathNetwork].trustedPathId = trustedPathId;
+					ppc[trustedPathNetwork].mtu = 0; // use default
+				}
+			}
+			fclose(trustpaths);
+		}
+
+		// Read local config file
+		Mutex::Lock _l2(_localConfig_m);
+		std::string lcbuf;
+		if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S "local.conf").c_str(),lcbuf)) {
+			try {
+				_localConfig = OSUtils::jsonParse(lcbuf);
+				if (!_localConfig.is_object()) {
+					fprintf(stderr,"WARNING: unable to parse local.conf (root element is not a JSON object)" ZT_EOL_S);
+				}
+			} catch ( ... ) {
+				fprintf(stderr,"WARNING: unable to parse local.conf (invalid JSON)" ZT_EOL_S);
+			}
+		}
+
+		// Get any trusted paths in local.conf (we'll parse the rest of physical[] elsewhere)
+		json &physical = _localConfig["physical"];
+		if (physical.is_object()) {
+			for(json::iterator phy(physical.begin());phy!=physical.end();++phy) {
+				InetAddress net(OSUtils::jsonString(phy.key(),"").c_str());
+				if (net) {
+					if (phy.value().is_object()) {
+						uint64_t tpid;
+						if ((tpid = OSUtils::jsonInt(phy.value()["trustedPathId"],0ULL)) != 0ULL) {
+							if ((net.ss_family == AF_INET)||(net.ss_family == AF_INET6))
+								ppc[net].trustedPathId = tpid;
+						}
+						ppc[net].mtu = (int)OSUtils::jsonInt(phy.value()["mtu"],0ULL); // 0 means use default
+					}
+				}
+			}
+		}
+
+		json &settings = _localConfig["settings"];
+		if (settings.is_object()) {
+			// Allow controller DB path to be put somewhere else
+			const std::string cdbp(OSUtils::jsonString(settings["controllerDbPath"],""));
+			if (cdbp.length() > 0)
+				_controllerDbPath = cdbp;
+
+			// Bind to wildcard instead of to specific interfaces (disables full tunnel capability)
+			json &bind = settings["bind"];
+			if (bind.is_array()) {
+				for(unsigned long i=0;i<bind.size();++i) {
+					const std::string ips(OSUtils::jsonString(bind[i],""));
+					if (ips.length() > 0) {
+						InetAddress ip(ips.c_str());
+						if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6))
+							explicitBind.push_back(ip);
+					}
+				}
+			}
+		}
+
+		// Set trusted paths if there are any
+		if (ppc.size() > 0) {
+			for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::iterator i(ppc.begin());i!=ppc.end();++i)
+				_node->setPhysicalPathConfiguration(reinterpret_cast<const struct sockaddr_storage *>(&(i->first)),&(i->second));
+		}
+	}
+
 	virtual ReasonForTermination reasonForTermination() const
 	{
 		Mutex::Lock _l(_termReason_m);
@@ -1082,16 +1179,6 @@ public:
 #ifdef __SYNOLOGY__
 		// Authenticate via Synology's built-in cgi script
 		if (!isAuth) {
-			/*
-			fprintf(stderr, "path = %s\n", path.c_str());
-			fprintf(stderr, "headers.size=%d\n", headers.size());
-			std::map<std::string, std::string>::const_iterator it(headers.begin());
-			while(it != headers.end()) {
-				fprintf(stderr,"header[%s] = %s\n", (it->first).c_str(), (it->second).c_str());
-				it++;
-			}
-			*/
-			// parse out url args
 			int synotoken_pos = path.find("SynoToken");
 			int argpos = path.find("?");
 			if(synotoken_pos != std::string::npos && argpos != std::string::npos) {
@@ -1104,10 +1191,6 @@ public:
 				setenv("HTTP_COOKIE", cookie_val.c_str(), true);
 				setenv("HTTP_X_SYNO_TOKEN", synotoken_val.c_str(), true);
 				setenv("REMOTE_ADDR", ah2->second.c_str(),true);
-					//fprintf(stderr, "HTTP_COOKIE: %s\n",std::getenv ("HTTP_COOKIE"));
-					//fprintf(stderr, "HTTP_X_SYNO_TOKEN: %s\n",std::getenv ("HTTP_X_SYNO_TOKEN"));
-					//fprintf(stderr, "REMOTE_ADDR: %s\n",std::getenv ("REMOTE_ADDR"));
-				// check synology web auth
 				char user[256], buf[1024];
 				FILE *fp = NULL;
 				bzero(user, 256);
@@ -1153,6 +1236,23 @@ public:
 					json &settings = res["config"]["settings"];
 					settings["primaryPort"] = OSUtils::jsonInt(settings["primaryPort"],(uint64_t)_primaryPort) & 0xffff;
 					settings["allowTcpFallbackRelay"] = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],_allowTcpFallbackRelay);
+
+					if (_multipathMode) {
+						json &multipathConfig = res["multipath"];
+						ZT_PeerList *pl = _node->peers();
+						char peerAddrStr[256];
+						if (pl) {
+							for(unsigned long i=0;i<pl->peerCount;++i) {
+								if (pl->peers[i].hadAggregateLink) {
+									nlohmann::json pj;
+									_peerAggregateLinkToJson(pj,&(pl->peers[i]));
+									OSUtils::ztsnprintf(peerAddrStr,sizeof(peerAddrStr),"%.10llx",pl->peers[i].address);
+									multipathConfig[peerAddrStr] = (pj);
+								}
+							}
+						}
+					}
+
 #ifdef ZT_USE_MINIUPNPC
 					settings["portMappingEnabled"] = OSUtils::jsonBool(settings["portMappingEnabled"],true);
 #else
@@ -1481,6 +1581,16 @@ public:
 
 		_primaryPort = (unsigned int)OSUtils::jsonInt(settings["primaryPort"],(uint64_t)_primaryPort) & 0xffff;
 		_allowTcpFallbackRelay = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],true);
+		_secondaryPort = (unsigned int)OSUtils::jsonInt(settings["secondaryPort"],0);
+		_tertiaryPort = (unsigned int)OSUtils::jsonInt(settings["tertiaryPort"],0);
+		if (_secondaryPort != 0 || _tertiaryPort != 0) {
+			fprintf(stderr,"WARNING: using manually-specified ports. This can cause NAT issues." ZT_EOL_S);
+		}
+		_multipathMode = (unsigned int)OSUtils::jsonInt(settings["multipathMode"],0);
+		if (_multipathMode != 0 && _allowTcpFallbackRelay) {
+			fprintf(stderr,"WARNING: multipathMode cannot be used with allowTcpFallbackRelay. Disabling allowTcpFallbackRelay" ZT_EOL_S);
+			_allowTcpFallbackRelay = false;
+		}
 		_portMappingEnabled = OSUtils::jsonBool(settings["portMappingEnabled"],true);
 
 #ifndef ZT_SDK
@@ -1518,6 +1628,47 @@ public:
 		}
 	}
 
+#if ZT_VAULT_SUPPORT
+		json &vault = settings["vault"];
+		if (vault.is_object()) {
+			const std::string url(OSUtils::jsonString(vault["vaultURL"], "").c_str());
+			if (!url.empty()) {
+				_vaultURL = url;
+			}
+
+			const std::string token(OSUtils::jsonString(vault["vaultToken"], "").c_str());
+			if (!token.empty()) {
+				_vaultToken = token;
+			}
+
+			const std::string path(OSUtils::jsonString(vault["vaultPath"], "").c_str());
+			if (!path.empty()) {
+				_vaultPath = path;
+			}
+		}
+
+		// also check environment variables for values.  Environment variables
+		// will override local.conf variables
+		const std::string envURL(getenv("VAULT_ADDR"));
+		if (!envURL.empty()) {
+			_vaultURL = envURL;
+		}
+
+		const std::string envToken(getenv("VAULT_TOKEN"));
+		if (!envToken.empty()) {
+			_vaultToken = envToken;
+		}
+
+		const std::string envPath(getenv("VAULT_PATH"));
+		if (!envPath.empty()) {
+			_vaultPath = envPath;
+		}
+
+		if (!_vaultURL.empty() && !_vaultToken.empty()) {
+			_vaultEnabled = true;
+		}
+#endif
+
 	// Checks if a managed IP or route target is allowed
 	bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &target)
 	{
@@ -1632,6 +1783,18 @@ public:
 				const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
 				const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
 
+				InetAddress *src = NULL;
+				for (unsigned int j=0; j<n.config.assignedAddressCount; ++j) {
+					const InetAddress *const tmp = reinterpret_cast<const InetAddress *>(&(n.config.assignedAddresses[j]));
+					if (target->isV4() && tmp->isV4()) {
+						src = reinterpret_cast<InetAddress *>(&(n.config.assignedAddresses[j]));
+						break;
+					} else if (target->isV6() && tmp->isV6()) {
+						src = reinterpret_cast<InetAddress *>(&(n.config.assignedAddresses[j]));
+						break;
+					}
+				}
+
 				if ( (!checkIfManagedIsAllowed(n,*target)) || ((via->ss_family == target->ss_family)&&(matchIpOnly(myIps,*via))) )
 					continue;
 
@@ -1659,7 +1822,7 @@ public:
 					continue;
 
 				// Add and apply new routes
-				n.managedRoutes.push_back(SharedPtr<ManagedRoute>(new ManagedRoute(*target,*via,tapdev)));
+				n.managedRoutes.push_back(SharedPtr<ManagedRoute>(new ManagedRoute(*target,*via,*src,tapdev)));
 				if (!n.managedRoutes.back()->sync())
 					n.managedRoutes.pop_back();
 #endif
@@ -1922,7 +2085,7 @@ public:
 	inline void phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) {}
 	inline void phyOnUnixClose(PhySocket *sock,void **uptr) {}
 	inline void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) {}
-	inline void phyOnUnixWritable(PhySocket *sock,void **uptr,bool lwip_invoked) {}
+	inline void phyOnUnixWritable(PhySocket *sock,void **uptr) {}
 
 	inline int nodeVirtualNetworkConfigFunction(uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwc)
 	{
@@ -2082,8 +2245,88 @@ public:
 		}
 	}
 
+#if ZT_VAULT_SUPPORT
+	inline bool nodeVaultPutIdentity(enum ZT_StateObjectType type, const void *data, int len)
+	{
+		bool retval = false;
+		if (type != ZT_STATE_OBJECT_IDENTITY_PUBLIC && type != ZT_STATE_OBJECT_IDENTITY_SECRET) {
+			return retval;
+		}
+
+		CURL *curl = curl_easy_init();
+		if (curl) {
+			char token[512] = { 0 };
+			snprintf(token, sizeof(token), "X-Vault-Token: %s", _vaultToken.c_str());
+
+			struct curl_slist *chunk = NULL;
+			chunk = curl_slist_append(chunk, token);
+
+
+			char content_type[512] = { 0 };
+			snprintf(content_type, sizeof(content_type), "Content-Type: application/json");
+
+			chunk = curl_slist_append(chunk, content_type);
+
+			curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
+
+			char url[2048] = { 0 };
+			snprintf(url, sizeof(url), "%s/v1/%s", _vaultURL.c_str(), _vaultPath.c_str());
+
+			curl_easy_setopt(curl, CURLOPT_URL, url);
+
+			json d = json::object();
+			if (type == ZT_STATE_OBJECT_IDENTITY_PUBLIC) {
+				std::string key((const char*)data, len);
+				d["public"] = key;
+			}
+			else if (type == ZT_STATE_OBJECT_IDENTITY_SECRET) {
+				std::string key((const char*)data, len);
+				d["secret"] = key;
+			}
+
+			if (!d.empty()) {
+				std::string post = d.dump();
+
+				if (!post.empty()) {
+					curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post.c_str());
+					curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post.length());
+
+#ifndef NDEBUG
+					curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+#endif
+
+					CURLcode res = curl_easy_perform(curl);
+					if (res == CURLE_OK) {
+						long response_code = 0;
+						curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
+						if (response_code == 200 || response_code == 204) {
+							retval = true;
+						}
+					}
+				}
+			}
+
+			curl_easy_cleanup(curl);
+			curl = NULL;
+			curl_slist_free_all(chunk);
+			chunk = NULL;
+		}
+
+		return retval;
+	}
+#endif
+
 	inline void nodeStatePutFunction(enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len)
 	{
+#if ZT_VAULT_SUPPORT
+		if (_vaultEnabled && (type == ZT_STATE_OBJECT_IDENTITY_SECRET || type == ZT_STATE_OBJECT_IDENTITY_PUBLIC)) {
+			if (nodeVaultPutIdentity(type, data, len)) {
+				// value successfully written to Vault
+				return;
+			}
+			// else fallback to disk
+		}
+#endif
 		char p[1024];
 		FILE *f;
 		bool secure = false;
@@ -2150,8 +2393,93 @@ public:
 		}
 	}
 
+#if ZT_VAULT_SUPPORT
+	inline int nodeVaultGetIdentity(enum ZT_StateObjectType type, void *data, unsigned int maxlen)
+	{
+		if (type != ZT_STATE_OBJECT_IDENTITY_SECRET && type != ZT_STATE_OBJECT_IDENTITY_PUBLIC) {
+			return -1;
+		}
+
+		int ret = -1;
+		CURL *curl = curl_easy_init();
+		if (curl) {
+			char token[512] = { 0 };
+			snprintf(token, sizeof(token), "X-Vault-Token: %s", _vaultToken.c_str());
+
+			struct curl_slist *chunk = NULL;
+			chunk = curl_slist_append(chunk, token);
+			curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
+
+			char url[2048] = { 0 };
+			snprintf(url, sizeof(url), "%s/v1/%s", _vaultURL.c_str(), _vaultPath.c_str());
+
+			curl_easy_setopt(curl, CURLOPT_URL, url);
+
+			std::string response;
+			std::string res_headers;
+
+			curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curlResponseWrite);
+			curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
+			curl_easy_setopt(curl, CURLOPT_HEADERDATA, &res_headers);
+
+#ifndef NDEBUG
+			curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+#endif
+
+			CURLcode res = curl_easy_perform(curl);
+
+			if (res == CURLE_OK) {
+				long response_code = 0;
+				curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
+				if (response_code == 200) {
+					try {
+						json payload = json::parse(response);
+						if (!payload["data"].is_null()) {
+							json &d = payload["data"];
+							if (type == ZT_STATE_OBJECT_IDENTITY_SECRET) {
+								std::string secret = OSUtils::jsonString(d["secret"],"");
+
+								if (!secret.empty()) {
+									ret = (int)secret.length();
+									memcpy(data, secret.c_str(), ret);
+								}
+							}
+							else if (type == ZT_STATE_OBJECT_IDENTITY_PUBLIC) {
+								std::string pub = OSUtils::jsonString(d["public"],"");
+
+								if (!pub.empty()) {
+									ret = (int)pub.length();
+									memcpy(data, pub.c_str(), ret);
+								}
+							}
+						}
+					}
+					catch (...) {
+						ret = -1;
+					}
+				}
+			}
+
+			curl_easy_cleanup(curl);
+			curl = NULL;
+			curl_slist_free_all(chunk);
+			chunk = NULL;
+		}
+		return ret;
+	}
+#endif
+
 	inline int nodeStateGetFunction(enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen)
 	{
+#if ZT_VAULT_SUPPORT
+		if (_vaultEnabled && (type == ZT_STATE_OBJECT_IDENTITY_SECRET || type == ZT_STATE_OBJECT_IDENTITY_PUBLIC) ) {
+			int retval = nodeVaultGetIdentity(type, data, maxlen);
+			if (retval >= 0)
+				return retval;
+
+			// else continue file based lookup
+		}
+#endif
 		char p[4096];
 		switch(type) {
 			case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
@@ -2179,6 +2507,17 @@ public:
 		if (f) {
 			int n = (int)fread(data,1,maxlen,f);
 			fclose(f);
+#if ZT_VAULT_SUPPORT
+			if (_vaultEnabled && (type == ZT_STATE_OBJECT_IDENTITY_SECRET || type == ZT_STATE_OBJECT_IDENTITY_PUBLIC)) {
+				// If we've gotten here while Vault is enabled, Vault does not know the key and it's been
+				// read from disk instead.
+				//
+				// We should put the value in Vault and remove the local file.
+				if (nodeVaultPutIdentity(type, data, n)) {
+					unlink(p);
+				}
+			}
+#endif
 			if (n >= 0)
 				return n;
 		}

+ 1 - 1
version.h

@@ -40,7 +40,7 @@
 /**
  * Revision
  */
-#define ZEROTIER_ONE_VERSION_REVISION 12
+#define ZEROTIER_ONE_VERSION_REVISION 13
 
 /**
  * Build version

部分文件因为文件数量过多而无法显示