parser.c (goaccess-1.7) | : | parser.c (goaccess-1.7.1) | ||
---|---|---|---|---|
/** | /** | |||
* parser.c -- web log parsing | * parser.c -- web log parsing | |||
* ______ ___ | * ______ ___ | |||
* / ____/___ / | _____________ __________ | * / ____/___ / | _____________ __________ | |||
* / / __/ __ \/ /| |/ ___/ ___/ _ \/ ___/ ___/ | * / / __/ __ \/ /| |/ ___/ ___/ _ \/ ___/ ___/ | |||
* / /_/ / /_/ / ___ / /__/ /__/ __(__ |__ ) | * / /_/ / /_/ / ___ / /__/ /__/ __(__ |__ ) | |||
* \____/\____/_/ |_\___/\___/\___/____/____/ | * \____/\____/_/ |_\___/\___/\___/____/____/ | |||
* | * | |||
* The MIT License (MIT) | * The MIT License (MIT) | |||
* Copyright (c) 2009-2022 Gerardo Orellana <hello @ goaccess.io> | * Copyright (c) 2009-2023 Gerardo Orellana <hello @ goaccess.io> | |||
* | * | |||
* Permission is hereby granted, free of charge, to any person obtaining a copy | * Permission is hereby granted, free of charge, to any person obtaining a copy | |||
* of this software and associated documentation files (the "Software"), to deal | * of this software and associated documentation files (the "Software"), to deal | |||
* in the Software without restriction, including without limitation the rights | * in the Software without restriction, including without limitation the rights | |||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
* copies of the Software, and to permit persons to whom the Software is | * copies of the Software, and to permit persons to whom the Software is | |||
* furnished to do so, subject to the following conditions: | * furnished to do so, subject to the following conditions: | |||
* | * | |||
* The above copyright notice and this permission notice shall be included in al l | * The above copyright notice and this permission notice shall be included in al l | |||
* copies or substantial portions of the Software. | * copies or substantial portions of the Software. | |||
skipping to change at line 112 | skipping to change at line 112 | |||
/* Reset an instance of GLog structure. */ | /* Reset an instance of GLog structure. */ | |||
void | void | |||
reset_struct (Logs * logs) { | reset_struct (Logs * logs) { | |||
int i = 0; | int i = 0; | |||
for (i = 0; i < logs->size; ++i) | for (i = 0; i < logs->size; ++i) | |||
logs->glog[i].invalid = logs->glog[i].processed = 0; | logs->glog[i].invalid = logs->glog[i].processed = 0; | |||
} | } | |||
/* Allocate memory for a new Logs and GLog instance. | ||||
* | ||||
* On success, the newly allocated Logs is returned . */ | ||||
Logs * | ||||
new_logs (int size) { | ||||
Logs *logs = xmalloc (sizeof (*logs)); | ||||
memset (logs, 0, sizeof *logs); | ||||
logs->glog = xcalloc (size, sizeof (GLog)); | ||||
logs->size = size; | ||||
logs->idx = 0; | ||||
return logs; | ||||
} | ||||
/* Allocate, initialize and add the given filename to our logs structure. | ||||
* | ||||
* On error, 1 is returned. | ||||
* On success, the given filename is added to the Logs structure and 0 is | ||||
* returned. */ | ||||
int | ||||
set_glog (Logs * logs, const char *filename) { | ||||
GLog *tmp = NULL, *glog = NULL; | ||||
int newlen = 0; | ||||
char const *err; | ||||
char *fvh = NULL, *fn = (char *) filename; | ||||
if (logs->size - 1 < logs->idx) { | ||||
newlen = logs->size + 1; | ||||
if (!(tmp = xrealloc (logs->glog, newlen * sizeof (GLog)))) | ||||
return ERR_LOG_REALLOC_FAILURE; | ||||
logs->glog = tmp; | ||||
memset (logs->glog + logs->idx, 0, (logs->idx + 1 - logs->size) * sizeof *lo | ||||
gs->glog); | ||||
logs->size = newlen; | ||||
} | ||||
glog = logs->glog; | ||||
glog[logs->idx].errors = xcalloc (MAX_LOG_ERRORS, sizeof (char *)); | ||||
glog[logs->idx].filename = xstrdup (fn); | ||||
glog[logs->idx].fname = xstrdup (basename (fn)); | ||||
if (!glog->pipe && conf.fname_as_vhost) { | ||||
if (!(fvh = regex_extract_string (glog[logs->idx].fname, conf.fname_as_vhost | ||||
, 1, &err))) | ||||
FATAL ("%s %s[%s]", err, glog[logs->idx].fname, conf.fname_as_vhost); | ||||
glog[logs->idx].fname_as_vhost = fvh; | ||||
} | ||||
logs->processed = &(glog[logs->idx].processed); | ||||
logs->filename = glog[logs->idx].filename; | ||||
logs->idx++; | ||||
return 0; | ||||
} | ||||
/* Ensure the given filename is part of our original list of files. | ||||
* | ||||
* On error, 1 is returned. | ||||
* On success, the given filename is added to the Logs structure and 0 is | ||||
* returned. */ | ||||
int | ||||
set_log (Logs * logs, const char *value) { | ||||
if (str_inarray (value, conf.filenames, conf.filenames_idx) < 0) | ||||
return ERR_LOG_NOT_FOUND; | ||||
return set_glog (logs, value); | ||||
} | ||||
/* Allocate memory for a new set of Logs including a GLog instance. | /* Allocate memory for a new set of Logs including a GLog instance. | |||
* | * | |||
* On success, the newly allocated Logs is returned . */ | * On success, the newly allocated Logs is returned . */ | |||
Logs * | Logs * | |||
init_logs (int size) { | init_logs (int size) { | |||
Logs *logs = NULL; | Logs *logs = NULL; | |||
GLog *glog = NULL; | GLog *glog = NULL; | |||
char *fvh = NULL; | int i = 0, ret = 0; | |||
char const *err; | ||||
int i = 0; | ||||
/* if no logs no a pipe nor restoring, nothing to do then */ | /* if no logs no a pipe nor restoring, nothing to do then */ | |||
if (!size && !conf.restore) | if (!size && !conf.restore) | |||
return NULL; | return NULL; | |||
/* If no logs nor a pipe but restoring, we still need an minimal instance of | /* If no logs nor a pipe but restoring, we still need an minimal instance of | |||
* logs and a glog */ | * logs and a glog */ | |||
logs = xcalloc (1, sizeof (*logs)); | ||||
if (!size) { | if (!size) { | |||
logs = xcalloc (1, sizeof (*logs)); | ||||
logs->glog = xcalloc (1, sizeof (*glog)); | logs->glog = xcalloc (1, sizeof (*glog)); | |||
logs->processed = &(logs->glog[0].processed); | logs->processed = &(logs->glog[0].processed); | |||
return logs; | return logs; | |||
} | } | |||
glog = xcalloc (size, sizeof (*glog)); | logs = new_logs (size); | |||
for (i = 0; i < size; ++i) { | logs->size = size; | |||
glog[i].errors = xcalloc (MAX_LOG_ERRORS, sizeof (char *)); | ||||
glog[i].filename = xstrdup (conf.filenames[i]); | ||||
glog[i].fname = xstrdup (basename (glog[i].filename)); | ||||
if (!glog->pipe && conf.fname_as_vhost) { | ||||
if (!(fvh = regex_extract_string (glog[i].fname, conf.fname_as_vhost, 1, & | ||||
err))) | ||||
FATAL ("%s %s[%s]", err, glog[i].fname, conf.fname_as_vhost); | ||||
glog[i].fname_as_vhost = fvh; | ||||
} | ||||
logs->processed = &(glog[i].processed); | for (i = 0; i < size; ++i) { | |||
logs->filename = glog[i].filename; | if ((ret = set_log (logs, conf.filenames[i]))) | |||
FATAL ("%s\n", ERR_LOG_NOT_FOUND_MSG); | ||||
} | } | |||
logs->glog = glog; | ||||
logs->size = size; | ||||
return logs; | return logs; | |||
} | } | |||
/* Free all log errors stored during parsing. */ | /* Free all log errors stored during parsing. */ | |||
void | void | |||
free_logerrors (GLog * glog) { | free_logerrors (GLog * glog) { | |||
int i; | int i; | |||
if (!glog->log_erridx) | if (!glog->log_erridx) | |||
return; | return; | |||
skipping to change at line 787 | skipping to change at line 843 | |||
* of it. | * of it. | |||
* | * | |||
* On success, a malloc'd error message is assigned to the log | * On success, a malloc'd error message is assigned to the log | |||
* structure and 1 is returned. */ | * structure and 1 is returned. */ | |||
static int | static int | |||
spec_err (GLogItem * logitem, int code, const char spec, const char *tkn) { | spec_err (GLogItem * logitem, int code, const char spec, const char *tkn) { | |||
char *err = NULL; | char *err = NULL; | |||
const char *fmt = NULL; | const char *fmt = NULL; | |||
switch (code) { | switch (code) { | |||
case SPEC_TOKN_NUL: | case ERR_SPEC_TOKN_NUL: | |||
fmt = "Token for '%%%c' specifier is NULL."; | fmt = "Token for '%%%c' specifier is NULL."; | |||
err = xmalloc (snprintf (NULL, 0, fmt, spec) + 1); | err = xmalloc (snprintf (NULL, 0, fmt, spec) + 1); | |||
sprintf (err, fmt, spec); | sprintf (err, fmt, spec); | |||
break; | break; | |||
case SPEC_TOKN_INV: | case ERR_SPEC_TOKN_INV: | |||
fmt = "Token '%s' doesn't match specifier '%%%c'"; | fmt = "Token '%s' doesn't match specifier '%%%c'"; | |||
err = xmalloc (snprintf (NULL, 0, fmt, (tkn ? tkn : "-"), spec) + 1); | err = xmalloc (snprintf (NULL, 0, fmt, (tkn ? tkn : "-"), spec) + 1); | |||
sprintf (err, fmt, (tkn ? tkn : "-"), spec); | sprintf (err, fmt, (tkn ? tkn : "-"), spec); | |||
break; | break; | |||
case SPEC_SFMT_MIS: | case ERR_SPEC_SFMT_MIS: | |||
fmt = "Missing braces '%s' and ignore chars for specifier '%%%c'"; | fmt = "Missing braces '%s' and ignore chars for specifier '%%%c'"; | |||
err = xmalloc (snprintf (NULL, 0, fmt, (tkn ? tkn : "-"), spec) + 1); | err = xmalloc (snprintf (NULL, 0, fmt, (tkn ? tkn : "-"), spec) + 1); | |||
sprintf (err, fmt, (tkn ? tkn : "-"), spec); | sprintf (err, fmt, (tkn ? tkn : "-"), spec); | |||
break; | break; | |||
case SPEC_LINE_INV: | case ERR_SPEC_LINE_INV: | |||
fmt = "Incompatible format due to early parsed line ending '\\0'."; | fmt = "Incompatible format due to early parsed line ending '\\0'."; | |||
err = xmalloc (snprintf (NULL, 0, fmt, (tkn ? tkn : "-")) + 1); | err = xmalloc (snprintf (NULL, 0, fmt, (tkn ? tkn : "-")) + 1); | |||
sprintf (err, fmt, (tkn ? tkn : "-")); | sprintf (err, fmt, (tkn ? tkn : "-")); | |||
break; | break; | |||
} | } | |||
logitem->errstr = err; | logitem->errstr = err; | |||
return code; | return code; | |||
} | } | |||
skipping to change at line 880 | skipping to change at line 936 | |||
/* Attempt to parse date format containing spaces, | /* Attempt to parse date format containing spaces, | |||
* i.e., syslog date format (Jul\s15, Nov\s\s2). | * i.e., syslog date format (Jul\s15, Nov\s\s2). | |||
* Note that it's possible a date could contain some padding, e.g., | * Note that it's possible a date could contain some padding, e.g., | |||
* Dec\s\s2 vs Nov\s22, so we attempt to take that into consideration by loo king | * Dec\s\s2 vs Nov\s22, so we attempt to take that into consideration by loo king | |||
* ahead the log string and counting the # of spaces until we find an alphan um char. */ | * ahead the log string and counting the # of spaces until we find an alphan um char. */ | |||
if ((fmtspcs = count_matches (dfmt, ' ')) && (pch = strchr (*str, ' '))) | if ((fmtspcs = count_matches (dfmt, ' ')) && (pch = strchr (*str, ' '))) | |||
dspc = find_alpha_count (pch); | dspc = find_alpha_count (pch); | |||
if (!(tkn = parse_string (&(*str), end, MAX (dspc, fmtspcs) + 1))) | if (!(tkn = parse_string (&(*str), end, MAX (dspc, fmtspcs) + 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
if (str_to_time (tkn, dfmt, &tm, 1) != 0 || set_date (&logitem->date, tm) != 0) { | if (str_to_time (tkn, dfmt, &tm, 1) != 0 || set_date (&logitem->date, tm) != 0) { | |||
spec_err (logitem, SPEC_TOKN_INV, *p, tkn); | spec_err (logitem, ERR_SPEC_TOKN_INV, *p, tkn); | |||
free (tkn); | free (tkn); | |||
return 1; | return 1; | |||
} | } | |||
set_numeric_date (&logitem->numdate, logitem->date); | set_numeric_date (&logitem->numdate, logitem->date); | |||
set_tm_dt_logitem (logitem, tm); | set_tm_dt_logitem (logitem, tm); | |||
free (tkn); | free (tkn); | |||
break; | break; | |||
/* time */ | /* time */ | |||
case 't': | case 't': | |||
if (logitem->time) | if (logitem->time) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
if (str_to_time (tkn, tfmt, &tm, 1) != 0 || set_time (&logitem->time, tm) != 0) { | if (str_to_time (tkn, tfmt, &tm, 1) != 0 || set_time (&logitem->time, tm) != 0) { | |||
spec_err (logitem, SPEC_TOKN_INV, *p, tkn); | spec_err (logitem, ERR_SPEC_TOKN_INV, *p, tkn); | |||
free (tkn); | free (tkn); | |||
return 1; | return 1; | |||
} | } | |||
set_tm_tm_logitem (logitem, tm); | set_tm_tm_logitem (logitem, tm); | |||
free (tkn); | free (tkn); | |||
break; | break; | |||
/* date/time as decimal, i.e., timestamps, ms/us */ | /* date/time as decimal, i.e., timestamps, ms/us */ | |||
case 'x': | case 'x': | |||
if (logitem->time && logitem->date) | if (logitem->time && logitem->date) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
if (str_to_time (tkn, tfmt, &tm, 1) != 0 || set_date (&logitem->date, tm) != 0 || | if (str_to_time (tkn, tfmt, &tm, 1) != 0 || set_date (&logitem->date, tm) != 0 || | |||
set_time (&logitem->time, tm) != 0) { | set_time (&logitem->time, tm) != 0) { | |||
spec_err (logitem, SPEC_TOKN_INV, *p, tkn); | spec_err (logitem, ERR_SPEC_TOKN_INV, *p, tkn); | |||
free (tkn); | free (tkn); | |||
return 1; | return 1; | |||
} | } | |||
set_numeric_date (&logitem->numdate, logitem->date); | set_numeric_date (&logitem->numdate, logitem->date); | |||
set_tm_dt_logitem (logitem, tm); | set_tm_dt_logitem (logitem, tm); | |||
set_tm_tm_logitem (logitem, tm); | set_tm_tm_logitem (logitem, tm); | |||
free (tkn); | free (tkn); | |||
break; | break; | |||
/* Virtual Host */ | /* Virtual Host */ | |||
case 'v': | case 'v': | |||
if (logitem->vhost) | if (logitem->vhost) | |||
return 0; | return 0; | |||
tkn = parse_string (&(*str), end, 1); | tkn = parse_string (&(*str), end, 1); | |||
if (tkn == NULL) | if (tkn == NULL) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
logitem->vhost = tkn; | logitem->vhost = tkn; | |||
break; | break; | |||
/* remote user */ | /* remote user */ | |||
case 'e': | case 'e': | |||
if (logitem->userid) | if (logitem->userid) | |||
return 0; | return 0; | |||
tkn = parse_string (&(*str), end, 1); | tkn = parse_string (&(*str), end, 1); | |||
if (tkn == NULL) | if (tkn == NULL) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
logitem->userid = tkn; | logitem->userid = tkn; | |||
break; | break; | |||
/* cache status */ | /* cache status */ | |||
case 'C': | case 'C': | |||
if (logitem->cache_status) | if (logitem->cache_status) | |||
return 0; | return 0; | |||
tkn = parse_string (&(*str), end, 1); | tkn = parse_string (&(*str), end, 1); | |||
if (tkn == NULL) | if (tkn == NULL) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
if (is_cache_hit (tkn)) | if (is_cache_hit (tkn)) | |||
logitem->cache_status = tkn; | logitem->cache_status = tkn; | |||
else | else | |||
free (tkn); | free (tkn); | |||
break; | break; | |||
/* remote hostname (IP only) */ | /* remote hostname (IP only) */ | |||
case 'h': | case 'h': | |||
if (logitem->host) | if (logitem->host) | |||
return 0; | return 0; | |||
/* per https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2 */ | /* per https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2 */ | |||
/* square brackets are possible */ | /* square brackets are possible */ | |||
if (*str[0] == '[' && (*str += 1) && **str) | if (*str[0] == '[' && (*str += 1) && **str) | |||
end = "]"; | end = "]"; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
if (!conf.no_ip_validation && invalid_ipaddr (tkn, &logitem->type_ip)) { | if (!conf.no_ip_validation && invalid_ipaddr (tkn, &logitem->type_ip)) { | |||
spec_err (logitem, SPEC_TOKN_INV, *p, tkn); | spec_err (logitem, ERR_SPEC_TOKN_INV, *p, tkn); | |||
free (tkn); | free (tkn); | |||
return 1; | return 1; | |||
} | } | |||
/* require a valid host token (e.g., ord38s18-in-f14.1e100.net) even when we 're | /* require a valid host token (e.g., ord38s18-in-f14.1e100.net) even when we 're | |||
* not validating the IP */ | * not validating the IP */ | |||
if (conf.no_ip_validation && *tkn == '\0') { | if (conf.no_ip_validation && *tkn == '\0') { | |||
spec_err (logitem, SPEC_TOKN_INV, *p, tkn); | spec_err (logitem, ERR_SPEC_TOKN_INV, *p, tkn); | |||
free (tkn); | free (tkn); | |||
return 1; | return 1; | |||
} | } | |||
logitem->host = tkn; | logitem->host = tkn; | |||
break; | break; | |||
/* request method */ | /* request method */ | |||
case 'm': | case 'm': | |||
if (logitem->method) | if (logitem->method) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
{ | { | |||
const char *meth = NULL; | const char *meth = NULL; | |||
if (!(meth = extract_method (tkn))) { | if (!(meth = extract_method (tkn))) { | |||
spec_err (logitem, SPEC_TOKN_INV, *p, tkn); | spec_err (logitem, ERR_SPEC_TOKN_INV, *p, tkn); | |||
free (tkn); | free (tkn); | |||
return 1; | return 1; | |||
} | } | |||
logitem->method = xstrdup (meth); | logitem->method = xstrdup (meth); | |||
free (tkn); | free (tkn); | |||
} | } | |||
break; | break; | |||
/* request not including method or protocol */ | /* request not including method or protocol */ | |||
case 'U': | case 'U': | |||
if (logitem->req) | if (logitem->req) | |||
return 0; | return 0; | |||
tkn = parse_string (&(*str), end, 1); | tkn = parse_string (&(*str), end, 1); | |||
if (tkn == NULL || *tkn == '\0') { | if (tkn == NULL || *tkn == '\0') { | |||
free (tkn); | free (tkn); | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
} | } | |||
if ((logitem->req = decode_url (tkn)) == NULL) { | if ((logitem->req = decode_url (tkn)) == NULL) { | |||
spec_err (logitem, SPEC_TOKN_INV, *p, tkn); | spec_err (logitem, ERR_SPEC_TOKN_INV, *p, tkn); | |||
free (tkn); | free (tkn); | |||
return 1; | return 1; | |||
} | } | |||
free (tkn); | free (tkn); | |||
break; | break; | |||
/* query string alone, e.g., ?param=goaccess&tbm=shop */ | /* query string alone, e.g., ?param=goaccess&tbm=shop */ | |||
case 'q': | case 'q': | |||
if (logitem->qstr) | if (logitem->qstr) | |||
return 0; | return 0; | |||
tkn = parse_string (&(*str), end, 1); | tkn = parse_string (&(*str), end, 1); | |||
if (tkn == NULL || *tkn == '\0') { | if (tkn == NULL || *tkn == '\0') { | |||
free (tkn); | free (tkn); | |||
return 0; | return 0; | |||
} | } | |||
if ((logitem->qstr = decode_url (tkn)) == NULL) { | if ((logitem->qstr = decode_url (tkn)) == NULL) { | |||
spec_err (logitem, SPEC_TOKN_INV, *p, tkn); | spec_err (logitem, ERR_SPEC_TOKN_INV, *p, tkn); | |||
free (tkn); | free (tkn); | |||
return 1; | return 1; | |||
} | } | |||
free (tkn); | free (tkn); | |||
break; | break; | |||
/* request protocol */ | /* request protocol */ | |||
case 'H': | case 'H': | |||
if (logitem->protocol) | if (logitem->protocol) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
{ | { | |||
const char *proto = NULL; | const char *proto = NULL; | |||
if (!(proto = extract_protocol (tkn))) { | if (!(proto = extract_protocol (tkn))) { | |||
spec_err (logitem, SPEC_TOKN_INV, *p, tkn); | spec_err (logitem, ERR_SPEC_TOKN_INV, *p, tkn); | |||
free (tkn); | free (tkn); | |||
return 1; | return 1; | |||
} | } | |||
logitem->protocol = xstrdup (proto); | logitem->protocol = xstrdup (proto); | |||
free (tkn); | free (tkn); | |||
} | } | |||
break; | break; | |||
/* request, including method + protocol */ | /* request, including method + protocol */ | |||
case 'r': | case 'r': | |||
if (logitem->req) | if (logitem->req) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
logitem->req = parse_req (tkn, &logitem->method, &logitem->protocol); | logitem->req = parse_req (tkn, &logitem->method, &logitem->protocol); | |||
free (tkn); | free (tkn); | |||
break; | break; | |||
/* Status Code */ | /* Status Code */ | |||
case 's': | case 's': | |||
if (logitem->status) | if (logitem->status) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
/* do not validate HTTP status code */ | /* do not validate HTTP status code */ | |||
if (conf.no_strict_status) { | if (conf.no_strict_status) { | |||
logitem->status = tkn; | logitem->status = tkn; | |||
break; | break; | |||
} | } | |||
status = strtol (tkn, &sEnd, 10); | status = strtol (tkn, &sEnd, 10); | |||
if (tkn == sEnd || *sEnd != '\0' || errno == ERANGE || status < 100 || statu s > 599) { | if (tkn == sEnd || *sEnd != '\0' || errno == ERANGE || status < 100 || statu s > 599) { | |||
spec_err (logitem, SPEC_TOKN_INV, *p, tkn); | spec_err (logitem, ERR_SPEC_TOKN_INV, *p, tkn); | |||
free (tkn); | free (tkn); | |||
return 1; | return 1; | |||
} | } | |||
logitem->status = tkn; | logitem->status = tkn; | |||
break; | break; | |||
/* size of response in bytes - excluding HTTP headers */ | /* size of response in bytes - excluding HTTP headers */ | |||
case 'b': | case 'b': | |||
if (logitem->resp_size) | if (logitem->resp_size) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
bandw = strtoull (tkn, &bEnd, 10); | bandw = strtoull (tkn, &bEnd, 10); | |||
if (tkn == bEnd || *bEnd != '\0' || errno == ERANGE) | if (tkn == bEnd || *bEnd != '\0' || errno == ERANGE) | |||
bandw = 0; | bandw = 0; | |||
logitem->resp_size = bandw; | logitem->resp_size = bandw; | |||
conf.bandwidth = 1; | conf.bandwidth = 1; | |||
free (tkn); | free (tkn); | |||
break; | break; | |||
/* referrer */ | /* referrer */ | |||
case 'R': | case 'R': | |||
skipping to change at line 1151 | skipping to change at line 1207 | |||
} | } | |||
logitem->agent = tkn; | logitem->agent = tkn; | |||
set_agent_hash (logitem); | set_agent_hash (logitem); | |||
break; | break; | |||
/* time taken to serve the request, in milliseconds as a decimal number */ | /* time taken to serve the request, in milliseconds as a decimal number */ | |||
case 'L': | case 'L': | |||
/* ignore it if we already have served time */ | /* ignore it if we already have served time */ | |||
if (logitem->serve_time) | if (logitem->serve_time) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
serve_secs = strtoull (tkn, &bEnd, 10); | serve_secs = strtoull (tkn, &bEnd, 10); | |||
if (tkn == bEnd || *bEnd != '\0' || errno == ERANGE) | if (tkn == bEnd || *bEnd != '\0' || errno == ERANGE) | |||
serve_secs = 0; | serve_secs = 0; | |||
/* convert it to microseconds */ | /* convert it to microseconds */ | |||
logitem->serve_time = (serve_secs > 0) ? serve_secs * MILS : 0; | logitem->serve_time = (serve_secs > 0) ? serve_secs * MILS : 0; | |||
contains_usecs (); /* set flag */ | contains_usecs (); /* set flag */ | |||
free (tkn); | free (tkn); | |||
break; | break; | |||
/* time taken to serve the request, in seconds with a milliseconds | /* time taken to serve the request, in seconds with a milliseconds | |||
* resolution */ | * resolution */ | |||
case 'T': | case 'T': | |||
/* ignore it if we already have served time */ | /* ignore it if we already have served time */ | |||
if (logitem->serve_time) | if (logitem->serve_time) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
if (strchr (tkn, '.') != NULL) | if (strchr (tkn, '.') != NULL) | |||
serve_secs = strtod (tkn, &bEnd); | serve_secs = strtod (tkn, &bEnd); | |||
else | else | |||
serve_secs = strtoull (tkn, &bEnd, 10); | serve_secs = strtoull (tkn, &bEnd, 10); | |||
if (tkn == bEnd || *bEnd != '\0' || errno == ERANGE) | if (tkn == bEnd || *bEnd != '\0' || errno == ERANGE) | |||
serve_secs = 0; | serve_secs = 0; | |||
/* convert it to microseconds */ | /* convert it to microseconds */ | |||
logitem->serve_time = (serve_secs > 0) ? serve_secs * SECS : 0; | logitem->serve_time = (serve_secs > 0) ? serve_secs * SECS : 0; | |||
contains_usecs (); /* set flag */ | contains_usecs (); /* set flag */ | |||
free (tkn); | free (tkn); | |||
break; | break; | |||
/* time taken to serve the request, in microseconds */ | /* time taken to serve the request, in microseconds */ | |||
case 'D': | case 'D': | |||
/* ignore it if we already have served time */ | /* ignore it if we already have served time */ | |||
if (logitem->serve_time) | if (logitem->serve_time) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
serve_time = strtoull (tkn, &bEnd, 10); | serve_time = strtoull (tkn, &bEnd, 10); | |||
if (tkn == bEnd || *bEnd != '\0' || errno == ERANGE) | if (tkn == bEnd || *bEnd != '\0' || errno == ERANGE) | |||
serve_time = 0; | serve_time = 0; | |||
logitem->serve_time = serve_time; | logitem->serve_time = serve_time; | |||
contains_usecs (); /* set flag */ | contains_usecs (); /* set flag */ | |||
free (tkn); | free (tkn); | |||
break; | break; | |||
/* time taken to serve the request, in nanoseconds */ | ||||
case 'n': | ||||
/* ignore it if we already have served time */ | ||||
if (logitem->serve_time) | ||||
return 0; | ||||
if (!(tkn = parse_string (&(*str), end, 1))) | ||||
return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | ||||
serve_time = strtoull (tkn, &bEnd, 10); | ||||
if (tkn == bEnd || *bEnd != '\0' || errno == ERANGE) | ||||
serve_time = 0; | ||||
/* convert it to microseconds */ | ||||
logitem->serve_time = (serve_time > 0) ? serve_time / MILS : 0; | ||||
contains_usecs (); /* set flag */ | ||||
free (tkn); | ||||
break; | ||||
/* UMS: Krypto (TLS) "ECDHE-RSA-AES128-GCM-SHA256" */ | /* UMS: Krypto (TLS) "ECDHE-RSA-AES128-GCM-SHA256" */ | |||
case 'k': | case 'k': | |||
/* error to set this twice */ | /* error to set this twice */ | |||
if (logitem->tls_cypher) | if (logitem->tls_cypher) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
#if defined(HAVE_LIBSSL) && defined(HAVE_CIPHER_STD_NAME) | #if defined(HAVE_LIBSSL) && defined(HAVE_CIPHER_STD_NAME) | |||
{ | { | |||
char *tmp = NULL; | char *tmp = NULL; | |||
for (tmp = tkn; isdigit (*tmp); tmp++); | for (tmp = tkn; isdigit (*tmp); tmp++); | |||
if (!strlen (tmp)) | if (!strlen (tmp)) | |||
extract_tls_version_cipher (tkn, &logitem->tls_cypher, &logitem->tls_typ e); | extract_tls_version_cipher (tkn, &logitem->tls_cypher, &logitem->tls_typ e); | |||
else | else | |||
logitem->tls_cypher = tkn; | logitem->tls_cypher = tkn; | |||
} | } | |||
skipping to change at line 1230 | skipping to change at line 1303 | |||
#endif | #endif | |||
break; | break; | |||
/* UMS: Krypto (TLS) parameters like "TLSv1.2" */ | /* UMS: Krypto (TLS) parameters like "TLSv1.2" */ | |||
case 'K': | case 'K': | |||
/* error to set this twice */ | /* error to set this twice */ | |||
if (logitem->tls_type) | if (logitem->tls_type) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
logitem->tls_type = tkn; | logitem->tls_type = tkn; | |||
break; | break; | |||
/* UMS: Mime-Type like "text/html" */ | /* UMS: Mime-Type like "text/html" */ | |||
case 'M': | case 'M': | |||
/* error to set this twice */ | /* error to set this twice */ | |||
if (logitem->mime_type) | if (logitem->mime_type) | |||
return 0; | return 0; | |||
if (!(tkn = parse_string (&(*str), end, 1))) | if (!(tkn = parse_string (&(*str), end, 1))) | |||
return spec_err (logitem, SPEC_TOKN_NUL, *p, NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, *p, NULL); | |||
logitem->mime_type = tkn; | logitem->mime_type = tkn; | |||
break; | break; | |||
/* move forward through str until not a space */ | /* move forward through str until not a space */ | |||
case '~': | case '~': | |||
find_alpha (&(*str)); | find_alpha (&(*str)); | |||
break; | break; | |||
/* everything else skip it */ | /* everything else skip it */ | |||
default: | default: | |||
skipping to change at line 1361 | skipping to change at line 1434 | |||
* | * | |||
* If no IP is found, 1 is returned. | * If no IP is found, 1 is returned. | |||
* On success, the malloc'd token is assigned to a GLogItem->host and 0 is retur ned. */ | * On success, the malloc'd token is assigned to a GLogItem->host and 0 is retur ned. */ | |||
static int | static int | |||
find_xff_host (GLogItem * logitem, char **str, char **p) { | find_xff_host (GLogItem * logitem, char **str, char **p) { | |||
char *skips = NULL, *extract = NULL; | char *skips = NULL, *extract = NULL; | |||
char pch[2] = { 0 }; | char pch[2] = { 0 }; | |||
int res = 0; | int res = 0; | |||
if (!(skips = extract_braces (p))) | if (!(skips = extract_braces (p))) | |||
return spec_err (logitem, SPEC_SFMT_MIS, **p, "{}"); | return spec_err (logitem, ERR_SPEC_SFMT_MIS, **p, "{}"); | |||
/* if the log format current char is not within the braces special chars, then | /* if the log format current char is not within the braces special chars, then | |||
* we assume the range of IPs are within hard delimiters */ | * we assume the range of IPs are within hard delimiters */ | |||
if (!strchr (skips, **p) && strchr (*str, **p)) { | if (!strchr (skips, **p) && strchr (*str, **p)) { | |||
strcpy (pch, (char[2]) { (char) **p, '\0' }); | strcpy (pch, (char[2]) { (char) **p, '\0' }); | |||
if (!(extract = parse_string (&(*str), pch, 1))) | if (!(extract = parse_string (&(*str), pch, 1))) | |||
goto clean; | goto clean; | |||
if (!(res = set_xff_host (logitem, &extract, skips, 1))) | if (!(res = set_xff_host (logitem, &extract, skips, 1))) | |||
free (extract); | free (extract); | |||
skipping to change at line 1394 | skipping to change at line 1467 | |||
* | * | |||
* On error, or unable to parse it, 1 is returned. | * On error, or unable to parse it, 1 is returned. | |||
* On success, the malloc'd token is assigned to a GLogItem member and | * On success, the malloc'd token is assigned to a GLogItem member and | |||
* 0 is returned. */ | * 0 is returned. */ | |||
static int | static int | |||
special_specifier (GLogItem * logitem, char **str, char **p) { | special_specifier (GLogItem * logitem, char **str, char **p) { | |||
switch (**p) { | switch (**p) { | |||
/* XFF remote hostname (IP only) */ | /* XFF remote hostname (IP only) */ | |||
case 'h': | case 'h': | |||
if (find_xff_host (logitem, str, p)) | if (find_xff_host (logitem, str, p)) | |||
return spec_err (logitem, SPEC_TOKN_NUL, 'h', NULL); | return spec_err (logitem, ERR_SPEC_TOKN_NUL, 'h', NULL); | |||
break; | break; | |||
} | } | |||
return 0; | return 0; | |||
} | } | |||
/* Iterate over the given log format. | /* Iterate over the given log format. | |||
* | * | |||
* On error, or unable to parse it, 1 is returned. | * On error, or unable to parse it, 1 is returned. | |||
* On success, the malloc'd token is assigned to a GLogItem member and | * On success, the malloc'd token is assigned to a GLogItem member and | |||
skipping to change at line 1426 | skipping to change at line 1499 | |||
for (p = lfmt; *p; p++) { | for (p = lfmt; *p; p++) { | |||
if (*p == '%') { | if (*p == '%') { | |||
perc++; | perc++; | |||
continue; | continue; | |||
} | } | |||
if (*p == '~' && perc == 0) { | if (*p == '~' && perc == 0) { | |||
tilde++; | tilde++; | |||
continue; | continue; | |||
} | } | |||
if (*str == '\0') | if (*str == '\0') | |||
return spec_err (logitem, SPEC_LINE_INV, '-', NULL); | return spec_err (logitem, ERR_SPEC_LINE_INV, '-', NULL); | |||
if (*str == '\n') | if (*str == '\n') | |||
return 0; | return 0; | |||
if (tilde && *p != '\0') { | if (tilde && *p != '\0') { | |||
if (*str == '\0') | if (*str == '\0') | |||
return 0; | return 0; | |||
if (special_specifier (logitem, &str, &p) == 1) | if (special_specifier (logitem, &str, &p) == 1) | |||
return 1; | return 1; | |||
tilde = 0; | tilde = 0; | |||
} | } | |||
End of changes. 46 change blocks. | ||||
58 lines changed or deleted | 132 lines changed or added |