@@ -40,10 +40,15 @@ #include #include #include #include #include + +#ifndef FILED_DONT_TIMEOUT +#include +#include +#endif /* Compile time constants */ #define FILED_VERSION "1.13" #define FILED_SENDFILE_MAX 16777215 #define FILED_MAX_FAILURE_COUNT 30 @@ -585,29 +590,170 @@ filed_log_msg("START"); return(0); } #endif + +#ifdef FILED_DONT_TIMEOUT +#define filed_sockettimeout_thread_init() 0 +#define filed_sockettimeout_accept(x) /**/ +#define filed_sockettimeout_processing_start(x) /**/ +#define filed_sockettimeout_processing_end(x) /**/ +#define filed_sockettimeout_close(x) /**/ +#else +_Atomic time_t filed_sockettimeout_time; +struct { + _Atomic time_t expiration_time; + _Atomic pthread_t thread_id; + bool valid; +}* filed_sockettimeout_sockstatus; +long filed_sockettimeout_sockstatus_length; +int filed_sockettimeout_devnull_fd; + +static int filed_sockettimeout_sockfd_in_range(int sockfd) { + if (sockfd < 3) { + return(0); + } + + if (sockfd > filed_sockettimeout_sockstatus_length) { + return(0); + } + + return(1); +} + +static void filed_sockettimeout_expire(int sockfd, int length) { + time_t now, expire; + + now = atomic_load(&filed_sockettimeout_time); + + expire = now + length; + + atomic_store(&filed_sockettimeout_sockstatus[sockfd].expiration_time, expire); + + return; +} + +static void filed_sockettimeout_accept(int sockfd) { + if (!filed_sockettimeout_sockfd_in_range(sockfd)) { + return; + } + + filed_sockettimeout_expire(sockfd, 60); + + atomic_store(&filed_sockettimeout_sockstatus[sockfd].thread_id, pthread_self()); + + atomic_store(&filed_sockettimeout_sockstatus[sockfd].valid, true); + + return; +} + +static void filed_sockettimeout_processing_start(int sockfd) { + if (!filed_sockettimeout_sockfd_in_range(sockfd)) { + return; + } + + filed_sockettimeout_expire(sockfd, 86400); + + return; +} + +static void filed_sockettimeout_processing_end(int sockfd) { + if (!filed_sockettimeout_sockfd_in_range(sockfd)) { + return; + } + + filed_sockettimeout_expire(sockfd, 60); + + return; +} + +static void filed_sockettimeout_close(int sockfd) { + if (!filed_sockettimeout_sockfd_in_range(sockfd)) { + return; + } + + atomic_store(&filed_sockettimeout_sockstatus[sockfd].valid, false); + + return; +} static void *filed_sockettimeout_thread(void *arg) { + time_t now, expiration_time; + pthread_t thread_id; + long idx; + int count; + bool valid; + while (1) { - usleep(300000); + for (count = 0; count < 10; count++) { + usleep(30000000); + + now = time(NULL); + + atomic_store(&filed_sockettimeout_time, now); + } + + for (idx = 0; idx < filed_sockettimeout_sockstatus_length; idx++) { + valid = atomic_load(&filed_sockettimeout_sockstatus[idx].valid); + + if (!valid) { + continue; + } + + expiration_time = atomic_load(&filed_sockettimeout_sockstatus[idx].expiration_time); + + thread_id = atomic_load(&filed_sockettimeout_sockstatus[idx].thread_id); + + if (expiration_time > now) { + continue; + } + + filed_sockettimeout_close(idx); + + dup2(filed_sockettimeout_devnull_fd, idx); + + pthread_kill(thread_id, SIGPIPE); + } } return(NULL); - /* NOTREACHED */ + /* NOTREACH: We don't actually take any arguments */ arg = arg; } static int filed_sockettimeout_thread_init(void) { pthread_t thread_id; + long maxfd, idx; + + maxfd = sysconf(_SC_OPEN_MAX); + if (maxfd <= 0) { + maxfd = 4096; + } + + filed_sockettimeout_sockstatus = malloc(sizeof(*filed_sockettimeout_sockstatus) * maxfd); + if (filed_sockettimeout_sockstatus == NULL) { + return(-1); + } + + for (idx = 0; idx < maxfd; idx++) { + filed_sockettimeout_sockstatus[idx].valid = false; + } + + filed_sockettimeout_sockstatus_length = maxfd; + + filed_sockettimeout_devnull_fd = open("/dev/null", O_RDWR); + if (filed_sockettimeout_devnull_fd < 0) { + return(-1); + } pthread_create(&thread_id, NULL, filed_sockettimeout_thread, NULL); return(0); } +#endif /* Format time per RFC2616 */ static char *filed_format_time(char *buffer, size_t buffer_len, const time_t timeinfo) { struct tm timeinfo_tm, *timeinfo_tm_p; @@ -950,10 +1096,12 @@ log->http_code = error_number; filed_log_entry(log); /* Close connection */ + filed_sockettimeout_close(fileno(fp)); + fclose(fp); return; } @@ -972,10 +1120,12 @@ log->http_code = http_code; filed_log_entry(log); /* Close connection */ + filed_sockettimeout_close(fileno(fp)); + fclose(fp); return; /* Currently unused: path */ @@ -1009,10 +1159,12 @@ date_current = filed_format_time(date_current_b, sizeof(date_current_b), time(NULL)); /* Open socket as ANSI I/O for ease of use */ fp = fdopen(fd, "w+b"); if (fp == NULL) { + filed_sockettimeout_close(fd); + close(fd); log->buffer[0] = '\0'; log->http_code = -1; log->reason = "fdopen_failed"; @@ -1029,10 +1181,12 @@ filed_error_page(fp, date_current, 500, FILED_REQUEST_METHOD_GET, "format", log); return(FILED_CONNECTION_CLOSE); } + + filed_sockettimeout_processing_start(fd); path = request->path; strcpy(log->buffer, path); log->method = request->method; @@ -1183,14 +1337,18 @@ filed_log_entry(log); close(fileinfo->fd); if (request->headers.connection != FILED_CONNECTION_KEEP_ALIVE) { + filed_sockettimeout_close(fd); + fclose(fp); return(FILED_CONNECTION_CLOSE); } + + filed_sockettimeout_processing_end(fd); return(FILED_CONNECTION_KEEP_ALIVE); } /* Handle incoming connections */ @@ -1247,10 +1405,12 @@ filed_log_free(log); continue; } + + filed_sockettimeout_accept(fd); /* Fill in log structure */ if (filed_log_ip((struct sockaddr *) &addr, log->ip, sizeof(log->ip)) == NULL) { log->ip[0] = '\0'; log->port = 0;