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 *ss, 296 #else 297 struct sockaddr_in *ss, 298 #endif 299 int is_tcp, 300 ProtobufCBinaryData *addr, protobuf_c_boolean *has_addr, 301 uint32_t *port, protobuf_c_boolean *has_port) 302 { 303 #ifdef INET6 304 assert(ss->ss_family == AF_INET6 || ss->ss_family == AF_INET); 305 if (ss->ss_family == AF_INET6) { 306 struct sockaddr_in6 *s = (struct sockaddr_in6 *) ss; 307 308 /* socket_family */ 309 dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET6; 310 dm->m.has_socket_family = 1; 311 312 /* addr: query_address or response_address */ 313 addr->data = s->sin6_addr.s6_addr; 314 addr->len = 16; /* IPv6 */ 315 *has_addr = 1; 316 317 /* port: query_port or response_port */ 318 *port = ntohs(s->sin6_port); 319 *has_port = 1; 320 } else if (ss->ss_family == AF_INET) { 321 #else 322 if (ss->sin_family == AF_INET) { 323 #endif /* INET6 */ 324 struct sockaddr_in *s = (struct sockaddr_in *) ss; 325 326 /* socket_family */ 327 dm->m.socket_family = DNSTAP__SOCKET_FAMILY__INET; 328 dm->m.has_socket_family = 1; 329 330 /* addr: query_address or response_address */ 331 addr->data = (uint8_t *) &s->sin_addr.s_addr; 332 addr->len = 4; /* IPv4 */ 333 *has_addr = 1; 334 335 /* port: query_port or response_port */ 336 *port = ntohs(s->sin_port); 337 *has_port = 1; 338 } 339 340 if (!is_tcp) { 341 /* socket_protocol */ 342 dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__UDP; 343 dm->m.has_socket_protocol = 1; 344 } else { 345 /* socket_protocol */ 346 dm->m.socket_protocol = DNSTAP__SOCKET_PROTOCOL__TCP; 347 dm->m.has_socket_protocol = 1; 348 } 349 } 350 351 void 352 dt_msg_send_auth_query(struct dt_env *env, 353 #ifdef INET6 354 struct sockaddr_storage* addr, 355 #else 356 struct sockaddr_in* addr, 357 #endif 358 int is_tcp, uint8_t* zone, size_t zonelen, uint8_t* pkt, size_t pktlen) 359 { 360 struct dt_msg dm; 361 struct timeval qtime; 362 363 gettimeofday(&qtime, NULL); 364 365 /* type */ 366 dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__AUTH_QUERY); 367 368 if(zone) { 369 /* query_zone */ 370 dm.m.query_zone.data = zone; 371 dm.m.query_zone.len = zonelen; 372 dm.m.has_query_zone = 1; 373 } 374 375 /* query_time */ 376 dt_fill_timeval(&qtime, 377 &dm.m.query_time_sec, &dm.m.has_query_time_sec, 378 &dm.m.query_time_nsec, &dm.m.has_query_time_nsec); 379 380 /* query_message */ 381 dt_fill_buffer(pkt, pktlen, &dm.m.query_message, &dm.m.has_query_message); 382 383 /* socket_family, socket_protocol, query_address, query_port */ 384 dt_msg_fill_net(&dm, addr, is_tcp, 385 &dm.m.query_address, &dm.m.has_query_address, 386 &dm.m.query_port, &dm.m.has_query_port); 387 388 if (dt_pack(&dm.d, &dm.buf, &dm.len_buf)) 389 dt_send(env, dm.buf, dm.len_buf); 390 } 391 392 void 393 dt_msg_send_auth_response(struct dt_env *env, 394 #ifdef INET6 395 struct sockaddr_storage* addr, 396 #else 397 struct sockaddr_in* addr, 398 #endif 399 int is_tcp, uint8_t* zone, size_t zonelen, uint8_t* pkt, size_t pktlen) 400 { 401 struct dt_msg dm; 402 struct timeval rtime; 403 404 gettimeofday(&rtime, NULL); 405 406 /* type */ 407 dt_msg_init(env, &dm, DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE); 408 409 if(zone) { 410 /* query_zone */ 411 dm.m.query_zone.data = zone; 412 dm.m.query_zone.len = zonelen; 413 dm.m.has_query_zone = 1; 414 } 415 416 /* response_time */ 417 dt_fill_timeval(&rtime, 418 &dm.m.response_time_sec, &dm.m.has_response_time_sec, 419 &dm.m.response_time_nsec, &dm.m.has_response_time_nsec); 420 421 /* response_message */ 422 dt_fill_buffer(pkt, pktlen, &dm.m.response_message, &dm.m.has_response_message); 423 424 /* socket_family, socket_protocol, query_address, query_port */ 425 dt_msg_fill_net(&dm, addr, is_tcp, 426 &dm.m.query_address, &dm.m.has_query_address, 427 &dm.m.query_port, &dm.m.has_query_port); 428 429 if (dt_pack(&dm.d, &dm.buf, &dm.len_buf)) 430 dt_send(env, dm.buf, dm.len_buf); 431 } 432 433 #endif /* USE_DNSTAP */ 434