diff --git a/include/tins/pdu_iterator.h b/include/tins/pdu_iterator.h new file mode 100644 index 0000000..abc5685 --- /dev/null +++ b/include/tins/pdu_iterator.h @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2017, Matias Fontanini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef TINS_PDU_ITERATOR_H +#define TINS_PDU_ITERATOR_H + +#include + +namespace Tins { + +class PDU; +class Packet; + +/** + * Base class for PDU iterators + */ +template +class PDUIteratorBase { +public: + /** + * Iterator's category + */ + typedef std::bidirectional_iterator_tag iterator_category; + + /** + * Iterator difference type + */ + typedef std::ptrdiff_t difference_type; + + /** + * Advances this iterator + */ + Concrete& operator++() { + advance(); + return static_cast(*this); + } + + /** + * Advances this iterator + */ + Concrete operator++(int) { + Concrete output = static_cast(*this); + advance(); + return output; + } + + /** + * Moves this iterator back + */ + Concrete& operator--() { + retreat(); + return static_cast(*this); + } + + /** + * Moves this iterator back + */ + Concrete operator--(int) { + Concrete output = static_cast(*this); + retreat(); + return output; + } +private: + void advance() { + Concrete& self = static_cast(*this); + self = Concrete(self->inner_pdu()); + } + + void retreat() { + Concrete& self = static_cast(*this); + self = Concrete(self->parent_pdu()); + } +}; + +/** + * Compares iterators for equality + * + * \param lhs The left hand side iterator to be compared + * \param rhs The right hand side iterator to be compared + */ +template +bool operator==(const PDUIteratorBase& lhs, const PDUIteratorBase& rhs) { + const PDU* lhs_pdu = &*static_cast(lhs); + const PDU* rhs_pdu = &*static_cast(rhs); + return lhs_pdu == rhs_pdu; +} + +/** + * Compares iterators for equality + * + * \param lhs The left hand side iterator to be compared + * \param rhs The right hand side iterator to be compared + */ +template +bool operator!=(const PDUIteratorBase& lhs, const PDUIteratorBase& rhs) { + return !(lhs == rhs); +} + +/** + * Iterator class for PDUs + */ +class PDUIterator : public PDUIteratorBase { +public: + /** + * The used pointer type + */ + typedef PDU* pointer; + + /** + * The used reference type + */ + typedef PDU& reference; + + /** + * The used value type + */ + typedef PDU& value_type; + + /** + * Constructs an iterator using a PDU + * + * \param pdu The PDU to be used for iteration + */ + PDUIterator(pointer pdu); + + /** + * Get the stored PDU pointer + */ + pointer operator->(); + + /** + * Get the stored PDU pointer + */ + pointer operator->() const; + + /** + * Dereference and get the stored PDU + */ + value_type operator*(); + + /** + * Dereference and get the stored PDU + */ + const value_type operator*() const; +private: + pointer pdu_; +}; + +/** + * Const iterator class for PDUs + */ +class ConstPDUIterator : public PDUIteratorBase { +public: + /** + * The used pointer type + */ + typedef const PDU* pointer; + + /** + * The used reference type + */ + typedef const PDU& reference; + + /** + * The used value type + */ + typedef const PDU& value_type; + + /** + * Constructs an iterator using a PDU + * + * \param pdu The PDU to be used for iteration + */ + ConstPDUIterator(pointer pdu); + + /** + * Construct from a PDU iterator + */ + ConstPDUIterator(PDUIterator iterator); + + /** + * Get the stored PDU pointer + */ + pointer operator->() const; + + /** + * Dereference and get the stored PDU + */ + value_type operator*() const; +private: + pointer pdu_; +}; + +/* + * \brief PDU iterator class + * + * This class allows iterating all PDUs in a packet. + * + * Note that this keeps pointers to the original PDUs so you need to guarantee that they're + * still in scope while you iterate them. + */ +template +class PDUIteratorRange { +public: + /** + * Constructs a PDU iterator range + * + * \param start The beginning of the range + * \param end The end of the range + */ + PDUIteratorRange(Iterator start, Iterator end) + : start_(start), end_(end) { + + } + + template + PDUIteratorRange(const PDUIteratorRange& other) + : start_(&*other.begin()), end_(&*other.end()) { + + } + + /* + * Gets the beginning of the range + */ + Iterator begin() { + return start_; + } + + /* + * Gets the beginning of the range + */ + Iterator begin() const { + return start_; + } + + /* + * Gets the end of the range + */ + Iterator end() { + return end_; + } + + /* + * Gets the end of the range + */ + Iterator end() const { + return end_; + } +private: + Iterator start_; + Iterator end_; +}; + +/** + * Creates an iterator range out of a PDU + */ +PDUIteratorRange iterate_pdus(PDU* pdu); + +/** + * Creates an iterator range out of a PDU + */ +PDUIteratorRange iterate_pdus(PDU& pdu); + +/** + * Creates an iterator range out of a PDU + */ +PDUIteratorRange iterate_pdus(Packet& packet); + +/** + * Creates an iterator range out of a PDU + */ +PDUIteratorRange iterate_pdus(const PDU* pdu); + +/** + * Creates an iterator range out of a PDU + */ +PDUIteratorRange iterate_pdus(const PDU& pdu); + +/** + * Creates an iterator range out of a packet + */ +PDUIteratorRange iterate_pdus(const Packet& packet); + +} // Tins + +#endif // TINS_PDU_ITERATOR_H diff --git a/include/tins/tins.h b/include/tins/tins.h index 86ff7bf..55a5efd 100644 --- a/include/tins/tins.h +++ b/include/tins/tins.h @@ -74,5 +74,6 @@ #include "ipsec.h" #include "ip_reassembler.h" #include "ppi.h" +#include "pdu_iterator.h" #endif // TINS_TINS_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 82a7ae0..6f1dd91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,6 +46,7 @@ set(SOURCES pdu.cpp radiotap.cpp address_range.cpp + pdu_iterator.cpp rawpdu.cpp rsn_information.cpp sll.cpp diff --git a/src/pdu_iterator.cpp b/src/pdu_iterator.cpp new file mode 100644 index 0000000..12c154d --- /dev/null +++ b/src/pdu_iterator.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017, Matias Fontanini + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "pdu_iterator.h" +#include "pdu.h" +#include "packet.h" + +namespace Tins { + +// PDUIterator +PDUIterator::PDUIterator(pointer pdu) +: pdu_(pdu) { + +} + +PDUIterator::pointer PDUIterator::operator->() { + return pdu_; +} + +PDUIterator::pointer PDUIterator::operator->() const { + return pdu_; +} + +PDUIterator::value_type PDUIterator::operator*() { + return *pdu_; +} + +const PDUIterator::value_type PDUIterator::operator*() const { + return *pdu_; +} + +// ConstPDUIterator + +ConstPDUIterator::ConstPDUIterator(pointer pdu) +: pdu_(pdu) { + +} + +ConstPDUIterator::ConstPDUIterator(PDUIterator iterator) +: pdu_(&*iterator) { + +} + +ConstPDUIterator::pointer ConstPDUIterator::operator->() const { + return pdu_; +} + +ConstPDUIterator::value_type ConstPDUIterator::operator*() const { + return *pdu_; +} + +// Helpers + +PDUIteratorRange iterate_pdus(PDU* pdu) { + return PDUIteratorRange(pdu, 0); +} + +PDUIteratorRange iterate_pdus(PDU& pdu) { + return PDUIteratorRange(&pdu, 0); +} + +PDUIteratorRange iterate_pdus(Packet& packet) { + return PDUIteratorRange(packet.pdu(), 0); +} + +PDUIteratorRange iterate_pdus(const PDU* pdu) { + return PDUIteratorRange(pdu, 0); +} + +PDUIteratorRange iterate_pdus(const PDU& pdu) { + return PDUIteratorRange(&pdu, 0); +} + +PDUIteratorRange iterate_pdus(const Packet& packet) { + return PDUIteratorRange(packet.pdu(), 0); +} + +} // Tins diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt index e2b5dca..d35244c 100644 --- a/tests/src/CMakeLists.txt +++ b/tests/src/CMakeLists.txt @@ -76,6 +76,7 @@ CREATE_TEST(matches_response) CREATE_TEST(mpls) CREATE_TEST(network_interface) CREATE_TEST(pdu) +CREATE_TEST(pdu_iterator) CREATE_TEST(pppoe) CREATE_TEST(radiotap) CREATE_TEST(rc4_eapol) diff --git a/tests/src/pdu_iterator_test.cpp b/tests/src/pdu_iterator_test.cpp new file mode 100644 index 0000000..22fa529 --- /dev/null +++ b/tests/src/pdu_iterator_test.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include "ip.h" +#include "tcp.h" +#include "rawpdu.h" +#include "pdu_iterator.h" + +using std::distance; +using std::map; + +using namespace Tins; + +class PDUIteratorTest : public testing::Test { +public: + template + void test() { + IP ip = IP("1.2.3.4", "4.3.2.1") / TCP(22, 23) / RawPDU("asd"); + map pdu_types; + pdu_types[0] = PDU::IP; + pdu_types[1] = PDU::TCP; + pdu_types[2] = PDU::RAW; + + PDUIteratorRange range = iterate_pdus(ip); + EXPECT_EQ(3, distance(range.begin(), range.end())); + + size_t iteration = 0; + for (Iterator iter = range.begin(); iter != range.end(); iter++) { + EXPECT_EQ(pdu_types[iteration], iter->pdu_type()); + EXPECT_EQ(pdu_types[iteration], (*iter).pdu_type()); + ++iteration; + } + + Iterator iter = range.begin(); + ++iter; + iter++; + --iter; + iter--; + EXPECT_EQ(PDU::IP, iter->pdu_type()); + EXPECT_EQ(iter, range.begin()); + EXPECT_NE(iter, range.end()); + + const PDU& pdu = *iterate_pdus(ip).begin(); + EXPECT_EQ(PDU::IP, pdu.pdu_type()); + EXPECT_GT(const_cast(pdu).serialize().size(), 0); + } +}; + +TEST_F(PDUIteratorTest, Range) { + test(); +} + +TEST_F(PDUIteratorTest, RangeConst) { + test(); +}