From: aowadmin@mallory.draper.net Subject: DNS Caching Code Hi, We at AgeofWar.ORG are now seeing several hundred unique IP addresses each day. Blocked gethostbyaddr() delays had become a significant problem for us. While we feel that our own name servers are quite fast the same is not true for other name servers out in the network. After several iterations of "in mud caching" schemes involving linked lists and worse, I now feel that we have a stable solution with adequate performance. As good netizens, and in keeping with the unencumbered spirit of Linux and open software (hint hint mud authors), I am releasing the caching code into the net without any type of licensing requirement whatsoever. So... use it, modify it, curse it (don't blame me if it breaks), or throw it out as you like. Regards, Reed, **************************** aowdns.h ***************************** char *aowdns(struct in_addr sin_addr); void dnspurge(void); **************************** aowdns.c ***************************** /* ********************************************************************** * aowdns - Maintain IP/HostName Cache * * This module is invoked from CircleMUD comm.c code to translate * an IP address into its corresponding HostName. It is known to * run on RedHat Linux 5.0 systems. * * In order to avoid the very long delay times often associated with * slow DNS servers and blocked gethostbyaddr(), a database cache * is maintained. If the IP address is found in the cache then * gethostbyaddr() delays are circumvented. Otherwise the HostName is * retrieved with gethostbyaddr() and saved in the cache for future * use. A pointer to a character array containing a hostname or * equivalent IP address is always returned * * Cached entries are held for the period defined by KEEP_DAYS. * About once an hour the cache is scanned and old entries are * purged. * * As CircleMUD and NDBM both have db.h header files a conflict exists * and this module must be compiled seperately. The following command * seems to work well: gcc -c -o aowdns.o -g aowdns.c * * When linking with circle, remember to add -lndbm and aowdns.o * to the linker command string in the Makefile. * * Sample invocation from comm.c: * * strncpy(newd->host, aowdns(peer.sin_addr), HOST_LENGTH) * * This code may be freely distributed and modified as you like. * No license whatsoever is required. However, I would appreciate it * if you kept this notice and my name intact. * * Author: Reed H. Petty, Server Admin and Sometimes Coder * AgeofWar.ORG Port 4000 * rhp@ageofwar.org * ********************************************************************** */ #include #include #include #include #include #include #include #include #include #include #include "aowdns.h" #define KEEP_DAYS 10 struct { time_t created; char name[128]; } dnsrec; static time_t last_cleanup=0; DBM *dnsdb = 0; datum key_datum; datum data_datum; /*************************************************************/ char *aowdns (struct in_addr sin_addr) { struct hostent *from; struct in_addr ip = sin_addr; int addr; if (!(dnsdb = dbm_open("dnscache", O_RDWR, 0600))) { unlink("dnscache.db"); if (!(dnsdb = dbm_open("dnscache", O_RDWR | O_CREAT, 0600))) { perror("dns database open failed"); exit(1); } } /* Purge old entries from the database if its time */ if ((last_cleanup + 600) > time(NULL)) dnspurge(); /* Retrieve cached entry, if it exists */ key_datum.dptr = (void *)&ip; key_datum.dsize = sizeof(ip); data_datum = dbm_fetch(dnsdb, key_datum); if (data_datum.dptr) { memcpy(&dnsrec,data_datum.dptr,data_datum.dsize); dbm_close(dnsdb); return(dnsrec.name); } /* This ip address is not cached, look it up with gethostbyaddr() */ if (from = gethostbyaddr((char *)&ip, sizeof(ip), AF_INET)) { strncpy(dnsrec.name,from->h_name,sizeof(dnsrec.name)); dnsrec.name[ sizeof(dnsrec.name)-1 ] = 0; } else { addr = ntohl(ip.s_addr); sprintf(dnsrec.name, "%03u.%03u.%03u.%03u", (int) ((addr & 0xFF000000) >> 24), (int) ((addr & 0x00FF0000) >> 16), (int) ((addr & 0x0000FF00) >> 8), (int) ((addr & 0x000000FF))); } /* Insert a new cache entry */ dnsrec.created = time(NULL); data_datum.dptr = (void *)&dnsrec; data_datum.dsize = sizeof(dnsrec.created) + strlen(dnsrec.name) + 1; key_datum.dptr = (void *)&ip; key_datum.dsize = sizeof(ip); dbm_store(dnsdb, key_datum, data_datum, DBM_REPLACE); dbm_close(dnsdb); return(dnsrec.name); } /**********************************************************************/ void dnspurge(void) { time_t now = time(NULL); last_cleanup = now; for (key_datum=dbm_firstkey(dnsdb); key_datum.dptr; key_datum=dbm_nextkey(dnsdb)) { data_datum = dbm_fetch(dnsdb, key_datum); memcpy(&dnsrec,data_datum.dptr,data_datum.dsize); if ((dnsrec.created + KEEP_DAYS) < now) dbm_delete(dnsdb, key_datum); } } /* ******************************************************************* * dnslist - List the contents of the dnscache * * This is a companion module to aowdns.c... * * Compile: gcc -o dnslist dnslist.c -g -lndbm * * Sample invocation: ./dnslist dnscache (leave the .db off) * * This code may be freely distributed and modified as you like. * No license whatsover is required. However, I would appreciate it * if you kept this notice and my name intact. * * Author: Reed H. Petty, Server Admin and Sometimes Coder * AgeofWar.ORG Port 4000 * rhp@ageofwar.org ******************************************************************* */ **************************** dnslist.c ***************************** #include #include #include #include #include #include #include int main(int argc, char **argv) { struct dns_file { time_t created; char name[80]; } dnsrec; int dnskey = 0; datum key_datum; datum data_datum; DBM *dbm_ptr; if (argc < 2) { printf("Usage: dnslist filename (omit the .db suffix)\n"); exit(1); } if (!(dbm_ptr = dbm_open(argv[1], O_RDWR, 0600))) { perror("database open failed"); exit(1); } for (key_datum=dbm_firstkey(dbm_ptr); key_datum.dptr; key_datum=dbm_nextkey(dbm_ptr)) { data_datum = dbm_fetch(dbm_ptr, key_datum); memcpy(&dnsrec,data_datum.dptr,data_datum.dsize); memcpy(&dnskey,key_datum.dptr,key_datum.dsize); fprintf(stdout,"%03u.%03u.%03u.%03u %d %s\n", (dnskey & 0x000000FF), (dnskey & 0x0000FF00) >> 8, (dnskey & 0x00FF0000) >> 16, (dnskey & 0xFF000000) >> 24, dnsrec.created, dnsrec.name); } dbm_close(dbm_ptr); }