commit b3dc5f8
uint
·
2026-01-28 23:25:34 +0000 UTC
parent b2b738a
add rlimits, CLOEXEC, socket io timeouts .
5 files changed,
+110,
-6
+19,
-0
1@@ -91,6 +91,25 @@ Example:
2 .Bd -literal -offset indent
3 cors_origin=http://127.0.0.1:8000
4 .Ed
5+
6+.It Ic http_io_timeout
7+Maximum number of seconds the server will block on a single socket
8+read/write operation.
9+.Pp
10+This limits how long a client may stall for while sending a request or
11+while receiving a response.
12+If the timeout is exceeded, the connection is closed.
13+.Pp
14+This option used for protecting against stalled or bad-acting clients.
15+This does not limit the duration of a request or stream, only I/O operations.
16+.Pp
17+Valid values are integers in the range 1 to 300.
18+.Pp
19+Example:
20+.Bd -literal -offset indent
21+http_io_timeout=5
22+.Ed
23+
24 .El
25 .Sh AUTHENTICATION
26 Authentication entries are specified using repeated groups of the keys
+12,
-0
1@@ -28,6 +28,18 @@ server_port=8088
2 #verbose_log=false
3 verbose_log=true
4
5+# Security
6+#
7+# HTTP IO timeout (in seconds)
8+#
9+# Limits how long the server will block on a single socket read/write.
10+# This does not apply for requests/streaming.
11+# If a client stops sending data or stops reading responses for longer
12+# than this value, the connection is dropped.
13+#
14+# Recommended: LAN=5, WAN/VPN/Mobile=15-30
15+http_io_timeout=5
16+
17 # Cross-Origin Resource Sharing (CORS)
18 #
19 # Controls which web origins may access the API.
+20,
-3
1@@ -18,6 +18,7 @@ char server_addr[64] = "127.0.0.1";
2 int server_port = 8088;
3 bool verbose_log = true;
4 char cors_origin[1024] = "";
5+int http_io_timeout = 5;
6
7 static int load_path(const char* path)
8 {
9@@ -104,12 +105,23 @@ static int set_kv(const char* k, const char* v)
10 return 0;
11 }
12
13- /* auth */
14 if (strcmp(k, "cors_origin") == 0) {
15 snprintf(cors_origin, sizeof(cors_origin), "%s", v);
16 return 0;
17 }
18
19+ if (strcmp(k, "io_timeout_sec") == 0) {
20+ char* end = NULL;
21+ long t = strtol(v, &end, 10);
22+ if (!end || *end != '\0')
23+ return -1;
24+ if (t < 1 || t > 300)
25+ return -1;
26+ http_io_timeout = (int)t;
27+ return 0;
28+ }
29+
30+ /* auth */
31 if (strcmp(k, "user") == 0) {
32 return users_push(v);
33 }
34@@ -166,15 +178,20 @@ void config_load(void)
35 return;
36
37 log:
38+ (void)0;
39+
40+ char port[8];
41+ char tmo[16];
42+
43 LOG(true, "CONF", "Config File %s", path);
44 LOG(true, "CONF", "Media Directory %s", media_dir);
45 LOG(true, "CONF", "Server Address %s", server_addr);
46-
47- char port[8];
48 snprintf(port, sizeof(port), "%d", server_port);
49 LOG(true, "CONF", "Server Port %s", port);
50 LOG(true, "CONF", "Verbose Logging %s", (verbose_log) ? "true" : "false");
51 LOG(true, "CONF", "CORS origins %s", cors_origin);
52+ snprintf(tmo, sizeof(tmo), "%d", http_io_timeout);
53+ LOG(true, "CONF", "HTTP IO Timeout %s", tmo);
54
55 return;
56 }
+3,
-2
1@@ -7,9 +7,10 @@
2
3 extern char media_dir[4096];
4 extern char server_addr[64];
5-extern int server_port;
6-extern bool verbose_log;
7 extern char cors_origin[1024];
8+extern bool verbose_log;
9+extern int server_port;
10+extern int http_io_timeout;
11
12 enum {
13 HTTP_REQ_MAX = 8192,
+56,
-1
1@@ -9,6 +9,7 @@
2 */
3
4 #include <errno.h>
5+#include <fcntl.h>
6 #include <signal.h>
7 #include <stdarg.h>
8 #include <stdbool.h>
9@@ -16,7 +17,9 @@
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13+#include <sys/resource.h>
14 #include <sys/socket.h>
15+#include <sys/time.h>
16 #include <time.h>
17 #include <unistd.h>
18
19@@ -28,6 +31,10 @@
20 #include "log.h"
21 #include "scan.h"
22
23+static void apply_rlimits(void);
24+static void fd_set_cloexec(int fd);
25+static void sock_set_timeouts(int fd);
26+
27 void die(const char* s, int e);
28 void run(void);
29 void setup(void);
30@@ -35,6 +42,40 @@ void setup(void);
31 int sock;
32 struct library lib;
33
34+static void apply_rlimits(void)
35+{
36+ struct rlimit rl;
37+
38+ rl.rlim_cur = 0;
39+ rl.rlim_max = 0;
40+ (void)setrlimit(RLIMIT_CORE, &rl);
41+
42+ rl.rlim_cur = 1024;
43+ rl.rlim_max = 1024;
44+ (void)setrlimit(RLIMIT_NOFILE, &rl);
45+
46+ rl.rlim_cur = 256;
47+ rl.rlim_max = 256;
48+ (void)setrlimit(RLIMIT_NPROC, &rl);
49+}
50+
51+static void fd_set_cloexec(int fd)
52+{
53+ int f = fcntl(fd, F_GETFD);
54+ if (f >= 0)
55+ (void)fcntl(fd, F_SETFD, f | FD_CLOEXEC);
56+}
57+
58+static void sock_set_timeouts(int fd)
59+{
60+ struct timeval tv;
61+ tv.tv_sec = http_io_timeout;
62+ tv.tv_usec = 0;
63+
64+ (void)setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
65+ (void)setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
66+}
67+
68 void die(const char* s, int e)
69 {
70 perror(s);
71@@ -50,6 +91,7 @@ void run(void)
72 continue;
73 continue;
74 }
75+ fd_set_cloexec(c);
76 LOG(verbose_log, "CORE", "Connection Accepted");
77
78 pid_t pid = fork();
79@@ -59,6 +101,11 @@ void run(void)
80 }
81
82 if (pid == 0) {
83+#ifdef __OpenBSD__
84+ if (pledge("stdio inet rpath", NULL) < 0)
85+ _exit(EXIT_FAILURE);
86+#endif
87+ sock_set_timeouts(c);
88 (void)http_handle(c);
89 shutdown(c, SHUT_WR);
90 close(c);
91@@ -76,11 +123,13 @@ void setup(void)
92 them to turn into zombies */
93
94 config_load();
95+ apply_rlimits();
96
97 int ret = 1;
98 sock = socket(AF_INET, SOCK_STREAM, 0);
99 if (sock < 0)
100 die("socket", EXIT_FAILURE);
101+ fd_set_cloexec(sock);
102
103 int yes = 1;
104 ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
105@@ -108,6 +157,12 @@ void setup(void)
106 die("listen", EXIT_FAILURE);
107
108 LOG(verbose_log, "CORE", "Listening on %s:%d", server_addr, server_port);
109+#ifdef __OpenBSD__
110+ if (unveil(media_dir, "r") < 0)
111+ die("unveil", EXIT_FAILURE);
112+ if (unveil(NULL, NULL) < 0)
113+ die("unveil", EXIT_FAILURE);
114+#endif
115 }
116
117 int main(int argc, char* argv[])
118@@ -134,7 +189,7 @@ int main(int argc, char* argv[])
119
120 setup();
121 #ifdef __OpenBSD__
122- if (pledge("stdio inet", NULL) < 0)
123+ if (pledge("stdio inet proc rpath", NULL) < 0)
124 die("pledge", EXIT_FAILURE);
125 #endif /* __OpenBSD__ */
126 run();