suricata
log-dnslog.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2013 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Victor Julien <victor@inliniac.net>
22  *
23  * Implements dns logging portion of the engine.
24  */
25 
26 #include "suricata-common.h"
27 #include "debug.h"
28 #include "detect.h"
29 #include "pkt-var.h"
30 #include "conf.h"
31 
32 #include "threads.h"
33 #include "threadvars.h"
34 #include "tm-threads.h"
35 
36 #include "util-print.h"
37 #include "util-unittest.h"
38 
39 #include "util-debug.h"
40 
41 #include "output.h"
42 #include "log-dnslog.h"
43 #include "app-layer-dns-common.h"
44 #include "app-layer-dns-udp.h"
45 #include "app-layer.h"
46 #include "util-privs.h"
47 #include "util-buffer.h"
48 
49 #include "util-logopenfile.h"
50 #include "util-time.h"
51 
52 #ifndef HAVE_RUST
53 
54 #define DEFAULT_LOG_FILENAME "dns.log"
55 
56 #define MODULE_NAME "LogDnsLog"
57 
58 #define OUTPUT_BUFFER_SIZE 65535
59 
60 /* we can do query logging as well, but it's disabled for now as the
61  * TX id handling doesn't expect it */
62 #define QUERY 0
63 
64 typedef struct LogDnsFileCtx_ {
66  uint32_t flags; /** Store mode */
68 
69 typedef struct LogDnsLogThread_ {
71  /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
72  uint32_t dns_cnt;
73 
76 
77 static void LogQuery(LogDnsLogThread *aft, char *timebuf, char *srcip, char *dstip, Port sp, Port dp, DNSTransaction *tx, DNSQueryEntry *entry)
78 {
79  LogDnsFileCtx *hlog = aft->dnslog_ctx;
80 
81  SCLogDebug("got a DNS request and now logging !!");
82 
83  /* reset */
84  MemBufferReset(aft->buffer);
85 
86  /* time & tx */
88  "%s [**] Query TX %04x [**] ", timebuf, tx->tx_id);
89 
90  /* query */
91  PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
92  (uint8_t *)((uint8_t *)entry + sizeof(DNSQueryEntry)),
93  entry->len);
94 
95  char record[16] = "";
96  DNSCreateTypeString(entry->type, record, sizeof(record));
98  " [**] %s [**] %s:%" PRIu16 " -> %s:%" PRIu16 "\n",
99  record, srcip, sp, dstip, dp);
100 
101  hlog->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer),
102  MEMBUFFER_OFFSET(aft->buffer), hlog->file_ctx);
103 }
104 
105 static void LogAnswer(LogDnsLogThread *aft, char *timebuf, char *srcip, char *dstip, Port sp, Port dp, DNSTransaction *tx, DNSAnswerEntry *entry)
106 {
107  LogDnsFileCtx *hlog = aft->dnslog_ctx;
108 
109  SCLogDebug("got a DNS response and now logging !!");
110 
111  /* reset */
112  MemBufferReset(aft->buffer);
113  /* time & tx*/
115  "%s [**] Response TX %04x [**] ", timebuf, tx->tx_id);
116 
117  if (entry == NULL) {
118  if (tx->rcode) {
119  char rcode[16] = "";
120  DNSCreateRcodeString(tx->rcode, rcode, sizeof(rcode));
121  MemBufferWriteString(aft->buffer, "%s", rcode);
122  } else if (tx->recursion_desired) {
123  MemBufferWriteString(aft->buffer, "Recursion Desired");
124  }
125  } else {
126  /* query */
127  if (entry->fqdn_len > 0) {
128  PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size,
129  (uint8_t *)((uint8_t *)entry + sizeof(DNSAnswerEntry)),
130  entry->fqdn_len);
131  } else {
132  MemBufferWriteString(aft->buffer, "<no data>");
133  }
134 
135  char record[16] = "";
136  DNSCreateTypeString(entry->type, record, sizeof(record));
138  " [**] %s [**] TTL %u [**] ", record, entry->ttl);
139 
140  uint8_t *ptr = (uint8_t *)((uint8_t *)entry + sizeof(DNSAnswerEntry) + entry->fqdn_len);
141  if (entry->type == DNS_RECORD_TYPE_A && entry->data_len == 4) {
142  char a[16] = "";
143  PrintInet(AF_INET, (const void *)ptr, a, sizeof(a));
144  MemBufferWriteString(aft->buffer, "%s", a);
145  } else if (entry->type == DNS_RECORD_TYPE_AAAA && entry->data_len == 16) {
146  char a[46];
147  PrintInet(AF_INET6, (const void *)ptr, a, sizeof(a));
148  MemBufferWriteString(aft->buffer, "%s", a);
149  } else if (entry->data_len == 0) {
150  MemBufferWriteString(aft->buffer, "<no data>");
151  } else {
152  PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset,
153  aft->buffer->size, ptr, entry->data_len);
154  }
155  }
156 
157  /* ip/tcp header info */
159  " [**] %s:%" PRIu16 " -> %s:%" PRIu16 "\n",
160  srcip, sp, dstip, dp);
161 
162  hlog->file_ctx->Write((const char *)MEMBUFFER_BUFFER(aft->buffer),
163  MEMBUFFER_OFFSET(aft->buffer), hlog->file_ctx);
164 }
165 
166 static int LogDnsLogger(ThreadVars *tv, void *data, const Packet *p,
167  Flow *f, void *state, void *tx, uint64_t tx_id, uint8_t direction)
168 {
169  LogDnsLogThread *aft = (LogDnsLogThread *)data;
170  DNSTransaction *dns_tx = (DNSTransaction *)tx;
171  SCLogDebug("pcap_cnt %"PRIu64, p->pcap_cnt);
172  char timebuf[64];
173  CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
174 
175  int ipproto = 0;
176  if (PKT_IS_IPV4(p))
177  ipproto = AF_INET;
178  else if (PKT_IS_IPV6(p))
179  ipproto = AF_INET6;
180 
181  char srcip[46], dstip[46];
182  Port sp, dp;
183  if ((PKT_IS_TOCLIENT(p))) {
184  switch (ipproto) {
185  case AF_INET:
186  PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip));
187  PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip));
188  break;
189  case AF_INET6:
190  PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip));
191  PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip));
192  break;
193  default:
194  goto end;
195  }
196  sp = p->sp;
197  dp = p->dp;
198  } else {
199  switch (ipproto) {
200  case AF_INET:
201  PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), srcip, sizeof(srcip));
202  PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), dstip, sizeof(dstip));
203  break;
204  case AF_INET6:
205  PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), srcip, sizeof(srcip));
206  PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), dstip, sizeof(dstip));
207  break;
208  default:
209  goto end;
210  }
211  sp = p->dp;
212  dp = p->sp;
213  }
214 
215  if (direction == STREAM_TOSERVER) {
216  DNSQueryEntry *query = NULL;
217  TAILQ_FOREACH(query, &dns_tx->query_list, next) {
218  LogQuery(aft, timebuf, dstip, srcip, dp, sp, dns_tx, query);
219  }
220  } else if (direction == STREAM_TOCLIENT) {
221  if (dns_tx->rcode)
222  LogAnswer(aft, timebuf, srcip, dstip, sp, dp, dns_tx, NULL);
223  if (dns_tx->recursion_desired)
224  LogAnswer(aft, timebuf, srcip, dstip, sp, dp, dns_tx, NULL);
225 
226  DNSAnswerEntry *entry = NULL;
227  TAILQ_FOREACH(entry, &dns_tx->answer_list, next) {
228  LogAnswer(aft, timebuf, srcip, dstip, sp, dp, dns_tx, entry);
229  }
230 
231  entry = NULL;
232  TAILQ_FOREACH(entry, &dns_tx->authority_list, next) {
233  LogAnswer(aft, timebuf, srcip, dstip, sp, dp, dns_tx, entry);
234  }
235  }
236 
237  aft->dns_cnt++;
238 end:
239  return 0;
240 }
241 
242 static int LogDnsRequestLogger(ThreadVars *tv, void *data, const Packet *p,
243  Flow *f, void *state, void *tx, uint64_t tx_id)
244 {
245  return LogDnsLogger(tv, data, p, f, state, tx, tx_id, STREAM_TOSERVER);
246 }
247 
248 static int LogDnsResponseLogger(ThreadVars *tv, void *data, const Packet *p,
249  Flow *f, void *state, void *tx, uint64_t tx_id)
250 {
251  return LogDnsLogger(tv, data, p, f, state, tx, tx_id, STREAM_TOCLIENT);
252 }
253 
254 static TmEcode LogDnsLogThreadInit(ThreadVars *t, const void *initdata, void **data)
255 {
256  LogDnsLogThread *aft = SCMalloc(sizeof(LogDnsLogThread));
257  if (unlikely(aft == NULL))
258  return TM_ECODE_FAILED;
259  memset(aft, 0, sizeof(LogDnsLogThread));
260 
261  if(initdata == NULL)
262  {
263  SCLogDebug("Error getting context for LogDNSLog. \"initdata\" argument NULL");
264  SCFree(aft);
265  return TM_ECODE_FAILED;
266  }
267 
269  if (aft->buffer == NULL) {
270  SCFree(aft);
271  return TM_ECODE_FAILED;
272  }
273 
274  /* Use the Ouptut Context (file pointer and mutex) */
275  aft->dnslog_ctx= ((OutputCtx *)initdata)->data;
276 
277  *data = (void *)aft;
278  return TM_ECODE_OK;
279 }
280 
281 static TmEcode LogDnsLogThreadDeinit(ThreadVars *t, void *data)
282 {
283  LogDnsLogThread *aft = (LogDnsLogThread *)data;
284  if (aft == NULL) {
285  return TM_ECODE_OK;
286  }
287 
288  MemBufferFree(aft->buffer);
289  /* clear memory */
290  memset(aft, 0, sizeof(LogDnsLogThread));
291 
292  SCFree(aft);
293  return TM_ECODE_OK;
294 }
295 
296 static void LogDnsLogExitPrintStats(ThreadVars *tv, void *data)
297 {
298  LogDnsLogThread *aft = (LogDnsLogThread *)data;
299  if (aft == NULL) {
300  return;
301  }
302 
303  SCLogInfo("DNS logger logged %" PRIu32 " transactions", aft->dns_cnt);
304 }
305 
306 static void LogDnsLogDeInitCtx(OutputCtx *output_ctx)
307 {
308  LogDnsFileCtx *dnslog_ctx = (LogDnsFileCtx *)output_ctx->data;
309  LogFileFreeCtx(dnslog_ctx->file_ctx);
310  SCFree(dnslog_ctx);
311  SCFree(output_ctx);
312 }
313 
314 /** \brief Create a new dns log LogFileCtx.
315  * \param conf Pointer to ConfNode containing this loggers configuration.
316  * \return NULL if failure, LogFileCtx* to the file_ctx if succesful
317  * */
318 static OutputInitResult LogDnsLogInitCtx(ConfNode *conf)
319 {
320  OutputInitResult result = { NULL, false };
322 
323  if(file_ctx == NULL) {
324  SCLogError(SC_ERR_DNS_LOG_GENERIC, "couldn't create new file_ctx");
325  return result;
326  }
327 
328  if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
329  LogFileFreeCtx(file_ctx);
330  return result;
331  }
332 
333  LogDnsFileCtx *dnslog_ctx = SCMalloc(sizeof(LogDnsFileCtx));
334  if (unlikely(dnslog_ctx == NULL)) {
335  LogFileFreeCtx(file_ctx);
336  return result;
337  }
338  memset(dnslog_ctx, 0x00, sizeof(LogDnsFileCtx));
339 
340  dnslog_ctx->file_ctx = file_ctx;
341 
342  OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
343  if (unlikely(output_ctx == NULL)) {
344  LogFileFreeCtx(file_ctx);
345  SCFree(dnslog_ctx);
346  return result;
347  }
348 
349  output_ctx->data = dnslog_ctx;
350  output_ctx->DeInit = LogDnsLogDeInitCtx;
351 
352  SCLogDebug("DNS log output initialized");
353 
356 
357  result.ctx = output_ctx;
358  result.ok = true;
359  return result;
360 }
361 
362 #endif /* !HAVE_RUST */
363 
364 void LogDnsLogRegister (void)
365 {
366 #ifndef HAVE_RUST
367  /* Request logger. */
369  LogDnsLogInitCtx, ALPROTO_DNS, LogDnsRequestLogger, 0, 1,
370  LogDnsLogThreadInit, LogDnsLogThreadDeinit, LogDnsLogExitPrintStats);
371 
372  /* Response logger. */
374  LogDnsLogInitCtx, ALPROTO_DNS, LogDnsResponseLogger, 1, 1,
375  LogDnsLogThreadInit, LogDnsLogThreadDeinit, LogDnsLogExitPrintStats);
376 
377  /* enable the logger for the app layer */
378  SCLogDebug("registered %s", MODULE_NAME);
379 #endif /* !HAVE_RUST */
380 }
struct DNSAnswerEntry_ DNSAnswerEntry
DNS Answer storage. Stored in TX list.
MemBuffer * MemBufferCreateNew(uint32_t size)
Definition: util-buffer.c:32
#define GET_IPV4_SRC_ADDR_PTR(p)
Definition: decode.h:213
#define SCLogDebug(...)
Definition: util-debug.h:335
void LogDnsLogRegister(void)
Definition: log-dnslog.c:364
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:350
#define MemBufferWriteString(dst,...)
Write a string buffer to the Membuffer dst.
Definition: util-buffer.h:162
struct HtpBodyChunk_ * next
#define PKT_IS_TOCLIENT(p)
Definition: decode.h:257
#define unlikely(expr)
Definition: util-optimize.h:35
#define GET_IPV4_DST_ADDR_PTR(p)
Definition: decode.h:214
Port sp
Definition: decode.h:414
#define MemBufferReset(mem_buffer)
Reset the mem buffer.
Definition: util-buffer.h:42
Port dp
Definition: decode.h:422
DNS Transaction, request/reply with same TX id.
#define PKT_IS_IPV6(p)
Definition: decode.h:251
void(* DeInit)(struct OutputCtx_ *)
Definition: tm-modules.h:84
void DNSCreateRcodeString(uint8_t rcode, char *str, size_t str_size)
uint64_t pcap_cnt
Definition: decode.h:566
DNS Answer storage. Stored in TX list.
#define PKT_IS_IPV4(p)
Definition: decode.h:250
DNS Query storage. Stored in TX list.
#define SCCalloc(nm, a)
Definition: util-mem.h:205
#define GET_IPV6_DST_ADDR(p)
Definition: decode.h:219
uint32_t size
Definition: util-buffer.h:29
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
#define DNS_RECORD_TYPE_A
void AppLayerParserRegisterLogger(uint8_t ipproto, AppProto alproto)
void OutputRegisterTxModuleWithProgress(LoggerId id, const char *name, const char *conf_name, OutputInitFunc InitFunc, AppProto alproto, TxLogger TxLogFunc, int tc_log_progress, int ts_log_progress, ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit, ThreadExitPrintStatsFunc ThreadExitPrintStats)
Register a tx output module with progress.
Definition: output.c:357
LogFileCtx * LogFileNewCtx(void)
LogFileNewCtx() Get a new LogFileCtx.
uint8_t * buffer
Definition: util-buffer.h:28
#define STREAM_TOCLIENT
Definition: stream.h:32
LogFileCtx * file_ctx
Definition: log-dnslog.c:65
int SCConfLogOpenGeneric(ConfNode *conf, LogFileCtx *log_ctx, const char *default_filename, int rotate)
open a generic output "log file", which may be a regular file or a socket
#define MODULE_NAME
Definition: log-dnslog.c:56
const char * PrintInet(int af, const void *src, char *dst, socklen_t size)
Definition: util-print.c:267
uint16_t Port
Definition: decode.h:234
#define DNS_RECORD_TYPE_AAAA
#define MEMBUFFER_BUFFER(mem_buffer)
Get the MemBuffers underlying buffer.
Definition: util-buffer.h:50
struct LogDnsFileCtx_ LogDnsFileCtx
void PrintRawUriBuf(char *retbuf, uint32_t *offset, uint32_t retbuflen, uint8_t *buf, uint32_t buflen)
Definition: util-print.c:118
MemBuffer * buffer
Definition: log-dnslog.c:74
Definition: conf.h:32
int(* Write)(const char *buffer, int buffer_len, struct LogFileCtx_ *fp)
OutputCtx * ctx
Definition: output.h:42
#define SCMalloc(a)
Definition: util-mem.h:174
#define GET_IPV6_SRC_ADDR(p)
Definition: decode.h:218
int LogFileFreeCtx(LogFileCtx *lf_ctx)
LogFileFreeCtx() Destroy a LogFileCtx (Close the file and free memory)
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:254
#define SCFree(a)
Definition: util-mem.h:236
#define OUTPUT_BUFFER_SIZE
Definition: log-dnslog.c:58
uint16_t tx_id
#define MEMBUFFER_OFFSET(mem_buffer)
Get the MemBuffers current offset.
Definition: util-buffer.h:55
void * data
Definition: tm-modules.h:81
uint32_t dns_cnt
Definition: log-dnslog.c:72
#define STREAM_TOSERVER
Definition: stream.h:31
#define DEFAULT_LOG_FILENAME
Definition: log-dnslog.c:54
uint32_t flags
Definition: log-dnslog.c:66
Per thread variable structure.
Definition: threadvars.h:57
struct timeval ts
Definition: decode.h:450
uint32_t offset
Definition: util-buffer.h:30
Flow data structure.
Definition: flow.h:327
void MemBufferFree(MemBuffer *buffer)
Definition: util-buffer.c:82
void DNSCreateTypeString(uint16_t type, char *str, size_t str_size)
LogDnsFileCtx * dnslog_ctx
Definition: log-dnslog.c:70
struct LogDnsLogThread_ LogDnsLogThread
void CreateTimeString(const struct timeval *ts, char *str, size_t size)
Definition: util-time.c:237