diff options
-rw-r--r-- | make-freebsd.mk | 2 | ||||
-rw-r--r-- | make-linux.mk | 2 | ||||
-rw-r--r-- | make-mac.mk | 2 | ||||
-rw-r--r-- | objects.mk | 1 | ||||
-rw-r--r-- | one.cpp | 16 | ||||
-rw-r--r-- | osdep/Http.cpp | 264 | ||||
-rw-r--r-- | osdep/Http.hpp | 134 | ||||
-rw-r--r-- | osdep/OSUtils.cpp | 3 | ||||
-rw-r--r-- | osdep/OSUtils.hpp | 9 | ||||
-rw-r--r-- | osdep/Phy.hpp | 8 | ||||
-rw-r--r-- | selftest.cpp | 47 | ||||
-rw-r--r-- | service/OneService.cpp | 8 | ||||
-rw-r--r-- | updater/HttpClient.cpp | 590 | ||||
-rw-r--r-- | updater/HttpClient.hpp | 110 |
14 files changed, 470 insertions, 726 deletions
diff --git a/make-freebsd.mk b/make-freebsd.mk index c381a0a8..5ee2a0a3 100644 --- a/make-freebsd.mk +++ b/make-freebsd.mk @@ -71,7 +71,7 @@ selftest: $(OBJS) selftest.o # ./buildinstaller.sh clean: - rm -rf *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o build-* zerotier-one zerotier-idtool zerotier-selftest ZeroTierOneInstaller-* + rm -rf *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o build-* zerotier-one zerotier-idtool zerotier-selftest ZeroTierOneInstaller-* debug: FORCE make -j 4 ZT_DEBUG=1 diff --git a/make-linux.mk b/make-linux.mk index 082ba07d..63111fdf 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -76,7 +76,7 @@ installer: one FORCE ./buildinstaller.sh clean: - rm -rf *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o zerotier-one zerotier-idtool zerotier-selftest build-* ZeroTierOneInstaller-* *.deb *.rpm + rm -rf *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o zerotier-one zerotier-idtool zerotier-selftest build-* ZeroTierOneInstaller-* *.deb *.rpm debug: FORCE make -j 4 ZT_DEBUG=1 diff --git a/make-mac.mk b/make-mac.mk index 2a38714a..425d9ded 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -75,7 +75,7 @@ selftest: $(OBJS) selftest.o # $(CODESIGN) -vvv "build-ZeroTierUI-release/ZeroTier One.app" clean: - rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o zerotier-one zerotier-idtool zerotier-selftest ZeroTierOneInstaller-* + rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o zerotier-one zerotier-idtool zerotier-selftest ZeroTierOneInstaller-* # For our use -- builds official signed binary, packages in installer and download DMG official: FORCE @@ -23,6 +23,7 @@ OBJS=\ node/Switch.o \ node/Topology.o \ node/Utils.o \ + osdep/Http.o \ osdep/OSUtils.o \ service/ControlPlane.o \ service/OneService.o @@ -76,6 +76,18 @@ using namespace ZeroTier; static OneService *volatile zt1Service = (OneService *)0; /****************************************************************************/ +/* zerotier-cli personality */ +/****************************************************************************/ + +#ifdef __WINDOWS__ +static int cli(int argc, _TCHAR* argv[]) +#else +static int cli(int argc,char **argv) +#endif +{ +} + +/****************************************************************************/ /* zerotier-idtool personality */ /****************************************************************************/ @@ -107,9 +119,9 @@ static Identity getIdFromArg(char *arg) } #ifdef __WINDOWS__ -int idtool(int argc, _TCHAR* argv[]) +static int idtool(int argc, _TCHAR* argv[]) #else -int idtool(int argc,char **argv) +static int idtool(int argc,char **argv) #endif { if (argc < 2) { 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); diff --git a/selftest.cpp b/selftest.cpp index 8c8c6fa1..de952cb1 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -25,8 +25,6 @@ * LLC. Start here: http://www.zerotier.com/ */ -#define ZT_TEST_PHY - #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -55,9 +53,8 @@ #include "node/Node.hpp" #include "osdep/OSUtils.hpp" -#ifdef ZT_TEST_PHY #include "osdep/Phy.hpp" -#endif +#include "osdep/Http.hpp" #ifdef ZT_ENABLE_NETWORK_CONTROLLER #include "controller/SqliteNetworkController.hpp" @@ -581,7 +578,6 @@ static int testOther() return 0; } -#ifdef ZT_TEST_PHY #define ZT_TEST_PHY_NUM_UDP_PACKETS 10000 #define ZT_TEST_PHY_UDP_PACKET_SIZE 1000 #define ZT_TEST_PHY_NUM_VALID_TCP_CONNECTS 10 @@ -641,11 +637,8 @@ struct TestPhyHandlers } } }; -#endif // ZT_TEST_PHY - static int testPhy() { -#ifdef ZT_TEST_PHY char udpTestPayload[ZT_TEST_PHY_UDP_PACKET_SIZE]; memset(udpTestPayload,0xff,sizeof(udpTestPayload)); @@ -720,11 +713,11 @@ static int testPhy() } else { std::cout << "got " << phyTestTcpConnectSuccessCount << " connect successes, " << phyTestTcpConnectFailCount << " failures, and " << phyTestTcpByteCount << " bytes, OK" << std::endl; } -#endif // ZT_TEST_PHY + return 0; } -static int testSqliteNetconfMaster() +static int testSqliteNetworkController() { #ifdef ZT_ENABLE_NETWORK_CONTROLLER try { @@ -745,6 +738,37 @@ static int testSqliteNetconfMaster() return 0; } +static int testHttp() +{ + std::map<std::string,std::string> requestHeaders,responseHeaders; + std::string responseBody; + + InetAddress downloadZerotierDotCom("142.4.214.72/80"); + + std::cout << "[http] GET http://download.zerotier.com/dev/1k @" << downloadZerotierDotCom.toString() << " ... "; std::cout.flush(); + requestHeaders["Host"] = "download.zerotier.com"; + unsigned int sc = Http::GET(1024 * 1024 * 16,60000,reinterpret_cast<const struct sockaddr *>(&downloadZerotierDotCom),"/dev/1k",requestHeaders,responseHeaders,responseBody); + std::cout << sc << " " << responseBody.length() << " bytes "; + if (sc == 0) + std::cout << "ERROR: " << responseBody << std::endl; + else std::cout << "DONE" << std::endl; + + std::cout << "[http] GET http://download.zerotier.com/dev/4m @" << downloadZerotierDotCom.toString() << " ... "; std::cout.flush(); + requestHeaders["Host"] = "download.zerotier.com"; + sc = Http::GET(1024 * 1024 * 16,60000,reinterpret_cast<const struct sockaddr *>(&downloadZerotierDotCom),"/dev/4m",requestHeaders,responseHeaders,responseBody); + std::cout << sc << " " << responseBody.length() << " bytes "; + if (sc == 0) + std::cout << "ERROR: " << responseBody << std::endl; + else std::cout << "DONE" << std::endl; + + downloadZerotierDotCom = InetAddress("1.0.0.1/1234"); + std::cout << "[http] GET @" << downloadZerotierDotCom.toString() << " ... "; std::cout.flush(); + sc = Http::GET(1024 * 1024 * 16,2500,reinterpret_cast<const struct sockaddr *>(&downloadZerotierDotCom),"/dev/4m",requestHeaders,responseHeaders,responseBody); + std::cout << sc << " (should be 0, time out)" << std::endl; + + return 0; +} + #ifdef __WINDOWS__ int _tmain(int argc, _TCHAR* argv[]) #else @@ -795,7 +819,8 @@ int main(int argc,char **argv) srand((unsigned int)time(0)); r |= testPhy(); - r |= testSqliteNetconfMaster(); + r |= testHttp(); + r |= testSqliteNetworkController(); r |= testCrypto(); r |= testPacket(); r |= testOther(); diff --git a/service/OneService.cpp b/service/OneService.cpp index 1d86b11a..5ab66e72 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -70,9 +70,6 @@ namespace ZeroTier { typedef OSXEthernetTap EthernetTap; } namespace ZeroTier { -// Used to convert HTTP header names to ASCII lower case -static const unsigned char ZT_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 }; - class OneServiceImpl; static int SnodeVirtualNetworkConfigFunction(ZT1_Node *node,void *uptr,uint64_t nwid,enum ZT1_VirtualNetworkConfigOperation op,const ZT1_VirtualNetworkConfig *nwconf); @@ -92,8 +89,7 @@ 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); - -const struct http_parser_settings HTTP_PARSER_SETTINGS = { +static const struct http_parser_settings HTTP_PARSER_SETTINGS = { ShttpOnMessageBegin, ShttpOnUrl, ShttpOnStatus, @@ -719,7 +715,7 @@ static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length) htc->currentHeaderValue.assign("",0); } for(size_t i=0;i<length;++i) - htc->currentHeaderField.push_back((char)ZT_TOLOWER_TABLE[(unsigned int)ptr[i]]); + htc->currentHeaderField.push_back(OSUtils::toLower(ptr[i])); return 0; } static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length) diff --git a/updater/HttpClient.cpp b/updater/HttpClient.cpp deleted file mode 100644 index 1cf78204..00000000 --- a/updater/HttpClient.cpp +++ /dev/null @@ -1,590 +0,0 @@ -/* - * 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 "../node/Constants.hpp" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#ifdef __WINDOWS__ -#include <WinSock2.h> -#include <Windows.h> -#include <winhttp.h> -#include <locale> -#include <codecvt> -#endif // __WINDOWS__ - -#ifdef __UNIX_LIKE__ -#include <unistd.h> -#include <signal.h> -#include <fcntl.h> -#include <sys/select.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/socket.h> -#include <sys/wait.h> -#endif // __UNIX_LIKE__ - -#include <vector> -#include <utility> -#include <algorithm> - -#include "HttpClient.hpp" -#include "Thread.hpp" -#include "OSUtils.hpp" -#include "../node/Utils.hpp" - -namespace ZeroTier { - -#ifdef __UNIX_LIKE__ - -// The *nix implementation calls 'curl' externally rather than linking to it. -// This makes it an optional dependency that can be avoided in tiny systems -// provided you don't want to have automatic software updates... or want to -// do them via another method. - -#ifdef __APPLE__ -// TODO: get proxy configuration -#endif - -// Paths where "curl" may be found on the system -#define NUM_CURL_PATHS 6 -static const char *CURL_PATHS[NUM_CURL_PATHS] = { "/usr/bin/curl","/bin/curl","/usr/local/bin/curl","/usr/sbin/curl","/sbin/curl","/usr/libexec/curl" }; - -// Maximum message length -#define CURL_MAX_MESSAGE_LENGTH (1024 * 1024 * 64) - -// Internal private thread class that performs request, notifies handler, -// and then commits suicide by deleting itself. -class HttpClient_Private_Request -{ -public: - HttpClient_Private_Request(HttpClient *parent,const char *method,const std::string &url,const std::map<std::string,std::string> &headers,unsigned int timeout,void (*handler)(void *,int,const std::string &,const std::string &),void *arg) : - _url(url), - _headers(headers), - _timeout(timeout), - _handler(handler), - _arg(arg), - _parent(parent), - _pid(0), - _cancelled(false) - { - _myThread = Thread::start(this); - } - - ~HttpClient_Private_Request() - { - Mutex::Lock _l(_parent->_requests_m); - _parent->_requests.erase((HttpClient::Request)this); - } - - void threadMain() - { - char *curlArgs[1024]; - char buf[16384]; - fd_set readfds,writefds,errfds; - struct timeval tv; - - std::string curlPath; - for(int i=0;i<NUM_CURL_PATHS;++i) { - if (OSUtils::fileExists(CURL_PATHS[i])) { - curlPath = CURL_PATHS[i]; - break; - } - } - - if (!curlPath.length()) { - _doH(_arg,-1,_url,"unable to locate 'curl' binary in /usr/bin, /bin, /usr/local/bin, /usr/sbin, or /sbin"); - delete this; - return; - } - if (!_url.length()) { - _doH(_arg,-1,_url,"cannot fetch empty URL"); - delete this; - return; - } - - curlArgs[0] = const_cast <char *>(curlPath.c_str()); - curlArgs[1] = const_cast <char *>("-D"); - curlArgs[2] = const_cast <char *>("-"); // append headers before output - int argPtr = 3; - std::vector<std::string> headerArgs; - for(std::map<std::string,std::string>::const_iterator h(_headers.begin());h!=_headers.end();++h) { - headerArgs.push_back(h->first); - headerArgs.back().append(": "); - headerArgs.back().append(h->second); - } - for(std::vector<std::string>::iterator h(headerArgs.begin());h!=headerArgs.end();++h) { - if (argPtr >= (1024 - 4)) // leave room for terminating NULL and URL - break; - curlArgs[argPtr++] = const_cast <char *>("-H"); - curlArgs[argPtr++] = const_cast <char *>(h->c_str()); - } - curlArgs[argPtr++] = const_cast <char *>(_url.c_str()); - curlArgs[argPtr] = (char *)0; - - if (_cancelled) { - delete this; - return; - } - - int curlStdout[2]; - int curlStderr[2]; - ::pipe(curlStdout); - ::pipe(curlStderr); - - _pid = (long)vfork(); - if (_pid < 0) { - // fork() failed - ::close(curlStdout[0]); - ::close(curlStdout[1]); - ::close(curlStderr[0]); - ::close(curlStderr[1]); - _doH(_arg,-1,_url,"unable to fork()"); - delete this; - return; - } else if (_pid > 0) { - // fork() succeeded, in parent process - ::close(curlStdout[1]); - ::close(curlStderr[1]); - fcntl(curlStdout[0],F_SETFL,O_NONBLOCK); - fcntl(curlStderr[0],F_SETFL,O_NONBLOCK); - - int exitCode = -1; - unsigned long long timesOutAt = OSUtils::now() + ((unsigned long long)_timeout * 1000ULL); - bool timedOut = false; - bool tooLong = false; - - while (!_cancelled) { - FD_ZERO(&readfds); - FD_ZERO(&writefds); - FD_ZERO(&errfds); - FD_SET(curlStdout[0],&readfds); - FD_SET(curlStderr[0],&readfds); - FD_SET(curlStdout[0],&errfds); - FD_SET(curlStderr[0],&errfds); - tv.tv_sec = 1; - tv.tv_usec = 0; - select(std::max(curlStdout[0],curlStderr[0])+1,&readfds,&writefds,&errfds,&tv); - - if (FD_ISSET(curlStdout[0],&readfds)) { - int n = (int)::read(curlStdout[0],buf,sizeof(buf)); - if (n > 0) { - _body.append(buf,n); - // Reset timeout when data is read... - timesOutAt = OSUtils::now() + ((unsigned long long)_timeout * 1000ULL); - } else if (n < 0) - break; - if (_body.length() > CURL_MAX_MESSAGE_LENGTH) { - tooLong = true; - break; - } - } - - if (FD_ISSET(curlStderr[0],&readfds)) - ::read(curlStderr[0],buf,sizeof(buf)); - - if (FD_ISSET(curlStdout[0],&errfds)||FD_ISSET(curlStderr[0],&errfds)) - break; - - if (OSUtils::now() >= timesOutAt) { - timedOut = true; - break; - } - - if (waitpid(_pid,&exitCode,WNOHANG) > 0) { - for(;;) { - // Drain output... - int n = (int)::read(curlStdout[0],buf,sizeof(buf)); - if (n <= 0) - break; - else { - _body.append(buf,n); - if (_body.length() > CURL_MAX_MESSAGE_LENGTH) { - tooLong = true; - break; - } - } - } - _pid = 0; - break; - } - } - - if (_pid > 0) { - ::kill(_pid,SIGKILL); - waitpid(_pid,&exitCode,0); - } - _pid = 0; - - ::close(curlStdout[0]); - ::close(curlStderr[0]); - - if (timedOut) - _doH(_arg,-1,_url,"connection timed out"); - else if (tooLong) - _doH(_arg,-1,_url,"response too long"); - else if (exitCode) - _doH(_arg,-1,_url,"connection failed (curl returned non-zero exit code)"); - else { - unsigned long idx = 0; - - // Grab status line and headers, which will prefix output on - // success and will end with an empty line. - std::vector<std::string> headers; - headers.push_back(std::string()); - while (idx < _body.length()) { - char c = _body[idx++]; - if (c == '\n') { - if (!headers.back().length()) { - headers.pop_back(); - break; - } else headers.push_back(std::string()); - } else if (c != '\r') - headers.back().push_back(c); - } - if (headers.empty()||(!headers.front().length())) { - _doH(_arg,-1,_url,"HTTP response empty"); - delete this; - return; - } - - // Parse first line -- HTTP status code and response - size_t scPos = headers.front().find(' '); - if (scPos == std::string::npos) { - _doH(_arg,-1,_url,"invalid HTTP response (no status line)"); - delete this; - return; - } - ++scPos; - unsigned int rcode = Utils::strToUInt(headers.front().substr(scPos,3).c_str()); - if ((!rcode)||(rcode > 999)) { - _doH(_arg,-1,_url,"invalid HTTP response (invalid response code)"); - delete this; - return; - } - - // Serve up the resulting data to the handler - if (rcode == 200) - _doH(_arg,rcode,_url,_body.substr(idx)); - else if ((scPos + 4) < headers.front().length()) - _doH(_arg,rcode,_url,headers.front().substr(scPos+4)); - else _doH(_arg,rcode,_url,"(no status message from server)"); - } - - delete this; - return; - } else { - // fork() succeeded, in child process - ::dup2(curlStdout[1],STDOUT_FILENO); - ::close(curlStdout[1]); - ::dup2(curlStderr[1],STDERR_FILENO); - ::close(curlStderr[1]); - ::execv(curlPath.c_str(),curlArgs); - ::exit(-1); // only reached if execv() fails - } - } - - inline void cancel() - { - { - Mutex::Lock _l(_cancelled_m); - _cancelled = true; - if (_pid > 0) - ::kill(_pid,SIGKILL); - } - Thread::join(_myThread); - } - -private: - inline void _doH(void *arg,int code,const std::string &url,const std::string &body) - { - Mutex::Lock _l(_cancelled_m); - try { - if ((!_cancelled)&&(_handler)) - _handler(arg,code,url,body); - } catch ( ... ) {} - } - - const std::string _url; - std::string _body; - std::map<std::string,std::string> _headers; - unsigned int _timeout; - void (*_handler)(void *,int,const std::string &,const std::string &); - void *_arg; - HttpClient *_parent; - long _pid; - volatile bool _cancelled; - Mutex _cancelled_m; - Thread _myThread; -}; - -#endif // __UNIX_LIKE__ - -#ifdef __WINDOWS__ - -#define WIN_MAX_MESSAGE_LENGTH (1024 * 1024 * 64) - -// Internal private thread class that performs request, notifies handler, -// and then commits suicide by deleting itself. -class HttpClient_Private_Request : NonCopyable -{ -public: - HttpClient_Private_Request(HttpClient *parent,const char *method,const std::string &url,const std::map<std::string,std::string> &headers,unsigned int timeout,void (*handler)(void *,int,const std::string &,const std::string &),void *arg) : - _url(url), - _headers(headers), - _timeout(timeout), - _handler(handler), - _arg(arg), - _parent(parent), - _hRequest((HINTERNET)0) - { - _myThread = Thread::start(this); - } - - ~HttpClient_Private_Request() - { - Mutex::Lock _l(_parent->_requests_m); - _parent->_requests.erase((HttpClient::Request)this); - } - - void threadMain() - { - HINTERNET hSession = (HINTERNET)0; - HINTERNET hConnect = (HINTERNET)0; - HINTERNET hRequest = (HINTERNET)0; - - try { - hSession = WinHttpOpen(L"ZeroTier One HttpClient/1.0 (WinHttp)",WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,WINHTTP_NO_PROXY_NAME,WINHTTP_NO_PROXY_BYPASS,0); - if (!hSession) { - _handler(_arg,-1,_url,"WinHttpOpen() failed"); - goto closeAndReturnFromHttp; - } - int timeoutMs = (int)_timeout * 1000; - WinHttpSetTimeouts(hSession,timeoutMs,timeoutMs,timeoutMs,timeoutMs); - - std::wstring_convert< std::codecvt_utf8<wchar_t> > wcconv; - std::wstring wurl(wcconv.from_bytes(_url)); - - URL_COMPONENTS uc; - memset(&uc,0,sizeof(uc)); - uc.dwStructSize = sizeof(uc); - uc.dwSchemeLength = -1; - uc.dwHostNameLength = -1; - uc.dwUrlPathLength = -1; - uc.dwExtraInfoLength = -1; - if (!WinHttpCrackUrl(wurl.c_str(),(DWORD)wurl.length(),0,&uc)) { - _handler(_arg,-1,_url,"unable to parse URL: WinHttpCrackUrl() failed"); - goto closeAndReturnFromHttp; - } - if ((!uc.lpszHostName)||(!uc.lpszUrlPath)||(!uc.lpszScheme)||(uc.dwHostNameLength <= 0)||(uc.dwUrlPathLength <= 0)||(uc.dwSchemeLength <= 0)) { - _handler(_arg,-1,_url,"unable to parse URL: missing scheme, host name, or path"); - goto closeAndReturnFromHttp; - } - std::wstring urlScheme(uc.lpszScheme,uc.dwSchemeLength); - std::wstring urlHostName(uc.lpszHostName,uc.dwHostNameLength); - std::wstring urlPath(uc.lpszUrlPath,uc.dwUrlPathLength); - if ((uc.lpszExtraInfo)&&(uc.dwExtraInfoLength > 0)) - urlPath.append(uc.lpszExtraInfo,uc.dwExtraInfoLength); - - if (urlScheme != L"http") { - _handler(_arg,-1,_url,"only 'http' scheme is supported"); - goto closeAndReturnFromHttp; - } - - hConnect = WinHttpConnect(hSession,urlHostName.c_str(),((uc.nPort > 0) ? uc.nPort : 80),0); - if (!hConnect) { - _handler(_arg,-1,_url,"connection failed"); - goto closeAndReturnFromHttp; - } - - { - Mutex::Lock _rl(_hRequest_m); - _hRequest = WinHttpOpenRequest(hConnect,L"GET",urlPath.c_str(),NULL,WINHTTP_NO_REFERER,WINHTTP_DEFAULT_ACCEPT_TYPES,0); - if (!_hRequest) { - _handler(_arg,-1,_url,"error sending request (1)"); - goto closeAndReturnFromHttp; - } - if (!WinHttpSendRequest(_hRequest,WINHTTP_NO_ADDITIONAL_HEADERS,0,WINHTTP_NO_REQUEST_DATA,0,0,0)) { - _handler(_arg,-1,_url,"error sending request (2)"); - goto closeAndReturnFromHttp; - } - hRequest = _hRequest; - } - - if (WinHttpReceiveResponse(hRequest,NULL)) { - DWORD dwStatusCode = 0; - DWORD dwTmp = sizeof(dwStatusCode); - WinHttpQueryHeaders(hRequest,WINHTTP_QUERY_STATUS_CODE| WINHTTP_QUERY_FLAG_NUMBER,NULL,&dwStatusCode,&dwTmp,NULL); - - DWORD dwSize; - do { - dwSize = 0; - if (!WinHttpQueryDataAvailable(hRequest,&dwSize)) { - _handler(_arg,-1,_url,"receive error (1)"); - goto closeAndReturnFromHttp; - } - - { - Mutex::Lock _rl(_hRequest_m); - if (!_hRequest) { - _handler(_arg,-1,_url,"request cancelled"); - goto closeAndReturnFromHttp; - } - } - - char *outBuffer = new char[dwSize]; - DWORD dwRead = 0; - if (!WinHttpReadData(hRequest,(LPVOID)outBuffer,dwSize,&dwRead)) { - _handler(_arg,-1,_url,"receive error (2)"); - goto closeAndReturnFromHttp; - } - - { - Mutex::Lock _rl(_hRequest_m); - if (!_hRequest) { - _handler(_arg,-1,_url,"request cancelled"); - goto closeAndReturnFromHttp; - } - - _body.append(outBuffer,dwRead); - delete [] outBuffer; - if (_body.length() > WIN_MAX_MESSAGE_LENGTH) { - _handler(_arg,-1,_url,"result too large"); - goto closeAndReturnFromHttp; - } - } - } while ((dwSize > 0)&&(_hRequest)); - - { - Mutex::Lock _rl(_hRequest_m); - if (!_hRequest) { - _handler(_arg,-1,_url,"request cancelled"); - goto closeAndReturnFromHttp; - } - - _handler(_arg,dwStatusCode,_url,_body); - } - } else { - _handler(_arg,-1,_url,"receive response failed"); - } - } catch ( ... ) { - _handler(_arg,-1,_url,"unexpected exception"); - } - -closeAndReturnFromHttp: - { - Mutex::Lock _rl(_hRequest_m); - if (_hRequest) { - WinHttpCloseHandle(_hRequest); - _hRequest = (HINTERNET)0; - } - } - if (hConnect) - WinHttpCloseHandle(hConnect); - if (hSession) - WinHttpCloseHandle(hSession); - delete this; - return; - } - - inline void cancel() - { - Mutex::Lock _rl(_hRequest_m); - if (_hRequest) { - WinHttpCloseHandle(_hRequest); - _hRequest = (HINTERNET)0; - } - } - - const std::string _url; - std::string _body; - std::map<std::string,std::string> _headers; - unsigned int _timeout; - void (*_handler)(void *,int,const std::string &,const std::string &); - void *_arg; - HttpClient *_parent; - HINTERNET _hRequest; - Mutex _hRequest_m; - Thread _myThread; -}; - -#endif // __WINDOWS__ - -const std::map<std::string,std::string> HttpClient::NO_HEADERS; - -HttpClient::HttpClient() -{ -} - -HttpClient::~HttpClient() -{ - std::set<Request> reqs; - { - Mutex::Lock _l(_requests_m); - reqs = _requests; - } - - for(std::set<Request>::iterator r(reqs.begin());r!=reqs.end();++r) - this->cancel(*r); - - for(;;) { - _requests_m.lock(); - if (_requests.empty()) { - _requests_m.unlock(); - break; - } else { - _requests_m.unlock(); - Thread::sleep(250); - } - } -} - -void HttpClient::cancel(HttpClient::Request req) -{ - Mutex::Lock _l(_requests_m); - if (_requests.count(req) == 0) - return; - ((HttpClient_Private_Request *)req)->cancel(); -} - -HttpClient::Request HttpClient::_do( - const char *method, - const std::string &url, - const std::map<std::string,std::string> &headers, - unsigned int timeout, - void (*handler)(void *,int,const std::string &,const std::string &), - void *arg) -{ - HttpClient::Request r = (HttpClient::Request)(new HttpClient_Private_Request(this,method,url,headers,timeout,handler,arg)); - Mutex::Lock _l(_requests_m); - _requests.insert(r); - return r; -} - -} // namespace ZeroTier diff --git a/updater/HttpClient.hpp b/updater/HttpClient.hpp deleted file mode 100644 index 00400a8c..00000000 --- a/updater/HttpClient.hpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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_HTTPCLIENT_HPP -#define ZT_HTTPCLIENT_HPP - -#include <string> -#include <map> -#include <set> - -#include "../node/Mutex.hpp" - -namespace ZeroTier { - -class HttpClient_Private_Request; - -/** - * HTTP client that does queries in the background - * - * The handler method takes the following arguments: an arbitrary pointer, an - * HTTP response code, the URL queried, whether or not the message body was - * stored on disk, and the message body. - * - * If stored on disk, the body string contains the path and the file must be - * moved or deleted by the receiver when it's done. If an error occurs, the - * response code will be negative and the body will be the error message. - * - * All headers in the returned headers map will have their header names - * converted to lower case, e.g. "content-type". - * - * Currently only the "http" transport is guaranteed to be supported on all - * platforms. - */ -class HttpClient -{ -public: - friend class HttpClient_Private_Request; - typedef void * Request; - - HttpClient(); - ~HttpClient(); - - /** - * Empty map for convenience use - */ - static const std::map<std::string,std::string> NO_HEADERS; - - /** - * Request a URL using the GET method - */ - inline Request GET( - const std::string &url, - const std::map<std::string,std::string> &headers, - unsigned int timeout, - void (*handler)(void *,int,const std::string &,const std::string &), - void *arg) - { - return _do("GET",url,headers,timeout,handler,arg); - } - - /** - * Cancel a request - * - * If the request is not active, this does nothing. This may take some time - * depending on HTTP implementation. It may also not kill instantly, but - * it will prevent the handler function from ever being called and cause the - * request to die silently when complete. - */ - void cancel(Request req); - -private: - Request _do( - const char *method, - const std::string &url, - const std::map<std::string,std::string> &headers, - unsigned int timeout, - void (*handler)(void *,int,const std::string &,const std::string &), - void *arg); - - std::set<Request> _requests; - Mutex _requests_m; -}; - -} // namespace ZeroTier - -#endif |