-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathhttps.c
121 lines (102 loc) · 3.27 KB
/
https.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/*
* Copyright (c) 2021 Jan Klemkow <[email protected]>
*
* Permission to use, copy, modify, and 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.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define S_(x) #x
#define S(x) S_(x)
#define respond(str) do { \
fputs("HTTP/1.1 " str "\r\n\r\n", stdout); \
exit(EXIT_SUCCESS); \
} while (0)
int
main(void)
{
char buf[BUFSIZ];
char method[BUFSIZ+1];
char path[pathconf("/", _PC_PATH_MAX)+1];
char file[pathconf("/", _PC_PATH_MAX)+1];
char htdocs[pathconf("/", _PC_PATH_MAX)+1];
char resolved[pathconf("/", _PC_PATH_MAX)+1];
char host[sysconf(_SC_HOST_NAME_MAX)+1];
enum connection { KEEP_ALIVE, CLOSE } connection;
struct stat sb;
unsigned int major;
unsigned int minor;
size_t n;
FILE *fh;
strcpy(htdocs, "/var/www/htdocs");
#ifdef __OpenBSD__
if (unveil(htdocs, "r") == -1)
respond("500 Internal Server Error");
if (pledge("stdio rpath", NULL) == -1)
respond("500 Internal Server Error");
#endif
next:
connection = CLOSE;
memset(host, 0, sizeof host);
memset(&sb, 0, sizeof sb);
memset(path, 0, sizeof path);
memset(resolved, 0, sizeof resolved);
/* parse method */
if (scanf("%" S(BUFSIZ) "s %" S(PATH_MAX) "s HTTP/%u.%u\r\n",
method, path, &major, &minor) != 4)
respond("400 Bad Request");
/* parse header fields */
while (fgets(buf, sizeof buf, stdin) != NULL &&
strcmp(buf, "\r\n") != 0) {
if (strcmp(buf, "Connection: keep-alive\r\n") == 0)
connection = KEEP_ALIVE;
if (sscanf(buf, "Host: %" S(sysconf(_SC_HOST_NAME_MAX)+1) "s\r\n", host) == 1)
continue;
}
/* check for default file */
if (strcmp(path, "/") == 0)
strcpy(path, "index.html");
snprintf(file, sizeof file, "%s/%s/%s", htdocs, host, path);
if (realpath(file, path) == NULL) {
if (errno == ENOENT)
respond("404 Not Found");
respond("400 Bad Request");
}
/* check that realpath is inside htdocs */
if (strncmp(htdocs, file, strlen(htdocs)) != 0)
respond("400 Bad Request");
if ((fh = fopen(path, "r")) == NULL)
respond("400 Bad Request");
if (fstat(fileno(fh), &sb) == -1)
respond("500 Internal Server Error");
/* response header */
fputs("HTTP/1.1 200 OK\r\n", stdout);
printf("Content-Length: %lld\r\n", sb.st_size);
fputs("\r\n", stdout);
/* transfer body */
while ((n = fread(buf, sizeof *buf, sizeof buf, fh)) > 0)
if (fwrite(buf, sizeof *buf, n, stdout) == 0)
return EXIT_FAILURE;
if (fclose(fh) == EOF)
return EXIT_FAILURE;
if (fflush(stdout) == EOF)
err(EXIT_FAILURE, "fflush");
if (connection == KEEP_ALIVE)
goto next;
return EXIT_SUCCESS;
}