diff
filebin.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 106 insertions(+), 31 deletions(-)
diff --git a/filebin.c b/filebin.c
index b2ac476..04b03f8 100644
--- a/filebin.c
+++ b/filebin.c
@@ -21,15 +21,15 @@
#include <unistd.h>
#define PORT 8080
-#define MAX_REQ (16 * 1024 * 1024)
+#define DEFAULT_MAX_REQ (16 * 1024 * 1024)
#define ENABLE_INDEX 0
#define DEFAULT_TTL_SEC (3 * 24 * 60 * 60)
#define MAX_TTL_SEC (3 * 24 * 60 * 60)
#define CLEANUP_INTERVAL_SEC 60
#define CLIENT_IDLE_TIMEOUT_SEC 30
#define INITIAL_REQ_CAP 8192
-#define MAX_CLIENTS 128
-#define STORAGE_CAP_GB 100
+#define DEFAULT_MAX_CLIENTS 128
+#define DEFAULT_STORAGE_CAP_GB 100
#if defined(__OpenBSD__) || defined(__linux__) || defined(__FreeBSD__) || \
defined(__NetBSD__) || defined(__DragonFly__)
@@ -37,9 +37,9 @@
#endif
#ifdef HAVE_CHROOT
-#define OPTSTR "dc:p:u:"
+#define OPTSTR "dc:m:n:p:s:t:u:"
#else
-#define OPTSTR "dp:u:"
+#define OPTSTR "dm:n:p:s:t:u:"
#endif
#ifdef HAVE_CHROOT
@@ -74,6 +74,12 @@ static const char *html_index =
"</select> "
"<button>Upload</button></form></div>";
+static size_t g_max_req = DEFAULT_MAX_REQ;
+static int g_max_clients = DEFAULT_MAX_CLIENTS;
+static off_t g_storage_cap_bytes = (off_t)DEFAULT_STORAGE_CAP_GB *
+ 1024 * 1024 * 1024;
+static long g_default_ttl_sec = DEFAULT_TTL_SEC;
+
static int has_suffix(const char *, const char *);
static int grow_reqbuf(struct client *);
static int hdr_get(const char *, const char *, char *, size_t);
@@ -83,6 +89,8 @@ static int isimg_ext(const char *);
static int req_done(const char *, size_t);
static int mk_fdir(void);
static int parse_uint(const char *, unsigned int *);
+static int parse_size_mb(const char *, size_t *);
+static int parse_size_gb(const char *, off_t *);
static int parse_ttl(const char *, long *);
static int parse_ug(const char *, uid_t *, gid_t *);
static int meta_read(const char *, time_t *);
@@ -403,13 +411,7 @@ meta_read(const char *name, time_t *exp)
static off_t
cap_bytes(void)
{
- off_t cap;
-
- cap = (off_t)STORAGE_CAP_GB;
- cap *= 1024;
- cap *= 1024;
- cap *= 1024;
- return cap;
+ return g_storage_cap_bytes;
}
static off_t
@@ -474,7 +476,7 @@ cleanup_expired(void)
continue;
}
if (meta_read(e->d_name, &exp) == 0)
- exp = st.st_mtime + DEFAULT_TTL_SEC;
+ exp = st.st_mtime + (time_t)g_default_ttl_sec;
if (now < exp)
continue;
snprintf(path, sizeof(path), "f/%s", e->d_name);
@@ -548,6 +550,40 @@ hdr_has_chunked(const char *hdr)
return 0;
}
+static int
+parse_size_mb(const char *s, size_t *out)
+{
+ unsigned long v;
+ char *end;
+
+ errno = 0;
+ v = strtoul(s, &end, 10);
+ if (s[0] == '\0' || *end != '\0' || errno != 0)
+ return 0;
+ if (v == 0 || v > (unsigned long)((size_t)-1 / (1024UL * 1024UL)))
+ return 0;
+ *out = (size_t)v * 1024UL * 1024UL;
+ return 1;
+}
+
+static int
+parse_size_gb(const char *s, off_t *out)
+{
+ unsigned long v;
+ char *end;
+ off_t gb;
+
+ errno = 0;
+ v = strtoul(s, &end, 10);
+ if (s[0] == '\0' || *end != '\0' || errno != 0)
+ return 0;
+ gb = (off_t)v;
+ if (gb <= 0 || gb > (off_t)-1 / (1024 * 1024 * 1024))
+ return 0;
+ *out = gb * 1024 * 1024 * 1024;
+ return 1;
+}
+
static int
parse_hex_size(const char *p, size_t len, size_t *out)
{
@@ -619,7 +655,7 @@ decode_chunked_body(const char *in, size_t inlen, char **out, size_t *outlen)
}
break;
}
- if (chunk > inlen - off || woff > MAX_REQ - chunk) {
+ if (chunk > inlen - off || woff > g_max_req - chunk) {
free(buf);
return 0;
}
@@ -816,7 +852,7 @@ serve_upload(int fd, char *req, size_t req_len)
unsigned char r[12];
FILE *fp;
- ttl_sec = DEFAULT_TTL_SEC;
+ ttl_sec = g_default_ttl_sec;
dec_body = NULL;
filename[0] = '\0';
file_body = NULL;
@@ -850,7 +886,7 @@ serve_upload(int fd, char *req, size_t req_len)
raw_len = req_len - (size_t)(body - req);
if (decode_chunked_body(body, raw_len, &dec_body, &dec_len) == 0 ||
- dec_len == 0 || dec_len > MAX_REQ) {
+ dec_len == 0 || dec_len > g_max_req) {
free(dec_body);
sendtxt(fd, "400 Bad Request", "bad chunked body\n");
return;
@@ -859,7 +895,7 @@ serve_upload(int fd, char *req, size_t req_len)
body_len = (long)dec_len;
} else {
body_len = atol(cl);
- if (body_len <= 0 || body_len > MAX_REQ) {
+ if (body_len <= 0 || (size_t)body_len > g_max_req) {
sendtxt(fd, "413 Payload Too Large", "bad size\n");
return;
}
@@ -1105,11 +1141,11 @@ grow_reqbuf(struct client *cl)
if (cl->off < cl->cap)
return 1;
- if (cl->cap >= MAX_REQ)
+ if (cl->cap >= g_max_req)
return 0;
newcap = cl->cap == 0 ? INITIAL_REQ_CAP : cl->cap * 2;
- if (newcap > MAX_REQ)
- newcap = MAX_REQ;
+ if (newcap > g_max_req)
+ newcap = g_max_req;
p = realloc(cl->req, newcap + 1);
if (p == NULL)
return 0;
@@ -1183,9 +1219,12 @@ usage(void)
{
#ifdef HAVE_CHROOT
fprintf(stderr,
- "usage: filebin [-d] [-c chrootdir] [-p port] [-u user:group]\n");
+ "usage: filebin [-d] [-c chrootdir] [-m maxmb] [-n maxclients] "
+ "[-p port] [-s storagegb] [-t ttl] [-u user:group]\n");
#else
- fprintf(stderr, "usage: filebin [-d] [-p port] [-u user:group]\n");
+ fprintf(stderr,
+ "usage: filebin [-d] [-m maxmb] [-n maxclients] [-p port] "
+ "[-s storagegb] [-t ttl] [-u user:group]\n");
#endif
exit(1);
}
@@ -1291,13 +1330,13 @@ main(int argc, char *argv[])
const char *chroot_dir;
#endif
char *end;
- struct client clients[MAX_CLIENTS];
- struct pollfd pfds[MAX_CLIENTS + 1];
+ struct client *clients;
+ struct pollfd *pfds;
struct sockaddr_in sin;
time_t last_cleanup;
uid_t run_uid;
gid_t run_gid;
- int map[MAX_CLIENTS + 1];
+ int *map;
int c;
int ch;
int do_daemon;
@@ -1308,10 +1347,18 @@ main(int argc, char *argv[])
int port;
int pr;
int s;
+ size_t max_req_mb;
+ off_t storage_cap_bytes;
+ unsigned int max_clients;
+ long ttl_cfg;
port = PORT;
do_daemon = 0;
do_drop = 0;
+ max_req_mb = DEFAULT_MAX_REQ / (1024 * 1024);
+ max_clients = DEFAULT_MAX_CLIENTS;
+ storage_cap_bytes = (off_t)DEFAULT_STORAGE_CAP_GB * 1024 * 1024 * 1024;
+ ttl_cfg = DEFAULT_TTL_SEC;
run_uid = 0;
run_gid = 0;
#ifdef HAVE_CHROOT
@@ -1334,6 +1381,27 @@ main(int argc, char *argv[])
port <= 0 || port > 65535)
usage();
break;
+ case 'm':
+ if (parse_size_mb(optarg, &g_max_req) == 0)
+ usage();
+ break;
+ case 'n':
+ if (parse_uint(optarg, &max_clients) == 0 || max_clients == 0)
+ usage();
+ if (max_clients > INT_MAX - 1)
+ usage();
+ g_max_clients = (int)max_clients;
+ break;
+ case 's':
+ if (parse_size_gb(optarg, &storage_cap_bytes) == 0)
+ usage();
+ g_storage_cap_bytes = storage_cap_bytes;
+ break;
+ case 't':
+ if (parse_ttl(optarg, &ttl_cfg) == 0)
+ usage();
+ g_default_ttl_sec = ttl_cfg;
+ break;
case 'u':
if (parse_ug(optarg, &run_uid, &run_gid) == 0)
diex("invalid user:group", optarg);
@@ -1347,6 +1415,13 @@ main(int argc, char *argv[])
argv += optind;
if (argc != 0)
usage();
+ (void)max_req_mb;
+
+ clients = calloc((size_t)g_max_clients, sizeof(*clients));
+ pfds = calloc((size_t)g_max_clients + 1, sizeof(*pfds));
+ map = calloc((size_t)g_max_clients + 1, sizeof(*map));
+ if (clients == NULL || pfds == NULL || map == NULL)
+ die("calloc");
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
@@ -1375,7 +1450,7 @@ main(int argc, char *argv[])
die("set_nb");
if (do_daemon != 0)
daemonize_self();
- for (i = 0; i < MAX_CLIENTS; i++) {
+ for (i = 0; i < g_max_clients; i++) {
clients[i].req = NULL;
clients[i].off = 0;
clients[i].cap = 0;
@@ -1394,7 +1469,7 @@ main(int argc, char *argv[])
cleanup_expired();
last_cleanup = now;
}
- for (i = 0; i < MAX_CLIENTS; i++) {
+ for (i = 0; i < g_max_clients; i++) {
if (clients[i].fd >= 0 &&
now - clients[i].last_active >= CLIENT_IDLE_TIMEOUT_SEC)
client_close(&clients[i]);
@@ -1405,7 +1480,7 @@ main(int argc, char *argv[])
pfds[0].revents = 0;
map[0] = -1;
nfds = 1;
- for (i = 0; i < MAX_CLIENTS; i++) {
+ for (i = 0; i < g_max_clients; i++) {
if (clients[i].fd < 0)
continue;
pfds[nfds].fd = clients[i].fd;
@@ -1434,11 +1509,11 @@ main(int argc, char *argv[])
close(c);
continue;
}
- for (i = 0; i < MAX_CLIENTS; i++) {
+ for (i = 0; i < g_max_clients; i++) {
if (clients[i].fd < 0)
break;
}
- if (i == MAX_CLIENTS) {
+ if (i == g_max_clients) {
close(c);
continue;
}
@@ -1481,7 +1556,7 @@ main(int argc, char *argv[])
client_close(cl);
break;
}
- if (cl->off >= MAX_REQ) {
+ if (cl->off >= g_max_req) {
sendtxt(cl->fd, "413 Payload Too Large", "bad size\n");
client_close(cl);
break;