| @@ -1,2091 +1,2088 @@ | | | @@ -1,2091 +1,2088 @@ |
1 | /* $NetBSD: bozohttpd.c,v 1.27 2011/03/29 07:22:31 jmmv Exp $ */ | | 1 | /* $NetBSD: bozohttpd.c,v 1.28 2011/08/27 15:33:59 joerg Exp $ */ |
2 | | | 2 | |
3 | /* $eterna: bozohttpd.c,v 1.176 2010/09/20 22:26:28 mrg Exp $ */ | | 3 | /* $eterna: bozohttpd.c,v 1.176 2010/09/20 22:26:28 mrg Exp $ */ |
4 | | | 4 | |
5 | /* | | 5 | /* |
6 | * Copyright (c) 1997-2010 Matthew R. Green | | 6 | * Copyright (c) 1997-2010 Matthew R. Green |
7 | * All rights reserved. | | 7 | * All rights reserved. |
8 | * | | 8 | * |
9 | * Redistribution and use in source and binary forms, with or without | | 9 | * Redistribution and use in source and binary forms, with or without |
10 | * modification, are permitted provided that the following conditions | | 10 | * modification, are permitted provided that the following conditions |
11 | * are met: | | 11 | * are met: |
12 | * 1. Redistributions of source code must retain the above copyright | | 12 | * 1. Redistributions of source code must retain the above copyright |
13 | * notice, this list of conditions and the following disclaimer. | | 13 | * notice, this list of conditions and the following disclaimer. |
14 | * 2. Redistributions in binary form must reproduce the above copyright | | 14 | * 2. Redistributions in binary form must reproduce the above copyright |
15 | * notice, this list of conditions and the following disclaimer and | | 15 | * notice, this list of conditions and the following disclaimer and |
16 | * dedication in the documentation and/or other materials provided | | 16 | * dedication in the documentation and/or other materials provided |
17 | * with the distribution. | | 17 | * with the distribution. |
18 | * | | 18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | | 19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | | 20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
21 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | | 21 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
22 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | | 22 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | | 23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
24 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | | 24 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | | 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
26 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | | 26 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
29 | * SUCH DAMAGE. | | 29 | * SUCH DAMAGE. |
30 | * | | 30 | * |
31 | */ | | 31 | */ |
32 | | | 32 | |
33 | /* this program is dedicated to the Great God of Processed Cheese */ | | 33 | /* this program is dedicated to the Great God of Processed Cheese */ |
34 | | | 34 | |
35 | /* | | 35 | /* |
36 | * bozohttpd.c: minimal httpd; provides only these features: | | 36 | * bozohttpd.c: minimal httpd; provides only these features: |
37 | * - HTTP/0.9 (by virtue of ..) | | 37 | * - HTTP/0.9 (by virtue of ..) |
38 | * - HTTP/1.0 | | 38 | * - HTTP/1.0 |
39 | * - HTTP/1.1 | | 39 | * - HTTP/1.1 |
40 | * - CGI/1.1 this will only be provided for "system" scripts | | 40 | * - CGI/1.1 this will only be provided for "system" scripts |
41 | * - automatic "missing trailing slash" redirections | | 41 | * - automatic "missing trailing slash" redirections |
42 | * - configurable translation of /~user/ to ~user/public_html, | | 42 | * - configurable translation of /~user/ to ~user/public_html, |
43 | * however, this does not include cgi-bin support | | 43 | * however, this does not include cgi-bin support |
44 | * - access lists via libwrap via inetd/tcpd | | 44 | * - access lists via libwrap via inetd/tcpd |
45 | * - virtual hosting | | 45 | * - virtual hosting |
46 | * - not that we do not even pretend to understand MIME, but | | 46 | * - not that we do not even pretend to understand MIME, but |
47 | * rely only on the HTTP specification | | 47 | * rely only on the HTTP specification |
48 | * - ipv6 support | | 48 | * - ipv6 support |
49 | * - automatic `index.html' generation | | 49 | * - automatic `index.html' generation |
50 | * - configurable server name | | 50 | * - configurable server name |
51 | * - directory index generation | | 51 | * - directory index generation |
52 | * - daemon mode (lacks libwrap support) | | 52 | * - daemon mode (lacks libwrap support) |
53 | * - .htpasswd support | | 53 | * - .htpasswd support |
54 | */ | | 54 | */ |
55 | | | 55 | |
56 | /* | | 56 | /* |
57 | * requirements for minimal http/1.1 (at least, as documented in | | 57 | * requirements for minimal http/1.1 (at least, as documented in |
58 | * <draft-ietf-http-v11-spec-rev-06> which expired may 18, 1999): | | 58 | * <draft-ietf-http-v11-spec-rev-06> which expired may 18, 1999): |
59 | * | | 59 | * |
60 | * - 14.15: content-encoding handling. [1] | | 60 | * - 14.15: content-encoding handling. [1] |
61 | * | | 61 | * |
62 | * - 14.16: content-length handling. this is only a SHOULD header | | 62 | * - 14.16: content-length handling. this is only a SHOULD header |
63 | * thus we could just not send it ever. [1] | | 63 | * thus we could just not send it ever. [1] |
64 | * | | 64 | * |
65 | * - 14.17: content-type handling. [1] | | 65 | * - 14.17: content-type handling. [1] |
66 | * | | 66 | * |
67 | * - 14.25/28: if-{,un}modified-since handling. maybe do this, but | | 67 | * - 14.25/28: if-{,un}modified-since handling. maybe do this, but |
68 | * i really don't want to have to parse 3 differnet date formats | | 68 | * i really don't want to have to parse 3 differnet date formats |
69 | * | | 69 | * |
70 | * [1] need to revisit to ensure proper behaviour | | 70 | * [1] need to revisit to ensure proper behaviour |
71 | * | | 71 | * |
72 | * and the following is a list of features that we do not need | | 72 | * and the following is a list of features that we do not need |
73 | * to have due to other limits, or are too lazy. there are more | | 73 | * to have due to other limits, or are too lazy. there are more |
74 | * of these than are listed, but these are of particular note, | | 74 | * of these than are listed, but these are of particular note, |
75 | * and could perhaps be implemented. | | 75 | * and could perhaps be implemented. |
76 | * | | 76 | * |
77 | * - 3.5/3.6: content/transfer codings. probably can ignore | | 77 | * - 3.5/3.6: content/transfer codings. probably can ignore |
78 | * this? we "SHOULD"n't. but 4.4 says we should ignore a | | 78 | * this? we "SHOULD"n't. but 4.4 says we should ignore a |
79 | * `content-length' header upon reciept of a `transfer-encoding' | | 79 | * `content-length' header upon reciept of a `transfer-encoding' |
80 | * header. | | 80 | * header. |
81 | * | | 81 | * |
82 | * - 5.1.1: request methods. only MUST support GET and HEAD, | | 82 | * - 5.1.1: request methods. only MUST support GET and HEAD, |
83 | * but there are new ones besides POST that are currently | | 83 | * but there are new ones besides POST that are currently |
84 | * supported: OPTIONS PUT DELETE TRACE and CONNECT, plus | | 84 | * supported: OPTIONS PUT DELETE TRACE and CONNECT, plus |
85 | * extensions not yet known? | | 85 | * extensions not yet known? |
86 | * | | 86 | * |
87 | * - 10.1: we can ignore informational status codes | | 87 | * - 10.1: we can ignore informational status codes |
88 | * | | 88 | * |
89 | * - 10.3.3/10.3.4/10.3.8: just use '302' codes always. | | 89 | * - 10.3.3/10.3.4/10.3.8: just use '302' codes always. |
90 | * | | 90 | * |
91 | * - 14.1/14.2/14.3/14.27: we do not support Accept: headers.. | | 91 | * - 14.1/14.2/14.3/14.27: we do not support Accept: headers.. |
92 | * just ignore them and send the request anyway. they are | | 92 | * just ignore them and send the request anyway. they are |
93 | * only SHOULD. | | 93 | * only SHOULD. |
94 | * | | 94 | * |
95 | * - 14.5/14.16/14.35: we don't do ranges. from section 14.35.2 | | 95 | * - 14.5/14.16/14.35: we don't do ranges. from section 14.35.2 |
96 | * `A server MAY ignore the Range header'. but it might be nice. | | 96 | * `A server MAY ignore the Range header'. but it might be nice. |
97 | * since 20080301 we support simple range headers. | | 97 | * since 20080301 we support simple range headers. |
98 | * | | 98 | * |
99 | * - 14.9: we aren't a cache. | | 99 | * - 14.9: we aren't a cache. |
100 | * | | 100 | * |
101 | * - 14.15: content-md5 would be nice... | | 101 | * - 14.15: content-md5 would be nice... |
102 | * | | 102 | * |
103 | * - 14.24/14.26/14.27: be nice to support this... | | 103 | * - 14.24/14.26/14.27: be nice to support this... |
104 | * | | 104 | * |
105 | * - 14.44: not sure about this Vary: header. ignore it for now. | | 105 | * - 14.44: not sure about this Vary: header. ignore it for now. |
106 | */ | | 106 | */ |
107 | | | 107 | |
108 | #ifndef INDEX_HTML | | 108 | #ifndef INDEX_HTML |
109 | #define INDEX_HTML "index.html" | | 109 | #define INDEX_HTML "index.html" |
110 | #endif | | 110 | #endif |
111 | #ifndef SERVER_SOFTWARE | | 111 | #ifndef SERVER_SOFTWARE |
112 | #define SERVER_SOFTWARE "bozohttpd/20100920" | | 112 | #define SERVER_SOFTWARE "bozohttpd/20100920" |
113 | #endif | | 113 | #endif |
114 | #ifndef DIRECT_ACCESS_FILE | | 114 | #ifndef DIRECT_ACCESS_FILE |
115 | #define DIRECT_ACCESS_FILE ".bzdirect" | | 115 | #define DIRECT_ACCESS_FILE ".bzdirect" |
116 | #endif | | 116 | #endif |
117 | #ifndef REDIRECT_FILE | | 117 | #ifndef REDIRECT_FILE |
118 | #define REDIRECT_FILE ".bzredirect" | | 118 | #define REDIRECT_FILE ".bzredirect" |
119 | #endif | | 119 | #endif |
120 | #ifndef ABSREDIRECT_FILE | | 120 | #ifndef ABSREDIRECT_FILE |
121 | #define ABSREDIRECT_FILE ".bzabsredirect" | | 121 | #define ABSREDIRECT_FILE ".bzabsredirect" |
122 | #endif | | 122 | #endif |
123 | #ifndef PUBLIC_HTML | | 123 | #ifndef PUBLIC_HTML |
124 | #define PUBLIC_HTML "public_html" | | 124 | #define PUBLIC_HTML "public_html" |
125 | #endif | | 125 | #endif |
126 | | | 126 | |
127 | #ifndef USE_ARG | | 127 | #ifndef USE_ARG |
128 | #define USE_ARG(x) /*LINTED*/(void)&(x) | | 128 | #define USE_ARG(x) /*LINTED*/(void)&(x) |
129 | #endif | | 129 | #endif |
130 | | | 130 | |
131 | /* | | 131 | /* |
132 | * And so it begins .. | | 132 | * And so it begins .. |
133 | */ | | 133 | */ |
134 | | | 134 | |
135 | #include <sys/param.h> | | 135 | #include <sys/param.h> |
136 | #include <sys/socket.h> | | 136 | #include <sys/socket.h> |
137 | #include <sys/time.h> | | 137 | #include <sys/time.h> |
138 | #include <sys/mman.h> | | 138 | #include <sys/mman.h> |
139 | | | 139 | |
140 | #include <arpa/inet.h> | | 140 | #include <arpa/inet.h> |
141 | | | 141 | |
142 | #include <ctype.h> | | 142 | #include <ctype.h> |
143 | #include <dirent.h> | | 143 | #include <dirent.h> |
144 | #include <errno.h> | | 144 | #include <errno.h> |
145 | #include <fcntl.h> | | 145 | #include <fcntl.h> |
146 | #include <netdb.h> | | 146 | #include <netdb.h> |
147 | #include <pwd.h> | | 147 | #include <pwd.h> |
148 | #include <grp.h> | | 148 | #include <grp.h> |
149 | #include <signal.h> | | 149 | #include <signal.h> |
150 | #include <stdarg.h> | | 150 | #include <stdarg.h> |
151 | #include <stdlib.h> | | 151 | #include <stdlib.h> |
152 | #include <string.h> | | 152 | #include <string.h> |
153 | #include <syslog.h> | | 153 | #include <syslog.h> |
154 | #include <time.h> | | 154 | #include <time.h> |
155 | #include <unistd.h> | | 155 | #include <unistd.h> |
156 | | | 156 | |
157 | #ifndef __attribute__ | | | |
158 | #define __attribute__(x) | | | |
159 | #endif /* __attribute__ */ | | | |
160 | | | | |
161 | #include "bozohttpd.h" | | 157 | #include "bozohttpd.h" |
162 | | | 158 | |
163 | #ifndef MAX_WAIT_TIME | | 159 | #ifndef MAX_WAIT_TIME |
164 | #define MAX_WAIT_TIME 60 /* hang around for 60 seconds max */ | | 160 | #define MAX_WAIT_TIME 60 /* hang around for 60 seconds max */ |
165 | #endif | | 161 | #endif |
166 | | | 162 | |
167 | /* variables and functions */ | | 163 | /* variables and functions */ |
168 | #ifndef LOG_FTP | | 164 | #ifndef LOG_FTP |
169 | #define LOG_FTP LOG_DAEMON | | 165 | #define LOG_FTP LOG_DAEMON |
170 | #endif | | 166 | #endif |
171 | | | 167 | |
172 | volatile sig_atomic_t alarmhit; | | 168 | volatile sig_atomic_t alarmhit; |
173 | | | 169 | |
174 | /* | | 170 | /* |
175 | * check there's enough space in the prefs and names arrays. | | 171 | * check there's enough space in the prefs and names arrays. |
176 | */ | | 172 | */ |
177 | static int | | 173 | static int |
178 | size_arrays(bozoprefs_t *bozoprefs, unsigned needed) | | 174 | size_arrays(bozoprefs_t *bozoprefs, unsigned needed) |
179 | { | | 175 | { |
180 | char **temp; | | 176 | char **temp; |
181 | | | 177 | |
182 | if (bozoprefs->size == 0) { | | 178 | if (bozoprefs->size == 0) { |
183 | /* only get here first time around */ | | 179 | /* only get here first time around */ |
184 | bozoprefs->size = needed; | | 180 | bozoprefs->size = needed; |
185 | if ((bozoprefs->name = calloc(sizeof(char *), needed)) == NULL) { | | 181 | if ((bozoprefs->name = calloc(sizeof(char *), needed)) == NULL) { |
186 | (void) fprintf(stderr, "size_arrays: bad alloc\n"); | | 182 | (void) fprintf(stderr, "size_arrays: bad alloc\n"); |
187 | return 0; | | 183 | return 0; |
188 | } | | 184 | } |
189 | if ((bozoprefs->value = calloc(sizeof(char *), needed)) == NULL) { | | 185 | if ((bozoprefs->value = calloc(sizeof(char *), needed)) == NULL) { |
190 | free(bozoprefs->name); | | 186 | free(bozoprefs->name); |
191 | (void) fprintf(stderr, "size_arrays: bad alloc\n"); | | 187 | (void) fprintf(stderr, "size_arrays: bad alloc\n"); |
192 | return 0; | | 188 | return 0; |
193 | } | | 189 | } |
194 | } else if (bozoprefs->c == bozoprefs->size) { | | 190 | } else if (bozoprefs->c == bozoprefs->size) { |
195 | /* only uses 'needed' when filled array */ | | 191 | /* only uses 'needed' when filled array */ |
196 | bozoprefs->size += needed; | | 192 | bozoprefs->size += needed; |
197 | temp = realloc(bozoprefs->name, sizeof(char *) * needed); | | 193 | temp = realloc(bozoprefs->name, sizeof(char *) * needed); |
198 | if (temp == NULL) { | | 194 | if (temp == NULL) { |
199 | (void) fprintf(stderr, "size_arrays: bad alloc\n"); | | 195 | (void) fprintf(stderr, "size_arrays: bad alloc\n"); |
200 | return 0; | | 196 | return 0; |
201 | } | | 197 | } |
202 | bozoprefs->name = temp; | | 198 | bozoprefs->name = temp; |
203 | temp = realloc(bozoprefs->value, sizeof(char *) * needed); | | 199 | temp = realloc(bozoprefs->value, sizeof(char *) * needed); |
204 | if (temp == NULL) { | | 200 | if (temp == NULL) { |
205 | (void) fprintf(stderr, "size_arrays: bad alloc\n"); | | 201 | (void) fprintf(stderr, "size_arrays: bad alloc\n"); |
206 | return 0; | | 202 | return 0; |
207 | } | | 203 | } |
208 | bozoprefs->value = temp; | | 204 | bozoprefs->value = temp; |
209 | } | | 205 | } |
210 | return 1; | | 206 | return 1; |
211 | } | | 207 | } |
212 | | | 208 | |
213 | static int | | 209 | static int |
214 | findvar(bozoprefs_t *bozoprefs, const char *name) | | 210 | findvar(bozoprefs_t *bozoprefs, const char *name) |
215 | { | | 211 | { |
216 | unsigned i; | | 212 | unsigned i; |
217 | | | 213 | |
218 | for (i = 0 ; i < bozoprefs->c && strcmp(bozoprefs->name[i], name) != 0; i++) | | 214 | for (i = 0 ; i < bozoprefs->c && strcmp(bozoprefs->name[i], name) != 0; i++) |
219 | ; | | 215 | ; |
220 | return (i == bozoprefs->c) ? -1 : (int)i; | | 216 | return (i == bozoprefs->c) ? -1 : (int)i; |
221 | } | | 217 | } |
222 | | | 218 | |
223 | int | | 219 | int |
224 | bozo_set_pref(bozoprefs_t *bozoprefs, const char *name, const char *value) | | 220 | bozo_set_pref(bozoprefs_t *bozoprefs, const char *name, const char *value) |
225 | { | | 221 | { |
226 | int i; | | 222 | int i; |
227 | | | 223 | |
228 | if ((i = findvar(bozoprefs, name)) < 0) { | | 224 | if ((i = findvar(bozoprefs, name)) < 0) { |
229 | /* add the element to the array */ | | 225 | /* add the element to the array */ |
230 | if (size_arrays(bozoprefs, bozoprefs->size + 15)) { | | 226 | if (size_arrays(bozoprefs, bozoprefs->size + 15)) { |
231 | bozoprefs->name[i = bozoprefs->c++] = strdup(name); | | 227 | bozoprefs->name[i = bozoprefs->c++] = strdup(name); |
232 | } | | 228 | } |
233 | } else { | | 229 | } else { |
234 | /* replace the element in the array */ | | 230 | /* replace the element in the array */ |
235 | if (bozoprefs->value[i]) { | | 231 | if (bozoprefs->value[i]) { |
236 | free(bozoprefs->value[i]); | | 232 | free(bozoprefs->value[i]); |
237 | bozoprefs->value[i] = NULL; | | 233 | bozoprefs->value[i] = NULL; |
238 | } | | 234 | } |
239 | } | | 235 | } |
240 | /* sanity checks for range of values go here */ | | 236 | /* sanity checks for range of values go here */ |
241 | bozoprefs->value[i] = strdup(value); | | 237 | bozoprefs->value[i] = strdup(value); |
242 | return 1; | | 238 | return 1; |
243 | } | | 239 | } |
244 | | | 240 | |
245 | /* | | 241 | /* |
246 | * get a variable's value, or NULL | | 242 | * get a variable's value, or NULL |
247 | */ | | 243 | */ |
248 | char * | | 244 | char * |
249 | bozo_get_pref(bozoprefs_t *bozoprefs, const char *name) | | 245 | bozo_get_pref(bozoprefs_t *bozoprefs, const char *name) |
250 | { | | 246 | { |
251 | int i; | | 247 | int i; |
252 | | | 248 | |
253 | return ((i = findvar(bozoprefs, name)) < 0) ? NULL : | | 249 | return ((i = findvar(bozoprefs, name)) < 0) ? NULL : |
254 | bozoprefs->value[i]; | | 250 | bozoprefs->value[i]; |
255 | } | | 251 | } |
256 | | | 252 | |
257 | char * | | 253 | char * |
258 | bozo_http_date(char *date, size_t datelen) | | 254 | bozo_http_date(char *date, size_t datelen) |
259 | { | | 255 | { |
260 | struct tm *tm; | | 256 | struct tm *tm; |
261 | time_t now; | | 257 | time_t now; |
262 | | | 258 | |
263 | /* Sun, 06 Nov 1994 08:49:37 GMT */ | | 259 | /* Sun, 06 Nov 1994 08:49:37 GMT */ |
264 | now = time(NULL); | | 260 | now = time(NULL); |
265 | tm = gmtime(&now); /* HTTP/1.1 spec rev 06 sez GMT only */ | | 261 | tm = gmtime(&now); /* HTTP/1.1 spec rev 06 sez GMT only */ |
266 | strftime(date, datelen, "%a, %d %b %Y %H:%M:%S GMT", tm); | | 262 | strftime(date, datelen, "%a, %d %b %Y %H:%M:%S GMT", tm); |
267 | return date; | | 263 | return date; |
268 | } | | 264 | } |
269 | | | 265 | |
270 | /* | | 266 | /* |
271 | * convert "in" into the three parts of a request (first line). | | 267 | * convert "in" into the three parts of a request (first line). |
272 | * we allocate into file and query, but return pointers into | | 268 | * we allocate into file and query, but return pointers into |
273 | * "in" for proto and method. | | 269 | * "in" for proto and method. |
274 | */ | | 270 | */ |
275 | static void | | 271 | static void |
276 | parse_request(bozohttpd_t *httpd, char *in, char **method, char **file, | | 272 | parse_request(bozohttpd_t *httpd, char *in, char **method, char **file, |
277 | char **query, char **proto) | | 273 | char **query, char **proto) |
278 | { | | 274 | { |
279 | ssize_t len; | | 275 | ssize_t len; |
280 | char *val; | | 276 | char *val; |
281 | | | 277 | |
282 | USE_ARG(httpd); | | 278 | USE_ARG(httpd); |
283 | debug((httpd, DEBUG_EXPLODING, "parse in: %s", in)); | | 279 | debug((httpd, DEBUG_EXPLODING, "parse in: %s", in)); |
284 | *method = *file = *query = *proto = NULL; | | 280 | *method = *file = *query = *proto = NULL; |
285 | | | 281 | |
286 | len = (ssize_t)strlen(in); | | 282 | len = (ssize_t)strlen(in); |
287 | val = bozostrnsep(&in, " \t\n\r", &len); | | 283 | val = bozostrnsep(&in, " \t\n\r", &len); |
288 | if (len < 1 || val == NULL) | | 284 | if (len < 1 || val == NULL) |
289 | return; | | 285 | return; |
290 | *method = val; | | 286 | *method = val; |
291 | | | 287 | |
292 | while (*in == ' ' || *in == '\t') | | 288 | while (*in == ' ' || *in == '\t') |
293 | in++; | | 289 | in++; |
294 | val = bozostrnsep(&in, " \t\n\r", &len); | | 290 | val = bozostrnsep(&in, " \t\n\r", &len); |
295 | if (len < 1) { | | 291 | if (len < 1) { |
296 | if (len == 0) | | 292 | if (len == 0) |
297 | *file = val; | | 293 | *file = val; |
298 | else | | 294 | else |
299 | *file = in; | | 295 | *file = in; |
300 | } else { | | 296 | } else { |
301 | *file = val; | | 297 | *file = val; |
302 | | | 298 | |
303 | *query = strchr(*file, '?'); | | 299 | *query = strchr(*file, '?'); |
304 | if (*query) | | 300 | if (*query) |
305 | *(*query)++ = '\0'; | | 301 | *(*query)++ = '\0'; |
306 | | | 302 | |
307 | if (in) { | | 303 | if (in) { |
308 | while (*in && (*in == ' ' || *in == '\t')) | | 304 | while (*in && (*in == ' ' || *in == '\t')) |
309 | in++; | | 305 | in++; |
310 | if (*in) | | 306 | if (*in) |
311 | *proto = in; | | 307 | *proto = in; |
312 | } | | 308 | } |
313 | } | | 309 | } |
314 | | | 310 | |
315 | /* allocate private copies */ | | 311 | /* allocate private copies */ |
316 | *file = bozostrdup(httpd, *file); | | 312 | *file = bozostrdup(httpd, *file); |
317 | if (*query) | | 313 | if (*query) |
318 | *query = bozostrdup(httpd, *query); | | 314 | *query = bozostrdup(httpd, *query); |
319 | | | 315 | |
320 | debug((httpd, DEBUG_FAT, | | 316 | debug((httpd, DEBUG_FAT, |
321 | "url: method: \"%s\" file: \"%s\" query: \"%s\" proto: \"%s\"", | | 317 | "url: method: \"%s\" file: \"%s\" query: \"%s\" proto: \"%s\"", |
322 | *method, *file, *query, *proto)); | | 318 | *method, *file, *query, *proto)); |
323 | } | | 319 | } |
324 | | | 320 | |
325 | /* | | 321 | /* |
326 | * cleanup a bozo_httpreq_t after use | | 322 | * cleanup a bozo_httpreq_t after use |
327 | */ | | 323 | */ |
328 | void | | 324 | void |
329 | bozo_clean_request(bozo_httpreq_t *request) | | 325 | bozo_clean_request(bozo_httpreq_t *request) |
330 | { | | 326 | { |
331 | struct bozoheaders *hdr, *ohdr = NULL; | | 327 | struct bozoheaders *hdr, *ohdr = NULL; |
332 | | | 328 | |
333 | if (request == NULL) | | 329 | if (request == NULL) |
334 | return; | | 330 | return; |
335 | | | 331 | |
336 | /* If SSL enabled cleanup SSL structure. */ | | 332 | /* If SSL enabled cleanup SSL structure. */ |
337 | bozo_ssl_destroy(request->hr_httpd); | | 333 | bozo_ssl_destroy(request->hr_httpd); |
338 | | | 334 | |
339 | /* clean up request */ | | 335 | /* clean up request */ |
340 | #define MF(x) if (request->x) free(request->x) | | 336 | #define MF(x) if (request->x) free(request->x) |
341 | MF(hr_remotehost); | | 337 | MF(hr_remotehost); |
342 | MF(hr_remoteaddr); | | 338 | MF(hr_remoteaddr); |
343 | MF(hr_serverport); | | 339 | MF(hr_serverport); |
344 | MF(hr_file); | | 340 | MF(hr_file); |
345 | MF(hr_oldfile); | | 341 | MF(hr_oldfile); |
346 | MF(hr_query); | | 342 | MF(hr_query); |
347 | #undef MF | | 343 | #undef MF |
348 | bozo_auth_cleanup(request); | | 344 | bozo_auth_cleanup(request); |
349 | for (hdr = SIMPLEQ_FIRST(&request->hr_headers); hdr; | | 345 | for (hdr = SIMPLEQ_FIRST(&request->hr_headers); hdr; |
350 | hdr = SIMPLEQ_NEXT(hdr, h_next)) { | | 346 | hdr = SIMPLEQ_NEXT(hdr, h_next)) { |
351 | free(hdr->h_value); | | 347 | free(hdr->h_value); |
352 | free(hdr->h_header); | | 348 | free(hdr->h_header); |
353 | if (ohdr) | | 349 | if (ohdr) |
354 | free(ohdr); | | 350 | free(ohdr); |
355 | ohdr = hdr; | | 351 | ohdr = hdr; |
356 | } | | 352 | } |
357 | if (ohdr) | | 353 | if (ohdr) |
358 | free(ohdr); | | 354 | free(ohdr); |
359 | | | 355 | |
360 | free(request); | | 356 | free(request); |
361 | } | | 357 | } |
362 | | | 358 | |
363 | /* | | 359 | /* |
364 | * send a HTTP/1.1 408 response if we timeout. | | 360 | * send a HTTP/1.1 408 response if we timeout. |
365 | */ | | 361 | */ |
366 | /* ARGSUSED */ | | 362 | /* ARGSUSED */ |
367 | static void | | 363 | static void |
368 | alarmer(int sig) | | 364 | alarmer(int sig) |
369 | { | | 365 | { |
370 | alarmhit = 1; | | 366 | alarmhit = 1; |
371 | } | | 367 | } |
372 | | | 368 | |
373 | /* | | 369 | /* |
374 | * add or merge this header (val: str) into the requests list | | 370 | * add or merge this header (val: str) into the requests list |
375 | */ | | 371 | */ |
376 | static bozoheaders_t * | | 372 | static bozoheaders_t * |
377 | addmerge_header(bozo_httpreq_t *request, char *val, | | 373 | addmerge_header(bozo_httpreq_t *request, char *val, |
378 | char *str, ssize_t len) | | 374 | char *str, ssize_t len) |
379 | { | | 375 | { |
380 | struct bozoheaders *hdr; | | 376 | struct bozoheaders *hdr; |
381 | | | 377 | |
382 | USE_ARG(len); | | 378 | USE_ARG(len); |
383 | /* do we exist already? */ | | 379 | /* do we exist already? */ |
384 | SIMPLEQ_FOREACH(hdr, &request->hr_headers, h_next) { | | 380 | SIMPLEQ_FOREACH(hdr, &request->hr_headers, h_next) { |
385 | if (strcasecmp(val, hdr->h_header) == 0) | | 381 | if (strcasecmp(val, hdr->h_header) == 0) |
386 | break; | | 382 | break; |
387 | } | | 383 | } |
388 | | | 384 | |
389 | if (hdr) { | | 385 | if (hdr) { |
390 | /* yup, merge it in */ | | 386 | /* yup, merge it in */ |
391 | char *nval; | | 387 | char *nval; |
392 | | | 388 | |
393 | if (asprintf(&nval, "%s, %s", hdr->h_value, str) == -1) { | | 389 | if (asprintf(&nval, "%s, %s", hdr->h_value, str) == -1) { |
394 | (void)bozo_http_error(request->hr_httpd, 500, NULL, | | 390 | (void)bozo_http_error(request->hr_httpd, 500, NULL, |
395 | "memory allocation failure"); | | 391 | "memory allocation failure"); |
396 | return NULL; | | 392 | return NULL; |
397 | } | | 393 | } |
398 | free(hdr->h_value); | | 394 | free(hdr->h_value); |
399 | hdr->h_value = nval; | | 395 | hdr->h_value = nval; |
400 | } else { | | 396 | } else { |
401 | /* nope, create a new one */ | | 397 | /* nope, create a new one */ |
402 | | | 398 | |
403 | hdr = bozomalloc(request->hr_httpd, sizeof *hdr); | | 399 | hdr = bozomalloc(request->hr_httpd, sizeof *hdr); |
404 | hdr->h_header = bozostrdup(request->hr_httpd, val); | | 400 | hdr->h_header = bozostrdup(request->hr_httpd, val); |
405 | if (str && *str) | | 401 | if (str && *str) |
406 | hdr->h_value = bozostrdup(request->hr_httpd, str); | | 402 | hdr->h_value = bozostrdup(request->hr_httpd, str); |
407 | else | | 403 | else |
408 | hdr->h_value = bozostrdup(request->hr_httpd, " "); | | 404 | hdr->h_value = bozostrdup(request->hr_httpd, " "); |
409 | | | 405 | |
410 | SIMPLEQ_INSERT_TAIL(&request->hr_headers, hdr, h_next); | | 406 | SIMPLEQ_INSERT_TAIL(&request->hr_headers, hdr, h_next); |
411 | request->hr_nheaders++; | | 407 | request->hr_nheaders++; |
412 | } | | 408 | } |
413 | | | 409 | |
414 | return hdr; | | 410 | return hdr; |
415 | } | | 411 | } |
416 | | | 412 | |
417 | /* | | 413 | /* |
418 | * as the prototype string is not constant (eg, "HTTP/1.1" is equivalent | | 414 | * as the prototype string is not constant (eg, "HTTP/1.1" is equivalent |
419 | * to "HTTP/001.01"), we MUST parse this. | | 415 | * to "HTTP/001.01"), we MUST parse this. |
420 | */ | | 416 | */ |
421 | static int | | 417 | static int |
422 | process_proto(bozo_httpreq_t *request, const char *proto) | | 418 | process_proto(bozo_httpreq_t *request, const char *proto) |
423 | { | | 419 | { |
424 | char majorstr[16], *minorstr; | | 420 | char majorstr[16], *minorstr; |
425 | int majorint, minorint; | | 421 | int majorint, minorint; |
426 | | | 422 | |
427 | if (proto == NULL) { | | 423 | if (proto == NULL) { |
428 | got_proto_09: | | 424 | got_proto_09: |
429 | request->hr_proto = request->hr_httpd->consts.http_09; | | 425 | request->hr_proto = request->hr_httpd->consts.http_09; |
430 | debug((request->hr_httpd, DEBUG_FAT, "request %s is http/0.9", | | 426 | debug((request->hr_httpd, DEBUG_FAT, "request %s is http/0.9", |
431 | request->hr_file)); | | 427 | request->hr_file)); |
432 | return 0; | | 428 | return 0; |
433 | } | | 429 | } |
434 | | | 430 | |
435 | if (strncasecmp(proto, "HTTP/", 5) != 0) | | 431 | if (strncasecmp(proto, "HTTP/", 5) != 0) |
436 | goto bad; | | 432 | goto bad; |
437 | strncpy(majorstr, proto + 5, sizeof majorstr); | | 433 | strncpy(majorstr, proto + 5, sizeof majorstr); |
438 | majorstr[sizeof(majorstr)-1] = 0; | | 434 | majorstr[sizeof(majorstr)-1] = 0; |
439 | minorstr = strchr(majorstr, '.'); | | 435 | minorstr = strchr(majorstr, '.'); |
440 | if (minorstr == NULL) | | 436 | if (minorstr == NULL) |
441 | goto bad; | | 437 | goto bad; |
442 | *minorstr++ = 0; | | 438 | *minorstr++ = 0; |
443 | | | 439 | |
444 | majorint = atoi(majorstr); | | 440 | majorint = atoi(majorstr); |
445 | minorint = atoi(minorstr); | | 441 | minorint = atoi(minorstr); |
446 | | | 442 | |
447 | switch (majorint) { | | 443 | switch (majorint) { |
448 | case 0: | | 444 | case 0: |
449 | if (minorint != 9) | | 445 | if (minorint != 9) |
450 | break; | | 446 | break; |
451 | goto got_proto_09; | | 447 | goto got_proto_09; |
452 | case 1: | | 448 | case 1: |
453 | if (minorint == 0) | | 449 | if (minorint == 0) |
454 | request->hr_proto = request->hr_httpd->consts.http_10; | | 450 | request->hr_proto = request->hr_httpd->consts.http_10; |
455 | else if (minorint == 1) | | 451 | else if (minorint == 1) |
456 | request->hr_proto = request->hr_httpd->consts.http_11; | | 452 | request->hr_proto = request->hr_httpd->consts.http_11; |
457 | else | | 453 | else |
458 | break; | | 454 | break; |
459 | | | 455 | |
460 | debug((request->hr_httpd, DEBUG_FAT, "request %s is %s", | | 456 | debug((request->hr_httpd, DEBUG_FAT, "request %s is %s", |
461 | request->hr_file, request->hr_proto)); | | 457 | request->hr_file, request->hr_proto)); |
462 | SIMPLEQ_INIT(&request->hr_headers); | | 458 | SIMPLEQ_INIT(&request->hr_headers); |
463 | request->hr_nheaders = 0; | | 459 | request->hr_nheaders = 0; |
464 | return 0; | | 460 | return 0; |
465 | } | | 461 | } |
466 | bad: | | 462 | bad: |
467 | return bozo_http_error(request->hr_httpd, 404, NULL, "unknown prototype"); | | 463 | return bozo_http_error(request->hr_httpd, 404, NULL, "unknown prototype"); |
468 | } | | 464 | } |
469 | | | 465 | |
470 | /* | | 466 | /* |
471 | * process each type of HTTP method, setting this HTTP requests | | 467 | * process each type of HTTP method, setting this HTTP requests |
472 | # method type. | | 468 | # method type. |
473 | */ | | 469 | */ |
474 | static struct method_map { | | 470 | static struct method_map { |
475 | const char *name; | | 471 | const char *name; |
476 | int type; | | 472 | int type; |
477 | } method_map[] = { | | 473 | } method_map[] = { |
478 | { "GET", HTTP_GET, }, | | 474 | { "GET", HTTP_GET, }, |
479 | { "POST", HTTP_POST, }, | | 475 | { "POST", HTTP_POST, }, |
480 | { "HEAD", HTTP_HEAD, }, | | 476 | { "HEAD", HTTP_HEAD, }, |
481 | #if 0 /* other non-required http/1.1 methods */ | | 477 | #if 0 /* other non-required http/1.1 methods */ |
482 | { "OPTIONS", HTTP_OPTIONS, }, | | 478 | { "OPTIONS", HTTP_OPTIONS, }, |
483 | { "PUT", HTTP_PUT, }, | | 479 | { "PUT", HTTP_PUT, }, |
484 | { "DELETE", HTTP_DELETE, }, | | 480 | { "DELETE", HTTP_DELETE, }, |
485 | { "TRACE", HTTP_TRACE, }, | | 481 | { "TRACE", HTTP_TRACE, }, |
486 | { "CONNECT", HTTP_CONNECT, }, | | 482 | { "CONNECT", HTTP_CONNECT, }, |
487 | #endif | | 483 | #endif |
488 | { NULL, 0, }, | | 484 | { NULL, 0, }, |
489 | }; | | 485 | }; |
490 | | | 486 | |
491 | static int | | 487 | static int |
492 | process_method(bozo_httpreq_t *request, const char *method) | | 488 | process_method(bozo_httpreq_t *request, const char *method) |
493 | { | | 489 | { |
494 | struct method_map *mmp; | | 490 | struct method_map *mmp; |
495 | | | 491 | |
496 | if (request->hr_proto == request->hr_httpd->consts.http_11) | | 492 | if (request->hr_proto == request->hr_httpd->consts.http_11) |
497 | request->hr_allow = "GET, HEAD, POST"; | | 493 | request->hr_allow = "GET, HEAD, POST"; |
498 | | | 494 | |
499 | for (mmp = method_map; mmp->name; mmp++) | | 495 | for (mmp = method_map; mmp->name; mmp++) |
500 | if (strcasecmp(method, mmp->name) == 0) { | | 496 | if (strcasecmp(method, mmp->name) == 0) { |
501 | request->hr_method = mmp->type; | | 497 | request->hr_method = mmp->type; |
502 | request->hr_methodstr = mmp->name; | | 498 | request->hr_methodstr = mmp->name; |
503 | return 0; | | 499 | return 0; |
504 | } | | 500 | } |
505 | | | 501 | |
506 | return bozo_http_error(request->hr_httpd, 404, request, "unknown method"); | | 502 | return bozo_http_error(request->hr_httpd, 404, request, "unknown method"); |
507 | } | | 503 | } |
508 | | | 504 | |
509 | /* | | 505 | /* |
510 | * This function reads a http request from stdin, returning a pointer to a | | 506 | * This function reads a http request from stdin, returning a pointer to a |
511 | * bozo_httpreq_t structure, describing the request. | | 507 | * bozo_httpreq_t structure, describing the request. |
512 | */ | | 508 | */ |
513 | bozo_httpreq_t * | | 509 | bozo_httpreq_t * |
514 | bozo_read_request(bozohttpd_t *httpd) | | 510 | bozo_read_request(bozohttpd_t *httpd) |
515 | { | | 511 | { |
516 | struct sigaction sa; | | 512 | struct sigaction sa; |
517 | char *str, *val, *method, *file, *proto, *query; | | 513 | char *str, *val, *method, *file, *proto, *query; |
518 | char *host, *addr, *port; | | 514 | char *host, *addr, *port; |
519 | char bufport[10]; | | 515 | char bufport[10]; |
520 | char hbuf[NI_MAXHOST], abuf[NI_MAXHOST]; | | 516 | char hbuf[NI_MAXHOST], abuf[NI_MAXHOST]; |
521 | struct sockaddr_storage ss; | | 517 | struct sockaddr_storage ss; |
522 | ssize_t len; | | 518 | ssize_t len; |
523 | int line = 0; | | 519 | int line = 0; |
524 | socklen_t slen; | | 520 | socklen_t slen; |
525 | bozo_httpreq_t *request; | | 521 | bozo_httpreq_t *request; |
526 | | | 522 | |
527 | /* | | 523 | /* |
528 | * if we're in daemon mode, bozo_daemon_fork() will return here twice | | 524 | * if we're in daemon mode, bozo_daemon_fork() will return here twice |
529 | * for each call. once in the child, returning 0, and once in the | | 525 | * for each call. once in the child, returning 0, and once in the |
530 | * parent, returning 1. for each child, then we can setup SSL, and | | 526 | * parent, returning 1. for each child, then we can setup SSL, and |
531 | * the parent can signal the caller there was no request to process | | 527 | * the parent can signal the caller there was no request to process |
532 | * and it will wait for another. | | 528 | * and it will wait for another. |
533 | */ | | 529 | */ |
534 | if (bozo_daemon_fork(httpd)) | | 530 | if (bozo_daemon_fork(httpd)) |
535 | return NULL; | | 531 | return NULL; |
536 | bozo_ssl_accept(httpd); | | 532 | bozo_ssl_accept(httpd); |
537 | | | 533 | |
538 | request = bozomalloc(httpd, sizeof(*request)); | | 534 | request = bozomalloc(httpd, sizeof(*request)); |
539 | memset(request, 0, sizeof(*request)); | | 535 | memset(request, 0, sizeof(*request)); |
540 | request->hr_httpd = httpd; | | 536 | request->hr_httpd = httpd; |
541 | request->hr_allow = request->hr_host = NULL; | | 537 | request->hr_allow = request->hr_host = NULL; |
542 | request->hr_content_type = request->hr_content_length = NULL; | | 538 | request->hr_content_type = request->hr_content_length = NULL; |
543 | request->hr_range = NULL; | | 539 | request->hr_range = NULL; |
544 | request->hr_last_byte_pos = -1; | | 540 | request->hr_last_byte_pos = -1; |
545 | request->hr_if_modified_since = NULL; | | 541 | request->hr_if_modified_since = NULL; |
546 | request->hr_file = NULL; | | 542 | request->hr_file = NULL; |
547 | request->hr_oldfile = NULL; | | 543 | request->hr_oldfile = NULL; |
548 | | | 544 | |
549 | slen = sizeof(ss); | | 545 | slen = sizeof(ss); |
550 | if (getpeername(0, (struct sockaddr *)(void *)&ss, &slen) < 0) | | 546 | if (getpeername(0, (struct sockaddr *)(void *)&ss, &slen) < 0) |
551 | host = addr = NULL; | | 547 | host = addr = NULL; |
552 | else { | | 548 | else { |
553 | if (getnameinfo((struct sockaddr *)(void *)&ss, slen, | | 549 | if (getnameinfo((struct sockaddr *)(void *)&ss, slen, |
554 | abuf, sizeof abuf, NULL, 0, NI_NUMERICHOST) == 0) | | 550 | abuf, sizeof abuf, NULL, 0, NI_NUMERICHOST) == 0) |
555 | addr = abuf; | | 551 | addr = abuf; |
556 | else | | 552 | else |
557 | addr = NULL; | | 553 | addr = NULL; |
558 | if (httpd->numeric == 0 && | | 554 | if (httpd->numeric == 0 && |
559 | getnameinfo((struct sockaddr *)(void *)&ss, slen, | | 555 | getnameinfo((struct sockaddr *)(void *)&ss, slen, |
560 | hbuf, sizeof hbuf, NULL, 0, 0) == 0) | | 556 | hbuf, sizeof hbuf, NULL, 0, 0) == 0) |
561 | host = hbuf; | | 557 | host = hbuf; |
562 | else | | 558 | else |
563 | host = NULL; | | 559 | host = NULL; |
564 | } | | 560 | } |
565 | if (host != NULL) | | 561 | if (host != NULL) |
566 | request->hr_remotehost = bozostrdup(request->hr_httpd, host); | | 562 | request->hr_remotehost = bozostrdup(request->hr_httpd, host); |
567 | if (addr != NULL) | | 563 | if (addr != NULL) |
568 | request->hr_remoteaddr = bozostrdup(request->hr_httpd, addr); | | 564 | request->hr_remoteaddr = bozostrdup(request->hr_httpd, addr); |
569 | slen = sizeof(ss); | | 565 | slen = sizeof(ss); |
570 | if (getsockname(0, (struct sockaddr *)(void *)&ss, &slen) < 0) | | 566 | if (getsockname(0, (struct sockaddr *)(void *)&ss, &slen) < 0) |
571 | port = NULL; | | 567 | port = NULL; |
572 | else { | | 568 | else { |
573 | if (getnameinfo((struct sockaddr *)(void *)&ss, slen, NULL, 0, | | 569 | if (getnameinfo((struct sockaddr *)(void *)&ss, slen, NULL, 0, |
574 | bufport, sizeof bufport, NI_NUMERICSERV) == 0) | | 570 | bufport, sizeof bufport, NI_NUMERICSERV) == 0) |
575 | port = bufport; | | 571 | port = bufport; |
576 | else | | 572 | else |
577 | port = NULL; | | 573 | port = NULL; |
578 | } | | 574 | } |
579 | if (port != NULL) | | 575 | if (port != NULL) |
580 | request->hr_serverport = bozostrdup(request->hr_httpd, port); | | 576 | request->hr_serverport = bozostrdup(request->hr_httpd, port); |
581 | | | 577 | |
582 | /* | | 578 | /* |
583 | * setup a timer to make sure the request is not hung | | 579 | * setup a timer to make sure the request is not hung |
584 | */ | | 580 | */ |
585 | sa.sa_handler = alarmer; | | 581 | sa.sa_handler = alarmer; |
586 | sigemptyset(&sa.sa_mask); | | 582 | sigemptyset(&sa.sa_mask); |
587 | sigaddset(&sa.sa_mask, SIGALRM); | | 583 | sigaddset(&sa.sa_mask, SIGALRM); |
588 | sa.sa_flags = 0; | | 584 | sa.sa_flags = 0; |
589 | sigaction(SIGALRM, &sa, NULL); /* XXX */ | | 585 | sigaction(SIGALRM, &sa, NULL); /* XXX */ |
590 | | | 586 | |
591 | alarm(MAX_WAIT_TIME); | | 587 | alarm(MAX_WAIT_TIME); |
592 | while ((str = bozodgetln(httpd, STDIN_FILENO, &len, bozo_read)) != NULL) { | | 588 | while ((str = bozodgetln(httpd, STDIN_FILENO, &len, bozo_read)) != NULL) { |
593 | alarm(0); | | 589 | alarm(0); |
594 | if (alarmhit) { | | 590 | if (alarmhit) { |
595 | (void)bozo_http_error(httpd, 408, NULL, | | 591 | (void)bozo_http_error(httpd, 408, NULL, |
596 | "request timed out"); | | 592 | "request timed out"); |
597 | goto cleanup; | | 593 | goto cleanup; |
598 | } | | 594 | } |
599 | line++; | | 595 | line++; |
600 | | | 596 | |
601 | if (line == 1) { | | 597 | if (line == 1) { |
602 | | | 598 | |
603 | if (len < 1) { | | 599 | if (len < 1) { |
604 | (void)bozo_http_error(httpd, 404, NULL, | | 600 | (void)bozo_http_error(httpd, 404, NULL, |
605 | "null method"); | | 601 | "null method"); |
606 | goto cleanup; | | 602 | goto cleanup; |
607 | } | | 603 | } |
608 | | | 604 | |
609 | bozo_warn(httpd, "got request ``%s'' from host %s to port %s", | | 605 | bozo_warn(httpd, "got request ``%s'' from host %s to port %s", |
610 | str, | | 606 | str, |
611 | host ? host : addr ? addr : "<local>", | | 607 | host ? host : addr ? addr : "<local>", |
612 | port ? port : "<stdin>"); | | 608 | port ? port : "<stdin>"); |
613 | | | 609 | |
614 | /* we allocate return space in file and query only */ | | 610 | /* we allocate return space in file and query only */ |
615 | parse_request(httpd, str, &method, &file, &query, &proto); | | 611 | parse_request(httpd, str, &method, &file, &query, &proto); |
616 | request->hr_file = file; | | 612 | request->hr_file = file; |
617 | request->hr_query = query; | | 613 | request->hr_query = query; |
618 | if (method == NULL) { | | 614 | if (method == NULL) { |
619 | (void)bozo_http_error(httpd, 404, NULL, | | 615 | (void)bozo_http_error(httpd, 404, NULL, |
620 | "null method"); | | 616 | "null method"); |
621 | goto cleanup; | | 617 | goto cleanup; |
622 | } | | 618 | } |
623 | if (file == NULL) { | | 619 | if (file == NULL) { |
624 | (void)bozo_http_error(httpd, 404, NULL, | | 620 | (void)bozo_http_error(httpd, 404, NULL, |
625 | "null file"); | | 621 | "null file"); |
626 | goto cleanup; | | 622 | goto cleanup; |
627 | } | | 623 | } |
628 | | | 624 | |
629 | /* | | 625 | /* |
630 | * note that we parse the proto first, so that we | | 626 | * note that we parse the proto first, so that we |
631 | * can more properly parse the method and the url. | | 627 | * can more properly parse the method and the url. |
632 | */ | | 628 | */ |
633 | | | 629 | |
634 | if (process_proto(request, proto) || | | 630 | if (process_proto(request, proto) || |
635 | process_method(request, method)) { | | 631 | process_method(request, method)) { |
636 | goto cleanup; | | 632 | goto cleanup; |
637 | } | | 633 | } |
638 | | | 634 | |
639 | debug((httpd, DEBUG_FAT, "got file \"%s\" query \"%s\"", | | 635 | debug((httpd, DEBUG_FAT, "got file \"%s\" query \"%s\"", |
640 | request->hr_file, | | 636 | request->hr_file, |
641 | request->hr_query ? request->hr_query : "<none>")); | | 637 | request->hr_query ? request->hr_query : "<none>")); |
642 | | | 638 | |
643 | /* http/0.9 has no header processing */ | | 639 | /* http/0.9 has no header processing */ |
644 | if (request->hr_proto == httpd->consts.http_09) | | 640 | if (request->hr_proto == httpd->consts.http_09) |
645 | break; | | 641 | break; |
646 | } else { /* incoming headers */ | | 642 | } else { /* incoming headers */ |
647 | bozoheaders_t *hdr; | | 643 | bozoheaders_t *hdr; |
648 | | | 644 | |
649 | if (*str == '\0') | | 645 | if (*str == '\0') |
650 | break; | | 646 | break; |
651 | | | 647 | |
652 | val = bozostrnsep(&str, ":", &len); | | 648 | val = bozostrnsep(&str, ":", &len); |
653 | debug((httpd, DEBUG_EXPLODING, | | 649 | debug((httpd, DEBUG_EXPLODING, |
654 | "read_req2: after bozostrnsep: str ``%s'' val ``%s''", | | 650 | "read_req2: after bozostrnsep: str ``%s'' val ``%s''", |
655 | str, val)); | | 651 | str, val)); |
656 | if (val == NULL || len == -1) { | | 652 | if (val == NULL || len == -1) { |
657 | (void)bozo_http_error(httpd, 404, request, | | 653 | (void)bozo_http_error(httpd, 404, request, |
658 | "no header"); | | 654 | "no header"); |
659 | goto cleanup; | | 655 | goto cleanup; |
660 | } | | 656 | } |
661 | while (*str == ' ' || *str == '\t') | | 657 | while (*str == ' ' || *str == '\t') |
662 | len--, str++; | | 658 | len--, str++; |
663 | while (*val == ' ' || *val == '\t') | | 659 | while (*val == ' ' || *val == '\t') |
664 | val++; | | 660 | val++; |
665 | | | 661 | |
666 | if (bozo_auth_check_headers(request, val, str, len)) | | 662 | if (bozo_auth_check_headers(request, val, str, len)) |
667 | goto next_header; | | 663 | goto next_header; |
668 | | | 664 | |
669 | hdr = addmerge_header(request, val, str, len); | | 665 | hdr = addmerge_header(request, val, str, len); |
670 | | | 666 | |
671 | if (strcasecmp(hdr->h_header, "content-type") == 0) | | 667 | if (strcasecmp(hdr->h_header, "content-type") == 0) |
672 | request->hr_content_type = hdr->h_value; | | 668 | request->hr_content_type = hdr->h_value; |
673 | else if (strcasecmp(hdr->h_header, "content-length") == 0) | | 669 | else if (strcasecmp(hdr->h_header, "content-length") == 0) |
674 | request->hr_content_length = hdr->h_value; | | 670 | request->hr_content_length = hdr->h_value; |
675 | else if (strcasecmp(hdr->h_header, "host") == 0) | | 671 | else if (strcasecmp(hdr->h_header, "host") == 0) |
676 | request->hr_host = hdr->h_value; | | 672 | request->hr_host = hdr->h_value; |
677 | /* HTTP/1.1 rev06 draft spec: 14.20 */ | | 673 | /* HTTP/1.1 rev06 draft spec: 14.20 */ |
678 | else if (strcasecmp(hdr->h_header, "expect") == 0) { | | 674 | else if (strcasecmp(hdr->h_header, "expect") == 0) { |
679 | (void)bozo_http_error(httpd, 417, request, | | 675 | (void)bozo_http_error(httpd, 417, request, |
680 | "we don't support Expect:"); | | 676 | "we don't support Expect:"); |
681 | goto cleanup; | | 677 | goto cleanup; |
682 | } | | 678 | } |
683 | else if (strcasecmp(hdr->h_header, "referrer") == 0 || | | 679 | else if (strcasecmp(hdr->h_header, "referrer") == 0 || |
684 | strcasecmp(hdr->h_header, "referer") == 0) | | 680 | strcasecmp(hdr->h_header, "referer") == 0) |
685 | request->hr_referrer = hdr->h_value; | | 681 | request->hr_referrer = hdr->h_value; |
686 | else if (strcasecmp(hdr->h_header, "range") == 0) | | 682 | else if (strcasecmp(hdr->h_header, "range") == 0) |
687 | request->hr_range = hdr->h_value; | | 683 | request->hr_range = hdr->h_value; |
688 | else if (strcasecmp(hdr->h_header, | | 684 | else if (strcasecmp(hdr->h_header, |
689 | "if-modified-since") == 0) | | 685 | "if-modified-since") == 0) |
690 | request->hr_if_modified_since = hdr->h_value; | | 686 | request->hr_if_modified_since = hdr->h_value; |
691 | | | 687 | |
692 | debug((httpd, DEBUG_FAT, "adding header %s: %s", | | 688 | debug((httpd, DEBUG_FAT, "adding header %s: %s", |
693 | hdr->h_header, hdr->h_value)); | | 689 | hdr->h_header, hdr->h_value)); |
694 | } | | 690 | } |
695 | next_header: | | 691 | next_header: |
696 | alarm(MAX_WAIT_TIME); | | 692 | alarm(MAX_WAIT_TIME); |
697 | } | | 693 | } |
698 | | | 694 | |
699 | /* now, clear it all out */ | | 695 | /* now, clear it all out */ |
700 | alarm(0); | | 696 | alarm(0); |
701 | signal(SIGALRM, SIG_DFL); | | 697 | signal(SIGALRM, SIG_DFL); |
702 | | | 698 | |
703 | /* RFC1945, 8.3 */ | | 699 | /* RFC1945, 8.3 */ |
704 | if (request->hr_method == HTTP_POST && | | 700 | if (request->hr_method == HTTP_POST && |
705 | request->hr_content_length == NULL) { | | 701 | request->hr_content_length == NULL) { |
706 | (void)bozo_http_error(httpd, 400, request, | | 702 | (void)bozo_http_error(httpd, 400, request, |
707 | "missing content length"); | | 703 | "missing content length"); |
708 | goto cleanup; | | 704 | goto cleanup; |
709 | } | | 705 | } |
710 | | | 706 | |
711 | /* HTTP/1.1 draft rev-06, 14.23 & 19.6.1.1 */ | | 707 | /* HTTP/1.1 draft rev-06, 14.23 & 19.6.1.1 */ |
712 | if (request->hr_proto == httpd->consts.http_11 && | | 708 | if (request->hr_proto == httpd->consts.http_11 && |
713 | request->hr_host == NULL) { | | 709 | request->hr_host == NULL) { |
714 | (void)bozo_http_error(httpd, 400, request, | | 710 | (void)bozo_http_error(httpd, 400, request, |
715 | "missing Host header"); | | 711 | "missing Host header"); |
716 | goto cleanup; | | 712 | goto cleanup; |
717 | } | | 713 | } |
718 | | | 714 | |
719 | if (request->hr_range != NULL) { | | 715 | if (request->hr_range != NULL) { |
720 | debug((httpd, DEBUG_FAT, "hr_range: %s", request->hr_range)); | | 716 | debug((httpd, DEBUG_FAT, "hr_range: %s", request->hr_range)); |
721 | /* support only simple ranges %d- and %d-%d */ | | 717 | /* support only simple ranges %d- and %d-%d */ |
722 | if (strchr(request->hr_range, ',') == NULL) { | | 718 | if (strchr(request->hr_range, ',') == NULL) { |
723 | const char *rstart, *dash; | | 719 | const char *rstart, *dash; |
724 | | | 720 | |
725 | rstart = strchr(request->hr_range, '='); | | 721 | rstart = strchr(request->hr_range, '='); |
726 | if (rstart != NULL) { | | 722 | if (rstart != NULL) { |
727 | rstart++; | | 723 | rstart++; |
728 | dash = strchr(rstart, '-'); | | 724 | dash = strchr(rstart, '-'); |
729 | if (dash != NULL && dash != rstart) { | | 725 | if (dash != NULL && dash != rstart) { |
730 | dash++; | | 726 | dash++; |
731 | request->hr_have_range = 1; | | 727 | request->hr_have_range = 1; |
732 | request->hr_first_byte_pos = | | 728 | request->hr_first_byte_pos = |
733 | strtoll(rstart, NULL, 10); | | 729 | strtoll(rstart, NULL, 10); |
734 | if (request->hr_first_byte_pos < 0) | | 730 | if (request->hr_first_byte_pos < 0) |
735 | request->hr_first_byte_pos = 0; | | 731 | request->hr_first_byte_pos = 0; |
736 | if (*dash != '\0') { | | 732 | if (*dash != '\0') { |
737 | request->hr_last_byte_pos = | | 733 | request->hr_last_byte_pos = |
738 | strtoll(dash, NULL, 10); | | 734 | strtoll(dash, NULL, 10); |
739 | if (request->hr_last_byte_pos < 0) | | 735 | if (request->hr_last_byte_pos < 0) |
740 | request->hr_last_byte_pos = -1; | | 736 | request->hr_last_byte_pos = -1; |
741 | } | | 737 | } |
742 | } | | 738 | } |
743 | } | | 739 | } |
744 | } | | 740 | } |
745 | } | | 741 | } |
746 | | | 742 | |
747 | debug((httpd, DEBUG_FAT, "bozo_read_request returns url %s in request", | | 743 | debug((httpd, DEBUG_FAT, "bozo_read_request returns url %s in request", |
748 | request->hr_file)); | | 744 | request->hr_file)); |
749 | return request; | | 745 | return request; |
750 | | | 746 | |
751 | cleanup: | | 747 | cleanup: |
752 | bozo_clean_request(request); | | 748 | bozo_clean_request(request); |
753 | | | 749 | |
754 | return NULL; | | 750 | return NULL; |
755 | } | | 751 | } |
756 | | | 752 | |
757 | static int | | 753 | static int |
758 | mmap_and_write_part(bozohttpd_t *httpd, int fd, off_t first_byte_pos, size_t sz) | | 754 | mmap_and_write_part(bozohttpd_t *httpd, int fd, off_t first_byte_pos, size_t sz) |
759 | { | | 755 | { |
760 | size_t mappedsz, wroffset; | | 756 | size_t mappedsz, wroffset; |
761 | off_t mappedoffset; | | 757 | off_t mappedoffset; |
762 | char *addr; | | 758 | char *addr; |
763 | void *mappedaddr; | | 759 | void *mappedaddr; |
764 | | | 760 | |
765 | /* | | 761 | /* |
766 | * we need to ensure that both the size *and* offset arguments to | | 762 | * we need to ensure that both the size *and* offset arguments to |
767 | * mmap() are page-aligned. our formala for this is: | | 763 | * mmap() are page-aligned. our formala for this is: |
768 | * | | 764 | * |
769 | * input offset: first_byte_pos | | 765 | * input offset: first_byte_pos |
770 | * input size: sz | | 766 | * input size: sz |
771 | * | | 767 | * |
772 | * mapped offset = page align truncate (input offset) | | 768 | * mapped offset = page align truncate (input offset) |
773 | * mapped size = | | 769 | * mapped size = |
774 | * page align extend (input offset - mapped offset + input size) | | 770 | * page align extend (input offset - mapped offset + input size) |
775 | * write offset = input offset - mapped offset | | 771 | * write offset = input offset - mapped offset |
776 | * | | 772 | * |
777 | * we use the write offset in all writes | | 773 | * we use the write offset in all writes |
778 | */ | | 774 | */ |
779 | mappedoffset = first_byte_pos & ~(httpd->page_size - 1); | | 775 | mappedoffset = first_byte_pos & ~(httpd->page_size - 1); |
780 | mappedsz = (size_t) | | 776 | mappedsz = (size_t) |
781 | (first_byte_pos - mappedoffset + sz + httpd->page_size - 1) & | | 777 | (first_byte_pos - mappedoffset + sz + httpd->page_size - 1) & |
782 | ~(httpd->page_size - 1); | | 778 | ~(httpd->page_size - 1); |
783 | wroffset = (size_t)(first_byte_pos - mappedoffset); | | 779 | wroffset = (size_t)(first_byte_pos - mappedoffset); |
784 | | | 780 | |
785 | addr = mmap(0, mappedsz, PROT_READ, MAP_SHARED, fd, mappedoffset); | | 781 | addr = mmap(0, mappedsz, PROT_READ, MAP_SHARED, fd, mappedoffset); |
786 | if (addr == (char *)-1) { | | 782 | if (addr == (char *)-1) { |
787 | bozo_warn(httpd, "mmap failed: %s", strerror(errno)); | | 783 | bozo_warn(httpd, "mmap failed: %s", strerror(errno)); |
788 | return -1; | | 784 | return -1; |
789 | } | | 785 | } |
790 | mappedaddr = addr; | | 786 | mappedaddr = addr; |
791 | | | 787 | |
792 | #ifdef MADV_SEQUENTIAL | | 788 | #ifdef MADV_SEQUENTIAL |
793 | (void)madvise(addr, sz, MADV_SEQUENTIAL); | | 789 | (void)madvise(addr, sz, MADV_SEQUENTIAL); |
794 | #endif | | 790 | #endif |
795 | while (sz > BOZO_WRSZ) { | | 791 | while (sz > BOZO_WRSZ) { |
796 | if (bozo_write(httpd, STDOUT_FILENO, addr + wroffset, | | 792 | if (bozo_write(httpd, STDOUT_FILENO, addr + wroffset, |
797 | BOZO_WRSZ) != BOZO_WRSZ) { | | 793 | BOZO_WRSZ) != BOZO_WRSZ) { |
798 | bozo_warn(httpd, "write failed: %s", strerror(errno)); | | 794 | bozo_warn(httpd, "write failed: %s", strerror(errno)); |
799 | goto out; | | 795 | goto out; |
800 | } | | 796 | } |
801 | debug((httpd, DEBUG_OBESE, "wrote %d bytes", BOZO_WRSZ)); | | 797 | debug((httpd, DEBUG_OBESE, "wrote %d bytes", BOZO_WRSZ)); |
802 | sz -= BOZO_WRSZ; | | 798 | sz -= BOZO_WRSZ; |
803 | addr += BOZO_WRSZ; | | 799 | addr += BOZO_WRSZ; |
804 | } | | 800 | } |
805 | if (sz && (size_t)bozo_write(httpd, STDOUT_FILENO, addr + wroffset, | | 801 | if (sz && (size_t)bozo_write(httpd, STDOUT_FILENO, addr + wroffset, |
806 | sz) != sz) { | | 802 | sz) != sz) { |
807 | bozo_warn(httpd, "final write failed: %s", strerror(errno)); | | 803 | bozo_warn(httpd, "final write failed: %s", strerror(errno)); |
808 | goto out; | | 804 | goto out; |
809 | } | | 805 | } |
810 | debug((httpd, DEBUG_OBESE, "wrote %d bytes", (int)sz)); | | 806 | debug((httpd, DEBUG_OBESE, "wrote %d bytes", (int)sz)); |
811 | out: | | 807 | out: |
812 | if (munmap(mappedaddr, mappedsz) < 0) { | | 808 | if (munmap(mappedaddr, mappedsz) < 0) { |
813 | bozo_warn(httpd, "munmap failed"); | | 809 | bozo_warn(httpd, "munmap failed"); |
814 | return -1; | | 810 | return -1; |
815 | } | | 811 | } |
816 | | | 812 | |
817 | return 0; | | 813 | return 0; |
818 | } | | 814 | } |
819 | | | 815 | |
820 | static int | | 816 | static int |
821 | parse_http_date(const char *val, time_t *timestamp) | | 817 | parse_http_date(const char *val, time_t *timestamp) |
822 | { | | 818 | { |
823 | char *remainder; | | 819 | char *remainder; |
824 | struct tm tm; | | 820 | struct tm tm; |
825 | | | 821 | |
826 | if ((remainder = strptime(val, "%a, %d %b %Y %T GMT", &tm)) == NULL && | | 822 | if ((remainder = strptime(val, "%a, %d %b %Y %T GMT", &tm)) == NULL && |
827 | (remainder = strptime(val, "%a, %d-%b-%y %T GMT", &tm)) == NULL && | | 823 | (remainder = strptime(val, "%a, %d-%b-%y %T GMT", &tm)) == NULL && |
828 | (remainder = strptime(val, "%a %b %d %T %Y", &tm)) == NULL) | | 824 | (remainder = strptime(val, "%a %b %d %T %Y", &tm)) == NULL) |
829 | return 0; /* Invalid HTTP date format */ | | 825 | return 0; /* Invalid HTTP date format */ |
830 | | | 826 | |
831 | if (*remainder) | | 827 | if (*remainder) |
832 | return 0; /* No trailing garbage */ | | 828 | return 0; /* No trailing garbage */ |
833 | | | 829 | |
834 | *timestamp = timegm(&tm); | | 830 | *timestamp = timegm(&tm); |
835 | return 1; | | 831 | return 1; |
836 | } | | 832 | } |
837 | | | 833 | |
838 | /* | | 834 | /* |
839 | * checks to see if this request has a valid .bzdirect file. returns | | 835 | * checks to see if this request has a valid .bzdirect file. returns |
840 | * 0 on failure and 1 on success. | | 836 | * 0 on failure and 1 on success. |
841 | */ | | 837 | */ |
842 | static int | | 838 | static int |
843 | check_direct_access(bozo_httpreq_t *request) | | 839 | check_direct_access(bozo_httpreq_t *request) |
844 | { | | 840 | { |
845 | FILE *fp; | | 841 | FILE *fp; |
846 | struct stat sb; | | 842 | struct stat sb; |
847 | char dir[MAXPATHLEN], dirfile[MAXPATHLEN], *basename; | | 843 | char dir[MAXPATHLEN], dirfile[MAXPATHLEN], *basename; |
848 | | | 844 | |
849 | snprintf(dir, sizeof(dir), "%s", request->hr_file + 1); | | 845 | snprintf(dir, sizeof(dir), "%s", request->hr_file + 1); |
850 | debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir)); | | 846 | debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir)); |
851 | basename = strrchr(dir, '/'); | | 847 | basename = strrchr(dir, '/'); |
852 | | | 848 | |
853 | if ((!basename || basename[1] != '\0') && | | 849 | if ((!basename || basename[1] != '\0') && |
854 | lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode)) | | 850 | lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode)) |
855 | /* nothing */; | | 851 | /* nothing */; |
856 | else if (basename == NULL) | | 852 | else if (basename == NULL) |
857 | strcpy(dir, "."); | | 853 | strcpy(dir, "."); |
858 | else { | | 854 | else { |
859 | *basename++ = '\0'; | | 855 | *basename++ = '\0'; |
860 | bozo_check_special_files(request, basename); | | 856 | bozo_check_special_files(request, basename); |
861 | } | | 857 | } |
862 | | | 858 | |
863 | snprintf(dirfile, sizeof(dirfile), "%s/%s", dir, DIRECT_ACCESS_FILE); | | 859 | snprintf(dirfile, sizeof(dirfile), "%s/%s", dir, DIRECT_ACCESS_FILE); |
864 | if (stat(dirfile, &sb) < 0 || | | 860 | if (stat(dirfile, &sb) < 0 || |
865 | (fp = fopen(dirfile, "r")) == NULL) | | 861 | (fp = fopen(dirfile, "r")) == NULL) |
866 | return 0; | | 862 | return 0; |
867 | fclose(fp); | | 863 | fclose(fp); |
868 | return 1; | | 864 | return 1; |
869 | } | | 865 | } |
870 | | | 866 | |
871 | /* | | 867 | /* |
872 | * do automatic redirection -- if there are query parameters for the URL | | 868 | * do automatic redirection -- if there are query parameters for the URL |
873 | * we will tack these on to the new (redirected) URL. | | 869 | * we will tack these on to the new (redirected) URL. |
874 | */ | | 870 | */ |
875 | static void | | 871 | static void |
876 | handle_redirect(bozo_httpreq_t *request, | | 872 | handle_redirect(bozo_httpreq_t *request, |
877 | const char *url, int absolute) | | 873 | const char *url, int absolute) |
878 | { | | 874 | { |
879 | bozohttpd_t *httpd = request->hr_httpd; | | 875 | bozohttpd_t *httpd = request->hr_httpd; |
880 | char *urlbuf; | | 876 | char *urlbuf; |
881 | char portbuf[20]; | | 877 | char portbuf[20]; |
882 | int query = 0; | | 878 | int query = 0; |
883 | | | 879 | |
884 | if (url == NULL) { | | 880 | if (url == NULL) { |
885 | if (asprintf(&urlbuf, "/%s/", request->hr_file) < 0) | | 881 | if (asprintf(&urlbuf, "/%s/", request->hr_file) < 0) |
886 | bozo_err(httpd, 1, "asprintf"); | | 882 | bozo_err(httpd, 1, "asprintf"); |
887 | url = urlbuf; | | 883 | url = urlbuf; |
888 | } else | | 884 | } else |
889 | urlbuf = NULL; | | 885 | urlbuf = NULL; |
890 | | | 886 | |
891 | if (request->hr_query && strlen(request->hr_query)) { | | 887 | if (request->hr_query && strlen(request->hr_query)) { |
892 | query = 1; | | 888 | query = 1; |
893 | } | | 889 | } |
894 | | | 890 | |
895 | if (request->hr_serverport && strcmp(request->hr_serverport, "80") != 0) | | 891 | if (request->hr_serverport && strcmp(request->hr_serverport, "80") != 0) |
896 | snprintf(portbuf, sizeof(portbuf), ":%s", | | 892 | snprintf(portbuf, sizeof(portbuf), ":%s", |
897 | request->hr_serverport); | | 893 | request->hr_serverport); |
898 | else | | 894 | else |
899 | portbuf[0] = '\0'; | | 895 | portbuf[0] = '\0'; |
900 | bozo_warn(httpd, "redirecting %s%s%s", httpd->virthostname, portbuf, url); | | 896 | bozo_warn(httpd, "redirecting %s%s%s", httpd->virthostname, portbuf, url); |
901 | debug((httpd, DEBUG_FAT, "redirecting %s", url)); | | 897 | debug((httpd, DEBUG_FAT, "redirecting %s", url)); |
902 | bozo_printf(httpd, "%s 301 Document Moved\r\n", request->hr_proto); | | 898 | bozo_printf(httpd, "%s 301 Document Moved\r\n", request->hr_proto); |
903 | if (request->hr_proto != httpd->consts.http_09) | | 899 | if (request->hr_proto != httpd->consts.http_09) |
904 | bozo_print_header(request, NULL, "text/html", NULL); | | 900 | bozo_print_header(request, NULL, "text/html", NULL); |
905 | if (request->hr_proto != httpd->consts.http_09) { | | 901 | if (request->hr_proto != httpd->consts.http_09) { |
906 | bozo_printf(httpd, "Location: http://"); | | 902 | bozo_printf(httpd, "Location: http://"); |
907 | if (absolute == 0) | | 903 | if (absolute == 0) |
908 | bozo_printf(httpd, "%s%s", httpd->virthostname, portbuf); | | 904 | bozo_printf(httpd, "%s%s", httpd->virthostname, portbuf); |
909 | if (query) { | | 905 | if (query) { |
910 | bozo_printf(httpd, "%s?%s\r\n", url, request->hr_query); | | 906 | bozo_printf(httpd, "%s?%s\r\n", url, request->hr_query); |
911 | } else { | | 907 | } else { |
912 | bozo_printf(httpd, "%s\r\n", url); | | 908 | bozo_printf(httpd, "%s\r\n", url); |
913 | } | | 909 | } |
914 | } | | 910 | } |
915 | bozo_printf(httpd, "\r\n"); | | 911 | bozo_printf(httpd, "\r\n"); |
916 | if (request->hr_method == HTTP_HEAD) | | 912 | if (request->hr_method == HTTP_HEAD) |
917 | goto head; | | 913 | goto head; |
918 | bozo_printf(httpd, "<html><head><title>Document Moved</title></head>\n"); | | 914 | bozo_printf(httpd, "<html><head><title>Document Moved</title></head>\n"); |
919 | bozo_printf(httpd, "<body><h1>Document Moved</h1>\n"); | | 915 | bozo_printf(httpd, "<body><h1>Document Moved</h1>\n"); |
920 | bozo_printf(httpd, "This document had moved <a href=\"http://"); | | 916 | bozo_printf(httpd, "This document had moved <a href=\"http://"); |
921 | if (query) { | | 917 | if (query) { |
922 | if (absolute) | | 918 | if (absolute) |
923 | bozo_printf(httpd, "%s?%s", url, request->hr_query); | | 919 | bozo_printf(httpd, "%s?%s", url, request->hr_query); |
924 | else | | 920 | else |
925 | bozo_printf(httpd, "%s%s%s?%s", httpd->virthostname, portbuf, url, | | 921 | bozo_printf(httpd, "%s%s%s?%s", httpd->virthostname, portbuf, url, |
926 | request->hr_query); | | 922 | request->hr_query); |
927 | } else { | | 923 | } else { |
928 | if (absolute) | | 924 | if (absolute) |
929 | bozo_printf(httpd, "%s", url); | | 925 | bozo_printf(httpd, "%s", url); |
930 | else | | 926 | else |
931 | bozo_printf(httpd, "%s%s%s", httpd->virthostname, portbuf, url); | | 927 | bozo_printf(httpd, "%s%s%s", httpd->virthostname, portbuf, url); |
932 | } | | 928 | } |
933 | bozo_printf(httpd, "\">here</a>\n"); | | 929 | bozo_printf(httpd, "\">here</a>\n"); |
934 | bozo_printf(httpd, "</body></html>\n"); | | 930 | bozo_printf(httpd, "</body></html>\n"); |
935 | head: | | 931 | head: |
936 | bozo_flush(httpd, stdout); | | 932 | bozo_flush(httpd, stdout); |
937 | if (urlbuf) | | 933 | if (urlbuf) |
938 | free(urlbuf); | | 934 | free(urlbuf); |
939 | } | | 935 | } |
940 | | | 936 | |
941 | /* | | 937 | /* |
942 | * deal with virtual host names; we do this: | | 938 | * deal with virtual host names; we do this: |
943 | * if we have a virtual path root (httpd->virtbase), and we are given a | | 939 | * if we have a virtual path root (httpd->virtbase), and we are given a |
944 | * virtual host spec (Host: ho.st or http://ho.st/), see if this | | 940 | * virtual host spec (Host: ho.st or http://ho.st/), see if this |
945 | * directory exists under httpd->virtbase. if it does, use this as the | | 941 | * directory exists under httpd->virtbase. if it does, use this as the |
946 | # new slashdir. | | 942 | # new slashdir. |
947 | */ | | 943 | */ |
948 | static int | | 944 | static int |
949 | check_virtual(bozo_httpreq_t *request) | | 945 | check_virtual(bozo_httpreq_t *request) |
950 | { | | 946 | { |
951 | bozohttpd_t *httpd = request->hr_httpd; | | 947 | bozohttpd_t *httpd = request->hr_httpd; |
952 | char *file = request->hr_file, *s; | | 948 | char *file = request->hr_file, *s; |
953 | size_t len; | | 949 | size_t len; |
954 | | | 950 | |
955 | if (!httpd->virtbase) | | 951 | if (!httpd->virtbase) |
956 | goto use_slashdir; | | 952 | goto use_slashdir; |
957 | | | 953 | |
958 | /* | | 954 | /* |
959 | * convert http://virtual.host/ to request->hr_host | | 955 | * convert http://virtual.host/ to request->hr_host |
960 | */ | | 956 | */ |
961 | debug((httpd, DEBUG_OBESE, "checking for http:// virtual host in ``%s''", | | 957 | debug((httpd, DEBUG_OBESE, "checking for http:// virtual host in ``%s''", |
962 | file)); | | 958 | file)); |
963 | if (strncasecmp(file, "http://", 7) == 0) { | | 959 | if (strncasecmp(file, "http://", 7) == 0) { |
964 | /* we would do virtual hosting here? */ | | 960 | /* we would do virtual hosting here? */ |
965 | file += 7; | | 961 | file += 7; |
966 | s = strchr(file, '/'); | | 962 | s = strchr(file, '/'); |
967 | /* HTTP/1.1 draft rev-06, 5.2: URI takes precedence over Host: */ | | 963 | /* HTTP/1.1 draft rev-06, 5.2: URI takes precedence over Host: */ |
968 | request->hr_host = file; | | 964 | request->hr_host = file; |
969 | request->hr_file = bozostrdup(request->hr_httpd, s ? s : "/"); | | 965 | request->hr_file = bozostrdup(request->hr_httpd, s ? s : "/"); |
970 | debug((httpd, DEBUG_OBESE, "got host ``%s'' file is now ``%s''", | | 966 | debug((httpd, DEBUG_OBESE, "got host ``%s'' file is now ``%s''", |
971 | request->hr_host, request->hr_file)); | | 967 | request->hr_host, request->hr_file)); |
972 | } else if (!request->hr_host) | | 968 | } else if (!request->hr_host) |
973 | goto use_slashdir; | | 969 | goto use_slashdir; |
974 | | | 970 | |
975 | /* | | 971 | /* |
976 | * ok, we have a virtual host, use scandir(3) to find a case | | 972 | * ok, we have a virtual host, use scandir(3) to find a case |
977 | * insensitive match for the virtual host we are asked for. | | 973 | * insensitive match for the virtual host we are asked for. |
978 | * note that if the virtual host is the same as the master, | | 974 | * note that if the virtual host is the same as the master, |
979 | * we don't need to do anything special. | | 975 | * we don't need to do anything special. |
980 | */ | | 976 | */ |
981 | len = strlen(request->hr_host); | | 977 | len = strlen(request->hr_host); |
982 | debug((httpd, DEBUG_OBESE, | | 978 | debug((httpd, DEBUG_OBESE, |
983 | "check_virtual: checking host `%s' under httpd->virtbase `%s' " | | 979 | "check_virtual: checking host `%s' under httpd->virtbase `%s' " |
984 | "for file `%s'", | | 980 | "for file `%s'", |
985 | request->hr_host, httpd->virtbase, request->hr_file)); | | 981 | request->hr_host, httpd->virtbase, request->hr_file)); |
986 | if (strncasecmp(httpd->virthostname, request->hr_host, len) != 0) { | | 982 | if (strncasecmp(httpd->virthostname, request->hr_host, len) != 0) { |
987 | s = 0; | | 983 | s = 0; |
988 | DIR *dirp; | | 984 | DIR *dirp; |
989 | struct dirent *d; | | 985 | struct dirent *d; |
990 | | | 986 | |
991 | if ((dirp = opendir(httpd->virtbase)) != NULL) { | | 987 | if ((dirp = opendir(httpd->virtbase)) != NULL) { |
992 | while ((d = readdir(dirp)) != NULL) { | | 988 | while ((d = readdir(dirp)) != NULL) { |
993 | if (strcmp(d->d_name, ".") == 0 || | | 989 | if (strcmp(d->d_name, ".") == 0 || |
994 | strcmp(d->d_name, "..") == 0) { | | 990 | strcmp(d->d_name, "..") == 0) { |
995 | continue; | | 991 | continue; |
996 | } | | 992 | } |
997 | debug((httpd, DEBUG_OBESE, "looking at dir``%s''", | | 993 | debug((httpd, DEBUG_OBESE, "looking at dir``%s''", |
998 | d->d_name)); | | 994 | d->d_name)); |
999 | if (strncasecmp(d->d_name, request->hr_host, | | 995 | if (strncasecmp(d->d_name, request->hr_host, |
1000 | len) == 0) { | | 996 | len) == 0) { |
1001 | /* found it, punch it */ | | 997 | /* found it, punch it */ |
1002 | debug((httpd, DEBUG_OBESE, "found it punch it")); | | 998 | debug((httpd, DEBUG_OBESE, "found it punch it")); |
1003 | httpd->virthostname = d->d_name; | | 999 | httpd->virthostname = d->d_name; |
1004 | if (asprintf(&s, "%s/%s", httpd->virtbase, | | 1000 | if (asprintf(&s, "%s/%s", httpd->virtbase, |
1005 | httpd->virthostname) < 0) | | 1001 | httpd->virthostname) < 0) |
1006 | bozo_err(httpd, 1, "asprintf"); | | 1002 | bozo_err(httpd, 1, "asprintf"); |
1007 | break; | | 1003 | break; |
1008 | } | | 1004 | } |
1009 | } | | 1005 | } |
1010 | closedir(dirp); | | 1006 | closedir(dirp); |
1011 | } | | 1007 | } |
1012 | else { | | 1008 | else { |
1013 | debug((httpd, DEBUG_FAT, "opendir %s failed: %s", | | 1009 | debug((httpd, DEBUG_FAT, "opendir %s failed: %s", |
1014 | httpd->virtbase, strerror(errno))); | | 1010 | httpd->virtbase, strerror(errno))); |
1015 | } | | 1011 | } |
1016 | if (s == 0) { | | 1012 | if (s == 0) { |
1017 | if (httpd->unknown_slash) | | 1013 | if (httpd->unknown_slash) |
1018 | goto use_slashdir; | | 1014 | goto use_slashdir; |
1019 | return bozo_http_error(httpd, 404, request, | | 1015 | return bozo_http_error(httpd, 404, request, |
1020 | "unknown URL"); | | 1016 | "unknown URL"); |
1021 | } | | 1017 | } |
1022 | } else | | 1018 | } else |
1023 | use_slashdir: | | 1019 | use_slashdir: |
1024 | s = httpd->slashdir; | | 1020 | s = httpd->slashdir; |
1025 | | | 1021 | |
1026 | /* | | 1022 | /* |
1027 | * ok, nailed the correct slashdir, chdir to it | | 1023 | * ok, nailed the correct slashdir, chdir to it |
1028 | */ | | 1024 | */ |
1029 | if (chdir(s) < 0) | | 1025 | if (chdir(s) < 0) |
1030 | return bozo_http_error(httpd, 404, request, | | 1026 | return bozo_http_error(httpd, 404, request, |
1031 | "can't chdir to slashdir"); | | 1027 | "can't chdir to slashdir"); |
1032 | return 0; | | 1028 | return 0; |
1033 | } | | 1029 | } |
1034 | | | 1030 | |
1035 | /* | | 1031 | /* |
1036 | * checks to see if this request has a valid .bzredirect file. returns | | 1032 | * checks to see if this request has a valid .bzredirect file. returns |
1037 | * 0 on failure and 1 on success. | | 1033 | * 0 on failure and 1 on success. |
1038 | */ | | 1034 | */ |
1039 | static void | | 1035 | static void |
1040 | check_bzredirect(bozo_httpreq_t *request) | | 1036 | check_bzredirect(bozo_httpreq_t *request) |
1041 | { | | 1037 | { |
1042 | struct stat sb; | | 1038 | struct stat sb; |
1043 | char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1]; | | 1039 | char dir[MAXPATHLEN], redir[MAXPATHLEN], redirpath[MAXPATHLEN + 1]; |
1044 | char *basename, *finalredir; | | 1040 | char *basename, *finalredir; |
1045 | int rv, absolute; | | 1041 | int rv, absolute; |
1046 | | | 1042 | |
1047 | /* | | 1043 | /* |
1048 | * if this pathname is really a directory, but doesn't end in /, | | 1044 | * if this pathname is really a directory, but doesn't end in /, |
1049 | * use it as the directory to look for the redir file. | | 1045 | * use it as the directory to look for the redir file. |
1050 | */ | | 1046 | */ |
1051 | snprintf(dir, sizeof(dir), "%s", request->hr_file + 1); | | 1047 | snprintf(dir, sizeof(dir), "%s", request->hr_file + 1); |
1052 | debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir)); | | 1048 | debug((request->hr_httpd, DEBUG_FAT, "check_bzredirect: dir %s", dir)); |
1053 | basename = strrchr(dir, '/'); | | 1049 | basename = strrchr(dir, '/'); |
1054 | | | 1050 | |
1055 | if ((!basename || basename[1] != '\0') && | | 1051 | if ((!basename || basename[1] != '\0') && |
1056 | lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode)) | | 1052 | lstat(dir, &sb) == 0 && S_ISDIR(sb.st_mode)) |
1057 | /* nothing */; | | 1053 | /* nothing */; |
1058 | else if (basename == NULL) | | 1054 | else if (basename == NULL) |
1059 | strcpy(dir, "."); | | 1055 | strcpy(dir, "."); |
1060 | else { | | 1056 | else { |
1061 | *basename++ = '\0'; | | 1057 | *basename++ = '\0'; |
1062 | bozo_check_special_files(request, basename); | | 1058 | bozo_check_special_files(request, basename); |
1063 | } | | 1059 | } |
1064 | | | 1060 | |
1065 | snprintf(redir, sizeof(redir), "%s/%s", dir, REDIRECT_FILE); | | 1061 | snprintf(redir, sizeof(redir), "%s/%s", dir, REDIRECT_FILE); |
1066 | if (lstat(redir, &sb) == 0) { | | 1062 | if (lstat(redir, &sb) == 0) { |
1067 | if (!S_ISLNK(sb.st_mode)) | | 1063 | if (!S_ISLNK(sb.st_mode)) |
1068 | return; | | 1064 | return; |
1069 | absolute = 0; | | 1065 | absolute = 0; |
1070 | } else { | | 1066 | } else { |
1071 | snprintf(redir, sizeof(redir), "%s/%s", dir, ABSREDIRECT_FILE); | | 1067 | snprintf(redir, sizeof(redir), "%s/%s", dir, ABSREDIRECT_FILE); |
1072 | if (lstat(redir, &sb) < 0 || !S_ISLNK(sb.st_mode)) | | 1068 | if (lstat(redir, &sb) < 0 || !S_ISLNK(sb.st_mode)) |
1073 | return; | | 1069 | return; |
1074 | absolute = 1; | | 1070 | absolute = 1; |
1075 | } | | 1071 | } |
1076 | debug((request->hr_httpd, DEBUG_FAT, | | 1072 | debug((request->hr_httpd, DEBUG_FAT, |
1077 | "check_bzredirect: calling readlink")); | | 1073 | "check_bzredirect: calling readlink")); |
1078 | rv = readlink(redir, redirpath, sizeof redirpath - 1); | | 1074 | rv = readlink(redir, redirpath, sizeof redirpath - 1); |
1079 | if (rv == -1 || rv == 0) { | | 1075 | if (rv == -1 || rv == 0) { |
1080 | debug((request->hr_httpd, DEBUG_FAT, "readlink failed")); | | 1076 | debug((request->hr_httpd, DEBUG_FAT, "readlink failed")); |
1081 | return; | | 1077 | return; |
1082 | } | | 1078 | } |
1083 | redirpath[rv] = '\0'; | | 1079 | redirpath[rv] = '\0'; |
1084 | debug((request->hr_httpd, DEBUG_FAT, | | 1080 | debug((request->hr_httpd, DEBUG_FAT, |
1085 | "readlink returned \"%s\"", redirpath)); | | 1081 | "readlink returned \"%s\"", redirpath)); |
1086 | | | 1082 | |
1087 | /* now we have the link pointer, redirect to the real place */ | | 1083 | /* now we have the link pointer, redirect to the real place */ |
1088 | if (absolute) | | 1084 | if (absolute) |
1089 | finalredir = redirpath; | | 1085 | finalredir = redirpath; |
1090 | else | | 1086 | else |
1091 | snprintf(finalredir = redir, sizeof(redir), "/%s/%s", dir, | | 1087 | snprintf(finalredir = redir, sizeof(redir), "/%s/%s", dir, |
1092 | redirpath); | | 1088 | redirpath); |
1093 | | | 1089 | |
1094 | debug((request->hr_httpd, DEBUG_FAT, | | 1090 | debug((request->hr_httpd, DEBUG_FAT, |
1095 | "check_bzredirect: new redir %s", finalredir)); | | 1091 | "check_bzredirect: new redir %s", finalredir)); |
1096 | handle_redirect(request, finalredir, absolute); | | 1092 | handle_redirect(request, finalredir, absolute); |
1097 | } | | 1093 | } |
1098 | | | 1094 | |
1099 | /* this fixes the %HH hack that RFC2396 requires. */ | | 1095 | /* this fixes the %HH hack that RFC2396 requires. */ |
1100 | static void | | 1096 | static void |
1101 | fix_url_percent(bozo_httpreq_t *request) | | 1097 | fix_url_percent(bozo_httpreq_t *request) |
1102 | { | | 1098 | { |
1103 | bozohttpd_t *httpd = request->hr_httpd; | | 1099 | bozohttpd_t *httpd = request->hr_httpd; |
1104 | char *s, *t, buf[3], *url; | | 1100 | char *s, *t, buf[3], *url; |
1105 | char *end; /* if end is not-zero, we don't translate beyond that */ | | 1101 | char *end; /* if end is not-zero, we don't translate beyond that */ |
1106 | | | 1102 | |
1107 | url = request->hr_file; | | 1103 | url = request->hr_file; |
1108 | | | 1104 | |
1109 | end = url + strlen(url); | | 1105 | end = url + strlen(url); |
1110 | | | 1106 | |
1111 | /* fast forward to the first % */ | | 1107 | /* fast forward to the first % */ |
1112 | if ((s = strchr(url, '%')) == NULL) | | 1108 | if ((s = strchr(url, '%')) == NULL) |
1113 | return; | | 1109 | return; |
1114 | | | 1110 | |
1115 | t = s; | | 1111 | t = s; |
1116 | do { | | 1112 | do { |
1117 | if (end && s >= end) { | | 1113 | if (end && s >= end) { |
1118 | debug((httpd, DEBUG_EXPLODING, | | 1114 | debug((httpd, DEBUG_EXPLODING, |
1119 | "fu_%%: past end, filling out..")); | | 1115 | "fu_%%: past end, filling out..")); |
1120 | while (*s) | | 1116 | while (*s) |
1121 | *t++ = *s++; | | 1117 | *t++ = *s++; |
1122 | break; | | 1118 | break; |
1123 | } | | 1119 | } |
1124 | debug((httpd, DEBUG_EXPLODING, | | 1120 | debug((httpd, DEBUG_EXPLODING, |
1125 | "fu_%%: got s == %%, s[1]s[2] == %c%c", | | 1121 | "fu_%%: got s == %%, s[1]s[2] == %c%c", |
1126 | s[1], s[2])); | | 1122 | s[1], s[2])); |
1127 | if (s[1] == '\0' || s[2] == '\0') { | | 1123 | if (s[1] == '\0' || s[2] == '\0') { |
1128 | (void)bozo_http_error(httpd, 400, request, | | 1124 | (void)bozo_http_error(httpd, 400, request, |
1129 | "percent hack missing two chars afterwards"); | | 1125 | "percent hack missing two chars afterwards"); |
1130 | goto copy_rest; | | 1126 | goto copy_rest; |
1131 | } | | 1127 | } |
1132 | if (s[1] == '0' && s[2] == '0') { | | 1128 | if (s[1] == '0' && s[2] == '0') { |
1133 | (void)bozo_http_error(httpd, 404, request, | | 1129 | (void)bozo_http_error(httpd, 404, request, |
1134 | "percent hack was %00"); | | 1130 | "percent hack was %00"); |
1135 | goto copy_rest; | | 1131 | goto copy_rest; |
1136 | } | | 1132 | } |
1137 | if (s[1] == '2' && s[2] == 'f') { | | 1133 | if (s[1] == '2' && s[2] == 'f') { |
1138 | (void)bozo_http_error(httpd, 404, request, | | 1134 | (void)bozo_http_error(httpd, 404, request, |
1139 | "percent hack was %2f (/)"); | | 1135 | "percent hack was %2f (/)"); |
1140 | goto copy_rest; | | 1136 | goto copy_rest; |
1141 | } | | 1137 | } |
1142 | | | 1138 | |
1143 | buf[0] = *++s; | | 1139 | buf[0] = *++s; |
1144 | buf[1] = *++s; | | 1140 | buf[1] = *++s; |
1145 | buf[2] = '\0'; | | 1141 | buf[2] = '\0'; |
1146 | s++; | | 1142 | s++; |
1147 | *t = (char)strtol(buf, NULL, 16); | | 1143 | *t = (char)strtol(buf, NULL, 16); |
1148 | debug((httpd, DEBUG_EXPLODING, | | 1144 | debug((httpd, DEBUG_EXPLODING, |
1149 | "fu_%%: strtol put '%02x' into *t", *t)); | | 1145 | "fu_%%: strtol put '%02x' into *t", *t)); |
1150 | if (*t++ == '\0') { | | 1146 | if (*t++ == '\0') { |
1151 | (void)bozo_http_error(httpd, 400, request, | | 1147 | (void)bozo_http_error(httpd, 400, request, |
1152 | "percent hack got a 0 back"); | | 1148 | "percent hack got a 0 back"); |
1153 | goto copy_rest; | | 1149 | goto copy_rest; |
1154 | } | | 1150 | } |
1155 | | | 1151 | |
1156 | while (*s && *s != '%') { | | 1152 | while (*s && *s != '%') { |
1157 | if (end && s >= end) | | 1153 | if (end && s >= end) |
1158 | break; | | 1154 | break; |
1159 | *t++ = *s++; | | 1155 | *t++ = *s++; |
1160 | } | | 1156 | } |
1161 | } while (*s); | | 1157 | } while (*s); |
1162 | copy_rest: | | 1158 | copy_rest: |
1163 | while (*s) { | | 1159 | while (*s) { |
1164 | if (s >= end) | | 1160 | if (s >= end) |
1165 | break; | | 1161 | break; |
1166 | *t++ = *s++; | | 1162 | *t++ = *s++; |
1167 | } | | 1163 | } |
1168 | *t = '\0'; | | 1164 | *t = '\0'; |
1169 | debug((httpd, DEBUG_FAT, "fix_url_percent returns %s in url", | | 1165 | debug((httpd, DEBUG_FAT, "fix_url_percent returns %s in url", |
1170 | request->hr_file)); | | 1166 | request->hr_file)); |
1171 | } | | 1167 | } |
1172 | | | 1168 | |
1173 | /* | | 1169 | /* |
1174 | * transform_request does this: | | 1170 | * transform_request does this: |
1175 | * - ``expand'' %20 crapola | | 1171 | * - ``expand'' %20 crapola |
1176 | * - punt if it doesn't start with / | | 1172 | * - punt if it doesn't start with / |
1177 | * - check httpd->untrustedref / referrer | | 1173 | * - check httpd->untrustedref / referrer |
1178 | * - look for "http://myname/" and deal with it. | | 1174 | * - look for "http://myname/" and deal with it. |
1179 | * - maybe call bozo_process_cgi() | | 1175 | * - maybe call bozo_process_cgi() |
1180 | * - check for ~user and call bozo_user_transform() if so | | 1176 | * - check for ~user and call bozo_user_transform() if so |
1181 | * - if the length > 1, check for trailing slash. if so, | | 1177 | * - if the length > 1, check for trailing slash. if so, |
1182 | * add the index.html file | | 1178 | * add the index.html file |
1183 | * - if the length is 1, return the index.html file | | 1179 | * - if the length is 1, return the index.html file |
1184 | * - disallow anything ending up with a file starting | | 1180 | * - disallow anything ending up with a file starting |
1185 | * at "/" or having ".." in it. | | 1181 | * at "/" or having ".." in it. |
1186 | * - anything else is a really weird internal error | | 1182 | * - anything else is a really weird internal error |
1187 | * - returns malloced file to serve, if unhandled | | 1183 | * - returns malloced file to serve, if unhandled |
1188 | */ | | 1184 | */ |
1189 | static int | | 1185 | static int |
1190 | transform_request(bozo_httpreq_t *request, int *isindex) | | 1186 | transform_request(bozo_httpreq_t *request, int *isindex) |
1191 | { | | 1187 | { |
1192 | bozohttpd_t *httpd = request->hr_httpd; | | 1188 | bozohttpd_t *httpd = request->hr_httpd; |
1193 | char *file, *newfile = NULL; | | 1189 | char *file, *newfile = NULL; |
1194 | size_t len; | | 1190 | size_t len; |
1195 | | | 1191 | |
1196 | file = NULL; | | 1192 | file = NULL; |
1197 | *isindex = 0; | | 1193 | *isindex = 0; |
1198 | debug((httpd, DEBUG_FAT, "tf_req: file %s", request->hr_file)); | | 1194 | debug((httpd, DEBUG_FAT, "tf_req: file %s", request->hr_file)); |
1199 | fix_url_percent(request); | | 1195 | fix_url_percent(request); |
1200 | if (check_virtual(request)) { | | 1196 | if (check_virtual(request)) { |
1201 | goto bad_done; | | 1197 | goto bad_done; |
1202 | } | | 1198 | } |
1203 | file = request->hr_file; | | 1199 | file = request->hr_file; |
1204 | | | 1200 | |
1205 | if (file[0] != '/') { | | 1201 | if (file[0] != '/') { |
1206 | (void)bozo_http_error(httpd, 404, request, "unknown URL"); | | 1202 | (void)bozo_http_error(httpd, 404, request, "unknown URL"); |
1207 | goto bad_done; | | 1203 | goto bad_done; |
1208 | } | | 1204 | } |
1209 | | | 1205 | |
1210 | check_bzredirect(request); | | 1206 | check_bzredirect(request); |
1211 | | | 1207 | |
1212 | if (httpd->untrustedref) { | | 1208 | if (httpd->untrustedref) { |
1213 | int to_indexhtml = 0; | | 1209 | int to_indexhtml = 0; |
1214 | | | 1210 | |
1215 | #define TOP_PAGE(x) (strcmp((x), "/") == 0 || \ | | 1211 | #define TOP_PAGE(x) (strcmp((x), "/") == 0 || \ |
1216 | strcmp((x) + 1, httpd->index_html) == 0 || \ | | 1212 | strcmp((x) + 1, httpd->index_html) == 0 || \ |
1217 | strcmp((x) + 1, "favicon.ico") == 0) | | 1213 | strcmp((x) + 1, "favicon.ico") == 0) |
1218 | | | 1214 | |
1219 | debug((httpd, DEBUG_EXPLODING, "checking httpd->untrustedref")); | | 1215 | debug((httpd, DEBUG_EXPLODING, "checking httpd->untrustedref")); |
1220 | /* | | 1216 | /* |
1221 | * first check that this path isn't allowed via .bzdirect file, | | 1217 | * first check that this path isn't allowed via .bzdirect file, |
1222 | * and then check referrer; make sure that people come via the | | 1218 | * and then check referrer; make sure that people come via the |
1223 | * real name... otherwise if we aren't looking at / or | | 1219 | * real name... otherwise if we aren't looking at / or |
1224 | * /index.html, redirect... we also special case favicon.ico. | | 1220 | * /index.html, redirect... we also special case favicon.ico. |
1225 | */ | | 1221 | */ |
1226 | if (check_direct_access(request)) | | 1222 | if (check_direct_access(request)) |
1227 | /* nothing */; | | 1223 | /* nothing */; |
1228 | else if (request->hr_referrer) { | | 1224 | else if (request->hr_referrer) { |
1229 | const char *r = request->hr_referrer; | | 1225 | const char *r = request->hr_referrer; |
1230 | | | 1226 | |
1231 | debug((httpd, DEBUG_FAT, | | 1227 | debug((httpd, DEBUG_FAT, |
1232 | "checking referrer \"%s\" vs virthostname %s", | | 1228 | "checking referrer \"%s\" vs virthostname %s", |
1233 | r, httpd->virthostname)); | | 1229 | r, httpd->virthostname)); |
1234 | if (strncmp(r, "http://", 7) != 0 || | | 1230 | if (strncmp(r, "http://", 7) != 0 || |
1235 | (strncasecmp(r + 7, httpd->virthostname, | | 1231 | (strncasecmp(r + 7, httpd->virthostname, |
1236 | strlen(httpd->virthostname)) != 0 && | | 1232 | strlen(httpd->virthostname)) != 0 && |
1237 | !TOP_PAGE(file))) | | 1233 | !TOP_PAGE(file))) |
1238 | to_indexhtml = 1; | | 1234 | to_indexhtml = 1; |
1239 | } else { | | 1235 | } else { |
1240 | const char *h = request->hr_host; | | 1236 | const char *h = request->hr_host; |
1241 | | | 1237 | |
1242 | debug((httpd, DEBUG_FAT, "url has no referrer at all")); | | 1238 | debug((httpd, DEBUG_FAT, "url has no referrer at all")); |
1243 | /* if there's no referrer, let / or /index.html past */ | | 1239 | /* if there's no referrer, let / or /index.html past */ |
1244 | if (!TOP_PAGE(file) || | | 1240 | if (!TOP_PAGE(file) || |
1245 | (h && strncasecmp(h, httpd->virthostname, | | 1241 | (h && strncasecmp(h, httpd->virthostname, |
1246 | strlen(httpd->virthostname)) != 0)) | | 1242 | strlen(httpd->virthostname)) != 0)) |
1247 | to_indexhtml = 1; | | 1243 | to_indexhtml = 1; |
1248 | } | | 1244 | } |
1249 | | | 1245 | |
1250 | if (to_indexhtml) { | | 1246 | if (to_indexhtml) { |
1251 | char *slashindexhtml; | | 1247 | char *slashindexhtml; |
1252 | | | 1248 | |
1253 | if (asprintf(&slashindexhtml, "/%s", | | 1249 | if (asprintf(&slashindexhtml, "/%s", |
1254 | httpd->index_html) < 0) | | 1250 | httpd->index_html) < 0) |
1255 | bozo_err(httpd, 1, "asprintf"); | | 1251 | bozo_err(httpd, 1, "asprintf"); |
1256 | debug((httpd, DEBUG_FAT, | | 1252 | debug((httpd, DEBUG_FAT, |
1257 | "httpd->untrustedref: redirecting %s to %s", | | 1253 | "httpd->untrustedref: redirecting %s to %s", |
1258 | file, slashindexhtml)); | | 1254 | file, slashindexhtml)); |
1259 | handle_redirect(request, slashindexhtml, 0); | | 1255 | handle_redirect(request, slashindexhtml, 0); |
1260 | free(slashindexhtml); | | 1256 | free(slashindexhtml); |
1261 | return 0; | | 1257 | return 0; |
1262 | } | | 1258 | } |
1263 | } | | 1259 | } |
1264 | | | 1260 | |
1265 | len = strlen(file); | | 1261 | len = strlen(file); |
1266 | if (/*CONSTCOND*/0) { | | 1262 | if (/*CONSTCOND*/0) { |
1267 | #ifndef NO_USER_SUPPORT | | 1263 | #ifndef NO_USER_SUPPORT |
1268 | } else if (len > 1 && httpd->enable_users && file[1] == '~') { | | 1264 | } else if (len > 1 && httpd->enable_users && file[1] == '~') { |
1269 | if (file[2] == '\0') { | | 1265 | if (file[2] == '\0') { |
1270 | (void)bozo_http_error(httpd, 404, request, | | 1266 | (void)bozo_http_error(httpd, 404, request, |
1271 | "missing username"); | | 1267 | "missing username"); |
1272 | goto bad_done; | | 1268 | goto bad_done; |
1273 | } | | 1269 | } |
1274 | if (strchr(file + 2, '/') == NULL) { | | 1270 | if (strchr(file + 2, '/') == NULL) { |
1275 | handle_redirect(request, NULL, 0); | | 1271 | handle_redirect(request, NULL, 0); |
1276 | return 0; | | 1272 | return 0; |
1277 | } | | 1273 | } |
1278 | debug((httpd, DEBUG_FAT, "calling bozo_user_transform")); | | 1274 | debug((httpd, DEBUG_FAT, "calling bozo_user_transform")); |
1279 | | | 1275 | |
1280 | return bozo_user_transform(request, isindex); | | 1276 | return bozo_user_transform(request, isindex); |
1281 | #endif /* NO_USER_SUPPORT */ | | 1277 | #endif /* NO_USER_SUPPORT */ |
1282 | } else if (len > 1) { | | 1278 | } else if (len > 1) { |
1283 | debug((httpd, DEBUG_FAT, "file[len-1] == %c", file[len-1])); | | 1279 | debug((httpd, DEBUG_FAT, "file[len-1] == %c", file[len-1])); |
1284 | if (file[len-1] == '/') { /* append index.html */ | | 1280 | if (file[len-1] == '/') { /* append index.html */ |
1285 | *isindex = 1; | | 1281 | *isindex = 1; |
1286 | debug((httpd, DEBUG_FAT, "appending index.html")); | | 1282 | debug((httpd, DEBUG_FAT, "appending index.html")); |
1287 | newfile = bozomalloc(httpd, | | 1283 | newfile = bozomalloc(httpd, |
1288 | len + strlen(httpd->index_html) + 1); | | 1284 | len + strlen(httpd->index_html) + 1); |
1289 | strcpy(newfile, file + 1); | | 1285 | strcpy(newfile, file + 1); |
1290 | strcat(newfile, httpd->index_html); | | 1286 | strcat(newfile, httpd->index_html); |
1291 | } else | | 1287 | } else |
1292 | newfile = bozostrdup(request->hr_httpd, file + 1); | | 1288 | newfile = bozostrdup(request->hr_httpd, file + 1); |
1293 | } else if (len == 1) { | | 1289 | } else if (len == 1) { |
1294 | debug((httpd, DEBUG_EXPLODING, "tf_req: len == 1")); | | 1290 | debug((httpd, DEBUG_EXPLODING, "tf_req: len == 1")); |
1295 | newfile = bozostrdup(request->hr_httpd, httpd->index_html); | | 1291 | newfile = bozostrdup(request->hr_httpd, httpd->index_html); |
1296 | *isindex = 1; | | 1292 | *isindex = 1; |
1297 | } else { /* len == 0 ? */ | | 1293 | } else { /* len == 0 ? */ |
1298 | (void)bozo_http_error(httpd, 500, request, | | 1294 | (void)bozo_http_error(httpd, 500, request, |
1299 | "request->hr_file is nul?"); | | 1295 | "request->hr_file is nul?"); |
1300 | goto bad_done; | | 1296 | goto bad_done; |
1301 | } | | 1297 | } |
1302 | | | 1298 | |
1303 | if (newfile == NULL) { | | 1299 | if (newfile == NULL) { |
1304 | (void)bozo_http_error(httpd, 500, request, "internal failure"); | | 1300 | (void)bozo_http_error(httpd, 500, request, "internal failure"); |
1305 | goto bad_done; | | 1301 | goto bad_done; |
1306 | } | | 1302 | } |
1307 | | | 1303 | |
1308 | /* | | 1304 | /* |
1309 | * look for "http://myname/" and deal with it as necessary. | | 1305 | * look for "http://myname/" and deal with it as necessary. |
1310 | */ | | 1306 | */ |
1311 | | | 1307 | |
1312 | /* | | 1308 | /* |
1313 | * stop traversing outside our domain | | 1309 | * stop traversing outside our domain |
1314 | * | | 1310 | * |
1315 | * XXX true security only comes from our parent using chroot(2) | | 1311 | * XXX true security only comes from our parent using chroot(2) |
1316 | * before execve(2)'ing us. or our own built in chroot(2) support. | | 1312 | * before execve(2)'ing us. or our own built in chroot(2) support. |
1317 | */ | | 1313 | */ |
1318 | if (*newfile == '/' || strcmp(newfile, "..") == 0 || | | 1314 | if (*newfile == '/' || strcmp(newfile, "..") == 0 || |
1319 | strstr(newfile, "/..") || strstr(newfile, "../")) { | | 1315 | strstr(newfile, "/..") || strstr(newfile, "../")) { |
1320 | (void)bozo_http_error(httpd, 403, request, "illegal request"); | | 1316 | (void)bozo_http_error(httpd, 403, request, "illegal request"); |
1321 | goto bad_done; | | 1317 | goto bad_done; |
1322 | } | | 1318 | } |
1323 | | | 1319 | |
1324 | if (bozo_auth_check(request, newfile)) | | 1320 | if (bozo_auth_check(request, newfile)) |
1325 | goto bad_done; | | 1321 | goto bad_done; |
1326 | | | 1322 | |
1327 | if (strlen(newfile)) { | | 1323 | if (strlen(newfile)) { |
1328 | request->hr_oldfile = request->hr_file; | | 1324 | request->hr_oldfile = request->hr_file; |
1329 | request->hr_file = newfile; | | 1325 | request->hr_file = newfile; |
1330 | } | | 1326 | } |
1331 | | | 1327 | |
1332 | if (bozo_process_cgi(request)) | | 1328 | if (bozo_process_cgi(request)) |
1333 | return 0; | | 1329 | return 0; |
1334 | | | 1330 | |
1335 | debug((httpd, DEBUG_FAT, "transform_request set: %s", newfile)); | | 1331 | debug((httpd, DEBUG_FAT, "transform_request set: %s", newfile)); |
1336 | return 1; | | 1332 | return 1; |
1337 | bad_done: | | 1333 | bad_done: |
1338 | debug((httpd, DEBUG_FAT, "transform_request returning: 0")); | | 1334 | debug((httpd, DEBUG_FAT, "transform_request returning: 0")); |
1339 | if (newfile) | | 1335 | if (newfile) |
1340 | free(newfile); | | 1336 | free(newfile); |
1341 | return 0; | | 1337 | return 0; |
1342 | } | | 1338 | } |
1343 | | | 1339 | |
1344 | /* | | 1340 | /* |
1345 | * bozo_process_request does the following: | | 1341 | * bozo_process_request does the following: |
1346 | * - check the request is valid | | 1342 | * - check the request is valid |
1347 | * - process cgi-bin if necessary | | 1343 | * - process cgi-bin if necessary |
1348 | * - transform a filename if necesarry | | 1344 | * - transform a filename if necesarry |
1349 | * - return the HTTP request | | 1345 | * - return the HTTP request |
1350 | */ | | 1346 | */ |
1351 | void | | 1347 | void |
1352 | bozo_process_request(bozo_httpreq_t *request) | | 1348 | bozo_process_request(bozo_httpreq_t *request) |
1353 | { | | 1349 | { |
1354 | bozohttpd_t *httpd = request->hr_httpd; | | 1350 | bozohttpd_t *httpd = request->hr_httpd; |
1355 | struct stat sb; | | 1351 | struct stat sb; |
1356 | time_t timestamp; | | 1352 | time_t timestamp; |
1357 | char *file; | | 1353 | char *file; |
1358 | const char *type, *encoding; | | 1354 | const char *type, *encoding; |
1359 | int fd, isindex; | | 1355 | int fd, isindex; |
1360 | | | 1356 | |
1361 | /* | | 1357 | /* |
1362 | * note that transform_request chdir()'s if required. also note | | 1358 | * note that transform_request chdir()'s if required. also note |
1363 | * that cgi is handed here. if transform_request() returns 0 | | 1359 | * that cgi is handed here. if transform_request() returns 0 |
1364 | * then the request has been handled already. | | 1360 | * then the request has been handled already. |
1365 | */ | | 1361 | */ |
1366 | if (transform_request(request, &isindex) == 0) | | 1362 | if (transform_request(request, &isindex) == 0) |
1367 | return; | | 1363 | return; |
1368 | | | 1364 | |
1369 | file = request->hr_file; | | 1365 | file = request->hr_file; |
1370 | | | 1366 | |
1371 | fd = open(file, O_RDONLY); | | 1367 | fd = open(file, O_RDONLY); |
1372 | if (fd < 0) { | | 1368 | if (fd < 0) { |
1373 | debug((httpd, DEBUG_FAT, "open failed: %s", strerror(errno))); | | 1369 | debug((httpd, DEBUG_FAT, "open failed: %s", strerror(errno))); |
1374 | if (errno == EPERM) | | 1370 | if (errno == EPERM) |
1375 | (void)bozo_http_error(httpd, 403, request, | | 1371 | (void)bozo_http_error(httpd, 403, request, |
1376 | "no permission to open file"); | | 1372 | "no permission to open file"); |
1377 | else if (errno == ENOENT) { | | 1373 | else if (errno == ENOENT) { |
1378 | if (!bozo_dir_index(request, file, isindex)) | | 1374 | if (!bozo_dir_index(request, file, isindex)) |
1379 | (void)bozo_http_error(httpd, 404, request, | | 1375 | (void)bozo_http_error(httpd, 404, request, |
1380 | "no file"); | | 1376 | "no file"); |
1381 | } else | | 1377 | } else |
1382 | (void)bozo_http_error(httpd, 500, request, "open file"); | | 1378 | (void)bozo_http_error(httpd, 500, request, "open file"); |
1383 | goto cleanup_nofd; | | 1379 | goto cleanup_nofd; |
1384 | } | | 1380 | } |
1385 | if (fstat(fd, &sb) < 0) { | | 1381 | if (fstat(fd, &sb) < 0) { |
1386 | (void)bozo_http_error(httpd, 500, request, "can't fstat"); | | 1382 | (void)bozo_http_error(httpd, 500, request, "can't fstat"); |
1387 | goto cleanup; | | 1383 | goto cleanup; |
1388 | } | | 1384 | } |
1389 | if (S_ISDIR(sb.st_mode)) { | | 1385 | if (S_ISDIR(sb.st_mode)) { |
1390 | handle_redirect(request, NULL, 0); | | 1386 | handle_redirect(request, NULL, 0); |
1391 | goto cleanup; | | 1387 | goto cleanup; |
1392 | } | | 1388 | } |
1393 | | | 1389 | |
1394 | if (request->hr_if_modified_since && | | 1390 | if (request->hr_if_modified_since && |
1395 | parse_http_date(request->hr_if_modified_since, ×tamp) && | | 1391 | parse_http_date(request->hr_if_modified_since, ×tamp) && |
1396 | timestamp >= sb.st_mtime) { | | 1392 | timestamp >= sb.st_mtime) { |
1397 | /* XXX ignore subsecond of timestamp */ | | 1393 | /* XXX ignore subsecond of timestamp */ |
1398 | bozo_printf(httpd, "%s 304 Not Modified\r\n", | | 1394 | bozo_printf(httpd, "%s 304 Not Modified\r\n", |
1399 | request->hr_proto); | | 1395 | request->hr_proto); |
1400 | bozo_printf(httpd, "\r\n"); | | 1396 | bozo_printf(httpd, "\r\n"); |
1401 | bozo_flush(httpd, stdout); | | 1397 | bozo_flush(httpd, stdout); |
1402 | goto cleanup; | | 1398 | goto cleanup; |
1403 | } | | 1399 | } |
1404 | | | 1400 | |
1405 | /* validate requested range */ | | 1401 | /* validate requested range */ |
1406 | if (request->hr_last_byte_pos == -1 || | | 1402 | if (request->hr_last_byte_pos == -1 || |
1407 | request->hr_last_byte_pos >= sb.st_size) | | 1403 | request->hr_last_byte_pos >= sb.st_size) |
1408 | request->hr_last_byte_pos = sb.st_size - 1; | | 1404 | request->hr_last_byte_pos = sb.st_size - 1; |
1409 | if (request->hr_have_range && | | 1405 | if (request->hr_have_range && |
1410 | request->hr_first_byte_pos > request->hr_last_byte_pos) { | | 1406 | request->hr_first_byte_pos > request->hr_last_byte_pos) { |
1411 | request->hr_have_range = 0; /* punt */ | | 1407 | request->hr_have_range = 0; /* punt */ |
1412 | request->hr_first_byte_pos = 0; | | 1408 | request->hr_first_byte_pos = 0; |
1413 | request->hr_last_byte_pos = sb.st_size - 1; | | 1409 | request->hr_last_byte_pos = sb.st_size - 1; |
1414 | } | | 1410 | } |
1415 | debug((httpd, DEBUG_FAT, "have_range %d first_pos %qd last_pos %qd", | | 1411 | debug((httpd, DEBUG_FAT, "have_range %d first_pos %lld last_pos %lld", |
1416 | request->hr_have_range, | | 1412 | request->hr_have_range, |
1417 | request->hr_first_byte_pos, request->hr_last_byte_pos)); | | 1413 | (long long)request->hr_first_byte_pos, |
| | | 1414 | (long long)request->hr_last_byte_pos)); |
1418 | if (request->hr_have_range) | | 1415 | if (request->hr_have_range) |
1419 | bozo_printf(httpd, "%s 206 Partial Content\r\n", | | 1416 | bozo_printf(httpd, "%s 206 Partial Content\r\n", |
1420 | request->hr_proto); | | 1417 | request->hr_proto); |
1421 | else | | 1418 | else |
1422 | bozo_printf(httpd, "%s 200 OK\r\n", request->hr_proto); | | 1419 | bozo_printf(httpd, "%s 200 OK\r\n", request->hr_proto); |
1423 | | | 1420 | |
1424 | if (request->hr_proto != httpd->consts.http_09) { | | 1421 | if (request->hr_proto != httpd->consts.http_09) { |
1425 | type = bozo_content_type(request, file); | | 1422 | type = bozo_content_type(request, file); |
1426 | encoding = bozo_content_encoding(request, file); | | 1423 | encoding = bozo_content_encoding(request, file); |
1427 | | | 1424 | |
1428 | bozo_print_header(request, &sb, type, encoding); | | 1425 | bozo_print_header(request, &sb, type, encoding); |
1429 | bozo_printf(httpd, "\r\n"); | | 1426 | bozo_printf(httpd, "\r\n"); |
1430 | } | | 1427 | } |
1431 | bozo_flush(httpd, stdout); | | 1428 | bozo_flush(httpd, stdout); |
1432 | | | 1429 | |
1433 | if (request->hr_method != HTTP_HEAD) { | | 1430 | if (request->hr_method != HTTP_HEAD) { |
1434 | off_t szleft, cur_byte_pos; | | 1431 | off_t szleft, cur_byte_pos; |
1435 | | | 1432 | |
1436 | szleft = | | 1433 | szleft = |
1437 | request->hr_last_byte_pos - request->hr_first_byte_pos + 1; | | 1434 | request->hr_last_byte_pos - request->hr_first_byte_pos + 1; |
1438 | cur_byte_pos = request->hr_first_byte_pos; | | 1435 | cur_byte_pos = request->hr_first_byte_pos; |
1439 | | | 1436 | |
1440 | retry: | | 1437 | retry: |
1441 | while (szleft) { | | 1438 | while (szleft) { |
1442 | size_t sz; | | 1439 | size_t sz; |
1443 | | | 1440 | |
1444 | /* This should take care of the first unaligned chunk */ | | 1441 | /* This should take care of the first unaligned chunk */ |
1445 | if ((cur_byte_pos & (httpd->page_size - 1)) != 0) | | 1442 | if ((cur_byte_pos & (httpd->page_size - 1)) != 0) |
1446 | sz = (size_t)(cur_byte_pos & ~httpd->page_size); | | 1443 | sz = (size_t)(cur_byte_pos & ~httpd->page_size); |
1447 | if ((off_t)httpd->mmapsz < szleft) | | 1444 | if ((off_t)httpd->mmapsz < szleft) |
1448 | sz = httpd->mmapsz; | | 1445 | sz = httpd->mmapsz; |
1449 | else | | 1446 | else |
1450 | sz = (size_t)szleft; | | 1447 | sz = (size_t)szleft; |
1451 | if (mmap_and_write_part(httpd, fd, cur_byte_pos, sz)) { | | 1448 | if (mmap_and_write_part(httpd, fd, cur_byte_pos, sz)) { |
1452 | if (errno == ENOMEM) { | | 1449 | if (errno == ENOMEM) { |
1453 | httpd->mmapsz /= 2; | | 1450 | httpd->mmapsz /= 2; |
1454 | if (httpd->mmapsz >= httpd->page_size) | | 1451 | if (httpd->mmapsz >= httpd->page_size) |
1455 | goto retry; | | 1452 | goto retry; |
1456 | } | | 1453 | } |
1457 | goto cleanup; | | 1454 | goto cleanup; |
1458 | } | | 1455 | } |
1459 | cur_byte_pos += sz; | | 1456 | cur_byte_pos += sz; |
1460 | szleft -= sz; | | 1457 | szleft -= sz; |
1461 | } | | 1458 | } |
1462 | } | | 1459 | } |
1463 | cleanup: | | 1460 | cleanup: |
1464 | close(fd); | | 1461 | close(fd); |
1465 | cleanup_nofd: | | 1462 | cleanup_nofd: |
1466 | close(STDIN_FILENO); | | 1463 | close(STDIN_FILENO); |
1467 | close(STDOUT_FILENO); | | 1464 | close(STDOUT_FILENO); |
1468 | /*close(STDERR_FILENO);*/ | | 1465 | /*close(STDERR_FILENO);*/ |
1469 | } | | 1466 | } |
1470 | | | 1467 | |
1471 | /* make sure we're not trying to access special files */ | | 1468 | /* make sure we're not trying to access special files */ |
1472 | int | | 1469 | int |
1473 | bozo_check_special_files(bozo_httpreq_t *request, const char *name) | | 1470 | bozo_check_special_files(bozo_httpreq_t *request, const char *name) |
1474 | { | | 1471 | { |
1475 | bozohttpd_t *httpd = request->hr_httpd; | | 1472 | bozohttpd_t *httpd = request->hr_httpd; |
1476 | | | 1473 | |
1477 | /* ensure basename(name) != special files */ | | 1474 | /* ensure basename(name) != special files */ |
1478 | if (strcmp(name, DIRECT_ACCESS_FILE) == 0) | | 1475 | if (strcmp(name, DIRECT_ACCESS_FILE) == 0) |
1479 | return bozo_http_error(httpd, 403, request, | | 1476 | return bozo_http_error(httpd, 403, request, |
1480 | "no permission to open direct access file"); | | 1477 | "no permission to open direct access file"); |
1481 | if (strcmp(name, REDIRECT_FILE) == 0) | | 1478 | if (strcmp(name, REDIRECT_FILE) == 0) |
1482 | return bozo_http_error(httpd, 403, request, | | 1479 | return bozo_http_error(httpd, 403, request, |
1483 | "no permission to open redirect file"); | | 1480 | "no permission to open redirect file"); |
1484 | if (strcmp(name, ABSREDIRECT_FILE) == 0) | | 1481 | if (strcmp(name, ABSREDIRECT_FILE) == 0) |
1485 | return bozo_http_error(httpd, 403, request, | | 1482 | return bozo_http_error(httpd, 403, request, |
1486 | "no permission to open redirect file"); | | 1483 | "no permission to open redirect file"); |
1487 | return bozo_auth_check_special_files(request, name); | | 1484 | return bozo_auth_check_special_files(request, name); |
1488 | } | | 1485 | } |
1489 | | | 1486 | |
1490 | /* generic header printing routine */ | | 1487 | /* generic header printing routine */ |
1491 | void | | 1488 | void |
1492 | bozo_print_header(bozo_httpreq_t *request, | | 1489 | bozo_print_header(bozo_httpreq_t *request, |
1493 | struct stat *sbp, const char *type, const char *encoding) | | 1490 | struct stat *sbp, const char *type, const char *encoding) |
1494 | { | | 1491 | { |
1495 | bozohttpd_t *httpd = request->hr_httpd; | | 1492 | bozohttpd_t *httpd = request->hr_httpd; |
1496 | off_t len; | | 1493 | off_t len; |
1497 | char date[40]; | | 1494 | char date[40]; |
1498 | | | 1495 | |
1499 | bozo_printf(httpd, "Date: %s\r\n", bozo_http_date(date, sizeof(date))); | | 1496 | bozo_printf(httpd, "Date: %s\r\n", bozo_http_date(date, sizeof(date))); |
1500 | bozo_printf(httpd, "Server: %s\r\n", httpd->server_software); | | 1497 | bozo_printf(httpd, "Server: %s\r\n", httpd->server_software); |
1501 | bozo_printf(httpd, "Accept-Ranges: bytes\r\n"); | | 1498 | bozo_printf(httpd, "Accept-Ranges: bytes\r\n"); |
1502 | if (sbp) { | | 1499 | if (sbp) { |
1503 | char filedate[40]; | | 1500 | char filedate[40]; |
1504 | struct tm *tm; | | 1501 | struct tm *tm; |
1505 | | | 1502 | |
1506 | tm = gmtime(&sbp->st_mtime); | | 1503 | tm = gmtime(&sbp->st_mtime); |
1507 | strftime(filedate, sizeof filedate, | | 1504 | strftime(filedate, sizeof filedate, |
1508 | "%a, %d %b %Y %H:%M:%S GMT", tm); | | 1505 | "%a, %d %b %Y %H:%M:%S GMT", tm); |
1509 | bozo_printf(httpd, "Last-Modified: %s\r\n", filedate); | | 1506 | bozo_printf(httpd, "Last-Modified: %s\r\n", filedate); |
1510 | } | | 1507 | } |
1511 | if (type && *type) | | 1508 | if (type && *type) |
1512 | bozo_printf(httpd, "Content-Type: %s\r\n", type); | | 1509 | bozo_printf(httpd, "Content-Type: %s\r\n", type); |
1513 | if (encoding && *encoding) | | 1510 | if (encoding && *encoding) |
1514 | bozo_printf(httpd, "Content-Encoding: %s\r\n", encoding); | | 1511 | bozo_printf(httpd, "Content-Encoding: %s\r\n", encoding); |
1515 | if (sbp) { | | 1512 | if (sbp) { |
1516 | if (request->hr_have_range) { | | 1513 | if (request->hr_have_range) { |
1517 | len = request->hr_last_byte_pos - | | 1514 | len = request->hr_last_byte_pos - |
1518 | request->hr_first_byte_pos +1; | | 1515 | request->hr_first_byte_pos +1; |
1519 | bozo_printf(httpd, | | 1516 | bozo_printf(httpd, |
1520 | "Content-Range: bytes %qd-%qd/%qd\r\n", | | 1517 | "Content-Range: bytes %qd-%qd/%qd\r\n", |
1521 | (long long) request->hr_first_byte_pos, | | 1518 | (long long) request->hr_first_byte_pos, |
1522 | (long long) request->hr_last_byte_pos, | | 1519 | (long long) request->hr_last_byte_pos, |
1523 | (long long) sbp->st_size); | | 1520 | (long long) sbp->st_size); |
1524 | } else | | 1521 | } else |
1525 | len = sbp->st_size; | | 1522 | len = sbp->st_size; |
1526 | bozo_printf(httpd, "Content-Length: %qd\r\n", (long long)len); | | 1523 | bozo_printf(httpd, "Content-Length: %qd\r\n", (long long)len); |
1527 | } | | 1524 | } |
1528 | if (request && request->hr_proto == httpd->consts.http_11) | | 1525 | if (request && request->hr_proto == httpd->consts.http_11) |
1529 | bozo_printf(httpd, "Connection: close\r\n"); | | 1526 | bozo_printf(httpd, "Connection: close\r\n"); |
1530 | bozo_flush(httpd, stdout); | | 1527 | bozo_flush(httpd, stdout); |
1531 | } | | 1528 | } |
1532 | | | 1529 | |
1533 | #ifndef NO_DEBUG | | 1530 | #ifndef NO_DEBUG |
1534 | void | | 1531 | void |
1535 | debug__(bozohttpd_t *httpd, int level, const char *fmt, ...) | | 1532 | debug__(bozohttpd_t *httpd, int level, const char *fmt, ...) |
1536 | { | | 1533 | { |
1537 | va_list ap; | | 1534 | va_list ap; |
1538 | int savederrno; | | 1535 | int savederrno; |
1539 | | | 1536 | |
1540 | /* only log if the level is low enough */ | | 1537 | /* only log if the level is low enough */ |
1541 | if (httpd->debug < level) | | 1538 | if (httpd->debug < level) |
1542 | return; | | 1539 | return; |
1543 | | | 1540 | |
1544 | savederrno = errno; | | 1541 | savederrno = errno; |
1545 | va_start(ap, fmt); | | 1542 | va_start(ap, fmt); |
1546 | if (httpd->logstderr) { | | 1543 | if (httpd->logstderr) { |
1547 | vfprintf(stderr, fmt, ap); | | 1544 | vfprintf(stderr, fmt, ap); |
1548 | fputs("\n", stderr); | | 1545 | fputs("\n", stderr); |
1549 | } else | | 1546 | } else |
1550 | vsyslog(LOG_DEBUG, fmt, ap); | | 1547 | vsyslog(LOG_DEBUG, fmt, ap); |
1551 | va_end(ap); | | 1548 | va_end(ap); |
1552 | errno = savederrno; | | 1549 | errno = savederrno; |
1553 | } | | 1550 | } |
1554 | #endif /* NO_DEBUG */ | | 1551 | #endif /* NO_DEBUG */ |
1555 | | | 1552 | |
1556 | /* these are like warn() and err(), except for syslog not stderr */ | | 1553 | /* these are like warn() and err(), except for syslog not stderr */ |
1557 | void | | 1554 | void |
1558 | bozo_warn(bozohttpd_t *httpd, const char *fmt, ...) | | 1555 | bozo_warn(bozohttpd_t *httpd, const char *fmt, ...) |
1559 | { | | 1556 | { |
1560 | va_list ap; | | 1557 | va_list ap; |
1561 | | | 1558 | |
1562 | va_start(ap, fmt); | | 1559 | va_start(ap, fmt); |
1563 | if (httpd->logstderr || isatty(STDERR_FILENO)) { | | 1560 | if (httpd->logstderr || isatty(STDERR_FILENO)) { |
1564 | //fputs("warning: ", stderr); | | 1561 | //fputs("warning: ", stderr); |
1565 | vfprintf(stderr, fmt, ap); | | 1562 | vfprintf(stderr, fmt, ap); |
1566 | fputs("\n", stderr); | | 1563 | fputs("\n", stderr); |
1567 | } else | | 1564 | } else |
1568 | vsyslog(LOG_INFO, fmt, ap); | | 1565 | vsyslog(LOG_INFO, fmt, ap); |
1569 | va_end(ap); | | 1566 | va_end(ap); |
1570 | } | | 1567 | } |
1571 | | | 1568 | |
1572 | void | | 1569 | void |
1573 | bozo_err(bozohttpd_t *httpd, int code, const char *fmt, ...) | | 1570 | bozo_err(bozohttpd_t *httpd, int code, const char *fmt, ...) |
1574 | { | | 1571 | { |
1575 | va_list ap; | | 1572 | va_list ap; |
1576 | | | 1573 | |
1577 | va_start(ap, fmt); | | 1574 | va_start(ap, fmt); |
1578 | if (httpd->logstderr || isatty(STDERR_FILENO)) { | | 1575 | if (httpd->logstderr || isatty(STDERR_FILENO)) { |
1579 | //fputs("error: ", stderr); | | 1576 | //fputs("error: ", stderr); |
1580 | vfprintf(stderr, fmt, ap); | | 1577 | vfprintf(stderr, fmt, ap); |
1581 | fputs("\n", stderr); | | 1578 | fputs("\n", stderr); |
1582 | } else | | 1579 | } else |
1583 | vsyslog(LOG_ERR, fmt, ap); | | 1580 | vsyslog(LOG_ERR, fmt, ap); |
1584 | va_end(ap); | | 1581 | va_end(ap); |
1585 | exit(code); | | 1582 | exit(code); |
1586 | } | | 1583 | } |
1587 | | | 1584 | |
1588 | /* this escape HTML tags */ | | 1585 | /* this escape HTML tags */ |
1589 | static void | | 1586 | static void |
1590 | escape_html(bozo_httpreq_t *request) | | 1587 | escape_html(bozo_httpreq_t *request) |
1591 | { | | 1588 | { |
1592 | int i, j; | | 1589 | int i, j; |
1593 | char *url = request->hr_file, *tmp; | | 1590 | char *url = request->hr_file, *tmp; |
1594 | | | 1591 | |
1595 | for (i = 0, j = 0; url[i]; i++) { | | 1592 | for (i = 0, j = 0; url[i]; i++) { |
1596 | switch (url[i]) { | | 1593 | switch (url[i]) { |
1597 | case '<': | | 1594 | case '<': |
1598 | case '>': | | 1595 | case '>': |
1599 | j += 4; | | 1596 | j += 4; |
1600 | break; | | 1597 | break; |
1601 | case '&': | | 1598 | case '&': |
1602 | j += 5; | | 1599 | j += 5; |
1603 | break; | | 1600 | break; |
1604 | } | | 1601 | } |
1605 | } | | 1602 | } |
1606 | | | 1603 | |
1607 | if (j == 0) | | 1604 | if (j == 0) |
1608 | return; | | 1605 | return; |
1609 | | | 1606 | |
1610 | if ((tmp = (char *) malloc(strlen(url) + j)) == 0) | | 1607 | if ((tmp = (char *) malloc(strlen(url) + j)) == 0) |
1611 | /* | | 1608 | /* |
1612 | * ouch, but we are only called from an error context, and | | 1609 | * ouch, but we are only called from an error context, and |
1613 | * most paths here come from malloc(3) failures anyway... | | 1610 | * most paths here come from malloc(3) failures anyway... |
1614 | * we could completely punt and just exit, but isn't returning | | 1611 | * we could completely punt and just exit, but isn't returning |
1615 | * an not-quite-correct error better than nothing at all? | | 1612 | * an not-quite-correct error better than nothing at all? |
1616 | */ | | 1613 | */ |
1617 | return; | | 1614 | return; |
1618 | | | 1615 | |
1619 | for (i = 0, j = 0; url[i]; i++) { | | 1616 | for (i = 0, j = 0; url[i]; i++) { |
1620 | switch (url[i]) { | | 1617 | switch (url[i]) { |
1621 | case '<': | | 1618 | case '<': |
1622 | memcpy(tmp + j, "<", 4); | | 1619 | memcpy(tmp + j, "<", 4); |
1623 | j += 4; | | 1620 | j += 4; |
1624 | break; | | 1621 | break; |
1625 | case '>': | | 1622 | case '>': |
1626 | memcpy(tmp + j, ">", 4); | | 1623 | memcpy(tmp + j, ">", 4); |
1627 | j += 4; | | 1624 | j += 4; |
1628 | break; | | 1625 | break; |
1629 | case '&': | | 1626 | case '&': |
1630 | memcpy(tmp + j, "&", 5); | | 1627 | memcpy(tmp + j, "&", 5); |
1631 | j += 5; | | 1628 | j += 5; |
1632 | break; | | 1629 | break; |
1633 | default: | | 1630 | default: |
1634 | tmp[j++] = url[i]; | | 1631 | tmp[j++] = url[i]; |
1635 | } | | 1632 | } |
1636 | } | | 1633 | } |
1637 | tmp[j] = 0; | | 1634 | tmp[j] = 0; |
1638 | | | 1635 | |
1639 | free(request->hr_file); | | 1636 | free(request->hr_file); |
1640 | request->hr_file = tmp; | | 1637 | request->hr_file = tmp; |
1641 | } | | 1638 | } |
1642 | | | 1639 | |
1643 | /* short map between error code, and short/long messages */ | | 1640 | /* short map between error code, and short/long messages */ |
1644 | static struct errors_map { | | 1641 | static struct errors_map { |
1645 | int code; /* HTTP return code */ | | 1642 | int code; /* HTTP return code */ |
1646 | const char *shortmsg; /* short version of message */ | | 1643 | const char *shortmsg; /* short version of message */ |
1647 | const char *longmsg; /* long version of message */ | | 1644 | const char *longmsg; /* long version of message */ |
1648 | } errors_map[] = { | | 1645 | } errors_map[] = { |
1649 | { 400, "400 Bad Request", "The request was not valid", }, | | 1646 | { 400, "400 Bad Request", "The request was not valid", }, |
1650 | { 401, "401 Unauthorized", "No authorization", }, | | 1647 | { 401, "401 Unauthorized", "No authorization", }, |
1651 | { 403, "403 Forbidden", "Access to this item has been denied",}, | | 1648 | { 403, "403 Forbidden", "Access to this item has been denied",}, |
1652 | { 404, "404 Not Found", "This item has not been found", }, | | 1649 | { 404, "404 Not Found", "This item has not been found", }, |
1653 | { 408, "408 Request Timeout", "This request took too long", }, | | 1650 | { 408, "408 Request Timeout", "This request took too long", }, |
1654 | { 417, "417 Expectation Failed","Expectations not available", }, | | 1651 | { 417, "417 Expectation Failed","Expectations not available", }, |
1655 | { 500, "500 Internal Error", "An error occured on the server", }, | | 1652 | { 500, "500 Internal Error", "An error occured on the server", }, |
1656 | { 501, "501 Not Implemented", "This request is not available", }, | | 1653 | { 501, "501 Not Implemented", "This request is not available", }, |
1657 | { 0, NULL, NULL, }, | | 1654 | { 0, NULL, NULL, }, |
1658 | }; | | 1655 | }; |
1659 | | | 1656 | |
1660 | static const char *help = "DANGER! WILL ROBINSON! DANGER!"; | | 1657 | static const char *help = "DANGER! WILL ROBINSON! DANGER!"; |
1661 | | | 1658 | |
1662 | static const char * | | 1659 | static const char * |
1663 | http_errors_short(int code) | | 1660 | http_errors_short(int code) |
1664 | { | | 1661 | { |
1665 | struct errors_map *ep; | | 1662 | struct errors_map *ep; |
1666 | | | 1663 | |
1667 | for (ep = errors_map; ep->code; ep++) | | 1664 | for (ep = errors_map; ep->code; ep++) |
1668 | if (ep->code == code) | | 1665 | if (ep->code == code) |
1669 | return (ep->shortmsg); | | 1666 | return (ep->shortmsg); |
1670 | return (help); | | 1667 | return (help); |
1671 | } | | 1668 | } |
1672 | | | 1669 | |
1673 | static const char * | | 1670 | static const char * |
1674 | http_errors_long(int code) | | 1671 | http_errors_long(int code) |
1675 | { | | 1672 | { |
1676 | struct errors_map *ep; | | 1673 | struct errors_map *ep; |
1677 | | | 1674 | |
1678 | for (ep = errors_map; ep->code; ep++) | | 1675 | for (ep = errors_map; ep->code; ep++) |
1679 | if (ep->code == code) | | 1676 | if (ep->code == code) |
1680 | return (ep->longmsg); | | 1677 | return (ep->longmsg); |
1681 | return (help); | | 1678 | return (help); |
1682 | } | | 1679 | } |
1683 | | | 1680 | |
1684 | /* the follow functions and variables are used in handling HTTP errors */ | | 1681 | /* the follow functions and variables are used in handling HTTP errors */ |
1685 | /* ARGSUSED */ | | 1682 | /* ARGSUSED */ |
1686 | int | | 1683 | int |
1687 | bozo_http_error(bozohttpd_t *httpd, int code, bozo_httpreq_t *request, | | 1684 | bozo_http_error(bozohttpd_t *httpd, int code, bozo_httpreq_t *request, |
1688 | const char *msg) | | 1685 | const char *msg) |
1689 | { | | 1686 | { |
1690 | char portbuf[20]; | | 1687 | char portbuf[20]; |
1691 | const char *header = http_errors_short(code); | | 1688 | const char *header = http_errors_short(code); |
1692 | const char *reason = http_errors_long(code); | | 1689 | const char *reason = http_errors_long(code); |
1693 | const char *proto = (request && request->hr_proto) ? | | 1690 | const char *proto = (request && request->hr_proto) ? |
1694 | request->hr_proto : httpd->consts.http_11; | | 1691 | request->hr_proto : httpd->consts.http_11; |
1695 | int size; | | 1692 | int size; |
1696 | | | 1693 | |
1697 | debug((httpd, DEBUG_FAT, "bozo_http_error %d: %s", code, msg)); | | 1694 | debug((httpd, DEBUG_FAT, "bozo_http_error %d: %s", code, msg)); |
1698 | if (header == NULL || reason == NULL) { | | 1695 | if (header == NULL || reason == NULL) { |
1699 | bozo_err(httpd, 1, | | 1696 | bozo_err(httpd, 1, |
1700 | "bozo_http_error() failed (short = %p, long = %p)", | | 1697 | "bozo_http_error() failed (short = %p, long = %p)", |
1701 | header, reason); | | 1698 | header, reason); |
1702 | return code; | | 1699 | return code; |
1703 | } | | 1700 | } |
1704 | | | 1701 | |
1705 | if (request && request->hr_serverport && | | 1702 | if (request && request->hr_serverport && |
1706 | strcmp(request->hr_serverport, "80") != 0) | | 1703 | strcmp(request->hr_serverport, "80") != 0) |
1707 | snprintf(portbuf, sizeof(portbuf), ":%s", | | 1704 | snprintf(portbuf, sizeof(portbuf), ":%s", |
1708 | request->hr_serverport); | | 1705 | request->hr_serverport); |
1709 | else | | 1706 | else |
1710 | portbuf[0] = '\0'; | | 1707 | portbuf[0] = '\0'; |
1711 | | | 1708 | |
1712 | if (request && request->hr_file) { | | 1709 | if (request && request->hr_file) { |
1713 | escape_html(request); | | 1710 | escape_html(request); |
1714 | size = snprintf(httpd->errorbuf, BUFSIZ, | | 1711 | size = snprintf(httpd->errorbuf, BUFSIZ, |
1715 | "<html><head><title>%s</title></head>\n" | | 1712 | "<html><head><title>%s</title></head>\n" |
1716 | "<body><h1>%s</h1>\n" | | 1713 | "<body><h1>%s</h1>\n" |
1717 | "%s: <pre>%s</pre>\n" | | 1714 | "%s: <pre>%s</pre>\n" |
1718 | "<hr><address><a href=\"http://%s%s/\">%s%s</a></address>\n" | | 1715 | "<hr><address><a href=\"http://%s%s/\">%s%s</a></address>\n" |
1719 | "</body></html>\n", | | 1716 | "</body></html>\n", |
1720 | header, header, request->hr_file, reason, | | 1717 | header, header, request->hr_file, reason, |
1721 | httpd->virthostname, portbuf, httpd->virthostname, portbuf); | | 1718 | httpd->virthostname, portbuf, httpd->virthostname, portbuf); |
1722 | if (size >= (int)BUFSIZ) { | | 1719 | if (size >= (int)BUFSIZ) { |
1723 | bozo_warn(httpd, | | 1720 | bozo_warn(httpd, |
1724 | "bozo_http_error buffer too small, truncated"); | | 1721 | "bozo_http_error buffer too small, truncated"); |
1725 | size = (int)BUFSIZ; | | 1722 | size = (int)BUFSIZ; |
1726 | } | | 1723 | } |
1727 | } else | | 1724 | } else |
1728 | size = 0; | | 1725 | size = 0; |
1729 | | | 1726 | |
1730 | bozo_printf(httpd, "%s %s\r\n", proto, header); | | 1727 | bozo_printf(httpd, "%s %s\r\n", proto, header); |
1731 | if (request) | | 1728 | if (request) |
1732 | bozo_auth_check_401(request, code); | | 1729 | bozo_auth_check_401(request, code); |
1733 | | | 1730 | |
1734 | bozo_printf(httpd, "Content-Type: text/html\r\n"); | | 1731 | bozo_printf(httpd, "Content-Type: text/html\r\n"); |
1735 | bozo_printf(httpd, "Content-Length: %d\r\n", size); | | 1732 | bozo_printf(httpd, "Content-Length: %d\r\n", size); |
1736 | bozo_printf(httpd, "Server: %s\r\n", httpd->server_software); | | 1733 | bozo_printf(httpd, "Server: %s\r\n", httpd->server_software); |
1737 | if (request && request->hr_allow) | | 1734 | if (request && request->hr_allow) |
1738 | bozo_printf(httpd, "Allow: %s\r\n", request->hr_allow); | | 1735 | bozo_printf(httpd, "Allow: %s\r\n", request->hr_allow); |
1739 | bozo_printf(httpd, "\r\n"); | | 1736 | bozo_printf(httpd, "\r\n"); |
1740 | if (size) | | 1737 | if (size) |
1741 | bozo_printf(httpd, "%s", httpd->errorbuf); | | 1738 | bozo_printf(httpd, "%s", httpd->errorbuf); |
1742 | bozo_flush(httpd, stdout); | | 1739 | bozo_flush(httpd, stdout); |
1743 | | | 1740 | |
1744 | return code; | | 1741 | return code; |
1745 | } | | 1742 | } |
1746 | | | 1743 | |
1747 | /* Below are various modified libc functions */ | | 1744 | /* Below are various modified libc functions */ |
1748 | | | 1745 | |
1749 | /* | | 1746 | /* |
1750 | * returns -1 in lenp if the string ran out before finding a delimiter, | | 1747 | * returns -1 in lenp if the string ran out before finding a delimiter, |
1751 | * but is otherwise the same as strsep. Note that the length must be | | 1748 | * but is otherwise the same as strsep. Note that the length must be |
1752 | * correctly passed in. | | 1749 | * correctly passed in. |
1753 | */ | | 1750 | */ |
1754 | char * | | 1751 | char * |
1755 | bozostrnsep(char **strp, const char *delim, ssize_t *lenp) | | 1752 | bozostrnsep(char **strp, const char *delim, ssize_t *lenp) |
1756 | { | | 1753 | { |
1757 | char *s; | | 1754 | char *s; |
1758 | const char *spanp; | | 1755 | const char *spanp; |
1759 | int c, sc; | | 1756 | int c, sc; |
1760 | char *tok; | | 1757 | char *tok; |
1761 | | | 1758 | |
1762 | if ((s = *strp) == NULL) | | 1759 | if ((s = *strp) == NULL) |
1763 | return (NULL); | | 1760 | return (NULL); |
1764 | for (tok = s;;) { | | 1761 | for (tok = s;;) { |
1765 | if (lenp && --(*lenp) == -1) | | 1762 | if (lenp && --(*lenp) == -1) |
1766 | return (NULL); | | 1763 | return (NULL); |
1767 | c = *s++; | | 1764 | c = *s++; |
1768 | spanp = delim; | | 1765 | spanp = delim; |
1769 | do { | | 1766 | do { |
1770 | if ((sc = *spanp++) == c) { | | 1767 | if ((sc = *spanp++) == c) { |
1771 | if (c == 0) | | 1768 | if (c == 0) |
1772 | s = NULL; | | 1769 | s = NULL; |
1773 | else | | 1770 | else |
1774 | s[-1] = '\0'; | | 1771 | s[-1] = '\0'; |
1775 | *strp = s; | | 1772 | *strp = s; |
1776 | return (tok); | | 1773 | return (tok); |
1777 | } | | 1774 | } |
1778 | } while (sc != 0); | | 1775 | } while (sc != 0); |
1779 | } | | 1776 | } |
1780 | /* NOTREACHED */ | | 1777 | /* NOTREACHED */ |
1781 | } | | 1778 | } |
1782 | | | 1779 | |
1783 | /* | | 1780 | /* |
1784 | * inspired by fgetln(3), but works for fd's. should work identically | | 1781 | * inspired by fgetln(3), but works for fd's. should work identically |
1785 | * except it, however, does *not* return the newline, and it does nul | | 1782 | * except it, however, does *not* return the newline, and it does nul |
1786 | * terminate the string. | | 1783 | * terminate the string. |
1787 | */ | | 1784 | */ |
1788 | char * | | 1785 | char * |
1789 | bozodgetln(bozohttpd_t *httpd, int fd, ssize_t *lenp, | | 1786 | bozodgetln(bozohttpd_t *httpd, int fd, ssize_t *lenp, |
1790 | ssize_t (*readfn)(bozohttpd_t *, int, void *, size_t)) | | 1787 | ssize_t (*readfn)(bozohttpd_t *, int, void *, size_t)) |
1791 | { | | 1788 | { |
1792 | ssize_t len; | | 1789 | ssize_t len; |
1793 | int got_cr = 0; | | 1790 | int got_cr = 0; |
1794 | char c, *nbuffer; | | 1791 | char c, *nbuffer; |
1795 | | | 1792 | |
1796 | /* initialise */ | | 1793 | /* initialise */ |
1797 | if (httpd->getln_buflen == 0) { | | 1794 | if (httpd->getln_buflen == 0) { |
1798 | /* should be plenty for most requests */ | | 1795 | /* should be plenty for most requests */ |
1799 | httpd->getln_buflen = 128; | | 1796 | httpd->getln_buflen = 128; |
1800 | httpd->getln_buffer = malloc((size_t)httpd->getln_buflen); | | 1797 | httpd->getln_buffer = malloc((size_t)httpd->getln_buflen); |
1801 | if (httpd->getln_buffer == NULL) { | | 1798 | if (httpd->getln_buffer == NULL) { |
1802 | httpd->getln_buflen = 0; | | 1799 | httpd->getln_buflen = 0; |
1803 | return NULL; | | 1800 | return NULL; |
1804 | } | | 1801 | } |
1805 | } | | 1802 | } |
1806 | len = 0; | | 1803 | len = 0; |
1807 | | | 1804 | |
1808 | /* | | 1805 | /* |
1809 | * we *have* to read one byte at a time, to not break cgi | | 1806 | * we *have* to read one byte at a time, to not break cgi |
1810 | * programs (for we pass stdin off to them). could fix this | | 1807 | * programs (for we pass stdin off to them). could fix this |
1811 | * by becoming a fd-passing program instead of just exec'ing | | 1808 | * by becoming a fd-passing program instead of just exec'ing |
1812 | * the program | | 1809 | * the program |
1813 | * | | 1810 | * |
1814 | * the above is no longer true, we are the fd-passing | | 1811 | * the above is no longer true, we are the fd-passing |
1815 | * program already. | | 1812 | * program already. |
1816 | */ | | 1813 | */ |
1817 | for (; readfn(httpd, fd, &c, 1) == 1; ) { | | 1814 | for (; readfn(httpd, fd, &c, 1) == 1; ) { |
1818 | debug((httpd, DEBUG_EXPLODING, "bozodgetln read %c", c)); | | 1815 | debug((httpd, DEBUG_EXPLODING, "bozodgetln read %c", c)); |
1819 | | | 1816 | |
1820 | if (len >= httpd->getln_buflen - 1) { | | 1817 | if (len >= httpd->getln_buflen - 1) { |
1821 | httpd->getln_buflen *= 2; | | 1818 | httpd->getln_buflen *= 2; |
1822 | debug((httpd, DEBUG_EXPLODING, "bozodgetln: " | | 1819 | debug((httpd, DEBUG_EXPLODING, "bozodgetln: " |
1823 | "reallocating buffer to buflen %zu", | | 1820 | "reallocating buffer to buflen %zu", |
1824 | httpd->getln_buflen)); | | 1821 | httpd->getln_buflen)); |
1825 | nbuffer = bozorealloc(httpd, httpd->getln_buffer, | | 1822 | nbuffer = bozorealloc(httpd, httpd->getln_buffer, |
1826 | (size_t)httpd->getln_buflen); | | 1823 | (size_t)httpd->getln_buflen); |
1827 | httpd->getln_buffer = nbuffer; | | 1824 | httpd->getln_buffer = nbuffer; |
1828 | } | | 1825 | } |
1829 | | | 1826 | |
1830 | httpd->getln_buffer[len++] = c; | | 1827 | httpd->getln_buffer[len++] = c; |
1831 | if (c == '\r') { | | 1828 | if (c == '\r') { |
1832 | got_cr = 1; | | 1829 | got_cr = 1; |
1833 | continue; | | 1830 | continue; |
1834 | } else if (c == '\n') { | | 1831 | } else if (c == '\n') { |
1835 | /* | | 1832 | /* |
1836 | * HTTP/1.1 spec says to ignore CR and treat | | 1833 | * HTTP/1.1 spec says to ignore CR and treat |
1837 | * LF as the real line terminator. even though | | 1834 | * LF as the real line terminator. even though |
1838 | * the same spec defines CRLF as the line | | 1835 | * the same spec defines CRLF as the line |
1839 | * terminator, it is recommended in section 19.3 | | 1836 | * terminator, it is recommended in section 19.3 |
1840 | * to do the LF trick for tolerance. | | 1837 | * to do the LF trick for tolerance. |
1841 | */ | | 1838 | */ |
1842 | if (got_cr) | | 1839 | if (got_cr) |
1843 | len -= 2; | | 1840 | len -= 2; |
1844 | else | | 1841 | else |
1845 | len -= 1; | | 1842 | len -= 1; |
1846 | break; | | 1843 | break; |
1847 | } | | 1844 | } |
1848 | | | 1845 | |
1849 | } | | 1846 | } |
1850 | httpd->getln_buffer[len] = '\0'; | | 1847 | httpd->getln_buffer[len] = '\0'; |
1851 | debug((httpd, DEBUG_OBESE, "bozodgetln returns: ``%s'' with len %d", | | 1848 | debug((httpd, DEBUG_OBESE, "bozodgetln returns: ``%s'' with len %zd", |
1852 | httpd->getln_buffer, len)); | | 1849 | httpd->getln_buffer, len)); |
1853 | *lenp = len; | | 1850 | *lenp = len; |
1854 | return httpd->getln_buffer; | | 1851 | return httpd->getln_buffer; |
1855 | } | | 1852 | } |
1856 | | | 1853 | |
1857 | void * | | 1854 | void * |
1858 | bozorealloc(bozohttpd_t *httpd, void *ptr, size_t size) | | 1855 | bozorealloc(bozohttpd_t *httpd, void *ptr, size_t size) |
1859 | { | | 1856 | { |
1860 | void *p; | | 1857 | void *p; |
1861 | | | 1858 | |
1862 | p = realloc(ptr, size); | | 1859 | p = realloc(ptr, size); |
1863 | if (p == NULL) { | | 1860 | if (p == NULL) { |
1864 | (void)bozo_http_error(httpd, 500, NULL, | | 1861 | (void)bozo_http_error(httpd, 500, NULL, |
1865 | "memory allocation failure"); | | 1862 | "memory allocation failure"); |
1866 | exit(1); | | 1863 | exit(1); |
1867 | } | | 1864 | } |
1868 | return (p); | | 1865 | return (p); |
1869 | } | | 1866 | } |
1870 | | | 1867 | |
1871 | void * | | 1868 | void * |
1872 | bozomalloc(bozohttpd_t *httpd, size_t size) | | 1869 | bozomalloc(bozohttpd_t *httpd, size_t size) |
1873 | { | | 1870 | { |
1874 | void *p; | | 1871 | void *p; |
1875 | | | 1872 | |
1876 | p = malloc(size); | | 1873 | p = malloc(size); |
1877 | if (p == NULL) { | | 1874 | if (p == NULL) { |
1878 | (void)bozo_http_error(httpd, 500, NULL, | | 1875 | (void)bozo_http_error(httpd, 500, NULL, |
1879 | "memory allocation failure"); | | 1876 | "memory allocation failure"); |
1880 | exit(1); | | 1877 | exit(1); |
1881 | } | | 1878 | } |
1882 | return (p); | | 1879 | return (p); |
1883 | } | | 1880 | } |
1884 | | | 1881 | |
1885 | char * | | 1882 | char * |
1886 | bozostrdup(bozohttpd_t *httpd, const char *str) | | 1883 | bozostrdup(bozohttpd_t *httpd, const char *str) |
1887 | { | | 1884 | { |
1888 | char *p; | | 1885 | char *p; |
1889 | | | 1886 | |
1890 | p = strdup(str); | | 1887 | p = strdup(str); |
1891 | if (p == NULL) { | | 1888 | if (p == NULL) { |
1892 | (void)bozo_http_error(httpd, 500, NULL, | | 1889 | (void)bozo_http_error(httpd, 500, NULL, |
1893 | "memory allocation failure"); | | 1890 | "memory allocation failure"); |
1894 | exit(1); | | 1891 | exit(1); |
1895 | } | | 1892 | } |
1896 | return (p); | | 1893 | return (p); |
1897 | } | | 1894 | } |
1898 | | | 1895 | |
1899 | /* set default values in bozohttpd_t struct */ | | 1896 | /* set default values in bozohttpd_t struct */ |
1900 | int | | 1897 | int |
1901 | bozo_init_httpd(bozohttpd_t *httpd) | | 1898 | bozo_init_httpd(bozohttpd_t *httpd) |
1902 | { | | 1899 | { |
1903 | /* make sure everything is clean */ | | 1900 | /* make sure everything is clean */ |
1904 | (void) memset(httpd, 0x0, sizeof(*httpd)); | | 1901 | (void) memset(httpd, 0x0, sizeof(*httpd)); |
1905 | | | 1902 | |
1906 | /* constants */ | | 1903 | /* constants */ |
1907 | httpd->consts.http_09 = "HTTP/0.9"; | | 1904 | httpd->consts.http_09 = "HTTP/0.9"; |
1908 | httpd->consts.http_10 = "HTTP/1.0"; | | 1905 | httpd->consts.http_10 = "HTTP/1.0"; |
1909 | httpd->consts.http_11 = "HTTP/1.1"; | | 1906 | httpd->consts.http_11 = "HTTP/1.1"; |
1910 | httpd->consts.text_plain = "text/plain"; | | 1907 | httpd->consts.text_plain = "text/plain"; |
1911 | | | 1908 | |
1912 | /* mmap region size */ | | 1909 | /* mmap region size */ |
1913 | httpd->mmapsz = BOZO_MMAPSZ; | | 1910 | httpd->mmapsz = BOZO_MMAPSZ; |
1914 | | | 1911 | |
1915 | /* error buffer for bozo_http_error() */ | | 1912 | /* error buffer for bozo_http_error() */ |
1916 | if ((httpd->errorbuf = malloc(BUFSIZ)) == NULL) { | | 1913 | if ((httpd->errorbuf = malloc(BUFSIZ)) == NULL) { |
1917 | (void) fprintf(stderr, | | 1914 | (void) fprintf(stderr, |
1918 | "bozohttpd: memory_allocation failure\n"); | | 1915 | "bozohttpd: memory_allocation failure\n"); |
1919 | return 0; | | 1916 | return 0; |
1920 | } | | 1917 | } |
1921 | return 1; | | 1918 | return 1; |
1922 | } | | 1919 | } |
1923 | | | 1920 | |
1924 | /* set default values in bozoprefs_t struct */ | | 1921 | /* set default values in bozoprefs_t struct */ |
1925 | int | | 1922 | int |
1926 | bozo_init_prefs(bozoprefs_t *prefs) | | 1923 | bozo_init_prefs(bozoprefs_t *prefs) |
1927 | { | | 1924 | { |
1928 | /* make sure everything is clean */ | | 1925 | /* make sure everything is clean */ |
1929 | (void) memset(prefs, 0x0, sizeof(*prefs)); | | 1926 | (void) memset(prefs, 0x0, sizeof(*prefs)); |
1930 | | | 1927 | |
1931 | /* set up default values */ | | 1928 | /* set up default values */ |
1932 | bozo_set_pref(prefs, "server software", SERVER_SOFTWARE); | | 1929 | bozo_set_pref(prefs, "server software", SERVER_SOFTWARE); |
1933 | bozo_set_pref(prefs, "index.html", INDEX_HTML); | | 1930 | bozo_set_pref(prefs, "index.html", INDEX_HTML); |
1934 | bozo_set_pref(prefs, "public_html", PUBLIC_HTML); | | 1931 | bozo_set_pref(prefs, "public_html", PUBLIC_HTML); |
1935 | | | 1932 | |
1936 | return 1; | | 1933 | return 1; |
1937 | } | | 1934 | } |
1938 | | | 1935 | |
1939 | /* set default values */ | | 1936 | /* set default values */ |
1940 | int | | 1937 | int |
1941 | bozo_set_defaults(bozohttpd_t *httpd, bozoprefs_t *prefs) | | 1938 | bozo_set_defaults(bozohttpd_t *httpd, bozoprefs_t *prefs) |
1942 | { | | 1939 | { |
1943 | return bozo_init_httpd(httpd) && bozo_init_prefs(prefs); | | 1940 | return bozo_init_httpd(httpd) && bozo_init_prefs(prefs); |
1944 | } | | 1941 | } |
1945 | | | 1942 | |
1946 | /* set the virtual host name, port and root */ | | 1943 | /* set the virtual host name, port and root */ |
1947 | int | | 1944 | int |
1948 | bozo_setup(bozohttpd_t *httpd, bozoprefs_t *prefs, const char *vhost, | | 1945 | bozo_setup(bozohttpd_t *httpd, bozoprefs_t *prefs, const char *vhost, |
1949 | const char *root) | | 1946 | const char *root) |
1950 | { | | 1947 | { |
1951 | struct passwd *pw; | | 1948 | struct passwd *pw; |
1952 | extern char **environ; | | 1949 | extern char **environ; |
1953 | static char *cleanenv[1] = { NULL }; | | 1950 | static char *cleanenv[1] = { NULL }; |
1954 | uid_t uid; | | 1951 | uid_t uid; |
1955 | char *chrootdir; | | 1952 | char *chrootdir; |
1956 | char *username; | | 1953 | char *username; |
1957 | char *portnum; | | 1954 | char *portnum; |
1958 | char *cp; | | 1955 | char *cp; |
1959 | int dirtyenv; | | 1956 | int dirtyenv; |
1960 | | | 1957 | |
1961 | dirtyenv = 0; | | 1958 | dirtyenv = 0; |
1962 | | | 1959 | |
1963 | if (vhost == NULL) { | | 1960 | if (vhost == NULL) { |
1964 | httpd->virthostname = bozomalloc(httpd, MAXHOSTNAMELEN+1); | | 1961 | httpd->virthostname = bozomalloc(httpd, MAXHOSTNAMELEN+1); |
1965 | /* XXX we do not check for FQDN here */ | | 1962 | /* XXX we do not check for FQDN here */ |
1966 | if (gethostname(httpd->virthostname, MAXHOSTNAMELEN+1) < 0) | | 1963 | if (gethostname(httpd->virthostname, MAXHOSTNAMELEN+1) < 0) |
1967 | bozo_err(httpd, 1, "gethostname"); | | 1964 | bozo_err(httpd, 1, "gethostname"); |
1968 | httpd->virthostname[MAXHOSTNAMELEN] = '\0'; | | 1965 | httpd->virthostname[MAXHOSTNAMELEN] = '\0'; |
1969 | } else { | | 1966 | } else { |
1970 | httpd->virthostname = strdup(vhost); | | 1967 | httpd->virthostname = strdup(vhost); |
1971 | } | | 1968 | } |
1972 | httpd->slashdir = strdup(root); | | 1969 | httpd->slashdir = strdup(root); |
1973 | if ((portnum = bozo_get_pref(prefs, "port number")) != NULL) { | | 1970 | if ((portnum = bozo_get_pref(prefs, "port number")) != NULL) { |
1974 | httpd->bindport = strdup(portnum); | | 1971 | httpd->bindport = strdup(portnum); |
1975 | } | | 1972 | } |
1976 | | | 1973 | |
1977 | /* go over preferences now */ | | 1974 | /* go over preferences now */ |
1978 | if ((cp = bozo_get_pref(prefs, "numeric")) != NULL && | | 1975 | if ((cp = bozo_get_pref(prefs, "numeric")) != NULL && |
1979 | strcmp(cp, "true") == 0) { | | 1976 | strcmp(cp, "true") == 0) { |
1980 | httpd->numeric = 1; | | 1977 | httpd->numeric = 1; |
1981 | } | | 1978 | } |
1982 | if ((cp = bozo_get_pref(prefs, "trusted referal")) != NULL && | | 1979 | if ((cp = bozo_get_pref(prefs, "trusted referal")) != NULL && |
1983 | strcmp(cp, "true") == 0) { | | 1980 | strcmp(cp, "true") == 0) { |
1984 | httpd->untrustedref = 1; | | 1981 | httpd->untrustedref = 1; |
1985 | } | | 1982 | } |
1986 | if ((cp = bozo_get_pref(prefs, "log to stderr")) != NULL && | | 1983 | if ((cp = bozo_get_pref(prefs, "log to stderr")) != NULL && |
1987 | strcmp(cp, "true") == 0) { | | 1984 | strcmp(cp, "true") == 0) { |
1988 | httpd->logstderr = 1; | | 1985 | httpd->logstderr = 1; |
1989 | } | | 1986 | } |
1990 | if ((cp = bozo_get_pref(prefs, "bind address")) != NULL) { | | 1987 | if ((cp = bozo_get_pref(prefs, "bind address")) != NULL) { |
1991 | httpd->bindaddress = strdup(cp); | | 1988 | httpd->bindaddress = strdup(cp); |
1992 | } | | 1989 | } |
1993 | if ((cp = bozo_get_pref(prefs, "background")) != NULL) { | | 1990 | if ((cp = bozo_get_pref(prefs, "background")) != NULL) { |
1994 | httpd->background = atoi(cp); | | 1991 | httpd->background = atoi(cp); |
1995 | } | | 1992 | } |
1996 | if ((cp = bozo_get_pref(prefs, "foreground")) != NULL && | | 1993 | if ((cp = bozo_get_pref(prefs, "foreground")) != NULL && |
1997 | strcmp(cp, "true") == 0) { | | 1994 | strcmp(cp, "true") == 0) { |
1998 | httpd->foreground = 1; | | 1995 | httpd->foreground = 1; |
1999 | } | | 1996 | } |
2000 | if ((cp = bozo_get_pref(prefs, "pid file")) != NULL) { | | 1997 | if ((cp = bozo_get_pref(prefs, "pid file")) != NULL) { |
2001 | httpd->pidfile = strdup(cp); | | 1998 | httpd->pidfile = strdup(cp); |
2002 | } | | 1999 | } |
2003 | if ((cp = bozo_get_pref(prefs, "unknown slash")) != NULL && | | 2000 | if ((cp = bozo_get_pref(prefs, "unknown slash")) != NULL && |
2004 | strcmp(cp, "true") == 0) { | | 2001 | strcmp(cp, "true") == 0) { |
2005 | httpd->unknown_slash = 1; | | 2002 | httpd->unknown_slash = 1; |
2006 | } | | 2003 | } |
2007 | if ((cp = bozo_get_pref(prefs, "virtual base")) != NULL) { | | 2004 | if ((cp = bozo_get_pref(prefs, "virtual base")) != NULL) { |
2008 | httpd->virtbase = strdup(cp); | | 2005 | httpd->virtbase = strdup(cp); |
2009 | } | | 2006 | } |
2010 | if ((cp = bozo_get_pref(prefs, "enable users")) != NULL && | | 2007 | if ((cp = bozo_get_pref(prefs, "enable users")) != NULL && |
2011 | strcmp(cp, "true") == 0) { | | 2008 | strcmp(cp, "true") == 0) { |
2012 | httpd->enable_users = 1; | | 2009 | httpd->enable_users = 1; |
2013 | } | | 2010 | } |
2014 | if ((cp = bozo_get_pref(prefs, "dirty environment")) != NULL && | | 2011 | if ((cp = bozo_get_pref(prefs, "dirty environment")) != NULL && |
2015 | strcmp(cp, "true") == 0) { | | 2012 | strcmp(cp, "true") == 0) { |
2016 | dirtyenv = 1; | | 2013 | dirtyenv = 1; |
2017 | } | | 2014 | } |
2018 | if ((cp = bozo_get_pref(prefs, "hide dots")) != NULL && | | 2015 | if ((cp = bozo_get_pref(prefs, "hide dots")) != NULL && |
2019 | strcmp(cp, "true") == 0) { | | 2016 | strcmp(cp, "true") == 0) { |
2020 | httpd->hide_dots = 1; | | 2017 | httpd->hide_dots = 1; |
2021 | } | | 2018 | } |
2022 | if ((cp = bozo_get_pref(prefs, "directory indexing")) != NULL && | | 2019 | if ((cp = bozo_get_pref(prefs, "directory indexing")) != NULL && |
2023 | strcmp(cp, "true") == 0) { | | 2020 | strcmp(cp, "true") == 0) { |
2024 | httpd->dir_indexing = 1; | | 2021 | httpd->dir_indexing = 1; |
2025 | } | | 2022 | } |
2026 | if ((cp = bozo_get_pref(prefs, "public_html")) != NULL) { | | 2023 | if ((cp = bozo_get_pref(prefs, "public_html")) != NULL) { |
2027 | httpd->public_html = strdup(cp); | | 2024 | httpd->public_html = strdup(cp); |
2028 | } | | 2025 | } |
2029 | httpd->server_software = | | 2026 | httpd->server_software = |
2030 | strdup(bozo_get_pref(prefs, "server software")); | | 2027 | strdup(bozo_get_pref(prefs, "server software")); |
2031 | httpd->index_html = strdup(bozo_get_pref(prefs, "index.html")); | | 2028 | httpd->index_html = strdup(bozo_get_pref(prefs, "index.html")); |
2032 | | | 2029 | |
2033 | /* | | 2030 | /* |
2034 | * initialise ssl and daemon mode if necessary. | | 2031 | * initialise ssl and daemon mode if necessary. |
2035 | */ | | 2032 | */ |
2036 | bozo_ssl_init(httpd); | | 2033 | bozo_ssl_init(httpd); |
2037 | bozo_daemon_init(httpd); | | 2034 | bozo_daemon_init(httpd); |
2038 | | | 2035 | |
2039 | if ((username = bozo_get_pref(prefs, "username")) == NULL) { | | 2036 | if ((username = bozo_get_pref(prefs, "username")) == NULL) { |
2040 | if ((pw = getpwuid(uid = 0)) == NULL) | | 2037 | if ((pw = getpwuid(uid = 0)) == NULL) |
2041 | bozo_err(httpd, 1, "getpwuid(0): %s", strerror(errno)); | | 2038 | bozo_err(httpd, 1, "getpwuid(0): %s", strerror(errno)); |
2042 | httpd->username = strdup(pw->pw_name); | | 2039 | httpd->username = strdup(pw->pw_name); |
2043 | } else { | | 2040 | } else { |
2044 | httpd->username = strdup(username); | | 2041 | httpd->username = strdup(username); |
2045 | if ((pw = getpwnam(httpd->username)) == NULL) | | 2042 | if ((pw = getpwnam(httpd->username)) == NULL) |
2046 | bozo_err(httpd, 1, "getpwnam(%s): %s", httpd->username, | | 2043 | bozo_err(httpd, 1, "getpwnam(%s): %s", httpd->username, |
2047 | strerror(errno)); | | 2044 | strerror(errno)); |
2048 | if (initgroups(pw->pw_name, pw->pw_gid) == -1) | | 2045 | if (initgroups(pw->pw_name, pw->pw_gid) == -1) |
2049 | bozo_err(httpd, 1, "initgroups: %s", strerror(errno)); | | 2046 | bozo_err(httpd, 1, "initgroups: %s", strerror(errno)); |
2050 | if (setgid(pw->pw_gid) == -1) | | 2047 | if (setgid(pw->pw_gid) == -1) |
2051 | bozo_err(httpd, 1, "setgid(%u): %s", pw->pw_gid, | | 2048 | bozo_err(httpd, 1, "setgid(%u): %s", pw->pw_gid, |
2052 | strerror(errno)); | | 2049 | strerror(errno)); |
2053 | uid = pw->pw_uid; | | 2050 | uid = pw->pw_uid; |
2054 | } | | 2051 | } |
2055 | /* | | 2052 | /* |
2056 | * handle chroot. | | 2053 | * handle chroot. |
2057 | */ | | 2054 | */ |
2058 | if ((chrootdir = bozo_get_pref(prefs, "chroot dir")) != NULL) { | | 2055 | if ((chrootdir = bozo_get_pref(prefs, "chroot dir")) != NULL) { |
2059 | httpd->rootdir = strdup(chrootdir); | | 2056 | httpd->rootdir = strdup(chrootdir); |
2060 | if (chdir(httpd->rootdir) == -1) | | 2057 | if (chdir(httpd->rootdir) == -1) |
2061 | bozo_err(httpd, 1, "chdir(%s): %s", httpd->rootdir, | | 2058 | bozo_err(httpd, 1, "chdir(%s): %s", httpd->rootdir, |
2062 | strerror(errno)); | | 2059 | strerror(errno)); |
2063 | if (chroot(httpd->rootdir) == -1) | | 2060 | if (chroot(httpd->rootdir) == -1) |
2064 | bozo_err(httpd, 1, "chroot(%s): %s", httpd->rootdir, | | 2061 | bozo_err(httpd, 1, "chroot(%s): %s", httpd->rootdir, |
2065 | strerror(errno)); | | 2062 | strerror(errno)); |
2066 | } | | 2063 | } |
2067 | | | 2064 | |
2068 | if (username != NULL) | | 2065 | if (username != NULL) |
2069 | if (setuid(uid) == -1) | | 2066 | if (setuid(uid) == -1) |
2070 | bozo_err(httpd, 1, "setuid(%d): %s", uid, | | 2067 | bozo_err(httpd, 1, "setuid(%d): %s", uid, |
2071 | strerror(errno)); | | 2068 | strerror(errno)); |
2072 | | | 2069 | |
2073 | /* | | 2070 | /* |
2074 | * prevent info leakage between different compartments. | | 2071 | * prevent info leakage between different compartments. |
2075 | * some PATH values in the environment would be invalided | | 2072 | * some PATH values in the environment would be invalided |
2076 | * by chroot. cross-user settings might result in undesirable | | 2073 | * by chroot. cross-user settings might result in undesirable |
2077 | * effects. | | 2074 | * effects. |
2078 | */ | | 2075 | */ |
2079 | if ((chrootdir != NULL || username != NULL) && !dirtyenv) | | 2076 | if ((chrootdir != NULL || username != NULL) && !dirtyenv) |
2080 | environ = cleanenv; | | 2077 | environ = cleanenv; |
2081 | | | 2078 | |
2082 | #ifdef _SC_PAGESIZE | | 2079 | #ifdef _SC_PAGESIZE |
2083 | httpd->page_size = (long)sysconf(_SC_PAGESIZE); | | 2080 | httpd->page_size = (long)sysconf(_SC_PAGESIZE); |
2084 | #else | | 2081 | #else |
2085 | httpd->page_size = 4096; | | 2082 | httpd->page_size = 4096; |
2086 | #endif | | 2083 | #endif |
2087 | debug((httpd, DEBUG_OBESE, "myname is %s, slashdir is %s", | | 2084 | debug((httpd, DEBUG_OBESE, "myname is %s, slashdir is %s", |
2088 | httpd->virthostname, httpd->slashdir)); | | 2085 | httpd->virthostname, httpd->slashdir)); |
2089 | | | 2086 | |
2090 | return 1; | | 2087 | return 1; |
2091 | } | | 2088 | } |