/* Hiawatha | log.c * * Logging functions */ #include "config.h" #include #include #include #include #include #include #include #include "global.h" #include "log.h" #define BUFFER_SIZE 2 * KILOBYTE #define TIMESTAMP_SIZE 40 #define IP_ADDRESS_SIZE MAX_IP_STR_LEN + 2 #define MAX_LOG_DELAY_TIMER 10 #define LOGFILE_OPEN_TIME 30 pthread_mutex_t accesslog_mutex; static int delay_timer = 0; void init_log_module(void) { pthread_mutex_init(&accesslog_mutex, NULL); delay_timer = 0; } /* Write a timestamp to a logfile. */ static void print_timestamp(char *str) { time_t t; struct tm *s; time(&t); s = localtime(&t); str[TIMESTAMP_SIZE - 1] = '\0'; strftime(str, TIMESTAMP_SIZE - 1, "%a %d %b %Y %T %z|", s); } /* Write an IP address to a logfile. */ static void print_ip_address(char *str, t_ip_addr *ip) { if (inet_ntop(ip->family, &(ip->value), str, IP_ADDRESS_SIZE) == NULL) { strcpy(str, "?.?.?.?|"); } else { strcat(str, "|"); } } static char *secure_string(char *str) { char *c = str; if (str != NULL) { while (*c != '\0') { if (*c == '\27') { *c = ' '; } c++; } } return str; } /*---< Main log functions >------------------------------------------*/ /* Log the Hiawatha process ID. */ void log_pid(t_config *config, pid_t pid) { FILE *fp; if ((fp = fopen(config->pidfile, "w")) != NULL) { fprintf(fp, "%d\n", (int)pid); fclose(fp); if (chmod(config->pidfile, S_IRUSR | S_IWUSR | S_IRGRP) == -1) { fprintf(stderr, "Warning: can't chmod PID file %s. Make sure it's only writable for root!", config->pidfile); } } else { fprintf(stderr, "Warning: can't write PID file %s.\n", config->pidfile); } } /* Log a text. */ void log_string(char *logfile, char *mesg) { FILE *fp; char str[TIMESTAMP_SIZE]; if ((fp = fopen(logfile, "a")) != NULL) { print_timestamp(str); fprintf(fp, "%s%s\n", str, mesg); fclose(fp); } } /* Log a system message. */ void log_system(t_session *session, char *mesg) { FILE *fp; char str[TIMESTAMP_SIZE + IP_ADDRESS_SIZE]; if ((fp = fopen(session->config->system_logfile, "a")) != NULL) { print_ip_address(str, &(session->ip_address)); print_timestamp(str + strlen(str)); fprintf(fp, "%s%s\n", str, mesg); fclose(fp); } } /* Log an error */ void log_error(t_session *session, char *mesg) { FILE *fp; char str[TIMESTAMP_SIZE + IP_ADDRESS_SIZE]; if ((fp = fopen(session->host->error_logfile, "a")) != NULL) { print_timestamp(str); print_ip_address(str + strlen(str), &(session->ip_address)); fprintf(fp, "%s%s\n", str, mesg); fclose(fp); } } /* Log a HTTP request. */ void log_request(t_session *session, int code) { char str[BUFFER_SIZE + 1], timestamp[TIMESTAMP_SIZE], ip_address[IP_ADDRESS_SIZE]; char *user, *field, *uri, *vars, *path_info; t_headerfield *headerfield; int offset, len; time_t t; struct tm *s; if (ip_allowed(&(session->ip_address), session->config->logfile_mask) != deny) { str[BUFFER_SIZE] = '\0'; #ifdef HAVE_REWRITE if (session->script_url == NULL) { #endif uri = secure_string(session->uri); path_info = secure_string(session->path_info); vars = secure_string(session->vars); #ifdef HAVE_REWRITE } else { uri = secure_string(session->script_url); path_info = NULL; vars = NULL; } #endif if ((user = session->remote_user) == NULL) { user = ""; } else { user = secure_string(user); } if (session->config->log_format == hiawatha) { /* Hiawatha log format */ print_ip_address(str, &(session->ip_address)); offset = strlen(str); print_timestamp(str + offset); offset += strlen(str + offset); snprintf(str + offset, BUFFER_SIZE - offset, "%d|%lld|%s|%s %s", code, (long long)session->bytes_sent, user, secure_string(session->method), uri); offset += strlen(str + offset); if ((offset < BUFFER_SIZE) && (path_info != NULL)) { snprintf(str + offset, BUFFER_SIZE - offset, "/%s", path_info); offset += strlen(str + offset); } if ((offset < BUFFER_SIZE) && (vars != NULL)) { snprintf(str + offset, BUFFER_SIZE - offset, "?%s", vars); offset += strlen(str + offset); } if (offset < BUFFER_SIZE) { snprintf(str + offset, BUFFER_SIZE - offset, " %s", secure_string(session->http_version)); offset += strlen(str + offset); } if (offset < BUFFER_SIZE) { headerfield = session->headerfields; while (headerfield != NULL) { if ((strncasecmp("Cookie:", headerfield->data, 7) != 0) && (strncasecmp("Authorization:", headerfield->data, 14) != 0)) { snprintf(str + offset, BUFFER_SIZE - offset, "|%s", secure_string(headerfield->data)); if ((offset += strlen(str + offset)) >= BUFFER_SIZE) { break; } } headerfield = headerfield->next; } } } else { /* Common Log Format */ print_ip_address(ip_address, &(session->ip_address)); if ((len = strlen(ip_address)) > 0) { ip_address[len - 1] = '\0'; } time(&t); s = localtime(&t); timestamp[TIMESTAMP_SIZE - 1] = '\0'; strftime(timestamp, TIMESTAMP_SIZE - 1, "%d/%b/%Y:%T %z", s); snprintf(str, BUFFER_SIZE, "%s - %s [%s] \"%s %s", ip_address, user, timestamp, secure_string(session->method), uri); offset = strlen(str); if ((offset < BUFFER_SIZE) && (path_info != NULL)) { snprintf(str + offset, BUFFER_SIZE - offset, "/%s", path_info); offset += strlen(str + offset); } if ((offset < BUFFER_SIZE) && (vars != NULL)) { snprintf(str + offset, BUFFER_SIZE - offset, "?%s", vars); offset += strlen(str + offset); } if (offset < BUFFER_SIZE) { snprintf(str + offset, BUFFER_SIZE - offset, " %s\" %d %lld", secure_string(session->http_version), code, (long long)session->bytes_sent); } if (session->config->log_format == extended) { /* Extended Common Log Format */ offset += strlen(str + offset); if (offset < BUFFER_SIZE) { if ((field = get_headerfield("Referer:", session->headerfields)) != NULL) { snprintf(str + offset, BUFFER_SIZE - offset, " \"%s\"", secure_string(field)); } else { snprintf(str + offset, BUFFER_SIZE - offset, " -"); } offset += strlen(str + offset); } if (offset < BUFFER_SIZE) { if ((field = get_headerfield("User-Agent:", session->headerfields)) != NULL) { snprintf(str + offset, BUFFER_SIZE - offset, " \"%s\"", secure_string(field)); } else { snprintf(str + offset, BUFFER_SIZE - offset, " -"); } } } } pthread_mutex_lock(&accesslog_mutex); if (*(session->host->access_fp) == NULL) { *(session->host->access_fp) = fopen(session->host->access_logfile, "a"); } if (*(session->host->access_fp) != NULL) { fprintf(*(session->host->access_fp), "%s\n", str); fflush(*(session->host->access_fp)); } pthread_mutex_unlock(&accesslog_mutex); } } /* Log garbage sent by a client. */ void log_garbage(t_session *session) { FILE *fp; char str[TIMESTAMP_SIZE + IP_ADDRESS_SIZE]; if ((session->config->garbage_logfile != NULL) && (session->request != NULL)) { if ((fp = fopen(session->config->garbage_logfile, "a")) != NULL) { print_ip_address(str, &(session->ip_address)); print_timestamp(str + strlen(str)); fprintf(fp, "%s%s\n", str, session->request); fclose(fp); } } } /* Log an unbanning. */ void log_unban(char *logfile, t_ip_addr *ip_address, unsigned long connect_attempts) { FILE *fp; char str[TIMESTAMP_SIZE + IP_ADDRESS_SIZE]; if ((fp = fopen(logfile, "a")) != NULL) { print_ip_address(str, ip_address); print_timestamp(str + strlen(str)); fprintf(fp, "%sUnbanned (%ld connect attempts during ban)\n", str, connect_attempts); fclose(fp); } } /* Log a CGI error. */ void log_file_error(t_session *session, char *file, char *mesg) { FILE *fp; char *c, str[TIMESTAMP_SIZE + IP_ADDRESS_SIZE]; int len = 0; c = mesg; while (*c != '\0') { if (*c == '\n') { if (*(c + 1) == '\0') { *c = '\0'; } else { *c = '|'; } } else { len++; } c++; } if (len > 0) { if ((fp = fopen(session->host->error_logfile, "a")) != NULL) { print_timestamp(str); print_ip_address(str + strlen(str), &(session->ip_address)); fprintf(fp, "%s%s|%s\n", str, file, secure_string(mesg)); fclose(fp); } } } /* Close open accesslogfiles. */ void log_close(t_host *host, bool force_close) { time_t now; if ((++delay_timer >= MAX_LOG_DELAY_TIMER) || force_close) { delay_timer = 0; now = time(NULL); pthread_mutex_lock(&accesslog_mutex); while (host != NULL) { if ((now >= host->access_time + LOGFILE_OPEN_TIME) || force_close) { if (*(host->access_fp) != NULL) { //fflush(*(host->access_fp)); fclose(*(host->access_fp)); *(host->access_fp) = NULL; } } host = host->next; } pthread_mutex_unlock(&accesslog_mutex); } }