commit 2b570ac

uint  ·  2026-01-25 18:24:30 +0000 UTC
parent f45ea23
refactor + improve saftey on http server, create config.h
5 files changed,  +169, -25
+1, -1
1@@ -1,6 +1,6 @@
2 ISC License
3 
4-Copyright 2025 uint
5+Copyright 2026 uint
6 
7 Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
8 
+9, -7
 1@@ -1,15 +1,17 @@
 2-CC=cc
 3+CC ?= cc
 4 
 5-CPPFLAGS=
 6-CFLAGS= -std=c99 -Wall -Wextra -O2 -Iserver/include
 7-
 8-DAE=paradosd
 9+CPPFLAGS =
10+CFLAGS = -std=c99 -Wall -Wextra -O2 -Iserver/include
11+OUT = parados
12 
13 all: parados
14 
15 parados:
16-	$(CC) server/*.c -o $(DAE)
17+	$(CC) server/*.c -o $(OUT)
18+
19+clean:
20+	rm -f $(OUT)
21 
22-clangd:
23+compile_flags:
24 	rm -f compile_flags.txt
25 	for f in ${CPPFLAGS} ${CFLAGS}; do echo $$f >> compile_flags.txt; done
+0, -0
+13, -0
 1@@ -0,0 +1,13 @@
 2+#include <stdbool.h>
 3+
 4+static const char*  media_dir     = "/path/to/media";
 5+static const char*  server_addr   = "127.0.0.1";
 6+static const int    server_port   = 3579;
 7+static const bool   verbose_log   = false;
 8+
 9+enum {
10+	HTTP_REQ_MAX   = 2048,
11+	HTTP_RESP_MAX  = 512,
12+	LISTEN_BACKLOG = 64,
13+};
14+
+146, -17
  1@@ -1,3 +1,5 @@
  2+#include <errno.h>
  3+#include <stddef.h>
  4 #include <stdio.h>
  5 #include <stdlib.h>
  6 #include <string.h>
  7@@ -7,9 +9,16 @@
  8 #include <arpa/inet.h>
  9 #include <netinet/in.h>
 10 
 11-#define PORT   6767
 12+#include "config.h"
 13 
 14 void die(const char* s, int e);
 15+int read_reqline(int c, char* method, size_t msz, char* path, size_t psz);
 16+void reply_text(int c, const char* status, const char* body);
 17+void run(void);
 18+void setup(void);
 19+int write_all(int fd, const void* buf, size_t n);
 20+
 21+int sock;
 22 
 23 void die(const char* s, int e)
 24 {
 25@@ -17,43 +26,163 @@ void die(const char* s, int e)
 26 	exit(e);
 27 }
 28 
 29-int main(void)
 30+int read_reqline(int c, char* method, size_t msz, char* path, size_t psz)
 31+{
 32+	char buf[HTTP_REQ_MAX];
 33+	size_t used = 0;
 34+
 35+	for (;;) {
 36+		if (used + 1 >= sizeof(buf))
 37+			return -1;
 38+
 39+		ssize_t r = read(c, buf + used, sizeof(buf) - 1 - used);
 40+		if (r <= 0)
 41+			return -1;
 42+
 43+		used += (size_t)r;
 44+		buf[used] = '\0';
 45+
 46+		char* eol = strstr(buf, "\r\n");
 47+		if (eol) {
 48+			*eol = '\0';
 49+			break;
 50+		}
 51+	}
 52+
 53+	if (sscanf(buf, "%15s %1023s", method, path) != 2)
 54+		return -1;
 55+
 56+	method[msz-1] = '\0';
 57+	path[psz-1] = '\0';
 58+
 59+	return 0;
 60+}
 61+
 62+void reply_text(int c, const char* status, const char* body)
 63+{
 64+	char resp[HTTP_RESP_MAX];
 65+	int n = snprintf(
 66+		resp, sizeof(resp),
 67+
 68+		"%s"
 69+		"Content-Type: text/plain\r\n"
 70+		"Content-Length: %zu\r\n"
 71+		"Connection: close\r\n"
 72+		"\r\n"
 73+		"%s",
 74+
 75+		status,
 76+		strlen(body), body
 77+	);
 78+
 79+	if (n < 0)
 80+		return;
 81+
 82+	if ((size_t)n >= sizeof(resp))
 83+		n = (int)(sizeof(resp) - 1);
 84+
 85+	(void)write_all(c, resp, (size_t)n);
 86+}
 87+
 88+void run(void)
 89+{
 90+	for (;;) {
 91+		int c = accept(sock, NULL, NULL);
 92+		if (c < 0) {
 93+			if (errno == EINTR)
 94+				continue;
 95+			continue;
 96+		}
 97+
 98+		char method[16];
 99+		char path[1024];
100+
101+		if (read_reqline(c, method, sizeof(method), path, sizeof(path)) < 0) {
102+			reply_text(c, "HTTP/1.1 400 Bad Request\r\n", "bad request\n");
103+			shutdown(c, SHUT_WR);
104+			close(c);
105+			continue;
106+		}
107+
108+		if (strcmp(method, "GET") != 0) {
109+			reply_text(c, "HTTP/1.1 405 Method Not Allowed\r\n", "method not allowed\n");
110+			shutdown(c, SHUT_WR);
111+			close(c);
112+			continue;
113+		}
114+
115+		if (strcmp(path, "/ping") == 0) {
116+			reply_text(c, "HTTP/1.1 200 OK\r\n", "ok\n");
117+			shutdown(c, SHUT_WR);
118+			close(c);
119+			continue;
120+		}
121+
122+		reply_text(c, "HTTP/1.1 404 Not Found\r\n", "not found\n");
123+		shutdown(c, SHUT_WR);
124+		close(c);
125+	}
126+}
127+
128+void setup(void)
129 {
130 	int ret = 1;
131-	int s = socket(AF_INET, SOCK_STREAM, 0);
132-	if (s < 0)
133+	sock = socket(AF_INET, SOCK_STREAM, 0);
134+	if (sock < 0)
135 		die("socket", EXIT_FAILURE);
136 
137 	int yes = 1;
138-	ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
139+	ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
140 	if (ret < 0)
141 		die("setsockopt", EXIT_FAILURE);
142 
143 	struct sockaddr_in a;
144 	memset(&a, 0, sizeof(a));
145 	a.sin_family = AF_INET;
146-	a.sin_port = htons(PORT);
147-	a.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
148+	a.sin_port = htons(server_port);
149+	ret = inet_pton(AF_INET, server_addr, &a.sin_addr);
150+	if (ret != 1)
151+		die("inet_pton", EXIT_FAILURE);
152 
153-	ret = bind(s, (struct sockaddr*)&a, sizeof(a));
154+	ret = bind(sock, (struct sockaddr*)&a, sizeof(a));
155 	if (ret < 0)
156 		die("bind", EXIT_FAILURE);
157 
158-	ret = listen(s, 64);
159+	ret = listen(sock, LISTEN_BACKLOG);
160 	if (ret < 0)
161 		die("listen", EXIT_FAILURE);
162 
163-	printf("listening on 127.0.0.1:%d\n", PORT);
164+	if (verbose_log)
165+		printf("listening on %s:%d\n", server_addr, server_port);
166+}
167 
168-	for (;;) {
169-		int c = accept(s, NULL, NULL);
170-		if (c < 0)
171-			continue;
172+int write_all(int fd, const void* buf, size_t n)
173+{
174+	const char* p = buf;
175 
176-		(void)write(c, "hello\n", 6);
177-		shutdown(c, SHUT_WR);
178-		close(c);
179+	while (n > 0) {
180+		ssize_t w = write(fd, p, n);
181+		if (w < 0) {
182+			if (errno == EINTR)
183+				continue;
184+			return -1;
185+		}
186+
187+		p += (size_t)w;
188+		n -= (size_t)w;
189 	}
190 
191+	return 0;
192+}
193+
194+int main(void)
195+{
196+	setup();
197+#ifdef __OpenBSD__
198+	if (pledge("stdio inet", NULL) < 0)
199+		die("pledge", EXIT_FAILURE);
200+#endif
201+	run();
202 	return EXIT_SUCCESS;
203 }
204+