/usr/include/tins/sniffer.h is in libtins-dev 1.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 | /*
* Copyright (c) 2012, 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_SNIFFER_H
#define TINS_SNIFFER_H
#include <pcap.h>
#include <string>
#include <memory>
#include <stdexcept>
#include "pdu.h"
#include "ethernetII.h"
#include "radiotap.h"
#include "packet.h"
#include "loopback.h"
#include "dot11/dot11_base.h"
#include "dot3.h"
#include "sll.h"
#include "cxxstd.h"
#include "exceptions.h"
namespace Tins {
/**
* \class BaseSniffer
* \brief Base class for sniffers.
*
* This class implements the basic sniffing operations. Subclasses
* should only initialize this object using a pcap_t pointer, which
* will be used to extract packets.
*
* Initialization must be done using the BaseSniffer::init method.
*/
class BaseSniffer {
public:
#if TINS_IS_CXX11
/**
* \brief Move constructor.
* This constructor is available only in C++11.
*/
BaseSniffer(BaseSniffer &&rhs) noexcept
{
*this = std::move(rhs);
}
/**
* \brief Move assignment operator.
* This opeartor is available only in C++11.
*/
BaseSniffer& operator=(BaseSniffer &&rhs) noexcept
{
handle = 0;
mask = rhs.mask;
iface_type = rhs.iface_type;
actual_filter.bf_insns = 0;
std::swap(handle, rhs.handle);
std::swap(actual_filter, rhs.actual_filter);
return *this;
}
#endif
/**
* \brief Sniffer destructor.
* This frees all memory used by the pcap handle.
*/
virtual ~BaseSniffer();
/**
* \brief Compiles a filter and uses it to capture one packet.
*
* This method returns the first sniffed packet that matches the
* sniffer's filter, or the first sniffed packet if no filter has
* been set.
*
* The return type is a thin wrapper over a PDU* and a Timestamp
* object. This wrapper can be both implicitly converted to a
* PDU* and a Packet object. So doing this:
*
* \code
* Sniffer s(...);
* std::unique_ptr<PDU> pdu(s.next_packet());
* // Packet takes care of the PDU*.
* Packet packet(s.next_packet());
* \endcode
*
* Is fine, but this:
*
* \code
* // bad!!
* PtrPacket p = s.next_packet();
*
* \endcode
*
* Is not, since PtrPacket can't be copy constructed.
*
* \sa Packet::release_pdu
*
* \return The captured packet, matching the given filter.
* If an error occured(probably compiling the filter), PtrPacket::pdu
* will return 0. Caller takes ownership of the PDU * stored in
* the PtrPacket.
*/
PtrPacket next_packet();
/**
* \brief Starts a sniffing loop, using a callback object for every
* sniffed packet.
*
* The callback object must implement an operator with some of
* the following(or compatible) signatures:
*
* \code
* bool operator()(PDU&);
* bool operator()(RefPacket&);
* \endcode
*
* This operator will be called using the sniffed packets
* as arguments. You can modify the parameter argument as you wish.
* Calling PDU methods like PDU::release_inner_pdu is perfectly
* valid.
*
* The callback taking a RefPacket will contain a timestamp
* indicating the moment in which the packet was taken out of
* the wire/pcap file.
*
* Note that the Functor object will be copied using its copy
* constructor, so that object should be some kind of proxy to
* another object which will process the packets(e.g. std::bind).
*
* \sa RefPacket
*
* \param cback_handler The callback handler object which should process packets.
* \param max_packets The maximum amount of packets to sniff. 0 == infinite.
*/
template<class Functor>
void sniff_loop(Functor function, uint32_t max_packets = 0);
/**
* \brief Sets a filter on this sniffer.
* \param filter The filter to be set.
* \return True iif it was possible to apply the filter.
*/
bool set_filter(const std::string &filter);
/**
* \brief Stops sniffing loops.
*/
void stop_sniff();
/**
* \brief Gets the file descriptor associated with the sniffer.
*/
int get_fd();
protected:
/**
* Default constructor.
*/
BaseSniffer();
/**
* \brief Initialices this BaseSniffer.
*
* \param phandle The pcap handle to be used for sniffing.
* \param filter The pcap filter which will be applied to the
* stream.
* \param if_mask The interface's subnet mask. If 0 is provided,
* then some IP broadcast tests won't work correctly.
*/
void init(pcap_t *phandle, const std::string &filter, bpf_u_int32 if_mask);
private:
template<class Functor>
struct LoopData {
pcap_t *handle;
Functor c_handler;
int iface_type;
LoopData(pcap_t *_handle, const Functor _handler,
int if_type)
: handle(_handle), c_handler(_handler), iface_type(if_type)
{ }
};
struct PCapLoopBreaker {
bool &went_well;
pcap_t *handle;
PCapLoopBreaker(bool &went_well, pcap_t *handle)
: went_well(went_well), handle(handle) { }
~PCapLoopBreaker() {
if(!went_well)
pcap_breakloop(handle);
}
};
BaseSniffer(const BaseSniffer&);
BaseSniffer &operator=(const BaseSniffer&);
static bool is_dot3(const uint8_t *ptr, size_t sz) {
return (sz >= 13 && ptr[12] < 8);
}
template<class ConcretePDU, class Functor>
static bool call_functor(LoopData<Functor> *data, const u_char *packet, const struct pcap_pkthdr *header);
bool compile_set_filter(const std::string &filter, bpf_program &prog);
template<class Functor>
static void callback_handler(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
pcap_t *handle;
bpf_u_int32 mask;
bpf_program actual_filter;
int iface_type;
};
/**
* \class Sniffer
* \brief Sniffs packets using pcap filters.
*
* This class uses a given filter to sniff packets and allow the user
* to handle them. Each time a filter is set, it's used until a new one
* is set. Both Sniffer::next_packet and Sniffer::sniff_loop have an
* optional filter parameter. If a filter is set using those parameter,
* the previously set filter is freed and the new one is used.
*/
class Sniffer : public BaseSniffer {
public:
/**
* \brief Constructs an instance of Sniffer.
* \param device The device which will be sniffed.
* \param max_packet_size The maximum packet size to be read.
* \param promisc bool indicating wether to put the interface in promiscuous mode.(optional)
* \param filter A capture filter to be used on the sniffing session.(optional);
*/
Sniffer(const std::string &device, unsigned max_packet_size,
bool promisc = false, const std::string &filter = "");
};
/**
* \class FileSniffer
* \brief Parses pcap files and interprets the packets in it.
*
* This class acts exactly in the same way that Sniffer, but reads
* packets from a pcap file instead of an interface.
*/
class FileSniffer : public BaseSniffer {
public:
/**
* \brief Constructs an instance of FileSniffer.
* \param file_name The pcap file which will be parsed.
* \param filter A capture filter to be used on the file.(optional);
*/
FileSniffer(const std::string &file_name, const std::string &filter = "");
};
template<class Functor>
void Tins::BaseSniffer::sniff_loop(Functor function, uint32_t max_packets) {
LoopData<Functor> data(handle, function, iface_type);
pcap_loop(handle, max_packets, &BaseSniffer::callback_handler<Functor>, (u_char*)&data);
}
template<class ConcretePDU, class Functor>
bool Tins::BaseSniffer::call_functor(LoopData<Functor> *data, const u_char *packet,
const struct pcap_pkthdr *header)
{
ConcretePDU some_pdu((const uint8_t*)packet, header->caplen);
Timestamp ts(header->ts);
RefPacket pck(some_pdu, ts);
return data->c_handler(pck);
}
template<class Functor>
void Tins::BaseSniffer::callback_handler(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) {
bool ret_val(true);
LoopData<Functor> *data = reinterpret_cast<LoopData<Functor>*>(args);
PCapLoopBreaker _(ret_val, data->handle);
try {
Internals::smart_ptr<PDU>::type pdu;
if(data->iface_type == DLT_EN10MB) {
ret_val = is_dot3((const uint8_t*)packet, header->caplen) ?
call_functor<Tins::Dot3>(data, packet, header) :
call_functor<Tins::EthernetII>(data, packet, header);
}
else if(data->iface_type == DLT_IEEE802_11_RADIO)
ret_val = call_functor<Tins::RadioTap>(data, packet, header);
else if(data->iface_type == DLT_IEEE802_11) {
Internals::smart_ptr<PDU>::type pdu(
Tins::Dot11::from_bytes((const uint8_t*)packet, header->caplen)
);
if(pdu.get()) {
RefPacket pck(*pdu, header->ts);
ret_val = data->c_handler(pck);
}
}
else if(data->iface_type == DLT_NULL)
ret_val = call_functor<Tins::Loopback>(data, packet, header);
else if(data->iface_type == DLT_LINUX_SLL)
ret_val = call_functor<Tins::SLL>(data, packet, header);
}
catch(malformed_packet&) {
ret_val = true;
}
catch(pdu_not_found&) {
ret_val = true;
}
}
template<class T>
class HandlerProxy {
public:
typedef T* ptr_type;
typedef bool (T::*fun_type)(PDU&) ;
HandlerProxy(ptr_type ptr, fun_type function)
: object(ptr), fun(function) {}
bool operator()(PDU &pdu) {
return (object->*fun)(pdu);
}
private:
ptr_type object;
fun_type fun;
};
template<class T>
HandlerProxy<T> make_sniffer_handler(T *ptr, typename HandlerProxy<T>::fun_type function)
{
return HandlerProxy<T>(ptr, function);
}
}
#endif // TINS_SNIFFER_H
|