ASF Bugzilla – Attachment 33180 Details for
Bug 57045
dumpio output strings are missing
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
fixed mod_dumpio.c module
mod_dumpio.c (text/plain), 12.92 KB, created by
Kurtis Rader
on 2015-10-12 01:01:46 UTC
(
hide
)
Description:
fixed mod_dumpio.c module
Filename:
MIME Type:
Creator:
Kurtis Rader
Created:
2015-10-12 01:01:46 UTC
Size:
12.92 KB
patch
obsolete
>/* Licensed to the Apache Software Foundation (ASF) under one or more > * contributor license agreements. See the NOTICE file distributed with > * this work for additional information regarding copyright ownership. > * The ASF licenses this file to You under the Apache License, Version 2.0 > * (the "License"); you may not use this file except in compliance with > * the License. You may obtain a copy of the License at > * > * http://www.apache.org/licenses/LICENSE-2.0 > * > * Unless required by applicable law or agreed to in writing, software > * distributed under the License is distributed on an "AS IS" BASIS, > * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. > * See the License for the specific language governing permissions and > * limitations under the License. > */ > >/* > * Originally written @ Covalent by Jim Jagielski > * > * Modified October 2015 by Kurtis Rader (krader@skepticism.us) to correctly > * handle null (zero) bytes so they appear in the logged output. Fixing > * PR57045: https://bz.apache.org/bugzilla/show_bug.cgi?id=57045 > */ > >/* > * mod_dumpio.c: > * Think of this as a filter sniffer for Apache 2.x. It logs all filter data > * right before and after it goes out on the wire (BUT right before SSL > * encoded or after SSL decoded). It can produce a *huge* amount of data in > * the error log. > */ > >// TODO: Switch to the ap_log_data family of functions in a future revision. >// Preferably one where we can justify breaking backward compatibility of our >// output. Even better would be to fork this code and create a new mod_dumpio2 >// module that uses the new logging functions. > >#include "httpd.h" >#include "http_connection.h" >#include "http_protocol.h" >#include "http_config.h" >#include "http_core.h" >#include "http_log.h" >#include "apr_strings.h" >#include "apr_thread_proc.h" > >module AP_MODULE_DECLARE_DATA dumpio_module; > >typedef struct dumpio_conf_t { > int enable_input; > int enable_output; >} dumpio_conf_t; > >// Function ap_log_cerror cannot record more than MAX_STRING_LEN characters. >// Excluding the IP address and port number the message prefix is ~130 chars >// long. The longest IPv6 address:port pair is 46 chars (IPv4 is 21 chars). Add >// a fudge factor of 100 characters to help ensure we don't attempt to log more >// data than can be written in case the length of the message prefix is longer >// than we expect. Note that we take into account which bytes are printable and >// those that might be expanded to a hex encoded sequence (\xNN) when >// determining if we've exceeded the following limit. >#define DUMPIO_MAX_DATA_LEN (MAX_STRING_LEN - 130 - 46 - 100) > >// The dumpbuf* functions are the workhorses of this module. Log the data in >// the bucket to the current error log. It is needed to correctly handle null >// (i.e., zero) bytes in the data we want to log. It allows us to encode 0x00 >// and 0xff bytes into sequences of 0xff 0x01 (\xff\x01) and 0xff 0x02 >// (\xff\x02) respectively. This is necessary because the ap_log_cerror >// function and friends stop processing data (i.e., logging it) when a zero >// byte is seen because they only handle null terminated strings. >typedef struct { > unsigned char buf[DUMPIO_MAX_DATA_LEN]; > int idx; > int space_left; >} encoded_data_t; > >// Initialize the data structures so we can begin logging data we receive. >static void dumpbuf_init(encoded_data_t *encoded_data) >{ > encoded_data->space_left = DUMPIO_MAX_DATA_LEN; > encoded_data->idx = 0; >} > >// Log any data we've buffered since the most recent call to dumpbuf. >static void dumpbuf_end(encoded_data_t *encoded_data, const ap_filter_t *f, > const char *bucket_type) >{ > if (encoded_data->idx) { > ap_log_cerror(APLOG_MARK, APLOG_TRACE7, 0, f->c, > "mod_dumpio: %s (%s-%s): %.*s", f->frec->name, > "data", bucket_type, encoded_data->idx, > encoded_data->buf); > } >} > >// Dump the buffer to the error log in a form that allows reconstructing the >// original data. >static void dumpbuf(encoded_data_t *encoded_data, ap_filter_t *f, > const char *bucket_type, unsigned char *input_buf, > apr_size_t nbytes) >{ > for (unsigned char *c = input_buf; c < input_buf + nbytes; ++c) { > if (encoded_data->space_left < 8) { > ap_log_cerror(APLOG_MARK, APLOG_TRACE7, 0, f->c, > "mod_dumpio: %s (%s-%s): %.*s", f->frec->name, > "data", bucket_type, encoded_data->idx, > encoded_data->buf); > dumpbuf_init(encoded_data); > } > > // We could use isprint() but we don't want the overhead. Also, on OS X > // isprint() appears to treat non ASCII printable values as printable. > // This probably has something to do with Unicode and is another reason > // to not use isprint(). > if (*c >= 0x20 && *c <= 0x7e) { // this range needs one char in the log > encoded_data->buf[encoded_data->idx++] = *c; > encoded_data->space_left -= 1; > } > else if (*c == 0) { > encoded_data->buf[encoded_data->idx++] = 0xff; > encoded_data->buf[encoded_data->idx++] = 0x01; > encoded_data->space_left -= 8; > } > else if (*c == 0xff) { > encoded_data->buf[encoded_data->idx++] = 0xff; > encoded_data->buf[encoded_data->idx++] = 0x02; > encoded_data->space_left -= 8; > } > else { > // It isn't worth it to special-case the few control chars, such > // as \t, that get encoded into two characters. Assume they all > // get encoded into a four char \xNN sequence. > encoded_data->buf[encoded_data->idx++] = *c; > encoded_data->space_left -= 4; > } > } >} > >// Log info about the data in the bucket as well as the data itself to the >// current error log. >static void dumpit(encoded_data_t *encoded_data, ap_filter_t *f, apr_bucket *b, > dumpio_conf_t *ptr) >{ > int is_metadata = APR_BUCKET_IS_METADATA(b); > > // I've commented this out because in the normal course of events it > // doesn't tell us anything we need to know and takes measurable time to > // execute and wastes space in the log file. In my testing adding this log > // message increases the log file size by 2%. Uncomment it if you're > // debugging this module. > // > // ap_log_cerror(APLOG_MARK, APLOG_TRACE7, 0, f->c, > // "mod_dumpio: %s (%s-%s): %" APR_SIZE_T_FMT " bytes", > // f->frec->name, is_metadata ? "metadata" : "data", > // b->type->name, b->length); > if (is_metadata) { > return; > } > > apr_size_t nbytes; > unsigned char *input_buf; > apr_status_t rv = apr_bucket_read(b, (const char **)&input_buf, &nbytes, > APR_BLOCK_READ); > if (rv != APR_SUCCESS) { > ap_log_cerror(APLOG_MARK, APLOG_TRACE7, rv, f->c, > "mod_dumpio: %s (%s-%s): %s", f->frec->name, > "data", b->type->name, "error reading data"); > return; > } > >#if !APR_CHARSET_EBCDIC > // The common case: ASCII charset. > dumpbuf(encoded_data, f, b->type->name, input_buf, nbytes); >#else > // The less common case: EBCDIC charset. This obviously screws up any > // binary data in the buffer such as a zip archive in a POST request. But > // people running on an IBM mainframe probably don't care since the > // original data is almost certainly unusable in their environment. It's > // better that we make the incoming and ougoing ASCII text easily readable > // in the error log on systems using EBCDIC. > unsigned char xlatebuf[MAX_STRING_LEN]; > while (nbytes) { > apr_size_t logbytes = nbytes; > if (logbytes > MAX_STRING_LEN) { > logbytes = MAX_STRING_LEN; > } > memcpy(xlatebuf, input_buf, logbytes); > ap_xlate_proto_from_ascii(xlatebuf, logbytes); > dumpbuf(encoded_data, f, b->type->name, xlatebuf, logbytes); > nbytes -= logbytes > } >#endif >} > >#define whichmode( mode ) \ > ( (( mode ) == AP_MODE_READBYTES) ? "readbytes" : \ > (( mode ) == AP_MODE_GETLINE) ? "getline" : \ > (( mode ) == AP_MODE_EATCRLF) ? "eatcrlf" : \ > (( mode ) == AP_MODE_SPECULATIVE) ? "speculative" : \ > (( mode ) == AP_MODE_EXHAUSTIVE) ? "exhaustive" : \ > (( mode ) == AP_MODE_INIT) ? "init" : "unknown" \ > ) > >static int dumpio_input_filter( > ap_filter_t *f, apr_bucket_brigade *bb, ap_input_mode_t mode, > apr_read_type_e block, apr_off_t readbytes) >{ > apr_bucket *b; > apr_status_t ret; > dumpio_conf_t *ptr = f->ctx; > > ap_log_cerror(APLOG_MARK, APLOG_TRACE7, 0, f->c, > "mod_dumpio: %s [%s-%s] %" APR_OFF_T_FMT " readbytes", > f->frec->name, whichmode(mode), > (block == APR_BLOCK_READ) ? "blocking" : "nonblocking", > readbytes); > > ret = ap_get_brigade(f->next, bb, mode, block, readbytes); > if (ret != APR_SUCCESS) { > ap_log_cerror(APLOG_MARK, APLOG_TRACE7, 0, f->c, > "mod_dumpio: %s - %d", f->frec->name, ret); > return ret; > } > > encoded_data_t encoded_data; > dumpbuf_init(&encoded_data); > apr_bucket *first_b = APR_BRIGADE_FIRST(bb); > apr_bucket *sentinel_b = APR_BRIGADE_SENTINEL(bb); > for (b = first_b; b != sentinel_b; b = APR_BUCKET_NEXT(b)) { > dumpit(&encoded_data, f, b, ptr); > } > dumpbuf_end(&encoded_data, f, first_b->type->name); > return APR_SUCCESS; >} > >static int dumpio_output_filter(ap_filter_t *f, apr_bucket_brigade *bb) >{ > apr_bucket *b; > dumpio_conf_t *ptr = f->ctx; > > ap_log_cerror(APLOG_MARK, APLOG_TRACE7, 0, f->c, "mod_dumpio: %s", > f->frec->name); > > encoded_data_t encoded_data; > dumpbuf_init(&encoded_data); > apr_bucket *first_b = APR_BRIGADE_FIRST(bb); > apr_bucket *sentinel_b = APR_BRIGADE_SENTINEL(bb); > for (b = first_b; b != sentinel_b; b = APR_BUCKET_NEXT(b)) { > if (APR_BUCKET_IS_EOS(b)) { // if we ever see an EOS, make sure to FLUSH > apr_bucket *flush = apr_bucket_flush_create(f->c->bucket_alloc); > APR_BUCKET_INSERT_BEFORE(b, flush); > } > dumpit(&encoded_data, f, b, ptr); > } > dumpbuf_end(&encoded_data, f, first_b->type->name); > > return ap_pass_brigade(f->next, bb); >} > >static int dumpio_pre_conn(conn_rec *c, void *csd) >{ > dumpio_conf_t *ptr = (dumpio_conf_t *)ap_get_module_config( > c->base_server->module_config, &dumpio_module); > > if (ptr->enable_input) > ap_add_input_filter("DUMPIO_IN", ptr, NULL, c); > if (ptr->enable_output) > ap_add_output_filter("DUMPIO_OUT", ptr, NULL, c); > > return OK; >} > >static void pre_read_request(request_rec *r, conn_rec *c) >{ > ap_log_cerror(APLOG_MARK, APLOG_TRACE7, 0, c, > "mod_dumpio: new request"); >} > >static void dumpio_register_hooks(apr_pool_t *p) >{ > // We know that the SSL code inserts itself at AP_FTYPE_CONNECTION + 5 and > // AP_FTYPE_CONNECTION + 4 (see the ssl_io_filter_register() function in > // modules/ssl/ssl_engine_io.c). We want to be inserted just ahead of that > // module so we use "+ 3" to ensure we're called just before the SSL filter > // but hopefully after anything else that might alter the data. > ap_register_output_filter("DUMPIO_OUT", dumpio_output_filter, > NULL, AP_FTYPE_CONNECTION + 3); > ap_register_input_filter("DUMPIO_IN", dumpio_input_filter, > NULL, AP_FTYPE_CONNECTION + 3); > ap_hook_pre_connection(dumpio_pre_conn, NULL, NULL, APR_HOOK_MIDDLE); > ap_hook_pre_read_request(pre_read_request, NULL, NULL, APR_HOOK_MIDDLE); >} > >static void *dumpio_create_sconfig(apr_pool_t *p, server_rec *s) >{ > dumpio_conf_t *ptr = apr_pcalloc(p, sizeof *ptr); > ptr->enable_input = 0; > ptr->enable_output = 0; > return ptr; >} > >static const char *dumpio_enable_input(cmd_parms *cmd, void *dummy, int arg) >{ > dumpio_conf_t *ptr = ap_get_module_config(cmd->server->module_config, > &dumpio_module); > ptr->enable_input = arg; > return NULL; >} > >static const char *dumpio_enable_output(cmd_parms *cmd, void *dummy, int arg) >{ > dumpio_conf_t *ptr = ap_get_module_config(cmd->server->module_config, > &dumpio_module); > ptr->enable_output = arg; > return NULL; >} > >static const command_rec dumpio_cmds[] = { > AP_INIT_FLAG("DumpIOInput", dumpio_enable_input, NULL, > RSRC_CONF, "Enable I/O Dump on Input Data"), > AP_INIT_FLAG("DumpIOOutput", dumpio_enable_output, NULL, > RSRC_CONF, "Enable I/O Dump on Output Data"), > { NULL } >}; > >AP_DECLARE_MODULE(dumpio) = { > STANDARD20_MODULE_STUFF, > NULL, // create per-dir config structures > NULL, // merge per-dir config structures > dumpio_create_sconfig, // create per-server config structures > NULL, // merge per-server config structures > dumpio_cmds, // table of config file commands > dumpio_register_hooks // register hooks >};
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 57045
:
33162
|
33163
|
33164
|
33179
| 33180 |
33181