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 * Originally written @ Covalent by Jim Jagielski 19 */ 20 21/* 22 * mod_dumpio.c: 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. 27 */ 28 29 30#include "httpd.h" 31#include "http_connection.h" 32#include "http_config.h" 33#include "http_core.h" 34#include "http_log.h" 35 36module AP_MODULE_DECLARE_DATA dumpio_module ; 37 38typedef struct dumpio_conf_t { 39 int enable_input; 40 int enable_output; 41 int loglevel; 42} dumpio_conf_t; 43 44/* 45 * Workhorse function: simply log to the current error_log 46 * info about the data in the bucket as well as the data itself 47 */ 48static void dumpit(ap_filter_t *f, apr_bucket *b) 49{ 50 conn_rec *c = f->c; 51 dumpio_conf_t *ptr = 52 (dumpio_conf_t *) ap_get_module_config(c->base_server->module_config, 53 &dumpio_module); 54 55 ap_log_error(APLOG_MARK, ptr->loglevel, 0, c->base_server, 56 "mod_dumpio: %s (%s-%s): %" APR_SIZE_T_FMT " bytes", 57 f->frec->name, 58 (APR_BUCKET_IS_METADATA(b)) ? "metadata" : "data", 59 b->type->name, 60 b->length) ; 61 62 if (!(APR_BUCKET_IS_METADATA(b))) { 63 const char *buf; 64 apr_size_t nbytes; 65 char *obuf; 66 if (apr_bucket_read(b, &buf, &nbytes, APR_BLOCK_READ) == APR_SUCCESS) { 67 if (nbytes) { 68 obuf = malloc(nbytes+1); /* use pool? */ 69 memcpy(obuf, buf, nbytes); 70#if APR_CHARSET_EBCDIC 71 ap_xlate_proto_from_ascii(obuf, nbytes); 72#endif 73 obuf[nbytes] = '\0'; 74 ap_log_error(APLOG_MARK, ptr->loglevel, 0, c->base_server, 75 "mod_dumpio: %s (%s-%s): %s", 76 f->frec->name, 77 (APR_BUCKET_IS_METADATA(b)) ? "metadata" : "data", 78 b->type->name, 79 obuf); 80 free(obuf); 81 } 82 } else { 83 ap_log_error(APLOG_MARK, ptr->loglevel, 0, c->base_server, 84 "mod_dumpio: %s (%s-%s): %s", 85 f->frec->name, 86 (APR_BUCKET_IS_METADATA(b)) ? "metadata" : "data", 87 b->type->name, 88 "error reading data"); 89 } 90 } 91} 92 93#define whichmode( mode ) \ 94 ( (( mode ) == AP_MODE_READBYTES) ? "readbytes" : \ 95 (( mode ) == AP_MODE_GETLINE) ? "getline" : \ 96 (( mode ) == AP_MODE_EATCRLF) ? "eatcrlf" : \ 97 (( mode ) == AP_MODE_SPECULATIVE) ? "speculative" : \ 98 (( mode ) == AP_MODE_EXHAUSTIVE) ? "exhaustive" : \ 99 (( mode ) == AP_MODE_INIT) ? "init" : "unknown" \ 100 ) 101 102static int dumpio_input_filter (ap_filter_t *f, apr_bucket_brigade *bb, 103 ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes) 104{ 105 106 apr_bucket *b; 107 apr_status_t ret; 108 conn_rec *c = f->c; 109 dumpio_conf_t *ptr = 110 (dumpio_conf_t *) ap_get_module_config(c->base_server->module_config, 111 &dumpio_module); 112 113 ap_log_error(APLOG_MARK, ptr->loglevel, 0, c->base_server, 114 "mod_dumpio: %s [%s-%s] %" APR_OFF_T_FMT " readbytes", 115 f->frec->name, 116 whichmode(mode), 117 ((block) == APR_BLOCK_READ) ? "blocking" : "nonblocking", 118 readbytes) ; 119 120 ret = ap_get_brigade(f->next, bb, mode, block, readbytes); 121 122 if (ret == APR_SUCCESS) { 123 for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) { 124 dumpit(f, b); 125 } 126 } else { 127 ap_log_error(APLOG_MARK, ptr->loglevel, 0, c->base_server, 128 "mod_dumpio: %s - %d", f->frec->name, ret) ; 129 return ret; 130 } 131 132 return APR_SUCCESS ; 133} 134 135static int dumpio_output_filter (ap_filter_t *f, apr_bucket_brigade *bb) 136{ 137 apr_bucket *b; 138 conn_rec *c = f->c; 139 dumpio_conf_t *ptr = 140 (dumpio_conf_t *) ap_get_module_config(c->base_server->module_config, 141 &dumpio_module); 142 143 ap_log_error(APLOG_MARK, ptr->loglevel, 0, c->base_server, "mod_dumpio: %s", f->frec->name) ; 144 145 for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) { 146 /* 147 * If we ever see an EOS, make sure to FLUSH. 148 */ 149 if (APR_BUCKET_IS_EOS(b)) { 150 apr_bucket *flush = apr_bucket_flush_create(f->c->bucket_alloc); 151 APR_BUCKET_INSERT_BEFORE(b, flush); 152 } 153 dumpit(f, b); 154 } 155 156 return ap_pass_brigade(f->next, bb) ; 157} 158 159static int dumpio_pre_conn(conn_rec *c, void *csd) 160{ 161 dumpio_conf_t *ptr = 162 (dumpio_conf_t *) ap_get_module_config(c->base_server->module_config, 163 &dumpio_module); 164 165 if (ptr->enable_input) 166 ap_add_input_filter("DUMPIO_IN", NULL, NULL, c); 167 if (ptr->enable_output) 168 ap_add_output_filter("DUMPIO_OUT", NULL, NULL, c); 169 return OK; 170} 171 172static void dumpio_register_hooks(apr_pool_t *p) 173{ 174/* 175 * We know that SSL is CONNECTION + 5 176 */ 177 ap_register_output_filter("DUMPIO_OUT", dumpio_output_filter, 178 NULL, AP_FTYPE_CONNECTION + 3) ; 179 180 ap_register_input_filter("DUMPIO_IN", dumpio_input_filter, 181 NULL, AP_FTYPE_CONNECTION + 3) ; 182 183 ap_hook_pre_connection(dumpio_pre_conn, NULL, NULL, APR_HOOK_MIDDLE); 184} 185 186static void *dumpio_create_sconfig(apr_pool_t *p, server_rec *s) 187{ 188 dumpio_conf_t *ptr = apr_pcalloc(p, sizeof *ptr); 189 ptr->enable_input = ptr->enable_output = 0; 190 ptr->loglevel = APLOG_DEBUG; 191 return ptr; 192} 193 194static const char *dumpio_enable_input(cmd_parms *cmd, void *dummy, int arg) 195{ 196 dumpio_conf_t *ptr = 197 (dumpio_conf_t *) ap_get_module_config(cmd->server->module_config, 198 &dumpio_module); 199 200 ptr->enable_input = arg; 201 return NULL; 202} 203 204static const char *dumpio_enable_output(cmd_parms *cmd, void *dummy, int arg) 205{ 206 dumpio_conf_t *ptr = 207 (dumpio_conf_t *) ap_get_module_config(cmd->server->module_config, 208 &dumpio_module); 209 210 ptr->enable_output = arg; 211 return NULL; 212} 213 214static const char *set_loglevel(cmd_parms *cmd, void *dummy, const char *arg) 215{ 216 char *str; 217 dumpio_conf_t *ptr = 218 (dumpio_conf_t *) ap_get_module_config(cmd->server->module_config, 219 &dumpio_module); 220 221 const char *err = ap_check_cmd_context(cmd, 222 NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT); 223 if (err != NULL) { 224 return err; 225 } 226 227 if ((str = ap_getword_conf(cmd->pool, &arg))) { 228 if (!strcasecmp(str, "emerg")) { 229 ptr->loglevel = APLOG_EMERG; 230 } 231 else if (!strcasecmp(str, "alert")) { 232 ptr->loglevel = APLOG_ALERT; 233 } 234 else if (!strcasecmp(str, "crit")) { 235 ptr->loglevel = APLOG_CRIT; 236 } 237 else if (!strcasecmp(str, "error")) { 238 ptr->loglevel = APLOG_ERR; 239 } 240 else if (!strcasecmp(str, "warn")) { 241 ptr->loglevel = APLOG_WARNING; 242 } 243 else if (!strcasecmp(str, "notice")) { 244 ptr->loglevel = APLOG_NOTICE; 245 } 246 else if (!strcasecmp(str, "info")) { 247 ptr->loglevel = APLOG_INFO; 248 } 249 else if (!strcasecmp(str, "debug")) { 250 ptr->loglevel = APLOG_DEBUG; 251 } 252 else { 253 return "DumpIOLogLevel requires level keyword: one of " 254 "emerg/alert/crit/error/warn/notice/info/debug"; 255 } 256 } 257 else { 258 return "DumpIOLogLevel requires level keyword"; 259 } 260 261 return NULL; 262} 263 264static const command_rec dumpio_cmds[] = { 265 AP_INIT_FLAG("DumpIOInput", dumpio_enable_input, NULL, 266 RSRC_CONF, "Enable I/O Dump on Input Data"), 267 AP_INIT_FLAG("DumpIOOutput", dumpio_enable_output, NULL, 268 RSRC_CONF, "Enable I/O Dump on Output Data"), 269 AP_INIT_TAKE1("DumpIOLogLevel", set_loglevel, NULL, RSRC_CONF, 270 "Level at which DumpIO info is logged"), 271 { NULL } 272}; 273 274module AP_MODULE_DECLARE_DATA dumpio_module = { 275 STANDARD20_MODULE_STUFF, 276 NULL, 277 NULL, 278 dumpio_create_sconfig, 279 NULL, 280 dumpio_cmds, 281 dumpio_register_hooks 282}; 283