]> granicus.if.org Git - apache/blob - support/logresolve.c
Correct a spurious access to whatever memory is at location "1" in the
[apache] / support / logresolve.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * logresolve 2.0
19  *
20  * Tom Rathborne - tomr uunet.ca - http://www.uunet.ca/~tomr/
21  * UUNET Canada, April 16, 1995
22  *
23  * Rewritten by David Robinson. (drtr ast.cam.ac.uk)
24  * Rewritten again, and ported to APR by Colm MacCarthaigh
25  *
26  * Usage: logresolve [-s filename] [-c] < access_log > new_log
27  *
28  * Arguments:
29  *    -s filename     name of a file to record statistics
30  *    -c              check the DNS for a matching A record for the host.
31  *
32  * Notes:             (For historical interest)
33  *
34  * To generate meaningful statistics from an HTTPD log file, it's good
35  * to have the domain name of each machine that accessed your site, but
36  * doing this on the fly can slow HTTPD down.
37  *
38  * Compiling NCSA HTTPD with the -DMINIMAL_DNS flag turns IP#->hostname
39  * resolution off. Before running your stats program, just run your log
40  * file through this program (logresolve) and all of your IP numbers will
41  * be resolved into hostnames (where possible).
42  *
43  * logresolve takes an HTTPD access log (in the COMMON log file format,
44  * or any other format that has the IP number/domain name as the first
45  * field for that matter), and outputs the same file with all of the
46  * domain names looked up. Where no domain name can be found, the IP
47  * number is left in.
48  *
49  * To minimize impact on your nameserver, logresolve has its very own
50  * internal hash-table cache. This means that each IP number will only
51  * be looked up the first time it is found in the log file.
52  *
53  * The -c option causes logresolve to apply the same check as httpd
54  * compiled with -DMAXIMUM_DNS; after finding the hostname from the IP
55  * address, it looks up the IP addresses for the hostname and checks
56  * that one of these matches the original address.
57  */
58
59 #include "apr.h"
60 #include "apr_lib.h"
61 #include "apr_hash.h"
62 #include "apr_getopt.h"
63 #include "apr_strings.h"
64 #include "apr_file_io.h"
65 #include "apr_network_io.h"
66
67 #if APR_HAVE_STDLIB_H
68 #include <stdlib.h>
69 #endif
70
71 static apr_file_t *errfile;
72 static const char *shortname = "logresolve";
73 static apr_hash_t *cache;
74
75 /* Statistics */
76 static int cachehits = 0;
77 static int cachesize = 0;
78 static int entries = 0;
79 static int resolves = 0;
80 static int withname = 0;
81 static int doublefailed = 0;
82 static int noreverse = 0;
83
84 /*
85  * prints various statistics to output
86  */
87 #define NL APR_EOL_STR
88 static void print_statistics (apr_file_t *output)
89 {
90     apr_file_printf(output, "logresolve Statistics:" NL);
91     apr_file_printf(output, "Entries: %d" NL, entries);
92     apr_file_printf(output, "    With name   : %d" NL, withname);
93     apr_file_printf(output, "    Resolves    : %d" NL, resolves);
94
95     if (noreverse) {
96         apr_file_printf(output, "    - No reverse : %d" NL,
97                         noreverse);
98     }
99
100     if (doublefailed) {
101         apr_file_printf(output, "    - Double lookup failed : %d" NL,
102                         doublefailed);
103     }
104
105     apr_file_printf(output, "Cache hits      : %d" NL, cachehits);
106     apr_file_printf(output, "Cache size      : %d" NL, cachesize);
107 }
108
109 /*
110  * usage info
111  */
112 static void usage(void)
113 {
114     apr_file_printf(errfile,
115     "%s -- Resolve IP-addresses to hostnames in Apache log files."           NL
116     "Usage: %s [-s STATFILE] [-c]"                                           NL
117                                                                              NL
118     "Options:"                                                               NL
119     "  -s   Record statistics to STATFILE when finished."                    NL
120                                                                              NL
121     "  -c   Perform double lookups when resolving IP addresses."            NL,
122     shortname, shortname);
123     exit(1);
124 }
125 #undef NL
126
127 int main(int argc, const char * const argv[])
128 {
129     apr_file_t * outfile;
130     apr_file_t * infile;
131     apr_file_t * statsfile;
132     apr_sockaddr_t * ip;
133     apr_sockaddr_t * ipdouble;
134     apr_getopt_t * o;
135     apr_pool_t * pool;
136     apr_status_t status;
137     const char * arg;
138     char opt;
139     char * stats = NULL;
140     char * space;
141     char * hostname;
142 #if APR_MAJOR_VERSION > 1 || (APR_MAJOR_VERSION == 1 && APR_MINOR_VERSION >= 3)
143     char * inbuffer;
144     char * outbuffer;
145 #endif
146     char line[2048];
147     int doublelookups = 0;
148
149     if (apr_app_initialize(&argc, &argv, NULL) != APR_SUCCESS) {
150         return 1;
151     }
152     atexit(apr_terminate);
153
154     if (argc) {
155         shortname = apr_filepath_name_get(argv[0]);
156     }
157
158     if (apr_pool_create(&pool, NULL) != APR_SUCCESS) {
159         return 1;
160     }
161     apr_file_open_stderr(&errfile, pool);
162     apr_getopt_init(&o, pool, argc, argv);
163
164     while (1) {
165         status = apr_getopt(o, "s:c", &opt, &arg);
166         if (status == APR_EOF) {
167             break;
168         }
169         else if (status != APR_SUCCESS) {
170             usage();
171         }
172         else {
173             switch (opt) {
174             case 'c':
175                 if (doublelookups) {
176                     usage();
177                 }
178                 doublelookups = 1;
179                 break;
180             case 's':
181                 if (stats) {
182                     usage();
183                 }
184                 stats = apr_pstrdup(pool, arg);
185                 break;
186             } /* switch */
187         } /* else */
188     } /* while */
189
190     apr_file_open_stdout(&outfile, pool);
191     apr_file_open_stdin(&infile, pool);
192
193 #if APR_MAJOR_VERSION > 1 || (APR_MAJOR_VERSION == 1 && APR_MINOR_VERSION >= 3)
194     /* Allocate two new 10k file buffers */
195     if ((outbuffer = apr_palloc(pool, 10240)) == NULL ||
196         (inbuffer = apr_palloc(pool, 10240)) == NULL) {
197         return 1;
198     }
199
200     /* Set the buffers */
201     apr_file_buffer_set(infile, inbuffer, 10240);
202     apr_file_buffer_set(outfile, outbuffer, 10240);
203 #endif
204
205     cache = apr_hash_make(pool);
206
207     while (apr_file_gets(line, 2048, infile) == APR_SUCCESS) {
208         if (line[0] == '\0') {
209             continue;
210         }
211
212         /* Count our log entries */
213         entries++;
214
215         /* Check if this could even be an IP address */
216         if (!apr_isxdigit(line[0]) && line[0] != ':') {
217                 withname++;
218             apr_file_puts(line, outfile);
219             continue;
220         }
221
222         /* Terminate the line at the next space */
223         if ((space = strchr(line, ' ')) != NULL) {
224             *space = '\0';
225         }
226
227         /* See if we have it in our cache */
228         hostname = (char *) apr_hash_get(cache, line, APR_HASH_KEY_STRING);
229         if (hostname) {
230             apr_file_printf(outfile, hostname);
231             if (space) 
232                 apr_file_printf(outfile, " %s", space + 1);
233             cachehits++;
234             continue;
235         }
236
237         /* Parse the IP address */
238         status = apr_sockaddr_info_get(&ip, line, APR_UNSPEC ,0, 0, pool);
239         if (status != APR_SUCCESS) {
240             /* Not an IP address */
241             withname++;
242             if (space) *space = ' ';
243             apr_file_puts(line, outfile);
244             continue;
245         }
246
247         /* This does not make much sense, but historically "resolves" means
248          * "parsed as an IP address". It does not mean we actually resolved
249          * the IP address into a hostname.
250          */
251         resolves++;
252
253         /* From here on our we cache each result, even if it was not
254          * succesful
255          */
256         cachesize++;
257
258         /* Try and perform a reverse lookup */
259         status = apr_getnameinfo(&hostname, ip, 0) != APR_SUCCESS;
260         if (status || hostname == NULL) {
261             /* Could not perform a reverse lookup */
262             *space = ' ';
263             apr_file_puts(line, outfile);
264             noreverse++;
265
266             /* Add to cache */
267             *space = '\0';
268             apr_hash_set(cache, line, APR_HASH_KEY_STRING,
269                          apr_pstrdup(pool, line));
270             continue;
271         }
272
273         /* Perform a double lookup */
274         if (doublelookups) {
275             /* Do a forward lookup on our hostname, and see if that matches our
276              * original IP address.
277              */
278             status = apr_sockaddr_info_get(&ipdouble, hostname, ip->family, 0,
279                                            0, pool);
280             if (status == APR_SUCCESS ||
281                 memcmp(ipdouble->ipaddr_ptr, ip->ipaddr_ptr, ip->ipaddr_len)) {
282                 /* Double-lookup failed  */
283                 *space = ' ';
284                 apr_file_puts(line, outfile);
285                 doublefailed++;
286
287                 /* Add to cache */
288                 *space = '\0';
289                 apr_hash_set(cache, line, APR_HASH_KEY_STRING,
290                              apr_pstrdup(pool, line));
291                 continue;
292             }
293         }
294
295         /* Outout the resolved name */
296         apr_file_printf(outfile, "%s %s", hostname, space + 1);
297
298         /* Store it in the cache */
299         apr_hash_set(cache, line, APR_HASH_KEY_STRING,
300                      apr_pstrdup(pool, hostname));
301     }
302
303     /* Flush any remaining output */
304     apr_file_flush(outfile);
305
306     if (stats) {
307         if (apr_file_open(&statsfile, stats,
308                        APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE,
309                           APR_OS_DEFAULT, pool) != APR_SUCCESS) {
310             apr_file_printf(errfile, "%s: Could not open %s for writing.",
311                             shortname, stats);
312             return 1;
313         }
314         print_statistics(statsfile);
315         apr_file_close(statsfile);
316     }
317
318     return 0;
319 }