1 /* dnstap support for NSD */ 2 3 /* 4 * Copyright (c) 2013-2014, Farsight Security, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * 3. Neither the name of the copyright holder nor the names of its 19 * contributors may be used to endorse or promote products derived from 20 * this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 26 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 27 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 #include "dnstap/dnstap_config.h" 36 37 #ifdef USE_DNSTAP 38 39 #include "config.h" 40 #include <string.h> 41 #include <sys/time.h> 42 #ifdef HAVE_SYS_STAT_H 43 #include <sys/stat.h> 44 #endif 45 #include <errno.h> 46 #include <unistd.h> 47 #include "util.h" 48 #include "options.h" 49 50 #include <fstrm.h> 51 #include <protobuf-c/protobuf-c.h> 52 53 #include "dnstap/dnstap.h" 54 #include "dnstap/dnstap.pb-c.h" 55 56 #define DNSTAP_CONTENT_TYPE "protobuf:dnstap.Dnstap" 57 #define DNSTAP_INITIAL_BUF_SIZE 256 58 59 struct dt_msg { 60 void *buf; 61 size_t len_buf; 62 Dnstap__Dnstap d; 63 Dnstap__Message m; 64 }; 65 66 static int 67 dt_pack(const Dnstap__Dnstap *d, void **buf, size_t *sz) 68 { 69 ProtobufCBufferSimple sbuf; 70 71 memset(&sbuf, 0, sizeof(sbuf)); 72 sbuf.base.append = protobuf_c_buffer_simple_append; 73 sbuf.len = 0; 74 sbuf.alloced = DNSTAP_INITIAL_BUF_SIZE; 75 sbuf.data = malloc(sbuf.alloced); 76 if (sbuf.data == NULL) 77 return 0; 78 sbuf.must_free_data = 1; 79 80 *sz = dnstap__dnstap__pack_to_buffer(d, (ProtobufCBuffer *) &sbuf); 81 if (sbuf.data == NULL) 82 return 0; 83 *buf = sbuf.data; 84 85 return 1; 86 } 87 88 static void 89 dt_send(const struct dt_env *env, void *buf, size_t len_buf) 90 { 91 fstrm_res res; 92 if (!buf) 93 return; 94 res = fstrm_iothr_submit(env->iothr, env->ioq, buf, len_buf, 95 fstrm_free_wrapper, NULL); 96 if (res != fstrm_res_success) 97 free(buf); 98 } 99 100 static void 101 dt_msg_init(const struct dt_env *env, 102 struct dt_msg *dm, 103 Dnstap__Message__Type mtype) 104 { 105 memset(dm, 0, sizeof(*dm)); 106 dm->d.base.descriptor = &dnstap__dnstap__descriptor; 107 dm->m.base.descriptor = &dnstap__message__descriptor; 108 dm->d.type = DNSTAP__DNSTAP__TYPE__MESSAGE; 109 dm->d.message = &dm->m; 110 dm->m.type = mtype; 111 if (env->identity != NULL) { 112 dm->d.identity.data = (uint8_t *) env->identity; 113 dm->d.identity.len = (size_t) env->len_identity; 114 dm->d.has_identity = 1; 115 } 116 if (env->version != NULL) { 117 dm->d.version.data = (uint8_t *) env->version; 118 dm->d.version.len = (size_t) env->len_version; 119 dm->d.has_version = 1; 120 } 121 } 122 123 /* check that the socket file can be opened and exists, print error if not */ 124 static void 125 check_socket_file(const char* socket_path) 126 { 127 struct stat statbuf; 128 memset(&statbuf, 0, sizeof(statbuf)); 129 if(stat(socket_path, &statbuf) < 0) { 130 log_msg(LOG_WARNING, "could not open dnstap-socket-path: %s, %s", 131 socket_path, strerror(errno)); 132 } 133 } 134 135 struct dt_env * 136 dt_create(const char *socket_path, unsigned num_workers) 137 { 138 #ifndef NDEBUG 139 fstrm_res res; 140 #endif 141 struct dt_env *env; 142 struct fstrm_iothr_options *fopt; 143 struct fstrm_unix_writer_options *fuwopt; 144 struct fstrm_writer *fw; 145 struct fstrm_writer_options *fwopt; 146 147 VERBOSITY(1, (LOG_INFO, "attempting to connect to dnstap socket %s", 148 socket_path)); 149 assert(socket_path != NULL); 150 assert(num_workers > 0); 151 check_socket_file(socket_path); 152 153 env = (struct dt_env *) calloc(1, sizeof(struct dt_env)); 154 if (!env) 155 return NULL; 156 157 fwopt = fstrm_writer_options_init(); 158 #ifndef NDEBUG 159 res = 160 #else 161 (void) 162 #endif 163 fstrm_writer_options_add_content_type(fwopt, 164 DNSTAP_CONTENT_TYPE, sizeof(DNSTAP_CONTENT_TYPE) - 1); 165 assert(res == fstrm_res_success); 166 167 fuwopt = fstrm_unix_writer_options_init(); 168 fstrm_unix_writer_options_set_socket_path(fuwopt, socket_path); 169 170 fw = fstrm_unix_writer_init(fuwopt, fwopt); 171 assert(fw != NULL); 172 173 fopt = fstrm_iothr_options_init(); 174 fstrm_iothr_options_set_num_input_queues(fopt, num_workers); 175 env->iothr = fstrm_iothr_init(fopt, &fw); 176 if (env->iothr == NULL) { 177 log_msg(LOG_ERR, "dt_create: fstrm_iothr_init() failed"); 178 fstrm_writer_destroy(&fw); 179 free(env); 180 env = NULL; 181 } 182 fstrm_iothr_options_destroy(&fopt); 183 fstrm_unix_writer_options_destroy(&fuwopt); 184 fstrm_writer_options_destroy(&fwopt); 185 186 return env; 187 } 188 189 static void 190 dt_apply_identity(struct dt_env *env, struct nsd_options *cfg) 191 { 192 char buf[MAXHOSTNAMELEN+1]; 193 if (!cfg->dnstap_send_identity) 194 return; 195 free(env->identity); 196 if (cfg->dnstap_identity == NULL || cfg->dnstap_identity[0] == 0) { 197 if (gethostname(buf, MAXHOSTNAMELEN) == 0) { 198 buf[MAXHOSTNAMELEN] = 0; 199 env->identity = strdup(buf); 200 } else { 201 error("dt_apply_identity: gethostname() failed"); 202 } 203 } else { 204 env->identity = strdup(cfg->dnstap_identity); 205 } 206 if (env->identity == NULL) 207 error("dt_apply_identity: strdup() failed"); 208 env->len_identity = (unsigned int)strlen(env->identity); 209 VERBOSITY(1, (LOG_INFO, "dnstap identity field set to \"%s\"", 210 env->identity)); 211 } 212 213 static void 214 dt_apply_version(struct dt_env *env, struct nsd_options *cfg) 215 { 216 if (!cfg->dnstap_send_version) 217 return; 218 free(env->version); 219 if (cfg->dnstap_version == NULL || cfg->dnstap_version[0] == 0) 220 env->version = strdup(PACKAGE_STRING); 221 else 222 env->version = strdup(cfg->dnstap_version); 223 if (env->version == NULL) 224 error("dt_apply_version: strdup() failed"); 225 env->len_version = (unsigned int)strlen(env->version); 226 VERBOSITY(1, (LOG_INFO, "dnstap version field set to \"%s\"", 227 env->version)); 228 } 229 230 void 231 dt_apply_cfg(struct dt_env *env, struct nsd_options *cfg) 232 { 233 if (!cfg->dnstap_enable) 234 return; 235 236 dt_apply_identity(env, cfg); 237 dt_apply_version(env, cfg); 238 if ((env->log_auth_query_messages = (unsigned int) 239 cfg->dnstap_log_auth_query_messages)) 240 { 241 VERBOSITY(1, (LOG_INFO, "dnstap Message/AUTH_QUERY enabled")); 242 } 243 if ((env->log_auth_response_messages = (unsigned int) 244 cfg->dnstap_log_auth_response_messages)) 245 { 246 VERBOSITY(1, (LOG_INFO, "dnstap Message/AUTH_RESPONSE enabled")); 247 } 248 } 249 250 int 251 dt_init(struct dt_env *env) 252 { 253 env->ioq = fstrm_iothr_get_input_queue(env->iothr); 254 if (env->ioq == NULL) 255 return 0; 256 return 1; 257 } 258 259 void 260 dt_delete(struct dt_env *env) 261 { 262 if (!env) 263 return; 264 VERBOSITY(1, (LOG_INFO, "closing dnstap socket")); 265 fstrm_iothr_destroy(&env->iothr); 266 free(env->identity); 267 free(env->version); 268 free(env); 269 } 270 271 static void 272 dt_fill_timeval(const struct timeval *tv, 273 uint64_t *time_sec, protobuf_c_boolean *has_time_sec, 274 uint32_t *time_nsec, protobuf_c_boolean *has_time_nsec) 275 { 276 #ifndef S_SPLINT_S 277 *time_sec = tv->tv_sec; 278 *time_nsec = tv->tv_usec * 1000; 279 #endif 280 *has_time_sec = 1; 281 *has_time_nsec = 1; 282 } 283 284 static void 285 dt_fill_buffer(uint8_t* pkt, size_t pktlen, ProtobufCBinaryData *p, protobuf_c_boolean *has) 286 { 287 p->len = pktlen; 288 p->data = pkt; 289 *has = 1; 290 } 291 292 static void 293 dt_msg_fill_net(struct dt_msg *dm, 294 #ifdef INET6 295 struct sockaddr_storage *rs, 296 struct sockaddr_storage *qs, 297 #else 298 struct sockaddr_in *rs, 299 struct sockaddr_in *qs, 300 #endif 301 int is_tcp, 302 ProtobufCBinaryData *raddr, protobuf_c_boolean *has_raddr, 303 uint32_t *rport, protobuf_c_boolean *has_rport, 304 ProtobufCBinaryData *qaddr, protobuf_c_boolean *has_qaddr, 305 uint32_t *qport, protobuf_c_boolean *has_qport) 306 307 { 308 #ifdef INET6 309 assert(qs->ss_family == AF_INET6 || qs->ss_family == AF_INET); 310 if (qs->ss_family == AF_INET6) { 311 struct sockaddr_in6 *s = (struct sockaddr_in6 *) qs; 312 313 /* socket_family */ 314 dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET6; 315 dm->m.has_socket_family = 1; 316 317 /* addr: query_address or response_address */ 318 qaddr->data = s->sin6_addr.s6_addr; 319 qaddr->len = 16; /* IPv6 */ 320 *has_qaddr = 1; 321 322 /* port: query_port or response_port */ 323 *qport = ntohs(s->sin6_port); 324 *has_qport = 1; 325 } else if (qs->ss_family == AF_INET) { 326 #else 327 if (qs->sin_family == AF_INET) { 328 #endif /* INET6 */ 329 struct sockaddr_in *s = (struct sockaddr_in *) qs; 330 331 /* socket_family */ 332 dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET; 333 dm->m.has_socket_family = 1; 334 335 /* addr: query_address or response_address */ 336 qaddr->data = (uint8_t *) &s->sin_addr.s_addr; 337 qaddr->len = 4; /* IPv4 */ 338 *has_qaddr = 1; 339 340 /* port: query_port or response_port */ 341 *qport = ntohs(s->sin_port); 342 *has_qport = 1; 343 } 344 345 #ifdef INET6 346 assert(rs->ss_family == AF_INET6 || rs->ss_family == AF_INET); 347 if (rs->ss_family == AF_INET6) { 348 struct sockaddr_in6 *s = (struct sockaddr_in6 *) rs; 349 350 /* addr: query_address or response_address */ 351 raddr->data = s->sin6_addr.s6_addr; 352 raddr->len = 16; /* IPv6 */ 353 *has_raddr = 1; 354 355 /* port: query_port or response_port */ 356 *rport = ntohs(s->sin6_port); 357 *has_rport = 1; 358 } else if (rs->ss_family == AF_INET) { 359 #else 360 if (rs->sin_family == AF_INET) { 361 #endif /* INET6 */ 362 struct sockaddr_in *s = (struct sockaddr_in *) rs; 363 364 /* addr: query_address or response_address */ 365 raddr->data = (uint8_t *) &s->sin_addr.s_addr; 366 raddr->len = 4; /* IPv4 */ 367 *has_raddr = 1; 368 369 /* port: query_port or response_port */ 370 *rport = ntohs(s->sin_port); 371 *has_rport = 1; 372 } 373 374 375 if (!is_tcp) { 376 /* socket_protocol */ 377 dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__UDP; 378 dm->m.has_socket_protocol = 1; 379 } else { 380 /* socket_protocol */ 381 dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__TCP; 382 dm->m.has_socket_protocol = 1; 383 } 384 } 385 386 void 387 dt_msg_send_auth_query(struct dt_env *env, 388 #ifdef INET6 389 struct sockaddr_storage* local_addr, 390 struct sockaddr_storage* addr, 391 #else 392 struct sockaddr_in* local_addr, 393 struct sockaddr_in* addr, 394 #endif 395 int is_tcp, uint8_t* zone, size_t zonelen, uint8_t* pkt, size_t pktlen) 396 { 397 struct dt_msg dm; 398 struct timeval qtime; 399 400 gettimeofday(&qtime, NULL); 401 402 /* type */ 403 dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__AUTH_QUERY); 404 405 if(zone) { 406 /* query_zone */ 407 dm.m.query_zone.data = zone; 408 dm.m.query_zone.len = zonelen; 409 dm.m.has_query_zone = 1; 410 } 411 412 /* query_time */ 413 dt_fill_timeval(&qtime, 414 &dm.m.query_time_sec, &dm.m.has_query_time_sec, 415 &dm.m.query_time_nsec, &dm.m.has_query_time_nsec); 416 417 /* query_message */ 418 dt_fill_buffer(pkt, pktlen, &dm.m.query_message, &dm.m.has_query_message); 419 420 /* socket_family, socket_protocol, query_address, query_port, reponse_address (local_address), response_port (local_port) */ 421 dt_msg_fill_net(&dm, local_addr, addr, is_tcp, 422 &dm.m.response_address, &dm.m.has_response_address, 423 &dm.m.response_port, &dm.m.has_response_port, 424 &dm.m.query_address, &dm.m.has_query_address, 425 &dm.m.query_port, &dm.m.has_query_port); 426 427 428 if (dt_pack(&dm.d, &dm.buf, &dm.len_buf)) 429 dt_send(env, dm.buf, dm.len_buf); 430 } 431 432 void 433 dt_msg_send_auth_response(struct dt_env *env, 434 #ifdef INET6 435 struct sockaddr_storage* local_addr, 436 struct sockaddr_storage* addr, 437 #else 438 struct sockaddr_in* local_addr, 439 struct sockaddr_in* addr, 440 #endif 441 int is_tcp, uint8_t* zone, size_t zonelen, uint8_t* pkt, size_t pktlen) 442 { 443 struct dt_msg dm; 444 struct timeval rtime; 445 446 gettimeofday(&rtime, NULL); 447 448 /* type */ 449 dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE); 450 451 if(zone) { 452 /* query_zone */ 453 dm.m.query_zone.data = zone; 454 dm.m.query_zone.len = zonelen; 455 dm.m.has_query_zone = 1; 456 } 457 458 /* response_time */ 459 dt_fill_timeval(&rtime, 460 &dm.m.response_time_sec, &dm.m.has_response_time_sec, 461 &dm.m.response_time_nsec, &dm.m.has_response_time_nsec); 462 463 /* response_message */ 464 dt_fill_buffer(pkt, pktlen, &dm.m.response_message, &dm.m.has_response_message); 465 466 /* socket_family, socket_protocol, query_address, query_port, response_address (local_address), response_port (local_port) */ 467 dt_msg_fill_net(&dm, local_addr, addr, is_tcp, 468 &dm.m.response_address, &dm.m.has_response_address, 469 &dm.m.response_port, &dm.m.has_response_port, 470 &dm.m.query_address, &dm.m.has_query_address, 471 &dm.m.query_port, &dm.m.has_query_port); 472 473 if (dt_pack(&dm.d, &dm.buf, &dm.len_buf)) 474 dt_send(env, dm.buf, dm.len_buf); 475 } 476 477 #endif /* USE_DNSTAP */ 478