A hint: This file contains one or more very long lines, so maybe it is better readable using the pure text view mode that shows the contents as wrapped lines within the browser window.
1 2 #include <string.h> 3 #include "httpd.h" 4 #include "http_config.h" 5 #include "http_core.h" 6 #include "http_log.h" 7 #include "http_protocol.h" 8 #include "http_request.h" 9 #include "http_main.h" 10 #include "http_conf_globals.h" 11 #include "ap_mm.h" 12 13 #include "mod_dns.h" 14 #include "dns_lock.h" 15 #include "hash.h" 16 // djbdns 17 #include "buffer.h" 18 #include "exit.h" 19 #include "strerr.h" 20 #include "ip4.h" 21 #include "dns.h" 22 #include "stralloc.h" 23 #include "uint16.h" 24 #include "byte.h" 25 // end djbdns 26 27 module MODULE_VAR_EXPORT dns_module; 28 29 30 31 //BEGIN DEBUG 32 #define RD_Lock (shared->rd_Lock) 33 #define WR_Lock (shared->wr_Lock) 34 #define CHECK_RLOCK_IN //{ if(WR_Lock) fprintf(stderr,"CHECK_RLOCK_IN: FUCK pid=%d!!!\n",getpid()); RD_Lock++; } 35 #define CHECK_RLOCK_OUT //{ if(WR_Lock||RD_Lock<1) fprintf(stderr,"CHECK_RLOCK_OUT: FUCK w=%d,r=%d,pid=%d!!!\n",WR_Lock,RD_Lock,getpid()); RD_Lock--; } 36 #define CHECK_WLOCK_IN //{ if(WR_Lock) fprintf(stderr,"CHECK_WLOCK_IN: FUCK pid=%d!!!\n",getpid()); WR_Lock++; } 37 #define CHECK_WLOCK_OUT //{ if(!WR_Lock||RD_Lock) fprintf(stderr,"CHECK_WLOCK_OUT: FUCK w=%d,r=%d,pid=%d!!!\n",WR_Lock,RD_Lock,getpid()); WR_Lock--; } 38 //END DEBUG 39 40 41 42 // locks: the ap_mm_lock/ap_mm_unlock functions are not reliable (on linux/i386 at least) 43 //#define SHARED_RD_LOCK() ap_mm_lock (mm, AP_MM_LOCK_RD) 44 //#define SHARED_RW_LOCK() ap_mm_lock (mm, AP_MM_LOCK_RW) 45 //#define SHARED_UNLOCK() ap_mm_unlock (mm) 46 #define SHARED_RD_LOCK() moddns_mutex_lock (shared->lock) 47 #define SHARED_RW_LOCK() moddns_mutex_lock (shared->lock) 48 #define SHARED_UNLOCK() moddns_mutex_unlock (shared->lock) 49 50 51 #define OPT_INT_UNSET (-1) 52 #define NVL_INT(this,that) ((this) != OPT_INT_UNSET ? (this) : (that)) 53 54 55 typedef struct { 56 int lookup_enabled; 57 int timeout_ms; 58 int resendtimer_ms; 59 } moddns_dir_config; 60 61 static void *moddns_create_dir_config(pool *p, char *path) 62 { 63 moddns_dir_config *cfg = (moddns_dir_config *)ap_pcalloc(p, sizeof(moddns_dir_config)); 64 cfg->lookup_enabled = OPT_INT_UNSET; 65 cfg->timeout_ms = atoi(OPT_DNS_TIMEOUT_DEFAULT); 66 cfg->resendtimer_ms = atoi(OPT_DNS_RESENDTIMER_DEFAULT); 67 return (void *)cfg; 68 } 69 70 71 static void *moddns_merge_dir_config (pool *p, void *basev, void *addv) 72 { 73 moddns_dir_config *new = (moddns_dir_config *)ap_pcalloc (p, sizeof(moddns_dir_config)); 74 moddns_dir_config *base = (moddns_dir_config *)basev; 75 moddns_dir_config *add = (moddns_dir_config *)addv; 76 77 new->lookup_enabled = NVL_INT (add->lookup_enabled, base->lookup_enabled); 78 new->timeout_ms = NVL_INT (add->timeout_ms, base->timeout_ms); 79 new->resendtimer_ms = NVL_INT (add->resendtimer_ms, base->resendtimer_ms); 80 81 return (void*)new; 82 } 83 84 85 static const char *moddns_cmd_AdnsLookups (cmd_parms *p, void *d, char *arg) 86 { 87 moddns_dir_config *cf = (moddns_dir_config *)d; 88 if (!strcasecmp(arg, "on")) { 89 cf->lookup_enabled = OPT_DNS_LOOKUP_ON; 90 } else if (!strcasecmp(arg, "off")) { 91 cf->lookup_enabled = OPT_DNS_LOOKUP_OFF; 92 } else { 93 return "parameter must be 'on' or 'off'"; 94 } 95 return NULL; 96 } 97 98 static const char *moddns_cmd_AdnsTimeout_ms (cmd_parms *p, void *d, char *arg) 99 { 100 moddns_dir_config *cf = (moddns_dir_config *)d; 101 int timeout = atoi(arg); 102 if (timeout > 0) { 103 cf->timeout_ms = timeout; 104 } else { 105 return "parameter must be positive number of milliseconds"; 106 } 107 return NULL; 108 } 109 110 static const char *moddns_cmd_AdnsResendTimer_ms (cmd_parms *p, void *d, char *arg) 111 { 112 moddns_dir_config *cf = (moddns_dir_config *)d; 113 int timeout = atoi(arg); 114 if (timeout > 0) { 115 cf->resendtimer_ms = timeout; 116 } else { 117 return "parameter must be positive number of milliseconds"; 118 } 119 return NULL; 120 } 121 122 123 124 125 static command_rec moddns_cmds[] = 126 { 127 { 128 "DnsLookups", /* directive name */ 129 moddns_cmd_AdnsLookups, /* config action routine */ 130 NULL, /* argument to include in call */ 131 OR_ALL, /* where available */ 132 TAKE1, /* arguments */ 133 "DNS lookup {on|off} switch; default is 'off'" 134 }, 135 { 136 "DnsTimeout_ms", /* directive name */ 137 moddns_cmd_AdnsTimeout_ms, /* config action routine */ 138 NULL, /* argument to include in call */ 139 OR_ALL, /* where available */ 140 TAKE1, /* arguments */ 141 "DNS lookup timeout; default is '" OPT_DNS_TIMEOUT_DEFAULT "' ms" 142 }, 143 { 144 "DnsResendTimer_ms", /* directive name */ 145 moddns_cmd_AdnsResendTimer_ms, /* config action routine */ 146 NULL, /* argument to include in call */ 147 OR_ALL, /* where available */ 148 TAKE1, /* arguments */ 149 "DNS UDP resend timeout; default is '" OPT_DNS_RESENDTIMER_DEFAULT "' ms" 150 }, 151 {NULL} 152 }; 153 154 155 156 typedef struct { 157 int rd_Lock; // DEBUG 158 int wr_Lock; // DEBUG 159 // 160 hash_t iphash; 161 hnode_t** htable; // free at module exit 162 struct Stats_t { 163 long hits; // cache performance stats 164 long lookups; // incremented for every external lookup attempt 165 long good; // hostname found 166 long timeouts; // hostname not found as a result of a timeout 167 long retries[5]; // number of UDP packets resends that eventually obtained a response 168 double timewait; // how many seconds have we spent waiting for a dns reply? 169 } stats; 170 // 171 int allocated; // shared memory size 172 int lock; // semaphore 173 int capacity; 174 int rq_pos; // write-pos in ring_queue 175 struct { 176 time_t age; 177 hnode_t* node; 178 } ring_queue[1]; // each time a new node is allocated, write its pointer to [rq_pos++] 179 } moddns_shared_t; 180 181 #define RETRY_HISTOGRAM_ARRAY_SIZE (sizeof(shared->stats.retries)/sizeof(shared->stats.retries[0])) 182 183 184 185 186 187 typedef struct { 188 int timeout_ms; // total time for each query 189 int retry_ms; // UDP resend timer 190 } Timeouts_t; 191 192 193 194 195 196 197 198 199 200 // module private globals 201 static moddns_shared_t *shared; 202 static AP_MM *mm; 203 204 205 // comparison function for searching ip hash 206 static int iphash_comp_f (const void *a, const void *b) 207 { 208 return strcmp ((const char*)a, (const char*)b); 209 } 210 static hnode_t* moddns_hnode_alloc_f (void *mm) 211 { 212 return ap_mm_malloc ((AP_MM*)mm, sizeof(hnode_t)); 213 } 214 static void moddns_hnode_free_f (hnode_t* node, void *mm) 215 { 216 ap_mm_free ((AP_MM*)mm, node); 217 } 218 219 220 221 222 223 224 225 //////////////////////////////////// 226 // MODULE INITIALIZATION FUNCTION // 227 //////////////////////////////////// 228 229 230 // cleanup 231 232 static void moddns_exit_hook (void* server) 233 { 234 hscan_t it; 235 hnode_t* node; 236 int i; 237 238 //fprintf(stderr, "moddns_exit_hook: pid=%d\n", getpid()); 239 240 SHARED_RW_LOCK(); 241 { 242 int ctotal = shared->stats.hits + shared->stats.lookups; 243 244 errno = 0; 245 ap_log_error (APLOG_MARK, APLOG_NOTICE, server, "mod_dns: Cache performance: %ld hits; %ld misses; %.02f%% hit ratio", 246 shared->stats.hits, 247 shared->stats.lookups, 248 (ctotal ? 100.0*shared->stats.hits/ctotal : 0.0) 249 ); 250 251 ap_log_error (APLOG_MARK, APLOG_NOTICE, server, "mod_dns: DNS performance: %ld good; %ld bad; %ld timeout; %.02f%% good; %.02fs avg", 252 shared->stats.good, 253 shared->stats.lookups - shared->stats.good, 254 shared->stats.timeouts, 255 (shared->stats.lookups ? 100.0*shared->stats.good/shared->stats.lookups : 0.0), 256 (shared->stats.lookups ? shared->stats.timewait/shared->stats.lookups : 0.0) 257 ); 258 259 260 // free all shared allocations.. 261 hash_scan_begin (&it, &shared->iphash); 262 for (i=0; (node = hash_scan_next(&it)); ++i) { 263 hash_scan_delete (&shared->iphash, node); 264 ap_mm_free (mm, hnode_get(node)); 265 ap_mm_free (mm, (char*)hnode_getkey(node)); 266 moddns_hnode_free_f (node, mm); 267 } 268 errno = 0; 269 ap_log_error (APLOG_MARK, APLOG_NOTICE, server, "mod_dns: Released %d/%d ip/host pairs from shared cache", 270 i, shared->capacity); 271 272 if (shared->htable) { 273 ap_mm_free (mm, shared->htable); 274 shared->htable = 0; 275 } 276 } 277 SHARED_UNLOCK(); 278 moddns_mutex_zap (shared->lock); // get rid of mutex 279 280 ap_mm_free (mm, shared); 281 shared = 0; 282 283 ap_mm_destroy (mm); 284 mm = 0; 285 286 } 287 288 289 290 // init 291 292 static void moddns_init_hook (server_rec *s, pool *p) 293 { 294 int avail; 295 296 // register cleanup 297 298 ap_register_cleanup (p, s, moddns_exit_hook, ap_null_cleanup); 299 300 //fprintf(stderr, "moddns_init_hook: pid=%d\n", getpid()); 301 302 303 304 305 mm = ap_mm_create(DEFAULT_DNS_SHARED_SIZE, DEFAULT_DNS_SHARED_FILE); 306 if (!mm) { 307 if (ap_mm_error()) { 308 fprintf (stderr, "mod_dns: Cannot allocate shared memory: %s\n", ap_mm_error()); 309 } else { 310 fprintf (stderr, "mod_dns: Apache must be compiled with EAPI and mm support\n"); 311 } 312 exit(1); 313 } 314 315 316 if (ap_mm_permission (mm, DNS_MM_FILE_MODE, ap_user_id, -1)) { 317 perror ("mod_dns: ap_mm_permission"); 318 exit(1); 319 } 320 321 322 avail = ap_mm_available (mm); 323 errno = 0; 324 ap_log_error (APLOG_MARK, APLOG_NOTICE, s, "mod_dns: Allocated %d bytes of shared cache memory", avail); 325 //ap_mm_calloc (mm, 1, 7790); 326 327 328 // yeah, we'll crash and burn if something fails, but at least an error is printed first.. 329 330 shared = ap_mm_calloc (mm, 1, sizeof(moddns_shared_t) + DEFAULT_DNS_HASH_SIZE * sizeof(shared->ring_queue[0])); 331 if (!shared) { 332 perror ("mod_dns: Failed to allocate shared fixed space"); 333 exit(1); 334 } 335 shared->capacity = DEFAULT_DNS_HASH_SIZE; 336 shared->lock = moddns_mutex_init (); 337 shared->allocated = avail; 338 SHARED_RW_LOCK(); 339 340 shared->htable = ap_mm_malloc (mm, sizeof(hnode_t*) * DEFAULT_DNS_HASH_SIZE); 341 if (!shared->htable) { 342 perror ("mod_dns: Failed to allocated shared hash table"); 343 exit(1); 344 } 345 if (!hash_init (&shared->iphash, DEFAULT_DNS_HASH_SIZE, iphash_comp_f, NULL, shared->htable, DEFAULT_DNS_HASH_SIZE)) { 346 perror ("mod_dns: Failed to initialize shared hash"); 347 exit(1); 348 } 349 errno = 0; 350 ap_log_error (APLOG_MARK, APLOG_NOTICE, s, "mod_dns: Initialized shared cache with %d ip/host slots", DEFAULT_DNS_HASH_SIZE); 351 352 // set node allocator to use shared memory 353 hash_set_allocator (&shared->iphash, moddns_hnode_alloc_f, moddns_hnode_free_f, mm); 354 355 356 SHARED_UNLOCK(); 357 } 358 359 360 /* 361 SHARED_RD_LOCK(); 362 SHARED_RW_LOCK(); 363 SHARED_UNLOCK(); 364 */ 365 366 367 368 369 370 ///////////////////////////////////////////////////////////////// 371 // success: return 0 372 // failure: return error code (EAGAIN for timeout) 373 374 375 static int dns_resolve_timed (const char *q, const char qtype[2], Timeouts_t* times) 376 { 377 struct taia start; 378 struct taia now; 379 struct taia diff = { {times->timeout_ms/1000}, 1000000*(times->timeout_ms%1000), 0 }; 380 struct taia retry = { {times->retry_ms/1000}, 1000000*(times->retry_ms%1000), 0 }; 381 struct taia deadline = { { 0 } }; 382 struct taia chkpnt = { { 0 } }; 383 char servers[64]; 384 iopause_fd x[1]; 385 int r = 0; 386 int transmits = 0; 387 388 taia_now (&start); 389 taia_add (&deadline, &start, &diff); // final deadline 390 391 if (dns_resolvconfip(servers) == -1) return -1; 392 if (dns_transmit_start(&dns_resolve_tx,servers,1,q,qtype,"\0\0\0\0") == -1) return -1; 393 394 while (r == 0) { 395 taia_now (&now); 396 chkpnt = retry; // retry timeout 397 taia_add (&chkpnt, &chkpnt, &now); 398 if (taia_less(&deadline,&chkpnt)) { 399 chkpnt = deadline; 400 } 401 if (taia_less(&chkpnt,&now)) { 402 r = EAGAIN; // timeout 403 break; 404 } 405 transmits++; 406 dns_transmit_io (&dns_resolve_tx, x, &chkpnt); 407 iopause (x, 1, &chkpnt, &now); 408 r = dns_transmit_get (&dns_resolve_tx, x, &now); 409 if (r == -1) break; 410 if (r == 1) { r = 0; break; } // success 411 } 412 taia_now (&now); 413 taia_sub (&diff, &now, &start); 414 shared->stats.timewait += diff.sec.x + diff.nano / 1000000000.0; 415 if (r != EAGAIN && transmits > 0) { 416 if (transmits > RETRY_HISTOGRAM_ARRAY_SIZE) transmits = RETRY_HISTOGRAM_ARRAY_SIZE; 417 shared->stats.retries[transmits-1] ++; 418 } 419 return r; 420 } 421 422 423 // unlike DJB's version, this routine fills in the allocated <q> pointer 424 // dns_name.c not used anymore 425 426 static int dns_name_packetp (stralloc *out,const char *buf,unsigned int len, char** qp) 427 { 428 unsigned int pos; 429 char header[12]; 430 uint16 numanswers; 431 uint16 datalen; 432 433 if (!stralloc_copys(out,"")) return -1; 434 435 pos = dns_packet_copy(buf,len,0,header,12); if (!pos) return -1; 436 uint16_unpack_big(header + 6,&numanswers); 437 pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1; 438 pos += 4; 439 440 while (numanswers--) { 441 pos = dns_packet_skipname(buf,len,pos); if (!pos) return -1; 442 pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) return -1; 443 uint16_unpack_big(header + 8,&datalen); 444 if (byte_equal(header,2,DNS_T_PTR)) 445 if (byte_equal(header + 2,2,DNS_C_IN)) { 446 if (!dns_packet_getname(buf,len,pos,qp)) return -1; 447 if (!dns_domain_todot_cat(out,*qp)) return -1; 448 return 0; 449 } 450 pos += datalen; 451 } 452 453 return 0; 454 } 455 456 457 458 //static char *q = 0; // i wish djb didn't like globals so much... lame.. 459 460 static int dns_name4_timed (stralloc *out, const char ip[4], Timeouts_t* times) 461 { 462 int err = -1; 463 char name[DNS_NAME4_DOMAIN]; 464 char *q = 0; 465 466 dns_name4_domain(name,ip); 467 err = dns_resolve_timed (name, DNS_T_PTR, times); 468 if (err) return err; 469 err = dns_name_packetp (out, dns_resolve_tx.packet, dns_resolve_tx.packetlen, &q); 470 if (err == -1) return err; 471 dns_transmit_free(&dns_resolve_tx); 472 dns_domain_free(&q); 473 return 0; 474 } 475 476 477 static int dns_ptr_lookup_djb (struct in_addr* addr_in, char *hostname_out, Timeouts_t* times) 478 { 479 int err = -1; 480 static stralloc out = { 0 }; // WARNING! Single threaded! keep using same buffer 481 union { 482 unsigned long int s_addr; 483 char b[4]; 484 } ip; 485 486 ip.s_addr = addr_in->s_addr; // network byte-order 487 488 err = dns_name4_timed (&out, ip.b, times); 489 490 memcpy (hostname_out, out.s, out.len); 491 hostname_out[out.len] = 0; 492 493 return err; 494 } 495 496 497 498 499 static int dns_ptr_lookup (moddns_dir_config *cf, struct in_addr* addr_in, char *hostname_out) 500 { 501 Timeouts_t times; 502 times.timeout_ms = cf->timeout_ms; 503 times.retry_ms = cf->resendtimer_ms; 504 return dns_ptr_lookup_djb (addr_in, hostname_out, ×); 505 } 506 507 508 509 510 511 512 static int moddns_lookup_hook (request_rec *r) 513 { 514 moddns_dir_config *cf = MODDNS_DIR_CONFIG(r); 515 conn_rec *cn = r->connection; 516 int err; 517 hnode_t *cnode; 518 char* shm_addr = 0; 519 char* shm_host = 0; 520 int tries; 521 char hostname[256] = ""; 522 523 if (cn->remote_host != NULL) { 524 return OK; 525 } 526 if (cf->lookup_enabled != OPT_DNS_LOOKUP_ON) { 527 return OK; // lookup disabled.. 528 } 529 530 SHARED_RD_LOCK(); 531 CHECK_RLOCK_IN 532 //fprintf(stderr, "AP_MM_LOCK_RD locked\n"); 533 { 534 cnode = hash_lookup (&shared->iphash, cn->remote_ip); 535 if (cnode) { 536 cn->remote_host = ap_pstrdup (cn->pool, (char*)hnode_get(cnode)); 537 shared->stats.hits++; 538 CHECK_RLOCK_OUT 539 SHARED_UNLOCK(); 540 errno = 0; 541 // ap_log_error (APLOG_MARK, APLOG_DEBUG, r->server, "mod_dns: Cached [%s] -> [%s]", cn->remote_ip, cn->remote_host); 542 return OK; 543 } 544 } 545 CHECK_RLOCK_OUT 546 SHARED_UNLOCK(); 547 548 shared->stats.lookups++; 549 550 // perform IP -> HOST lookup 551 552 err = dns_ptr_lookup (cf, &cn->remote_addr.sin_addr, hostname); 553 554 errno = 0; // clear so ap_log_error doesn't print junk 555 556 if (err == EAGAIN) { 557 // timeout 558 shared->stats.timeouts++; 559 ap_log_error (APLOG_MARK, APLOG_INFO, r->server, "mod_dns: Failed to resolve [%s]: %d ms timeout", cn->remote_ip, cf->timeout_ms); 560 } 561 else if (err) { 562 ap_log_error (APLOG_MARK, APLOG_NOTICE, r->server, "mod_dns: Failed to resolve [%s]: %s", cn->remote_ip, strerror(err)); 563 } 564 else { 565 if (hostname[0]) { 566 shared->stats.good++; 567 cn->remote_host = ap_pstrdup (cn->pool, hostname); 568 ap_str_tolower (cn->remote_host); 569 ap_log_error (APLOG_MARK, APLOG_DEBUG, r->server, "mod_dns: Resolved [%s] -> [%s]", cn->remote_ip, cn->remote_host); 570 } else { 571 // ap_log_error (APLOG_MARK, APLOG_INFO, r->server, "mod_dns: Failed to resolve [%s]: %s", cn->remote_ip, moddns_strerror(answer->status)); 572 } 573 } 574 575 // if it failed, store "" in remote_host so we dont try it again.. 576 if (!cn->remote_host) cn->remote_host = ""; 577 578 SHARED_RW_LOCK(); // upgrade readlock to RW (well, for now we only use one type of lock..) 579 CHECK_WLOCK_IN 580 { 581 //fprintf(stderr, "AP_MM_LOCK_RW locked\n"); 582 cnode = hash_lookup (&shared->iphash, cn->remote_ip); 583 if (cnode) { 584 // oops, someone else got here first.. 585 char* cachedhost = hnode_get(cnode); 586 if (cachedhost[0] || !cn->remote_host[0]) { 587 // ..and they cached a successful lookup, OR we got a failure too... 588 CHECK_WLOCK_OUT 589 SHARED_UNLOCK(); 590 return OK; 591 } 592 hash_delete (&shared->iphash, cnode); // unlink from list (since we reinsert below) 593 ap_log_error (APLOG_MARK, APLOG_INFO, r->server, "mod_dns: Upgrading negative [%s] record with [%s]", 594 cn->remote_ip, cn->remote_host); 595 ap_mm_free (mm, cachedhost); 596 shm_addr = (char*)hnode_getkey(cnode); // reuse it below since it's the same IP! 597 } 598 599 600 // retrying not fully implemented: it currently doesn't try 601 // to free up any memory even if allocation fails. see bottom of oop.. 602 for (tries=1; tries--; ) { 603 604 if (hash_isfull(&shared->iphash)) { 605 // buffer is full. THIS CANNOT HAPPEN IF WE JUST CALLED hash_delete ABOVE 606 hnode_t *trash = shared->ring_queue[shared->rq_pos].node; // if this is null it means the code is fucked... 607 if (!trash) { 608 ap_log_error (APLOG_MARK, APLOG_EMERG, r->server, "mod_dns: Shared memory corruption: node=%d", shared->rq_pos); 609 CHECK_WLOCK_OUT 610 SHARED_UNLOCK(); // try not to crash the server... 611 } 612 ap_log_error (APLOG_MARK, APLOG_DEBUG, r->server, "mod_dns: Recycled [%s] record: node=%d", 613 (char*)hnode_getkey(trash), shared->rq_pos); 614 hash_delete (&shared->iphash, trash); 615 ap_mm_free (mm, (void*)hnode_getkey(trash)); // free up old key 616 ap_mm_free (mm, hnode_get(trash)); // free up old value 617 moddns_hnode_free_f (trash, mm); 618 shared->ring_queue[shared->rq_pos].node = NULL; 619 } 620 621 if (!shm_addr) shm_addr = ap_mm_strdup (mm, cn->remote_ip); 622 623 if (!shm_host) shm_host = ap_mm_strdup (mm, cn->remote_host); 624 625 if (!cnode) { 626 cnode = moddns_hnode_alloc_f (mm); 627 if (cnode) hnode_init (cnode, 0); 628 } 629 630 if (shm_addr && shm_host && cnode) { 631 hnode_put (cnode, shm_host); 632 shared->ring_queue[shared->rq_pos].age = time(0); 633 shared->ring_queue[shared->rq_pos].node = cnode; 634 shared->rq_pos = (shared->rq_pos + 1) % shared->capacity; 635 hash_insert (&shared->iphash, cnode, shm_addr); 636 break; 637 } 638 // allocation failed, free up some space.. 639 errno = 0; 640 ap_log_error (APLOG_MARK, APLOG_ERR, r->server, "mod_dns: Shared memory full: %ld/%d nodes cached", 641 hash_count(&shared->iphash), shared->capacity); 642 // try to free something here *TODO* 643 } 644 if (tries < 0) { 645 ap_log_error (APLOG_MARK, APLOG_ERR, r->server, "mod_dns: Failed allocate shared cache memory: %s", ap_mm_error()); 646 if (shm_addr) ap_mm_free (mm, shm_addr); 647 if (shm_host) ap_mm_free (mm, shm_host); 648 if (cnode) moddns_hnode_free_f (cnode, mm); 649 } 650 } 651 CHECK_WLOCK_OUT 652 SHARED_UNLOCK(); 653 654 return OK; 655 } 656 657 658 659 660 // display statistcs handler 661 662 static int moddns_stats_handler (request_rec *r) 663 { 664 struct Stats_t *s = &shared->stats; 665 666 r->content_type = "text/html"; 667 ap_send_http_header (r); 668 669 // no locking necessary for reading data.. (any inconsistencies are 670 { 671 int usage; 672 time_t oldest; 673 struct { int hh; int mm; int ss; } age = { 0 }; 674 long reqtotal = s->hits + s->lookups; 675 long baddns = s->lookups - s->good; 676 long responses = s->lookups - s->timeouts; 677 int rq_pos = shared->rq_pos; 678 679 if (shared->ring_queue[rq_pos].node) { usage = shared->capacity; } 680 else { usage = rq_pos; } 681 682 oldest = shared->ring_queue[rq_pos].age; 683 if (!oldest && rq_pos>0) oldest = shared->ring_queue[0].age; 684 if (oldest) age.ss = time(0) - oldest; 685 age.mm = age.ss/60; age.ss %= 60; 686 age.hh = age.mm/60; age.mm %= 60; 687 688 ap_rprintf (r, 689 "<head>\n" 690 " <title>mod_dns statistics [%s]</title>\n" 691 "</head>\n" 692 "<body>\n" 693 "<table height=\"100%%\" width=\"100%%\" cellspacing=0 cellpadding=0 border=0><tr><td align=center valign=middle>\n" 694 "<table border=0 cellpadding=4 cellspacing=2 bgcolor=white>\n" 695 " <tr align=right> <td rowspan=4 align=left valign=top>Cache</td>\n" 696 " <td align=left>Usage/Size</td> <td>%d/%d</td> <td>%.02f%%</td> </tr>\n" 697 " <tr align=right> <td align=left>Age</td> <td>%02d:%02d:%02d</td> <td></td> </tr>\n" 698 " <tr align=right> <td align=left>Hits</td> <td>%ld</td> <td>%.02f%%</td> </tr>\n" 699 " <tr align=right> <td align=left>Misses</td> <td>%ld</td> <td>%.02f%%</td> </tr>\n" 700 " <tr align=right> <td rowspan=2 align=left valign=top>Shared memory</td>\n" 701 " <td align=left>Total</td> <td>%d bytes</td> <td></td> </tr>\n" 702 " <tr align=right> <td align=left>Free</td> <td>%d bytes</td> <td>%.02f%%</td> </tr>\n" 703 " <tr align=right> <td rowspan=5 align=left valign=top>DNS Resolver</td>\n" 704 " <td align=left>Total lookups</td> <td>%ld</td> <td></td> </tr>\n" 705 " <tr align=right> <td align=left>Good lookups</td> <td>%ld</td> <td>%.02f%%</td> </tr>\n" 706 " <tr align=right> <td align=left>Bad lookups</td> <td>%ld</td> <td>%.02f%%</td> </tr>\n" 707 " <tr align=right> <td align=left>Lookup timeouts</td> <td>%ld</td> <td>%.02f%%</td> </tr>\n" 708 " <tr align=right> <td align=left>Avg lookup time</td> <td>%.02f ms</td> <td></td> </tr>\n" 709 " <tr align=right> <td rowspan=5 align=left valign=top>Server responding</td>\n" 710 " <td align=left>Without retries</td> <td>%ld</td> <td>%.02f%%</td> </tr>\n" 711 " <tr align=right> <td align=left>After 1 retry</td> <td>%ld</td> <td>%.02f%%</td> </tr>\n" 712 " <tr align=right> <td align=left>After 2 retries</td> <td>%ld</td> <td>%.02f%%</td> </tr>\n" 713 " <tr align=right> <td align=left>After 3 retries</td> <td>%ld</td> <td>%.02f%%</td> </tr>\n" 714 " <tr align=right> <td align=left>After 4+ retries</td> <td>%ld</td> <td>%.02f%%</td> </tr>\n" 715 "</table>\n" 716 " <tr align=center valign=bottom> <td colspan=4 height=1><font size=\"-2\">mod_dns v%s - Copyright © 2001 Fredrik Sjoholm <fredrik@sjoholm.com> - <a href=\"http://oss.ezic.com/\">http://oss.ezic.com/</a></font></td> </tr>\n" 717 "</td></tr></table>\n" 718 "</body>\n", 719 ap_get_server_name (r), 720 usage, shared->capacity, 100.0*usage/shared->capacity, 721 age.hh, age.mm, age.ss, 722 s->hits, reqtotal ? 100.0*s->hits/reqtotal : 0.0, 723 s->lookups, reqtotal ? 100.0*s->lookups/reqtotal : 0.0, 724 shared->allocated, 725 ap_mm_available(mm), 100.0*ap_mm_available(mm)/shared->allocated, 726 s->lookups, 727 s->good, s->lookups ? 100.0*s->good/s->lookups : 0.0, 728 baddns, s->lookups ? 100.0*baddns/s->lookups : 0.0, 729 s->timeouts, s->lookups ? 100.0*s->timeouts/s->lookups : 0.0, 730 s->lookups ? 1000.0*s->timewait/s->lookups : 0.0, 731 s->retries[0], responses ? 100.0*s->retries[0]/responses : 0.0, 732 s->retries[1], responses ? 100.0*s->retries[1]/responses : 0.0, 733 s->retries[2], responses ? 100.0*s->retries[2]/responses : 0.0, 734 s->retries[3], responses ? 100.0*s->retries[3]/responses : 0.0, 735 s->retries[4], responses ? 100.0*s->retries[4]/responses : 0.0, 736 MOD_DNS_VERSION 737 ); 738 } 739 740 return OK; 741 } 742 743 744 745 static handler_rec moddns_handlers[] = 746 { 747 { "dns-stats-handler", moddns_stats_handler }, 748 {NULL} 749 }; 750 751 752 753 /* Tell Apache what phases of the transaction we handle */ 754 module MODULE_VAR_EXPORT dns_module = 755 { 756 STANDARD_MODULE_STUFF, 757 moddns_init_hook, /* module initializer */ 758 moddns_create_dir_config, /* per-directory config creator */ 759 moddns_merge_dir_config, /* dir config merger */ 760 NULL, /* server config creator */ 761 NULL, /* server config merger */ 762 moddns_cmds, /* command table */ 763 moddns_handlers, /* [7] content handlers */ 764 NULL, /* [2] URI-to-filename translation */ 765 NULL, /* [5] check/validate user_id */ 766 NULL, /* [6] check user_id is valid *here* */ 767 NULL, /* [4] check access by host address */ 768 NULL, /* [7] MIME type checker/setter */ 769 NULL, /* [8] fixups */ 770 NULL, /* [9] logger */ 771 moddns_lookup_hook, /* [3] header parser */ 772 NULL, /* process initialization */ 773 NULL, /* process exit/cleanup */ 774 NULL /* [1] post read_request handling */ 775 }; 776 777 778 779 780 781 782