blob: f28ec8c72d6c47e24df16e75d8b7680bae20ba76 [file] [log] [blame]
Yingdi Yu2c9e7712014-10-20 11:55:05 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/*
Varun Patilb3d03b92021-01-08 17:09:32 +05303 * Copyright (c) 2013-2021, Regents of the University of California
Junxiao Shi9887fd12016-07-23 02:09:00 +00004 * Yingdi Yu
Yingdi Yu2c9e7712014-10-20 11:55:05 -07005 *
6 * BSD license, See the LICENSE file for more information
7 *
8 * Author: Yingdi Yu <yingdi@cs.ucla.edu>
Yingdi Yuf3401182015-02-02 20:21:07 -08009 * Qiuhan Ding <qiuhanding@cs.ucla.edu>
Yingdi Yu2c9e7712014-10-20 11:55:05 -070010 */
11
12#include "controller-backend.hpp"
13
14#ifndef Q_MOC_RUN
Varun Patila24bd3e2020-11-24 10:08:33 +053015#include <iostream>
16#include <boost/asio.hpp>
17
Yingdi Yu7ff31f02015-02-05 11:21:07 -080018#include <ndn-cxx/util/segment-fetcher.hpp>
Varun Patil3d850902020-11-23 12:19:14 +053019#include <ndn-cxx/security/signing-helpers.hpp>
20#include <ndn-cxx/security/certificate-fetcher-offline.hpp>
Varun Patila24bd3e2020-11-24 10:08:33 +053021
Yingdi Yu2c9e7712014-10-20 11:55:05 -070022#include "invitation.hpp"
Yingdi Yu2c9e7712014-10-20 11:55:05 -070023#endif
24
Yingdi Yueb692ac2015-02-10 18:46:18 -080025namespace chronochat {
Yingdi Yu2c9e7712014-10-20 11:55:05 -070026
27using std::string;
28
29using ndn::Face;
Yingdi Yu2c9e7712014-10-20 11:55:05 -070030
Qiuhan Dingba3e57a2015-01-08 19:07:39 -080031static const ndn::Name::Component ROUTING_HINT_SEPARATOR =
32 ndn::name::Component::fromEscapedString("%F0%2E");
33static const int MAXIMUM_REQUEST = 3;
Yingdi Yuf3401182015-02-02 20:21:07 -080034static const int CONNECTION_RETRY_TIMER = 3;
Yingdi Yu2c9e7712014-10-20 11:55:05 -070035
36ControllerBackend::ControllerBackend(QObject* parent)
37 : QThread(parent)
Yingdi Yuf3401182015-02-02 20:21:07 -080038 , m_shouldResume(false)
Yingdi Yu2c9e7712014-10-20 11:55:05 -070039 , m_contactManager(m_face)
Yingdi Yu2c9e7712014-10-20 11:55:05 -070040{
41 // connection to contact manager
42 connect(this, SIGNAL(identityUpdated(const QString&)),
43 &m_contactManager, SLOT(onIdentityUpdated(const QString&)));
44
45 connect(&m_contactManager, SIGNAL(contactIdListReady(const QStringList&)),
46 this, SLOT(onContactIdListReady(const QStringList&)));
47
Varun Patilb3d03b92021-01-08 17:09:32 +053048 m_validator = std::make_shared<ndn::security::ValidatorNull>();
Yingdi Yu2c9e7712014-10-20 11:55:05 -070049}
50
Varun Patilb3d03b92021-01-08 17:09:32 +053051ControllerBackend::~ControllerBackend() = default;
Yingdi Yu2c9e7712014-10-20 11:55:05 -070052
53void
54ControllerBackend::run()
55{
Yingdi Yuf3401182015-02-02 20:21:07 -080056 bool shouldResume = false;
57 do {
58 try {
59 setInvitationListener();
60 m_face.processEvents();
61 }
Varun Patila24bd3e2020-11-24 10:08:33 +053062 catch (const std::runtime_error& e) {
Yingdi Yuf3401182015-02-02 20:21:07 -080063 {
64 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
65 m_isNfdConnected = false;
66 }
67 emit nfdError();
68 {
69 std::lock_guard<std::mutex>lock(m_resumeMutex);
70 m_shouldResume = true;
71 }
72#ifdef BOOST_THREAD_USES_CHRONO
73 time::seconds reconnectTimer = time::seconds(CONNECTION_RETRY_TIMER);
74#else
75 boost::posix_time::time_duration reconnectTimer;
76 reconnectTimer = boost::posix_time::seconds(CONNECTION_RETRY_TIMER);
77#endif
78 while (!m_isNfdConnected) {
79#ifdef BOOST_THREAD_USES_CHRONO
80 boost::this_thread::sleep_for(reconnectTimer);
81#else
82 boost::this_thread::sleep(reconnectTimer);
83#endif
84 }
85 }
86 {
87 std::lock_guard<std::mutex>lock(m_resumeMutex);
88 shouldResume = m_shouldResume;
89 m_shouldResume = false;
90 }
91 } while (shouldResume);
Yingdi Yu2c9e7712014-10-20 11:55:05 -070092 std::cerr << "Bye!" << std::endl;
93}
94
95// private methods:
96
97void
98tmpOnInvitationInterest(const ndn::Name& prefix,
99 const ndn::Interest& interest)
100{
101 std::cerr << "tmpOnInvitationInterest" << std::endl;
102}
103
104void
105tmpOnInvitationRegisterFailed(const Name& prefix,
106 const string& failInfo)
107{
108 std::cerr << "tmpOnInvitationRegisterFailed" << std::endl;
109}
110
111void
112tmpUnregisterPrefixSuccessCallback()
113{
114 std::cerr << "tmpUnregisterPrefixSuccessCallback" << std::endl;
115}
116
117void
118tmpUnregisterPrefixFailureCallback(const string& failInfo)
119{
120 std::cerr << "tmpUnregisterPrefixSuccessCallback" << std::endl;
121}
122
123void
124ControllerBackend::setInvitationListener()
125{
126 QMutexLocker locker(&m_mutex);
127
128 Name invitationPrefix;
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800129 Name requestPrefix;
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700130 Name routingPrefix = getInvitationRoutingPrefix();
131 size_t offset = 0;
132 if (!routingPrefix.isPrefixOf(m_identity)) {
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800133 invitationPrefix.append(routingPrefix).append(ROUTING_HINT_SEPARATOR);
134 requestPrefix.append(routingPrefix).append(ROUTING_HINT_SEPARATOR);
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700135 offset = routingPrefix.size() + 1;
136 }
137 invitationPrefix.append(m_identity).append("CHRONOCHAT-INVITATION");
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800138 requestPrefix.append(m_identity).append("CHRONOCHAT-INVITATION-REQUEST");
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700139
Varun Patila24bd3e2020-11-24 10:08:33 +0530140 m_invitationListenerHandle = m_face.setInterestFilter(invitationPrefix,
141 bind(&ControllerBackend::onInvitationInterest, this, _1, _2, offset),
142 bind(&ControllerBackend::onInvitationRegisterFailed, this, _1, _2));
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700143
Varun Patila24bd3e2020-11-24 10:08:33 +0530144 m_requestListenerHandle = m_face.setInterestFilter(requestPrefix,
145 bind(&ControllerBackend::onInvitationRequestInterest, this, _1, _2, offset),
146 [] (const Name& prefix, const std::string& failInfo) {});
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700147}
148
149ndn::Name
150ControllerBackend::getInvitationRoutingPrefix()
151{
152 return Name("/ndn/broadcast");
153}
154
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700155
156void
157ControllerBackend::onInvitationInterest(const ndn::Name& prefix,
158 const ndn::Interest& interest,
159 size_t routingPrefixOffset)
160{
Varun Patilb3d03b92021-01-08 17:09:32 +0530161 auto invitationInterest = std::make_shared<Interest>(interest.getName().getSubName(routingPrefixOffset));
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700162
163 // check if the chatroom already exists;
164 try {
165 Invitation invitation(invitationInterest->getName());
166 if (m_chatDialogList.contains(QString::fromStdString(invitation.getChatroom())))
167 return;
168 }
Varun Patila24bd3e2020-11-24 10:08:33 +0530169 catch (const Invitation::Error& e) {
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700170 // Cannot parse the invitation;
171 return;
172 }
173
Varun Patil3d850902020-11-23 12:19:14 +0530174 m_validator->validate(
175 *invitationInterest,
176 bind(&ControllerBackend::onInvitationValidated, this, _1),
177 bind(&ControllerBackend::onInvitationValidationFailed, this, _1, _2));
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700178}
179
180void
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800181ControllerBackend::onInvitationRegisterFailed(const Name& prefix, const std::string& failInfo)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700182{
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700183}
184
185void
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800186ControllerBackend::onInvitationRequestInterest(const ndn::Name& prefix,
187 const ndn::Interest& interest,
188 size_t routingPrefixOffset)
189{
190 shared_ptr<const Data> data = m_ims.find(interest);
191 if (data != nullptr) {
192 m_face.put(*data);
193 return;
194 }
195 Name interestName = interest.getName();
196 size_t i;
197 for (i = 0; i < interestName.size(); i++)
198 if (interestName.at(i) == Name::Component("CHRONOCHAT-INVITATION-REQUEST"))
199 break;
200 if (i < interestName.size()) {
201 string chatroom = interestName.at(i+1).toUri();
202 string alias = interestName.getSubName(i+2).getPrefix(-1).toUri();
203 emit invitationRequestReceived(QString::fromStdString(alias),
204 QString::fromStdString(chatroom),
205 interestName);
206 }
207}
208
209void
Varun Patil3d850902020-11-23 12:19:14 +0530210ControllerBackend::onInvitationValidated(const Interest& interest)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700211{
Varun Patil3d850902020-11-23 12:19:14 +0530212 Invitation invitation(interest.getName());
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700213 // Should be obtained via a method of ContactManager.
Varun Patil3d850902020-11-23 12:19:14 +0530214 string alias = invitation.getInviterCertificate().getKeyName().getPrefix(-1).toUri();
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700215
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800216 emit invitationValidated(QString::fromStdString(alias),
217 QString::fromStdString(invitation.getChatroom()),
Varun Patil3d850902020-11-23 12:19:14 +0530218 interest.getName());
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700219}
220
221void
Varun Patil3d850902020-11-23 12:19:14 +0530222ControllerBackend::onInvitationValidationFailed(const Interest& interest,
223 const ndn::security::ValidationError& failureInfo)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700224{
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700225}
226
227void
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800228ControllerBackend::onLocalPrefix(const ndn::ConstBufferPtr& data)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700229{
230 Name prefix;
231
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800232 Block contentBlock(tlv::Content, data);
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700233 try {
234 contentBlock.parse();
235
236 for (Block::element_const_iterator it = contentBlock.elements_begin();
237 it != contentBlock.elements_end(); it++) {
238 Name candidate;
239 candidate.wireDecode(*it);
240 if (candidate.isPrefixOf(m_identity)) {
241 prefix = candidate;
242 break;
243 }
244 }
245
246 if (prefix.empty()) {
247 if (contentBlock.elements_begin() != contentBlock.elements_end())
248 prefix.wireDecode(*contentBlock.elements_begin());
249 else
250 prefix = Name("/private/local");
251 }
252 }
Varun Patila24bd3e2020-11-24 10:08:33 +0530253 catch (const Block::Error& e) {
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700254 prefix = Name("/private/local");
255 }
256
257 updateLocalPrefix(prefix);
258}
259
260void
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800261ControllerBackend::onLocalPrefixError(uint32_t code, const std::string& msg)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700262{
263 Name localPrefix("/private/local");
264 updateLocalPrefix(localPrefix);
265}
266
267void
268ControllerBackend::updateLocalPrefix(const Name& localPrefix)
269{
270 if (m_localPrefix.empty() || m_localPrefix != localPrefix) {
271 m_localPrefix = localPrefix;
272 emit localPrefixUpdated(QString::fromStdString(localPrefix.toUri()));
273 }
274}
275
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800276void
Varun Patil3d850902020-11-23 12:19:14 +0530277ControllerBackend::onRequestResponse(const Interest& interest, const Data& data)
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800278{
279 size_t i;
280 Name interestName = interest.getName();
281 for (i = 0; i < interestName.size(); i++) {
282 if (interestName.at(i) == Name::Component("CHRONOCHAT-INVITATION-REQUEST"))
283 break;
284 }
285 Name::Component chatroomName = interestName.at(i+1);
286 Block contentBlock = data.getContent();
287 int res = ndn::readNonNegativeInteger(contentBlock);
Qiuhan Ding59c45432015-04-01 18:15:03 -0700288 if (m_chatDialogList.contains(QString::fromStdString(chatroomName.toUri())))
289 return;
290
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800291 // if data is true,
292 if (res == 1)
293 emit startChatroom(QString::fromStdString(chatroomName.toUri()), false);
294 else
295 emit invitationRequestResult("You are rejected to enter chatroom: " + chatroomName.toUri());
296}
297
298void
299ControllerBackend::onRequestTimeout(const Interest& interest, int& resendTimes)
300{
301 if (resendTimes < MAXIMUM_REQUEST)
302 m_face.expressInterest(interest,
303 bind(&ControllerBackend::onRequestResponse, this, _1, _2),
Varun Patil3d850902020-11-23 12:19:14 +0530304 bind(&ControllerBackend::onRequestTimeout, this, _1, resendTimes + 1),
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800305 bind(&ControllerBackend::onRequestTimeout, this, _1, resendTimes + 1));
306 else
307 emit invitationRequestResult("Invitation request times out.");
308}
309
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700310// public slots:
311void
312ControllerBackend::shutdown()
313{
Yingdi Yuf3401182015-02-02 20:21:07 -0800314 {
315 std::lock_guard<std::mutex>lock(m_resumeMutex);
316 m_shouldResume = false;
317 }
318 {
319 // In this case, we just stop checking the nfd connection and exit
320 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
321 m_isNfdConnected = true;
322 }
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700323 m_face.getIoService().stop();
324}
325
326void
327ControllerBackend::addChatroom(QString chatroom)
328{
329 m_chatDialogList.append(chatroom);
330}
331
332void
333ControllerBackend::removeChatroom(QString chatroom)
334{
335 m_chatDialogList.removeAll(chatroom);
336}
337
338void
339ControllerBackend::onUpdateLocalPrefixAction()
340{
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800341 Interest interest("/localhop/nfd/rib/routable-prefixes");
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700342 interest.setInterestLifetime(time::milliseconds(1000));
343 interest.setMustBeFresh(true);
344
Varun Patil3d850902020-11-23 12:19:14 +0530345 auto fetcher = ndn::util::SegmentFetcher::start(m_face,
346 interest,
347 m_nullValidator);
348 fetcher->onComplete.connect(bind(&ControllerBackend::onLocalPrefix, this, _1));
349 fetcher->onError.connect(bind(&ControllerBackend::onLocalPrefixError, this, _1, _2));
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700350}
351
352void
353ControllerBackend::onIdentityChanged(const QString& identity)
354{
355 m_chatDialogList.clear();
356
357 m_identity = Name(identity.toStdString());
358
359 std::cerr << "ControllerBackend::onIdentityChanged: " << m_identity << std::endl;
360
361 m_keyChain.createIdentity(m_identity);
362
363 setInvitationListener();
364
365 emit identityUpdated(identity);
366}
367
368void
369ControllerBackend::onInvitationResponded(const ndn::Name& invitationName, bool accepted)
370{
Varun Patilb3d03b92021-01-08 17:09:32 +0530371 auto response = std::make_shared<Data>();
Varun Patil3d850902020-11-23 12:19:14 +0530372 shared_ptr<ndn::security::Certificate> chatroomCert;
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700373
374 // generate reply;
375 if (accepted) {
376 Name responseName = invitationName;
377 responseName.append(m_localPrefix.wireEncode());
378
379 response->setName(responseName);
380
381 // We should create a particular certificate for this chatroom,
382 //but let's use default one for now.
Varun Patilb3d03b92021-01-08 17:09:32 +0530383 chatroomCert = std::make_shared<ndn::security::Certificate>(
Varun Patil3d850902020-11-23 12:19:14 +0530384 m_keyChain.createIdentity(m_identity).getDefaultKey().getDefaultCertificate());
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700385
386 response->setContent(chatroomCert->wireEncode());
387 response->setFreshnessPeriod(time::milliseconds(1000));
388 }
389 else {
390 response->setName(invitationName);
391 response->setFreshnessPeriod(time::milliseconds(1000));
392 }
393
Varun Patil3d850902020-11-23 12:19:14 +0530394 m_keyChain.sign(*response, ndn::security::signingByIdentity(m_identity));
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700395
396 // Check if we need a wrapper
397 Name invitationRoutingPrefix = getInvitationRoutingPrefix();
398 if (invitationRoutingPrefix.isPrefixOf(m_identity))
399 m_face.put(*response);
400 else {
401 Name wrappedName;
402 wrappedName.append(invitationRoutingPrefix)
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800403 .append(ROUTING_HINT_SEPARATOR)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700404 .append(response->getName());
405
Varun Patilb3d03b92021-01-08 17:09:32 +0530406 auto wrappedData = std::make_shared<Data>(wrappedName);
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700407 wrappedData->setContent(response->wireEncode());
408 wrappedData->setFreshnessPeriod(time::milliseconds(1000));
409
Varun Patil3d850902020-11-23 12:19:14 +0530410 m_keyChain.sign(*wrappedData, ndn::security::signingByIdentity(m_identity));
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700411 m_face.put(*wrappedData);
412 }
413
414 Invitation invitation(invitationName);
415 emit startChatroomOnInvitation(invitation, true);
416}
417
418void
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800419ControllerBackend::onInvitationRequestResponded(const ndn::Name& invitationResponseName,
420 bool accepted)
421{
Varun Patilb3d03b92021-01-08 17:09:32 +0530422 auto response = std::make_shared<Data>(invitationResponseName);
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800423 if (accepted)
Junxiao Shi9887fd12016-07-23 02:09:00 +0000424 response->setContent(ndn::makeNonNegativeIntegerBlock(tlv::Content, 1));
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800425 else
Junxiao Shi9887fd12016-07-23 02:09:00 +0000426 response->setContent(ndn::makeNonNegativeIntegerBlock(tlv::Content, 0));
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800427
Varun Patil3d850902020-11-23 12:19:14 +0530428 response->setFreshnessPeriod(time::milliseconds(1000));
429 m_keyChain.sign(*response, ndn::security::signingByIdentity(m_identity));
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800430 m_ims.insert(*response);
431 m_face.put(*response);
432}
433
434void
435ControllerBackend::onSendInvitationRequest(const QString& chatroomName, const QString& prefix)
436{
437 if (prefix.length() == 0)
438 return;
439 Name interestName = getInvitationRoutingPrefix();
440 interestName.append(ROUTING_HINT_SEPARATOR).append(prefix.toStdString());
441 interestName.append("CHRONOCHAT-INVITATION-REQUEST");
442 interestName.append(chatroomName.toStdString());
443 interestName.append(m_identity);
444 interestName.appendTimestamp();
445 Interest interest(interestName);
446 interest.setInterestLifetime(time::milliseconds(10000));
447 interest.setMustBeFresh(true);
Varun Patila24bd3e2020-11-24 10:08:33 +0530448 interest.setCanBePrefix(true);
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800449 interest.getNonce();
450 m_face.expressInterest(interest,
451 bind(&ControllerBackend::onRequestResponse, this, _1, _2),
Varun Patil3d850902020-11-23 12:19:14 +0530452 bind(&ControllerBackend::onRequestTimeout, this, _1, 0),
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800453 bind(&ControllerBackend::onRequestTimeout, this, _1, 0));
454}
455
456void
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700457ControllerBackend::onContactIdListReady(const QStringList& list)
458{
459 ContactList contactList;
460
461 m_contactManager.getContactList(contactList);
Varun Patil3d850902020-11-23 12:19:14 +0530462 // m_validator.cleanTrustAnchor();
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700463
Varun Patil3d850902020-11-23 12:19:14 +0530464 // for (ContactList::const_iterator it = contactList.begin(); it != contactList.end(); it++)
465 // m_validator.addTrustAnchor((*it)->getPublicKeyName(), (*it)->getPublicKey());
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700466}
467
Yingdi Yuf3401182015-02-02 20:21:07 -0800468void
469ControllerBackend::onNfdReconnect()
470{
471 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
472 m_isNfdConnected = true;
473}
474
Yingdi Yueb692ac2015-02-10 18:46:18 -0800475} // namespace chronochat
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700476
477#if WAF
478#include "controller-backend.moc"
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700479#endif