Windows now builds and runs selftest correctly, and fixed a Windows (and possibly other platforms) issue in Phy<>.

This commit is contained in:
Adam Ierymenko 2015-04-24 15:05:28 -07:00
parent 54954f5b88
commit f5848972f9
12 changed files with 223 additions and 112 deletions

View file

@ -685,7 +685,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
outp.append(pid); outp.append(pid);
outp.append(nwid); outp.append(nwid);
outp.append((uint16_t)netconfStr.length()); outp.append((uint16_t)netconfStr.length());
outp.append(netconfStr.data(),netconfStr.length()); outp.append(netconfStr.data(),(unsigned int)netconfStr.length());
outp.compress(); outp.compress();
if (outp.size() > ZT_PROTO_MAX_PACKET_LENGTH) { if (outp.size() > ZT_PROTO_MAX_PACKET_LENGTH) {
TRACE("NETWORK_CONFIG_REQUEST failed: internal error: netconf size %u is too large",(unsigned int)netconfStr.length()); TRACE("NETWORK_CONFIG_REQUEST failed: internal error: netconf size %u is too large",(unsigned int)netconfStr.length());

View file

@ -437,7 +437,7 @@ void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now) void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now)
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
unsigned long tmp = _multicastGroupsBehindMe.size(); unsigned long tmp = (unsigned long)_multicastGroupsBehindMe.size();
_multicastGroupsBehindMe[mg] = now; _multicastGroupsBehindMe[mg] = now;
if (tmp != _multicastGroupsBehindMe.size()) if (tmp != _multicastGroupsBehindMe.size())
_announceMulticastGroups(); _announceMulticastGroups();

View file

@ -426,7 +426,7 @@ std::string Node::dataStoreGet(const char *name)
std::string r; std::string r;
unsigned long olen = 0; unsigned long olen = 0;
do { do {
long n = _dataStoreGetFunction(reinterpret_cast<ZT1_Node *>(this),_uPtr,name,buf,sizeof(buf),r.length(),&olen); long n = _dataStoreGetFunction(reinterpret_cast<ZT1_Node *>(this),_uPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen);
if (n <= 0) if (n <= 0)
return std::string(); return std::string();
r.append(buf,n); r.append(buf,n);
@ -454,14 +454,14 @@ void Node::postTrace(const char *module,unsigned int line,const char *fmt,...)
Mutex::Lock _l(traceLock); Mutex::Lock _l(traceLock);
time_t now = (time_t)(_now / 1000ULL);
#ifdef __WINDOWS__ #ifdef __WINDOWS__
ctime_s(tmp3,sizeof(tmp3),&now); ctime_s(tmp3,sizeof(tmp3),&now);
char *nowstr = tmp3; char *nowstr = tmp3;
#else #else
time_t now = (time_t)(_now / 1000ULL);
char *nowstr = ctime_r(&now,tmp3); char *nowstr = ctime_r(&now,tmp3);
#endif #endif
unsigned long nowstrlen = strlen(nowstr); unsigned long nowstrlen = (unsigned long)strlen(nowstr);
if (nowstr[nowstrlen-1] == '\n') if (nowstr[nowstrlen-1] == '\n')
nowstr[--nowstrlen] = (char)0; nowstr[--nowstrlen] = (char)0;
if (nowstr[nowstrlen-1] == '\r') if (nowstr[nowstrlen-1] == '\r')

View file

@ -130,7 +130,7 @@ 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 ShttpOnStatus(http_parser *parser,const char *ptr,size_t length)
{ {
HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data); HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
hh->messageSize += length; hh->messageSize += (unsigned long)length;
if (hh->messageSize > hh->maxResponseSize) if (hh->messageSize > hh->maxResponseSize)
return -1; return -1;
return 0; return 0;
@ -138,13 +138,13 @@ 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 ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length)
{ {
HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data); HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
hh->messageSize += length; hh->messageSize += (unsigned long)length;
if (hh->messageSize > hh->maxResponseSize) if (hh->messageSize > hh->maxResponseSize)
return -1; return -1;
if ((hh->currentHeaderField.length())&&(hh->currentHeaderValue.length())) { if ((hh->currentHeaderField.length())&&(hh->currentHeaderValue.length())) {
(*hh->responseHeaders)[hh->currentHeaderField] = hh->currentHeaderValue; (*hh->responseHeaders)[hh->currentHeaderField] = hh->currentHeaderValue;
hh->currentHeaderField.assign("",0); hh->currentHeaderField = "";
hh->currentHeaderValue.assign("",0); hh->currentHeaderValue = "";
} }
for(size_t i=0;i<length;++i) for(size_t i=0;i<length;++i)
hh->currentHeaderField.push_back(OSUtils::toLower(ptr[i])); hh->currentHeaderField.push_back(OSUtils::toLower(ptr[i]));
@ -153,7 +153,7 @@ 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 ShttpOnValue(http_parser *parser,const char *ptr,size_t length)
{ {
HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data); HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
hh->messageSize += length; hh->messageSize += (unsigned long)length;
if (hh->messageSize > hh->maxResponseSize) if (hh->messageSize > hh->maxResponseSize)
return -1; return -1;
hh->currentHeaderValue.append(ptr,length); hh->currentHeaderValue.append(ptr,length);
@ -169,7 +169,7 @@ static int ShttpOnHeadersComplete(http_parser *parser)
static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length) static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length)
{ {
HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data); HttpPhyHandler *hh = reinterpret_cast<HttpPhyHandler *>(parser->data);
hh->messageSize += length; hh->messageSize += (unsigned long)length;
if (hh->messageSize > hh->maxResponseSize) if (hh->messageSize > hh->maxResponseSize)
return -1; return -1;
hh->responseBody->append(ptr,length); hh->responseBody->append(ptr,length);
@ -198,7 +198,7 @@ unsigned int Http::_do(
{ {
try { try {
responseHeaders.clear(); responseHeaders.clear();
responseBody.assign("",0); responseBody = "";
HttpPhyHandler handler; HttpPhyHandler handler;

View file

@ -89,7 +89,7 @@ public:
* *
* @return HTTP status code or 0 on error (responseBody will contain error message) * @return HTTP status code or 0 on error (responseBody will contain error message)
*/ */
static inline unsigned int DELETE( static inline unsigned int DEL(
unsigned long maxResponseSize, unsigned long maxResponseSize,
unsigned long timeout, unsigned long timeout,
const struct sockaddr *remoteAddress, const struct sockaddr *remoteAddress,

View file

@ -45,6 +45,7 @@
#ifdef __WINDOWS__ #ifdef __WINDOWS__
#include <WinSock2.h> #include <WinSock2.h>
#include <Windows.h> #include <Windows.h>
#include <Shlwapi.h>
#else #else
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
@ -96,9 +97,15 @@ public:
static inline bool mkdir(const char *path) static inline bool mkdir(const char *path)
throw() throw()
{ {
#ifdef __WINDOWS__
if (::PathIsDirectoryA(path))
return true;
return (::CreateDirectoryA(path,NULL) == TRUE);
#else
if (::mkdir(path,0755) != 0) if (::mkdir(path,0755) != 0)
return (errno == EEXIST); return (errno == EEXIST);
return true; return true;
#endif
} }
static inline bool mkdir(const std::string &path) throw() { return OSUtils::mkdir(path.c_str()); } static inline bool mkdir(const std::string &path) throw() { return OSUtils::mkdir(path.c_str()); }

View file

@ -339,7 +339,11 @@ public:
inline bool udpSend(PhySocket *sock,const struct sockaddr *remoteAddress,const void *data,unsigned long len) inline bool udpSend(PhySocket *sock,const struct sockaddr *remoteAddress,const void *data,unsigned long len)
{ {
PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock)); PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
#if defined(_WIN32) || defined(_WIN64)
return ((long)::sendto(sws.sock,reinterpret_cast<const char *>(data),len,0,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == (long)len);
#else
return ((long)::sendto(sws.sock,data,len,0,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == (long)len); return ((long)::sendto(sws.sock,data,len,0,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == (long)len);
#endif
} }
/** /**
@ -522,8 +526,8 @@ public:
inline long tcpSend(PhySocket *sock,const void *data,unsigned long len,bool callCloseHandler = true) inline long tcpSend(PhySocket *sock,const void *data,unsigned long len,bool callCloseHandler = true)
{ {
PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock)); PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
long n = (long)::send(sws.sock,data,len,0);
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
long n = (long)::send(sws.sock,reinterpret_cast<const char *>(data),len,0);
if (n == SOCKET_ERROR) { if (n == SOCKET_ERROR) {
switch(WSAGetLastError()) { switch(WSAGetLastError()) {
case WSAEINTR: case WSAEINTR:
@ -535,6 +539,7 @@ public:
} }
} }
#else // not Windows #else // not Windows
long n = (long)::send(sws.sock,data,len,0);
if (n < 0) { if (n < 0) {
switch(errno) { switch(errno) {
#ifdef EAGAIN #ifdef EAGAIN
@ -614,8 +619,10 @@ public:
#endif #endif
} }
for(typename std::list<PhySocketImpl>::iterator s(_socks.begin()),nexts;s!=_socks.end();s=nexts) { bool atEnd = false;
for(typename std::list<PhySocketImpl>::iterator s(_socks.begin()),nexts;(!atEnd);s=nexts) {
nexts = s; ++nexts; // we can delete the linked list item, so traverse now nexts = s; ++nexts; // we can delete the linked list item, so traverse now
atEnd = (nexts == _socks.end()); // if we delete the last element, s!=_socks.end() will no longer terminate our loop
switch (s->type) { switch (s->type) {
@ -644,9 +651,10 @@ public:
break; break;
case ZT_PHY_SOCKET_TCP_OUT_CONNECTED: case ZT_PHY_SOCKET_TCP_OUT_CONNECTED:
case ZT_PHY_SOCKET_TCP_IN: case ZT_PHY_SOCKET_TCP_IN: {
if (FD_ISSET(s->sock,&rfds)) { ZT_PHY_SOCKFD_TYPE sock = s->sock; // if closed, s->sock becomes invalid as s is no longer dereferencable
long n = (long)::recv(s->sock,buf,sizeof(buf),0); if (FD_ISSET(sock,&rfds)) {
long n = (long)::recv(sock,buf,sizeof(buf),0);
if (n <= 0) { if (n <= 0) {
this->close((PhySocket *)&(*s),true); this->close((PhySocket *)&(*s),true);
} else { } else {
@ -655,12 +663,12 @@ public:
} catch ( ... ) {} } catch ( ... ) {}
} }
} }
if ((FD_ISSET(s->sock,&wfds))&&(FD_ISSET(s->sock,&_writefds))) { if ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))) {
try { try {
_handler->phyOnTcpWritable((PhySocket *)&(*s),&(s->uptr)); _handler->phyOnTcpWritable((PhySocket *)&(*s),&(s->uptr));
} catch ( ... ) {} } catch ( ... ) {}
} }
break; } break;
case ZT_PHY_SOCKET_TCP_LISTEN: case ZT_PHY_SOCKET_TCP_LISTEN:
if (FD_ISSET(s->sock,&rfds)) { if (FD_ISSET(s->sock,&rfds)) {

View file

@ -37,7 +37,7 @@
#include <WinSock2.h> #include <WinSock2.h>
#include <Windows.h> #include <Windows.h>
#include <string.h> #include <string.h>
#include "Mutex.hpp" #include "../node/Mutex.hpp"
namespace ZeroTier { namespace ZeroTier {

View file

@ -44,42 +44,71 @@
#include <nldef.h> #include <nldef.h>
#include <iostream> #include <iostream>
#include <set>
#include "../node/Constants.hpp" #include "../node/Constants.hpp"
#include "../node/Utils.hpp" #include "../node/Utils.hpp"
#include "../node/Mutex.hpp" #include "../node/Mutex.hpp"
#include "WindowsEthernetTap.hpp" #include "WindowsEthernetTap.hpp"
#include "OSUtils.hpp"
#include "..\windows\TapDriver\tap-windows.h" #include "..\windows\TapDriver\tap-windows.h"
// ff:ff:ff:ff:ff:ff with no ADI // ff:ff:ff:ff:ff:ff with no ADI
static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0); //static const ZeroTier::MulticastGroup _blindWildcardMulticastGroup(ZeroTier::MAC(0xff),0);
#define ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE #define ZT_WINDOWS_CREATE_FAKE_DEFAULT_ROUTE
namespace ZeroTier { namespace ZeroTier {
namespace {
class WindowsEthernetTapEnv
{
public:
WindowsEthernetTapEnv()
{
#ifdef _WIN64
is64Bit = TRUE;
devcon = "\\devcon_x64.exe";
tapDriver = "\\tap-windows\\x64\\zttap200.inf";
#else
is64Bit = FALSE;
IsWow64Process(GetCurrentProcess(),&is64Bit);
devcon = ((is64Bit == TRUE) ? "\\devcon_x64.exe" : "\\devcon_x86.exe");
tapDriver = ((is64Bit == TRUE) ? "\\tap-windows\\x64\\zttap200.inf" : "\\tap-windows\\x86\\zttap200.inf");
#endif
}
BOOL is64Bit;
std::string devcon;
std::string tapDriver;
};
static const WindowsEthernetTapEnv WINENV;
} // anonymous namespace
// Only create or delete devices one at a time // Only create or delete devices one at a time
static Mutex _systemTapInitLock; static Mutex _systemTapInitLock;
WindowsEthernetTap::WindowsEthernetTap( WindowsEthernetTap::WindowsEthernetTap(
const char *pathToHelpers, const char *hp,
const MAC &mac, const MAC &mac,
unsigned int mtu, unsigned int mtu,
unsigned int metric, unsigned int metric,
uint64_t nwid, uint64_t nwid,
const char *desiredDevice,
const char *friendlyName, const char *friendlyName,
void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg) : void *arg) :
EthernetTap("WindowsEthernetTap",mac,mtu,metric),
_handler(handler), _handler(handler),
_arg(arg), _arg(arg),
_mac(mac),
_nwid(nwid), _nwid(nwid),
_tap(INVALID_HANDLE_VALUE), _tap(INVALID_HANDLE_VALUE),
_injectSemaphore(INVALID_HANDLE_VALUE), _injectSemaphore(INVALID_HANDLE_VALUE),
_pathToHelpers(pathToHelpers), _pathToHelpers(hp),
_run(true), _run(true),
_initialized(false), _initialized(false),
_enabled(true) _enabled(true)
@ -169,11 +198,11 @@ WindowsEthernetTap::WindowsEthernetTap(
PROCESS_INFORMATION processInfo; PROCESS_INFORMATION processInfo;
memset(&startupInfo,0,sizeof(STARTUPINFOA)); memset(&startupInfo,0,sizeof(STARTUPINFOA));
memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _pathToHelpers + WindowsEthernetTapFactory::WINENV.devcon + "\" install \"" + _pathToHelpers + WindowsEthernetTapFactory::WINENV.tapDriver + "\" zttap200").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _pathToHelpers + WINENV.devcon + "\" install \"" + _pathToHelpers + WINENV.tapDriver + "\" zttap200").c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
RegCloseKey(nwAdapters); RegCloseKey(nwAdapters);
if (devconLog != INVALID_HANDLE_VALUE) if (devconLog != INVALID_HANDLE_VALUE)
CloseHandle(devconLog); CloseHandle(devconLog);
throw std::runtime_error(std::string("unable to find or execute devcon at ") + WindowsEthernetTapFactory::WINENV.devcon); throw std::runtime_error(std::string("unable to find or execute devcon at ") + WINENV.devcon);
} }
WaitForSingleObject(processInfo.hProcess,INFINITE); WaitForSingleObject(processInfo.hProcess,INFINITE);
CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hProcess);
@ -296,18 +325,18 @@ bool WindowsEthernetTap::enabled() const
return _enabled; return _enabled;
} }
bool WindowsEthernetTap::addIP(const InetAddress &ip) bool WindowsEthernetTap::addIp(const InetAddress &ip)
{ {
if (!_initialized) if (!_initialized)
return false; return false;
if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT? if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT?
return false; return false;
std::set<InetAddress> haveIps(ips()); std::vector<InetAddress> haveIps(ips());
try { try {
// Add IP to interface at the netlink level if not already assigned. // Add IP to interface at the netlink level if not already assigned.
if (!haveIps.count(ip)) { if (!std::binary_search(haveIps.begin(),haveIps.end(),ip)) {
MIB_UNICASTIPADDRESS_ROW ipr; MIB_UNICASTIPADDRESS_ROW ipr;
InitializeUnicastIpAddressEntry(&ipr); InitializeUnicastIpAddressEntry(&ipr);
@ -333,12 +362,9 @@ bool WindowsEthernetTap::addIP(const InetAddress &ip)
ipr.InterfaceLuid = _deviceLuid; ipr.InterfaceLuid = _deviceLuid;
ipr.InterfaceIndex = _getDeviceIndex(); ipr.InterfaceIndex = _getDeviceIndex();
if (CreateUnicastIpAddressEntry(&ipr) == NO_ERROR) { if (CreateUnicastIpAddressEntry(&ipr) != NO_ERROR)
haveIps.insert(ip);
} else {
return false; return false;
} }
}
std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress")); std::vector<std::string> regIps(_getRegistryIPv4Value("IPAddress"));
if (std::find(regIps.begin(),regIps.end(),ip.toIpString()) == regIps.end()) { if (std::find(regIps.begin(),regIps.end(),ip.toIpString()) == regIps.end()) {
@ -348,14 +374,13 @@ bool WindowsEthernetTap::addIP(const InetAddress &ip)
_setRegistryIPv4Value("IPAddress",regIps); _setRegistryIPv4Value("IPAddress",regIps);
_setRegistryIPv4Value("SubnetMask",regSubnetMasks); _setRegistryIPv4Value("SubnetMask",regSubnetMasks);
} }
//_syncIpsWithRegistry(haveIps,_netCfgInstanceId);
} catch ( ... ) { } catch ( ... ) {
return false; return false;
} }
return true; return true;
} }
bool WindowsEthernetTap::removeIP(const InetAddress &ip) bool WindowsEthernetTap::removeIp(const InetAddress &ip)
{ {
if (!_initialized) if (!_initialized)
return false; return false;
@ -371,7 +396,7 @@ bool WindowsEthernetTap::removeIP(const InetAddress &ip)
break; break;
case AF_INET6: case AF_INET6:
addr.set(ipt->Table[i].Address.Ipv6.sin6_addr.u.Byte,16,ipt->Table[i].OnLinkPrefixLength); addr.set(ipt->Table[i].Address.Ipv6.sin6_addr.u.Byte,16,ipt->Table[i].OnLinkPrefixLength);
if (addr.isLinkLocal()) if (addr.ipScope() == InetAddress::IP_SCOPE_LINK_LOCAL)
continue; // can't remove link-local IPv6 addresses continue; // can't remove link-local IPv6 addresses
break; break;
} }
@ -402,10 +427,10 @@ bool WindowsEthernetTap::removeIP(const InetAddress &ip)
return false; return false;
} }
std::set<InetAddress> WindowsEthernetTap::ips() const std::vector<InetAddress> WindowsEthernetTap::ips() const
{ {
static const InetAddress linkLocalLoopback("fe80::1",64); // what is this and why does Windows assign it? static const InetAddress linkLocalLoopback("fe80::1",64); // what is this and why does Windows assign it?
std::set<InetAddress> addrs; std::vector<InetAddress> addrs;
if (!_initialized) if (!_initialized)
return addrs; return addrs;
@ -419,12 +444,12 @@ std::set<InetAddress> WindowsEthernetTap::ips() const
case AF_INET: { case AF_INET: {
InetAddress ip(&(ipt->Table[i].Address.Ipv4.sin_addr.S_un.S_addr),4,ipt->Table[i].OnLinkPrefixLength); InetAddress ip(&(ipt->Table[i].Address.Ipv4.sin_addr.S_un.S_addr),4,ipt->Table[i].OnLinkPrefixLength);
if (ip != InetAddress::LO4) if (ip != InetAddress::LO4)
addrs.insert(ip); addrs.push_back(ip);
} break; } break;
case AF_INET6: { case AF_INET6: {
InetAddress ip(ipt->Table[i].Address.Ipv6.sin6_addr.u.Byte,16,ipt->Table[i].OnLinkPrefixLength); InetAddress ip(ipt->Table[i].Address.Ipv6.sin6_addr.u.Byte,16,ipt->Table[i].OnLinkPrefixLength);
if ((ip != linkLocalLoopback)&&(ip != InetAddress::LO6)) if ((ip != linkLocalLoopback)&&(ip != InetAddress::LO6))
addrs.insert(ip); addrs.push_back(ip);
} break; } break;
} }
} }
@ -433,6 +458,9 @@ std::set<InetAddress> WindowsEthernetTap::ips() const
} }
} catch ( ... ) {} // sanity check, shouldn't happen unless out of memory } catch ( ... ) {} // sanity check, shouldn't happen unless out of memory
std::sort(addrs.begin(),addrs.end());
std::unique(addrs.begin(),addrs.end());
return addrs; return addrs;
} }
@ -472,23 +500,15 @@ void WindowsEthernetTap::setFriendlyName(const char *dn)
} }
} }
bool WindowsEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups) void WindowsEthernetTap::scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed)
{ {
if (!_initialized) if (!_initialized)
return false; return;
HANDLE t = _tap; HANDLE t = _tap;
if (t == INVALID_HANDLE_VALUE) if (t == INVALID_HANDLE_VALUE)
return false; return;
std::set<MulticastGroup> newGroups; std::vector<MulticastGroup> newGroups;
// Ensure that groups are added for each IP... this handles the MAC:ADI
// groups that are created from IPv4 addresses. Some of these may end
// up being duplicates of what the IOCTL returns but that's okay since
// the set<> will filter that.
std::set<InetAddress> ipaddrs(ips());
for(std::set<InetAddress>::const_iterator i(ipaddrs.begin());i!=ipaddrs.end();++i)
newGroups.insert(MulticastGroup::deriveMulticastGroupForAddressResolution(*i));
// The ZT1 tap driver supports an IOCTL to get multicast memberships at the L2 // The ZT1 tap driver supports an IOCTL to get multicast memberships at the L2
// level... something Windows does not seem to expose ordinarily. This lets // level... something Windows does not seem to expose ordinarily. This lets
@ -503,32 +523,28 @@ bool WindowsEthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
i += 6; i += 6;
if ((mac.isMulticast())&&(!mac.isBroadcast())) { if ((mac.isMulticast())&&(!mac.isBroadcast())) {
// exclude the nulls that may be returned or any other junk Windows puts in there // exclude the nulls that may be returned or any other junk Windows puts in there
newGroups.insert(MulticastGroup(mac,0)); newGroups.push_back(MulticastGroup(mac,0));
} }
} }
} }
bool changed = false; std::vector<InetAddress> allIps(ips());
for(std::vector<InetAddress>::iterator ip(allIps.begin());ip!=allIps.end();++ip)
newGroups.push_back(MulticastGroup::deriveMulticastGroupForAddressResolution(*ip));
for(std::set<MulticastGroup>::iterator mg(newGroups.begin());mg!=newGroups.end();++mg) { std::sort(newGroups.begin(),newGroups.end());
if (!groups.count(*mg)) { std::unique(newGroups.begin(),newGroups.end());
groups.insert(*mg);
changed = true; for(std::vector<MulticastGroup>::iterator m(newGroups.begin());m!=newGroups.end();++m) {
if (!std::binary_search(_multicastGroups.begin(),_multicastGroups.end(),*m))
added.push_back(*m);
} }
} for(std::vector<MulticastGroup>::iterator m(_multicastGroups.begin());m!=_multicastGroups.end();++m) {
for(std::set<MulticastGroup>::iterator mg(groups.begin());mg!=groups.end();) { if (!std::binary_search(newGroups.begin(),newGroups.end(),*m))
if ((!newGroups.count(*mg))&&(*mg != _blindWildcardMulticastGroup)) { removed.push_back(*m);
groups.erase(mg++);
changed = true;
} else ++mg;
} }
return changed; _multicastGroups.swap(newGroups);
}
bool WindowsEthernetTap::injectPacketFromHost(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
{
return false;
} }
void WindowsEthernetTap::threadMain() void WindowsEthernetTap::threadMain()
@ -699,8 +715,8 @@ void WindowsEthernetTap::threadMain()
MAC from(tapReadBuf + 6,6); MAC from(tapReadBuf + 6,6);
unsigned int etherType = ((((unsigned int)tapReadBuf[12]) & 0xff) << 8) | (((unsigned int)tapReadBuf[13]) & 0xff); unsigned int etherType = ((((unsigned int)tapReadBuf[12]) & 0xff) << 8) | (((unsigned int)tapReadBuf[13]) & 0xff);
try { try {
Buffer<4096> tmp(tapReadBuf + 14,bytesRead - 14); // TODO: decode vlans
_handler(_arg,from,to,etherType,tmp); _handler(_arg,_nwid,from,to,etherType,0,tapReadBuf + 14,bytesRead - 14);
} catch ( ... ) {} // handlers should not throw } catch ( ... ) {} // handlers should not throw
} }
} }
@ -733,6 +749,49 @@ void WindowsEthernetTap::threadMain()
::free(tapReadBuf); ::free(tapReadBuf);
} }
void WindowsEthernetTap::destroyAllPersistentTapDevices(const char *pathToHelpers)
{
char subkeyName[4096];
char subkeyClass[4096];
char data[4096];
std::set<std::string> instanceIdPathsToRemove;
{
HKEY nwAdapters;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",0,KEY_READ|KEY_WRITE,&nwAdapters) != ERROR_SUCCESS)
return;
for(DWORD subkeyIndex=0;;++subkeyIndex) {
DWORD type;
DWORD dataLen;
DWORD subkeyNameLen = sizeof(subkeyName);
DWORD subkeyClassLen = sizeof(subkeyClass);
FILETIME lastWriteTime;
if (RegEnumKeyExA(nwAdapters,subkeyIndex,subkeyName,&subkeyNameLen,(DWORD *)0,subkeyClass,&subkeyClassLen,&lastWriteTime) == ERROR_SUCCESS) {
type = 0;
dataLen = sizeof(data);
if (RegGetValueA(nwAdapters,subkeyName,"ComponentId",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS) {
data[dataLen] = '\0';
if (!strnicmp(data,"zttap",5)) {
std::string instanceIdPath;
type = 0;
dataLen = sizeof(data);
if (RegGetValueA(nwAdapters,subkeyName,"DeviceInstanceID",RRF_RT_ANY,&type,(PVOID)data,&dataLen) == ERROR_SUCCESS)
instanceIdPath.assign(data,dataLen);
if (instanceIdPath.length() != 0)
instanceIdPathsToRemove.insert(instanceIdPath);
}
}
} else break; // end of list or failure
}
RegCloseKey(nwAdapters);
}
for(std::set<std::string>::iterator iidp(instanceIdPathsToRemove.begin());iidp!=instanceIdPathsToRemove.end();++iidp)
_deletePersistentTapDevice(pathToHelpers,iidp->c_str());
}
bool WindowsEthernetTap::_disableTapDevice() bool WindowsEthernetTap::_disableTapDevice()
{ {
HANDLE devconLog = CreateFileA((_pathToHelpers + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); HANDLE devconLog = CreateFileA((_pathToHelpers + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
@ -748,7 +807,7 @@ bool WindowsEthernetTap::_disableTapDevice()
PROCESS_INFORMATION processInfo; PROCESS_INFORMATION processInfo;
memset(&startupInfo,0,sizeof(STARTUPINFOA)); memset(&startupInfo,0,sizeof(STARTUPINFOA));
memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _pathToHelpers + WindowsEthernetTapFactory::WINENV.devcon + "\" disable @" + _deviceInstanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _pathToHelpers + WINENV.devcon + "\" disable @" + _deviceInstanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
if (devconLog != INVALID_HANDLE_VALUE) if (devconLog != INVALID_HANDLE_VALUE)
CloseHandle(devconLog); CloseHandle(devconLog);
return false; return false;
@ -778,7 +837,7 @@ bool WindowsEthernetTap::_enableTapDevice()
PROCESS_INFORMATION processInfo; PROCESS_INFORMATION processInfo;
memset(&startupInfo,0,sizeof(STARTUPINFOA)); memset(&startupInfo,0,sizeof(STARTUPINFOA));
memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _pathToHelpers + WindowsEthernetTapFactory::WINENV.devcon + "\" enable @" + _deviceInstanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) { if (!CreateProcessA(NULL,(LPSTR)(std::string("\"") + _pathToHelpers + WINENV.devcon + "\" enable @" + _deviceInstanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
if (devconLog != INVALID_HANDLE_VALUE) if (devconLog != INVALID_HANDLE_VALUE)
CloseHandle(devconLog); CloseHandle(devconLog);
return false; return false;
@ -863,4 +922,26 @@ void WindowsEthernetTap::_setRegistryIPv4Value(const char *regKey,const std::vec
} }
} }
void WindowsEthernetTap::_deletePersistentTapDevice(const char *pathToHelpers,const char *instanceId)
{
HANDLE devconLog = CreateFileA((std::string(pathToHelpers) + "\\devcon.log").c_str(),GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
STARTUPINFOA startupInfo;
startupInfo.cb = sizeof(startupInfo);
if (devconLog != INVALID_HANDLE_VALUE) {
SetFilePointer(devconLog,0,0,FILE_END);
startupInfo.hStdOutput = devconLog;
startupInfo.hStdError = devconLog;
}
PROCESS_INFORMATION processInfo;
memset(&startupInfo,0,sizeof(STARTUPINFOA));
memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
if (CreateProcessA(NULL,(LPSTR)(std::string("\"") + pathToHelpers + WINENV.devcon + "\" remove @" + instanceId).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
WaitForSingleObject(processInfo.hProcess,INFINITE);
CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
if (devconLog != INVALID_HANDLE_VALUE)
CloseHandle(devconLog);
}
} // namespace ZeroTier } // namespace ZeroTier

View file

@ -37,42 +37,38 @@
#include <queue> #include <queue>
#include <stdexcept> #include <stdexcept>
#include "EthernetTap.hpp"
#include "../node/Constants.hpp" #include "../node/Constants.hpp"
#include "../node/Mutex.hpp" #include "../node/Mutex.hpp"
#include "../node/Thread.hpp"
#include "../node/Array.hpp" #include "../node/Array.hpp"
#include "../node/MulticastGroup.hpp" #include "../node/MulticastGroup.hpp"
#include "../osdep/Thread.hpp"
namespace ZeroTier { namespace ZeroTier {
class WindowsEthernetTap : public EthernetTap class WindowsEthernetTap
{ {
public: public:
WindowsEthernetTap( WindowsEthernetTap(
const char *pathToHelpers, const char *hp,
const MAC &mac, const MAC &mac,
unsigned int mtu, unsigned int mtu,
unsigned int metric, unsigned int metric,
uint64_t nwid, uint64_t nwid,
const char *desiredDevice,
const char *friendlyName, const char *friendlyName,
void (*handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &), void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
void *arg); void *arg);
virtual ~WindowsEthernetTap(); ~WindowsEthernetTap();
virtual void setEnabled(bool en); void setEnabled(bool en);
virtual bool enabled() const; bool enabled() const;
virtual bool addIP(const InetAddress &ip); bool addIp(const InetAddress &ip);
virtual bool removeIP(const InetAddress &ip); bool removeIp(const InetAddress &ip);
virtual std::set<InetAddress> ips() const; std::vector<InetAddress> ips() const;
virtual void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len); void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
virtual std::string deviceName() const; std::string deviceName() const;
virtual void setFriendlyName(const char *friendlyName); void setFriendlyName(const char *friendlyName);
virtual bool updateMulticastGroups(std::set<MulticastGroup> &groups); void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
virtual bool injectPacketFromHost(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
inline const NET_LUID &luid() const { return _deviceLuid; } inline const NET_LUID &luid() const { return _deviceLuid; }
inline const GUID &guid() const { return _deviceGuid; } inline const GUID &guid() const { return _deviceGuid; }
@ -81,15 +77,19 @@ public:
void threadMain() void threadMain()
throw(); throw();
static void destroyAllPersistentTapDevices(const char *pathToHelpers);
private: private:
bool _disableTapDevice(); bool _disableTapDevice();
bool _enableTapDevice(); bool _enableTapDevice();
NET_IFINDEX _getDeviceIndex(); // throws on failure NET_IFINDEX _getDeviceIndex(); // throws on failure
std::vector<std::string> _getRegistryIPv4Value(const char *regKey); std::vector<std::string> _getRegistryIPv4Value(const char *regKey);
void _setRegistryIPv4Value(const char *regKey,const std::vector<std::string> &value); void _setRegistryIPv4Value(const char *regKey,const std::vector<std::string> &value);
static void _deletePersistentTapDevice(const char *pathToHelpers,const char *instanceId);
void (*_handler)(void *,const MAC &,const MAC &,unsigned int,const Buffer<4096> &); void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
void *_arg; void *_arg;
MAC _mac;
uint64_t _nwid; uint64_t _nwid;
Thread _thread; Thread _thread;
@ -101,6 +101,8 @@ private:
std::string _netCfgInstanceId; // NetCfgInstanceId, a GUID std::string _netCfgInstanceId; // NetCfgInstanceId, a GUID
std::string _deviceInstanceId; // DeviceInstanceID, another kind of "instance ID" std::string _deviceInstanceId; // DeviceInstanceID, another kind of "instance ID"
std::vector<MulticastGroup> _multicastGroups;
std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending; std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending;
Mutex _injectPending_m; Mutex _injectPending_m;

View file

@ -628,7 +628,7 @@ struct TestPhyHandlers
{ {
std::string *testMessage = (std::string *)*uptr; std::string *testMessage = (std::string *)*uptr;
if ((testMessage)&&(testMessage->length() > 0)) { if ((testMessage)&&(testMessage->length() > 0)) {
long sent = testPhyInstance->tcpSend(sock,(const void *)testMessage->data(),testMessage->length(),true); long sent = testPhyInstance->tcpSend(sock,(const void *)testMessage->data(),(unsigned long)testMessage->length(),true);
if (sent > 0) if (sent > 0)
testMessage->erase(0,sent); testMessage->erase(0,sent);
} }
@ -804,6 +804,11 @@ int main(int argc,char **argv)
{ {
int r = 0; int r = 0;
#ifdef __WINDOWS__
WSADATA wsaData;
WSAStartup(MAKEWORD(2,2),&wsaData);
#endif
// Code to generate the C25519 test vectors -- did this once and then // Code to generate the C25519 test vectors -- did this once and then
// put these up top so that we can ensure that every platform produces // put these up top so that we can ensure that every platform produces
// the same result. // the same result.

View file

@ -53,6 +53,10 @@
#include "OneService.hpp" #include "OneService.hpp"
#include "ControlPlane.hpp" #include "ControlPlane.hpp"
#ifdef __WINDOWS__
#include <ShlObj.h>
#endif
// Include the right tap device driver for this platform -- add new platforms here // Include the right tap device driver for this platform -- add new platforms here
#ifdef __APPLE__ #ifdef __APPLE__
#include "../osdep/OSXEthernetTap.hpp" #include "../osdep/OSXEthernetTap.hpp"
@ -62,6 +66,10 @@ namespace ZeroTier { typedef OSXEthernetTap EthernetTap; }
#include "../osdep/LinuxEthernetTap.hpp" #include "../osdep/LinuxEthernetTap.hpp"
namespace ZeroTier { typedef LinuxEthernetTap EthernetTap; } namespace ZeroTier { typedef LinuxEthernetTap EthernetTap; }
#endif #endif
#ifdef __WINDOWS__
#include "../osdep/WindowsEthernetTap.hpp"
namespace ZeroTier { typedef WindowsEthernetTap EthernetTap; }
#endif
// Sanity limits for HTTP // Sanity limits for HTTP
#define ZT_MAX_HTTP_MESSAGE_SIZE (1024 * 1024 * 8) #define ZT_MAX_HTTP_MESSAGE_SIZE (1024 * 1024 * 8)
@ -384,7 +392,7 @@ public:
inline void phyOnTcpWritable(PhySocket *sock,void **uptr) inline void phyOnTcpWritable(PhySocket *sock,void **uptr)
{ {
HttpConnection *htc = reinterpret_cast<HttpConnection *>(*uptr); HttpConnection *htc = reinterpret_cast<HttpConnection *>(*uptr);
long sent = _phy.tcpSend(sock,htc->body.data() + htc->writePtr,htc->body.length() - htc->writePtr,true); long sent = _phy.tcpSend(sock,htc->body.data() + htc->writePtr,(unsigned long)htc->body.length() - htc->writePtr,true);
if (sent < 0) { if (sent < 0) {
return; // close handler will have been called, so everything's dead return; // close handler will have been called, so everything's dead
} else { } else {
@ -395,7 +403,7 @@ public:
if (htc->shouldKeepAlive) { if (htc->shouldKeepAlive) {
htc->writing = false; htc->writing = false;
htc->writePtr = 0; htc->writePtr = 0;
htc->body.assign("",0); htc->body = "";
} else { } else {
_phy.close(sock); // will call close handler to delete from _httpConnections _phy.close(sock); // will call close handler to delete from _httpConnections
} }
@ -417,7 +425,7 @@ public:
_homePath.c_str(), _homePath.c_str(),
MAC(nwc->mac), MAC(nwc->mac),
nwc->mtu, nwc->mtu,
ZT_IF_METRIC, (unsigned int)ZT_IF_METRIC,
nwid, nwid,
friendlyName, friendlyName,
StapFrameHandler, StapFrameHandler,
@ -683,19 +691,19 @@ static void StapFrameHandler(void *uptr,uint64_t nwid,const MAC &from,const MAC
static int ShttpOnMessageBegin(http_parser *parser) static int ShttpOnMessageBegin(http_parser *parser)
{ {
HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data); HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data);
htc->currentHeaderField.assign("",0); htc->currentHeaderField = "";
htc->currentHeaderValue.assign("",0); htc->currentHeaderValue = "";
htc->messageSize = 0; htc->messageSize = 0;
htc->url.assign("",0); htc->url = "";
htc->status.assign("",0); htc->status = "";
htc->headers.clear(); htc->headers.clear();
htc->body.assign("",0); htc->body = "";
return 0; return 0;
} }
static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length) static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length)
{ {
HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data); HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data);
htc->messageSize += length; htc->messageSize += (unsigned long)length;
if (htc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE) if (htc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE)
return -1; return -1;
htc->url.append(ptr,length); htc->url.append(ptr,length);
@ -704,7 +712,7 @@ 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 ShttpOnStatus(http_parser *parser,const char *ptr,size_t length)
{ {
HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data); HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data);
htc->messageSize += length; htc->messageSize += (unsigned long)length;
if (htc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE) if (htc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE)
return -1; return -1;
htc->status.append(ptr,length); htc->status.append(ptr,length);
@ -713,13 +721,13 @@ 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 ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length)
{ {
HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data); HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data);
htc->messageSize += length; htc->messageSize += (unsigned long)length;
if (htc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE) if (htc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE)
return -1; return -1;
if ((htc->currentHeaderField.length())&&(htc->currentHeaderValue.length())) { if ((htc->currentHeaderField.length())&&(htc->currentHeaderValue.length())) {
htc->headers[htc->currentHeaderField] = htc->currentHeaderValue; htc->headers[htc->currentHeaderField] = htc->currentHeaderValue;
htc->currentHeaderField.assign("",0); htc->currentHeaderField = "";
htc->currentHeaderValue.assign("",0); htc->currentHeaderValue = "";
} }
for(size_t i=0;i<length;++i) for(size_t i=0;i<length;++i)
htc->currentHeaderField.push_back(OSUtils::toLower(ptr[i])); htc->currentHeaderField.push_back(OSUtils::toLower(ptr[i]));
@ -728,7 +736,7 @@ 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 ShttpOnValue(http_parser *parser,const char *ptr,size_t length)
{ {
HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data); HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data);
htc->messageSize += length; htc->messageSize += (unsigned long)length;
if (htc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE) if (htc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE)
return -1; return -1;
htc->currentHeaderValue.append(ptr,length); htc->currentHeaderValue.append(ptr,length);
@ -744,7 +752,7 @@ static int ShttpOnHeadersComplete(http_parser *parser)
static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length) static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length)
{ {
HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data); HttpConnection *htc = reinterpret_cast<HttpConnection *>(parser->data);
htc->messageSize += length; htc->messageSize += (unsigned long)length;
if (htc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE) if (htc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE)
return -1; return -1;
htc->body.append(ptr,length); htc->body.append(ptr,length);