summaryrefslogtreecommitdiff
path: root/osdep
diff options
context:
space:
mode:
Diffstat (limited to 'osdep')
-rw-r--r--osdep/Http.cpp264
-rw-r--r--osdep/Http.hpp134
-rw-r--r--osdep/OSUtils.cpp3
-rw-r--r--osdep/OSUtils.hpp9
-rw-r--r--osdep/Phy.hpp8
5 files changed, 414 insertions, 4 deletions
diff --git a/osdep/Http.cpp b/osdep/Http.cpp
new file mode 100644
index 00000000..57efc556
--- /dev/null
+++ b/osdep/Http.cpp
@@ -0,0 +1,264 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "Http.hpp"
+#include "Phy.hpp"
+#include "OSUtils.hpp"
+#include "../node/Constants.hpp"
+#include "../node/Utils.hpp"
+#include "../ext/http-parser/http_parser.h"
+
+namespace ZeroTier {
+
+namespace {
+
+static int ShttpOnMessageBegin(http_parser *parser);
+static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length);
+static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length);
+static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length);
+static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length);
+static int ShttpOnHeadersComplete(http_parser *parser);
+static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length);
+static int ShttpOnMessageComplete(http_parser *parser);
+static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
+ ShttpOnMessageBegin,
+ ShttpOnUrl,
+ ShttpOnStatus,
+ ShttpOnHeaderField,
+ ShttpOnValue,
+ ShttpOnHeadersComplete,
+ ShttpOnBody,
+ ShttpOnMessageComplete
+};
+
+struct HttpPhyHandler
+{
+ // not used
+ inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len) {}
+ inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) {}
+
+ inline void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)
+ {
+ if (success) {
+ phy->tcpSetNotifyWritable(sock,true);
+ } else {
+ *responseBody = "connection failed";
+ error = true;
+ done = true;
+ }
+ }
+
+ inline void phyOnTcpClose(PhySocket *sock,void **uptr)
+ {
+ done = true;
+ }
+
+ inline void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
+ {
+ lastActivity = OSUtils::now();
+ http_parser_execute(&parser,&HTTP_PARSER_SETTINGS,(const char *)data,len);
+ if ((parser.upgrade)||(parser.http_errno != HPE_OK))
+ phy->close(sock);
+ }
+
+ inline void phyOnTcpWritable(PhySocket *sock,void **uptr)
+ {
+ if (writePtr < writeSize) {
+ long n = phy->tcpSend(sock,writeBuf + writePtr,writeSize - writePtr,true);
+ if (n > 0)
+ writePtr += n;
+ }
+ if (writePtr >= writeSize)
+ phy->tcpSetNotifyWritable(sock,false);
+ }
+
+ http_parser parser;
+ std::string currentHeaderField;
+ std::string currentHeaderValue;
+ unsigned long messageSize;
+ unsigned long writePtr;
+ uint64_t lastActivity;
+ unsigned long writeSize;
+ char writeBuf[32768];
+
+ unsigned long maxResponseSize;
+ std::map<std::string,std::string> *responseHeaders;
+ std::string *responseBody;
+ bool error;
+ bool done;
+
+ Phy<HttpPhyHandler *> *phy;
+ PhySocket *sock;
+};
+
+static int ShttpOnMessageBegin(http_parser *parser)
+{
+ return 0;
+}
+static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length)
+{
+ return 0;
+}
+static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length)
+{
+ HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
+ hh->messageSize += length;
+ if (hh->messageSize > hh->maxResponseSize)
+ return -1;
+ return 0;
+}
+static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length)
+{
+ HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
+ hh->messageSize += length;
+ if (hh->messageSize > hh->maxResponseSize)
+ return -1;
+ if ((hh->currentHeaderField.length())&&(hh->currentHeaderValue.length())) {
+ (*hh->responseHeaders)[hh->currentHeaderField] = hh->currentHeaderValue;
+ hh->currentHeaderField.assign("",0);
+ hh->currentHeaderValue.assign("",0);
+ }
+ for(size_t i=0;i<length;++i)
+ hh->currentHeaderField.push_back(OSUtils::toLower(ptr[i]));
+ return 0;
+}
+static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length)
+{
+ HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
+ hh->messageSize += length;
+ if (hh->messageSize > hh->maxResponseSize)
+ return -1;
+ hh->currentHeaderValue.append(ptr,length);
+ return 0;
+}
+static int ShttpOnHeadersComplete(http_parser *parser)
+{
+ HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
+ if ((hh->currentHeaderField.length())&&(hh->currentHeaderValue.length()))
+ (*hh->responseHeaders)[hh->currentHeaderField] = hh->currentHeaderValue;
+ return 0;
+}
+static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length)
+{
+ HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
+ hh->messageSize += length;
+ if (hh->messageSize > hh->maxResponseSize)
+ return -1;
+ hh->responseBody->append(ptr,length);
+ return 0;
+}
+static int ShttpOnMessageComplete(http_parser *parser)
+{
+ HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
+ hh->phy->close(hh->sock);
+ return 0;
+}
+
+} // anonymous namespace
+
+unsigned int Http::_do(
+ const char *method,
+ unsigned long maxResponseSize,
+ unsigned long timeout,
+ const struct sockaddr *remoteAddress,
+ const char *path,
+ const std::map<std::string,std::string> &requestHeaders,
+ const void *requestBody,
+ unsigned long requestBodyLength,
+ std::map<std::string,std::string> &responseHeaders,
+ std::string &responseBody)
+{
+ try {
+ responseHeaders.clear();
+ responseBody.assign("",0);
+
+ HttpPhyHandler handler;
+
+ http_parser_init(&(handler.parser),HTTP_RESPONSE);
+ handler.parser.data = (void *)&handler;
+ handler.messageSize = 0;
+ handler.writePtr = 0;
+ handler.lastActivity = OSUtils::now();
+
+ try {
+ handler.writeSize = Utils::snprintf(handler.writeBuf,sizeof(handler.writeBuf),"GET %s HTTP/1.1\r\n",path);
+ for(std::map<std::string,std::string>::const_iterator h(requestHeaders.begin());h!=requestHeaders.end();++h)
+ handler.writeSize += Utils::snprintf(handler.writeBuf + handler.writeSize,sizeof(handler.writeBuf) - handler.writeSize,"%s: %s\r\n",h->first.c_str(),h->second.c_str());
+ handler.writeSize += Utils::snprintf(handler.writeBuf + handler.writeSize,sizeof(handler.writeBuf) - handler.writeSize,"\r\n");
+ if ((requestBody)&&(requestBodyLength)) {
+ if ((handler.writeSize + requestBodyLength) > sizeof(handler.writeBuf)) {
+ responseBody = "request too large";
+ return 0;
+ }
+ memcpy(handler.writeBuf + handler.writeSize,requestBody,requestBodyLength);
+ handler.writeSize += requestBodyLength;
+ }
+ } catch ( ... ) {
+ responseBody = "request too large";
+ return 0;
+ }
+
+ handler.maxResponseSize = maxResponseSize;
+ handler.responseHeaders = &responseHeaders;
+ handler.responseBody = &responseBody;
+ handler.error = false;
+ handler.done = false;
+
+ Phy<HttpPhyHandler *> phy(&handler,true);
+
+ bool instantConnect = false;
+ handler.phy = &phy;
+ handler.sock = phy.tcpConnect((const struct sockaddr *)remoteAddress,instantConnect,(void *)0,true);
+ if (!handler.sock) {
+ responseBody = "connection failed (2)";
+ return 0;
+ }
+
+ while (!handler.done) {
+ phy.poll(timeout / 2);
+ if ((timeout)&&((unsigned long)(OSUtils::now() - handler.lastActivity) > timeout)) {
+ phy.close(handler.sock);
+ responseBody = "timed out";
+ return 0;
+ }
+ }
+
+ return ((handler.error) ? 0 : ((handler.parser.http_errno != HPE_OK) ? 0 : handler.parser.status_code));
+ } catch (std::exception &exc) {
+ responseBody = exc.what();
+ return 0;
+ } catch ( ... ) {
+ responseBody = "unknown exception";
+ return 0;
+ }
+}
+
+} // namespace ZeroTier
diff --git a/osdep/Http.hpp b/osdep/Http.hpp
new file mode 100644
index 00000000..c73cc112
--- /dev/null
+++ b/osdep/Http.hpp
@@ -0,0 +1,134 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015 ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef ZT_HTTP_HPP
+#define ZT_HTTP_HPP
+
+#include <string>
+#include <map>
+#include <stdexcept>
+
+#if defined(_WIN32) || defined(_WIN64)
+#include <WinSock2.h>
+#include <WS2tcpip.h>
+#include <Windows.h>
+#else
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#endif
+
+namespace ZeroTier {
+
+/**
+ * Simple synchronous HTTP client used for updater and cli
+ */
+class Http
+{
+public:
+ /**
+ * Make HTTP GET request
+ *
+ * The caller must set all headers, including Host.
+ *
+ * @return HTTP status code or 0 on error (responseBody will contain error message)
+ */
+ static inline unsigned int GET(
+ unsigned long maxResponseSize,
+ unsigned long timeout,
+ const struct sockaddr *remoteAddress,
+ const char *path,
+ const std::map<std::string,std::string> &requestHeaders,
+ std::map<std::string,std::string> &responseHeaders,
+ std::string &responseBody)
+ {
+ return _do(
+ "GET",
+ maxResponseSize,
+ timeout,
+ remoteAddress,
+ path,
+ requestHeaders,
+ (const void *)0,
+ 0,
+ responseHeaders,
+ responseBody);
+ }
+
+ /**
+ * Make HTTP POST request
+ *
+ * It is the responsibility of the caller to set all headers. With POST, the
+ * Content-Length and Content-Type headers must be set or the POST will not
+ * work.
+ *
+ * @return HTTP status code or 0 on error (responseBody will contain error message)
+ */
+ static inline unsigned int POST(
+ unsigned long maxResponseSize,
+ unsigned long timeout,
+ const struct sockaddr *remoteAddress,
+ const char *path,
+ const std::map<std::string,std::string> &requestHeaders,
+ const void *postData,
+ unsigned long postDataLength,
+ std::map<std::string,std::string> &responseHeaders,
+ std::string &responseBody)
+ {
+ return _do(
+ "POST",
+ maxResponseSize,
+ timeout,
+ remoteAddress,
+ path,
+ requestHeaders,
+ postData,
+ postDataLength,
+ responseHeaders,
+ responseBody);
+ }
+
+private:
+ static unsigned int _do(
+ const char *method,
+ unsigned long maxResponseSize,
+ unsigned long timeout,
+ const struct sockaddr *remoteAddress,
+ const char *path,
+ const std::map<std::string,std::string> &requestHeaders,
+ const void *requestBody,
+ unsigned long requestBodyLength,
+ std::map<std::string,std::string> &responseHeaders,
+ std::string &responseBody);
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp
index 60e6d6ba..a8639a12 100644
--- a/osdep/OSUtils.cpp
+++ b/osdep/OSUtils.cpp
@@ -206,4 +206,7 @@ bool OSUtils::writeFile(const char *path,const void *buf,unsigned int len)
return false;
}
+// Used to convert HTTP header names to ASCII lower case
+const unsigned char OSUtils::TOLOWER_TABLE[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
+
} // namespace ZeroTier
diff --git a/osdep/OSUtils.hpp b/osdep/OSUtils.hpp
index fe054ba2..0cf4916b 100644
--- a/osdep/OSUtils.hpp
+++ b/osdep/OSUtils.hpp
@@ -220,6 +220,15 @@ public:
* @return True if entire file was successfully written
*/
static inline bool writeFile(const char *path,const std::string &s) { return writeFile(path,s.data(),(unsigned int)s.length()); }
+
+ /**
+ * @param c ASCII character to convert
+ * @return Lower case ASCII character or unchanged if not a letter
+ */
+ static inline char toLower(char c) throw() { return (char)OSUtils::TOLOWER_TABLE[(unsigned long)c]; }
+
+private:
+ static const unsigned char TOLOWER_TABLE[256];
};
} // namespace ZeroTier
diff --git a/osdep/Phy.hpp b/osdep/Phy.hpp
index e245813b..9bbfe43f 100644
--- a/osdep/Phy.hpp
+++ b/osdep/Phy.hpp
@@ -443,7 +443,7 @@ public:
#if defined(_WIN32) || defined(_WIN64)
{
BOOL f;
- f = TRUE; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f));
+ if (remoteAddress->sa_family == AF_INET6) { f = TRUE; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); }
f = TRUE; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f));
f = (_noDelay ? TRUE : FALSE); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f));
u_long iMode=1;
@@ -452,7 +452,7 @@ public:
#else
{
int f;
- f = 1; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f));
+ if (remoteAddress->sa_family == AF_INET6) { f = 1; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); }
f = 1; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f));
f = (_noDelay ? 1 : 0); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f));
fcntl(s,F_SETFL,O_NONBLOCK);
@@ -621,9 +621,9 @@ public:
case ZT_PHY_SOCKET_TCP_OUT_PENDING:
#if defined(_WIN32) || defined(_WIN64)
- if (FD_ISSET(s->sock,&efds))
+ if (FD_ISSET(s->sock,&efds)) {
this->close((PhySocket *)&(*s),true);
- else // ... if
+ } else // ... if
#endif
if (FD_ISSET(s->sock,&wfds)) {
socklen_t slen = sizeof(ss);