/* * vdestack_httpserv.c: IoTh tiny web server * * Copyright 2011-2020 Renzo Davoli - Virtual Square Team * University of Bologna - Italy * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #define _GNU_SOURCE #include <stdio.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <stddef.h> #include <stdint.h> #include <dirent.h> #include <pthread.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/wait.h> #include <netinet/in.h> #include <arpa/inet.h> #include <picoxnet.h> #include <nlinline+.h> NLINLINE_LIBMULTI(picox_) #define BUFSIZE 10240 static char *http_head = "HTTP/1.1 %d OK\r\n%sContent-Length: %d\r\n\r\n"; static char *ishtml = "Content-Type: text/html\r\n"; static char *html_head = "<HTML>\n<HEAD>\n<TITLE>vdestack IoTh http server</TITLE>\n</HEAD>\n<BODY>\n<H1>%s</H1>\n"; static char *html_tail = "</BODY></HTML>\n"; static void httpserv_senderr(int fd) { char *msg, *header; size_t size; FILE *f = open_memstream(&msg, &size); fprintf(f, html_head, "404: File not Found"); fprintf(f, html_tail); fclose(f); asprintf(&header, http_head, 404, ishtml, size); picox_write(fd, header, strlen(header)); picox_write(fd, msg, size); free(header); free(msg); } static void httpserv_sendfile(int fd, char *path) { char buf[BUFSIZE]; int infd = open(path, O_RDONLY); if (infd > 0) { int n; char *header; struct stat statbuf; fstat(infd, &statbuf); if (strlen(path)>5 && strcmp(path+(strlen(path)-5), ".html") == 0) asprintf(&header, http_head, 200, ishtml, statbuf.st_size); else asprintf(&header, http_head, 200, "", statbuf.st_size); picox_write(fd, header, strlen(header)); free(header); while ((n = read(infd, buf, BUFSIZE))>0) { picox_write(fd, buf, n); } close(infd); } } static void httpserv_senddir(int fd, char *path, char *webpath) { int i,n; char *msg, *header, *lastslash; struct dirent **namelist; size_t size; FILE *f=open_memstream(&msg, &size); fprintf(f, html_head, (*webpath) ? webpath : "/ (root)"); if (path == webpath) { lastslash = strrchr(webpath, '/'); if (lastslash) { int len = lastslash - webpath; fprintf(f, "<p><a href=\"/%*.*s\"> .. </a></p>\n", len, len, webpath); } else fprintf(f, "<p><a href=\"/\"> .. </a></p>\n"); } n = scandir(path, &namelist, NULL, alphasort); for (i = 0; i < n; i++) { if (namelist[i]->d_name[0] != '.') { fprintf(f,"<p><a href=\"%s/%s\"> %s </a></p>\n", webpath, namelist[i]->d_name, namelist[i]->d_name); } free(namelist[i]); } free(namelist); fprintf(f, html_tail); fclose(f); asprintf(&header, http_head, 200, ishtml, size); picox_write(fd, header, strlen(header)); picox_write(fd, msg, size); free(header); free(msg); } void *handle(void *arg) { int fd = (uintptr_t) arg; char buf[BUFSIZE]; char *end; int n = picox_read(fd, buf, BUFSIZE); if (n > 0) { if (strncmp(buf, "GET /", 5) == 0 && (end = strchr(buf+4, ' ')) != NULL){ struct stat sbuf; *end = 0; if (buf[5] == 0) httpserv_senddir(fd, ".", buf+5); else if (buf[5] != '/' && strstr(buf+4, "/../") == NULL && stat(buf+5, &sbuf)==0) { if (S_ISREG(sbuf.st_mode)) httpserv_sendfile(fd, buf+5); if (S_ISDIR(sbuf.st_mode)) httpserv_senddir(fd, buf+5, buf+5); } else httpserv_senderr(fd); } } picox_close(fd); return NULL; } static void perror_exit(char *s) { perror(s); exit(1); } int main(int arg, char *argv[]) { #define VDENET argv[1] #define IPADDR argv[2] #define PREFIX argv[3] int fd, connfd; struct picox *mystack = picox_newstack(VDENET); uint8_t ipaddr[16]; int ifindex = picox_if_nametoindex(mystack, "vde0"); unsigned long prefix = strtoul(PREFIX, NULL, 10); if (picox_linksetupdown(mystack, ifindex, 1) < 0) perror_exit("link up"); if (inet_pton(AF_INET6, IPADDR, ipaddr) == 1) { struct sockaddr_in6 servaddr = { .sin6_family = AF_INET6, .sin6_port = htons(80), .sin6_addr = in6addr_any}; if (picox_ipaddr_add(mystack, AF_INET6, ipaddr, prefix, ifindex) < 0) perror_exit(IPADDR); fd = picox_msocket(mystack, AF_INET6, SOCK_STREAM, 0); if (picox_bind(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) perror_exit("bind"); } else if (inet_pton(AF_INET, IPADDR, ipaddr) == 1) { struct sockaddr_in servaddr = { .sin_family = AF_INET, .sin_port = htons(80), .sin_addr.s_addr = htonl(INADDR_ANY)}; if (picox_ipaddr_add(mystack, AF_INET, ipaddr, prefix, ifindex) < 0) perror_exit(IPADDR); fd = picox_msocket(mystack, AF_INET, SOCK_STREAM, 0); if (picox_bind(fd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) perror_exit("bind"); } else { errno = EPROTO; perror_exit("error"); } picox_listen (fd, 5); for ( ; ; ) { pthread_t pt; connfd = picox_accept(fd, NULL, NULL); if (connfd < 0) break; pthread_create(&pt, NULL, handle, (void *) (uintptr_t)connfd); pthread_detach(pt); } picox_close(fd); picox_delstack(mystack); }