Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,22 @@ Small apache module to log body content of request (POST / FORM data). Data are

Note: You can do the same with mod_security, use this when you want a quick and lightweight solution.

###Installation:
### Installation:
```
make
make install
```

###Configuration:
### Configuration:
In `httpd.conf` (optional):
* `DumpPostMaxSize 1024`: limit the size of a log entry to `1024` bytes (default value: `1048576` i.e. 1MB)
* `DumpPostHeaderAdd Cookie Content-Type`: add HTTP Header to log together with POST (default value: None)
* `DumpPostLogFile`: specify a custom file to write the log entry (other than
error log)
* `DumpPostLogFile`: specify a custom file to write the log entry (other than error log)
* `DumpPostLogBinary`: [On/Off] Save binary requests data in hex string format.
* `DumpPostFilter`: add a filter to match on first header of request, if no filter is present all traffic will be dumped.

###Requirement:
### Requirement:
* apxs:
* Ubuntu: `sudo apt-get install apache2-threaded-dev`, edit Makefile change
`apxs` to `apxs2`
* `LogLevel` of at least `Info` (not important if using DumpPostLogFile)
* `LogLevel` of at least `Info` (not important if using DumpPostLogFile) use `Debug` to investigate common problems with files and request sizes.
124 changes: 107 additions & 17 deletions mod_dumpost.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ static void dumpit(request_rec *r, apr_bucket *b, char *buf, apr_size_t *current
if (nbytes) {
DEBUG(r, "%ld bytes read from bucket for request %s", nbytes, r->the_request);
nbytes = min(nbytes, cfg->max_size - *current_size);
strncpy(buf, ibuf, nbytes);
//strncpy(buf, ibuf, nbytes);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this commented out code, we know from the history.

for (int kk = 0; kk < nbytes; kk++) buf[kk]=ibuf[kk];
*current_size += nbytes;
}
} else {
Expand All @@ -57,6 +58,17 @@ static void dumpit(request_rec *r, apr_bucket *b, char *buf, apr_size_t *current
}
}

void hexArrayToStr(unsigned char* info, unsigned int infoLength, char **buffer, unsigned int start) {
const char* pszNibbleToHex = {"0123456789ABCDEF"};
int nNibble, i;
for (i = 0; i < infoLength; i++) {
nNibble = info[i] >> 4;
buffer[0][2 * i + start] = pszNibbleToHex[nNibble];
nNibble = info[i] & 0x0F;
buffer[0][2 * i + 1 + start] = pszNibbleToHex[nNibble];
}
}

apr_status_t logit(ap_filter_t *f) {
request_state *state = f->ctx;
request_rec *r = f->r;
Expand All @@ -75,23 +87,58 @@ apr_status_t logit(ap_filter_t *f) {
apr_ctime(time, r->request_time);

// condition taken from mod_security
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why changing the alignment here ? and below..

char *ip = r->connection->client_ip;
#else
#else
char *ip = r->connection->remote_ip;
#endif

#endif
apr_size_t nbytes_written;
char *text = apr_psprintf(r->pool, "[%s] %s \"%s\" %s\n",time, ip, r->the_request, state->buffer);
apr_status_t rc = apr_file_write_full(state->fd, text, strlen(text), &nbytes_written);

if (rc != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "mod_dumpost: error while writing to log");
return rc;
}
apr_file_close(state->fd);
char *text = apr_psprintf(r->pool, "[%s] %s \"%s\" ",time, ip, r->the_request);

//Test buffer to search non ASCII characters
int not_bin=1;
for (int i = 0; i < state->log_size; i++)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you align similar to other code ? 2 spaces, and {
} style ?

{
//if (state->buffer[i]==0) {
if (state->buffer[i]<0x20 || state->buffer[i]>0x7E) {
if (state->buffer[i]=='\n') continue;
if (state->buffer[i]=='\r') continue;
not_bin=0;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So "not_bin" == 0 mean it is binary ? this is a weird name, how about initialize it at 0 and not_bin = 1 here then ?
But honestly, I think you should drop this check -- if the user specify "I want to get hex" then we should just hex it, the data doesn't matter..

break;
}
}

int jj=strlen(text);
dumpost_cfg_t *cfg =
(dumpost_cfg_t *) ap_get_module_config(f->r->per_dir_config, &dumpost_module);

apr_status_t rc;
if (not_bin==0 && cfg->log_bin) {
char *text2 = apr_palloc(r->pool, (state->log_size*2) + 2 + jj);
sprintf(text2, "%s", text);
hexArrayToStr((unsigned char*)state->buffer, state->log_size, &text2, jj);
jj=jj+(state->log_size*2);
text2[jj]='\n';
jj++;
text2[jj]='\0';
rc = apr_file_write_full(state->fd, text2, jj, &nbytes_written);
} else {
if (not_bin==0) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "mod_dumpost: binary output is disabled, not dumping this request.");
return APR_SUCCESS;
}
char *text2 = apr_palloc(r->pool, (state->log_size) + 2 + jj);
sprintf(text2, "%s%s\n", text, state->buffer);
jj=strlen(text2);
rc = apr_file_write_full(state->fd, text2, jj, &nbytes_written);
}
if (rc != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "mod_dumpost: error while writing to log");
return rc;
}
apr_file_close(state->fd);

}

return APR_SUCCESS;
}

Expand All @@ -102,9 +149,33 @@ apr_status_t dumpost_input_filter (ap_filter_t *f, apr_bucket_brigade *bb,
(dumpost_cfg_t *) ap_get_module_config(f->r->per_dir_config, &dumpost_module);

apr_bucket *b;
apr_status_t ret;
/* restoring state */
apr_status_t ret;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

align properly this and below.

//Default status dont filter anything, dump all requests
int filter_this = 0;
char **filters = (cfg->filters->nelts > 0)?(char **) cfg->filters->elts : NULL;
if (filters!=NULL) {
//If theres filters present in config, discard all request until check.
filter_this = 1;
int fi=0;
for (;fi<cfg->filters->nelts;fi++) {
if (strstr(f->r->the_request, filters[fi]) != NULL) {
//This request will be dumped.
filter_this = 0;
break;
}
}
}
//In case of filtering this request, exit filter.
if (filter_this==1) {
//Continue the filtering and exit filter before allocating memory for this request.
if ((ret = ap_get_brigade(f->next, bb, mode, block, readbytes)) != APR_SUCCESS)
return ret;
return APR_SUCCESS;
}

/* restoring state */
request_state *state = f->ctx;

if (state == NULL) {
/* create state if not yet */
apr_pool_t *mp;
Expand All @@ -130,7 +201,7 @@ apr_status_t dumpost_input_filter (ap_filter_t *f, apr_bucket_brigade *bb,
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r, "mod_dumpost: unable to open the log file: %s %s", cfg->file, buferr);
}
}
//This doesn't work for oldest apr versions but i can obtain same result in a cleaner way using macro APR_BUCKET_IS_EOS() in dumpit function to detect when data stream ends
//This doesn't work for oldest apr versions but i can obtain same result in a cleaner way using macro APR_BUCKET_IS_EOS() in dumpit function to detect when data stream ends
apr_pool_pre_cleanup_register(state->mp, f, (apr_status_t (*)(void *))logit);
}

Expand Down Expand Up @@ -193,6 +264,8 @@ static void *dumpost_create_dconfig(apr_pool_t *mp, char *path) {
cfg->headers = apr_array_make(mp, 0, sizeof(char *));
cfg->pool = mp;
cfg->file = 0;
cfg->log_bin = 0;
cfg->filters = apr_array_make(mp, 0, sizeof(char *));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alignment

return cfg;
}

Expand All @@ -216,10 +289,27 @@ static const char *dumpost_log_file(cmd_parms *cmd, void *_cfg, const char *arg
return NULL;
}

static const char *dumpost_log_binary(cmd_parms *cmd, void *_cfg, const char *arg ){
dumpost_cfg_t *cfg = (dumpost_cfg_t *) _cfg;
if (strstr(arg, "On") != NULL || strstr(arg, "1") != NULL)
cfg->log_bin = 1;
else
cfg->log_bin = 0;
return NULL;
}

static const char *dumpost_filter(cmd_parms *cmd, void *_cfg, const char *arg) {
dumpost_cfg_t *cfg = (dumpost_cfg_t *) _cfg;
*(const char**) apr_array_push(cfg->filters) = arg;
return NULL;
}

static const command_rec dumpost_cmds[] = {
AP_INIT_TAKE1("DumpPostMaxSize", dumpost_set_max_size, NULL, RSRC_CONF, "Set maximum data size"),
AP_INIT_ITERATE("DumpPostHeaderAdd", dumpost_add_header, NULL, RSRC_CONF, "Add header to log"),
AP_INIT_TAKE1("DumpPostLogFile", dumpost_log_file, NULL, RSRC_CONF, "A custom file to log to"),
AP_INIT_TAKE1("DumpPostLogBinary", dumpost_log_binary, NULL, RSRC_CONF, "Should log binary data (On/Off)"),
AP_INIT_ITERATE("DumpPostFilter", dumpost_filter, NULL, RSRC_CONF, "Add matches to filter by text in the fist header"),
{ NULL }
};

Expand Down
3 changes: 3 additions & 0 deletions mod_dumpost.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#ifndef __MOD_DUMPOST__
#define __MOD_DUMPOST__

#define LOG_IS_FULL -1
#define DEFAULT_MAX_SIZE 1024*1024
#define min(a,b) (a)<(b)?(a):(b)
#define CREATEMODE ( APR_UREAD | APR_UWRITE | APR_GREAD )
Expand All @@ -28,6 +29,8 @@ typedef struct dumpost_cfg_t {
apr_size_t max_size;
apr_array_header_t *headers;
char *file;
apr_array_header_t *filters;
int log_bin;
} dumpost_cfg_t;

typedef struct {
Expand Down