/************************************************************************ * NCSA HTTPd Server * Software Development Group * National Center for Supercomputing Applications * University of Illinois at Urbana-Champaign * 605 E. Springfield, Champaign, IL 61820 * httpd@ncsa.uiuc.edu * * Copyright (C) 1995, Board of Trustees of the University of Illinois * ************************************************************************ * * http_log.c,v 1.84 1996/04/05 18:54:59 blong Exp * ************************************************************************ * * http_log.c: Dealing with the logs and errors * */ #include "config.h" #include "portability.h" #include #include #ifndef NO_STDLIB_H # include #endif /* NO_STDLIB_H */ #ifndef NO_MALLOC_H # ifdef NEED_SYS_MALLOC_H # include # else # include # endif /* NEED_SYS_MALLOC_H */ #endif /* NO_MALLOC_H */ #include #include #include "constants.h" #include "allocate.h" #include "http_log.h" #include "http_request.h" #include "http_send.h" #include "http_config.h" #include "host_config.h" #include "http_auth.h" #include "http_mime.h" #include "httpd.h" #include "util.h" #include "open_logfile.h" const char StatLine200[] = "200 Document follows"; const char StatLine204[] = "204 No Content"; const char StatLine206[] = "206 Partial Content"; const char StatLine301[] = "301 Moved Permanently"; const char StatLine302[] = "302 Moved Temporarily"; const char StatLine304[] = "304 Not modified"; const char StatLine400[] = "400 Bad Request"; const char StatLine401[] = "401 Unauthorized"; const char StatLine403[] = "403 Forbidden"; const char StatLine404[] = "404 Not Found"; const char StatLine408[] = "408 Request Timeout"; const char StatLine500[] = "500 Server Error"; const char StatLine501[] = "501 Not Implemented"; const char StatLine503[] = "503 Service Unavailable"; char error_msg[MAX_STRING_LEN]; /* Moved to http_request.c */ int ErrorStat=0; static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT ); static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ); void open_logs(per_host *host) { #ifdef SECURE_LOGS int flags; #endif /* SECURE_LOGS */ if (host->httpd_conf & HC_ERROR_FNAME) { if(!(host->error_log = fopen_logfile(host->error_fname,"a"))) { fprintf(stderr,"HTTPd: could not open error log file %s.\n", host->error_fname); perror("fopen"); exit(1); } } if (host->httpd_conf & HC_XFER_FNAME) { if((host->xfer_log = open_logfile(host->xfer_fname,xfer_flags, xfer_mode)) < 0) { fprintf(stderr,"HTTPd: could not open transfer log file %s.\n", host->xfer_fname); perror("open"); exit(1); } #ifdef SECURE_LOGS flags = fcntl(host->xfer_log, F_GETFD); flags |= FD_CLOEXEC; fcntl(host->xfer_log, F_SETFD, flags); #endif /* SECURE_LOGS */ } if (!(host->log_opts & LOG_COMBINED)) { if (host->httpd_conf & HC_AGENT_FNAME) { if(!(host->agent_log = fopen_logfile(host->agent_fname,"a"))) { fprintf(stderr,"HTTPd: could not open agent log file %s.\n", host->agent_fname); perror("fopen"); exit(1); } #ifdef SECURE_LOGS flags = fcntl(fileno(host->agent_log), F_GETFD); flags |= FD_CLOEXEC; fcntl(fileno(host->agent_log), F_SETFD, flags); #endif /* SECURE_LOGS */ } if (host->httpd_conf & HC_REFERER_FNAME) { if(!(host->referer_log = fopen_logfile(host->referer_fname,"a"))) { fprintf(stderr,"HTTPd: could not open referer log file %s.\n", host->referer_fname); perror("fopen"); exit(1); } #ifdef SECURE_LOGS flags = fcntl(fileno(host->referer_log), F_GETFD); flags |= FD_CLOEXEC; fcntl(fileno(host->referer_log), F_SETFD, flags); #endif /* SECURE_LOGS */ } } } void close_logs(per_host *host) { if (host->httpd_conf & HC_ERROR_FNAME) { fflush(host->error_log); fclose(host->error_log); } if (host->httpd_conf & HC_AGENT_FNAME) { fflush(host->agent_log); fclose(host->agent_log); } if (host->httpd_conf & HC_REFERER_FNAME) { fflush(host->referer_log); fclose(host->referer_log); } if (host->httpd_conf & HC_XFER_FNAME) { close(host->xfer_log); } } void error_log2stderr(FILE *error_log) { if(fileno(error_log) != STDERR_FILENO) dup2(fileno(error_log),STDERR_FILENO); } void log_pid(void) { FILE *pid_file; if(!(pid_file = fopen(pid_fname,"w"))) { fprintf(stderr,"HTTPd: could not log pid to file %s\n",pid_fname); exit(1); } fprintf(pid_file,"%d\n",getpid()); fclose(pid_file); } extern int num_children; void log_transaction(per_request *reqInfo) { char *str; long timz; struct tm *t; char *tstr,sign; #ifdef LOG_DURATION extern time_t request_time; time_t duration = request_time ? (time(NULL) - request_time) : 0; #endif /* LOG_DURATION */ str = newString(HUGE_STRING_LEN,STR_TMP); tstr = newString(MAX_STRING_LEN,STR_TMP); t = get_gmtoff(&timz); sign = (timz < 0 ? '-' : '+'); if(timz < 0) timz = -timz; strftime(tstr,MAX_STRING_LEN,"%d/%b/%Y:%H:%M:%S",t); sprintf(str,"%s %s %s [%s %c%02ld%02d] \"%s\" ", (reqInfo->remote_name ? reqInfo->remote_name : "-"), (do_rfc931 ? remote_logname : "-"), (reqInfo->auth_user[0] ? reqInfo->auth_user : "-"), tstr, sign, timz/3600, timz%3600, the_request); if(reqInfo->status != -1) sprintf(str,"%s%d ",str,reqInfo->status); else strcat(str,"- "); if(reqInfo->bytes_sent != -1) sprintf(str,"%s%ld",str,reqInfo->bytes_sent); else strcat(str,"-"); if (reqInfo->hostInfo->log_opts & LOG_SERVERNAME) { if (reqInfo->hostInfo->server_hostname) sprintf(str,"%s %s",str,reqInfo->hostInfo->server_hostname); else strcat(str," -"); } if (reqInfo->hostInfo->referer_ignore && reqInfo->inh_referer[0]) { char *str1; int bIgnore = 0; str1 = newString(MAX_STRING_LEN,STR_TMP); lim_strcpy(str1, reqInfo->hostInfo->referer_ignore, 255); if (reqInfo->hostInfo->referer_ignore[0]) { char* tok = strtok (str1, " "); while (tok) { if (strstr(reqInfo->inh_referer, tok)) { bIgnore = 1; break; } tok = strtok (NULL, " "); } } if (bIgnore) { reqInfo->inh_referer[0] = '\0'; } freeString(str1); } #ifdef LOG_DURATION sprintf(str+strlen(str), " %ld", duration); #endif /* LOG_DURATION */ if (!(reqInfo->hostInfo->log_opts & LOG_COMBINED)) { strcat(str,"\n"); write(reqInfo->hostInfo->xfer_log,str,strlen(str)); /* log the user agent */ if (reqInfo->inh_agent[0]) { if (reqInfo->hostInfo->log_opts & LOG_DATE) fprintf(reqInfo->hostInfo->agent_log, "[%s] %s\n",tstr, reqInfo->inh_agent); else fprintf(reqInfo->hostInfo->agent_log, "%s\n", reqInfo->inh_agent); fflush(reqInfo->hostInfo->agent_log); } /* log the referer */ if (reqInfo->inh_referer[0]) { if (reqInfo->hostInfo->log_opts & LOG_DATE) fprintf(reqInfo->hostInfo->referer_log, "[%s] %s -> %s\n",tstr, reqInfo->inh_referer, reqInfo->url); else fprintf(reqInfo->hostInfo->referer_log, "%s -> %s\n", reqInfo->inh_referer, reqInfo->url); fflush(reqInfo->hostInfo->referer_log); } } else { if (reqInfo->inh_referer[0]) sprintf(str,"%s \"%s\"",str,reqInfo->inh_referer); else strcat(str," \"\""); if (reqInfo->inh_agent[0]) sprintf(str,"%s \"%s\"\n",str,reqInfo->inh_agent); else strcat(str," \"\"\n"); write(reqInfo->hostInfo->xfer_log,str,strlen(str)); } freeString(str); freeString(tstr); } void log_error(char *err, FILE *fp) { fprintf(fp, "[%s] %s\n",get_time(),err); fflush(fp); } void log_reason(per_request *reqInfo, char *reason, char *file) { char *buffer; /* This might not be big enough, but since its in the heap, the * worst that will happen is a core dump, and its faster than a * malloc, and won't fragment the memory as badly. */ buffer = newString(HUGE_STRING_LEN,STR_TMP); sprintf(buffer,"HTTPd: access to %s failed for %s, reason: %s from %s", file,reqInfo->remote_name,reason, ( (reqInfo->inh_referer[0] != '\0') ? reqInfo->inh_referer : "-")); log_error(buffer,reqInfo->hostInfo->error_log); freeString(buffer); } void error_head(per_request *reqInfo, const char *err) { if(reqInfo->http_version != P_HTTP_0_9) { strcpy(reqInfo->outh_content_type,"text/html"); send_http_header(reqInfo); } if(reqInfo->method != M_HEAD) { rprintf(reqInfo,"%s%c",err,LF); rprintf(reqInfo,"

%s

%c",err,LF); } } void title_html(per_request *reqInfo, char *msg) { rprintf(reqInfo,"%s%c",msg,LF); rprintf(reqInfo,"

%s

%c",msg,LF); } int die(per_request *reqInfo, int type, char *err_string) { char *arguments; int RetVal=0; int x; int die_type; arguments = newString(MAX_STRING_LEN,STR_TMP); /* kill keepalive on errors until we figure out what to do such as compute content_length of error messages */ die_type = DIE_NORMAL; keep_alive.bKeepAlive = 0; /*die_type = keep_alive.bKeepAlive ? DIE_KEEPALIVE : DIE_NORMAL;*/ /* For custom error scripts */ strcpy(failed_request,the_request); strcpy(failed_url,reqInfo->url); switch(type) { case SC_NO_CONTENT: reqInfo->status = SC_NO_CONTENT; set_stat_line(reqInfo); if (reqInfo->http_version != P_HTTP_0_9) { send_http_header(reqInfo); } keep_alive.bKeepAlive = 0; RetVal = SC_NO_CONTENT; log_transaction(reqInfo); break; case SC_REDIRECT_TEMP: reqInfo->status = SC_REDIRECT_TEMP; set_stat_line(reqInfo); if (((x=have_doc_error(reqInfo,type)) >= 0) && (!ErrorStat)) { ErrorStat = reqInfo->status; GoErrorDoc(reqInfo,x,err_string); } else { keep_alive.bKeepAlive = 0; if(reqInfo->http_version != P_HTTP_0_9) { strcpy(reqInfo->outh_location,err_string); strcpy(reqInfo->outh_content_type,"text/html"); send_http_header(reqInfo); } if (reqInfo->method != M_HEAD) { title_html(reqInfo,"Document moved"); rprintf(reqInfo, "This document has moved here.

%c", err_string,LF); rprintf(reqInfo,"%c",LF); } log_transaction(reqInfo); } break; case SC_REDIRECT_PERM: reqInfo->status = SC_REDIRECT_PERM; set_stat_line(reqInfo); if (((x=have_doc_error(reqInfo,type)) >= 0) && (!ErrorStat)) { ErrorStat = reqInfo->status; GoErrorDoc(reqInfo,x,err_string); } else { keep_alive.bKeepAlive = 0; if(reqInfo->http_version != P_HTTP_0_9) { strcpy(reqInfo->outh_location,err_string); strcpy(reqInfo->outh_content_type,"text/html"); send_http_header(reqInfo); } if (reqInfo->method != M_HEAD) { title_html(reqInfo,"Document moved"); rprintf(reqInfo,"This document has permanently moved "); rprintf(reqInfo,"here.

%c%c", err_string,LF,LF); } log_transaction(reqInfo); } break; case SC_USE_LOCAL_COPY: reqInfo->status = SC_USE_LOCAL_COPY; set_stat_line(reqInfo); if (reqInfo->http_version != P_HTTP_0_9) { send_http_header(reqInfo); } keep_alive.bKeepAlive = 0; RetVal = SC_USE_LOCAL_COPY; log_transaction(reqInfo); break; case SC_AUTH_REQUIRED: reqInfo->status = SC_AUTH_REQUIRED; set_stat_line(reqInfo); strcpy(reqInfo->outh_www_auth, err_string); if (((x=have_doc_error(reqInfo,type)) >= 0) && (!ErrorStat)) { ErrorStat = reqInfo->status; GoErrorDoc(reqInfo,x,err_string); } else { keep_alive.bKeepAlive = 0; if(reqInfo->http_version != P_HTTP_0_9) { strcpy(reqInfo->outh_content_type,"text/html"); send_http_header(reqInfo); } if (reqInfo->method != M_HEAD) { title_html(reqInfo,"Authorization Required"); rprintf(reqInfo,"Browser not authentication-capable or %c",LF); rprintf(reqInfo,"authentication failed.%c",LF); rprintf(reqInfo,"%c",LF); } log_transaction(reqInfo); } break; case SC_AUTH_NO_WWW_AUTH: reqInfo->status = SC_AUTH_REQUIRED; set_stat_line(reqInfo); keep_alive.bKeepAlive = 0; if(reqInfo->http_version != P_HTTP_0_9) { strcpy(reqInfo->outh_content_type,"text/html"); send_http_header(reqInfo); } if (reqInfo->method != M_HEAD) { title_html(reqInfo,"Authorization Required"); rprintf(reqInfo,"You are not permitted to get this URL: %c",LF); rprintf(reqInfo,"%s%c",err_string,LF); rprintf(reqInfo,"%c",LF); } log_transaction(reqInfo); break; case SC_BAD_REQUEST: reqInfo->status = SC_BAD_REQUEST; set_stat_line(reqInfo); if (((x=have_doc_error(reqInfo,type)) >= 0) && (!ErrorStat)) { ErrorStat = reqInfo->status; GoErrorDoc(reqInfo,x,err_string); } else { error_head(reqInfo,StatLine400); keep_alive.bKeepAlive = 0; if (reqInfo->method != M_HEAD) { rprintf(reqInfo, "Your client sent a query that this server could%c",LF); rprintf(reqInfo,"not understand.

%c",LF); rprintf(reqInfo,"Reason: %s

%c",err_string,LF); rprintf(reqInfo,"%c",LF); } log_transaction(reqInfo); } break; case SC_BAD_IMAGEMAP: reqInfo->status = SC_BAD_REQUEST; set_stat_line(reqInfo); if (((x=have_doc_error(reqInfo,type)) >= 0) && (!ErrorStat)) { ErrorStat = reqInfo->status; GoErrorDoc(reqInfo,x,err_string); } else { error_head(reqInfo,StatLine400); keep_alive.bKeepAlive = 0; if (reqInfo->method != M_HEAD) { rprintf(reqInfo, "Server encountered error processing imagemap%c",LF); rprintf(reqInfo,"

Reason: %s

%c",err_string,LF); rprintf(reqInfo,"%c",LF); } log_transaction(reqInfo); } break; case SC_FORBIDDEN: if (reqInfo->outh_location[0]) reqInfo->status = SC_REDIRECT_TEMP; else reqInfo->status = SC_FORBIDDEN; set_stat_line(reqInfo); if (((x=have_doc_error(reqInfo,type)) >= 0) && (!ErrorStat)) { ErrorStat = reqInfo->status; GoErrorDoc(reqInfo,x,err_string); } else { error_head(reqInfo,StatLine403); keep_alive.bKeepAlive = 0; if (reqInfo->method != M_HEAD) { rprintf(reqInfo, "Your client does not have permission to get URL %s ", err_string); rprintf(reqInfo,"from this server.

%c",LF); rprintf(reqInfo,"%c",LF); } log_transaction(reqInfo); } break; case SC_NOT_FOUND: reqInfo->status = SC_NOT_FOUND; set_stat_line(reqInfo); if (((x=have_doc_error(reqInfo,type)) >= 0) && (!ErrorStat)) { ErrorStat = reqInfo->status; GoErrorDoc(reqInfo,x,err_string); } else { error_head(reqInfo,StatLine404); keep_alive.bKeepAlive = 0; if (reqInfo->method != M_HEAD) { rprintf(reqInfo, "The requested URL %s was not found on this server.%c", err_string,LF); rprintf(reqInfo,"%c",LF); } log_transaction(reqInfo); } break; case SC_SERVICE_UNAVAIL: reqInfo->status = SC_SERVICE_UNAVAIL; set_stat_line(reqInfo); if (((x=have_doc_error(reqInfo,type)) >= 0) && (!ErrorStat)) { ErrorStat = reqInfo->status; GoErrorDoc(reqInfo,x,err_string); } else { error_head(reqInfo,StatLine503); keep_alive.bKeepAlive = 0; if (reqInfo->method != M_HEAD) { rprintf(reqInfo, "The requested URL %s is temporarily unavailable", err_string); rprintf(reqInfo,"from this server.%c",LF); rprintf(reqInfo,"%c",LF); } log_transaction(reqInfo); } break; case SC_SERVER_ERROR: reqInfo->status = SC_SERVER_ERROR; set_stat_line(reqInfo); die_type = DIE_NORMAL; if (((x=have_doc_error(reqInfo,type)) >= 0) && (!ErrorStat)) { ErrorStat = reqInfo->status; log_error(err_string,reqInfo->hostInfo->error_log); GoErrorDoc(reqInfo,x,err_string); } else { error_head(reqInfo,StatLine500); keep_alive.bKeepAlive = 0; log_error(err_string,reqInfo->hostInfo->error_log); if (reqInfo->method != M_HEAD) { rprintf(reqInfo,"The server encountered an internal error or%c",LF); rprintf(reqInfo,"misconfiguration and was unable to complete %c",LF); rprintf(reqInfo,"your request.

%c",LF); rprintf(reqInfo,"Please contact the server administrator,%c",LF); rprintf(reqInfo," %s ",reqInfo->hostInfo->server_admin); rprintf(reqInfo,"and inform them of the time the error occurred%c",LF); rprintf(reqInfo,", and anything you might have done that may%c",LF); rprintf(reqInfo,"have caused the error.

%c",LF); rprintf(reqInfo,"Error: %s%c",err_string,LF); rprintf(reqInfo,"%c",LF); } log_transaction(reqInfo); } break; case SC_NOT_IMPLEMENTED: reqInfo->status = SC_NOT_IMPLEMENTED; set_stat_line(reqInfo); if (((x=have_doc_error(reqInfo,type)) >= 0) && (!ErrorStat)) { ErrorStat = reqInfo->status; GoErrorDoc(reqInfo,x,err_string); } else { error_head(reqInfo,StatLine501); keep_alive.bKeepAlive = 0; if (reqInfo->method != M_HEAD) { rprintf(reqInfo,"We are sorry to be unable to perform the method %s", err_string); rprintf(reqInfo," at this time or to this document.

%c",LF); rprintf(reqInfo,"%c",LF); } log_transaction(reqInfo); } break; case SC_NO_MEMORY: log_error("HTTPd: memory exhausted",reqInfo->hostInfo->error_log); reqInfo->status = SC_SERVER_ERROR; set_stat_line(reqInfo); die_type = DIE_NORMAL; error_head(reqInfo,StatLine500); keep_alive.bKeepAlive = 0; if (reqInfo->method != M_HEAD) { rprintf(reqInfo,"The server has temporarily run out of resources%c",LF); rprintf(reqInfo,"for your request. Please try again at a later time.

%c",LF); rprintf(reqInfo,"%c",LF); } log_transaction(reqInfo); break; case SC_CONF_ERROR: reqInfo->status = SC_SERVER_ERROR; set_stat_line(reqInfo); sprintf(arguments,"HTTPd: configuration error = %s",err_string); log_error(arguments,reqInfo->hostInfo->error_log); error_head(reqInfo,StatLine500); keep_alive.bKeepAlive = 0; if (reqInfo->method != M_HEAD) { rprintf(reqInfo,"The server has encountered a misconfiguration.%c",LF); rprintf(reqInfo,"The error was %s.%c",err_string,LF); rprintf(reqInfo,"%c",LF); } log_transaction(reqInfo); } rflush(reqInfo); freeString(arguments); if (!RetVal) htexit(reqInfo,1,die_type); return RetVal; } int GoErrorDoc(per_request *reqInfo, int x, char *ErrString) { per_request *newInfo; newInfo = continue_request(reqInfo, FORCE_GET | KEEP_ENV | KEEP_AUTH); strcpy(newInfo->url,reqInfo->hostInfo->doc_errors[x]->DocErrorFile); if (ErrString) sprintf(newInfo->args,"error=%s",ErrString); process_request(newInfo); free_request(newInfo,ONLY_LAST); return TRUE; } /* Add error to error table. Should be called from http_config.c at startup * With new VirtualHost support, need to create the array of values if this * is the first call to add_doc_error. */ int add_doc_error(per_host *host, char* errornum, char* name) { char *tmp; if (host->num_doc_errors == 0) { host->doc_errors = (ErrorDoc **) malloc(sizeof(ErrorDoc*)); } else { host->doc_errors = (ErrorDoc **) realloc(host->doc_errors, (host->num_doc_errors+1) * sizeof(ErrorDoc*)); } host->doc_errors[host->num_doc_errors]=(ErrorDoc *)malloc(sizeof(ErrorDoc)); tmp = (char *) malloc(strlen(name)+1); strcpy(tmp,name); host->doc_errors[host->num_doc_errors]->DocErrorNum = atoi(errornum); host->doc_errors[host->num_doc_errors]->DocErrorFile = tmp; return ++(host->num_doc_errors); } /* Do we have a defined error for errornum? */ int have_doc_error(per_request *reqInfo, int errornum) { int x=0; if (!reqInfo->hostInfo) return -1; while ((x < reqInfo->hostInfo->num_doc_errors) && (reqInfo->hostInfo->doc_errors[x]->DocErrorNum != errornum)) { x++; } if (x < reqInfo->hostInfo->num_doc_errors) return x; else return -1; } /* Reset Error Types (for SIGHUP-restart) */ void free_doc_errors(per_host *host) { int x=0; for (x = 0 ; x < host->num_doc_errors; x++) { free(host->doc_errors[x]->DocErrorFile); free(host->doc_errors[x]); } free(host->doc_errors); }