/* * logresolve 1.1 * * Tom Rathborne - tomr@uunet.ca - http://www.uunet.ca/~tomr/ * UUNET Canada, April 16, 1995 * * Rewritten by David Robinson. (drtr@ast.cam.ac.uk) * IPv6 extension by Nakata H. (info@nakata-jp.org) * * Usage: logresolve [-s filename] [-c] < access_log > new_log * * Arguments: * -s filename name of a file to record statistics * -c check the DNS for a matching A record for the host. * * Notes: * * To generate meaningful statistics from an HTTPD log file, it's good * to have the domain name of each machine that accessed your site, but * doing this on the fly can slow HTTPD down. * * Compiling NCSA HTTPD with the -DMINIMAL_DNS flag turns IP#->hostname * resolution off. Before running your stats program, just run your log * file through this program (logresolve) and all of your IP numbers will * be resolved into hostnames (where possible). * * logresolve takes an HTTPD access log (in the COMMON log file format, * or any other format that has the IP number/domain name as the first * field for that matter), and outputs the same file with all of the * domain names looked up. Where no domain name can be found, the IP * number is left in. * * To minimize impact on your nameserver, logresolve has its very own * internal hash-table cache. This means that each IP number will only * be looked up the first time it is found in the log file. * * The -c option causes logresolve to apply the same check as httpd * compiled with -DMAXIMUM_DNS; after finding the hostname from the IP * address, it looks up the IP addresses for the hostname and checks * that one of these matches the original address. */ #include "apr_lib.h" #if APR_HAVE_STDIO_H #include #endif #if APR_HAVE_STDLIB_H #include #endif #if APR_HAVE_CTYPE_H #include #endif #if APR_HAVE_NETDB_H #include #endif #if APR_HAVE_NETINET_IN_H #include #endif #if APR_HAVE_STRING_H #include #endif #if APR_HAVE_SYS_SOCKET_H #include #endif #if APR_HAVE_ARPA_INET_H #include #endif static void cgethost(struct in_addr ipnum, char *string, int check); static int get_line(char *s, int n); static void stats(FILE *output); #ifdef BEOS #define NO_ADDRESS NO_DATA #endif /* maximum line length */ #define MAXLINE 1024 /* maximum length of a domain name */ #ifndef MAXDNAME #define MAXDNAME 256 #endif /* number of buckets in cache hash apr_table_t */ #define BUCKETS 256 #if !APR_HAVE_STRDUP char *strdup (const char *str) { char *dup; if (!(dup = (char *) malloc(strlen(str) + 1))) return NULL; dup = strcpy(dup, str); return dup; } #endif /* * struct nsrec - record of nameservice for cache linked list * * ipnum - IP number hostname - hostname noname - nonzero if IP number has no * hostname, i.e. hostname=IP number */ struct nsrec { struct in_addr ipnum; char *hostname; int noname; struct nsrec *next; } *nscache[BUCKETS]; #ifdef AF_INET6 struct ns6rec { struct in6_addr ipnum6; char *hostname; int noname; struct ns6rec *next; } *ns6cache[BUCKETS]; #endif /* AF_INET6 */ /* * statistics - obvious */ #ifndef h_errno #ifdef __CYGWIN__ extern __declspec(dllimport) int h_errno; #else extern int h_errno; /* some machines don't have this in their headers */ #endif #endif /* largest value for h_errno */ #define MAX_ERR (NO_ADDRESS) #define UNKNOWN_ERR (MAX_ERR+1) #define NO_REVERSE (MAX_ERR+2) static int cachehits = 0; static int cachesize = 0; static int entries = 0; static int resolves = 0; static int withname = 0; static int errors[MAX_ERR + 3]; /* * cgethost - gets hostname by IP address, caching, and adding unresolvable * IP numbers with their IP number as hostname, setting noname flag */ static void cgethost (struct in_addr ipnum, char *string, int check) { struct nsrec **current, *new; struct hostent *hostdata; char *name; current = &nscache[((ipnum.s_addr + (ipnum.s_addr >> 8) + (ipnum.s_addr >> 16) + (ipnum.s_addr >> 24)) % BUCKETS)]; while (*current != NULL && ipnum.s_addr != (*current)->ipnum.s_addr) current = &(*current)->next; if (*current == NULL) { cachesize++; new = (struct nsrec *) malloc(sizeof(struct nsrec)); if (new == NULL) { perror("malloc"); fprintf(stderr, "Insufficient memory\n"); exit(1); } *current = new; new->next = NULL; new->ipnum = ipnum; hostdata = gethostbyaddr((const char *) &ipnum, sizeof(struct in_addr), AF_INET); if (hostdata == NULL) { if (h_errno > MAX_ERR) errors[UNKNOWN_ERR]++; else errors[h_errno]++; new->noname = h_errno; name = strdup(inet_ntoa(ipnum)); } else { new->noname = 0; name = strdup(hostdata->h_name); if (check) { if (name == NULL) { perror("strdup"); fprintf(stderr, "Insufficient memory\n"); exit(1); } hostdata = gethostbyname(name); if (hostdata != NULL) { char **hptr; for (hptr = hostdata->h_addr_list; *hptr != NULL; hptr++) if (((struct in_addr *) (*hptr))->s_addr == ipnum.s_addr) break; if (*hptr == NULL) hostdata = NULL; } if (hostdata == NULL) { fprintf(stderr, "Bad host: %s != %s\n", name, inet_ntoa(ipnum)); new->noname = NO_REVERSE; free(name); name = strdup(inet_ntoa(ipnum)); errors[NO_REVERSE]++; } } } new->hostname = name; if (new->hostname == NULL) { perror("strdup"); fprintf(stderr, "Insufficient memory\n"); exit(1); } } else cachehits++; /* size of string == MAXDNAME +1 */ strncpy(string, (*current)->hostname, MAXDNAME); string[MAXDNAME] = '\0'; } #ifdef AF_INET6 static int cache6size = 0; static void cgethost6 (struct in6_addr ipnum6, char *string, int check) { struct ns6rec **current, *new; struct hostent *hostdata; char *name, tmp[INET6_ADDRSTRLEN + 1]; current = &ns6cache[((ipnum6.s6_addr32[0] + ipnum6.s6_addr32[1] + ipnum6.s6_addr32[2] + ipnum6.s6_addr32[3]) % BUCKETS)]; while (*current != NULL && memcmp(&(ipnum6.s6_addr), &((*current)->ipnum6.s6_addr), sizeof(ipnum6.s6_addr)) != 0 ) current = &(*current)->next; if (*current == NULL) { cache6size++; new = (struct ns6rec *) malloc(sizeof(struct ns6rec)); if (new == NULL) { perror("malloc"); fprintf(stderr, "Insufficient memory\n"); exit(1); } *current = new; new->next = NULL; new->ipnum6 = ipnum6; hostdata = gethostbyaddr((const char *) &ipnum6, sizeof(struct in6_addr), AF_INET6); if (hostdata == NULL) { if (h_errno > MAX_ERR) errors[UNKNOWN_ERR]++; else errors[h_errno]++; new->noname = h_errno; if ( (name = malloc(INET6_ADDRSTRLEN + 1)) != NULL) { inet_ntop(AF_INET6, &ipnum6, name, INET6_ADDRSTRLEN); } } else { new->noname = 0; name = strdup(hostdata->h_name); if (check) { if (name == NULL) { perror("strdup"); fprintf(stderr, "Insufficient memory\n"); exit(1); } hostdata = gethostbyname2(name, AF_INET6); if (hostdata != NULL) { char **hptr; for (hptr = hostdata->h_addr_list; *hptr != NULL; hptr++) if (((struct in6_addr *) (*hptr))->s6_addr == ipnum6.s6_addr) break; if (*hptr == NULL) hostdata = NULL; } if (hostdata == NULL) { inet_ntop(AF_INET6, &ipnum6, tmp, INET6_ADDRSTRLEN); fprintf(stderr, "Bad host: %s != %s\n", name, tmp); new->noname = NO_REVERSE; free(name); name = strdup(tmp); errors[NO_REVERSE]++; } } } new->hostname = name; if (new->hostname == NULL) { perror("strdup"); fprintf(stderr, "Insufficient memory\n"); exit(1); } } else cachehits++; /* size of string == MAXDNAME +1 */ strncpy(string, (*current)->hostname, MAXDNAME); string[MAXDNAME] = '\0'; if ( strlen(string) < MAXDNAME - 6 ) { strcat(string, "(IPv6)"); } } #endif /* AF_INET6 */ /* * prints various statistics to output */ static void stats (FILE *output) { int i; char *ipstring; struct nsrec *current; char *errstring[MAX_ERR + 3]; for (i = 0; i < MAX_ERR + 3; i++) errstring[i] = "Unknown error"; errstring[HOST_NOT_FOUND] = "Host not found"; errstring[TRY_AGAIN] = "Try again"; errstring[NO_RECOVERY] = "Non recoverable error"; errstring[NO_DATA] = "No data record"; errstring[NO_ADDRESS] = "No address"; errstring[NO_REVERSE] = "No reverse entry"; fprintf(output, "logresolve Statistics:\n"); fprintf(output, "Entries: %d\n", entries); fprintf(output, " With name : %d\n", withname); fprintf(output, " Resolves : %d\n", resolves); if (errors[HOST_NOT_FOUND]) fprintf(output, " - Not found : %d\n", errors[HOST_NOT_FOUND]); if (errors[TRY_AGAIN]) fprintf(output, " - Try again : %d\n", errors[TRY_AGAIN]); if (errors[NO_DATA]) fprintf(output, " - No data : %d\n", errors[NO_DATA]); if (errors[NO_ADDRESS]) fprintf(output, " - No address: %d\n", errors[NO_ADDRESS]); if (errors[NO_REVERSE]) fprintf(output, " - No reverse: %d\n", errors[NO_REVERSE]); fprintf(output, "Cache hits : %d\n", cachehits); fprintf(output, "Cache size : %d\n", cachesize); #ifdef AF_INET6 fprintf(output, "IPv6 Cache size : %d\n", cache6size); #endif /* AF_INET6 */ fprintf(output, "Cache buckets : IP number * hostname\n"); for (i = 0; i < BUCKETS; i++) for (current = nscache[i]; current != NULL; current = current->next) { ipstring = inet_ntoa(current->ipnum); if (current->noname == 0) fprintf(output, " %3d %15s - %s\n", i, ipstring, current->hostname); else { if (current->noname > MAX_ERR + 2) fprintf(output, " %3d %15s : Unknown error\n", i, ipstring); else fprintf(output, " %3d %15s : %s\n", i, ipstring, errstring[current->noname]); } } } /* * gets a line from stdin */ static int get_line (char *s, int n) { char *cp; if (!fgets(s, n, stdin)) return (0); cp = strchr(s, '\n'); if (cp) *cp = '\0'; return (1); } int main (int argc, char *argv[]) { struct in_addr ipnum; struct in6_addr ipnum6; char *bar, hoststring[MAXDNAME + 1], line[MAXLINE], *statfile; int i, check; #ifdef WIN32 /* If we apr'ify this code, apr_pool_create/apr_pool_destroy * should perform the WSAStartup/WSACleanup for us. */ WSADATA wsaData; WSAStartup(0x101, &wsaData); #endif check = 0; statfile = NULL; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-c") == 0) check = 1; else if (strcmp(argv[i], "-s") == 0) { if (i == argc - 1) { fprintf(stderr, "logresolve: missing filename to -s\n"); exit(1); } i++; statfile = argv[i]; } else { fprintf(stderr, "Usage: logresolve [-s statfile] [-c] < input > output\n"); exit(0); } } for (i = 0; i < BUCKETS; i++) nscache[i] = NULL; for (i = 0; i < MAX_ERR + 2; i++) errors[i] = 0; while (get_line(line, MAXLINE)) { if (line[0] == '\0') continue; entries++; if (!apr_isdigit(line[0])) { /* short cut */ puts(line); withname++; continue; } bar = strchr(line, ' '); if (bar != NULL) *bar = '\0'; ipnum.s_addr = inet_addr(line); if (ipnum.s_addr == 0xffffffffu) { #ifdef AF_INET6 if ( inet_pton(AF_INET6, line, &ipnum6) <= 0 ) { #endif /* AF_INET6 */ if (bar != NULL) *bar = ' '; puts(line); withname++; continue; #ifdef AF_INET6 } #endif /* AF_INET6 */ } resolves++; #ifdef AF_INET6 if (ipnum.s_addr == 0xffffffffu) { cgethost6(ipnum6, hoststring, check); } else { #endif /* AF_INET6 */ cgethost(ipnum, hoststring, check); #ifdef AF_INET6 } #endif /* AF_INET6 */ if (bar != NULL) printf("%s %s\n", hoststring, bar + 1); else puts(hoststring); } #ifdef WIN32 WSACleanup(); #endif if (statfile != NULL) { FILE *fp; fp = fopen(statfile, "w"); if (fp == NULL) { fprintf(stderr, "logresolve: could not open statistics file '%s'\n" ,statfile); exit(1); } stats(fp); fclose(fp); } return (0); }