blob: 581f3e030eaa595323d77966b5c7978cd3b68275 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2016, Regents of the University of California.
*
* This file is part of NDNS (Named Data Networking Domain Name Service).
* See AUTHORS.md for complete list of NDNS authors and contributors.
*
* NDNS 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.
*
* NDNS 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
* NDNS, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "daemon/name-server.hpp"
#include "daemon/db-mgr.hpp"
#include "clients/response.hpp"
#include "clients/query.hpp"
#include "test-common.hpp"
#include "unit/database-test-data.hpp"
namespace ndn {
namespace ndns {
namespace tests {
NDNS_LOG_INIT("NameServerTest")
class NameServerFixture : public DbTestData
{
public:
NameServerFixture()
: face({false, true})
, zone(m_root.getName())
, validator(face)
, server(zone, m_certName, face, m_session, m_keyChain, validator)
{
// ensure prefix is registered
run();
}
void
run()
{
face.getIoService().poll();
face.getIoService().reset();
}
public:
ndn::util::DummyClientFace face;
const Name& zone;
Validator validator;
ndns::NameServer server;
};
BOOST_FIXTURE_TEST_SUITE(NameServer, NameServerFixture)
BOOST_AUTO_TEST_CASE(NdnsQuery)
{
Query q(zone, ndns::label::NDNS_ITERATIVE_QUERY);
q.setRrLabel(Name("net"));
q.setRrType(ndns::label::NS_RR_TYPE);
bool hasDataBack = false;
face.onSendData.connectSingleShot([&] (const Data& data) {
hasDataBack = true;
NDNS_LOG_TRACE("get Data back");
BOOST_CHECK_EQUAL(data.getName().getPrefix(-1), q.toInterest().getName());
Response resp;
BOOST_CHECK_NO_THROW(resp.fromData(zone, data));
BOOST_CHECK_EQUAL(resp.getContentType(), NDNS_LINK);
});
face.receive(q.toInterest());
run();
BOOST_CHECK_EQUAL(hasDataBack, true);
}
BOOST_AUTO_TEST_CASE(KeyQuery)
{
Query q(zone, ndns::label::NDNS_ITERATIVE_QUERY);
q.setQueryType(ndns::label::NDNS_CERT_QUERY);
q.setRrType(ndns::label::CERT_RR_TYPE);
size_t nDataBack = 0;
// will ask for non-existing record
face.onSendData.connectSingleShot([&] (const Data& data) {
++nDataBack;
NDNS_LOG_TRACE("get Data back");
BOOST_CHECK_EQUAL(data.getName().getPrefix(-1), q.toInterest().getName());
Response resp;
BOOST_CHECK_NO_THROW(resp.fromData(zone, data));
BOOST_CHECK_EQUAL(resp.getContentType(), NDNS_NACK);
});
face.receive(q.toInterest());
run();
// will ask for the existing record (will have type NDNS_KEY, as it is certificate)
face.onSendData.connectSingleShot([&] (const Data& data) {
++nDataBack;
NDNS_LOG_TRACE("get Data back");
BOOST_CHECK_EQUAL(data.getName().getPrefix(-1), q.toInterest().getName());
Response resp;
BOOST_CHECK_NO_THROW(resp.fromData(zone, data));
BOOST_CHECK_EQUAL(resp.getContentType(), NDNS_KEY);
});
q.setRrLabel("dsk-1");
face.receive(q.toInterest());
run();
BOOST_CHECK_EQUAL(nDataBack, 2);
// explicit interest with correct version
face.receive(Interest("/test19/KEY/dsk-1/ID-CERT/%FDd"));
face.onSendData.connectSingleShot([&] (const Data& data) {
++nDataBack;
Response resp;
BOOST_CHECK_NO_THROW(resp.fromData(zone, data));
BOOST_CHECK_EQUAL(resp.getContentType(), NDNS_KEY);
});
run();
BOOST_CHECK_EQUAL(nDataBack, 3);
// explicit interest with wrong version
face.receive(Interest("/test19/KEY/dsk-1/ID-CERT/%FD010101010"));
face.onSendData.connectSingleShot([&] (const Data& data) {
++nDataBack;
Response resp;
BOOST_CHECK_NO_THROW(resp.fromData(zone, data));
BOOST_CHECK_EQUAL(resp.getContentType(), NDNS_NACK);
});
run();
BOOST_CHECK_EQUAL(nDataBack, 4);
}
BOOST_AUTO_TEST_CASE(UpdateReplaceRr)
{
Response re;
re.setZone(zone);
re.setQueryType(label::NDNS_ITERATIVE_QUERY);
re.setRrLabel(Name("net"));
re.setRrType(label::NS_RR_TYPE);
re.setContentType(NDNS_RESP);
std::string str = "ns1.ndnsim.net";
re.addRr(makeBinaryBlock(ndns::tlv::RrData, str.c_str(), str.size()));
str = "ns2.ndnsim.net";
re.addRr(makeBinaryBlock(ndns::tlv::RrData, str.c_str(), str.size()));
shared_ptr<Data> data = re.toData();
m_keyChain.sign(*data, m_certName);
Query q(Name(zone), ndns::label::NDNS_ITERATIVE_QUERY);
const Block& block = data->wireEncode();
Name name;
name.append(block);
q.setRrLabel(name);
q.setRrType(label::NDNS_UPDATE_LABEL);
bool hasDataBack = false;
face.onSendData.connectSingleShot([&] (const Data& data) {
hasDataBack = true;
NDNS_LOG_TRACE("get Data back");
BOOST_CHECK_EQUAL(data.getName().getPrefix(-1), q.toInterest().getName());
Response resp;
BOOST_CHECK_NO_THROW(resp.fromData(zone, data));
BOOST_CHECK_EQUAL(resp.getContentType(), NDNS_RESP); // by default NDNS_BLOB is enough
BOOST_CHECK_GT(resp.getRrs().size(), 0);
Block block = resp.getRrs()[0];
block.parse();
int ret = -1;
BOOST_CHECK_EQUAL(block.type(), ndns::tlv::RrData);
Block::element_const_iterator val = block.elements_begin();
BOOST_CHECK_EQUAL(val->type(), ndns::tlv::UpdateReturnCode); // the first must be return code
ret = readNonNegativeInteger(*val);
BOOST_CHECK_EQUAL(ret, 0);
});
face.receive(q.toInterest());
run();
BOOST_CHECK_EQUAL(hasDataBack, true);
}
BOOST_AUTO_TEST_CASE(UpdateInsertNewRr)
{
Response re;
re.setZone(zone);
re.setQueryType(label::NDNS_ITERATIVE_QUERY);
re.setRrLabel(Name("net-XYZ")); // insert new records
re.setRrType(label::NS_RR_TYPE);
re.setContentType(NDNS_RESP);
std::string str = "ns1.ndnsim.net";
re.addRr(makeBinaryBlock(ndns::tlv::RrData, str.c_str(), str.size()));
str = "ns2.ndnsim.net";
re.addRr(makeBinaryBlock(ndns::tlv::RrData, str.c_str(), str.size()));
shared_ptr<Data> data = re.toData();
m_keyChain.sign(*data, m_certName);
Query q(Name(zone), ndns::label::NDNS_ITERATIVE_QUERY);
const Block& block = data->wireEncode();
Name name;
name.append(block);
q.setRrLabel(name);
q.setRrType(label::NDNS_UPDATE_LABEL);
bool hasDataBack = false;
face.onSendData.connectSingleShot([&] (const Data& data) {
hasDataBack = true;
NDNS_LOG_TRACE("get Data back");
BOOST_CHECK_EQUAL(data.getName().getPrefix(-1), q.toInterest().getName());
Response resp;
BOOST_CHECK_NO_THROW(resp.fromData(zone, data));
BOOST_CHECK_EQUAL(resp.getContentType(), NDNS_RESP); // by default NDNS_BLOB is enough
BOOST_CHECK_GT(resp.getRrs().size(), 0);
Block block = resp.getRrs()[0];
block.parse();
int ret = -1;
BOOST_CHECK_EQUAL(block.type(), ndns::tlv::RrData);
Block::element_const_iterator val = block.elements_begin();
BOOST_CHECK_EQUAL(val->type(), ndns::tlv::UpdateReturnCode); // the first must be return code
ret = readNonNegativeInteger(*val);
BOOST_CHECK_EQUAL(ret, 0);
});
face.receive(q.toInterest());
run();
BOOST_CHECK_EQUAL(hasDataBack, true);
}
BOOST_AUTO_TEST_CASE(UpdateValidatorCannotFetchCert)
{
Name dskName = m_keyChain.generateRsaKeyPair(TEST_IDENTITY_NAME, false);
std::vector<CertificateSubjectDescription> desc;
time::system_clock::TimePoint notBefore = time::system_clock::now();
time::system_clock::TimePoint notAfter = notBefore + time::days(365);
shared_ptr<IdentityCertificate> dskCert =
m_keyChain.prepareUnsignedIdentityCertificate(dskName, m_certName,
notBefore, notAfter, desc);
m_keyChain.sign(*dskCert, m_certName);
m_keyChain.addCertificateAsKeyDefault(*dskCert);
NDNS_LOG_TRACE("KeyChain: add cert: " << dskCert->getName() << ". KeyLocator: "
<< dskCert->getSignature().getKeyLocator().getName());
Rrset rrset(&m_root);
Name label = dskCert->getName().getPrefix(-2).getSubName(m_root.getName().size() + 1);
rrset.setLabel(label);
rrset.setType(label::CERT_RR_TYPE);
rrset.setVersion(dskCert->getName().get(-1));
rrset.setTtl(m_root.getTtl());
rrset.setData(dskCert->wireEncode());
m_session.insert(rrset);
NDNS_LOG_TRACE("DB: zone " << m_root << " add a ID-CERT RR with name="
<< dskCert->getName() << " rrLabel=" << label);
Response re;
re.setZone(zone);
re.setQueryType(label::NDNS_ITERATIVE_QUERY);
re.setRrLabel(Name("ndnsim-XYZ")); // insert new records
re.setRrType(label::NS_RR_TYPE);
re.setContentType(NDNS_RESP);
std::string str = "ns1.ndnsim.net";
re.addRr(makeBinaryBlock(ndns::tlv::RrData, str.c_str(), str.size()));
str = "ns2.ndnsim.net";
re.addRr(makeBinaryBlock(ndns::tlv::RrData, str.c_str(), str.size()));
shared_ptr<Data> data = re.toData();
m_keyChain.sign(*data, dskCert->getName());
Query q(Name(zone), ndns::label::NDNS_ITERATIVE_QUERY);
const Block& block = data->wireEncode();
Name name;
name.append(block);
q.setRrLabel(name);
q.setRrType(label::NDNS_UPDATE_LABEL);
bool hasDataBack = false;
// no data back, since the Update cannot pass verification
face.onSendData.connectSingleShot([&] (const Data& data) {
hasDataBack = true;
BOOST_FAIL("UNEXPECTED");
});
face.receive(q.toInterest());
run();
BOOST_CHECK_EQUAL(hasDataBack, false);
}
class NameServerFixture2 : public DbTestData
{
public:
NameServerFixture2()
: face(io, m_keyChain, {false, true})
, validatorFace(io, m_keyChain, {false, true})
, zone(m_root.getName())
, validator(validatorFace) // different face for validator
, server(zone, m_certName, face, m_session, m_keyChain, validator)
{
// ensure prefix is registered
run();
validatorFace.onSendInterest.connect([this] (const Interest& interest) {
NDNS_LOG_TRACE("validatorFace get Interest: " << interest.getName());
shared_ptr<const Interest> i = interest.shared_from_this();
io.post([i, this] {
face.receive(*i);
});
});
}
void
run()
{
io.poll();
io.reset();
}
public:
boost::asio::io_service io;
ndn::util::DummyClientFace face;
ndn::util::DummyClientFace validatorFace;
const Name& zone;
Validator validator;
ndns::NameServer server;
};
BOOST_FIXTURE_TEST_CASE(UpdateValidatorFetchCert, NameServerFixture2)
{
Name dskName = m_keyChain.generateRsaKeyPair(TEST_IDENTITY_NAME, false);
std::vector<CertificateSubjectDescription> desc;
time::system_clock::TimePoint notBefore = time::system_clock::now();
time::system_clock::TimePoint notAfter = notBefore + time::days(365);
shared_ptr<IdentityCertificate> dskCert =
m_keyChain.prepareUnsignedIdentityCertificate(dskName, m_certName,
notBefore, notAfter, desc);
m_keyChain.sign(*dskCert, m_certName);
m_keyChain.addCertificateAsKeyDefault(*dskCert);
NDNS_LOG_TRACE("KeyChain: add cert: " << dskCert->getName() << ". KeyLocator: "
<< dskCert->getSignature().getKeyLocator().getName());
Rrset rrset(&m_root);
Name label = dskCert->getName().getPrefix(-2).getSubName(m_root.getName().size() + 1);
rrset.setLabel(label);
rrset.setType(label::CERT_RR_TYPE);
rrset.setVersion(dskCert->getName().get(-1));
rrset.setTtl(m_root.getTtl());
rrset.setData(dskCert->wireEncode());
m_session.insert(rrset);
NDNS_LOG_TRACE("DB: zone " << m_root << " add a ID-CERT RR with name="
<< dskCert->getName() << " rrLabel=" << label);
Response re;
re.setZone(zone);
re.setQueryType(label::NDNS_ITERATIVE_QUERY);
re.setRrLabel(Name("ndnsim-XYZ")); // insert new records
re.setRrType(label::NS_RR_TYPE);
re.setContentType(NDNS_RESP);
std::string str = "ns1.ndnsim.net";
re.addRr(makeBinaryBlock(ndns::tlv::RrData, str.c_str(), str.size()));
str = "ns2.ndnsim.net";
re.addRr(makeBinaryBlock(ndns::tlv::RrData, str.c_str(), str.size()));
shared_ptr<Data> data = re.toData();
m_keyChain.sign(*data, dskCert->getName());
Query q(Name(zone), ndns::label::NDNS_ITERATIVE_QUERY);
const Block& block = data->wireEncode();
Name name;
name.append(block);
q.setRrLabel(name);
q.setRrType(label::NDNS_UPDATE_LABEL);
bool hasDataBack = false;
shared_ptr<Regex> regex = make_shared<Regex>("(<>*)<KEY>(<>+)<ID-CERT><>");
face.onSendData.connect([&] (const Data& data) {
if (regex->match(data.getName())) {
shared_ptr<const Data> d = data.shared_from_this();
io.post([d, this] {
validatorFace.receive(*d); // It's data requested by validator
});
}
else {
// cert is requested by validator
hasDataBack = true;
NDNS_LOG_TRACE("get Data back");
BOOST_CHECK_EQUAL(data.getName().getPrefix(-1), q.toInterest().getName());
Response resp;
BOOST_CHECK_NO_THROW(resp.fromData(zone, data));
BOOST_CHECK_EQUAL(resp.getContentType(), NDNS_RESP); // by default NDNS_BLOB is enough
BOOST_CHECK_GT(resp.getRrs().size(), 0);
Block block = resp.getRrs()[0];
block.parse();
int ret = -1;
BOOST_CHECK_EQUAL(block.type(), ndns::tlv::RrData);
Block::element_const_iterator val = block.elements_begin();
BOOST_CHECK_EQUAL(val->type(), ndns::tlv::UpdateReturnCode); // the first must be return code
ret = readNonNegativeInteger(*val);
BOOST_CHECK_EQUAL(ret, 0);
}
});
face.receive(q.toInterest());
run();
BOOST_CHECK_EQUAL(hasDataBack, true);
}
BOOST_AUTO_TEST_SUITE_END()
} // namespace tests
} // namespace ndns
} // namespace ndn