Commit 7f0bf288 authored by Pavel Doležal's avatar Pavel Doležal

Merge branch 'develop'

parents 1b970059 355c4d16
Pipeline #63716 passed with stages
in 6 minutes and 52 seconds
......@@ -7,7 +7,7 @@
#
cmake_minimum_required(VERSION 3.5)
project(cdns VERSION 0.3.0)
project(cdns VERSION 0.4.0)
include(GNUInstallDirs)
......@@ -18,7 +18,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
find_package(Boost REQUIRED)
find_package(ZLIB REQUIRED)
find_package(LibLZMA REQUIRED)
find_package(CBOR REQUIRED)
find_package(Doxygen)
option(BUILD_TESTS "Set to ON to build tests that use Google Test framework" OFF)
......@@ -30,7 +29,7 @@ add_library(cdns SHARED
${sources}
)
target_link_libraries(cdns ${Boost_LIBRARIES} CBOR::CBOR ZLIB::ZLIB ${LIBLZMA_LIBRARIES})
target_link_libraries(cdns ${Boost_LIBRARIES} ZLIB::ZLIB ${LIBLZMA_LIBRARIES})
target_include_directories(cdns PUBLIC ${Boost_INCLUDE_DIRS} ${LIBLZMA_INCLUDE_DIRS})
include(CheckCCompilerFlag)
......
......@@ -8,7 +8,6 @@ This project has the following dependencies:
* [CMake >= 3.5] (https://cmake.org/)
* [Boost] (https://www.boost.org/)
* [libcbor] (https://github.com/PJK/libcbor)
* [zlib] (https://www.zlib.net/)
* [XZ Utils] (https://tukaani.org/xz/)
......@@ -24,7 +23,54 @@ make install
```
If you don't want to build the test suite with the library, you can omit `-DBUILD_TESTS` option.
To generate Doxygen documentation run `make doc`.
To generate Doxygen documentation run `make doc`. Doxygen documentation for current release can be found [here](https://knot.pages.labs.nic.cz/c-dns/).
# Installation from packages
Packages for Debian 10 and 9 and Ubuntu 20.04, 18.04 and 16.04 are available from
[OBS (openSUSE Build Service)](https://build.opensuse.org/project/show/home:CZ-NIC:dns-probe).
First you need to add the OBS repository for given distribution to your system's repository list and download the repository's signing key:
##### Debian 10
```shell
sudo echo 'deb http://download.opensuse.org/repositories/home:/CZ-NIC:/dns-probe/Debian_10/ /' > /etc/apt/sources.list.d/dns-probe.list
wget -nv http://download.opensuse.org/repositories/home:CZ-NIC:dns-probe/Debian_10/Release.key -O Release.key
```
##### Debian 9
```shell
sudo echo 'deb http://download.opensuse.org/repositories/home:/CZ-NIC:/dns-probe/Debian_9.0/ /' > /etc/apt/sources.list.d/dns-probe.list
wget -nv http://download.opensuse.org/repositories/home:CZ-NIC:dns-probe/Debian_9.0/Release.key -O Release.key
```
##### Ubuntu 20.04
```shell
sudo echo 'deb http://download.opensuse.org/repositories/home:/CZ-NIC:/dns-probe/xUbuntu_20.04/ /' > /etc/apt/sources.list.d/dns-probe.list
wget -nv http://download.opensuse.org/repositories/home:CZ-NIC:dns-probe/xUbuntu_20.04/Release.key -O Release.key
```
##### Ubuntu 18.04
```shell
sudo echo 'deb http://download.opensuse.org/repositories/home:/CZ-NIC:/dns-probe/xUbuntu_18.04/ /' > /etc/apt/sources.list.d/dns-probe.list
wget -nv http://download.opensuse.org/repositories/home:CZ-NIC:dns-probe/xUbuntu_18.04/Release.key -O Release.key
```
##### Ubuntu 16.04
```shell
sudo echo 'deb http://download.opensuse.org/repositories/home:/CZ-NIC:/dns-probe/xUbuntu_16.04/ /' > /etc/apt/sources.list.d/dns-probe.list
wget -nv http://download.opensuse.org/repositories/home:CZ-NIC:dns-probe/xUbuntu_16.04/Release.key -O Release.key
```
Now you need to add the signing key to your system, update the repository list and then you can finally install the C-DNS library:
```shell
sudo apt-key add - < Release.key
sudo apt-get update
sudo apt-get install libcdns0 libcdns-dev
```
The C-DNS library is separated into two packages. `libcdns0` package installs the shared library and `libcdns-dev` package installs
development headers.
## Basic Usage
......@@ -32,16 +78,32 @@ To use the C-DNS library you only have to include the `<cdns/cdns.h>` header fil
```cpp
#include <cdns/cdns.h>
...
// Create C-DNS file
CDNS::FilePreamble fp;
CDNS::CdnsExporter exporter(fp, "output.out", CDNS::CborOutputCompression::XZ);
CDNS::QueryResponse qr;
CDNS::CdnsExporter* exporter = new CDNS::CdnsExporter(fp, "output.out", CDNS::CborOutputCompression::NO_COMPRESSION);
CDNS::GenericQueryResponse qr;
qr.client_port = 1234;
exporter.buffer_qr(qr);
exporter.write_block();
```
exporter->buffer_qr(qr);
exporter->write_block();
delete exporter;
// Read C-DNS file
std::ifstream ifs("output.out", std::ifstream::binary);
CDNS::CdnsReader reader = new CDNS::CdnsReader(ifs);
## TODO-List
bool end = false;
while (true) {
CDNS::CdnsBlockRead block = reader->read_block(end);
if (end)
break;
* [ ] Implement C-DNS decoder
* [ ] Enhance library interface
while (true) {
CDNS::GenericQueryResponse gqr = block.read_generic_qr(end);
if (end)
break;
...
}
}
delete reader;
```
#
# Copyright © 2019 CZ.NIC, z. s. p. o.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
#
# - Try to find libcbor include dirs and libraries
#
# Usage of this module as follows:
#
# find_package(CBOR)
#
# Variables used by this module, they can change the default behaviour and need
# to be set before calling find_package:
#
# CBOR_ROOT_DIR Set this variable to the root installation of
# libcbor if the module has problems finding the
# proper installation path.
#
# Variables defined by this module:
#
# CBOR_FOUND System has libcbor, include and library dirs found
# CBOR_INCLUDE_DIR The libcbor include directories.
# CBOR_LIBRARY The libcbor library
find_path(CBOR_ROOT_DIR NAMES include/cbor.h)
find_path(CBOR_INCLUDE_DIR NAMES cbor.h HINTS ${CBOR_ROOT_DIR}/include)
find_library(CBOR_LIBRARY NAMES cbor HINTS ${CBOR_ROOT_DIR}/lib)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CBOR DEFAULT_MSG CBOR_ROOT_DIR)
mark_as_advanced(
CBOR_ROOT_DIR
CBOR_INCLUDE_DIR
CBOR_LIBRARY
)
if(CBOR_FOUND)
add_library(CBOR::CBOR INTERFACE IMPORTED)
set_property(TARGET CBOR::CBOR PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${CBOR_INCLUDE_DIR})
set_property(TARGET CBOR::CBOR PROPERTY INTERFACE_LINK_LIBRARIES ${CBOR_LIBRARY})
endif()
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
......@@ -6,6 +6,8 @@
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include <algorithm>
#include "cdns.h"
std::size_t CDNS::CdnsExporter::write_block(CdnsBlock& block)
......@@ -44,3 +46,47 @@ std::size_t CDNS::CdnsExporter::write_file_header()
return written;
}
void CDNS::CdnsReader::read_file_header()
{
bool indef = false;
uint64_t length = m_decoder.read_array_start(indef);
if (length != 3 && !indef)
throw CdnsDecoderException("Invalid structure of C-DNS file");
// Read File type ID -> "C-DNS" string
std::string file_start = m_decoder.read_textstring();
std::transform(file_start.begin(), file_start.end(), file_start.begin(), toupper);
if (file_start != "C-DNS")
throw CdnsDecoderException(("Invalid File type ID: " + file_start).c_str());
// Read File preamble
m_file_preamble.read(m_decoder);
// Read start of File blocks array
m_blocks_count = m_decoder.read_array_start(m_indef_blocks);
}
CDNS::CdnsBlockRead CDNS::CdnsReader::read_block(bool& eof)
{
CdnsBlockRead block;
eof = false;
if (m_indef_blocks && m_decoder.peek_type() == CborType::BREAK) {
m_decoder.read_break();
eof = true;
m_indef_blocks = false;
m_blocks_count = m_blocks_read;
return block;
}
else if (!m_indef_blocks && m_blocks_read == m_blocks_count) {
eof = true;
return block;
}
block.read(m_decoder, m_file_preamble.m_block_parameters);
m_blocks_read++;
return block;
}
\ No newline at end of file
......@@ -8,11 +8,11 @@
#pragma once
#include <cbor.h>
#include <time.h>
#include <endian.h>
#include <string.h>
#include <stdlib.h>
#include <istream>
#include <sys/socket.h>
#include "format_specification.h"
......@@ -25,11 +25,12 @@
#include "timestamp.h"
#include "writer.h"
#include "cdns_encoder.h"
#include "cdns_decoder.h"
namespace CDNS {
/**
* @brief Class serving as C-DNS library's main interface with users
* @brief Class serving as C-DNS library's main interface for writing C-DNS to output
*
* CdnsExporter is initialized with filled File preamble structure, output destination
* and output compression option. User only needs to call buffer_*() methods, which
......@@ -98,7 +99,7 @@ namespace CDNS {
*/
std::size_t buffer_aec(const GenericAddressEventCount& aec, const boost::optional<BlockStatistics>& stats = boost::none) {
std::size_t written = 0;
if (m_block.add_addres_event_count(aec, stats))
if (m_block.add_address_event_count(aec, stats))
written = write_block();
return written;
......@@ -273,4 +274,52 @@ namespace CDNS {
*/
std::size_t m_blocks_written;
};
/**
* @brief Class serving as C-DNS library's main interface for reading C-DNS data from
* input
*
* CdnsReader is initialized with valid input stream containing uncompressed C-DNS data.
* CdnsReader constructor automatically reads the beginning of C-DNS file including its
* file preamble. User then reads the input by Blocks with the read_block(bool& eof) method.
* From these Blocks user can extract Query Response pairs and other data. When CdnsReader
* reaches the end of C-DNS data it sets the "eof" parameter in read_block() method to TRUE.
* CdnsBlockRead returned by this call is then empty.
*/
class CdnsReader {
public:
/**
* @brief Construct a new CdnsReader object to read uncompressed C-DNS data.
* The constructor automatically reads the start of C-DNS file and filles
* the m_file_preamble item.
* @param input Valid input stream to read C-DNS data from
*/
CdnsReader(std::istream& input) : m_file_preamble(),
m_decoder(input),
m_blocks_count(0),
m_blocks_read(0),
m_indef_blocks(false) { read_file_header(); }
/**
* @brief Read whole C-DNS Block from input stream
* @param eof If set by this method to TRUE, then reader has reached the end
* of C-DNS file and the returned C-DNS block is empty. Otherwise set to FALSE.
* @return New C-DNS Block read from input stream
*/
CdnsBlockRead read_block(bool& eof);
FilePreamble m_file_preamble; //!< C-DNS file preamble
private:
/**
* @brief Reads the start of C-DNS file including file preamble and
* start of the block array from input stream
*/
void read_file_header();
CdnsDecoder m_decoder;
uint64_t m_blocks_count;
uint64_t m_blocks_read;
bool m_indef_blocks;
};
}
/**
* Copyright © 2020 CZ.NIC, z. s. p. o.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "cdns_decoder.h"
CDNS::CborType CDNS::CdnsDecoder::peek_type()
{
read_to_buffer();
if (static_cast<CborType>(m_p[0]) == CborType::BREAK)
return CborType::BREAK;
else
return static_cast<CborType>(m_p[0] & 0xE0);
}
uint64_t CDNS::CdnsDecoder::read_unsigned()
{
CborType cbor_type;
uint8_t item_length;
read_cbor_type(cbor_type, item_length);
if (cbor_type != CborType::UNSIGNED) {
throw CdnsDecoderException(("read_unsigned() called on wrong major type " +
std::to_string(static_cast<uint8_t>(cbor_type) >> 5)).c_str());
}
else if (item_length >= 28) {
throw CdnsDecoderException(("Unsupported CBOR additional information value: " +
std::to_string(item_length)).c_str());
}
return read_int(item_length);
}
int64_t CDNS::CdnsDecoder::read_negative()
{
CborType cbor_type;
uint8_t item_length;
read_cbor_type(cbor_type, item_length);
if (cbor_type != CborType::NEGATIVE) {
throw CdnsDecoderException(("read_negative() called on wrong major type " +
std::to_string(static_cast<uint8_t>(cbor_type) >> 5)).c_str());
}
else if (item_length >= 28) {
throw CdnsDecoderException(("Unsupported CBOR additional information value: " +
std::to_string(item_length)).c_str());
}
return -1 - read_int(item_length);
}
int64_t CDNS::CdnsDecoder::read_integer()
{
CborType peek = peek_type();
switch (peek) {
case CborType::UNSIGNED:
return read_unsigned();
break;
case CborType::NEGATIVE:
return read_negative();
break;
default:
throw CdnsDecoderException(("read_integer() called on wrong major type " +
std::to_string(static_cast<uint8_t>(peek) >> 5)).c_str());
break;
}
}
bool CDNS::CdnsDecoder::read_bool()
{
CborType cbor_type;
uint8_t bool_value;
read_cbor_type(cbor_type, bool_value);
if (cbor_type != CborType::SIMPLE && cbor_type != CborType::UNSIGNED) {
throw CdnsDecoderException(("read_bool() called on wrong major type " +
std::to_string(static_cast<uint8_t>(cbor_type) >> 5)).c_str());
}
else if (cbor_type == CborType::SIMPLE && (bool_value != 20 && bool_value != 21)) {
throw CdnsDecoderException("CBOR additional information value isn't bool");
}
else if (cbor_type == CborType::UNSIGNED && bool_value >= 28) {
throw CdnsDecoderException(("Unsupported CBOR additional information value: " +
std::to_string(bool_value)).c_str());
}
if (cbor_type == CborType::SIMPLE) {
return bool_value == 21;
}
else {
return read_int(bool_value) != 0;
}
}
std::string CDNS::CdnsDecoder::read_bytestring()
{
CborType cbor_type;
uint8_t item_length;
read_cbor_type(cbor_type, item_length);
if (cbor_type != CborType::BYTE_STRING) {
throw CdnsDecoderException(("read_bytestring() called on wrong major type " +
std::to_string(static_cast<uint8_t>(cbor_type) >> 5)).c_str());
}
else if (item_length >= 28 && item_length <= 30) {
throw CdnsDecoderException(("Unsupported CBOR additional information value: " +
std::to_string(item_length)).c_str());
}
return read_string(CborType::BYTE_STRING, read_int(item_length), item_length == 31 ? true : false);
}
std::string CDNS::CdnsDecoder::read_textstring()
{
CborType cbor_type;
uint8_t item_length;
read_cbor_type(cbor_type, item_length);
if (cbor_type != CborType::TEXT_STRING) {
throw CdnsDecoderException(("read_textstring() called on wrong major type " +
std::to_string(static_cast<uint8_t>(cbor_type) >> 5)).c_str());
}
else if (item_length >= 28 && item_length <= 30) {
throw CdnsDecoderException(("Unsupported CBOR additional information value: " +
std::to_string(item_length)).c_str());
}
return read_string(CborType::TEXT_STRING, read_int(item_length), item_length == 31 ? true : false);
}
uint64_t CDNS::CdnsDecoder::read_array_start(bool& indef)
{
CborType cbor_type;
uint8_t array_length_value;
read_cbor_type(cbor_type, array_length_value);
if (cbor_type != CborType::ARRAY) {
throw CdnsDecoderException(("read_array_start() called on wrong major type " +
std::to_string(static_cast<uint8_t>(cbor_type) >> 5)).c_str());
}
else if (array_length_value >= 28 && array_length_value <= 30) {
throw CdnsDecoderException(("Unsupported CBOR additional information value: " +
std::to_string(array_length_value)).c_str());
}
if (array_length_value == 31) {
indef = true;
return 0;
}
indef = false;
return read_int(array_length_value);
}
uint64_t CDNS::CdnsDecoder::read_map_start(bool& indef)
{
CborType cbor_type;
uint8_t map_length_value;
read_cbor_type(cbor_type, map_length_value);
if (cbor_type != CborType::MAP) {
throw CdnsDecoderException(("read_map_start() called on wrong major type " +
std::to_string(static_cast<uint8_t>(cbor_type) >> 5)).c_str());
}
else if (map_length_value >= 28 && map_length_value <= 30) {
throw CdnsDecoderException(("Unsupported CBOR additional information value: " +
std::to_string(map_length_value)).c_str());
}
if (map_length_value == 31) {
indef = true;
return 0;
}
indef = false;
return read_int(map_length_value);
}
void CDNS::CdnsDecoder::read_break()
{
CborType cbor_type;
uint8_t break_code;
read_cbor_type(cbor_type, break_code);
if (cbor_type != CborType::SIMPLE || break_code != 31) {
throw CdnsDecoderException(("read_break() called on wrong major type " +
std::to_string(static_cast<uint8_t>(cbor_type) >> 5)).c_str());
}
}
void CDNS::CdnsDecoder::skip_item()
{
CborType cbor_type;
uint8_t item_length;
read_cbor_type(cbor_type, item_length);
switch (cbor_type) {
case CborType::UNSIGNED:
case CborType::NEGATIVE:
case CborType::TAG:
if (item_length >= 28) {
throw CdnsDecoderException(("Unsupported CBOR additional information value: " +
std::to_string(item_length)).c_str());
}
read_int(item_length);
break;
case CborType::SIMPLE:
if (item_length >= 28 && item_length <= 30) {
throw CdnsDecoderException(("Unsupported CBOR additional information value: " +
std::to_string(item_length)).c_str());
}
read_int(item_length);
break;
case CborType::BYTE_STRING:
case CborType::TEXT_STRING:
if (item_length >= 28 && item_length <= 30) {
throw CdnsDecoderException(("Unsupported CBOR additional information value: " +
std::to_string(item_length)).c_str());
}
read_string(cbor_type, read_int(item_length), item_length == 31 ? true : false);
break;
case CborType::ARRAY:
case CborType::MAP:
if (item_length >= 28 && item_length <= 30) {
throw CdnsDecoderException(("Unsupported CBOR additional information value: " +
std::to_string(item_length)).c_str());
}
if (item_length == 31) {
while(true) {
if (peek_type() == CborType::SIMPLE && (m_p[0] & 0x1F) == 31) {
m_p++;
break;
}
skip_item();
if (cbor_type == CborType::MAP)
skip_item();
}
}
else {
uint64_t item_count = read_int(item_length);
for (int i = 0; i < item_count; i++) {
skip_item();
if (cbor_type == CborType::MAP)
skip_item();
}
}
break;
default:
throw CdnsDecoderException(("Unknown CBOR major type " +
std::to_string(static_cast<uint8_t>(cbor_type) >> 5)).c_str());
break;
}
}
void CDNS::CdnsDecoder::read_cbor_type(CborType& cbor_type, uint8_t& additional)
{
read_to_buffer();
cbor_type = static_cast<CborType>(m_p[0] & 0xE0);
additional = m_p[0] & 0x1F;
m_p++;
}
uint64_t CDNS::CdnsDecoder::read_int(uint8_t item_length)
{
uint64_t value = 0;
if (item_length <= 23) {
return item_length;
}
else if (item_length >= 24 && item_length <= 27) {
for (int i = 1 << (item_length - 24); i > 0; i--) {
read_to_buffer();
value += (m_p[0] << ((i - 1) * 8));
m_p++;
}
}
return value;
}
std::string CDNS::CdnsDecoder::read_string(CborType cbor_type, uint64_t length, bool indef)
{
std::string ret;
if (!indef) {
ret.reserve(length);
for (int i = 0; i < length; i++) {
read_to_buffer();
ret.push_back(m_p[0]);
m_p++;
}
}
else {
while (peek_type() != CborType::SIMPLE) {
CborType chunk_type;
uint8_t chunk_length_value;
read_cbor_type(chunk_type, chunk_length_value);
if (chunk_type != cbor_type) {
throw CdnsDecoderException(("Different chunk major type inside indefinite length string: " +
std::to_string(static_cast<uint8_t>(chunk_type) >> 5)).c_str());
}
else if (chunk_length_value == 31) {
throw CdnsDecoderException("Indefinite length chunk inside indefinite length string");
}
uint64_t chunk_length = read_int(chunk_length_value);
ret.reserve(ret.size() + chunk_length);
for (int i = 0; i < chunk_length; i++) {
read_to_buffer();
ret.push_back(m_p[0]);
m_p++;
}
}
read_break();
}
return ret;
}