172 lines
5.3 KiB
C++
172 lines
5.3 KiB
C++
/*
|
|
* Copyright (c) 2019-2021, OARC, Inc.
|
|
* Copyright (c) 2019, DENIC eG
|
|
* All rights reserved.
|
|
*
|
|
* This file is part of dnsmeter.
|
|
*
|
|
* dnsmeter 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.
|
|
*
|
|
* dnsmeter 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 dnsmeter. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "payload_file.h"
|
|
#include "exceptions.h"
|
|
#include "query.h"
|
|
|
|
#define __FAVOR_BSD 1
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <pcap/pcap.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/udp.h>
|
|
|
|
#pragma pack(push) /* push current alignment to stack */
|
|
#pragma pack(1) /* set alignment to 1 byte boundary */
|
|
struct ETHER {
|
|
unsigned char destination[6];
|
|
unsigned char source[6];
|
|
unsigned short type;
|
|
};
|
|
#pragma pack(pop) /* restore original alignment from stack */
|
|
|
|
PayloadFile::PayloadFile()
|
|
{
|
|
validLinesInQueryFile = 0;
|
|
payloadIsPcap = false;
|
|
}
|
|
|
|
bool PayloadFile::detectPcap(ppl7::File& ff)
|
|
{
|
|
unsigned char buffer[8];
|
|
if (ff.read(buffer, 8) != 8) {
|
|
ff.seek(0);
|
|
return false;
|
|
}
|
|
ff.seek(0);
|
|
unsigned int magic = ppl7::Peek32(buffer + 0);
|
|
if (magic == 0xa1b2c3d4 || magic == 0xa1b23c4d)
|
|
return true;
|
|
if (magic == 0xd4c3b2a1 || magic == 0x4d3cb2a1)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void PayloadFile::openQueryFile(const ppl7::String& Filename)
|
|
{
|
|
if (Filename.isEmpty())
|
|
throw InvalidQueryFile("File not given");
|
|
ppl7::File QueryFile;
|
|
QueryFile.open(Filename, ppl7::File::READ);
|
|
if (QueryFile.size() == 0) {
|
|
throw InvalidQueryFile("File is empty [%s]", (const char*)Filename);
|
|
}
|
|
printf("INFO: Loading and precompile payload. This could take some time...\n");
|
|
if (detectPcap(QueryFile)) {
|
|
loadAndCompilePcapFile(Filename);
|
|
} else {
|
|
loadAndCompile(QueryFile);
|
|
}
|
|
printf("INFO: %llu queries loaded\n", validLinesInQueryFile);
|
|
it = querycache.begin();
|
|
}
|
|
|
|
void PayloadFile::loadAndCompile(ppl7::File& ff)
|
|
{
|
|
ppl7::ByteArray buf(4096);
|
|
ppl7::String buffer;
|
|
validLinesInQueryFile = 0;
|
|
unsigned char* compiled_query = (unsigned char*)buf.ptr();
|
|
while (1) {
|
|
try {
|
|
if (ff.eof())
|
|
throw ppl7::EndOfFileException();
|
|
ff.gets(buffer, 1024);
|
|
buffer.trim();
|
|
if (buffer.isEmpty())
|
|
continue;
|
|
if (buffer.c_str()[0] == '#')
|
|
continue;
|
|
try {
|
|
// Precompile Query
|
|
int size = MakeQuery(buffer, compiled_query, 4096, false);
|
|
querycache.push_back(ppl7::ByteArray(compiled_query, size));
|
|
validLinesInQueryFile++;
|
|
} catch (...) {
|
|
// ignore invalid queries
|
|
continue;
|
|
}
|
|
} catch (const ppl7::EndOfFileException&) {
|
|
if (validLinesInQueryFile == 0) {
|
|
throw InvalidQueryFile("No valid Queries found in Queryfile");
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PayloadFile::loadAndCompilePcapFile(const ppl7::String& Filename)
|
|
{
|
|
char errorbuffer[PCAP_ERRBUF_SIZE];
|
|
struct pcap_pkthdr hdr;
|
|
payloadIsPcap = true;
|
|
validLinesInQueryFile = 0;
|
|
pcap_t* pp = pcap_open_offline((const char*)Filename, errorbuffer);
|
|
if (!pp)
|
|
throw InvalidQueryFile("%s", errorbuffer);
|
|
ppluint64 pkts_total = 0;
|
|
const u_char* pkt;
|
|
while ((pkt = pcap_next(pp, &hdr)) != NULL) {
|
|
pkts_total++;
|
|
//printf ("len=%d, caplen=%d\n",hdr.len,hdr.caplen);
|
|
const struct ETHER* eth = (const struct ETHER*)pkt;
|
|
if (hdr.caplen > 4096)
|
|
continue;
|
|
if (eth->type != htons(0x0800))
|
|
continue;
|
|
const struct ip* iphdr = (const struct ip*)(pkt + 14);
|
|
if (iphdr->ip_v != 4)
|
|
continue;
|
|
const struct udphdr* udp = (const struct udphdr*)(pkt + 14 + sizeof(struct ip));
|
|
if (udp->uh_dport != htons(53))
|
|
continue;
|
|
const struct DNS_HEADER* dns = (const struct DNS_HEADER*)(pkt + 14 + sizeof(struct ip) + sizeof(struct udphdr));
|
|
if (dns->qr != 0 || dns->opcode != 0)
|
|
continue;
|
|
querycache.push_back(ppl7::ByteArray(pkt, hdr.caplen));
|
|
validLinesInQueryFile++;
|
|
}
|
|
printf("Packets read from pcap file: %llu, valid UDP DNS queries: %llu\n",
|
|
pkts_total, validLinesInQueryFile);
|
|
pcap_close(pp);
|
|
if (validLinesInQueryFile == 0) {
|
|
throw InvalidQueryFile("No valid Queries found in pcap file [%s]", (const char*)Filename);
|
|
}
|
|
}
|
|
|
|
const ppl7::ByteArrayPtr& PayloadFile::getQuery()
|
|
{
|
|
QueryMutex.lock();
|
|
const ppl7::ByteArrayPtr& bap = *it;
|
|
++it;
|
|
if (it == querycache.end())
|
|
it = querycache.begin();
|
|
QueryMutex.unlock();
|
|
return bap;
|
|
}
|
|
|
|
bool PayloadFile::isPcap()
|
|
{
|
|
return payloadIsPcap;
|
|
}
|