Index: filed.1 ================================================================== --- filed.1 +++ filed.1 @@ -6,10 +6,11 @@ .ll +10 .B filed .RB [{ \-h | \-\-help }] .RB [{ \-d | \-\-daemon }] .RB [{ \-v | \-\-version }] +.RB [{ \-V | \-\-vhost }] .RB [{ \-b | \-\-bind } .IR address ] .RB [{ \-p | \-\-port } .IR port ] .RB [{ \-t | \-\-threads } @@ -44,10 +45,15 @@ .B -v (or --version) Instructs .B filed to print out its version number and then exit. +.TP +.B -V (or --vhost) +instructs filed to prepend all requests with their HTTP +Host header. + .TP .B -b (or --bind) Specifies the address to listen for incoming HTTP requests on. Index: filed.c ================================================================== --- filed.c +++ filed.c @@ -30,14 +30,20 @@ #define PORT 80 #define THREAD_COUNT 5 #define BIND_ADDR "::" #define CACHE_SIZE 8209 #define LOG_FILE "-" + +/* Configuration options that work threads need to be aware of */ +struct filed_options { + int vhosts_enabled; +}; /* Arguments for worker threads */ struct filed_worker_thread_args { int fd; + struct filed_options options; }; /* Arguments for logging threads */ struct filed_logging_thread_args { FILE *fp; @@ -57,11 +63,11 @@ /* Request variables */ struct filed_http_request { /** Buffers **/ struct filed_fileinfo fileinfo; - char tmpbuf[1010]; + char tmpbuf[FILED_PATH_BUFFER_SIZE]; /** HTTP Request information **/ /*** Type of request (HEAD or GET) ***/ enum { FILED_REQUEST_METHOD_GET, @@ -75,10 +81,15 @@ struct { int present; off_t offset; /*** Range start ***/ off_t length; /*** Range length ***/ } range; + + struct { + int present; + char host[FILED_PATH_BUFFER_SIZE]; + } host; } headers; }; /* Log record */ struct filed_log_entry { @@ -625,23 +636,26 @@ return(buffer); } /* Process an HTTP request and return the path requested */ -static struct filed_http_request *filed_get_http_request(FILE *fp, struct filed_http_request *buffer_st) { +static struct filed_http_request *filed_get_http_request(FILE *fp, struct filed_http_request *buffer_st, struct filed_options *options) { char *method, *path; char *buffer, *workbuffer, *workbuffer_next; char *fgets_ret; size_t buffer_len; off_t range_start, range_end, range_length; int range_request; + int snprintf_ret; int i; + /* Set to default values */ range_start = 0; range_end = 0; range_request = 0; range_length = -1; + buffer_st->headers.host.present = 0; buffer = buffer_st->tmpbuf; buffer_len = sizeof(buffer_st->tmpbuf); fgets_ret = fgets(buffer, buffer_len, fp); @@ -660,11 +674,11 @@ buffer++; path = buffer; /* Terminate path component */ - buffer = strpbrk(buffer, "\r\n "); + buffer = strpbrk(path, "\r\n "); if (buffer != NULL) { *buffer = '\0'; buffer++; } @@ -711,10 +725,24 @@ if (*workbuffer != '\r' && *workbuffer != '\n') { range_end = strtoull(workbuffer, &workbuffer_next, 10); } } } + } else if (strncasecmp(buffer, "Host: ", 5) == 0) { + buffer_st->headers.host.present = 1; + + workbuffer = strpbrk(buffer + 5, "\r\n:"); + if (workbuffer != NULL) { + *workbuffer = '\0'; + } + + workbuffer = buffer + 5; + while (*workbuffer == ' ') { + workbuffer++; + } + + strcpy(buffer_st->headers.host.host, workbuffer); } if (memcmp(buffer, "\r\n", 2) == 0) { break; } @@ -737,10 +765,29 @@ /* Fill up structure to return */ buffer_st->headers.range.present = range_request; buffer_st->headers.range.offset = range_start; buffer_st->headers.range.length = range_length; + + /* If vhosts are enabled, compute new path */ + if (options->vhosts_enabled) { + if (buffer_st->headers.host.present == 1) { + buffer = buffer_st->tmpbuf; + buffer_len = sizeof(buffer_st->tmpbuf); + + snprintf_ret = snprintf(buffer, buffer_len, "/%s%s%s", + buffer_st->headers.host.host, + buffer_st->path[0] == '/' ? "" : "/", + buffer_st->path + ); + if (snprintf_ret >= 0) { + if (((unsigned int) snprintf_ret) < buffer_len) { + strcpy(buffer_st->path, buffer); + } + } + } + } return(buffer_st); } /* Return an error page */ @@ -762,11 +809,11 @@ return; } /* Handle a single request from a client */ -static void filed_handle_client(int fd, struct filed_http_request *request, struct filed_log_entry *log) { +static void filed_handle_client(int fd, struct filed_http_request *request, struct filed_log_entry *log, struct filed_options *options) { struct filed_fileinfo *fileinfo; ssize_t sendfile_ret; size_t sendfile_size; off_t sendfile_offset, sendfile_sent, sendfile_len; char *path; @@ -783,11 +830,11 @@ close(fd); return; } - request = filed_get_http_request(fp, request); + request = filed_get_http_request(fp, request, options); if (request == NULL) { filed_error_page(fp, date_current, 500, FILED_REQUEST_METHOD_GET); log->buffer[0] = '\0'; @@ -960,19 +1007,21 @@ /* Handle incoming connections */ static void *filed_worker_thread(void *arg_v) { struct filed_worker_thread_args *arg; struct filed_http_request request; struct filed_log_entry *log, local_dummy_log; + struct filed_options *options; struct sockaddr_in6 addr; socklen_t addrlen; int failure_count = 0, max_failure_count = FILED_MAX_FAILURE_COUNT; int master_fd, fd; /* Read arguments */ arg = arg_v; master_fd = arg->fd; + options = &arg->options; while (1) { /* Failure loop prevention */ if (failure_count > max_failure_count) { break; @@ -1011,11 +1060,11 @@ /* Reset failure count*/ failure_count = 0; /* Handle socket */ - filed_handle_client(fd, &request, log); + filed_handle_client(fd, &request, log, options); } /* Report error */ filed_log_msg("THREAD_DIED ABNORMAL"); @@ -1025,20 +1074,21 @@ local_dummy_log.type = 0; local_dummy_log.type = local_dummy_log.type; } /* Create worker threads */ -static int filed_worker_threads_init(int fd, int thread_count) { +static int filed_worker_threads_init(int fd, int thread_count, struct filed_options *options) { struct filed_worker_thread_args *arg; pthread_t threadid; int pthread_ret; int i; for (i = 0; i < thread_count; i++) { arg = malloc(sizeof(*arg)); arg->fd = fd; + memcpy(&arg->options, options, sizeof(*options)); pthread_ret = pthread_create(&threadid, NULL, filed_worker_thread, arg); if (pthread_ret != 0) { return(-1); } @@ -1056,10 +1106,11 @@ fprintf(output, "Usage: filed []\n"); fprintf(output, " Options:\n"); fprintf(output, " -h, --help\n"); fprintf(output, " -d, --daemon\n"); fprintf(output, " -v, --version\n"); + fprintf(output, " -V, --vhost\n"); fprintf(output, " -b
, --bind
\n"); fprintf(output, " -p , --port \n"); fprintf(output, " -t , --threads \n"); fprintf(output, " -c , --cache \n"); fprintf(output, " -l , --log \n"); @@ -1073,10 +1124,13 @@ fprintf(output, "\n"); fprintf(output, " -d (or --daemon) instructs filed to become a daemon after initializing\n"); fprintf(output, " the listening TCP socket and log files.\n"); fprintf(output, "\n"); fprintf(output, " -v (or --version) instructs filed print out the version number and exit.\n"); + fprintf(output, "\n"); + fprintf(output, " -V (or --vhost) instructs filed to prepend all requests with their HTTP\n"); + fprintf(output, " Host header.\n"); fprintf(output, "\n"); fprintf(output, " -b (or --bind) specifies the address to listen for incoming HTTP\n"); fprintf(output, " requests on. The default value is \"%s\".\n", BIND_ADDR); fprintf(output, "\n"); fprintf(output, " -p (or --port) specifies the TCP port number to listen for incoming HTTP\n"); @@ -1224,11 +1278,12 @@ return(0); } /* Run process */ int main(int argc, char **argv) { - struct option options[11]; + struct option options[12]; + struct filed_options thread_options; const char *bind_addr = BIND_ADDR, *newroot = NULL, *log_file = LOG_FILE; FILE *log_fp; uid_t user = 0; int port = PORT, thread_count = THREAD_COUNT; int cache_size = CACHE_SIZE; @@ -1235,10 +1290,13 @@ int init_ret, chroot_ret, setuid_ret, lookup_ret, chdir_ret; int setuid_enabled = 0, daemon_enabled = 0; int ch; int fd; + /* Set default values */ + thread_options.vhosts_enabled = 0; + /* Process arguments */ filed_getopt_long_setopt(&options[0], "port", required_argument, 'p'); filed_getopt_long_setopt(&options[1], "threads", required_argument, 't'); filed_getopt_long_setopt(&options[2], "cache", required_argument, 'c'); filed_getopt_long_setopt(&options[3], "bind", required_argument, 'b'); @@ -1246,12 +1304,13 @@ filed_getopt_long_setopt(&options[5], "root", required_argument, 'r'); filed_getopt_long_setopt(&options[6], "help", no_argument, 'h'); filed_getopt_long_setopt(&options[7], "daemon", no_argument, 'd'); filed_getopt_long_setopt(&options[8], "log", required_argument, 'l'); filed_getopt_long_setopt(&options[9], "version", no_argument, 'v'); - filed_getopt_long_setopt(&options[10], NULL, 0, 0); - while ((ch = getopt_long(argc, argv, "p:t:c:b:u:r:l:hdv", options, NULL)) != -1) { + filed_getopt_long_setopt(&options[10], "vhost", no_argument, 'V'); + filed_getopt_long_setopt(&options[11], NULL, 0, 0); + while ((ch = getopt_long(argc, argv, "p:t:c:b:u:r:l:hdvV", options, NULL)) != -1) { switch(ch) { case 'p': port = atoi(optarg); break; case 't': @@ -1278,10 +1337,14 @@ case 'l': log_file = strdup(optarg); break; case 'd': daemon_enabled = 1; + break; + case 'V': + thread_options.vhosts_enabled = 1; + break; case 'v': printf("filed version %s\n", FILED_VERSION); return(0); @@ -1365,11 +1428,11 @@ return(4); } /* Create worker threads */ - init_ret = filed_worker_threads_init(fd, thread_count); + init_ret = filed_worker_threads_init(fd, thread_count, &thread_options); if (init_ret != 0) { perror("filed_worker_threads_init"); return(5); }