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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Originally written @ Covalent by Jim Jagielski
23 * Think of this as a filter sniffer for Apache 2.x. It logs
24 * all filter data right before and after it goes out on the
25 * wire (BUT right before SSL encoded or after SSL decoded).
26 * It can produce a *huge* amount of data.
31 #include "http_connection.h"
32 #include "http_config.h"
33 #include "http_core.h"
36 module AP_MODULE_DECLARE_DATA dumpio_module
;
38 typedef struct dumpio_conf_t
{
44 /* consider up to 80 additional characters, and factor the longest
45 * line length of all \xNN sequences; log_error cannot record more
46 * than MAX_STRING_LEN characters.
48 #define dumpio_MAX_STRING_LEN MAX_STRING_LEN / 4 - 80
51 * Workhorse function: simply log to the current error_log
52 * info about the data in the bucket as well as the data itself
54 static void dumpit(ap_filter_t
*f
, apr_bucket
*b
, dumpio_conf_t
*ptr
)
58 ap_log_error(APLOG_MARK
, ptr
->loglevel
, 0, c
->base_server
,
59 "mod_dumpio: %s (%s-%s): %" APR_SIZE_T_FMT
" bytes",
61 (APR_BUCKET_IS_METADATA(b
)) ? "metadata" : "data",
65 if (!(APR_BUCKET_IS_METADATA(b
)))
67 #if APR_CHARSET_EBCDIC
68 char xlatebuf
[dumpio_MAX_STRING_LEN
+ 1];
73 apr_status_t rv
= apr_bucket_read(b
, &buf
, &nbytes
, APR_BLOCK_READ
);
75 if (rv
== APR_SUCCESS
)
80 if (logbytes
> dumpio_MAX_STRING_LEN
)
81 logbytes
= dumpio_MAX_STRING_LEN
;
84 #if APR_CHARSET_EBCDIC
85 memcpy(xlatebuf
, buf
, logbytes
);
86 ap_xlate_proto_from_ascii(xlatebuf
, logbytes
);
87 xlatebuf
[logbytes
] = '\0';
88 ap_log_error(APLOG_MARK
, ptr
->loglevel
, 0, c
->base_server
,
89 "mod_dumpio: %s (%s-%s): %s", f
->frec
->name
,
90 (APR_BUCKET_IS_METADATA(b
)) ? "metadata" : "data",
91 b
->type
->name
, xlatebuf
);
93 /* XXX: Seriously flawed; we do not pay attention to embedded
94 * \0's in the request body, these should be escaped; however,
95 * the logging function already performs a significant amount
96 * of escaping, and so any escaping would be double-escaped.
97 * The coding solution is to throw away the current logic
98 * within ap_log_error, and introduce new vformatter %-escapes
99 * for escaping text, and for binary text (fixed len strings).
101 ap_log_error(APLOG_MARK
| APLOG_NOERRNO
, ptr
->loglevel
, 0, c
->base_server
,
102 "mod_dumpio: %s (%s-%s): %.*s", f
->frec
->name
,
103 (APR_BUCKET_IS_METADATA(b
)) ? "metadata" : "data",
104 b
->type
->name
, logbytes
, buf
);
108 ap_log_error(APLOG_MARK
, ptr
->loglevel
, rv
, c
->base_server
,
109 "mod_dumpio: %s (%s-%s): %s", f
->frec
->name
,
110 (APR_BUCKET_IS_METADATA(b
)) ? "metadata" : "data",
111 b
->type
->name
, "error reading data");
116 #define whichmode( mode ) \
117 ( (( mode ) == AP_MODE_READBYTES) ? "readbytes" : \
118 (( mode ) == AP_MODE_GETLINE) ? "getline" : \
119 (( mode ) == AP_MODE_EATCRLF) ? "eatcrlf" : \
120 (( mode ) == AP_MODE_SPECULATIVE) ? "speculative" : \
121 (( mode ) == AP_MODE_EXHAUSTIVE) ? "exhaustive" : \
122 (( mode ) == AP_MODE_INIT) ? "init" : "unknown" \
125 static int dumpio_input_filter (ap_filter_t
*f
, apr_bucket_brigade
*bb
,
126 ap_input_mode_t mode
, apr_read_type_e block
, apr_off_t readbytes
)
132 dumpio_conf_t
*ptr
= f
->ctx
;
134 ap_log_error(APLOG_MARK
, ptr
->loglevel
, 0, c
->base_server
,
135 "mod_dumpio: %s [%s-%s] %" APR_OFF_T_FMT
" readbytes",
138 ((block
) == APR_BLOCK_READ
) ? "blocking" : "nonblocking",
141 ret
= ap_get_brigade(f
->next
, bb
, mode
, block
, readbytes
);
143 if (ret
== APR_SUCCESS
) {
144 for (b
= APR_BRIGADE_FIRST(bb
); b
!= APR_BRIGADE_SENTINEL(bb
); b
= APR_BUCKET_NEXT(b
)) {
148 ap_log_error(APLOG_MARK
, ptr
->loglevel
, 0, c
->base_server
,
149 "mod_dumpio: %s - %d", f
->frec
->name
, ret
) ;
155 static int dumpio_output_filter (ap_filter_t
*f
, apr_bucket_brigade
*bb
)
159 dumpio_conf_t
*ptr
= f
->ctx
;
161 ap_log_error(APLOG_MARK
, ptr
->loglevel
, 0, c
->base_server
, "mod_dumpio: %s", f
->frec
->name
) ;
163 for (b
= APR_BRIGADE_FIRST(bb
); b
!= APR_BRIGADE_SENTINEL(bb
); b
= APR_BUCKET_NEXT(b
)) {
165 * If we ever see an EOS, make sure to FLUSH.
167 if (APR_BUCKET_IS_EOS(b
)) {
168 apr_bucket
*flush
= apr_bucket_flush_create(f
->c
->bucket_alloc
);
169 APR_BUCKET_INSERT_BEFORE(b
, flush
);
174 return ap_pass_brigade(f
->next
, bb
) ;
177 static int dumpio_pre_conn(conn_rec
*c
, void *csd
)
181 ptr
= (dumpio_conf_t
*) ap_get_module_config(c
->base_server
->module_config
,
184 if (ptr
->enable_input
)
185 ap_add_input_filter("DUMPIO_IN", ptr
, NULL
, c
);
186 if (ptr
->enable_output
)
187 ap_add_output_filter("DUMPIO_OUT", ptr
, NULL
, c
);
191 static void dumpio_register_hooks(apr_pool_t
*p
)
194 * We know that SSL is CONNECTION + 5
196 ap_register_output_filter("DUMPIO_OUT", dumpio_output_filter
,
197 NULL
, AP_FTYPE_CONNECTION
+ 3) ;
199 ap_register_input_filter("DUMPIO_IN", dumpio_input_filter
,
200 NULL
, AP_FTYPE_CONNECTION
+ 3) ;
202 ap_hook_pre_connection(dumpio_pre_conn
, NULL
, NULL
, APR_HOOK_MIDDLE
);
205 static void *dumpio_create_sconfig(apr_pool_t
*p
, server_rec
*s
)
207 dumpio_conf_t
*ptr
= apr_pcalloc(p
, sizeof *ptr
);
208 ptr
->enable_input
= ptr
->enable_output
= 0;
209 ptr
->loglevel
= APLOG_DEBUG
;
213 static const char *dumpio_enable_input(cmd_parms
*cmd
, void *dummy
, int arg
)
216 (dumpio_conf_t
*) ap_get_module_config(cmd
->server
->module_config
,
219 ptr
->enable_input
= arg
;
223 static const char *dumpio_enable_output(cmd_parms
*cmd
, void *dummy
, int arg
)
226 (dumpio_conf_t
*) ap_get_module_config(cmd
->server
->module_config
,
229 ptr
->enable_output
= arg
;
233 static const char *set_loglevel(cmd_parms
*cmd
, void *dummy
, const char *arg
)
237 (dumpio_conf_t
*) ap_get_module_config(cmd
->server
->module_config
,
240 const char *err
= ap_check_cmd_context(cmd
,
241 NOT_IN_DIR_LOC_FILE
|NOT_IN_LIMIT
);
246 if ((str
= ap_getword_conf(cmd
->pool
, &arg
))) {
247 if (!strcasecmp(str
, "emerg")) {
248 ptr
->loglevel
= APLOG_EMERG
;
250 else if (!strcasecmp(str
, "alert")) {
251 ptr
->loglevel
= APLOG_ALERT
;
253 else if (!strcasecmp(str
, "crit")) {
254 ptr
->loglevel
= APLOG_CRIT
;
256 else if (!strcasecmp(str
, "error")) {
257 ptr
->loglevel
= APLOG_ERR
;
259 else if (!strcasecmp(str
, "warn")) {
260 ptr
->loglevel
= APLOG_WARNING
;
262 else if (!strcasecmp(str
, "notice")) {
263 ptr
->loglevel
= APLOG_NOTICE
;
265 else if (!strcasecmp(str
, "info")) {
266 ptr
->loglevel
= APLOG_INFO
;
268 else if (!strcasecmp(str
, "debug")) {
269 ptr
->loglevel
= APLOG_DEBUG
;
272 return "DumpIOLogLevel requires level keyword: one of "
273 "emerg/alert/crit/error/warn/notice/info/debug";
277 return "DumpIOLogLevel requires level keyword";
283 static const command_rec dumpio_cmds
[] = {
284 AP_INIT_FLAG("DumpIOInput", dumpio_enable_input
, NULL
,
285 RSRC_CONF
, "Enable I/O Dump on Input Data"),
286 AP_INIT_FLAG("DumpIOOutput", dumpio_enable_output
, NULL
,
287 RSRC_CONF
, "Enable I/O Dump on Output Data"),
288 AP_INIT_TAKE1("DumpIOLogLevel", set_loglevel
, NULL
, RSRC_CONF
,
289 "Level at which DumpIO info is logged"),
293 module AP_MODULE_DECLARE_DATA dumpio_module
= {
294 STANDARD20_MODULE_STUFF
,
297 dumpio_create_sconfig
,
300 dumpio_register_hooks