diff options
author | Adam Ierymenko <adam.ierymenko@gmail.com> | 2014-09-24 09:01:58 -0700 |
---|---|---|
committer | Adam Ierymenko <adam.ierymenko@gmail.com> | 2014-09-24 09:01:58 -0700 |
commit | 9180a309862f1d2abb946381b5b92a823913d425 (patch) | |
tree | 1846da0b6d9656ff89d5c54707a44369453f0931 /node/IncomingPacket.cpp | |
parent | 61d0f27d2aa5700bd39e91cec84b8c1ba04c08a5 (diff) | |
download | infinitytier-9180a309862f1d2abb946381b5b92a823913d425.tar.gz infinitytier-9180a309862f1d2abb946381b5b92a823913d425.zip |
.
Diffstat (limited to 'node/IncomingPacket.cpp')
-rw-r--r-- | node/IncomingPacket.cpp | 907 |
1 files changed, 907 insertions, 0 deletions
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp new file mode 100644 index 00000000..336d0646 --- /dev/null +++ b/node/IncomingPacket.cpp @@ -0,0 +1,907 @@ +/* + * ZeroTier One - Global Peer to Peer Ethernet + * Copyright (C) 2011-2014 ZeroTier Networks LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "../version.h" + +#include "Constants.hpp" +#include "Defaults.hpp" +#include "RuntimeEnvironment.hpp" +#include "Topology.hpp" +#include "PacketDecoder.hpp" +#include "Switch.hpp" +#include "Peer.hpp" +#include "NodeConfig.hpp" +#include "Service.hpp" +#include "SoftwareUpdater.hpp" +#include "SHA512.hpp" + +namespace ZeroTier { + +bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r) +{ + if ((!encrypted())&&(verb() == Packet::VERB_HELLO)) { + // Unencrypted HELLOs are handled here since they are used to + // populate our identity cache in the first place. _doHELLO() is special + // in that it contains its own authentication logic. + //TRACE("<< HELLO from %s(%s) (normal unencrypted HELLO)",source().toString().c_str(),_remoteAddress.toString().c_str()); + return _doHELLO(_r); + } + + SharedPtr<Peer> peer = _r->topology->getPeer(source()); + if (peer) { + // Resume saved intermediate decode state? + if (_step == DECODE_WAITING_FOR_MULTICAST_FRAME_ORIGINAL_SENDER_LOOKUP) { + // In this state we have already authenticated and decrypted the + // packet and are waiting for the lookup of the original sender + // for a multicast frame. So check to see if we've got it. + return _doMULTICAST_FRAME(_r,peer); + } else if (_step == DECODE_WAITING_FOR_NETWORK_MEMBERSHIP_CERTIFICATE_SIGNER_LOOKUP) { + // In this state we have already authenticated and decoded the + // packet and we're waiting for the identity of the cert's signer. + return _doNETWORK_MEMBERSHIP_CERTIFICATE(_r,peer); + } // else this is the initial decode pass, so validate packet et. al. + + if (!dearmor(peer->key())) { + TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",source().toString().c_str(),_remoteAddress.toString().c_str(),size()); + return true; + } + if (!uncompress()) { + TRACE("dropped packet from %s(%s), compressed data invalid",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + + //TRACE("<< %s from %s(%s)",Packet::verbString(verb()),source().toString().c_str(),_remoteAddress.toString().c_str()); + + switch(verb()) { + //case Packet::VERB_NOP: + default: // ignore unknown verbs, but if they pass auth check they are still valid + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),verb(),0,Packet::VERB_NOP,Utils::now()); + return true; + case Packet::VERB_HELLO: + return _doHELLO(_r); + case Packet::VERB_ERROR: + return _doERROR(_r,peer); + case Packet::VERB_OK: + return _doOK(_r,peer); + case Packet::VERB_WHOIS: + return _doWHOIS(_r,peer); + case Packet::VERB_RENDEZVOUS: + return _doRENDEZVOUS(_r,peer); + case Packet::VERB_FRAME: + return _doFRAME(_r,peer); + case Packet::VERB_EXT_FRAME: + return _doEXT_FRAME(_r,peer); + case Packet::VERB_P5_MULTICAST_FRAME: + return _doP5_MULTICAST_FRAME(_r,peer); + case Packet::VERB_MULTICAST_LIKE: + return _doMULTICAST_LIKE(_r,peer); + case Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE: + return _doNETWORK_MEMBERSHIP_CERTIFICATE(_r,peer); + case Packet::VERB_NETWORK_CONFIG_REQUEST: + return _doNETWORK_CONFIG_REQUEST(_r,peer); + case Packet::VERB_NETWORK_CONFIG_REFRESH: + return _doNETWORK_CONFIG_REFRESH(_r,peer); + } + } else { + _step = DECODE_WAITING_FOR_SENDER_LOOKUP; // should already be this... + _r->sw->requestWhois(source()); + return false; + } +} + +bool PacketDecoder::_doERROR(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer) +{ + try { + Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB]; + uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_IN_RE_PACKET_ID); + Packet::ErrorCode errorCode = (Packet::ErrorCode)(*this)[ZT_PROTO_VERB_ERROR_IDX_ERROR_CODE]; + + TRACE("ERROR %s from %s(%s) in-re %s",Packet::errorString(errorCode),source().toString().c_str(),_remoteAddress.toString().c_str(),Packet::verbString(inReVerb)); + + switch(errorCode) { + case Packet::ERROR_OBJ_NOT_FOUND: + if (inReVerb == Packet::VERB_WHOIS) { + if (_r->topology->isSupernode(source())) + _r->sw->cancelWhoisRequest(Address(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH)); + } else if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) { + SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); + if ((network)&&(network->controller() == source())) + network->setNotFound(); + } + break; + case Packet::ERROR_IDENTITY_COLLISION: + // TODO: if it comes from a supernode, regenerate a new identity + // if (_r->topology->isSupernode(source())) {} + break; + case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: { + SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); + if (network) + network->pushMembershipCertificate(source(),true,Utils::now()); + } break; + case Packet::ERROR_NETWORK_ACCESS_DENIED_: { + SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); + if ((network)&&(network->controller() == source())) + network->setAccessDenied(); + } break; + default: + break; + } + + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb,Utils::now()); + } catch (std::exception &ex) { + TRACE("dropped ERROR from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); + } catch ( ... ) { + TRACE("dropped ERROR from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +bool PacketDecoder::_doHELLO(const RuntimeEnvironment *_r) +{ + try { + unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION]; + unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION]; + unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION]; + unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION); + uint64_t timestamp = at<uint64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP); + Identity id(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY); + + if (protoVersion != ZT_PROTO_VERSION) { + TRACE("dropped HELLO from %s(%s): protocol version mismatch (%u, expected %u)",source().toString().c_str(),_remoteAddress.toString().c_str(),protoVersion,(unsigned int)ZT_PROTO_VERSION); + return true; + } + if (!id.locallyValidate()) { + TRACE("dropped HELLO from %s(%s): identity invalid",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + + // Do we already have this peer? + SharedPtr<Peer> peer(_r->topology->getPeer(id.address())); + if (peer) { + // Check to make sure this isn't a colliding identity (different key, + // but same address). The odds are spectacularly low but it could happen. + // Could also be a sign of someone doing something nasty. + if (peer->identity() != id) { + unsigned char key[ZT_PEER_SECRET_KEY_LENGTH]; + if (_r->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) { + if (dearmor(key)) { // ensure packet is authentic, otherwise drop + LOG("rejected HELLO from %s(%s): address already claimed",source().toString().c_str(),_remoteAddress.toString().c_str()); + Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)Packet::VERB_HELLO); + outp.append(packetId()); + outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION); + outp.armor(key,true); + _fromSock->send(_remoteAddress,outp.data(),outp.size()); + } else { + LOG("rejected HELLO from %s(%s): packet failed authentication",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + } else { + LOG("rejected HELLO from %s(%s): key agreement failed",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; + } else if (!dearmor(peer->key())) { + LOG("rejected HELLO from %s(%s): packet failed authentication",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } // else continue and respond + } else { + // If we don't have a peer record on file, check the identity cache (if + // we have one) to see if we have a cached identity. Then check that for + // collision before adding a new peer. + Identity alreadyHaveCachedId(_r->topology->getIdentity(id.address())); + if ((alreadyHaveCachedId)&&(id != alreadyHaveCachedId)) { + unsigned char key[ZT_PEER_SECRET_KEY_LENGTH]; + if (_r->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) { + if (dearmor(key)) { // ensure packet is authentic, otherwise drop + LOG("rejected HELLO from %s(%s): address already claimed",source().toString().c_str(),_remoteAddress.toString().c_str()); + Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)Packet::VERB_HELLO); + outp.append(packetId()); + outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION); + outp.armor(key,true); + _fromSock->send(_remoteAddress,outp.data(),outp.size()); + } else { + LOG("rejected HELLO from %s(%s): packet failed authentication",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + } else { + LOG("rejected HELLO from %s(%s): key agreement failed",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; + } // else continue since identity is already known and matches + + // If this is a new peer, learn it + SharedPtr<Peer> newPeer(new Peer(_r->identity,id)); + if (!dearmor(newPeer->key())) { + LOG("rejected HELLO from %s(%s): packet failed authentication",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + peer = _r->topology->addPeer(newPeer); + } + + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),Packet::VERB_HELLO,0,Packet::VERB_NOP,Utils::now()); + peer->setRemoteVersion(vMajor,vMinor,vRevision); + + // If a supernode has a version higher than ours, this causes a software + // update check to run now. + if ((_r->updater)&&(_r->topology->isSupernode(peer->address()))) + _r->updater->sawRemoteVersion(vMajor,vMinor,vRevision); + + Packet outp(source(),_r->identity.address(),Packet::VERB_OK); + outp.append((unsigned char)Packet::VERB_HELLO); + outp.append(packetId()); + outp.append(timestamp); + outp.append((unsigned char)ZT_PROTO_VERSION); + outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR); + outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR); + outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); + outp.armor(peer->key(),true); + _fromSock->send(_remoteAddress,outp.data(),outp.size()); + } catch (std::exception &ex) { + TRACE("dropped HELLO from %s(%s): %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); + } catch ( ... ) { + TRACE("dropped HELLO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +bool PacketDecoder::_doOK(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer) +{ + try { + Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB]; + uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID); + + //TRACE("%s(%s): OK(%s)",source().toString().c_str(),_remoteAddress.toString().c_str(),Packet::verbString(inReVerb)); + + switch(inReVerb) { + case Packet::VERB_HELLO: { + unsigned int latency = std::min((unsigned int)(Utils::now() - at<uint64_t>(ZT_PROTO_VERB_HELLO__OK__IDX_TIMESTAMP)),(unsigned int)0xffff); + unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MAJOR_VERSION]; + unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION]; + unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION); + TRACE("%s(%s): OK(HELLO), version %u.%u.%u, latency %u",source().toString().c_str(),_remoteAddress.toString().c_str(),vMajor,vMinor,vRevision,latency); + peer->addDirectLatencyMeasurment(latency); + peer->setRemoteVersion(vMajor,vMinor,vRevision); + + // If a supernode has a version higher than ours, this causes a software + // update check to run now. This might bum-rush download.zerotier.com, but + // it's hosted on S3 so hopefully it can take it. This should cause updates + // to propagate out very quickly. + if ((_r->updater)&&(_r->topology->isSupernode(peer->address()))) + _r->updater->sawRemoteVersion(vMajor,vMinor,vRevision); + } break; + case Packet::VERB_WHOIS: { + // Right now only supernodes are allowed to send OK(WHOIS) to prevent + // poisoning attacks. Further decentralization will require some other + // kind of trust mechanism. + if (_r->topology->isSupernode(source())) { + Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY); + if (id.locallyValidate()) + _r->sw->doAnythingWaitingForPeer(_r->topology->addPeer(SharedPtr<Peer>(new Peer(_r->identity,id)))); + } + } break; + case Packet::VERB_NETWORK_CONFIG_REQUEST: { + SharedPtr<Network> nw(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_NETWORK_ID))); + if ((nw)&&(nw->controller() == source())) { + // OK(NETWORK_CONFIG_REQUEST) is only accepted from a network's + // controller. + unsigned int dictlen = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT_LEN); + std::string dict((const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT,dictlen),dictlen); + if (dict.length()) { + nw->setConfiguration(Dictionary(dict)); + TRACE("got network configuration for network %.16llx from %s",(unsigned long long)nw->id(),source().toString().c_str()); + } + } + } break; + default: + break; + } + + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,Utils::now()); + } catch (std::exception &ex) { + TRACE("dropped OK from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); + } catch ( ... ) { + TRACE("dropped OK from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +bool PacketDecoder::_doWHOIS(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer) +{ + try { + if (payloadLength() == ZT_ADDRESS_LENGTH) { + Identity id(_r->topology->getIdentity(Address(payload(),ZT_ADDRESS_LENGTH))); + if (id) { + Packet outp(source(),_r->identity.address(),Packet::VERB_OK); + outp.append((unsigned char)Packet::VERB_WHOIS); + outp.append(packetId()); + id.serialize(outp,false); + outp.armor(peer->key(),true); + _fromSock->send(_remoteAddress,outp.data(),outp.size()); + //TRACE("sent WHOIS response to %s for %s",source().toString().c_str(),Address(payload(),ZT_ADDRESS_LENGTH).toString().c_str()); + } else { + Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)Packet::VERB_WHOIS); + outp.append(packetId()); + outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND); + outp.append(payload(),ZT_ADDRESS_LENGTH); + outp.armor(peer->key(),true); + _fromSock->send(_remoteAddress,outp.data(),outp.size()); + //TRACE("sent WHOIS ERROR to %s for %s (not found)",source().toString().c_str(),Address(payload(),ZT_ADDRESS_LENGTH).toString().c_str()); + } + } else { + TRACE("dropped WHOIS from %s(%s): missing or invalid address",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,Utils::now()); + } catch ( ... ) { + TRACE("dropped WHOIS from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +bool PacketDecoder::_doRENDEZVOUS(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer) +{ + try { + /* + * At the moment, we only obey RENDEZVOUS if it comes from a designated + * supernode. If relay offloading is implemented to scale the net, this + * will need reconsideration. + * + * The reason is that RENDEZVOUS could technically be used to cause a + * peer to send a weird encrypted UDP packet to an arbitrary IP:port. + * The sender of RENDEZVOUS has no control over the content of this + * packet, but it's still maybe something we want to not allow just + * anyone to order due to possible DDOS or network forensic implications. + * So if we diversify relays, we'll need some way of deciding whether the + * sender is someone we should trust with a RENDEZVOUS hint. + */ + if (_r->topology->isSupernode(source())) { + Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); + SharedPtr<Peer> withPeer(_r->topology->getPeer(with)); + if (withPeer) { + unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT); + unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN]; + if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) { + InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port); + TRACE("RENDEZVOUS from %s says %s might be at %s, starting NAT-t",source().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str()); + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,Utils::now()); + _r->sw->contact(withPeer,atAddr); + } else { + TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + } else { + TRACE("ignored RENDEZVOUS from %s(%s) to meet unknown peer %s",source().toString().c_str(),_remoteAddress.toString().c_str(),with.toString().c_str()); + } + } else { + TRACE("ignored RENDEZVOUS from %s(%s): source not supernode",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + } catch (std::exception &ex) { + TRACE("dropped RENDEZVOUS from %s(%s): %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); + } catch ( ... ) { + TRACE("dropped RENDEZVOUS from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +bool PacketDecoder::_doFRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer) +{ + try { + SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID))); + if (network) { + if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) { + if (!network->isAllowed(peer->address())) { + TRACE("dropped FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned long long)network->id()); + _sendErrorNeedCertificate(_r,peer,network->id()); + return true; + } + + unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE); + if (!network->config()->permitsEtherType(etherType)) { + TRACE("dropped FRAME from %s(%s): ethertype %.4x not allowed on %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned int)etherType,(unsigned long long)network->id()); + return true; + } + + network->tapPut(MAC(peer->address(),network->id()),network->mac(),etherType,data() + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD); + + /* Source moves "closer" to us in multicast propagation priority when + * we receive unicast frames from it. This is called "implicit social + * ordering" in other docs. */ + _r->mc->bringCloser(network->id(),peer->address()); + + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,Utils::now()); + return true; + } + } else { + TRACE("dropped FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID)); + } + } catch (std::exception &ex) { + TRACE("dropped FRAME from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); + } catch ( ... ) { + TRACE("dropped FRAME from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +bool PacketDecoder::_doEXT_FRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer) +{ + try { + SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID))); + if (network) { + if (size() > ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD) { + if ((*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS] != 0) { + TRACE("dropped EXT_FRAME due to unknown flags"); + return true; + } + + if (!network->isAllowed(peer->address())) { + TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),network->id()); + _sendErrorNeedCertificate(_r,peer,network->id()); + return true; + } + + unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_ETHERTYPE); + if (!network->config()->permitsEtherType(etherType)) { + TRACE("dropped EXT_FRAME from %s(%s): ethertype %.4x not allowed on network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),(unsigned int)etherType,(unsigned long long)network->id()); + return true; + } + + const MAC to(field(ZT_PROTO_VERB_EXT_FRAME_IDX_TO,ZT_PROTO_VERB_EXT_FRAME_LEN_TO),ZT_PROTO_VERB_EXT_FRAME_LEN_TO); + const MAC from(field(ZT_PROTO_VERB_EXT_FRAME_IDX_FROM,ZT_PROTO_VERB_EXT_FRAME_LEN_FROM),ZT_PROTO_VERB_EXT_FRAME_LEN_FROM); + + if ((!from)||(from.isMulticast())||(from == network->mac())||(!to)) { + TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: invalid source or destination MAC",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str()); + return true; + } + + // If it's not from the sending peer, they must be allowed to bridge into this network + if (from != MAC(peer->address(),network->id())) { + if (network->permitsBridging(peer->address())) { + network->learnBridgeRoute(from,peer->address()); + } else { + TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: sender not allowed to bridge into %.16llx",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id()); + return true; + } + } // else: it is valid to send a non-bridged packet this way instead of as FRAME, but this isn't done by current code + + // If it's not to us, we must be allowed to bridge into this network + if (to != network->mac()) { + if (!network->permitsBridging(_r->identity.address())) { + TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: I cannot bridge to %.16llx or bridging disabled on network",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id()); + return true; + } + } + + network->tapPut(from,to,etherType,data() + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,size() - ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD); + + /* Source moves "closer" to us in multicast propagation priority when + * we receive unicast frames from it. This is called "implicit social + * ordering" in other docs. */ + _r->mc->bringCloser(network->id(),peer->address()); + + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,Utils::now()); + } + } else { + TRACE("dropped EXT_FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID)); + } + } catch (std::exception &ex) { + TRACE("dropped EXT_FRAME from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); + } catch ( ... ) { + TRACE("dropped EXT_FRAME from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer) +{ + try { + Address origin(Address(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ORIGIN,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_ORIGIN),ZT_ADDRESS_LENGTH)); + SharedPtr<Peer> originPeer(_r->topology->getPeer(origin)); + if (!originPeer) { + // We must have the origin's identity in order to authenticate a multicast + _r->sw->requestWhois(origin); + _step = DECODE_WAITING_FOR_MULTICAST_FRAME_ORIGINAL_SENDER_LOOKUP; // causes processing to come back here + return false; + } + + // These fields in the packet are changed by each forwarder + unsigned int depth = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PROPAGATION_DEPTH); + unsigned char *const fifo = field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PROPAGATION_FIFO,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_FIFO); + unsigned char *const bloom = field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PROPAGATION_BLOOM,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_BLOOM); + + // These fields don't -- they're signed by the original sender + const unsigned int flags = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FLAGS]; + const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID); + const uint16_t bloomNonce = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PROPAGATION_BLOOM_NONCE); + const unsigned int prefixBits = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PROPAGATION_PREFIX_BITS]; + const unsigned int prefix = (*this)[ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PROPAGATION_PREFIX]; + const uint64_t guid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_GUID); + const MAC sourceMac(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_SOURCE_MAC,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_SOURCE_MAC),ZT_PROTO_VERB_MULTICAST_FRAME_LEN_SOURCE_MAC); + const MulticastGroup dest(MAC(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_MAC,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_DEST_MAC),ZT_PROTO_VERB_MULTICAST_FRAME_LEN_DEST_MAC),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_DEST_ADI)); + const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_ETHERTYPE); + const unsigned int frameLen = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME_LEN); + const unsigned char *const frame = field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,frameLen); + const unsigned int signatureLen = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME + frameLen); + const unsigned char *const signature = field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME + frameLen + 2,signatureLen); + + if ((!sourceMac)||(sourceMac.isMulticast())) { + TRACE("dropped MULTICAST_FRAME from %s(%s): invalid source MAC %s",source().toString().c_str(),_remoteAddress.toString().c_str(),sourceMac.toString().c_str()); + return true; + } + + SharedPtr<Network> network(_r->nc->network(nwid)); + SharedPtr<NetworkConfig> nconf; + if (network) + nconf = network->config2(); + + /* Grab, verify, and learn certificate of network membership if any -- provided we are + * a member of this network. Note: we can do this before verification of the actual + * packet, since the certificate has its own separate signature. In other words a valid + * COM does not imply a valid multicast; they are two separate things. The ability to + * include the COM with the multicast is a performance optimization to allow peers to + * distribute their COM along with their packets instead of as a separate transaction. + * This causes network memberships to start working faster. */ + if (((flags & ZT_PROTO_VERB_MULTICAST_FRAME_FLAGS_HAS_MEMBERSHIP_CERTIFICATE))&&(network)) { + CertificateOfMembership originCom(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME + frameLen + 2 + signatureLen); + Address comSignedBy(originCom.signedBy()); + if ((originCom.networkId() == nwid)&&(comSignedBy == network->controller())) { + SharedPtr<Peer> comSigningPeer(_r->topology->getPeer(comSignedBy)); + if (!comSigningPeer) { + // Technically this should never happen because the COM should be signed by + // the master for this network (in current usage) and we ought to already have + // that cached. But handle it anyway. + _r->sw->requestWhois(comSignedBy); + _step = DECODE_WAITING_FOR_MULTICAST_FRAME_ORIGINAL_SENDER_LOOKUP; // causes processing to come back here + return false; + } else if (originCom.verify(comSigningPeer->identity())) { + // The certificate is valid so learn it. As explained above this does not + // imply validation of the multicast. That happens later. Look for a call + // to network->isAllowed(). + network->addMembershipCertificate(originCom); + } else { + // Go ahead and drop the multicast though if the COM was invalid, since this + // obviously signifies a problem. + LOG("dropped MULTICAST_FRAME from %s(%s): included COM failed authentication check",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + } else { + // Go ahead and drop the multicast here too, since this also ought never to + // happen and certainly indicates a problem. + LOG("dropped MULTICAST_FRAME from %s(%s): included COM is not for this network",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + } + + // Check the multicast frame's signature to verify that its original sender is + // who it claims to be. + const unsigned int signedPartLen = (ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME - ZT_PROTO_VERB_MULTICAST_FRAME_IDX__START_OF_SIGNED_PORTION) + frameLen; + if (!originPeer->identity().verify(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX__START_OF_SIGNED_PORTION,signedPartLen),signedPartLen,signature,signatureLen)) { + LOG("dropped MULTICAST_FRAME from %s(%s): failed signature verification, claims to be from %s",source().toString().c_str(),_remoteAddress.toString().c_str(),origin.toString().c_str()); + return true; + } + + // Security check to prohibit multicasts that are really Ethernet unicasts... + // otherwise people could do weird things like multicast out a TCP SYN. + if (!dest.mac().isMulticast()) { + LOG("dropped MULTICAST_FRAME from %s(%s): %s is not a multicast/broadcast address",source().toString().c_str(),_remoteAddress.toString().c_str(),dest.mac().toString().c_str()); + return true; + } + + // At this point the frame is basically valid, so we can call it a receive + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,Utils::now()); + + // This gets updated later in most cases but start with the global limit. + unsigned int maxDepth = ZT_MULTICAST_GLOBAL_MAX_DEPTH; + + if ((origin == _r->identity.address())||(_r->mc->deduplicate(nwid,guid))) { + // This is a boomerang or a duplicate of a multicast we've already seen. Ordinary + // nodes drop these, while supernodes will keep propagating them since they can + // act as bridges between sparse multicast networks more than once. + if (!_r->topology->amSupernode()) { + TRACE("dropped MULTICAST_FRAME from %s(%s): duplicate",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + } else { + // If we are actually a member of this network (will just about always + // be the case unless we're a supernode), check to see if we should + // inject the packet. This also gives us an opportunity to check things + // like multicast bandwidth constraints. + if ((network)&&(nconf)) { + // Learn real maxDepth from netconf + maxDepth = std::min((unsigned int)ZT_MULTICAST_GLOBAL_MAX_DEPTH,nconf->multicastDepth()); + if (!maxDepth) + maxDepth = ZT_MULTICAST_GLOBAL_MAX_DEPTH; + + if (!network->isAllowed(origin)) { + // Papers, please... + Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)Packet::VERB_MULTICAST_FRAME); + outp.append(packetId()); + outp.append((unsigned char)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE); + outp.append(nwid); + outp.armor(peer->key(),true); + _fromSock->send(_remoteAddress,outp.data(),outp.size()); + TRACE("dropped MULTICAST_FRAME from %s(%s) into %.16llx: sender %s not allowed or we don't have a certificate",source().toString().c_str(),_remoteAddress.toString().c_str(),nwid,origin.toString().c_str()); + return true; + } + + if (MAC(origin,network->id()) != sourceMac) { + if (!nconf->permitsBridging(origin)) { + TRACE("dropped MULTICAST_FRAME from %s(%s) into %.16llx: source mac %s doesn't belong to %s, and bridging is not supported on network",source().toString().c_str(),_remoteAddress.toString().c_str(),nwid,sourceMac.toString().c_str(),origin.toString().c_str()); + return true; + } + network->learnBridgeRoute(sourceMac,origin); + } + + if (!nconf->permitsEtherType(etherType)) { + TRACE("dropped MULTICAST_FRAME from %s(%s) into %.16llx: ethertype %u is not allowed",source().toString().c_str(),nwid,_remoteAddress.toString().c_str(),etherType); + return true; + } + + if (!network->updateAndCheckMulticastBalance(origin,dest,frameLen)) { + // Rate limits can only be checked by members of this network, but + // there should be enough of them that over-limit multicasts get + // their propagation aborted. + TRACE("dropped MULTICAST_FRAME from %s(%s): rate limits exceeded for sender %s",source().toString().c_str(),_remoteAddress.toString().c_str(),origin.toString().c_str()); + return true; + } + + network->tapPut(sourceMac,dest.mac(),etherType,frame,frameLen); + } + } + + // Depth of 0xffff means "do not forward." Check first since + // incrementing this would integer overflow a 16-bit int. + if (depth == 0xffff) { + TRACE("not forwarding MULTICAST_FRAME from %s(%s): depth == 0xffff (do not forward)",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + + // Check if graph traversal depth has exceeded configured maximum. + if (++depth > maxDepth) { + TRACE("not forwarding MULTICAST_FRAME from %s(%s): max propagation depth reached",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + + // Update depth in packet with new incremented value + setAt(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_PROPAGATION_DEPTH,(uint16_t)depth); + + // New FIFO with room for one extra, since head will be next hop + unsigned char newFifo[ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_FIFO + ZT_ADDRESS_LENGTH]; + unsigned char *newFifoPtr = newFifo; + unsigned char *const newFifoEnd = newFifo + sizeof(newFifo); + + // Copy old FIFO into new buffer, terminating at first NULL address + for(unsigned char *f=fifo,*const fifoEnd=(fifo + ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_FIFO);f!=fifoEnd;) { + unsigned char *nf = newFifoPtr; + unsigned char *e = nf + ZT_ADDRESS_LENGTH; + unsigned char *ftmp = f; + unsigned char zeroCheckMask = 0; + while (nf != e) + zeroCheckMask |= (*(nf++) = *(ftmp++)); + if (zeroCheckMask) { + f = ftmp; + newFifoPtr = nf; + } else break; + } + + // Add any other next hops we know about to FIFO + Multicaster::AddToPropagationQueue appender( + &newFifoPtr, + newFifoEnd, + bloom, + bloomNonce, + origin, + prefixBits, + prefix, + _r->topology, + Utils::now()); + if (nconf) { + for(std::set<Address>::const_iterator ab(nconf->activeBridges().begin());ab!=nconf->activeBridges().end();++ab) { + if (!appender(*ab)) + break; + } + } + _r->mc->getNextHops(nwid,dest,appender); + + // Zero-terminate new FIFO if not completely full. We pad the remainder with + // zeroes because this improves data compression ratios. + while (newFifoPtr != newFifoEnd) + *(newFifoPtr++) = (unsigned char)0; + + // First element in newFifo[] is next hop + Address nextHop(newFifo,ZT_ADDRESS_LENGTH); + if ((!nextHop)&&(!_r->topology->amSupernode())) { + SharedPtr<Peer> supernode(_r->topology->getBestSupernode(&origin,1,true)); + if (supernode) + nextHop = supernode->address(); + } + if ((!nextHop)||(nextHop == _r->identity.address())) { // check against our addr is a sanity check + //TRACE("not forwarding MULTICAST_FRAME from %s(%s): no next hop",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + + // The rest of newFifo[] goes back into the packet + memcpy(fifo,newFifo + ZT_ADDRESS_LENGTH,ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_FIFO); + + // Send to next hop, reusing this packet as scratch space + newInitializationVector(); + setDestination(nextHop); + setSource(_r->identity.address()); + compress(); // note: bloom filters and empty FIFOs are highly compressable! + _r->sw->send(*this,true); + + return true; + } catch (std::exception &ex) { + TRACE("dropped MULTICAST_FRAME from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); + } catch ( ... ) { + TRACE("dropped MULTICAST_FRAME from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + + return true; +} + +bool PacketDecoder::_doMULTICAST_LIKE(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer) +{ + try { + Address src(source()); + uint64_t now = Utils::now(); + + // Iterate through 18-byte network,MAC,ADI tuples + for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) { + uint64_t nwid = at<uint64_t>(ptr); + SharedPtr<Network> network(_r->nc->network(nwid)); + if ((_r->topology->amSupernode())||((network)&&(network->isAllowed(peer->address())))) { + _r->mc->likesGroup(nwid,src,MulticastGroup(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14)),now); + if (network) + network->pushMembershipCertificate(peer->address(),false,now); + } + } + + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,now); + } catch (std::exception &ex) { + TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); + } catch ( ... ) { + TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +bool PacketDecoder::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer) +{ + try { + CertificateOfMembership com; + + unsigned int ptr = ZT_PACKET_IDX_PAYLOAD; + while (ptr < size()) { + ptr += com.deserialize(*this,ptr); + if ((com.hasRequiredFields())&&(com.signedBy())) { + SharedPtr<Peer> signer(_r->topology->getPeer(com.signedBy())); + if (signer) { + if (com.verify(signer->identity())) { + uint64_t nwid = com.networkId(); + SharedPtr<Network> network(_r->nc->network(nwid)); + if (network) { + if (network->controller() == signer) + network->addMembershipCertificate(com); + } + } + } else { + _r->sw->requestWhois(com.signedBy()); + _step = DECODE_WAITING_FOR_NETWORK_MEMBERSHIP_CERTIFICATE_SIGNER_LOOKUP; + return false; + } + } + } + + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE,0,Packet::VERB_NOP,Utils::now()); + } catch (std::exception &ex) { + TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what()); + } catch ( ... ) { + TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +bool PacketDecoder::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer) +{ + try { + uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID); +#ifndef __WINDOWS__ + if (_r->netconfService) { + char tmp[128]; + unsigned int dictLen = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN); + + Dictionary request; + if (dictLen) + request["meta"] = std::string((const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT,dictLen),dictLen); + request["type"] = "netconf-request"; + request["peerId"] = peer->identity().toString(false); + Utils::snprintf(tmp,sizeof(tmp),"%.16llx",(unsigned long long)nwid); + request["nwid"] = tmp; + Utils::snprintf(tmp,sizeof(tmp),"%.16llx",(unsigned long long)packetId()); + request["requestId"] = tmp; + if (!hops()) + request["from"] = _remoteAddress.toString(); + //TRACE("to netconf:\n%s",request.toString().c_str()); + _r->netconfService->send(request); + } else { +#endif // !__WINDOWS__ + Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append(packetId()); + outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION); + outp.append(nwid); + outp.armor(peer->key(),true); + _fromSock->send(_remoteAddress,outp.data(),outp.size()); +#ifndef __WINDOWS__ + } +#endif // !__WINDOWS__ + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,Utils::now()); + } catch (std::exception &exc) { + TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); + } catch ( ... ) { + TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +bool PacketDecoder::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer) +{ + try { + unsigned int ptr = ZT_PACKET_IDX_PAYLOAD; + while ((ptr + sizeof(uint64_t)) <= size()) { + uint64_t nwid = at<uint64_t>(ptr); ptr += sizeof(uint64_t); + SharedPtr<Network> nw(_r->nc->network(nwid)); + if ((nw)&&(source() == nw->controller())) { // only respond to requests from controller + TRACE("NETWORK_CONFIG_REFRESH from %s, refreshing network %.16llx",source().toString().c_str(),nwid); + nw->requestConfiguration(); + } + } + peer->receive(_r,_fromSock,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP,Utils::now()); + } catch (std::exception &exc) { + TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); + } catch ( ... ) { + TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } + return true; +} + +void PacketDecoder::_sendErrorNeedCertificate(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer,uint64_t nwid) +{ + Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR); + outp.append((unsigned char)verb()); + outp.append(packetId()); + outp.append((unsigned char)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE); + outp.append(nwid); + outp.armor(peer->key(),true); + _fromSock->send(_remoteAddress,outp.data(),outp.size()); +} + +} // namespace ZeroTier |