summaryrefslogtreecommitdiffstats
path: root/src/tcp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tcp.cpp')
-rw-r--r--src/tcp.cpp268
1 files changed, 268 insertions, 0 deletions
diff --git a/src/tcp.cpp b/src/tcp.cpp
new file mode 100644
index 0000000..5787e84
--- /dev/null
+++ b/src/tcp.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2017-2024 OARC, Inc.
+ * Copyright (c) 2011-2017, IIS - The Internet Foundation in Sweden
+ * All rights reserved.
+ *
+ * This file is part of PacketQ.
+ *
+ * PacketQ 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.
+ *
+ * PacketQ 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 PacketQ. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tcp.h"
+#include "packet_handler.h"
+
+#include <cstring>
+#include <list>
+#include <map>
+#include <stdlib.h>
+
+namespace packetq {
+
+/// TCP Stream id class - serves as the key in the streams map
+class Stream_id {
+public:
+ /// constructor
+ Stream_id(in6addr_t& src_ip,
+ in6addr_t& dst_ip,
+ unsigned short src_port,
+ unsigned short dst_port)
+ {
+ m_src_ip = src_ip;
+ m_dst_ip = dst_ip;
+ m_src_port = src_port;
+ m_dst_port = dst_port;
+ }
+
+ /// < comparison operator for the std::map
+ bool operator<(const Stream_id& rhs) const
+ {
+ return memcmp(this, &rhs, sizeof(*this)) < 0;
+ }
+
+private:
+ in6addr_t m_src_ip, m_dst_ip;
+ unsigned short m_src_port, m_dst_port;
+};
+
+/// TCP data segment container
+/** Data_segment contains the data found in a single tcp packet
+ * Data_segment are inerted into a list in the Stream class
+ */
+class Data_segment {
+private:
+ Data_segment& operator=(const Data_segment& other);
+ Data_segment(Data_segment&& other) noexcept;
+ Data_segment const& operator=(Data_segment&& other);
+
+public:
+ /// Constructor taking a memory block with packet content
+ Data_segment(unsigned char* data, unsigned int len)
+ {
+ m_datasize = len;
+ m_data = new unsigned char[m_datasize];
+ for (unsigned int i = 0; i < m_datasize; i++) {
+ m_data[i] = data[i];
+ }
+ }
+ Data_segment(const Data_segment& other)
+ {
+ m_datasize = other.m_datasize;
+ m_data = new unsigned char[m_datasize];
+ for (unsigned int i = 0; i < m_datasize; i++) {
+ m_data[i] = other.m_data[i];
+ }
+ }
+ /// Destructor
+ ~Data_segment()
+ {
+ delete[] m_data;
+ }
+
+ /// size of the data
+ unsigned int m_datasize;
+ /// pointer to the data
+ unsigned char* m_data;
+};
+
+int g_count = 0;
+
+/// TCP Stream class
+/** The Stream class has an Stream_id and a list of Data_segemnts that make up
+ * a tcp data stream.
+ * The Streams are organized into a global map ( g_tcp_streams ) indexed by a Stream_id
+ */
+class Stream {
+public:
+ /// Constructor
+ Stream()
+ {
+ m_ser = g_count++;
+ m_content = false;
+ m_nseq = false;
+ m_seq = 0;
+ }
+ ~Stream()
+ {
+ m_segments.clear();
+ }
+ /// add a datasegment to the stream
+ /** If the segment has the expected sequence number
+ * the segment will be added to the list
+ */
+ void add(bool syn, unsigned int seq, Data_segment& s)
+ {
+ m_content = true;
+ if (!m_segments.size() || syn)
+ m_seq = seq;
+
+ if (m_seq == seq) {
+ m_content = true;
+ if ((s.m_datasize > 0 && s.m_datasize <= 65535)) {
+ m_segments.push_back(s);
+ m_seq = seq + s.m_datasize;
+ }
+ }
+ }
+ /// checka if there's any content in the stream
+ bool has_content()
+ {
+ return m_content;
+ }
+ /// Erase (and free) all segments and reset state
+ void erase()
+ {
+ m_content = false;
+ m_nseq = false;
+ m_segments.clear();
+ }
+ /// return the streams data size
+ int get_size()
+ {
+ int size = 0;
+ for (auto it = m_segments.begin();
+ it != m_segments.end(); it++) {
+ size += it->m_datasize;
+ }
+ return size;
+ }
+ /// debug functionality to dump a streams content
+ void dump()
+ {
+ int start = 2;
+ for (auto it = m_segments.begin();
+ it != m_segments.end(); it++) {
+ for (unsigned int i = start; i < it->m_datasize; i++) {
+ printf("%02x", it->m_data[i]);
+ }
+ start = 0;
+ }
+ printf("\n");
+ }
+ /// returns the data in the stream
+ /** The returned data is located in a static buffer shared by all streams
+ * the data is valid until the next call to get_buffer()
+ */
+ unsigned char* get_buffer()
+ {
+ int p = 0;
+ for (auto it = m_segments.begin();
+ it != m_segments.end(); it++) {
+ for (unsigned int i = 0; i < it->m_datasize; i++) {
+ m_buffer[p++] = it->m_data[i];
+ if (p >= 0xffff)
+ return m_buffer;
+ }
+ }
+ return m_buffer;
+ }
+
+private:
+ unsigned int m_seq;
+ int m_ser;
+ bool m_content;
+ bool m_nseq;
+ std::list<Data_segment> m_segments;
+
+ static unsigned char m_buffer[0x10000];
+};
+unsigned char Stream::m_buffer[0x10000];
+
+std::map<Stream_id, Stream> g_tcp_streams;
+
+/// assemble_tcp builds datastreams out of tcp packets
+/** TCP packets are inserted into streams. When the streams are closed
+ * the contained data is returned as a pointer the data
+ * it is up to the caller to free() the memory returned.
+ */
+unsigned char*
+assemble_tcp(
+ Payload& payload,
+ in6addr_t* src_ip,
+ in6addr_t* dst_ip,
+ unsigned short src_port,
+ unsigned short dst_port,
+ unsigned int* rest,
+ unsigned int seq,
+ unsigned char* data,
+ int len,
+ char syn,
+ char fin,
+ char rst,
+ char ack)
+{
+ Stream_id id(*src_ip, *dst_ip, src_port, dst_port);
+ Stream& str = g_tcp_streams[id];
+ bool data_avail = false;
+
+ if (!str.has_content()) {
+ Data_segment seg(data, len);
+ str.add(syn, seq, seg);
+ } else {
+ if (rst == 1) {
+ str.erase();
+ } else if (syn == 1) {
+ str.erase();
+ Data_segment seg(data, len);
+ str.add(syn, seq, seg);
+ } else {
+ Data_segment seg(data, len);
+ str.add(syn, seq, seg);
+ }
+ }
+
+ data = 0;
+ if (str.has_content()) {
+ int size = str.get_size();
+ if (size < 2) {
+ // need at least dnslen
+ return 0;
+ }
+ unsigned char* buffer = str.get_buffer();
+ int dns_size = (int(buffer[0]) << 8) | buffer[1];
+
+ data_avail = (fin == 1) && (rst == 0);
+ if (data_avail || dns_size + 2 == size) {
+ *rest = size;
+ if (*rest > 0xffff)
+ *rest = 0xffff;
+ data = (unsigned char*)payload.alloc(*rest);
+ memcpy(data, buffer, *rest);
+ str.erase();
+ g_tcp_streams.erase(id);
+ }
+ }
+ return data;
+}
+
+} // namespace packetq