Blame modules/metadata/mod_unique_id.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * mod_unique_id.c: generate a unique identifier for each request
Packit 90a5c9
 *
Packit 90a5c9
 * Original author: Dean Gaudet <dgaudet@arctic.org>
Packit 90a5c9
 * UUencoding modified by: Alvaro Martinez Echevarria <alvaro@lander.es>
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#define APR_WANT_BYTEFUNC   /* for htons() et al */
Packit 90a5c9
#include "apr_want.h"
Packit 90a5c9
#include "apr_general.h"    /* for APR_OFFSETOF                */
Packit 90a5c9
#include "apr_network_io.h"
Packit 90a5c9
Packit 90a5c9
#include "httpd.h"
Packit 90a5c9
#include "http_config.h"
Packit 90a5c9
#include "http_log.h"
Packit 90a5c9
#include "http_protocol.h"  /* for ap_hook_post_read_request */
Packit 90a5c9
Packit 90a5c9
#define ROOT_SIZE 10
Packit 90a5c9
Packit 90a5c9
typedef struct {
Packit 90a5c9
    unsigned int stamp;
Packit 90a5c9
    char root[ROOT_SIZE];
Packit 90a5c9
    unsigned short counter;
Packit 90a5c9
    unsigned int thread_index;
Packit 90a5c9
} unique_id_rec;
Packit 90a5c9
Packit 90a5c9
/* We are using thread_index (the index into the scoreboard), because we
Packit 90a5c9
 * cannot guarantee the thread_id will be an integer.
Packit 90a5c9
 *
Packit 90a5c9
 * This code looks like it won't give a unique ID with the new thread logic.
Packit 90a5c9
 * It will.  The reason is, we don't increment the counter in a thread_safe
Packit 90a5c9
 * manner.  Because the thread_index is also in the unique ID now, this does
Packit 90a5c9
 * not matter.  In order for the id to not be unique, the same thread would
Packit 90a5c9
 * have to get the same counter twice in the same second.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
/* Comments:
Packit 90a5c9
 *
Packit 90a5c9
 * We want an identifier which is unique across all hits, everywhere.
Packit 90a5c9
 * "everywhere" includes multiple httpd instances on the same machine, or on
Packit 90a5c9
 * multiple machines.  Essentially "everywhere" should include all possible
Packit 90a5c9
 * httpds across all servers at a particular "site".  We make some assumptions
Packit 90a5c9
 * that if the site has a cluster of machines then their time is relatively
Packit 90a5c9
 * synchronized.  We also assume that the first address returned by a
Packit 90a5c9
 * gethostbyname (gethostname()) is unique across all the machines at the
Packit 90a5c9
 * "site".
Packit 90a5c9
 *
Packit 90a5c9
 * The root is assumed to absolutely uniquely identify this one child
Packit 90a5c9
 * from all other currently running children on all servers (including
Packit 90a5c9
 * this physical server if it is running multiple httpds) from each
Packit 90a5c9
 * other.
Packit 90a5c9
 *
Packit 90a5c9
 * The stamp and counter are used to distinguish all hits for a
Packit 90a5c9
 * particular root.  The stamp is updated using r->request_time,
Packit 90a5c9
 * saving cpu cycles.  The counter is never reset, and is used to
Packit 90a5c9
 * permit up to 64k requests in a single second by a single child.
Packit 90a5c9
 *
Packit 90a5c9
 * The 144-bits of unique_id_rec are encoded using the alphabet
Packit 90a5c9
 * [A-Za-z0-9@-], resulting in 24 bytes of printable characters.  That is then
Packit 90a5c9
 * stuffed into the environment variable UNIQUE_ID so that it is available to
Packit 90a5c9
 * other modules.  The alphabet choice differs from normal base64 encoding
Packit 90a5c9
 * [A-Za-z0-9+/] because + and / are special characters in URLs and we want to
Packit 90a5c9
 * make it easy to use UNIQUE_ID in URLs.
Packit 90a5c9
 *
Packit 90a5c9
 * Note that UNIQUE_ID should be considered an opaque token by other
Packit 90a5c9
 * applications.  No attempt should be made to dissect its internal components.
Packit 90a5c9
 * It is an abstraction that may change in the future as the needs of this
Packit 90a5c9
 * module change.
Packit 90a5c9
 *
Packit 90a5c9
 * It is highly desirable that identifiers exist for "eternity".  But future
Packit 90a5c9
 * needs (such as much faster webservers, or moving to a
Packit 90a5c9
 * multithreaded server) may dictate a need to change the contents of
Packit 90a5c9
 * unique_id_rec.  Such a future implementation should ensure that the first
Packit 90a5c9
 * field is still a time_t stamp.  By doing that, it is possible for a site to
Packit 90a5c9
 * have a "flag second" in which they stop all of their old-format servers,
Packit 90a5c9
 * wait one entire second, and then start all of their new-servers.  This
Packit 90a5c9
 * procedure will ensure that the new space of identifiers is completely unique
Packit 90a5c9
 * from the old space.  (Since the first four unencoded bytes always differ.)
Packit 90a5c9
 *
Packit 90a5c9
 * Note: previous implementations used 32-bits of IP address plus pid
Packit 90a5c9
 * in place of the PRNG output in the "root" field.  This was
Packit 90a5c9
 * insufficient for IPv6-only hosts, required working DNS to determine
Packit 90a5c9
 * a unique IP address (fragile), and needed a [0, 1) second sleep
Packit 90a5c9
 * call at startup to avoid pid reuse.  Use of the PRNG avoids all
Packit 90a5c9
 * these issues.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Sun Jun  7 05:43:49 CEST 1998 -- Alvaro
Packit 90a5c9
 * More comments:
Packit 90a5c9
 * 1) The UUencoding prodecure is now done in a general way, avoiding the problems
Packit 90a5c9
 * with sizes and paddings that can arise depending on the architecture. Now the
Packit 90a5c9
 * offsets and sizes of the elements of the unique_id_rec structure are calculated
Packit 90a5c9
 * in unique_id_global_init; and then used to duplicate the structure without the
Packit 90a5c9
 * paddings that might exist. The multithreaded server fix should be now very easy:
Packit 90a5c9
 * just add a new "tid" field to the unique_id_rec structure, and increase by one
Packit 90a5c9
 * UNIQUE_ID_REC_MAX.
Packit 90a5c9
 * 2) unique_id_rec.stamp has been changed from "time_t" to "unsigned int", because
Packit 90a5c9
 * its size is 64bits on some platforms (linux/alpha), and this caused problems with
Packit 90a5c9
 * htonl/ntohl. Well, this shouldn't be a problem till year 2106.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * XXX: We should have a per-thread counter and not use cur_unique_id.counter
Packit 90a5c9
 * XXX: in all threads, because this is bad for performance on multi-processor
Packit 90a5c9
 * XXX: systems: Writing to the same address from several CPUs causes cache
Packit 90a5c9
 * XXX: thrashing.
Packit 90a5c9
 */
Packit 90a5c9
static unique_id_rec cur_unique_id;
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * Number of elements in the structure unique_id_rec.
Packit 90a5c9
 */
Packit 90a5c9
#define UNIQUE_ID_REC_MAX 4
Packit 90a5c9
Packit 90a5c9
static unsigned short unique_id_rec_offset[UNIQUE_ID_REC_MAX],
Packit 90a5c9
                      unique_id_rec_size[UNIQUE_ID_REC_MAX],
Packit 90a5c9
                      unique_id_rec_total_size,
Packit 90a5c9
                      unique_id_rec_size_uu;
Packit 90a5c9
Packit 90a5c9
static int unique_id_global_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_server)
Packit 90a5c9
{
Packit 90a5c9
    /*
Packit 90a5c9
     * Calculate the sizes and offsets in cur_unique_id.
Packit 90a5c9
     */
Packit 90a5c9
    unique_id_rec_offset[0] = APR_OFFSETOF(unique_id_rec, stamp);
Packit 90a5c9
    unique_id_rec_size[0] = sizeof(cur_unique_id.stamp);
Packit 90a5c9
    unique_id_rec_offset[1] = APR_OFFSETOF(unique_id_rec, root);
Packit 90a5c9
    unique_id_rec_size[1] = sizeof(cur_unique_id.root);
Packit 90a5c9
    unique_id_rec_offset[2] = APR_OFFSETOF(unique_id_rec, counter);
Packit 90a5c9
    unique_id_rec_size[2] = sizeof(cur_unique_id.counter);
Packit 90a5c9
    unique_id_rec_offset[3] = APR_OFFSETOF(unique_id_rec, thread_index);
Packit 90a5c9
    unique_id_rec_size[3] = sizeof(cur_unique_id.thread_index);
Packit 90a5c9
    unique_id_rec_total_size = unique_id_rec_size[0] + unique_id_rec_size[1] +
Packit 90a5c9
                               unique_id_rec_size[2] + unique_id_rec_size[3];
Packit 90a5c9
Packit 90a5c9
    /*
Packit 90a5c9
     * Calculate the size of the structure when encoded.
Packit 90a5c9
     */
Packit 90a5c9
    unique_id_rec_size_uu = (unique_id_rec_total_size*8+5)/6;
Packit 90a5c9
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void unique_id_child_init(apr_pool_t *p, server_rec *s)
Packit 90a5c9
{
Packit 90a5c9
    ap_random_insecure_bytes(&cur_unique_id.root,
Packit 90a5c9
                             sizeof(cur_unique_id.root));
Packit 90a5c9
Packit 90a5c9
    /*
Packit 90a5c9
     * If we use 0 as the initial counter we have a little less protection
Packit 90a5c9
     * against restart problems, and a little less protection against a clock
Packit 90a5c9
     * going backwards in time.
Packit 90a5c9
     */
Packit 90a5c9
    ap_random_insecure_bytes(&cur_unique_id.counter,
Packit 90a5c9
                             sizeof(cur_unique_id.counter));
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/* NOTE: This is *NOT* the same encoding used by base64encode ... the last two
Packit 90a5c9
 * characters should be + and /.  But those two characters have very special
Packit 90a5c9
 * meanings in URLs, and we want to make it easy to use identifiers in
Packit 90a5c9
 * URLs.  So we replace them with @ and -.
Packit 90a5c9
 */
Packit 90a5c9
static const char uuencoder[64] = {
Packit 90a5c9
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
Packit 90a5c9
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
Packit 90a5c9
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
Packit 90a5c9
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
Packit 90a5c9
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '@', '-',
Packit 90a5c9
};
Packit 90a5c9
Packit 90a5c9
static const char *gen_unique_id(const request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    char *str;
Packit 90a5c9
    /*
Packit 90a5c9
     * Buffer padded with two final bytes, used to copy the unique_id_red
Packit 90a5c9
     * structure without the internal paddings that it could have.
Packit 90a5c9
     */
Packit 90a5c9
    unique_id_rec new_unique_id;
Packit 90a5c9
    struct {
Packit 90a5c9
        unique_id_rec foo;
Packit 90a5c9
        unsigned char pad[2];
Packit 90a5c9
    } paddedbuf;
Packit 90a5c9
    unsigned char *x,*y;
Packit 90a5c9
    unsigned short counter;
Packit 90a5c9
    int i,j,k;
Packit 90a5c9
Packit 90a5c9
    memcpy(&new_unique_id.root, &cur_unique_id.root, ROOT_SIZE);
Packit 90a5c9
    new_unique_id.counter = cur_unique_id.counter;
Packit 90a5c9
    new_unique_id.stamp = htonl((unsigned int)apr_time_sec(r->request_time));
Packit 90a5c9
    new_unique_id.thread_index = htonl((unsigned int)r->connection->id);
Packit 90a5c9
Packit 90a5c9
    /* we'll use a temporal buffer to avoid uuencoding the possible internal
Packit 90a5c9
     * paddings of the original structure */
Packit 90a5c9
    x = (unsigned char *) &paddedbuf;
Packit 90a5c9
    k = 0;
Packit 90a5c9
    for (i = 0; i < UNIQUE_ID_REC_MAX; i++) {
Packit 90a5c9
        y = ((unsigned char *) &new_unique_id) + unique_id_rec_offset[i];
Packit 90a5c9
        for (j = 0; j < unique_id_rec_size[i]; j++, k++) {
Packit 90a5c9
            x[k] = y[j];
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
    /*
Packit 90a5c9
     * We reset two more bytes just in case padding is needed for the uuencoding.
Packit 90a5c9
     */
Packit 90a5c9
    x[k++] = '\0';
Packit 90a5c9
    x[k++] = '\0';
Packit 90a5c9
Packit 90a5c9
    /* alloc str and do the uuencoding */
Packit 90a5c9
    str = (char *)apr_palloc(r->pool, unique_id_rec_size_uu + 1);
Packit 90a5c9
    k = 0;
Packit 90a5c9
    for (i = 0; i < unique_id_rec_total_size; i += 3) {
Packit 90a5c9
        y = x + i;
Packit 90a5c9
        str[k++] = uuencoder[y[0] >> 2];
Packit 90a5c9
        str[k++] = uuencoder[((y[0] & 0x03) << 4) | ((y[1] & 0xf0) >> 4)];
Packit 90a5c9
        if (k == unique_id_rec_size_uu) break;
Packit 90a5c9
        str[k++] = uuencoder[((y[1] & 0x0f) << 2) | ((y[2] & 0xc0) >> 6)];
Packit 90a5c9
        if (k == unique_id_rec_size_uu) break;
Packit 90a5c9
        str[k++] = uuencoder[y[2] & 0x3f];
Packit 90a5c9
    }
Packit 90a5c9
    str[k++] = '\0';
Packit 90a5c9
Packit 90a5c9
    /* and increment the identifier for the next call */
Packit 90a5c9
Packit 90a5c9
    counter = ntohs(new_unique_id.counter) + 1;
Packit 90a5c9
    cur_unique_id.counter = htons(counter);
Packit 90a5c9
Packit 90a5c9
    return str;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
/*
Packit 90a5c9
 * There are two ways the generation of a unique id can be triggered:
Packit 90a5c9
 *
Packit 90a5c9
 * - from the post_read_request hook which calls set_unique_id()
Packit 90a5c9
 * - from error logging via the generate_log_id hook which calls
Packit 90a5c9
 *   generate_log_id(). This may happen before or after set_unique_id()
Packit 90a5c9
 *   has been called, or not at all.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
static int generate_log_id(const conn_rec *c, const request_rec *r,
Packit 90a5c9
                           const char **id)
Packit 90a5c9
{
Packit 90a5c9
    /* we do not care about connection ids */
Packit 90a5c9
    if (r == NULL)
Packit 90a5c9
        return DECLINED;
Packit 90a5c9
Packit 90a5c9
    /* XXX: do we need special handling for internal redirects? */
Packit 90a5c9
Packit 90a5c9
    /* if set_unique_id() has been called for this request, use it */
Packit 90a5c9
    *id = apr_table_get(r->subprocess_env, "UNIQUE_ID");
Packit 90a5c9
Packit 90a5c9
    if (!*id)
Packit 90a5c9
        *id = gen_unique_id(r);
Packit 90a5c9
    return OK;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static int set_unique_id(request_rec *r)
Packit 90a5c9
{
Packit 90a5c9
    const char *id = NULL;
Packit 90a5c9
    /* copy the unique_id if this is an internal redirect (we're never
Packit 90a5c9
     * actually called for sub requests, so we don't need to test for
Packit 90a5c9
     * them) */
Packit 90a5c9
    if (r->prev) {
Packit 90a5c9
       id = apr_table_get(r->subprocess_env, "REDIRECT_UNIQUE_ID");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!id) {
Packit 90a5c9
        /* if we have a log id, it was set by our generate_log_id() function
Packit 90a5c9
         * and we should reuse the same id
Packit 90a5c9
         */
Packit 90a5c9
        id = r->log_id;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (!id) {
Packit 90a5c9
        id = gen_unique_id(r);
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    /* set the environment variable */
Packit 90a5c9
    apr_table_setn(r->subprocess_env, "UNIQUE_ID", id);
Packit 90a5c9
Packit 90a5c9
    return DECLINED;
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void register_hooks(apr_pool_t *p)
Packit 90a5c9
{
Packit 90a5c9
    ap_hook_post_config(unique_id_global_init, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
    ap_hook_child_init(unique_id_child_init, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
    ap_hook_post_read_request(set_unique_id, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
    ap_hook_generate_log_id(generate_log_id, NULL, NULL, APR_HOOK_MIDDLE);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
AP_DECLARE_MODULE(unique_id) = {
Packit 90a5c9
    STANDARD20_MODULE_STUFF,
Packit 90a5c9
    NULL,                       /* dir config creater */
Packit 90a5c9
    NULL,                       /* dir merger --- default is to override */
Packit 90a5c9
    NULL,                       /* server config */
Packit 90a5c9
    NULL,                       /* merge server configs */
Packit 90a5c9
    NULL,                       /* command apr_table_t */
Packit 90a5c9
    register_hooks              /* register hooks */
Packit 90a5c9
};