| 1 |
/* |
| 2 |
firedns.c - firedns library |
| 3 |
Copyright (C) 2002 Ian Gulliver |
| 4 |
|
| 5 |
This file has been gutted and mucked with for use in BOPM - see the |
| 6 |
real library at http://ares.penguinhosting.net/~ian/ before you judge |
| 7 |
firedns based on this.. |
| 8 |
|
| 9 |
This program is free software; you can redistribute it and/or modify |
| 10 |
it under the terms of version 2 of the GNU General Public License as |
| 11 |
published by the Free Software Foundation. |
| 12 |
|
| 13 |
This program is distributed in the hope that it will be useful, |
| 14 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 |
GNU General Public License for more details. |
| 17 |
|
| 18 |
You should have received a copy of the GNU General Public License |
| 19 |
along with this program; if not, write to the Free Software |
| 20 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 21 |
*/ |
| 22 |
|
| 23 |
#include "setup.h" |
| 24 |
|
| 25 |
#include <stdlib.h> |
| 26 |
#include <time.h> |
| 27 |
#include <sys/types.h> |
| 28 |
#include <sys/socket.h> |
| 29 |
#include <poll.h> |
| 30 |
#include <sys/time.h> |
| 31 |
#include <netinet/in.h> |
| 32 |
#include <arpa/inet.h> |
| 33 |
#include <string.h> |
| 34 |
#include <unistd.h> |
| 35 |
#include <stdio.h> |
| 36 |
#include <errno.h> |
| 37 |
#include <fcntl.h> |
| 38 |
|
| 39 |
#include "compat.h" |
| 40 |
#include "memory.h" |
| 41 |
#include "firedns.h" |
| 42 |
#include "config.h" |
| 43 |
#include "list.h" |
| 44 |
#include "log.h" |
| 45 |
#include "dnsbl.h" |
| 46 |
|
| 47 |
#define FIREDNS_TRIES 3 |
| 48 |
|
| 49 |
int firedns_errno = FDNS_ERR_NONE; |
| 50 |
|
| 51 |
/* Variables local to this file */ |
| 52 |
static unsigned int firedns_fdinuse; |
| 53 |
|
| 54 |
/* up to FDNS_MAX nameservers; populated by firedns_init() */ |
| 55 |
static struct in_addr servers4[FDNS_MAX]; |
| 56 |
static struct in6_addr servers6[FDNS_MAX]; |
| 57 |
/* actual count of nameservers; set by firedns_init() */ |
| 58 |
static int i4; |
| 59 |
static int i6; |
| 60 |
|
| 61 |
/* |
| 62 |
* Linked list of open DNS queries; populated by firedns_add_query(), |
| 63 |
* decimated by firedns_getresult() |
| 64 |
*/ |
| 65 |
static list_t *CONNECTIONS; |
| 66 |
|
| 67 |
/* |
| 68 |
* List of errors, in order of values used in FDNS_ERR_*, returned by |
| 69 |
* firedns_strerror |
| 70 |
*/ |
| 71 |
static const char *errors[] = |
| 72 |
{ |
| 73 |
[FDNS_ERR_NONE] = "Success", |
| 74 |
[FDNS_ERR_FORMAT] = "Format error", |
| 75 |
[FDNS_ERR_SERVFAIL] = "Server failure", |
| 76 |
[FDNS_ERR_NXDOMAIN] = "Name error", |
| 77 |
[FDNS_ERR_NOIMPT] = "Not implemented", |
| 78 |
[FDNS_ERR_REFUSED] = "Refused", |
| 79 |
[FDNS_ERR_TIMEOUT] = "Timeout", |
| 80 |
[FDNS_ERR_NETWORK] = "Network error", |
| 81 |
[FDNS_ERR_FDLIMIT] = "FD Limit reached", |
| 82 |
[FDNS_ERR_OTHER] = "Unknown error" |
| 83 |
}; |
| 84 |
|
| 85 |
/* Structures */ |
| 86 |
|
| 87 |
/* open DNS query */ |
| 88 |
struct s_connection |
| 89 |
{ |
| 90 |
/* |
| 91 |
* unique ID (random number), matches header ID; both set by |
| 92 |
* firedns_add_query() |
| 93 |
*/ |
| 94 |
unsigned char id[2]; |
| 95 |
uint16_t class; |
| 96 |
uint16_t type; |
| 97 |
|
| 98 |
/* file descriptor returned from sockets */ |
| 99 |
int fd; |
| 100 |
void *info; |
| 101 |
time_t start; |
| 102 |
char lookup[256]; |
| 103 |
int v6; |
| 104 |
}; |
| 105 |
|
| 106 |
struct s_rr_middle |
| 107 |
{ |
| 108 |
uint16_t type; |
| 109 |
uint16_t class; |
| 110 |
|
| 111 |
/* XXX - firedns depends on this being 4 bytes */ |
| 112 |
uint32_t ttl; |
| 113 |
uint16_t rdlength; |
| 114 |
}; |
| 115 |
|
| 116 |
/* DNS query header */ |
| 117 |
struct s_header |
| 118 |
{ |
| 119 |
unsigned char id[2]; |
| 120 |
unsigned char flags1; |
| 121 |
#define FLAGS1_MASK_QR 0x80 |
| 122 |
/* bitshift right 3 */ |
| 123 |
#define FLAGS1_MASK_OPCODE 0x78 |
| 124 |
#define FLAGS1_MASK_AA 0x04 |
| 125 |
#define FLAGS1_MASK_TC 0x02 |
| 126 |
#define FLAGS1_MASK_RD 0x01 |
| 127 |
|
| 128 |
unsigned char flags2; |
| 129 |
#define FLAGS2_MASK_RA 0x80 |
| 130 |
#define FLAGS2_MASK_Z 0x70 |
| 131 |
#define FLAGS2_MASK_RCODE 0x0f |
| 132 |
|
| 133 |
uint16_t qdcount; |
| 134 |
uint16_t ancount; |
| 135 |
uint16_t nscount; |
| 136 |
uint16_t arcount; |
| 137 |
|
| 138 |
/* DNS question, populated by firedns_build_query_payload() */ |
| 139 |
unsigned char payload[512]; |
| 140 |
}; |
| 141 |
|
| 142 |
/* Function prototypes */ |
| 143 |
static struct s_connection *firedns_add_query(void); |
| 144 |
static int firedns_doquery(struct s_connection *); |
| 145 |
static int firedns_build_query_payload(const char *const, uint16_t, uint16_t, unsigned char *); |
| 146 |
static int firedns_send_requests(struct s_header *, struct s_connection *, int); |
| 147 |
|
| 148 |
|
| 149 |
void |
| 150 |
firedns_init(void) |
| 151 |
{ |
| 152 |
/* |
| 153 |
* populates servers4 (or -6) struct with up to FDNS_MAX nameserver IP |
| 154 |
* addresses from /etc/firedns.conf (or /etc/resolv.conf) |
| 155 |
*/ |
| 156 |
FILE *f; |
| 157 |
int i; |
| 158 |
struct in_addr addr4; |
| 159 |
struct in6_addr addr6; |
| 160 |
char buf[1024]; |
| 161 |
char *p = NULL; |
| 162 |
|
| 163 |
i6 = 0; |
| 164 |
i4 = 0; |
| 165 |
|
| 166 |
/* Initialize connections list */ |
| 167 |
CONNECTIONS = list_create(); |
| 168 |
|
| 169 |
srand((unsigned int)time(NULL)); |
| 170 |
memset(servers4, 0, sizeof(servers4)); |
| 171 |
memset(servers6, 0, sizeof(servers6)); |
| 172 |
|
| 173 |
/* read etc/firedns.conf if we've got it, otherwise parse /etc/resolv.conf */ |
| 174 |
f = fopen(FDNS_CONFIG_PREF, "r"); |
| 175 |
|
| 176 |
if (f == NULL) |
| 177 |
{ |
| 178 |
f = fopen(FDNS_CONFIG_FBCK, "r"); |
| 179 |
|
| 180 |
if (f == NULL) |
| 181 |
{ |
| 182 |
log_printf("Unable to open %s", FDNS_CONFIG_FBCK); |
| 183 |
return; |
| 184 |
} |
| 185 |
|
| 186 |
while (fgets(buf, sizeof(buf), f)) |
| 187 |
{ |
| 188 |
if ((p = strchr(buf, '\n'))) |
| 189 |
*p = '\0'; |
| 190 |
|
| 191 |
if (strncmp(buf, "nameserver", 10) == 0) |
| 192 |
{ |
| 193 |
i = 10; |
| 194 |
|
| 195 |
while (buf[i] == ' ' || buf[i] == '\t') |
| 196 |
++i; |
| 197 |
|
| 198 |
if (i6 < FDNS_MAX) |
| 199 |
{ |
| 200 |
if (inet_pton(AF_INET6, &buf[i], &addr6) > 0) |
| 201 |
{ |
| 202 |
memcpy(&servers6[i6++], &addr6, sizeof(struct in6_addr)); |
| 203 |
continue; |
| 204 |
} |
| 205 |
} |
| 206 |
|
| 207 |
if (i4 < FDNS_MAX) |
| 208 |
{ |
| 209 |
if (inet_pton(AF_INET, &buf[i], &addr4) > 0) |
| 210 |
memcpy(&servers4[i4++], &addr4, sizeof(struct in_addr)); |
| 211 |
} |
| 212 |
} |
| 213 |
} |
| 214 |
} |
| 215 |
else |
| 216 |
{ |
| 217 |
while (fgets(buf, sizeof(buf), f)) |
| 218 |
{ |
| 219 |
if ((p = strchr(buf, '\n'))) |
| 220 |
*p = '\0'; |
| 221 |
|
| 222 |
if (i6 < FDNS_MAX) |
| 223 |
{ |
| 224 |
if (inet_pton(AF_INET6, buf, &addr6) > 0) |
| 225 |
{ |
| 226 |
memcpy(&servers6[i6++], &addr6, sizeof(struct in6_addr)); |
| 227 |
continue; |
| 228 |
} |
| 229 |
} |
| 230 |
|
| 231 |
if (i4 < FDNS_MAX) |
| 232 |
{ |
| 233 |
if (inet_pton(AF_INET, buf, &addr4) > 0) |
| 234 |
memcpy(&servers4[i4++], &addr4, sizeof(struct in_addr)); |
| 235 |
} |
| 236 |
} |
| 237 |
} |
| 238 |
|
| 239 |
fclose(f); |
| 240 |
} |
| 241 |
|
| 242 |
/* immediate A query */ |
| 243 |
struct in_addr * |
| 244 |
firedns_resolveip4(const char *const name) |
| 245 |
{ |
| 246 |
static struct in_addr addr; |
| 247 |
|
| 248 |
if (inet_pton(AF_INET, name, &addr) > 0) |
| 249 |
return &addr; |
| 250 |
|
| 251 |
return (struct in_addr *)firedns_resolveip(FDNS_QRY_A, name); |
| 252 |
} |
| 253 |
|
| 254 |
/* immediate AAAA query */ |
| 255 |
struct in6_addr * |
| 256 |
firedns_resolveip6(const char * const name) |
| 257 |
{ |
| 258 |
static struct in6_addr addr; |
| 259 |
|
| 260 |
if (inet_pton(AF_INET6, name, &addr) > 0) |
| 261 |
return &addr; |
| 262 |
|
| 263 |
return (struct in6_addr *)firedns_resolveip(FDNS_QRY_AAAA, name); |
| 264 |
} |
| 265 |
|
| 266 |
/* resolve a query of a given type */ |
| 267 |
char * |
| 268 |
firedns_resolveip(int type, const char *const name) |
| 269 |
{ |
| 270 |
struct firedns_result *result; |
| 271 |
struct timeval tv; |
| 272 |
fd_set s; |
| 273 |
|
| 274 |
for (int t = 0; t < FIREDNS_TRIES; ++t) |
| 275 |
{ |
| 276 |
int fd = firedns_getip(type, name, NULL); |
| 277 |
|
| 278 |
if (fd == -1) |
| 279 |
return NULL; |
| 280 |
|
| 281 |
tv.tv_sec = 5; |
| 282 |
tv.tv_usec = 0; |
| 283 |
FD_ZERO(&s); |
| 284 |
FD_SET(fd, &s); |
| 285 |
select(fd + 1, &s, NULL, NULL, &tv); |
| 286 |
|
| 287 |
result = firedns_getresult(fd); |
| 288 |
|
| 289 |
if (firedns_errno == FDNS_ERR_NONE) |
| 290 |
/* |
| 291 |
* Return is from static memory in getresult, so there is no need to |
| 292 |
* copy it until the next call to firedns. |
| 293 |
*/ |
| 294 |
return result->text; |
| 295 |
else if (firedns_errno == FDNS_ERR_NXDOMAIN) |
| 296 |
return NULL; |
| 297 |
} |
| 298 |
|
| 299 |
if (firedns_errno == FDNS_ERR_NONE) |
| 300 |
firedns_errno = FDNS_ERR_TIMEOUT; |
| 301 |
|
| 302 |
return NULL; |
| 303 |
} |
| 304 |
|
| 305 |
/* |
| 306 |
* build, add and send specified query; retrieve result with |
| 307 |
* firedns_getresult() |
| 308 |
*/ |
| 309 |
int |
| 310 |
firedns_getip(int type, const char * const name, void *info) |
| 311 |
{ |
| 312 |
struct s_connection *s; |
| 313 |
node_t *node; |
| 314 |
int fd; |
| 315 |
|
| 316 |
s = firedns_add_query(); |
| 317 |
s->class = 1; |
| 318 |
s->type = type; |
| 319 |
s->info = info; |
| 320 |
strlcpy(s->lookup, name, sizeof(s->lookup)); |
| 321 |
|
| 322 |
if (firedns_fdinuse >= OptionsItem->dns_fdlimit) |
| 323 |
{ |
| 324 |
firedns_errno = FDNS_ERR_FDLIMIT; |
| 325 |
|
| 326 |
/* Don't add to queue if there is no info */ |
| 327 |
if (info == NULL) |
| 328 |
xfree(s); |
| 329 |
else |
| 330 |
{ |
| 331 |
node = node_create(s); |
| 332 |
list_add(CONNECTIONS, node); |
| 333 |
} |
| 334 |
|
| 335 |
return -1; |
| 336 |
} |
| 337 |
|
| 338 |
fd = firedns_doquery(s); |
| 339 |
|
| 340 |
if (fd == -1) |
| 341 |
{ |
| 342 |
xfree(s); |
| 343 |
return -1; |
| 344 |
} |
| 345 |
|
| 346 |
node = node_create(s); |
| 347 |
list_add(CONNECTIONS, node); |
| 348 |
|
| 349 |
return fd; |
| 350 |
} |
| 351 |
|
| 352 |
/* build DNS query, add to list */ |
| 353 |
static struct s_connection * |
| 354 |
firedns_add_query(void) |
| 355 |
{ |
| 356 |
struct s_connection *s; |
| 357 |
|
| 358 |
/* create new connection object */ |
| 359 |
s = xcalloc(sizeof *s); |
| 360 |
|
| 361 |
/* verified by firedns_getresult() */ |
| 362 |
s->id[0] = rand() % 255; |
| 363 |
s->id[1] = rand() % 255; |
| 364 |
s->fd = -1; |
| 365 |
|
| 366 |
return s; |
| 367 |
} |
| 368 |
|
| 369 |
static int |
| 370 |
firedns_doquery(struct s_connection *s) |
| 371 |
{ |
| 372 |
int len; |
| 373 |
struct s_header h; |
| 374 |
|
| 375 |
len = firedns_build_query_payload(s->lookup, s->type, 1, (unsigned char *)&h.payload); |
| 376 |
|
| 377 |
if (len == -1) |
| 378 |
{ |
| 379 |
firedns_errno = FDNS_ERR_FORMAT; |
| 380 |
return -1; |
| 381 |
} |
| 382 |
|
| 383 |
return firedns_send_requests(&h, s, len); |
| 384 |
} |
| 385 |
|
| 386 |
/* |
| 387 |
* populate payload with query: name= question, rr= record type |
| 388 |
*/ |
| 389 |
static int |
| 390 |
firedns_build_query_payload(const char *const name, uint16_t rr, uint16_t class, |
| 391 |
unsigned char *payload) |
| 392 |
{ |
| 393 |
int16_t payloadpos = 0; |
| 394 |
const char *tempchr, *tempchr2; |
| 395 |
uint16_t l; |
| 396 |
|
| 397 |
tempchr2 = name; |
| 398 |
|
| 399 |
/* split name up into labels, create query */ |
| 400 |
while ((tempchr = strchr(tempchr2, '.'))) |
| 401 |
{ |
| 402 |
l = tempchr - tempchr2; |
| 403 |
|
| 404 |
if (payloadpos + l + 1 > 507) |
| 405 |
return -1; |
| 406 |
|
| 407 |
payload[payloadpos++] = l; |
| 408 |
memcpy(&payload[payloadpos], tempchr2, l); |
| 409 |
payloadpos += l; |
| 410 |
tempchr2 = &tempchr[1]; |
| 411 |
} |
| 412 |
|
| 413 |
l = strlen(tempchr2); |
| 414 |
|
| 415 |
if (l) |
| 416 |
{ |
| 417 |
if (payloadpos + l + 2 > 507) |
| 418 |
return -1; |
| 419 |
|
| 420 |
payload[payloadpos++] = l; |
| 421 |
memcpy(&payload[payloadpos], tempchr2, l); |
| 422 |
payloadpos += l; |
| 423 |
payload[payloadpos++] = '\0'; |
| 424 |
} |
| 425 |
|
| 426 |
if (payloadpos > 508) |
| 427 |
return -1; |
| 428 |
|
| 429 |
l = htons(rr); |
| 430 |
memcpy(&payload[payloadpos], &l, 2); |
| 431 |
|
| 432 |
l = htons(class); |
| 433 |
memcpy(&payload[payloadpos + 2], &l, 2); |
| 434 |
|
| 435 |
return payloadpos + 4; |
| 436 |
} |
| 437 |
|
| 438 |
/* send DNS query */ |
| 439 |
static int |
| 440 |
firedns_send_requests(struct s_header *h, struct s_connection *s, int l) |
| 441 |
{ |
| 442 |
int i, sent_ok = 0; |
| 443 |
struct sockaddr_in addr4; |
| 444 |
struct sockaddr_in6 addr6; |
| 445 |
|
| 446 |
/* set header flags */ |
| 447 |
h->flags1 = 0 | FLAGS1_MASK_RD; |
| 448 |
h->flags2 = 0; |
| 449 |
h->qdcount = htons(1); |
| 450 |
h->ancount = htons(0); |
| 451 |
h->nscount = htons(0); |
| 452 |
h->arcount = htons(0); |
| 453 |
memcpy(h->id, s->id, 2); |
| 454 |
|
| 455 |
/* try to create ipv6 or ipv4 socket */ |
| 456 |
s->v6 = 0; |
| 457 |
|
| 458 |
if (i6 > 0) |
| 459 |
{ |
| 460 |
s->fd = socket(AF_INET6, SOCK_DGRAM, 0); |
| 461 |
|
| 462 |
if (s->fd != -1) |
| 463 |
{ |
| 464 |
if (fcntl(s->fd, F_SETFL, O_NONBLOCK)) |
| 465 |
{ |
| 466 |
close(s->fd); |
| 467 |
s->fd = -1; |
| 468 |
} |
| 469 |
} |
| 470 |
|
| 471 |
if (s->fd != -1) |
| 472 |
{ |
| 473 |
memset(&addr6, 0, sizeof(addr6)); |
| 474 |
addr6.sin6_family = AF_INET6; |
| 475 |
|
| 476 |
if (bind(s->fd, (struct sockaddr *)&addr6, sizeof(addr6)) == 0) |
| 477 |
s->v6 = 1; |
| 478 |
else |
| 479 |
close(s->fd); |
| 480 |
} |
| 481 |
} |
| 482 |
|
| 483 |
if (s->v6 == 0) |
| 484 |
{ |
| 485 |
s->fd = socket(AF_INET, SOCK_DGRAM, 0); |
| 486 |
|
| 487 |
if (s->fd != -1) |
| 488 |
{ |
| 489 |
if (fcntl(s->fd, F_SETFL, O_NONBLOCK)) |
| 490 |
{ |
| 491 |
close(s->fd); |
| 492 |
s->fd = -1; |
| 493 |
} |
| 494 |
} |
| 495 |
|
| 496 |
if (s->fd != -1) |
| 497 |
{ |
| 498 |
memset(&addr4, 0, sizeof(addr4)); |
| 499 |
addr4.sin_family = AF_INET; |
| 500 |
addr4.sin_port = 0; |
| 501 |
addr4.sin_addr.s_addr = INADDR_ANY; |
| 502 |
|
| 503 |
if (bind(s->fd, (struct sockaddr *)&addr4, sizeof(addr4)) != 0) |
| 504 |
{ |
| 505 |
close(s->fd); |
| 506 |
s->fd = -1; |
| 507 |
} |
| 508 |
} |
| 509 |
|
| 510 |
if (s->fd == -1) |
| 511 |
{ |
| 512 |
firedns_errno = FDNS_ERR_NETWORK; |
| 513 |
return -1; |
| 514 |
} |
| 515 |
} |
| 516 |
|
| 517 |
/* if we've got ipv6 support, an ip v6 socket, and ipv6 servers, send to them */ |
| 518 |
if (i6 > 0 && s->v6 == 1) |
| 519 |
{ |
| 520 |
for (i = 0; i < i6; i++) |
| 521 |
{ |
| 522 |
memset(&addr6, 0, sizeof(addr6)); |
| 523 |
memcpy(&addr6.sin6_addr, &servers6[i], sizeof(addr6.sin6_addr)); |
| 524 |
|
| 525 |
addr6.sin6_family = AF_INET6; |
| 526 |
addr6.sin6_port = htons(FDNS_PORT); |
| 527 |
|
| 528 |
if (sendto(s->fd, h, l + 12, 0, (struct sockaddr *)&addr6, sizeof(addr6)) > 0) |
| 529 |
sent_ok = 1; |
| 530 |
} |
| 531 |
} |
| 532 |
|
| 533 |
for (i = 0; i < i4; i++) |
| 534 |
{ |
| 535 |
/* send via ipv4-over-ipv6 if we've got an ipv6 socket */ |
| 536 |
if (s->v6 == 1) |
| 537 |
{ |
| 538 |
memset(&addr6, 0, sizeof(addr6)); |
| 539 |
memcpy(addr6.sin6_addr.s6_addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12); |
| 540 |
memcpy(&addr6.sin6_addr.s6_addr[12], &servers4[i].s_addr, 4); |
| 541 |
addr6.sin6_family = AF_INET6; |
| 542 |
addr6.sin6_port = htons(FDNS_PORT); |
| 543 |
|
| 544 |
if (sendto(s->fd, h, l + 12, 0, (struct sockaddr *)&addr6, sizeof(addr6)) > 0) |
| 545 |
sent_ok = 1; |
| 546 |
|
| 547 |
continue; |
| 548 |
} |
| 549 |
|
| 550 |
/* otherwise send via standard ipv4 boringness */ |
| 551 |
memset(&addr4, 0, sizeof(addr4)); |
| 552 |
memcpy(&addr4.sin_addr, &servers4[i], sizeof(addr4.sin_addr)); |
| 553 |
addr4.sin_family = AF_INET; |
| 554 |
addr4.sin_port = htons(FDNS_PORT); |
| 555 |
|
| 556 |
if (sendto(s->fd, h, l + 12, 0, (struct sockaddr *)&addr4, sizeof(addr4)) > 0) |
| 557 |
sent_ok = 1; |
| 558 |
} |
| 559 |
|
| 560 |
if (!sent_ok) |
| 561 |
{ |
| 562 |
close(s->fd); |
| 563 |
s->fd = -1; |
| 564 |
firedns_errno = FDNS_ERR_NETWORK; |
| 565 |
return -1; |
| 566 |
} |
| 567 |
|
| 568 |
time(&s->start); |
| 569 |
firedns_fdinuse++; |
| 570 |
firedns_errno = FDNS_ERR_NONE; |
| 571 |
|
| 572 |
return s->fd; |
| 573 |
} |
| 574 |
|
| 575 |
/* retrieve result of DNS query */ |
| 576 |
struct firedns_result * |
| 577 |
firedns_getresult(const int fd) |
| 578 |
{ |
| 579 |
static struct firedns_result result; |
| 580 |
struct s_header h; |
| 581 |
struct s_connection *c; |
| 582 |
node_t *node; |
| 583 |
int l, i, q, curanswer; |
| 584 |
struct s_rr_middle *rr, rrbacking; |
| 585 |
char *src, *dst; |
| 586 |
int bytes; |
| 587 |
|
| 588 |
firedns_errno = FDNS_ERR_OTHER; |
| 589 |
result.info = NULL; |
| 590 |
|
| 591 |
memset(result.text, 0, sizeof(result.text)); |
| 592 |
|
| 593 |
/* Find query in list of dns lookups */ |
| 594 |
LIST_FOREACH(node, CONNECTIONS->head) |
| 595 |
{ |
| 596 |
c = node->data; |
| 597 |
|
| 598 |
if (c->fd == fd) |
| 599 |
break; |
| 600 |
else |
| 601 |
c = NULL; |
| 602 |
} |
| 603 |
|
| 604 |
/* query not found */ |
| 605 |
if (c == NULL) |
| 606 |
return &result; |
| 607 |
|
| 608 |
/* query found -- we remove in cleanup */ |
| 609 |
l = recv(c->fd, &h,sizeof(struct s_header), 0); |
| 610 |
result.info = c->info; |
| 611 |
strlcpy(result.lookup, c->lookup, sizeof(result.lookup)); |
| 612 |
|
| 613 |
if (l == -1) |
| 614 |
{ |
| 615 |
firedns_errno = FDNS_ERR_NETWORK; |
| 616 |
goto cleanup; |
| 617 |
} |
| 618 |
|
| 619 |
if (l < 12) |
| 620 |
goto cleanup; |
| 621 |
|
| 622 |
if (c->id[0] != h.id[0] || c->id[1] != h.id[1]) |
| 623 |
/* |
| 624 |
* ID mismatch: we keep the connection, as this could be an answer to |
| 625 |
* a previous lookup.. |
| 626 |
*/ |
| 627 |
return NULL; |
| 628 |
|
| 629 |
if ((h.flags1 & FLAGS1_MASK_QR) == 0) |
| 630 |
goto cleanup; |
| 631 |
|
| 632 |
if ((h.flags1 & FLAGS1_MASK_OPCODE) != 0) |
| 633 |
goto cleanup; |
| 634 |
|
| 635 |
if ((h.flags2 & FLAGS2_MASK_RCODE) != 0) |
| 636 |
{ |
| 637 |
firedns_errno = (h.flags2 & FLAGS2_MASK_RCODE); |
| 638 |
goto cleanup; |
| 639 |
} |
| 640 |
|
| 641 |
h.ancount = ntohs(h.ancount); |
| 642 |
|
| 643 |
if (h.ancount < 1) |
| 644 |
{ |
| 645 |
firedns_errno = FDNS_ERR_NXDOMAIN; |
| 646 |
/* no sense going on if we don't have any answers */ |
| 647 |
goto cleanup; |
| 648 |
} |
| 649 |
|
| 650 |
/* skip queries */ |
| 651 |
i = 0; |
| 652 |
q = 0; |
| 653 |
l -= 12; |
| 654 |
h.qdcount = ntohs(h.qdcount); |
| 655 |
|
| 656 |
while (q < h.qdcount && i < l) |
| 657 |
{ |
| 658 |
if (h.payload[i] > 63) |
| 659 |
{ |
| 660 |
/* pointer */ |
| 661 |
i += 6; /* skip pointer, class and type */ |
| 662 |
q++; |
| 663 |
} |
| 664 |
else |
| 665 |
{ |
| 666 |
/* label */ |
| 667 |
if (h.payload[i] == 0) |
| 668 |
{ |
| 669 |
q++; |
| 670 |
i += 5; /* skip nil, class and type */ |
| 671 |
} |
| 672 |
else |
| 673 |
i += h.payload[i] + 1; /* skip length and label */ |
| 674 |
} |
| 675 |
} |
| 676 |
|
| 677 |
/* &h.payload[i] should now be the start of the first response */ |
| 678 |
curanswer = 0; |
| 679 |
|
| 680 |
while (curanswer < h.ancount) |
| 681 |
{ |
| 682 |
q = 0; |
| 683 |
|
| 684 |
while (q == 0 && i < l) |
| 685 |
{ |
| 686 |
if (h.payload[i] > 63) |
| 687 |
{ |
| 688 |
/* pointer */ |
| 689 |
i += 2; /* skip pointer */ |
| 690 |
q = 1; |
| 691 |
} |
| 692 |
else |
| 693 |
{ |
| 694 |
/* label */ |
| 695 |
if (h.payload[i] == 0) |
| 696 |
{ |
| 697 |
i++; |
| 698 |
q = 1; |
| 699 |
} |
| 700 |
else |
| 701 |
i += h.payload[i] + 1; /* skip length and label */ |
| 702 |
} |
| 703 |
} |
| 704 |
|
| 705 |
if (l - i < 10) |
| 706 |
goto cleanup; |
| 707 |
|
| 708 |
rr = (struct s_rr_middle *)&h.payload[i]; |
| 709 |
src = (char *)rr; |
| 710 |
dst = (char *)&rrbacking; |
| 711 |
|
| 712 |
for (bytes = sizeof(rrbacking); bytes; bytes--) |
| 713 |
*dst++ = *src++; |
| 714 |
|
| 715 |
rr = &rrbacking; |
| 716 |
i += 10; |
| 717 |
rr->rdlength = ntohs(rr->rdlength); |
| 718 |
|
| 719 |
if (ntohs(rr->type) != c->type) |
| 720 |
{ |
| 721 |
curanswer++; |
| 722 |
i += rr->rdlength; |
| 723 |
continue; |
| 724 |
} |
| 725 |
|
| 726 |
if (ntohs(rr->class) != c->class) |
| 727 |
{ |
| 728 |
curanswer++; |
| 729 |
i += rr->rdlength; |
| 730 |
continue; |
| 731 |
} |
| 732 |
|
| 733 |
break; |
| 734 |
} |
| 735 |
|
| 736 |
if (curanswer == h.ancount) |
| 737 |
goto cleanup; |
| 738 |
if (i + rr->rdlength > l) |
| 739 |
goto cleanup; |
| 740 |
if (rr->rdlength > 1023) |
| 741 |
goto cleanup; |
| 742 |
|
| 743 |
firedns_errno = FDNS_ERR_NONE; |
| 744 |
memcpy(result.text, &h.payload[i], rr->rdlength); |
| 745 |
result.text[rr->rdlength] = '\0'; |
| 746 |
|
| 747 |
/* Clean-up */ |
| 748 |
cleanup: |
| 749 |
list_remove(CONNECTIONS, node); |
| 750 |
node_free(node); |
| 751 |
close(c->fd); |
| 752 |
firedns_fdinuse--; |
| 753 |
xfree(c); |
| 754 |
|
| 755 |
return &result; |
| 756 |
} |
| 757 |
|
| 758 |
void |
| 759 |
firedns_cycle(void) |
| 760 |
{ |
| 761 |
node_t *node, *node_next; |
| 762 |
struct s_connection *p; |
| 763 |
struct firedns_result *res, new_result; |
| 764 |
static struct pollfd *ufds = NULL; |
| 765 |
int fd; |
| 766 |
unsigned int size, i; |
| 767 |
time_t timenow; |
| 768 |
|
| 769 |
if (LIST_SIZE(CONNECTIONS) == 0) |
| 770 |
return; |
| 771 |
|
| 772 |
if (ufds == NULL) |
| 773 |
ufds = xcalloc((sizeof *ufds) * OptionsItem->dns_fdlimit); |
| 774 |
|
| 775 |
time(&timenow); |
| 776 |
size = 0; |
| 777 |
|
| 778 |
LIST_FOREACH_SAFE(node, node_next, CONNECTIONS->head) |
| 779 |
{ |
| 780 |
if (size >= OptionsItem->dns_fdlimit) |
| 781 |
break; |
| 782 |
|
| 783 |
p = node->data; |
| 784 |
|
| 785 |
if (p->fd < 0) |
| 786 |
continue; |
| 787 |
|
| 788 |
if (p->fd > 0 && (p->start + FDNS_TIMEOUT) < timenow) |
| 789 |
{ |
| 790 |
/* Timed out - remove from list */ |
| 791 |
list_remove(CONNECTIONS, node); |
| 792 |
node_free(node); |
| 793 |
|
| 794 |
memset(new_result.text, 0, sizeof(new_result.text)); |
| 795 |
new_result.info = p->info; |
| 796 |
strlcpy(new_result.lookup, p->lookup, sizeof(new_result.lookup)); |
| 797 |
|
| 798 |
close(p->fd); |
| 799 |
firedns_fdinuse--; |
| 800 |
xfree(p); |
| 801 |
|
| 802 |
firedns_errno = FDNS_ERR_TIMEOUT; |
| 803 |
|
| 804 |
if (new_result.info) |
| 805 |
dnsbl_result(&new_result); |
| 806 |
|
| 807 |
continue; |
| 808 |
} |
| 809 |
|
| 810 |
ufds[size].events = 0; |
| 811 |
ufds[size].revents = 0; |
| 812 |
ufds[size].fd = p->fd; |
| 813 |
ufds[size].events = POLLIN; |
| 814 |
|
| 815 |
++size; |
| 816 |
} |
| 817 |
|
| 818 |
switch (poll(ufds, size, 0)) |
| 819 |
{ |
| 820 |
case -1: |
| 821 |
case 0: |
| 822 |
return; |
| 823 |
} |
| 824 |
|
| 825 |
LIST_FOREACH_SAFE(node, node_next, CONNECTIONS->head) |
| 826 |
{ |
| 827 |
p = node->data; |
| 828 |
|
| 829 |
if (p->fd > 0) |
| 830 |
{ |
| 831 |
for (i = 0; i < size; ++i) |
| 832 |
{ |
| 833 |
if ((ufds[i].revents & POLLIN) && ufds[i].fd == p->fd) |
| 834 |
{ |
| 835 |
fd = p->fd; |
| 836 |
res = firedns_getresult(fd); |
| 837 |
|
| 838 |
if (res && res->info) |
| 839 |
dnsbl_result(res); |
| 840 |
|
| 841 |
break; |
| 842 |
} |
| 843 |
} |
| 844 |
} |
| 845 |
else if (firedns_fdinuse < OptionsItem->dns_fdlimit) |
| 846 |
firedns_doquery(p); |
| 847 |
} |
| 848 |
} |
| 849 |
|
| 850 |
const char * |
| 851 |
firedns_strerror(int error) |
| 852 |
{ |
| 853 |
if (error == FDNS_ERR_NETWORK) |
| 854 |
return strerror(errno); |
| 855 |
|
| 856 |
return errors[error]; |
| 857 |
} |