1 /*	$OpenBSD: mod_log_referer.c,v 1.8 2004/12/02 19:42:48 henning Exp $ */
2 
3 /* ====================================================================
4  * The Apache Software License, Version 1.1
5  *
6  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
7  * reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  *
21  * 3. The end-user documentation included with the redistribution,
22  *    if any, must include the following acknowledgment:
23  *       "This product includes software developed by the
24  *        Apache Software Foundation (http://www.apache.org/)."
25  *    Alternately, this acknowledgment may appear in the software itself,
26  *    if and wherever such third-party acknowledgments normally appear.
27  *
28  * 4. The names "Apache" and "Apache Software Foundation" must
29  *    not be used to endorse or promote products derived from this
30  *    software without prior written permission. For written
31  *    permission, please contact apache@apache.org.
32  *
33  * 5. Products derived from this software may not be called "Apache",
34  *    nor may "Apache" appear in their name, without prior written
35  *    permission of the Apache Software Foundation.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals on behalf of the Apache Software Foundation.  For more
53  * information on the Apache Software Foundation, please see
54  * <http://www.apache.org/>.
55  *
56  * Portions of this software are based upon public domain software
57  * originally written at the National Center for Supercomputing Applications,
58  * University of Illinois, Urbana-Champaign.
59  */
60 
61 
62 #include "httpd.h"
63 #include "http_config.h"
64 #include "http_log.h"
65 #include "http_main.h"
66 #include "fdcache.h"
67 
68 module referer_log_module;
69 
70 static int xfer_flags = (O_WRONLY | O_APPEND | O_CREAT);
71 
72 static mode_t xfer_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
73 
74 typedef struct {
75     char *fname;
76     int referer_fd;
77     array_header *referer_ignore_list;
78 } referer_log_state;
79 
make_referer_log_state(pool * p,server_rec * s)80 static void *make_referer_log_state(pool *p, server_rec *s)
81 {
82     referer_log_state *cls =
83     (referer_log_state *) ap_palloc(p, sizeof(referer_log_state));
84 
85     cls->fname = "";
86     cls->referer_fd = -1;
87     cls->referer_ignore_list = ap_make_array(p, 1, sizeof(char *));
88     return (void *) cls;
89 }
90 
set_referer_log(cmd_parms * parms,void * dummy,char * arg)91 static const char *set_referer_log(cmd_parms *parms, void *dummy, char *arg)
92 {
93     referer_log_state *cls = ap_get_module_config(parms->server->module_config,
94                                                &referer_log_module);
95 
96     cls->fname = arg;
97     return NULL;
98 }
99 
add_referer_ignore(cmd_parms * parms,void * dummy,char * arg)100 static const char *add_referer_ignore(cmd_parms *parms, void *dummy, char *arg)
101 {
102     char **addme;
103     referer_log_state *cls = ap_get_module_config(parms->server->module_config,
104                                                &referer_log_module);
105 
106     addme = ap_push_array(cls->referer_ignore_list);
107     ap_str_tolower(arg);
108     *addme = arg;
109     return NULL;
110 }
111 
112 static const command_rec referer_log_cmds[] =
113 {
114     {"RefererLog", set_referer_log, NULL, RSRC_CONF, TAKE1,
115      "the filename of the referer log"},
116     {"RefererIgnore", add_referer_ignore, NULL, RSRC_CONF, ITERATE,
117      "referer hostnames to ignore"},
118     {NULL}
119 };
120 
open_referer_log(server_rec * s,pool * p)121 static void open_referer_log(server_rec *s, pool *p)
122 {
123     referer_log_state *cls = ap_get_module_config(s->module_config,
124                                                &referer_log_module);
125 
126     char *fname = ap_server_root_relative(p, cls->fname);
127 
128     if (cls->referer_fd > 0)
129         return;                 /* virtual log shared w/main server */
130 
131     if (*cls->fname == '|') {
132         piped_log *pl;
133 
134 	pl = ap_open_piped_log(p, cls->fname + 1);
135 	if (pl == NULL) {
136 	    ap_log_error(APLOG_MARK, APLOG_ERR, s,
137 			 "couldn't spawn referer log pipe");
138             exit(1);
139         }
140 
141         cls->referer_fd = ap_piped_log_write_fd(pl);
142     }
143     else if (*cls->fname != '\0') {
144 	if (ap_server_chroot_desired())
145 	    cls->referer_fd = fdcache_open(fname, xfer_flags, xfer_mode);
146 	else
147 	    cls->referer_fd = ap_popenf_ex(p, fname, xfer_flags, xfer_mode, 1);
148 
149         if (cls->referer_fd < 0) {
150 	    ap_log_error(APLOG_MARK, APLOG_ERR, s,
151 			 "could not open referer log file %s.", fname);
152 	    exit(1);
153 	}
154     }
155 }
156 
init_referer_log(server_rec * s,pool * p)157 static void init_referer_log(server_rec *s, pool *p)
158 {
159     for (; s; s = s->next)
160         open_referer_log(s, p);
161 }
162 
referer_log_transaction(request_rec * orig)163 static int referer_log_transaction(request_rec *orig)
164 {
165     char **ptrptr, **ptrptr2;
166     referer_log_state *cls = ap_get_module_config(orig->server->module_config,
167                                                &referer_log_module);
168 
169     char *str;
170     const char *referer;
171     char *referertest;
172     request_rec *r;
173 
174     if (cls->referer_fd < 0)
175         return OK;
176 
177     for (r = orig; r->next; r = r->next)
178         continue;
179     if (*cls->fname == '\0')    /* Don't log referer */
180         return DECLINED;
181 
182     referer = ap_table_get(orig->headers_in, "Referer");
183     if (referer != NULL) {
184 
185         referertest = ap_pstrdup(orig->pool, referer);
186         ap_str_tolower(referertest);
187         /* The following is an upsetting mess of pointers, I'm sorry
188            Anyone with the motiviation and/or the time should feel free
189            to make this cleaner... */
190 
191         ptrptr2 = (char **) (cls->referer_ignore_list->elts +
192                              (cls->referer_ignore_list->nelts *
193                               cls->referer_ignore_list->elt_size));
194 
195         /* Go through each element of the ignore list and compare it to the
196            referer_host.  If we get a match, return without logging */
197 
198         for (ptrptr = (char **) cls->referer_ignore_list->elts;
199              ptrptr < ptrptr2;
200              ptrptr = (char **) ((char *) ptrptr + cls->referer_ignore_list->elt_size)) {
201             if (strstr(referertest, *ptrptr))
202                 return OK;
203         }
204 
205 
206         str = ap_pstrcat(orig->pool, referer, " -> ", r->uri, "\n", NULL);
207         write(cls->referer_fd, str, strlen(str));
208     }
209 
210     return OK;
211 }
212 
213 module referer_log_module =
214 {
215     STANDARD_MODULE_STUFF,
216     init_referer_log,           /* initializer */
217     NULL,                       /* create per-dir config */
218     NULL,                       /* merge per-dir config */
219     make_referer_log_state,     /* server config */
220     NULL,                       /* merge server config */
221     referer_log_cmds,           /* command table */
222     NULL,                       /* handlers */
223     NULL,                       /* filename translation */
224     NULL,                       /* check_user_id */
225     NULL,                       /* check auth */
226     NULL,                       /* check access */
227     NULL,                       /* type_checker */
228     NULL,                       /* fixups */
229     referer_log_transaction,    /* logger */
230     NULL,                       /* header parser */
231     NULL,                       /* child_init */
232     NULL,                       /* child_exit */
233     NULL                        /* post read-request */
234 };
235