blob: 1e1195efd21759de8fcac83c31709910044e828e [file] [log] [blame]
Yanbiao Li8ee37ed2015-05-19 12:44:04 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev80b68e12015-09-17 17:01:04 -07003 * Copyright (c) 2013-2015 Regents of the University of California.
Yanbiao Li8ee37ed2015-05-19 12:44:04 -07004 *
Alexander Afanasyev80b68e12015-09-17 17:01:04 -07005 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Yanbiao Li8ee37ed2015-05-19 12:44:04 -07006 *
Alexander Afanasyev80b68e12015-09-17 17:01:04 -07007 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070010 *
Alexander Afanasyev80b68e12015-09-17 17:01:04 -070011 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070014 *
Alexander Afanasyev80b68e12015-09-17 17:01:04 -070015 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070020 */
21
22#ifndef NDN_MGMT_DISPATCHER_HPP
23#define NDN_MGMT_DISPATCHER_HPP
24
25#include "../face.hpp"
26#include "../security/key-chain.hpp"
27#include "../encoding/block.hpp"
28#include "control-response.hpp"
29#include "control-parameters.hpp"
30#include "status-dataset-context.hpp"
31
32#include <unordered_map>
33
34namespace ndn {
35namespace mgmt {
36
37// ---- AUTHORIZATION ----
38
39/** \brief a function to be called if authorization is successful
40 * \param requester a string that indicates the requester, whose semantics is determined by
41 * the Authorization function; this value is intended for logging only,
42 * and should not affect how the request is processed
43 */
44typedef std::function<void(const std::string& requester)> AcceptContinuation;
45
46/** \brief indicate how to reply in case authorization is rejected
47 */
48enum class RejectReply {
49 /** \brief do not reply
50 */
51 SILENT,
52 /** \brief reply with a ControlResponse where StatusCode is 403
53 */
54 STATUS403
55};
56
57/** \brief a function to be called if authorization is rejected
58 */
59typedef std::function<void(RejectReply act)> RejectContinuation;
60
61/** \brief a function that performs authorization
62 * \param prefix top-level prefix, e.g., "/localhost/nfd";
63 * This argument can be inspected to allow Interests only under a subset of
64 * top-level prefixes (e.g., allow "/localhost/nfd" only),
65 * or to use different trust model regarding to the prefix.
66 * \param interest incoming Interest
67 * \param params parsed ControlParameters for ControlCommand, otherwise nullptr;
68 * This is guaranteed to be not-null and have correct type for the command,
69 * but may not be valid (e.g., can have missing fields).
70 *
71 * Either accept or reject must be called after authorization completes.
72 */
73typedef std::function<void(const Name& prefix, const Interest& interest,
74 const ControlParameters* params,
Junxiao Shif65a3362015-09-06 20:54:54 -070075 const AcceptContinuation& accept,
76 const RejectContinuation& reject)> Authorization;
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070077
78/** \return an Authorization that accepts all Interests, with empty string as requester
79 */
80Authorization
81makeAcceptAllAuthorization();
82
83// ---- CONTROL COMMAND ----
84
85/** \brief a function to validate input ControlParameters
86 * \param params parsed ControlParameters;
87 * This is guaranteed to have correct type for the command.
88 */
89typedef std::function<bool(const ControlParameters& params)> ValidateParameters;
90
91/** \brief a function to be called after ControlCommandHandler completes
92 * \param resp the response to be sent to requester
93 */
94typedef std::function<void(const ControlResponse& resp)> CommandContinuation;
95
96/** \brief a function to handle an authorized ControlCommand
97 * \param prefix top-level prefix, e.g., "/localhost/nfd";
98 * \param interest incoming Interest
99 * \param params parsed ControlParameters;
100 * This is guaranteed to have correct type for the command,
101 * and is valid (e.g., has all required fields).
102 */
103typedef std::function<void(const Name& prefix, const Interest& interest,
104 const ControlParameters& params,
Junxiao Shif65a3362015-09-06 20:54:54 -0700105 const CommandContinuation& done)> ControlCommandHandler;
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700106
107
108/** \brief a function to handle a StatusDataset request
109 * \param prefix top-level prefix, e.g., "/localhost/nfd";
110 * \param interest incoming Interest; its Name doesn't contain version and segment components
111 *
112 * This function can generate zero or more blocks and pass them to \p append,
113 * and must call \p end upon completion.
114 */
115typedef std::function<void(const Name& prefix, const Interest& interest,
116 StatusDatasetContext& context)> StatusDatasetHandler;
117
118//---- NOTIFICATION STREAM ----
119
120/** \brief a function to post a notification
121 */
122typedef std::function<void(const Block& notification)> PostNotification;
123
124// ---- DISPATCHER ----
125
126/** \brief represents a dispatcher on server side of NFD Management protocol
127 */
128class Dispatcher : noncopyable
129{
130 class Error : public std::runtime_error
131 {
132 public:
133 explicit
134 Error(const std::string& what)
135 : std::runtime_error(what)
136 {
137 }
138 };
139
140public:
141 /** \brief constructor
142 * \param face the Face on which the dispatcher operates
143 * \param keyChain a KeyChain to sign Data
144 * \param signingInfo signing parameters to sign Data with \p keyChain
145 */
146 Dispatcher(Face& face, security::KeyChain& keyChain,
147 const security::SigningInfo& signingInfo = security::SigningInfo());
148
149 virtual
150 ~Dispatcher();
151
152 /** \brief add a top-level prefix
153 * \param prefix a top-level prefix, e.g., "/localhost/nfd"
154 * \param wantRegister whether prefix registration should be performed through the Face
155 * \param signingInfo signing parameters to sign the prefix registration command
156 * \throw std::out_of_range \p prefix overlaps with an existing top-level prefix
157 *
158 * Procedure for adding a top-level prefix:
159 * 1. if the new top-level prefix overlaps with an existing top-level prefix
160 * (one top-level prefix is a prefix of another top-level prefix), throw std::domain_error
161 * 2. if wantRegister is true, invoke face.registerPrefix for the top-level prefix;
162 * the returned RegisteredPrefixId shall be recorded internally, indexed by the top-level
163 * prefix
164 * 3. foreach relPrefix from ControlCommands and StatusDatasets,
165 * join the top-level prefix with the relPrefix to obtain the full prefix,
166 * and invoke non-registering overload of face.setInterestFilter,
167 * with the InterestHandler set to an appropriate private method to handle incoming Interests
168 * for the ControlCommand or StatusDataset;
169 * the returned InterestFilterId shall be recorded internally, indexed by the top-level
170 * prefix
171 */
172 void
173 addTopPrefix(const Name& prefix, bool wantRegister = true,
174 const security::SigningInfo& signingInfo = security::SigningInfo());
175
176 /** \brief remove a top-level prefix
177 * \param prefix a top-level prefix, e.g., "/localhost/nfd"
178 *
179 * Procedure for removing a top-level prefix:
180 * 1. if the top-level prefix has not been added, abort these steps
181 * 2. if the top-level prefix has been added with wantRegister,
182 * invoke face.unregisterPrefix with the RegisteredPrefixId
183 * 3. foreach InterestFilterId recorded during addTopPrefix,
184 * invoke face.unsetInterestFilter with the InterestFilterId
185 */
186 void
187 removeTopPrefix(const Name& prefix);
188
189public: // ControlCommand
190 /** \brief register a ControlCommand
191 * \tparam CP subclass of ControlParameters used by this command
192 * \param relPrefix a prefix for this command, e.g., "faces/create";
193 * relPrefixes in ControlCommands, StatusDatasets, NotificationStreams must be
194 * non-overlapping
195 * (no relPrefix is a prefix of another relPrefix)
Alexander Afanasyevf2a46222015-09-17 18:01:30 -0700196 * \param authorization Callback to authorize the incoming commands
197 * \param validateParams Callback to validate parameters of the incoming commands
198 * \param handler Callback to handle the commands
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700199 * \pre no top-level prefix has been added
200 * \throw std::out_of_range \p relPrefix overlaps with an existing relPrefix
201 * \throw std::domain_error one or more top-level prefix has been added
202 *
203 * Procedure for processing a ControlCommand:
204 * 1. extract the NameComponent containing ControlParameters (the component after relPrefix),
205 * and parse ControlParameters into type CP; if parsing fails, abort these steps
206 * 2. perform authorization; if authorization is rejected,
207 * perform the RejectReply action, and abort these steps
208 * 3. validate ControlParameters; if validation fails,
209 * make ControlResponse with StatusCode 400, and go to step 5
210 * 4. invoke handler, wait until CommandContinuation is called
211 * 5. encode the ControlResponse into one Data packet
212 * 6. sign the Data packet
213 * 7. if the Data packet is too large, abort these steps and log an error
214 * 8. send the signed Data packet
215 */
216 template<typename CP>
217 void
218 addControlCommand(const PartialName& relPrefix,
Junxiao Shif65a3362015-09-06 20:54:54 -0700219 const Authorization& authorization,
220 const ValidateParameters& validateParams,
221 const ControlCommandHandler& handler);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700222
223public: // StatusDataset
224 /** \brief register a StatusDataset or a prefix under which StatusDatasets can be requested
225 * \param relPrefix a prefix for this dataset, e.g., "faces/list";
226 * relPrefixes in ControlCommands, StatusDatasets, NotificationStreams must be
227 * non-overlapping
228 * (no relPrefix is a prefix of another relPrefix)
229 * \param authorization should set identity to Name() if the dataset is public
Alexander Afanasyevf2a46222015-09-17 18:01:30 -0700230 * \param handler Callback to process the incoming dataset requests
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700231 * \pre no top-level prefix has been added
232 * \throw std::out_of_range \p relPrefix overlaps with an existing relPrefix
233 * \throw std::domain_error one or more top-level prefix has been added
234 *
235 * The payload of the returned status dataset data packet is at most half of the maximum
236 * data packet size.
237 *
238 * Procedure for processing a StatusDataset request:
239 * 1. if the request Interest contains version or segment components, abort these steps;
240 * note: the request may contain more components after relPrefix, e.g., a query condition
241 * 2. perform authorization; if authorization is rejected,
242 * perform the RejectReply action, and abort these steps
243 * 3. invoke handler, store blocks passed to StatusDatasetAppend calls in a buffer,
244 * wait until StatusDatasetEnd is called
245 * 4. allocate a version
246 * 5. segment the buffer into one or more segments under the allocated version,
247 * such that the Data packets will not become too large after signing
248 * 6. set FinalBlockId on at least the last segment
249 * 7. sign the Data packets
250 * 8. send the signed Data packets
251 *
252 * As an optimization, a Data packet may be sent as soon as enough octets have been collected
253 * through StatusDatasetAppend calls.
254 */
255 void
256 addStatusDataset(const PartialName& relPrefix,
Junxiao Shif65a3362015-09-06 20:54:54 -0700257 const Authorization& authorization,
258 const StatusDatasetHandler& handler);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700259
260public: // NotificationStream
261 /** \brief register a NotificationStream
262 * \param relPrefix a prefix for this notification stream, e.g., "faces/events";
263 * relPrefixes in ControlCommands, StatusDatasets, NotificationStreams must be
264 * non-overlapping
265 * (no relPrefix is a prefix of another relPrefix)
266 * \return a function into which notifications can be posted
267 * \pre no top-level prefix has been added
268 * \throw std::out_of_range \p relPrefix overlaps with an existing relPrefix
269 * \throw std::domain_error one or more top-level prefix has been added
270 *
271 * Procedure for posting a notification:
272 * 1. if no top-level prefix has been added, or more than one top-level prefixes have been
273 * added,
274 * abort these steps and log an error
275 * 2. assign the next sequence number to the notification
276 * 3. place the notification block into one Data packet under the sole top-level prefix
277 * 4. sign the Data packet
278 * 5. if the Data packet is too large, abort these steps and log an error
279 * 6. send the signed Data packet
280 */
281 PostNotification
282 addNotificationStream(const PartialName& relPrefix);
283
284private:
285 typedef std::function<void(const Name& prefix,
286 const Interest& interest)> InterestHandler;
287
288 typedef std::function<void(const std::string& requester,
289 const Name& prefix,
290 const Interest& interest,
291 const ControlParameters*)> AuthorizationAcceptedCallback;
292
293 typedef std::function<void(RejectReply act,
294 const Interest& interest)> AuthorizationRejectedCallback;
295
296 /**
297 * @brief the parser of extracting control parameters from name component.
298 *
299 * @param component name component that may encode control parameters.
300 * @return a shared pointer to the extracted control parameters.
301 * @throw tlv::Error if the NameComponent cannot be parsed as ControlParameters
302 */
303 typedef std::function<shared_ptr<ControlParameters>(const name::Component& component)>
304 ControlParametersParser;
305
306 bool
307 isOverlappedWithOthers(const PartialName& relPrefix);
308
309 /**
310 * @brief process unauthorized request
311 *
312 * @param act action to reply
313 * @param interest the incoming Interest
314 */
315 void
316 afterAuthorizationRejected(RejectReply act, const Interest& interest);
317
318 void
319 sendData(const Name& dataName, const Block& content,
320 const MetaInfo& metaInfo);
321
322 /**
323 * @brief process the control-command Interest before authorization.
324 *
325 * @param prefix the top-level prefix
326 * @param relPrefix the relative prefix
327 * @param interest the incoming Interest
328 * @param parser to extract control parameters from the \p interest
329 * @param authorization to process validation on this command
330 * @param accepted the callback for successful authorization
331 * @param rejected the callback for failed authorization
332 */
333 void
334 processControlCommandInterest(const Name& prefix,
335 const Name& relPrefix,
336 const Interest& interest,
337 const ControlParametersParser& parser,
338 const Authorization& authorization,
339 const AuthorizationAcceptedCallback& accepted,
340 const AuthorizationRejectedCallback& rejected);
341
342 /**
343 * @brief process the authorized control-command.
344 *
345 * @param requester the requester
346 * @param prefix the top-level prefix
347 * @param interest the incoming Interest
348 * @param parameters control parameters of this command
349 * @param validate to validate control parameters
350 * @param handler to process this command
351 */
352 void
353 processAuthorizedControlCommandInterest(const std::string& requester,
354 const Name& prefix,
355 const Interest& interest,
356 const ControlParameters* parameters,
357 const ValidateParameters& validate,
358 const ControlCommandHandler& handler);
359
360 void
361 sendControlResponse(const ControlResponse& resp, const Interest& interest, bool isNack = false);
362
363 /**
364 * @brief process the status-dataset Interest before authorization.
365 *
366 * @param prefix the top-level prefix
367 * @param interest the incoming Interest
368 * @param authorization to process verification
369 * @param accepted callback for successful authorization
370 * @param rejected callback for failed authorization
371 */
372 void
373 processStatusDatasetInterest(const Name& prefix,
374 const Interest& interest,
375 const Authorization& authorization,
376 const AuthorizationAcceptedCallback& accepted,
377 const AuthorizationRejectedCallback& rejected);
378
379 /**
380 * @brief process the authorized status-dataset request
381 *
382 * @param requester the requester
383 * @param prefix the top-level prefix
384 * @param interest the incoming Interest
385 * @param handler to process this request
386 */
387 void
388 processAuthorizedStatusDatasetInterest(const std::string& requester,
389 const Name& prefix,
390 const Interest& interest,
391 const StatusDatasetHandler& handler);
392
393 void
394 postNotification(const Block& notification, const PartialName& relPrefix);
395
396private:
397 struct TopPrefixEntry
398 {
399 Name topPrefix;
400 bool wantRegister;
401 const ndn::RegisteredPrefixId* registerPrefixId;
402 std::vector<const ndn::InterestFilterId*> interestFilters;
403 };
404 std::unordered_map<Name, TopPrefixEntry> m_topLevelPrefixes;
405
406 Face& m_face;
407 security::KeyChain& m_keyChain;
408 security::SigningInfo m_signingInfo;
409
410 typedef std::unordered_map<PartialName, InterestHandler> HandlerMap;
411 typedef HandlerMap::iterator HandlerMapIt;
412 HandlerMap m_handlers;
413
414 // NotificationStream name => next sequence number
415 std::unordered_map<Name, uint64_t> m_streams;
416};
417
418template<typename CP>
419void
420Dispatcher::addControlCommand(const PartialName& relPrefix,
Junxiao Shif65a3362015-09-06 20:54:54 -0700421 const Authorization& authorization,
422 const ValidateParameters& validateParams,
423 const ControlCommandHandler& handler)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700424{
425 if (!m_topLevelPrefixes.empty()) {
426 throw std::domain_error("one or more top-level prefix has been added");
427 }
428
429 if (isOverlappedWithOthers(relPrefix)) {
430 throw std::out_of_range("relPrefix overlaps with another relPrefix");
431 }
432
433 ControlParametersParser parser =
434 [] (const name::Component& component) -> shared_ptr<ControlParameters> {
435 return make_shared<CP>(component.blockFromValue());
436 };
437
438 AuthorizationAcceptedCallback accepted =
439 bind(&Dispatcher::processAuthorizedControlCommandInterest, this,
440 _1, _2, _3, _4, validateParams, handler);
441
442 AuthorizationRejectedCallback rejected =
443 bind(&Dispatcher::afterAuthorizationRejected, this, _1, _2);
444
445 m_handlers[relPrefix] = bind(&Dispatcher::processControlCommandInterest, this,
446 _1, relPrefix, _2, parser, authorization, accepted, rejected);
447}
448
449} // namespace mgmt
450} // namespace ndn
451#endif // NDN_MGMT_DISPATCHER_HPP