blob: 5dbe5cd0765d7759c4b4753d66bb0a7820ae1f66 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2013-2015 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
* ndn-cxx library is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* ndn-cxx library 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 Lesser General Public License for more details.
*
* You should have received copies of the GNU General Public License and GNU Lesser
* General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
* <http://www.gnu.org/licenses/>.
*
* See AUTHORS.md for complete list of ndn-cxx authors and contributors.
*/
#include "dispatcher.hpp"
#include <algorithm>
// #define NDN_CXX_MGMT_DISPATCHER_ENABLE_LOGGING
namespace ndn {
namespace mgmt {
Authorization
makeAcceptAllAuthorization()
{
return [] (const Name& prefix,
const Interest& interest,
const ControlParameters* params,
const AcceptContinuation& accept,
const RejectContinuation& reject) {
accept("");
};
}
Dispatcher::Dispatcher(Face& face, security::KeyChain& keyChain,
const security::SigningInfo& signingInfo)
: m_face(face)
, m_keyChain(keyChain)
, m_signingInfo(signingInfo)
{
}
Dispatcher::~Dispatcher()
{
std::vector<Name> topPrefixNames;
std::transform(m_topLevelPrefixes.begin(),
m_topLevelPrefixes.end(),
std::back_inserter(topPrefixNames),
[] (const std::unordered_map<Name, TopPrefixEntry>::value_type& entry) {
return entry.second.topPrefix;
});
for (auto&& name : topPrefixNames) {
removeTopPrefix(name);
}
}
void
Dispatcher::addTopPrefix(const Name& prefix,
bool wantRegister,
const security::SigningInfo& signingInfo)
{
bool hasOverlap = std::any_of(m_topLevelPrefixes.begin(),
m_topLevelPrefixes.end(),
[&] (const std::unordered_map<Name, TopPrefixEntry>::value_type& x) {
return x.first.isPrefixOf(prefix) || prefix.isPrefixOf(x.first);
});
if (hasOverlap) {
BOOST_THROW_EXCEPTION(std::out_of_range("Top-level Prefixes overlapped"));
}
TopPrefixEntry& topPrefixEntry = m_topLevelPrefixes[prefix];;
topPrefixEntry.topPrefix = prefix;
topPrefixEntry.wantRegister = wantRegister;
if (wantRegister) {
RegisterPrefixFailureCallback failure = [] (const Name& name, const std::string& reason) {
BOOST_THROW_EXCEPTION(std::runtime_error(reason));
};
topPrefixEntry.registerPrefixId =
m_face.registerPrefix(prefix, bind([]{}), failure, signingInfo);
}
for (auto&& entry : m_handlers) {
Name fullPrefix = prefix;
fullPrefix.append(entry.first);
const InterestFilterId* interestFilterId =
m_face.setInterestFilter(fullPrefix, std::bind(entry.second, prefix, _2));
topPrefixEntry.interestFilters.push_back(interestFilterId);
}
}
void
Dispatcher::removeTopPrefix(const Name& prefix)
{
auto it = m_topLevelPrefixes.find(prefix);
if (it == m_topLevelPrefixes.end()) {
return;
}
const TopPrefixEntry& topPrefixEntry = it->second;
if (topPrefixEntry.wantRegister) {
m_face.unregisterPrefix(topPrefixEntry.registerPrefixId, bind([]{}), bind([]{}));
}
for (auto&& filter : topPrefixEntry.interestFilters) {
m_face.unsetInterestFilter(filter);
}
m_topLevelPrefixes.erase(it);
}
bool
Dispatcher::isOverlappedWithOthers(const PartialName& relPrefix)
{
bool hasOverlapWithHandlers =
std::any_of(m_handlers.begin(), m_handlers.end(),
[&] (const HandlerMap::value_type& entry) {
return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
});
bool hasOverlapWithStreams =
std::any_of(m_streams.begin(), m_streams.end(),
[&] (const std::unordered_map<PartialName, uint64_t>::value_type& entry) {
return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
});
return hasOverlapWithHandlers || hasOverlapWithStreams;
}
void
Dispatcher::afterAuthorizationRejected(RejectReply act, const Interest& interest)
{
if (act == RejectReply::STATUS403) {
sendControlResponse(ControlResponse(403, "authorization rejected"), interest);
}
}
void
Dispatcher::sendData(const Name& dataName, const Block& content,
const MetaInfo& metaInfo)
{
shared_ptr<Data> data = make_shared<Data>(dataName);
data->setContent(content).setMetaInfo(metaInfo);
m_keyChain.sign(*data, m_signingInfo);
try {
m_face.put(*data);
}
catch (Face::Error& e) {
#ifdef NDN_CXX_MGMT_DISPATCHER_ENABLE_LOGGING
std::clog << e.what() << std::endl;
#endif // NDN_CXX_MGMT_DISPATCHER_ENABLE_LOGGING.
}
}
void
Dispatcher::processControlCommandInterest(const Name& prefix,
const Name& relPrefix,
const Interest& interest,
const ControlParametersParser& parser,
const Authorization& authorization,
const AuthorizationAcceptedCallback& accepted,
const AuthorizationRejectedCallback& rejected)
{
// /<prefix>/<relPrefix>/<parameters>
size_t parametersLoc = prefix.size() + relPrefix.size();
const name::Component& pc = interest.getName().get(parametersLoc);
shared_ptr<ControlParameters> parameters;
try {
parameters = parser(pc);
}
catch (tlv::Error& e) {
return;
}
AcceptContinuation accept = bind(accepted, _1, prefix, interest, parameters.get());
RejectContinuation reject = bind(rejected, _1, interest);
authorization(prefix, interest, parameters.get(), accept, reject);
}
void
Dispatcher::processAuthorizedControlCommandInterest(const std::string& requester,
const Name& prefix,
const Interest& interest,
const ControlParameters* parameters,
const ValidateParameters& validateParams,
const ControlCommandHandler& handler)
{
if (validateParams(*parameters)) {
handler(prefix, interest, *parameters,
bind(&Dispatcher::sendControlResponse, this, _1, interest, false));
}
else {
sendControlResponse(ControlResponse(400, "failed in validating parameters"), interest);
}
}
void
Dispatcher::sendControlResponse(const ControlResponse& resp, const Interest& interest,
bool isNack/*= false*/)
{
MetaInfo info;
if (isNack) {
info.setType(tlv::ContentType_Nack);
}
sendData(interest.getName(), resp.wireEncode(), info);
}
void
Dispatcher::addStatusDataset(const PartialName& relPrefix,
const Authorization& authorization,
const StatusDatasetHandler& handler)
{
if (!m_topLevelPrefixes.empty()) {
BOOST_THROW_EXCEPTION(std::domain_error("one or more top-level prefix has been added"));
}
if (isOverlappedWithOthers(relPrefix)) {
BOOST_THROW_EXCEPTION(std::out_of_range("relPrefix overlapped"));
}
AuthorizationAcceptedCallback accepted =
bind(&Dispatcher::processAuthorizedStatusDatasetInterest, this,
_1, _2, _3, handler);
AuthorizationRejectedCallback rejected =
bind(&Dispatcher::afterAuthorizationRejected, this, _1, _2);
m_handlers[relPrefix] = bind(&Dispatcher::processStatusDatasetInterest, this,
_1, _2, authorization, accepted, rejected);
}
void
Dispatcher::processStatusDatasetInterest(const Name& prefix,
const Interest& interest,
const Authorization& authorization,
const AuthorizationAcceptedCallback& accepted,
const AuthorizationRejectedCallback& rejected)
{
const Name& interestName = interest.getName();
bool endsWithVersionOrSegment = interestName.size() >= 1 &&
(interestName[-1].isVersion() || interestName[-1].isSegment());
if (endsWithVersionOrSegment) {
return;
}
AcceptContinuation accept = bind(accepted, _1, prefix, interest, nullptr);
RejectContinuation reject = bind(rejected, _1, interest);
authorization(prefix, interest, nullptr, accept, reject);
}
void
Dispatcher::processAuthorizedStatusDatasetInterest(const std::string& requester,
const Name& prefix,
const Interest& interest,
const StatusDatasetHandler& handler)
{
StatusDatasetContext context(interest, bind(&Dispatcher::sendData, this, _1, _2, _3));
handler(prefix, interest, context);
}
PostNotification
Dispatcher::addNotificationStream(const PartialName& relPrefix)
{
if (!m_topLevelPrefixes.empty()) {
throw std::domain_error("one or more top-level prefix has been added");
}
if (isOverlappedWithOthers(relPrefix)) {
throw std::out_of_range("relPrefix overlaps with another relPrefix");
}
m_streams[relPrefix] = 0;
return bind(&Dispatcher::postNotification, this, _1, relPrefix);
}
void
Dispatcher::postNotification(const Block& notification, const PartialName& relPrefix)
{
if (m_topLevelPrefixes.empty() || m_topLevelPrefixes.size() > 1) {
#ifdef NDN_CXX_MGMT_DISPATCHER_ENABLE_LOGGING
std::clog << "no top-level prefix or too many top-level prefixes" << std::endl;
#endif // NDN_CXX_MGMT_DISPATCHER_ENABLE_LOGGING.
return;
}
Name streamName(m_topLevelPrefixes.begin()->second.topPrefix);
streamName.append(relPrefix);
streamName.appendSequenceNumber(m_streams[streamName]++);
sendData(streamName, notification, MetaInfo());
}
} // namespace mgmt
} // namespace ndn