#!/usr/bin/c -Wall -Wextra -std=gnu99 -pedantic -- #include #include #include #include #include #include #include #include #include #include #include #define _200 "HTTP/1.0 200 OK\r\n" #define _404 "HTTP/1.0 404 Not Found\r\n\r\nnot found\n" #define _405 "HTTP/1.0 405 Method Not Allowed\r\n\r\nnot allowed\n" #define EXTSN(IN, CMP) (!strncmp((IN)+strlen((IN))-strlen((CMP)), (CMP), strlen((CMP)))) // Extract path from a request. struct ref_t {const char *path, *ctyp;}; int xpath(char *req, struct ref_t *ref) { int mobile = !!strstr(req, "Mobile"); // Detect Mobile UA. char *p = strchr(req, ' '); // Extract path. int len = p - req; // Determine path length. req[len] = '\0'; // Truncate path string // Remove query strings. p = strchr(req, '?'); if (p) *p = '\0'; // Serve HTML copy to mobile devices b/c text-wrapping :( if (mobile && !!len && EXTSN(req, "txt")) strcat(req, ".html"); printf("%ld: %d,%s\n", time(NULL), mobile, req); fflush(stdout); // logging int is_html = !len || EXTSN(req, "html"); if (req[0] == '.') return 1; // dotfiles -> not allowed ref->path = (!len ? "index.html" : req); ref->ctyp = (is_html ? "text/html" : "text/plain"); return (!!strchr(req, '/')); // Disallow paths containing a slash } // Given a HTTP request, write to directly to the output fd. int respond(char *req, int ofd) { struct ref_t ref; // Drop non-GETs / netcat garbage / path traversals if (strncmp("GET ", req, 4) || !strchr(req+5, ' ') || xpath(req+5, &ref)) return write(ofd, _405, strlen(_405)); struct stat st; stat(ref.path, &st); // Not a regular file -> Return 404 if (!S_ISREG(st.st_mode)) return write(ofd, _404, strlen(_404)); int ifd = open(ref.path, O_RDONLY); // File does not exist -> Return 404 if (ifd == -1 || !S_ISREG(st.st_mode)) return write(ofd, _404, strlen(_404)); // Read and transmit the file sprintf(req, _200 "Content-Type: %s\r\nContent-Length: %ld\r\n\r\n", ref.ctyp, st.st_size); int ret = write(ofd, req, strlen(req)); if (ret != -1) ret = sendfile(ofd, ifd, 0, st.st_size); close(ifd); return ret; } int fd; void die(int sig) { shutdown(fd, SHUT_RDWR); close(fd); } int main(void) { // Create socket int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) err(1, "sock"); signal(SIGCHLD, SIG_IGN); // Ignore forked processes signal(SIGPIPE, SIG_IGN); // Ignore terminuated sockets if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int))) err(1, "setsockopt"); // Bind/Listen struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(2020); if (bind(sock, (struct sockaddr *) &addr, sizeof(addr))) err(1, "bind"); if (listen(sock, 16)) err(1, "listen"); // Backlog=16 while (1) { // Accept -> fork() socklen_t alen = sizeof(addr); fd = accept(sock, (struct sockaddr *) &addr, &alen); if (fd == -1) continue; if (fork()) close(fd); else break; } // Receive, Transmit, Cleanup char buf[1024]; int len = read(fd, buf, sizeof(buf)); if (len < 1) die(0); buf[len-1] = '\0'; respond(buf, fd); die(0); }