void procreadfile(struct connstruct *cn) { int rv = read(cn->filedesc, cn->databuf, BLOCKSIZE); if (rv <= 0) { close(cn->filedesc); cn->filedesc = -1; if (cn->close_when_done) /* close immediately */ removeconnection(cn); else { if (cn->is_v1_0) /* die now */ removeconnection(cn); else { /* keep socket open - HTTP 1.1 */ cn->state = STATE_WANT_TO_READ_HEAD; cn->numbytes = 0; } } return; } cn->numbytes = rv; cn->state = STATE_WANT_TO_SEND_FILE; }
void read_post_data(struct connstruct *cn) { char buf[MAXREADLENGTH], *next; char *post_data; int rv; memset(buf, 0, sizeof(buf)); rv = special_read(cn, buf, sizeof(buf) - 1); if (rv <= 0) { if (rv < 0 || !cn->is_ssl) /* really dead? */ removeconnection(cn); return; } buf[rv] = '\0'; next = buf; post_data = &cn->post_data[cn->post_read]; while (next < &buf[rv]) { *post_data++ = *next++; cn->post_read++; if (cn->post_read == cn->content_length) { /* No more POST data to be copied */ *post_data = '\0'; cn->post_state = 0; buildactualfile(cn); cn->state = STATE_WANT_TO_SEND_HEAD; return; } } /* More POST data to read */ }
static void procdirlisting(struct connstruct *cn) { char buf[MAXREQUESTLENGTH]; char actualfile[1024]; if (cn->reqtype == TYPE_HEAD) { snprintf(buf, sizeof(buf), HTTP_VERSION " 200 OK\nContent-Type: text/html\n\n"); if (write(cn->networkdesc, buf, strlen(buf)) < 0) { printf("procdirlisting: could not write"); TTY_FLUSH(); } removeconnection(cn); return; } strcpy(actualfile, cn->actualfile); if ((cn->dirp = opendir(actualfile)) == NULL) { send_error(cn, 404); return; } snprintf(buf, sizeof(buf), HTTP_VERSION " 200 OK\nContent-Type: text/html\n\n" "<html><body>\n<title>Directory Listing</title>\n" "<h3>Directory listing of %s://%s%s</h3><br />\n", cn->is_ssl ? "https" : "http", cn->server_name, cn->filereq); special_write(cn, buf, strlen(buf)); cn->state = STATE_DOING_DIR; }
void procreadhead(struct connstruct *cn) { char buf[MAXREADLENGTH], *tp, *next; int rv; memset(buf, 0, sizeof(buf)); rv = special_read(cn, buf, sizeof(buf) - 1); if (rv <= 0) { if (rv < 0 || !cn->is_ssl) /* really dead? */ removeconnection(cn); return; } buf[rv] = '\0'; next = tp = buf; #ifdef CONFIG_HTTP_HAS_AUTHORIZATION cn->authorization[0] = 0; #endif /* Split up lines and send to procheadelem() */ while (*next != '\0') { /* If we have a blank line, advance to next stage */ if (*next == '\r' || *next == '\n') { #if defined(CONFIG_HTTP_HAS_CGI) if (cn->reqtype == TYPE_POST && cn->content_length > 0) { if (init_read_post_data(buf, next, cn, rv) == 0) return; } #endif buildactualfile(cn); cn->state = STATE_WANT_TO_SEND_HEAD; return; } while (*next != '\r' && *next != '\n' && *next != '\0') next++; if (*next == '\r') { *next = '\0'; next += 2; } else if (*next == '\n') *next++ = '\0'; if (procheadelem(cn, tp) == 0) return; tp = next; } }
/* * listenproc_cleanup * * Cleanup handler for the listener process. * * Parameters: * arg : pointer : A pointer to a listenerinfo_t structure containing * information required by the thread. * * Return Value: * The function returns NULL. * * Remarks: * This routine was originally intended to work with the pthread_cleanup_push * and pthread_cleanup_pop mechanism, but support for this mechanism isn't * widely supported in the environments in which this code was tested. * Therefore this routine is called explicitly by listenproc. * */ static void * listenproc_cleanup (void * arg) { listenerinfo_t * myli = (listenerinfo_t *) arg; if (verbose >= 10) printf ("listenproc_cleanup: shutting down connection\n"); removeconnection (myli->cmgr, myli->conn); destroyconnection (myli->conn); close (myli->socketfd); free (myli); return NULL; }
void procsendfile(struct connstruct *cn) { int rv = special_write(cn, cn->databuf, cn->numbytes); if (rv < 0) removeconnection(cn); else if (rv == cn->numbytes) { cn->state = STATE_WANT_TO_READ_FILE; } else if (rv == 0) { /* Do nothing */ } else { memmove(cn->databuf, cn->databuf + rv, cn->numbytes - rv); cn->numbytes -= rv; } }
void procdodir(struct connstruct *cn) { struct dirent *dp; char buf[MAXREQUESTLENGTH]; char encbuf[1024]; char *file; do { buf[0] = 0; if ((dp = readdir(cn->dirp)) == NULL) { snprintf(buf, sizeof(buf), "</body></html>\n"); special_write(cn, buf, strlen(buf)); removeconnection(cn); closedir(cn->dirp); return; } file = dp->d_name; /* if no index file, don't display the ".." directory */ if (cn->filereq[0] == '/' && cn->filereq[1] == '\0' && strcmp(file, "..") == 0) continue; /* don't display files beginning with "." */ if (file[0] == '.' && file[1] != '.') continue; /* make sure a '/' is at the end of a directory */ if (cn->filereq[strlen(cn->filereq) - 1] != '/') strcat(cn->filereq, "/"); /* see if the dir + file is another directory */ snprintf(buf, sizeof(buf), "%s%s", cn->actualfile, file); if (isdir(buf)) strcat(file, "/"); urlencode((uint8_t *)file, encbuf); snprintf(buf, sizeof(buf), "<a href=\"%s%s\">%s</a><br />\n", cn->filereq, encbuf, file); } while (special_write(cn, buf, strlen(buf))); }
static void send_error(struct connstruct *cn, int err) { char buf[MAXREQUESTLENGTH]; char *title; char *text; switch (err) { case 403: title = "Forbidden"; text = "File is protected"; #ifdef CONFIG_HTTP_VERBOSE printf("axhttpd: access to %s denied\n", cn->filereq); TTY_FLUSH(); #endif break; case 404: title = "Not Found"; text = title; break; case 418: title = "POST data size is too large"; text = title; break; default: title = "Unknown"; text = "Unknown"; break; } snprintf(buf, sizeof(buf), HTTP_VERSION" 200 OK\n" "Content-Type: text/html\n\n" "<html><body>\n<title>%s</title>\n" "<h1>Error %d - %s</h1>\n</body></html>\n", title, err, text); special_write(cn, buf, strlen(buf)); #ifdef CONFIG_HTTP_VERBOSE printf("axhttpd: http error: %s [%d]\n", title, err); TTY_FLUSH(); #endif removeconnection(cn); }
int main(int argc, char *argv[]) { fd_set rfds, wfds; struct connstruct *tp, *to; struct serverstruct *sp; int rnum, wnum, active; int i = 1; time_t currtime; char *httpAddress = NULL; int httpPort = CONFIG_HTTP_PORT; char *httpsAddress = NULL; int httpsPort = CONFIG_HTTP_HTTPS_PORT; uint32_t options = CONFIG_HTTP_DEFAULT_SSL_OPTIONS; char *portStr; char *private_key = NULL; char *cert = NULL; #ifdef WIN32 WORD wVersionRequested = MAKEWORD(2, 2); WSADATA wsaData; WSAStartup(wVersionRequested,&wsaData); #else signal(SIGPIPE, SIG_IGN); #if defined(CONFIG_HTTP_HAS_CGI) signal(SIGCHLD, reaper); #endif #ifdef CONFIG_HTTP_VERBOSE signal(SIGQUIT, die); #endif #endif #ifdef CONFIG_HTTP_VERBOSE signal(SIGTERM, die); signal(SIGINT, sigint_cleanup); #endif tdate_init(); /* get some command-line parameters */ while (argv[i] != NULL) { if (strcmp(argv[i], "-p") == 0 && argv[i+1] != NULL) { if ((portStr = strchr(argv[i+1], ':')) != NULL) { httpAddress = argv[i+1]; *portStr = 0; httpPort = atoi(portStr + 1); } else httpPort = atoi(argv[i+1]); i += 2; continue; } if (strcmp(argv[i], "-s") == 0 && argv[i+1] != NULL) { if ((portStr = strchr(argv[i+1], ':')) != NULL) { httpsAddress = argv[i+1]; *portStr = 0; httpsPort = atoi(portStr + 1); } else httpsPort = atoi(argv[i+1]); i += 2; continue; } if (strcmp(argv[i], "-w") == 0 && argv[i+1] != NULL) { webroot = argv[i+1]; i += 2; continue; } if (strcmp(argv[i], "-cert") == 0 && argv[i+1] != NULL) { cert = argv[i+1]; i += 2; continue; } if (strcmp(argv[i], "-key") == 0 && argv[i+1] != NULL) { private_key = argv[i+1]; i += 2; continue; } printf("%s:\n" " [-p [address:]httpport]\n" " [-s [address:]httpsport]\n" " [-key private_key]\n" " [-cert cert]\n" " [-w webroot]\n", argv[0]); exit(1); } for (i = 0; i < INITIAL_CONNECTION_SLOTS; i++) { tp = freeconns; freeconns = (struct connstruct *)calloc(1, sizeof(struct connstruct)); freeconns->next = tp; } if ((active = openlistener(httpAddress, httpPort)) == -1) { #ifdef CONFIG_HTTP_VERBOSE fprintf(stderr, "ERR: Couldn't bind to port %d\n", httpPort); #endif exit(1); } addtoservers(active); if ((active = openlistener(httpsAddress, httpsPort)) == -1) { #ifdef CONFIG_HTTP_VERBOSE fprintf(stderr, "ERR: Couldn't bind to port %d\n", httpsPort); #endif exit(1); } addtoservers(active); if (cert != NULL && private_key != NULL) options |= SSL_NO_DEFAULT_KEY; servers->ssl_ctx = ssl_ctx_new(options, CONFIG_HTTP_SESSION_CACHE_SIZE); servers->is_ssl = 1; if (cert != NULL && private_key != NULL) { printf("YEAH\n"); if (ssl_obj_load(servers->ssl_ctx, SSL_OBJ_RSA_KEY, private_key, NULL)) { #ifdef CONFIG_HTTP_VERBOSE fprintf(stderr, "ERR: Couldn't load private key %s\n", private_key); #endif exit(1); } if (ssl_obj_load(servers->ssl_ctx, SSL_OBJ_X509_CERT, cert, NULL)) { #ifdef CONFIG_HTTP_VERBOSE fprintf(stderr, "ERR: Couldn't load cert %s\n", cert); #endif exit(1); } } #if defined(CONFIG_HTTP_HAS_CGI) addcgiext(CONFIG_HTTP_CGI_EXTENSIONS); #endif #if defined(CONFIG_HTTP_VERBOSE) #if defined(CONFIG_HTTP_HAS_CGI) printf("addcgiext %s\n", CONFIG_HTTP_CGI_EXTENSIONS); #endif printf("%s: listening on ports %d (http) and %d (https)\n", server_version, httpPort, httpsPort); TTY_FLUSH(); #endif ax_chdir(); #ifdef CONFIG_HTTP_ENABLE_DIFFERENT_USER { struct passwd *pd = getpwnam(CONFIG_HTTP_USER); if (pd != NULL) { int res = setuid(pd->pw_uid); res |= setgid(pd->pw_gid); #if defined(CONFIG_HTTP_VERBOSE) if (res == 0) { printf("change to '%s' successful\n", CONFIG_HTTP_USER); TTY_FLUSH(); } #endif } } #endif #ifndef WIN32 #ifdef CONFIG_HTTP_IS_DAEMON if (fork() > 0) /* parent will die */ exit(0); setsid(); #endif #endif /* main loop */ while (1) { struct timeval tv = { 10, 0 }; FD_ZERO(&rfds); FD_ZERO(&wfds); rnum = wnum = -1; sp = servers; while (sp != NULL) /* read each server port */ { FD_SET(sp->sd, &rfds); if (sp->sd > rnum) rnum = sp->sd; sp = sp->next; } /* Add the established sockets */ tp = usedconns; currtime = time(NULL); while (tp != NULL) { if (currtime > tp->timeout) /* timed out? Kill it. */ { to = tp; tp = tp->next; removeconnection(to); continue; } if (tp->state == STATE_WANT_TO_READ_HEAD) { FD_SET(tp->networkdesc, &rfds); if (tp->networkdesc > rnum) rnum = tp->networkdesc; } if (tp->state == STATE_WANT_TO_SEND_HEAD) { FD_SET(tp->networkdesc, &wfds); if (tp->networkdesc > wnum) wnum = tp->networkdesc; } if (tp->state == STATE_WANT_TO_READ_FILE) { FD_SET(tp->filedesc, &rfds); if (tp->filedesc > rnum) rnum = tp->filedesc; } if (tp->state == STATE_WANT_TO_SEND_FILE) { FD_SET(tp->networkdesc, &wfds); if (tp->networkdesc > wnum) wnum = tp->networkdesc; } #if defined(CONFIG_HTTP_DIRECTORIES) if (tp->state == STATE_DOING_DIR) { FD_SET(tp->networkdesc, &wfds); if (tp->networkdesc > wnum) wnum = tp->networkdesc; } #endif tp = tp->next; } active = select(wnum > rnum ? wnum+1 : rnum+1, rnum != -1 ? &rfds : NULL, wnum != -1 ? &wfds : NULL, NULL, usedconns ? &tv : NULL); /* timeout? */ if (active == 0) continue; /* New connection? */ sp = servers; while (active > 0 && sp != NULL) { if (FD_ISSET(sp->sd, &rfds)) { handlenewconnection(sp->sd, sp->is_ssl); active--; } sp = sp->next; } /* Handle the established sockets */ tp = usedconns; while (active > 0 && tp != NULL) { to = tp; tp = tp->next; if (to->state == STATE_WANT_TO_READ_HEAD && FD_ISSET(to->networkdesc, &rfds)) { active--; #if defined(CONFIG_HTTP_HAS_CGI) if (to->post_state) read_post_data(to); else #endif procreadhead(to); } if (to->state == STATE_WANT_TO_SEND_HEAD && FD_ISSET(to->networkdesc, &wfds)) { active--; procsendhead(to); } if (to->state == STATE_WANT_TO_READ_FILE && FD_ISSET(to->filedesc, &rfds)) { active--; procreadfile(to); } if (to->state == STATE_WANT_TO_SEND_FILE && FD_ISSET(to->networkdesc, &wfds)) { active--; procsendfile(to); } #if defined(CONFIG_HTTP_DIRECTORIES) if (to->state == STATE_DOING_DIR && FD_ISSET(to->networkdesc, &wfds)) { active--; procdodir(to); } #endif } } return 0; }
/* Returns 1 if elems should continue being read, 0 otherwise */ static int procheadelem(struct connstruct *cn, char *buf) { char *delim, *value; if ((delim = strchr(buf, ' ')) == NULL) return 0; *delim = 0; value = delim + 1; if (strcmp(buf, "GET") == 0 || strcmp(buf, "HEAD") == 0 || strcmp(buf, "POST") == 0) { if (buf[0] == 'H') cn->reqtype = TYPE_HEAD; else if (buf[0] == 'P') cn->reqtype = TYPE_POST; if ((delim = strchr(value, ' ')) == NULL) /* expect HTTP type */ return 0; *delim++ = 0; urldecode(value); if (sanitizefile(value) == 0) { send_error(cn, 403); return 0; } #if defined(CONFIG_HTTP_HAS_CGI) decode_path_info(cn, value); #else my_strncpy(cn->filereq, value, MAXREQUESTLENGTH); #endif cn->if_modified_since = -1; if (strcmp(delim, "HTTP/1.0") == 0) /* v1.0 HTTP? */ cn->is_v1_0 = 1; } else if (strcasecmp(buf, "Host:") == 0) { if (sanitizehost(value) == 0) { removeconnection(cn); return 0; } my_strncpy(cn->server_name, value, MAXREQUESTLENGTH); } else if (strcasecmp(buf, "Connection:") == 0 && strcmp(value, "close") == 0) { cn->close_when_done = 1; } else if (strcasecmp(buf, "If-Modified-Since:") == 0) { cn->if_modified_since = tdate_parse(value); } else if (strcasecmp(buf, "Expect:") == 0) { /* supposed to be safe to ignore 100-continue */ if (strcasecmp(value, "100-continue") != 0) { send_error(cn, 417); /* expectation failed */ return 0; } } #ifdef CONFIG_HTTP_HAS_AUTHORIZATION else if (strcasecmp(buf, "Authorization:") == 0 && strncmp(value, "Basic ", 6) == 0) { int size = sizeof(cn->authorization); if (base64_decode(&value[6], strlen(&value[6]), (uint8_t *)cn->authorization, &size)) cn->authorization[0] = 0; /* error */ else cn->authorization[size] = 0; } #endif #if defined(CONFIG_HTTP_HAS_CGI) else if (strcasecmp(buf, "Content-Length:") == 0) { sscanf(value, "%d", &cn->content_length); } else if (strcasecmp(buf, "Content-Type:") == 0) { my_strncpy(cn->cgicontenttype, value, MAXREQUESTLENGTH); } else if (strcasecmp(buf, "Cookie:") == 0) { my_strncpy(cn->cookie, value, MAXREQUESTLENGTH); } #endif return 1; }
static void proccgi(struct connstruct *cn) { int tpipe[2], spipe[2]; char *myargs[3]; char cgienv[CGI_ARG_SIZE][MAXREQUESTLENGTH]; char * cgiptr[CGI_ARG_SIZE + 4]; const char *type = "HEAD"; int cgi_index = 0, i; pid_t pid; snprintf(cgienv[0], MAXREQUESTLENGTH, HTTP_VERSION" 200 OK\nServer: %s\n%s", server_version, (cn->reqtype == TYPE_HEAD) ? "\n" : ""); special_write(cn, cgienv[0], strlen(cgienv[0])); if (cn->reqtype == TYPE_HEAD) { removeconnection(cn); return; } #ifdef CONFIG_HTTP_VERBOSE printf("[CGI]: %s:/%s\n", cn->is_ssl ? "https" : "http", cn->filereq); TTY_FLUSH(); #endif /* set up pipe that is used for sending POST query data to CGI script*/ if (cn->reqtype == TYPE_POST) { if (pipe(spipe) == -1) { printf("[CGI]: could not create pipe"); TTY_FLUSH(); return; } } if (pipe(tpipe) == -1) { printf("[CGI]: could not create pipe"); TTY_FLUSH(); return; } /* * use vfork() instead of fork() for performance */ if ((pid = vfork()) > 0) { /* parent */ /* Send POST query data to CGI script */ if ((cn->reqtype == TYPE_POST) && (cn->content_length > 0)) { if (write(spipe[1], cn->post_data, cn->content_length) == -1) { printf("[CGI]: could write to pipe"); TTY_FLUSH(); return; } close(spipe[0]); close(spipe[1]); /* free the memory that is allocated in read_post_data() */ free(cn->post_data); cn->post_data = NULL; } /* Close the write descriptor */ close(tpipe[1]); cn->filedesc = tpipe[0]; cn->state = STATE_WANT_TO_READ_FILE; cn->close_when_done = 1; return; } if (pid < 0) /* vfork failed */ exit(1); /* The problem child... */ /* Our stdout/stderr goes to the socket */ dup2(tpipe[1], 1); dup2(tpipe[1], 2); close(tpipe[0]); close(tpipe[1]); /* If it was a POST request, send the socket data to our stdin */ if (cn->reqtype == TYPE_POST) { dup2(spipe[0], 0); close(spipe[0]); close(spipe[1]); } else /* Otherwise we can shutdown the read side of the sock */ shutdown(cn->networkdesc, 0); myargs[0] = CONFIG_HTTP_CGI_LAUNCHER; myargs[1] = cn->actualfile; myargs[2] = NULL; /* * set the cgi args. A url is defined by: * http://$SERVER_NAME:$SERVER_PORT$SCRIPT_NAME$PATH_INFO?$QUERY_STRING * TODO: other CGI parameters? */ sprintf(cgienv[cgi_index++], "SERVER_SOFTWARE=%s", server_version); strcpy(cgienv[cgi_index++], "DOCUMENT_ROOT=" CONFIG_HTTP_WEBROOT); snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, "SERVER_NAME=%s", cn->server_name); sprintf(cgienv[cgi_index++], "SERVER_PORT=%d", cn->is_ssl ? CONFIG_HTTP_HTTPS_PORT : CONFIG_HTTP_PORT); snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, "REQUEST_URI=%s", cn->uri_request); snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, "SCRIPT_NAME=%s", cn->filereq); snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, "PATH_INFO=%s", cn->uri_path_info); snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, "QUERY_STRING=%s", cn->uri_query); snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, "REMOTE_ADDR=%s", cn->remote_addr); snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, "HTTP_COOKIE=%s", cn->cookie); /* note: small size */ #if defined(CONFIG_HTTP_HAS_AUTHORIZATION) snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, "REMOTE_USER=%s", cn->authorization); #endif switch (cn->reqtype) { case TYPE_GET: type = "GET"; break; #if defined(CONFIG_HTTP_HAS_CGI) case TYPE_POST: type = "POST"; sprintf(cgienv[cgi_index++], "CONTENT_LENGTH=%d", cn->content_length); snprintf(cgienv[cgi_index++], MAXREQUESTLENGTH, "CONTENT_TYPE=%s", cn->cgicontenttype); break; #endif } sprintf(cgienv[cgi_index++], "REQUEST_METHOD=%s", type); if (cn->is_ssl) strcpy(cgienv[cgi_index++], "HTTPS=on"); if (cgi_index >= CGI_ARG_SIZE) { printf("Content-type: text/plain\n\nToo many CGI args (%d, %d)\n", cgi_index, CGI_ARG_SIZE); _exit(1); } /* copy across the pointer indexes */ for (i = 0; i < cgi_index; i++) cgiptr[i] = cgienv[i]; cgiptr[i++] = "AUTH_TYPE=Basic"; cgiptr[i++] = "GATEWAY_INTERFACE=CGI/1.1"; cgiptr[i++] = "SERVER_PROTOCOL="HTTP_VERSION; cgiptr[i] = NULL; execve(myargs[0], myargs, cgiptr); printf("Content-type: text/plain\n\nshouldn't get here\n"); _exit(1); }
/* In this function we assume that the file has been checked for * maliciousness (".."s, etc) and has been decoded */ void procsendhead(struct connstruct *cn) { char buf[MAXREQUESTLENGTH]; struct stat stbuf; time_t t_time; struct tm *ptm; char date[32]; char last_modified[32]; char expires[32]; int file_exists; /* are we trying to access a file over the HTTP connection instead of a * HTTPS connection? Or is this directory disabled? */ if (htaccess_check(cn)) { send_error(cn, 403); return; } #ifdef CONFIG_HTTP_HAS_AUTHORIZATION if (auth_check(cn)) { /* see if there is a '.htpasswd' file */ #ifdef CONFIG_HTTP_VERBOSE printf("axhttpd: access to %s denied\n", cn->filereq); TTY_FLUSH(); #endif removeconnection(cn); return; } #endif file_exists = stat(cn->actualfile, &stbuf); #if defined(CONFIG_HTTP_HAS_CGI) if (file_exists != -1 && cn->is_cgi) { proccgi(cn); return; } #endif /* look for "index.html"? */ if (isdir(cn->actualfile)) { char tbuf[MAXREQUESTLENGTH]; snprintf(tbuf, MAXREQUESTLENGTH, "%s%s", cn->actualfile, index_file); if ((file_exists = stat(tbuf, &stbuf)) != -1) my_strncpy(cn->actualfile, tbuf, MAXREQUESTLENGTH); else { #if defined(CONFIG_HTTP_DIRECTORIES) /* If not, we do a directory listing of it */ procdirlisting(cn); #else send_error(cn, 404); #endif return; } } if (file_exists == -1) { send_error(cn, 404); return; } time(&t_time); ptm = gmtime(&t_time); strftime(date, sizeof(date), rfc1123_format, ptm); /* has the file been read before? */ if (cn->if_modified_since != -1) { ptm = gmtime(&stbuf.st_mtime); t_time = mktime(ptm); if (cn->if_modified_since >= t_time) { snprintf(buf, sizeof(buf), HTTP_VERSION" 304 Not Modified\nServer: " "%s\nDate: %s\n\n", server_version, date); special_write(cn, buf, strlen(buf)); cn->state = STATE_WANT_TO_READ_HEAD; return; } } if (cn->reqtype == TYPE_HEAD) { removeconnection(cn); return; } else { int flags = O_RDONLY; #if defined(CONFIG_PLATFORM_CYGWIN) flags |= O_BINARY; #endif cn->filedesc = open(cn->actualfile, flags); if (cn->filedesc < 0) { send_error(cn, 404); return; } ptm = gmtime(&stbuf.st_mtime); strftime(last_modified, sizeof(last_modified), rfc1123_format, ptm); t_time += CONFIG_HTTP_TIMEOUT; ptm = gmtime(&t_time); strftime(expires, sizeof(expires), rfc1123_format, ptm); snprintf(buf, sizeof(buf), HTTP_VERSION" 200 OK\nServer: %s\n" "Content-Type: %s\nContent-Length: %ld\n" "Date: %s\nLast-Modified: %s\nExpires: %s\n\n", server_version, getmimetype(cn->actualfile), (long) stbuf.st_size, date, last_modified, expires); special_write(cn, buf, strlen(buf)); #ifdef CONFIG_HTTP_VERBOSE printf("axhttpd: %s:/%s\n", cn->is_ssl ? "https" : "http", cn->filereq); TTY_FLUSH(); #endif cn->state = STATE_WANT_TO_READ_FILE; } }