"Fossies" - the Fresh Open Source Software Archive

Member "mod_dns/mod_dns.c" (24 Sep 2001, 23734 Bytes) of package /linux/www/apache_httpd_modules/old/mod_dns-1.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

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, &times);
  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 &copy; 2001 Fredrik Sjoholm &lt;fredrik@sjoholm.com&gt; - <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