💾 Archived View for gemini.rmf-dev.com › repo › Vaati › UploadService › files › a630a85d2b92de396ae0… captured on 2023-12-28 at 15:49:19. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-09-08)
-=-=-=-=-=-=-
0 /* See LICENSE for license details. */
1 #if defined(__linux__) || defined(__sun)
2 #define HAS_SENDFILE
3 #include <sys/sendfile.h>
4 #endif
5 #ifdef __FreeBSD__
6 #define HAS_SENDFILE
7 #define __BSD_VISIBLE 1
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <sys/uio.h>
11 #endif
12 #include "server.h"
13 #include "parser.h"
14 #include <stdint.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <strings.h>
18 #include <sys/types.h>
19 #include <time.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #ifdef __sun
24 #include <signal.h>
25 #endif
26 #define _GNU_SOURCE
27 size_t strlcpy(char*, const char*, size_t);
28
29 char header[] =
30 "HTTP/1.0 200 OK\r\n"
31 "Connection: close\r\n"
32 "Content-Length: %d\r\n"
33 "Content-Type: %s\r\n"
34 "Server: uploadservice\r\n\r\n";
35
36 void
37 print_now()
38 {
39 time_t now = time(NULL);
40 struct tm tm = *localtime(&now);
41 printf("[%d/%02d/%02d %02d:%02d:%02d] ",
42 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
43 tm.tm_hour, tm.tm_min, tm.tm_sec);
44 }
45
46 uint32_t
47 fnv(const char* ptr, int len)
48 {
49 uint32_t hash = 2166136261;
50 const char* start = ptr;
51 while (ptr - start < len) {
52 hash = hash * 2166136261;
53 hash = hash ^ *ptr;
54 ptr++;
55 }
56 return hash;
57 }
58
59 int verify_name = 0, listener = 0;
60
61 struct file {
62 char* data;
63 size_t size;
64 size_t length;
65 char* uri;
66 char type[256];
67 int hash;
68 };
69
70 #define FILES_TABLE_SIZE 64
71 struct file files[FILES_TABLE_SIZE] = {0};
72
73 int
74 load_file(const char* path, const char* uri, const char* type)
75 {
76 char buf[4096];
77 int hash, i;
78 size_t len, header_len;
79 FILE *f;
80
81 f = fopen(path, "rb");
82 if (!f) {
83 printf("%s: failed to open file\n", path);
84 return -1;
85 }
86 fseek(f, 0, SEEK_END);
87 len = ftell(f);
88 fseek(f, 0, SEEK_SET);
89 header_len = snprintf(buf, sizeof(buf), header, len, type);
90 if (header_len >= sizeof(buf)) {
91 printf("%s: header buffer overflow\n", path);
92 return -1;
93 }
94 hash = fnv(uri, strlen(uri))%FILES_TABLE_SIZE;
95 files[hash].data = malloc(header_len + len);
96 strlcpy(files[hash].data, buf, header_len+1);
97 files[hash].size = header_len + len;
98 if (fread(&files[hash].data[header_len], 1, len, f) != len) {
99 free(files[hash].data);
100 fclose(f);
101 printf("%s: failed to read file content\n", path);
102 return -1;
103 }
104 files[hash].uri = malloc(strlen(uri));
105 strcpy(files[hash].uri, uri);
106 strlcpy(files[hash].type, type, sizeof(files[hash].type));
107 files[hash].length = len;
108 i = 0;
109 for (; i < FILES_TABLE_SIZE; i++) {
110 if (files[i].hash != hash) continue;
111 printf("Files hash table is too small, "
112 "a collision happened\n");
113 exit(0);
114 }
115 fclose(f);
116 return 0;
117 }
118
119 #include <sys/stat.h>
120 #include <sys/socket.h>
121 #include <netinet/in.h>
122
123 char data_404[] =
124 "<html>\n"
125 "<head>\n"
126 " <title>404 Not Found</title>\n"
127 "</head>\n"
128 "<body>\n"
129 " <a href=\"/\">Go back</a>\n"
130 " <h1>404 Not Found</h1>\n"
131 "</body>\n"
132 "</html>\n";
133
134 char data_upload[] =
135 "<html>\n"
136 "<head>\n"
137 " <title>File uploaded</title>\n"
138 "</head>\n"
139 "<body>\n"
140 " <a href=\"/upload\">Go back</a>\n"
141 " <h1>File succesfully uploaded</h1>\n"
142 " <p>Download link : <a href=\"%s\">%s</a></p>\n"
143 "</body>\n"
144 "</html>\n";
145
146 int
147 path_to_url(const char* path, char* url, int len)
148 {
149 int j = 0;
150 int i = 0;
151 for (; path[i] && j < len; i++) {
152 char c = path[i];
153 if ((c >= 'a' && c <= 'z') ||
154 (c >= 'A' && c <= 'Z') ||
155 (c >= '0' && c <= '9') ||
156 c == '.' || c == '/' || c == '_') {
157 url[j] = path[i];
158 j++;
159 continue;
160 }
161 snprintf(&url[j], len - j, "%%%02X", c);
162 j += 3;
163 }
164 url[j] = '\0';
165 return j;
166 }
167
168 int
169 server_upload(struct http_request* req)
170 {
171 char file_name[256], path[1024], url[2048];
172 char *start, *name_ptr, *slash_ptr;
173 int name_fail, fd, len;
174
175 /* path + name */
176 start = strstr(req->packet, "\r\n\r\n");
177 if (!start) start = req->packet;
178 name_ptr = strstr(start, "filename=\"");
179 name_fail = 1;
180 if (name_ptr) {
181 char* name_end;
182
183 name_ptr += sizeof("filename=\"") - 1;
184 name_end = strstr(name_ptr, "\"\r\n");
185 if (name_end && (size_t)(name_end - name_ptr) <
186 sizeof(file_name)) {
187 strlcpy(file_name, name_ptr, name_end - name_ptr + 1);
188 name_fail = 0;
189 }
190 }
191 if (name_fail)
192 strlcpy(file_name, "generic.dat", sizeof(file_name));
193 snprintf(path, sizeof(path), "/download/%08X%08X/%s",
194 (unsigned)(rand() * rand() + rand()),
195 (unsigned)(rand() * rand() - rand()), file_name);
196
197 start = strstr(start+4, "\r\n\r\n");
198 if (!start) start = req->content;
199 slash_ptr = strrchr(path, '/');
200 if (!slash_ptr) return -1;
201 *slash_ptr = '\0';
202 mkdir(&path[1], 0700);
203 *slash_ptr = '/';
204 fd = open((char*)&path[1], O_RDWR|O_CREAT, 0600);
205 if (fd < 0)
206 return -1;
207 req->data = fd;
208 path_to_url(path, url, sizeof(url));
209 len = snprintf(req->updata, sizeof(req->updata),
210 data_upload, url, file_name);
211 snprintf(req->header, sizeof(req->header), header, len, "text/html");
212 http_parse(req);
213 return 0;
214 }
215
216 const char* extension[] = {
217 ".html",
218 ".htm",
219 ".txt",
220 ".aac",
221 ".mp3",
222 ".flac",
223 ".ogg",
224 ".wav",
225 ".midi",
226 ".avi",
227 ".webm",
228 ".mp4",
229 ".gif",
230 ".png",
231 ".jpg",
232 ".jpeg",
233 ".bmp",
234 ".ico",
235 ".svg",
236 ".pdf",
237 ".tar",
238 ".zip",
239 ".json"
240 };
241
242 const char* mime[] = {
243 "text/html",
244 "text/html",
245 "text/plain",
246 "audio/mpeg",
247 "audio/mpeg",
248 "audio/flac",
249 "audio/ogg",
250 "audio/wav",
251 "audio/midi",
252 "video/x-msvideo",
253 "video/webm",
254 "video/mp4",
255 "image/gif",
256 "image/png",
257 "image/jpeg",
258 "image/jpeg",
259 "image/bmp",
260 "image/vnd.microsoft.icon",
261 "image/svg+xml",
262 "application/pdf",
263 "application/tar",
264 "application/zip",
265 "application/json"
266 };
267
268 int
269 mime_from_extension(const char* ext)
270 {
271 size_t i;
272 for (i = 0; i < sizeof(mime)/sizeof(char*); i++)
273 if (!strcasecmp(ext, extension[i])) return i;
274 return -1;
275 }
276
277 int
278 is_hex(char c)
279 {
280 return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ||
281 (c >= '0' && c <= '9'));
282 }
283
284 int
285 format_path(const char* data, char* buf, int len)
286 {
287 int j = 0;
288 int i = 0;
289 for (; data[i] && j < len; i++) {
290 while (data[i] == '%') {
291 char hex[3];
292 unsigned int value;
293 char* error = NULL;
294 hex[0] = data[i+1];
295 if (!is_hex(hex[0])) break;
296 hex[1] = data[i+2];
297 if (!is_hex(hex[1])) break;
298 hex[2] = '\0';
299 value = strtoul(hex, NULL, 16);
300 if (!value || (error && *error) || value == '.')
301 break;
302 i+=2;
303 buf[j] = value;
304 j++;
305 goto end;
306 }
307 buf[j] = data[i];
308 j++;
309 end:;
310 }
311 buf[j] = 0;
312 return 0;
313 }
314
315 int
316 server_download(struct http_request* req)
317 {
318 char path[1024], header_buf[1024];
319 char *ptr, *file_name, *ext;
320 const char* mime_ptr;
321 int fd, mime_id, header_len;
322 size_t length;
323 FILE *f;
324
325 ptr = strstr(req->uri, "/download/");
326 if (!ptr) {
327 return -1;
328 }
329 ptr += sizeof("/download/") - 1;
330 file_name = strchr(ptr, '/');
331 if (!file_name)
332 return -1;
333 *file_name = '\0';
334 *file_name = '/';
335 if ((file_name[1] == '.' && file_name[2] == '.') ||
336 strchr(file_name+1, '/'))
337 return -1;
338 file_name++;
339 ptr -= sizeof("/download/") - 2;
340 format_path(ptr, path, sizeof(path));
341 fd = open(path, O_RDWR);
342 if (fd < 0) return -1;
343 f = fdopen(fd, "rb");
344 fseek(f, 0, SEEK_END);
345 length = ftell(f);
346 fseek(f, 0, SEEK_SET);
347 req->length = length;
348 req->data = fd;
349 /* mime */
350 ext = strrchr(file_name, '.');
351 mime_id = !ext?-1:mime_from_extension(ext);
352 mime_ptr = ((mime_id==-1)?"application/octet-stream":mime[mime_id]);
353 /* header */
354 header_len = snprintf(header_buf, sizeof(header_buf),
355 header, length, mime_ptr);
356 send(req->socket, header_buf, header_len, 0);
357 return 0;
358 }
359
360 int
361 server_send(struct http_request* req)
362 {
363 #ifndef HAS_SENDFILE
364 char packet[32768];
365 #else
366 off_t offset = req->sent;
367 #endif
368 size_t to_send;
369 int ret;
370
371 if (req->data < 0 || req->socket < 0) return -1;
372 to_send = req->length - req->sent;
373 if (to_send > 32768) to_send = 32768;
374 #ifndef HAS_SENDFILE
375 ret = read(req->data, packet, to_send);
376 ret = send(req->socket, packet, ret, 0);
377 req->sent += ret;
378 #elif __FreeBSD__
379 ret = sendfile(req->data, req->socket, offset,
380 to_send, NULL, &offset, 0);
381 if (!ret) ret = 1;
382 req->sent += offset;
383 #else
384 ret = sendfile(req->socket, req->data, &offset, to_send);
385 req->sent = offset;
386 #endif
387
388 if (ret <= 0) return -1;
389 if (req->sent >= req->length) return 0;
390 return 1;
391 }
392
393 int
394 server_serve(struct http_request* req)
395 {
396 int hash;
397 size_t bytes, l;
398 struct file *file;
399 char packet[4096];
400
401 if (req->method == GET &&
402 !strncmp(req->uri, "/download/", sizeof("/download/") - 1)) {
403 if (!server_download(req)) return 200;
404 else goto err_404;
405 }
406 if (req->method != GET) goto err_404;
407 hash = fnv(req->uri, strnlen(req->uri, sizeof(req->uri))) %
408 FILES_TABLE_SIZE;
409 file = &files[hash];
410 if (!file->length) {
411 err_404:;
412 memset(packet, 0, sizeof(packet));
413 l = snprintf(packet, sizeof(packet),
414 header, sizeof(data_404) - 1,
415 "text/html");
416 if (l >= sizeof(packet)) {
417 printf("packet buffer overflowing");
418 return -1;
419 }
420 l += strlcpy(&packet[l], data_404, sizeof(packet) - l);
421 send(req->socket, packet, l+1, 0);
422 return 404;
423 }
424 bytes = 0;
425 while (bytes < file->size) {
426 int ret = send(req->socket, &file->data[bytes],
427 file->size - bytes, 0);
428 if (ret <= 0) break;
429 bytes += ret;
430 }
431 return 200;
432 }
433
434 int
435 server_init(int port)
436 {
437 #ifdef DEBUG
438 struct linger sl;
439 #endif
440 struct sockaddr_in addr;
441 listener = socket(AF_INET, SOCK_STREAM, 0);
442 if (listener == -1) {
443 printf("Failed to create socket\n");
444 return -1;
445 }
446 #ifdef DEBUG
447 /* instant reset, useful for testing */
448 sl.l_onoff = 1;
449 sl.l_linger = 0;
450 setsockopt(listener, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl));
451 #endif
452 #ifdef __sun
453 signal(SIGPIPE, SIG_IGN);
454 #endif
455
456 memset(&addr, 0, sizeof(addr));
457 addr.sin_family = AF_INET;
458 addr.sin_addr.s_addr = htonl(INADDR_ANY);
459 addr.sin_port = htons(port);
460 if (bind(listener, (struct sockaddr*)&addr, sizeof(addr))) {
461 printf("Failed to bind socket on port %d\n", port);
462 return -1;
463 }
464 if (listen(listener, 5)) {
465 printf("Failed to listen on port %d\n", port);
466 return -1;
467 }
468 print_now();
469 printf("Listening on port %d\n", port);
470 fflush(stdout); /* to print log in sfm on illumos */
471 return 0;
472 }
473
474 int
475 server_accept(struct http_request* req)
476 {
477 unsigned int len;
478 int socket;
479
480 memset(req, 0, sizeof(struct http_request));
481 req->data = -1;
482 len = sizeof(req->addr);
483 socket = accept(listener, (struct sockaddr*)&req->addr, &len);
484 if (socket == -1) {
485 printf("Failed to accept socket\n");
486 return -1;
487 }
488 req->socket = socket;
489 return 0;
490 }
491
492 int
493 server_recv(struct http_request* req)
494 {
495 char *data_ptr, *end;
496 int bytes, blen;
497
498 bytes = recv(req->socket,
499 (req->data > -1)?req->packet:&req->packet[req->size],
500 (req->data > -1)?sizeof(req->packet):
501 (sizeof(req->packet) - req->size), 0);
502 if (bytes <= 0) return -1;
503 if (!req->boundary_found) {
504 char* ptr = strstr(&req->packet[req->size], "boundary=");
505 if (ptr) {
506 char* start = ptr + sizeof("boundary=");
507 ptr = start;
508 while (ptr++ && *ptr != '\r');
509 strlcpy(req->boundary, start, ptr - start);
510 req->boundary_found = 1;
511 }
512 }
513 req->size += bytes;
514 data_ptr = req->packet;
515 end = NULL;
516 blen = 0;
517 if (req->boundary_found)
518 blen = strnlen(req->boundary, sizeof(req->boundary));
519 while (req->data < 0 && req->boundary_found) {
520 char* ptr = strstr(req->packet, req->boundary);
521 if (!ptr) break;
522 ptr = strstr(ptr + blen, req->boundary);
523 if (!ptr) break;
524 req->content = ptr + strnlen(req->boundary,
525 sizeof(req->boundary));
526 req->length = req->size - (req->content - req->packet);
527 server_upload(req);
528 end = strstr(req->packet + req->size - blen - 8,
529 req->boundary);
530 data_ptr = strstr(req->content + 4, "\r\n\r\n") + 4;
531 bytes = req->size - (data_ptr - req->packet);
532 break;
533 }
534 if (req->data > -1) {
535 char packet[4096];
536 size_t size;
537 if (!end && data_ptr == req->packet)
538 end = strstr(data_ptr + bytes - blen - 8,
539 req->boundary);
540 write(req->data, data_ptr,
541 end?(end - data_ptr - 4):bytes);
542 if (!end) return 1;
543 size = snprintf(packet, sizeof(packet), "%s%s",
544 req->header, req->updata);
545 bytes = 0;
546 while ((size_t)bytes < size) {
547 int ret = send(req->socket, &packet[bytes],
548 size - bytes, 0);
549 if (ret <= 0) break;
550 bytes += ret;
551 }
552 return 2;
553 }
554 if (!req->boundary_found &&
555 req->packet[req->size - 1] == '\n' &&
556 req->packet[req->size - 2] == '\r' &&
557 req->packet[req->size - 3] == '\n' &&
558 req->packet[req->size - 4] == '\r') {
559 return 0;
560 }
561 return 1;
562 }
563
564 #include <poll.h>
565 #define MAX_REQUESTS 1024
566 #define TIMEOUT_SINCE_STARTED 1200000
567 #define TIMEOUT_SINCE_LAST 10000
568 struct http_request requests[MAX_REQUESTS];
569 struct pollfd fds[MAX_REQUESTS];
570 size_t requests_count = 0;
571
572 int
573 new_request()
574 {
575 int new = 1;
576 size_t i = 0;
577 time_t now = time(NULL);
578 for (; i < requests_count; i++) {
579 if (requests[i].done ||
580 now - requests[i].started >= TIMEOUT_SINCE_STARTED ||
581 now - requests[i].last >= TIMEOUT_SINCE_LAST) {
582 new = 0;
583 break;
584 }
585 }
586 if (new && i >= sizeof(requests)/sizeof(struct http_request) - 1)
587 return -1;
588 if (server_accept(&requests[i]))
589 return -1;
590 fds[i + 1].fd = requests[i].socket;
591 fds[i + 1].events = POLLIN;
592 requests[i].last = requests[i].started = time(NULL);
593 if (new)
594 requests_count++;
595 return 0;
596 }
597
598 void print_req(struct http_request* req, int code) {
599 #ifdef NO_PROXY
600 uint8_t *ptr;
601 print_now();
602 ptr = (uint8_t*)&req->addr.sin_addr.s_addr;
603 printf("%d.%d.%d.%d, %s, requested %s [%d]\n",
604 ptr[0], ptr[1], ptr[2], ptr[3],
605 req->useragent, req->uri, code);
606 #else
607 print_now();
608 printf("%s, %s, requested %s [%d]\n",
609 req->xrealip, req->useragent, req->uri, code);
610 #endif
611 fflush(stdout); /* to print log in sfm on illumos */
612 }
613
614 int
615 server_thread()
616 {
617 size_t i;
618 memset(requests, 0, sizeof(requests));
619 for (i = 1; i < sizeof(fds)/sizeof(struct pollfd); i++) {
620 fds[i].fd = -1;
621 fds[i].events = POLLIN;
622 }
623 fds[0].fd = listener;
624 fds[0].events = POLLIN;
625 while (1) {
626 int nfds, ready;
627 size_t i;
628
629 nfds = requests_count + 1;
630 ready = poll(fds, nfds, -1);
631 if (ready == -1) break;
632 if (fds[0].revents == POLLIN && new_request()) {
633 print_now();
634 printf("Failed to accept client\n");
635 fflush(stdout);
636 }
637 for (i = 0; i < requests_count; i++) {
638 int ret = 0;
639 struct http_request* req = &requests[i];
640 switch (fds[i + 1].revents) {
641 case 0:
642 break;
643 case POLLOUT:
644 requests[i].last = time(NULL);
645 if (req->length <= req->sent)
646 goto clean;
647 send_data:;
648 ret = server_send(req);
649 if (ret == 0) {
650 close(req->data);
651 req->data = -1;
652 continue;
653 }
654 if (ret == 1) continue;
655 goto clean;
656 case POLLIN:
657 requests[i].last = time(NULL);
658 ret = server_recv(req);
659 if (ret == 1) continue;
660 if (ret == 2) print_req(req, 200);
661 if (ret == 0) {
662 if (req->data > -1) {
663 close(req->data);
664 req->data = -1;
665 }
666 http_parse(req);
667 print_req(req, server_serve(req));
668 if (req->data > -1) {
669 fds[i + 1].events = POLLOUT;
670 goto send_data;
671 }
672 }
673 clean:
674 if (req->data > -1) {
675 close(req->data);
676 req->data = -1;
677 }
678
679 close(req->socket);
680 req->socket = -1;
681 req->done = 1;
682 if (i + 1 == requests_count)
683 requests_count--;
684 memset(&fds[i + 1], 0, sizeof(struct pollfd));
685 fds[i + 1].fd = -1;
686 fds[i + 1].events = POLLIN;
687 break;
688 default:
689 fds[i + 1].fd = -1;
690 fds[i + 1].events = POLLIN;
691 }
692 }
693 }
694 return 0;
695 }
696
697 int
698 server_stop()
699 {
700 close(listener);
701 return 0;
702 }
703