Files

291 lines
7.9 KiB
C

// SPDX-FileCopyrightText: 2024, Giovanni Dante Grazioli <wargio@libero.it>
// SPDX-License-Identifier: GPL-3.0-or-later
#include <ngx_config.h>
#include <naxsi_log.h>
#include <naxsi_macros.h>
void
naxsi_log_begin(naxsi_log_t* log, ngx_http_request_t* r, int as_json, int as_extensive)
{
log->buffer[0] = 0;
log->length = 0;
log->r = r;
log->rid = (const char*)naxsi_request_id(r);
log->as_json = as_json;
log->as_extensive = as_extensive;
}
static void
naxsi_log_as_text_flush(naxsi_log_t* log, ngx_int_t extend)
{
if (log->length > 0) {
ngx_http_naxsi_loc_conf_t* cf = ngx_http_get_module_loc_conf(log->r, ngx_http_naxsi_module);
ngx_log_t* ngx_log = cf->log ? cf->log : log->r->connection->log;
ngx_log_error(NGX_LOG_ERR,
ngx_log,
0,
"%s: %s",
log->as_extensive ? "NAXSI_EXLOG" : "NAXSI_FMT",
log->buffer);
}
if (extend) {
log->length = snprintf(log->buffer, NAXSI_LOG_MAX, "rid=%s", log->rid);
}
}
static void
naxsi_log_as_text_string_ex(naxsi_log_t* log,
const char* key,
size_t key_len,
u_char* val,
size_t val_len)
{
if (val_len >= (NGX_MAX_UINT32_VALUE / 4) - 1) {
val_len /= 4;
}
int truncate = 0;
while (val_len >= (NAXSI_LOG_MAX >> 1)) {
truncate = 1;
val_len /= 2;
}
size_t len = val_len + (2 * ngx_escape_uri(NULL, val, val_len, NGX_ESCAPE_URI_COMPONENT));
if (len >= NAXSI_LOG_MAX)
return;
// we always add 2 chars "&<key>="
if ((log->length + val_len + key_len + 2) >= NAXSI_LOG_MAX) {
naxsi_log_as_text_flush(log, 1);
}
log->length += snprintf(
log->buffer + log->length, NAXSI_LOG_MAX - log->length, log->length < 1 ? "%s=" : "&%s=", key);
ngx_escape_uri((u_char*)(log->buffer + log->length), val, val_len, NGX_ESCAPE_URI_COMPONENT);
log->length += len;
log->buffer[log->length] = 0;
if (truncate) {
log->length += snprintf(log->buffer + log->length, NAXSI_LOG_MAX - log->length, "...");
}
}
static void
naxsi_log_as_text_cstring_ex(naxsi_log_t* log,
const char* key,
size_t key_len,
const char* val,
size_t val_len)
{
// we always add 2 chars "&<key>=<value>"
if ((log->length + key_len + val_len + 2) >= NAXSI_LOG_MAX) {
naxsi_log_as_text_flush(log, 1);
}
log->length +=
snprintf(log->buffer + log->length, NAXSI_LOG_MAX - log->length, "&%s=%s", key, val);
}
static void
naxsi_log_as_text_number(naxsi_log_t* log, const char* key, ngx_int_t number)
{
int num_len = snprintf(NULL, 0, "&%s=%zu", key, number);
if ((log->length + num_len) >= NAXSI_LOG_MAX) {
naxsi_log_as_text_flush(log, 1);
}
log->length +=
snprintf(log->buffer + log->length, NAXSI_LOG_MAX - log->length, "&%s=%zu", key, number);
}
/* JSON functions */
static const char* json_hex = "0123456789abcdef";
static void
naxsi_escape_json_string(naxsi_log_t* log, const u_char* val, size_t length, int truncate)
{
char* out = log->buffer + log->length;
const char* end = log->buffer + NAXSI_LOG_MAX;
size_t max_len = end - out;
if (length > max_len) {
length = max_len;
}
*out++ = '"';
size_t i;
for (i = 0; i < length && out < end; i++) {
if (val[i] == '\n') {
// new line
break_if(out + 2 >= end);
*out++ = '\\';
*out++ = 'n';
} else if (val[i] == '\r') {
// return carriage
break_if(out + 2 >= end);
*out++ = '\\';
*out++ = 'r';
} else if (val[i] == '\\') {
// slash
break_if(out + 2 >= end);
*out++ = '\\';
*out++ = '\\';
} else if (val[i] == '\t') {
// tabulation
break_if(out + 2 >= end);
*out++ = '\\';
*out++ = 't';
} else if (val[i] == '"') {
// double quote
break_if(out + 2 >= end);
*out++ = '\\';
*out++ = '"';
} else if (val[i] == '\f') {
// form feed
break_if(out + 2 >= end);
*out++ = '\\';
*out++ = 'f';
} else if (val[i] == '\b') {
// backspace
break_if(out + 2 >= end);
*out++ = '\\';
*out++ = 'b';
} else if (!is_printable(val[i])) {
break_if(out + 4 >= end);
*out++ = '\\';
*out++ = 'u';
*out++ = '0';
*out++ = '0';
*out++ = json_hex[(val[i] >> 4) & 0x0f];
*out++ = json_hex[val[i] & 0x0f];
} else {
*out++ = val[i];
}
}
if (truncate && out + 4 < end) {
*out++ = '.';
*out++ = '.';
*out++ = '.';
}
if (out < end) {
*out++ = '"';
}
*out++ = 0;
log->length = out - log->buffer - 1;
}
static void
naxsi_log_as_json_flush(naxsi_log_t* log, ngx_int_t extend)
{
if (log->length > 0) {
ngx_http_naxsi_loc_conf_t* cf = ngx_http_get_module_loc_conf(log->r, ngx_http_naxsi_module);
ngx_log_t* ngx_log = cf->log ? cf->log : log->r->connection->log;
ngx_log_error(NGX_LOG_ERR, ngx_log, 0, "{%s}", log->buffer);
}
if (extend) {
log->length = snprintf(log->buffer, NAXSI_LOG_MAX, "\"rid\":\"%s\"", log->rid);
}
}
static void
naxsi_log_as_json_string_ex(naxsi_log_t* log,
const char* key,
size_t key_len,
u_char* val,
size_t val_len)
{
if (val_len >= NAXSI_LOG_MAX) {
val_len = NAXSI_LOG_MAX - (log->length + 6);
}
int truncate = 0;
while (val_len >= (NAXSI_LOG_MAX >> 1)) {
truncate = 1;
val_len /= 2;
}
// we always add 5 chars ,"<key>":"<value>"
if ((log->length + key_len + val_len + 6) >= NAXSI_LOG_MAX) {
naxsi_log_as_json_flush(log, 1);
}
if (log->length < 1) {
log->length += snprintf(log->buffer + log->length, NAXSI_LOG_MAX - log->length, "\"%s\":", key);
} else {
log->length +=
snprintf(log->buffer + log->length, NAXSI_LOG_MAX - log->length, ",\"%s\":", key);
}
naxsi_escape_json_string(log, val, val_len, truncate);
}
static void
naxsi_log_as_json_cstring_ex(naxsi_log_t* log,
const char* key,
size_t key_len,
const char* val,
size_t val_len)
{
// we always add 5 chars ,"<key>":"<value>"
if ((log->length + key_len + val_len + 6) >= NAXSI_LOG_MAX) {
naxsi_log_as_json_flush(log, 1);
}
log->length +=
snprintf(log->buffer + log->length, NAXSI_LOG_MAX - log->length, ",\"%s\":\"%s\"", key, val);
}
static void
naxsi_log_as_json_number(naxsi_log_t* log, const char* key, ngx_int_t number)
{
int num_len = snprintf(NULL, 0, "\"%s\":%zu", key, number);
if ((log->length + num_len) >= NAXSI_LOG_MAX) {
naxsi_log_as_text_flush(log, 1);
}
log->length +=
snprintf(log->buffer + log->length, NAXSI_LOG_MAX - log->length, ",\"%s\":%zu", key, number);
}
void
naxsi_log_end(naxsi_log_t* log)
{
if (log->as_json) {
naxsi_log_as_json_flush(log, 0);
} else {
naxsi_log_as_text_flush(log, 0);
}
}
void
naxsi_log_string_ex(naxsi_log_t* log, const char* key, size_t key_len, u_char* val, size_t val_len)
{
if (log->as_json) {
naxsi_log_as_json_string_ex(log, key, key_len, val, val_len);
} else {
naxsi_log_as_text_string_ex(log, key, key_len, val, val_len);
}
}
void
naxsi_log_cstring_ex(naxsi_log_t* log,
const char* key,
size_t key_len,
const char* val,
size_t val_len)
{
if (log->as_json) {
naxsi_log_as_json_cstring_ex(log, key, key_len, val, val_len);
} else {
naxsi_log_as_text_cstring_ex(log, key, key_len, val, val_len);
}
}
void
naxsi_log_number(naxsi_log_t* log, const char* key, ngx_int_t number)
{
if (log->as_json) {
naxsi_log_as_json_number(log, key, number);
} else {
naxsi_log_as_text_number(log, key, number);
}
}