commit bca5d6165ddaa8eb44ebca07121b9b30a993224f
parent ddee1411ee8620b891960a6eef4a9c45f11aaff6
Author: Anton Kling <anton@kling.gg>
Date: Sat, 15 Jan 2022 19:53:31 +0100
Formatted code using clang-format.
Diffstat:
M | smol_http.c | | | 281 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
1 file changed, 145 insertions(+), 136 deletions(-)
diff --git a/smol_http.c b/smol_http.c
@@ -31,99 +31,105 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <unistd.h>
// Should xxx.html not exist these will be supplied instead.
-#define DEFAULT_404_SITE "404 - Not Found\0"
-#define DEFAULT_400_SITE "400 - Bad Request\0"
+#define DEFAULT_404_SITE "404 - Not Found\0"
+#define DEFAULT_400_SITE "400 - Bad Request\0"
// Default port should -p not be supplied.
-#define DEFAULT_PORT 1337
+#define DEFAULT_PORT 1337
// Default directory should -d not be supplied.
-#define WEBSITE_ROOT "./site/"
+#define WEBSITE_ROOT "./site/"
-#define MAX_BUFFER 4096 // Size of the read buffer
+#define MAX_BUFFER 4096 // Size of the read buffer
#ifndef PATH_MAX
-#define PATH_MAX 4096
+#define PATH_MAX 4096
#endif
-#define COND_PERROR_EXP(condition, function_name, expression)\
- if(condition){perror(function_name);expression;}
+#define COND_PERROR_EXP(condition, function_name, expression) \
+ if (condition) { \
+ perror(function_name); \
+ expression; \
+ }
#define HAS_PLEDGE (__OpenBSD__ | __SerenityOS__)
#define HAS_UNVEIL (__OpenBSD__ | __SerenityOS__)
#if HAS_PLEDGE
-#define PLEDGE(promise, exec) COND_PERROR_EXP(-1 == pledge(promise, exec), "pledge", exit(1))
+#define PLEDGE(promise, exec) \
+ COND_PERROR_EXP(-1 == pledge(promise, exec), "pledge", exit(1))
#else
#define PLEDGE(promise, exec) ;
#endif
#if HAS_UNVEIL
-#define UNVEIL(path, permissions) COND_PERROR_EXP(-1 == unveil(path, permissions), "unveil", exit(1))
+#define UNVEIL(path, permissions) \
+ COND_PERROR_EXP(-1 == unveil(path, permissions), "unveil", exit(1))
#else
#define UNVEIL(path, permissions) ;
#endif
-#define ASSERT_NOT_REACHED assert(0)
+#define ASSERT_NOT_REACHED assert(0)
-#define COPYRIGHT_STATEMENT "\
+#define COPYRIGHT_STATEMENT \
+ "\
This program is licensed under Affero GNU Public License Version 3.\n\
See https://www.gnu.org/licenses/ for more information.\n\
THIS PROGRAM COMES WITHOUT ANY WARRANTY; without even the implied\n\
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
-static const struct
-{
- char *ext;
+static const struct {
+ char *ext;
char *type;
} mimes[] = {
- { "xml", "application/xml; charset=utf-8" },
- { "xhtml", "application/xhtml+xml; charset=utf-8" },
- { "html", "text/html; charset=utf-8" },
- { "htm", "text/html; charset=utf-8" },
- { "css", "text/css; charset=utf-8" },
- { "txt", "text/plain; charset=utf-8" },
- { "md", "text/plain; charset=utf-8" },
- { "c", "text/plain; charset=utf-8" },
- { "h", "text/plain; charset=utf-8" },
- { "gz", "application/x-gtar" },
- { "tar", "application/tar" },
- { "pdf", "application/x-pdf" },
- { "png", "image/png" },
- { "gif", "image/gif" },
- { "jpeg", "image/jpg" },
- { "jpg", "image/jpg" },
- { "iso", "application/x-iso9660-image" },
- { "webp", "image/webp" },
- { "svg", "image/svg+xml; charset=utf-8" },
- { "flac", "audio/flac" },
- { "mp3", "audio/mpeg" },
- { "ogg", "audio/ogg" },
- { "mp4", "video/mp4" },
- { "ogv", "video/ogg" },
- { "webm", "video/webm" },
+ { "xml", "application/xml; charset=utf-8" },
+ { "xhtml", "application/xhtml+xml; charset=utf-8" },
+ { "html", "text/html; charset=utf-8" },
+ { "htm", "text/html; charset=utf-8" },
+ { "css", "text/css; charset=utf-8" },
+ { "txt", "text/plain; charset=utf-8" },
+ { "md", "text/plain; charset=utf-8" },
+ { "c", "text/plain; charset=utf-8" },
+ { "h", "text/plain; charset=utf-8" },
+ { "gz", "application/x-gtar" },
+ { "tar", "application/tar" },
+ { "pdf", "application/x-pdf" },
+ { "png", "image/png" },
+ { "gif", "image/gif" },
+ { "jpeg", "image/jpg" },
+ { "jpg", "image/jpg" },
+ { "iso", "application/x-iso9660-image" },
+ { "webp", "image/webp" },
+ { "svg", "image/svg+xml; charset=utf-8" },
+ { "flac", "audio/flac" },
+ { "mp3", "audio/mpeg" },
+ { "ogg", "audio/ogg" },
+ { "mp4", "video/mp4" },
+ { "ogv", "video/ogg" },
+ { "webm", "video/webm" },
};
-const char * const get_mime(const char * file)
+const char *const get_mime(const char *file)
{
- for(;*file && *file++ != '.';);
- for(size_t i = 0;i < sizeof(mimes)/sizeof(mimes[0])-1;i++)
- if(0 == strcmp(mimes[i].ext, file))
+ for (; *file && *file++ != '.';)
+ ;
+ for (size_t i = 0; i < sizeof(mimes) / sizeof(mimes[0]) - 1; i++)
+ if (0 == strcmp(mimes[i].ext, file))
return mimes[i].type;
return "application/octet-stream";
}
-const char * const get_status_code(uint16_t code)
+const char *const status_code_to_error_message(uint16_t status_code)
{
- switch(code)
- {
-case 400:
- return "400 Bad Request";
-case 404:
- return "404 File Not Found";
-case 200: default:
- return "200 OK";
+ switch (status_code) {
+ case 400:
+ return "400 Bad Request";
+ case 404:
+ return "404 File Not Found";
+ case 200:
+ default:
+ return "200 OK";
}
}
@@ -135,21 +141,23 @@ void connection_handler(int socket_desc)
// We can ignore SIGPIPE as we already have checks
// that would deal with this.
- COND_PERROR_EXP(SIG_ERR == signal(SIGPIPE, SIG_IGN), "signal", goto cleanup);
+ COND_PERROR_EXP(SIG_ERR == signal(SIGPIPE, SIG_IGN), "signal",
+ goto cleanup);
ssize_t recv_size;
- COND_PERROR_EXP(-1 == (recv_size = recv(socket_desc,
- recv_buffer, MAX_BUFFER-1, 0)), "recv", goto cleanup);
+ COND_PERROR_EXP(-1 == (recv_size = recv(socket_desc, recv_buffer,
+ MAX_BUFFER - 1, 0)),
+ "recv", goto cleanup);
// Null terminate the request.
recv_buffer[recv_size] = 0;
- char * filename = recv_buffer;
+ char *filename = recv_buffer;
// Get to the second argument in the buffer.
- for(;*filename && *filename++ != ' ';);
+ for (; *filename && *filename++ != ' ';)
+ ;
// If we had only one argument then just provide 400.html.
- if(!(*filename))
- {
+ if (!(*filename)) {
filename = "/400.html";
status_code = 400;
goto skip_filename_parse;
@@ -157,114 +165,119 @@ void connection_handler(int socket_desc)
int enter_directory = 0;
uint16_t i;
- for(i = 0;filename[i] &&
- ' ' != filename[i] &&
- '\n' != filename[i] &&
- '\r' != filename[i];i++);
+ for (i = 0; filename[i] && ' ' != filename[i] && '\n' != filename[i] &&
+ '\r' != filename[i];
+ i++)
+ ;
filename[i] = 0;
struct stat statbuf;
- if(-1 == stat(filename, &statbuf))
- {
- if(ENOENT == errno)
+ if (-1 == stat(filename, &statbuf)) {
+ if (ENOENT == errno)
goto not_found;
goto cleanup;
}
- if(S_ISDIR(statbuf.st_mode))
- {
+ if (S_ISDIR(statbuf.st_mode)) {
enter_directory = 1;
chdir(filename);
filename = "index.html";
}
int fd;
- char * const_site_content;
+ char *const_site_content;
skip_filename_parse:
redo:
const_site_content = NULL;
- if(-1 == (fd = open(filename, O_RDONLY)))
- {
- if(1 == enter_directory)
- {
+ if (-1 == (fd = open(filename, O_RDONLY))) {
+ if (1 == enter_directory) {
enter_directory = 2;
goto write;
}
- if(400 == status_code)
- {
+ if (400 == status_code) {
const_site_content = DEFAULT_400_SITE;
goto write;
}
- if(0 == strcmp(filename, "/404.html") && 404 == status_code)
- {
+ if (0 == strcmp(filename, "/404.html") && 404 == status_code) {
const_site_content = DEFAULT_404_SITE;
goto write;
}
-not_found:
+ not_found:
filename = "/404.html";
status_code = 404;
goto redo;
}
write:
- if(0 > dprintf(socket_desc, "HTTP/1.0 %s\r\nContent-Type: %s\r\nServer: smol_http\r\n\r\n", get_status_code(status_code), get_mime(filename)))
- {
+ if (0 >
+ dprintf(socket_desc,
+ "HTTP/1.0 %s\r\nContent-Type: %s\r\nServer: smol_http\r\n\r\n",
+ status_code_to_error_message(status_code),
+ get_mime(filename))) {
puts("dprintf error");
goto cleanup;
}
- if(const_site_content)
- {
+ if (const_site_content) {
PLEDGE("stdio", NULL);
- COND_PERROR_EXP(-1 == write(socket_desc, const_site_content, strlen(const_site_content)), "write", /*NOP*/);
+ COND_PERROR_EXP(-1 == write(socket_desc, const_site_content,
+ strlen(const_site_content)),
+ "write",
+ /*NOP*/);
goto cleanup;
}
- // Should ./index.html be unable to be read we create a
+ // Should ./index.html be unable to be read we create a
// directory listing.
- if(2 == enter_directory)
- {
+ if (2 == enter_directory) {
// Get the directory contents and provide that to the client.
DIR *d;
- COND_PERROR_EXP(NULL == (d = opendir(".")), "opendir", goto cleanup);
+ COND_PERROR_EXP(NULL == (d = opendir(".")), "opendir",
+ goto cleanup);
char current_path[PATH_MAX];
char back_path[PATH_MAX];
- COND_PERROR_EXP(!realpath(".", current_path), "realpath", goto directory_cleanup)
- COND_PERROR_EXP(!realpath("..", back_path), "realpath", goto directory_cleanup)
- if(0 > dprintf(socket_desc, "Index of %s/<br><a href='%s'>./</a><br><a href='%s'>../</a><br>",
- current_path, current_path, back_path))
- {
+ COND_PERROR_EXP(!realpath(".", current_path), "realpath",
+ goto directory_cleanup)
+ COND_PERROR_EXP(!realpath("..", back_path), "realpath",
+ goto directory_cleanup)
+ if (0 >
+ dprintf(socket_desc,
+ "Index of %s/<br><a href='%s'>./</a><br><a href='%s'>../</a><br>",
+ current_path, current_path, back_path)) {
puts("dprintf error");
goto directory_cleanup;
}
- for(struct dirent * dir;(dir = readdir(d));)
- {
- if(0 == strcmp(dir->d_name, ".") || 0 == strcmp(dir->d_name, ".."))
+ for (struct dirent *dir; (dir = readdir(d));) {
+ if (0 == strcmp(dir->d_name, ".") ||
+ 0 == strcmp(dir->d_name, ".."))
continue;
char tmp_path[PATH_MAX];
- COND_PERROR_EXP(!realpath(dir->d_name, tmp_path), "realpath", break);
- if(0 > dprintf(socket_desc, "<a href='%s'>%s%s</a><br>", tmp_path, dir->d_name, (DT_DIR == dir->d_type)?"/":""))
- {
+ COND_PERROR_EXP(!realpath(dir->d_name, tmp_path),
+ "realpath", break);
+ if (0 > dprintf(socket_desc,
+ "<a href='%s'>%s%s</a><br>", tmp_path,
+ dir->d_name,
+ (DT_DIR == dir->d_type) ? "/" : "")) {
puts("dprintf error");
break;
}
}
-directory_cleanup:
+ directory_cleanup:
closedir(d);
goto cleanup;
}
PLEDGE("stdio", NULL);
char rwbuf[4096];
- for(int l;0 != (l = read(fd, rwbuf, sizeof(rwbuf)));)
- {
+ for (int l; 0 != (l = read(fd, rwbuf, sizeof(rwbuf)));) {
COND_PERROR_EXP(-1 == l, "read", break);
- COND_PERROR_EXP(-1 == write(socket_desc, rwbuf, l), "write", break);
+ COND_PERROR_EXP(-1 == write(socket_desc, rwbuf, l), "write",
+ break);
}
close(fd);
@@ -275,18 +288,17 @@ cleanup:
int drop_root_privleges(void)
{
- COND_PERROR_EXP(0 != seteuid(getuid()), "seteuid", return 0);
+ COND_PERROR_EXP(0 != seteuid(getuid()), "seteuid", return 0);
COND_PERROR_EXP(0 != setegid(getgid()), "setegid", return 0);
- if(0 == geteuid())
- {
+ if (0 == geteuid()) {
fprintf(stderr,
- "Error: Program can not be ran by a root user.\n");
+ "Error: Program can not be ran by a root user.\n");
return 0;
}
return 1;
}
-int init_server(short port, const char * website_root)
+int init_server(short port, const char *website_root)
{
int socket_desc, new_socket;
struct sockaddr_in server;
@@ -294,9 +306,9 @@ int init_server(short port, const char * website_root)
socklen_t c;
UNVEIL(website_root, "r");
- UNVEIL(NULL, NULL); // Disable usage of unveil()(this will also be
- // done by our pledge() call)
- // Enter a chroot jail ASAP.
+ // Disable usage of unveil()(this will also be
+ // done by our pledge() call)
+ UNVEIL(NULL, NULL);
COND_PERROR_EXP(0 != chroot(website_root), "chroot", return 1);
PLEDGE("stdio inet rpath exec id proc", "");
@@ -304,22 +316,21 @@ int init_server(short port, const char * website_root)
// But I will keep this check here just in case.
COND_PERROR_EXP(0 != chdir("/"), "chdir", return 1);
- COND_PERROR_EXP(-1 == (socket_desc =
- socket(AF_INET, SOCK_STREAM, 0)), "socket", return 1);
+ COND_PERROR_EXP(-1 == (socket_desc = socket(AF_INET, SOCK_STREAM, 0)),
+ "socket", return 1);
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(port);
- COND_PERROR_EXP(0 > bind(socket_desc,(struct sockaddr*)&server,
- sizeof(server)), "bind", return 1);
+ COND_PERROR_EXP(0 > bind(socket_desc, (struct sockaddr *)&server,
+ sizeof(server)),
+ "bind", return 1);
// Everything that requires root privleges is done,
// we can now drop privleges.
- if(!drop_root_privleges())
- {
- fprintf(stderr,
- "Unable to drop privleges.\n");
+ if (!drop_root_privleges()) {
+ fprintf(stderr, "Unable to drop privleges.\n");
return 1;
}
@@ -328,14 +339,13 @@ int init_server(short port, const char * website_root)
COND_PERROR_EXP(0 != listen(socket_desc, 3), "listen", return 1);
c = sizeof(struct sockaddr_in);
- for(;(new_socket = accept(socket_desc, &client, &c));)
- {
+ for (; (new_socket = accept(socket_desc, &client, &c));) {
COND_PERROR_EXP(-1 == new_socket, "accept", continue);
// Create a child and handle the connection
pid_t pid;
COND_PERROR_EXP(-1 == (pid = fork()), "fork", continue);
- if(0 != pid) // We are the parent.
+ if (0 != pid) // We are the parent.
{
close(new_socket);
continue;
@@ -349,31 +359,28 @@ int init_server(short port, const char * website_root)
return 0;
}
-void usage(const char * const str)
+void usage(const char *const str)
{
fprintf(stderr,
- "Usage: %s [-p PORT] [-d Website root directory] -h(Print this message)\n", str);
+ "Usage: %s [-p PORT] [-d Website root directory] -h(Print this message)\n",
+ str);
puts("---");
puts(COPYRIGHT_STATEMENT);
}
-int main(int argc, char ** argv)
+int main(int argc, char **argv)
{
- if(0 != geteuid())
- {
- fprintf(stderr,
- "Error: Program does not have root privleges.");
+ if (0 != geteuid()) {
+ fprintf(stderr, "Error: Program does not have root privleges.");
return 1;
}
short port = DEFAULT_PORT;
- char * website_root = WEBSITE_ROOT;
- for(int ch;-1 != (ch = getopt(argc, argv, "p:d:h"));)
- switch((char)ch)
- {
+ char *website_root = WEBSITE_ROOT;
+ for (int ch; - 1 != (ch = getopt(argc, argv, "p:d:h"));)
+ switch ((char)ch) {
case 'p':
- if(0 == (port = atoi(optarg)))
- {
+ if (0 == (port = atoi(optarg))) {
usage(argv[0]);
return 0;
}
@@ -381,7 +388,9 @@ int main(int argc, char ** argv)
case 'd':
website_root = optarg;
break;
- case '?': case ':': case 'h':
+ case '?':
+ case ':':
+ case 'h':
usage(argv[0]);
return 0;
}