]> granicus.if.org Git - apache/blob - modules/metadata/mod_unique_id.c
fix a compiler error with picky compilers that (correctly) don't let
[apache] / modules / metadata / mod_unique_id.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /*
60  * mod_unique_id.c: generate a unique identifier for each request
61  *
62  * Original author: Dean Gaudet <dgaudet@arctic.org>
63  * UUencoding modified by: Alvaro Martinez Echevarria <alvaro@lander.es>
64  */
65
66 #define APR_WANT_BYTEFUNC   /* for htons() et al */
67 #include "apr_want.h"
68 #include "apr_general.h"    /* for APR_XtOffsetOf                */
69 #include "apr_network_io.h"
70
71 #include "httpd.h"
72 #include "http_config.h"
73 #include "http_log.h"
74 #include "http_protocol.h"  /* for ap_hook_post_read_request */
75
76 #if APR_HAVE_UNISTD_H
77 #include <unistd.h>         /* for getpid() */
78 #endif
79
80 typedef struct {
81     unsigned int stamp;
82     unsigned int in_addr;
83     unsigned int pid;
84     unsigned short counter;
85     unsigned int thread_index;
86 } unique_id_rec;
87
88 /* We are using thread_index (the index into the scoreboard), because we
89  * cannot guarantee the thread_id will be an integer.
90  *
91  * This code looks like it won't give a unique ID with the new thread logic.
92  * It will.  The reason is, we don't increment the counter in a thread_safe 
93  * manner.  Because the thread_index is also in the unique ID now, this does
94  * not matter.  In order for the id to not be unique, the same thread would
95  * have to get the same counter twice in the same second. 
96  */
97
98 /* Comments:
99  *
100  * We want an identifier which is unique across all hits, everywhere.
101  * "everywhere" includes multiple httpd instances on the same machine, or on
102  * multiple machines.  Essentially "everywhere" should include all possible
103  * httpds across all servers at a particular "site".  We make some assumptions
104  * that if the site has a cluster of machines then their time is relatively
105  * synchronized.  We also assume that the first address returned by a
106  * gethostbyname (gethostname()) is unique across all the machines at the
107  * "site".
108  *
109  * We also further assume that pids fit in 32-bits.  If something uses more
110  * than 32-bits, the fix is trivial, but it requires the unrolled uuencoding
111  * loop to be extended.  * A similar fix is needed to support multithreaded
112  * servers, using a pid/tid combo.
113  *
114  * Together, the in_addr and pid are assumed to absolutely uniquely identify
115  * this one child from all other currently running children on all servers
116  * (including this physical server if it is running multiple httpds) from each
117  * other.
118  *
119  * The stamp and counter are used to distinguish all hits for a particular
120  * (in_addr,pid) pair.  The stamp is updated using r->request_time,
121  * saving cpu cycles.  The counter is never reset, and is used to permit up to
122  * 64k requests in a single second by a single child.
123  *
124  * The 112-bits of unique_id_rec are encoded using the alphabet
125  * [A-Za-z0-9@-], resulting in 19 bytes of printable characters.  That is then
126  * stuffed into the environment variable UNIQUE_ID so that it is available to
127  * other modules.  The alphabet choice differs from normal base64 encoding
128  * [A-Za-z0-9+/] because + and / are special characters in URLs and we want to
129  * make it easy to use UNIQUE_ID in URLs.
130  *
131  * Note that UNIQUE_ID should be considered an opaque token by other
132  * applications.  No attempt should be made to dissect its internal components.
133  * It is an abstraction that may change in the future as the needs of this
134  * module change.
135  *
136  * It is highly desirable that identifiers exist for "eternity".  But future
137  * needs (such as much faster webservers, moving to 64-bit pids, or moving to a
138  * multithreaded server) may dictate a need to change the contents of
139  * unique_id_rec.  Such a future implementation should ensure that the first
140  * field is still a time_t stamp.  By doing that, it is possible for a site to
141  * have a "flag second" in which they stop all of their old-format servers,
142  * wait one entire second, and then start all of their new-servers.  This
143  * procedure will ensure that the new space of identifiers is completely unique
144  * from the old space.  (Since the first four unencoded bytes always differ.)
145  */
146 /*
147  * Sun Jun  7 05:43:49 CEST 1998 -- Alvaro
148  * More comments:
149  * 1) The UUencoding prodecure is now done in a general way, avoiding the problems
150  * with sizes and paddings that can arise depending on the architecture. Now the
151  * offsets and sizes of the elements of the unique_id_rec structure are calculated
152  * in unique_id_global_init; and then used to duplicate the structure without the
153  * paddings that might exist. The multithreaded server fix should be now very easy:
154  * just add a new "tid" field to the unique_id_rec structure, and increase by one
155  * UNIQUE_ID_REC_MAX.
156  * 2) unique_id_rec.stamp has been changed from "time_t" to "unsigned int", because
157  * its size is 64bits on some platforms (linux/alpha), and this caused problems with
158  * htonl/ntohl. Well, this shouldn't be a problem till year 2106.
159  */
160
161 static unsigned global_in_addr;
162
163 static unique_id_rec cur_unique_id;
164
165 /*
166  * Number of elements in the structure unique_id_rec.
167  */
168 #define UNIQUE_ID_REC_MAX 5 
169
170 static unsigned short unique_id_rec_offset[UNIQUE_ID_REC_MAX],
171                       unique_id_rec_size[UNIQUE_ID_REC_MAX],
172                       unique_id_rec_total_size,
173                       unique_id_rec_size_uu;
174
175 static int unique_id_global_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_server)
176 {
177     char str[APRMAXHOSTLEN + 1];
178     apr_short_interval_time_t pause;
179     apr_status_t rv;
180     char *ipaddrstr;
181     apr_sockaddr_t *sockaddr;
182
183     /*
184      * Calculate the sizes and offsets in cur_unique_id.
185      */
186     unique_id_rec_offset[0] = APR_XtOffsetOf(unique_id_rec, stamp);
187     unique_id_rec_size[0] = sizeof(cur_unique_id.stamp);
188     unique_id_rec_offset[1] = APR_XtOffsetOf(unique_id_rec, in_addr);
189     unique_id_rec_size[1] = sizeof(cur_unique_id.in_addr);
190     unique_id_rec_offset[2] = APR_XtOffsetOf(unique_id_rec, pid);
191     unique_id_rec_size[2] = sizeof(cur_unique_id.pid);
192     unique_id_rec_offset[3] = APR_XtOffsetOf(unique_id_rec, counter);
193     unique_id_rec_size[3] = sizeof(cur_unique_id.counter);
194     unique_id_rec_offset[4] = APR_XtOffsetOf(unique_id_rec, thread_index);
195     unique_id_rec_size[4] = sizeof(cur_unique_id.thread_index);
196     unique_id_rec_total_size = unique_id_rec_size[0] + unique_id_rec_size[1] +
197                                unique_id_rec_size[2] + unique_id_rec_size[3] +
198                                unique_id_rec_size[4];
199
200     /*
201      * Calculate the size of the structure when encoded.
202      */
203     unique_id_rec_size_uu = (unique_id_rec_total_size*8+5)/6;
204
205     /*
206      * Now get the global in_addr.  Note that it is not sufficient to use one
207      * of the addresses from the main_server, since those aren't as likely to
208      * be unique as the physical address of the machine
209      */
210     if ((rv = apr_gethostname(str, sizeof(str) - 1, p)) != APR_SUCCESS) {
211         ap_log_error(APLOG_MARK, APLOG_ALERT, rv, main_server,
212           "mod_unique_id: unable to find hostname of the server");
213         return HTTP_INTERNAL_SERVER_ERROR;
214     }
215
216     if ((rv = apr_sockaddr_info_get(&sockaddr, str, AF_INET, 0, 0, p)) == APR_SUCCESS) {
217         global_in_addr = sockaddr->sa.sin.sin_addr.s_addr;
218     }
219     else {
220         ap_log_error(APLOG_MARK, APLOG_ALERT, rv, main_server,
221                     "mod_unique_id: unable to find IPv4 address of \"%s\"", str);
222 #if APR_HAVE_IPV6
223         if ((rv = apr_sockaddr_info_get(&sockaddr, str, AF_INET6, 0, 0, p)) == APR_SUCCESS) {
224             memcpy(&global_in_addr,
225                    (char *)sockaddr->ipaddr_ptr + sockaddr->ipaddr_len - sizeof(global_in_addr),
226                    sizeof(global_in_addr));
227             ap_log_error(APLOG_MARK, APLOG_ALERT, rv, main_server,
228                          "mod_unique_id: using low-order bits of IPv6 address "
229                          "as if they were unique");
230         }
231         else
232 #endif
233         return HTTP_INTERNAL_SERVER_ERROR;
234     }
235
236     apr_sockaddr_ip_get(&ipaddrstr, sockaddr);
237     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, main_server,
238                 "mod_unique_id: using ip addr %s",
239                  ipaddrstr);
240
241     /*
242      * If the server is pummelled with restart requests we could possibly end
243      * up in a situation where we're starting again during the same second
244      * that has been used in previous identifiers.  Avoid that situation.
245      * 
246      * In truth, for this to actually happen not only would it have to restart
247      * in the same second, but it would have to somehow get the same pids as
248      * one of the other servers that was running in that second. Which would
249      * mean a 64k wraparound on pids ... not very likely at all.
250      * 
251      * But protecting against it is relatively cheap.  We just sleep into the
252      * next second.
253      */
254     pause = (apr_short_interval_time_t)(1000000 - (apr_time_now() % APR_USEC_PER_SEC));
255     apr_sleep(pause);
256     return OK;
257 }
258
259 static void unique_id_child_init(apr_pool_t *p, server_rec *s)
260 {
261     pid_t pid;
262     apr_time_t tv;
263
264     /*
265      * Note that we use the pid because it's possible that on the same
266      * physical machine there are multiple servers (i.e. using Listen). But
267      * it's guaranteed that none of them will share the same pids between
268      * children.
269      * 
270      * XXX: for multithread this needs to use a pid/tid combo and probably
271      * needs to be expanded to 32 bits
272      */
273     pid = getpid();
274     cur_unique_id.pid = pid;
275
276     /*
277      * Test our assumption that the pid is 32-bits.  It's possible that
278      * 64-bit machines will declare pid_t to be 64 bits but only use 32
279      * of them.  It would have been really nice to test this during
280      * global_init ... but oh well.
281      */
282     if ((pid_t)cur_unique_id.pid != pid) {
283         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, 0, s,
284                     "oh no! pids are greater than 32-bits!  I'm broken!");
285     }
286
287     cur_unique_id.in_addr = global_in_addr;
288
289     /*
290      * If we use 0 as the initial counter we have a little less protection
291      * against restart problems, and a little less protection against a clock
292      * going backwards in time.
293      */
294     tv = apr_time_now();
295     /* Some systems have very low variance on the low end of their system
296      * counter, defend against that.
297      */
298     cur_unique_id.counter = (unsigned short)(tv % APR_USEC_PER_SEC / 10);
299
300     /*
301      * We must always use network ordering for these bytes, so that
302      * identifiers are comparable between machines of different byte
303      * orderings.  Note in_addr is already in network order.
304      */
305     cur_unique_id.pid = htonl(cur_unique_id.pid);
306     cur_unique_id.counter = htons(cur_unique_id.counter);
307 }
308
309 /* NOTE: This is *NOT* the same encoding used by base64encode ... the last two
310  * characters should be + and /.  But those two characters have very special
311  * meanings in URLs, and we want to make it easy to use identifiers in
312  * URLs.  So we replace them with @ and -.
313  */
314 static const char uuencoder[64] = {
315     'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
316     'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
317     'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
318     'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
319     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '@', '-',
320 };
321
322 static int gen_unique_id(request_rec *r)
323 {
324     char *str;
325     /*
326      * Buffer padded with two final bytes, used to copy the unique_id_red
327      * structure without the internal paddings that it could have.
328      */
329     unique_id_rec new_unique_id;
330     struct {
331         unique_id_rec foo;
332         unsigned char pad[2];
333     } paddedbuf;
334     unsigned char *x,*y;
335     unsigned short counter;
336     const char *e;
337     int i,j,k;
338
339     /* copy the unique_id if this is an internal redirect (we're never
340      * actually called for sub requests, so we don't need to test for
341      * them) */
342     if (r->prev && (e = apr_table_get(r->subprocess_env, "REDIRECT_UNIQUE_ID"))) {
343         apr_table_setn(r->subprocess_env, "UNIQUE_ID", e);
344         return DECLINED;
345     }
346     
347     new_unique_id.in_addr = cur_unique_id.in_addr;
348     new_unique_id.pid = cur_unique_id.pid;
349     new_unique_id.counter = cur_unique_id.counter;
350
351     new_unique_id.stamp = htonl((unsigned int)r->request_time);
352     new_unique_id.thread_index = htonl((unsigned int)r->connection->id);
353
354     /* we'll use a temporal buffer to avoid uuencoding the possible internal
355      * paddings of the original structure */
356     x = (unsigned char *) &paddedbuf;
357     y = (unsigned char *) &new_unique_id;
358     k = 0;
359     for (i = 0; i < UNIQUE_ID_REC_MAX; i++) {
360         y = ((unsigned char *) &new_unique_id) + unique_id_rec_offset[i];
361         for (j = 0; j < unique_id_rec_size[i]; j++, k++) {
362             x[k] = y[j];
363         }
364     }
365     /*
366      * We reset two more bytes just in case padding is needed for the uuencoding.
367      */
368     x[k++] = '\0';
369     x[k++] = '\0';
370     
371     /* alloc str and do the uuencoding */
372     str = (char *)apr_palloc(r->pool, unique_id_rec_size_uu + 1);
373     k = 0;
374     for (i = 0; i < unique_id_rec_total_size; i += 3) {
375         y = x + i;
376         str[k++] = uuencoder[y[0] >> 2];
377         str[k++] = uuencoder[((y[0] & 0x03) << 4) | ((y[1] & 0xf0) >> 4)];
378         if (k == unique_id_rec_size_uu) break;
379         str[k++] = uuencoder[((y[1] & 0x0f) << 2) | ((y[2] & 0xc0) >> 6)];
380         if (k == unique_id_rec_size_uu) break;
381         str[k++] = uuencoder[y[2] & 0x3f];
382     }
383     str[k++] = '\0';
384
385     /* set the environment variable */
386     apr_table_setn(r->subprocess_env, "UNIQUE_ID", str);
387
388     /* and increment the identifier for the next call */
389
390     counter = ntohs(new_unique_id.counter) + 1;
391     cur_unique_id.counter = htons(counter);
392
393     return DECLINED;
394 }
395
396 static void register_hooks(apr_pool_t *p)
397 {
398     ap_hook_post_config(unique_id_global_init, NULL, NULL, APR_HOOK_MIDDLE);
399     ap_hook_child_init(unique_id_child_init, NULL, NULL, APR_HOOK_MIDDLE);
400     ap_hook_post_read_request(gen_unique_id, NULL, NULL, APR_HOOK_MIDDLE); 
401 }
402
403 module AP_MODULE_DECLARE_DATA unique_id_module = {
404     STANDARD20_MODULE_STUFF,
405     NULL,                       /* dir config creater */
406     NULL,                       /* dir merger --- default is to override */
407     NULL,                       /* server config */
408     NULL,                       /* merge server configs */
409     NULL,                       /* command apr_table_t */
410     register_hooks              /* register hooks */
411 };