1 #include "base.h"
2 #include "array.h"
3 #include "buffer.h"
4 #include "log.h"
5 #include "etag.h"
6 #include "response.h"
7
8 #include <string.h>
9 #include <errno.h>
10
11 #include <time.h>
12
13 /*
14 * This was 'borrowed' from tcpdump.
15 *
16 *
17 * This is fun.
18 *
19 * In older BSD systems, socket addresses were fixed-length, and
20 * "sizeof (struct sockaddr)" gave the size of the structure.
21 * All addresses fit within a "struct sockaddr".
22 *
23 * In newer BSD systems, the socket address is variable-length, and
24 * there's an "sa_len" field giving the length of the structure;
25 * this allows socket addresses to be longer than 2 bytes of family
26 * and 14 bytes of data.
27 *
28 * Some commercial UNIXes use the old BSD scheme, some use the RFC 2553
29 * variant of the old BSD scheme (with "struct sockaddr_storage" rather
30 * than "struct sockaddr"), and some use the new BSD scheme.
31 *
32 * Some versions of GNU libc use neither scheme, but has an "SA_LEN()"
33 * macro that determines the size based on the address family. Other
34 * versions don't have "SA_LEN()" (as it was in drafts of RFC 2553
35 * but not in the final version). On the latter systems, we explicitly
36 * check the AF_ type to determine the length; we assume that on
37 * all those systems we have "struct sockaddr_storage".
38 */
39
40 #ifdef HAVE_IPV6
41 # ifndef SA_LEN
42 # ifdef HAVE_SOCKADDR_SA_LEN
43 # define SA_LEN(addr) ((addr)->sa_len)
44 # else /* HAVE_SOCKADDR_SA_LEN */
45 # ifdef HAVE_STRUCT_SOCKADDR_STORAGE
get_sa_len(const struct sockaddr * addr)46 static size_t get_sa_len(const struct sockaddr *addr) {
47 switch (addr->sa_family) {
48
49 # ifdef AF_INET
50 case AF_INET:
51 return (sizeof (struct sockaddr_in));
52 # endif
53
54 # ifdef AF_INET6
55 case AF_INET6:
56 return (sizeof (struct sockaddr_in6));
57 # endif
58
59 default:
60 return (sizeof (struct sockaddr));
61
62 }
63 }
64 # define SA_LEN(addr) (get_sa_len(addr))
65 # else /* HAVE_SOCKADDR_STORAGE */
66 # define SA_LEN(addr) (sizeof (struct sockaddr))
67 # endif /* HAVE_SOCKADDR_STORAGE */
68 # endif /* HAVE_SOCKADDR_SA_LEN */
69 # endif /* SA_LEN */
70 #endif
71
72
73
74
response_header_insert(server * srv,connection * con,const char * key,size_t keylen,const char * value,size_t vallen)75 int response_header_insert(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
76 data_string *ds;
77
78 UNUSED(srv);
79
80 if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
81 ds = data_response_init();
82 }
83 buffer_copy_string_len(ds->key, key, keylen);
84 buffer_copy_string_len(ds->value, value, vallen);
85
86 array_insert_unique(con->response.headers, (data_unset *)ds);
87
88 return 0;
89 }
90
response_header_overwrite(server * srv,connection * con,const char * key,size_t keylen,const char * value,size_t vallen)91 int response_header_overwrite(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
92 data_string *ds;
93
94 UNUSED(srv);
95
96 /* if there already is a key by this name overwrite the value */
97 if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) {
98 buffer_copy_string(ds->value, value);
99
100 return 0;
101 }
102
103 return response_header_insert(srv, con, key, keylen, value, vallen);
104 }
105
response_header_append(server * srv,connection * con,const char * key,size_t keylen,const char * value,size_t vallen)106 int response_header_append(server *srv, connection *con, const char *key, size_t keylen, const char *value, size_t vallen) {
107 data_string *ds;
108
109 UNUSED(srv);
110
111 /* if there already is a key by this name append the value */
112 if (NULL != (ds = (data_string *)array_get_element(con->response.headers, key))) {
113 buffer_append_string_len(ds->value, CONST_STR_LEN(", "));
114 buffer_append_string_len(ds->value, value, vallen);
115 return 0;
116 }
117
118 return response_header_insert(srv, con, key, keylen, value, vallen);
119 }
120
http_response_redirect_to_directory(server * srv,connection * con)121 int http_response_redirect_to_directory(server *srv, connection *con) {
122 buffer *o;
123
124 o = buffer_init();
125
126 if (con->conf.is_ssl) {
127 buffer_copy_string_len(o, CONST_STR_LEN("https://"));
128 } else {
129 buffer_copy_string_len(o, CONST_STR_LEN("http://"));
130 }
131 if (con->uri.authority->used) {
132 buffer_append_string_buffer(o, con->uri.authority);
133 } else {
134 /* get the name of the currently connected socket */
135 struct hostent *he;
136 #ifdef HAVE_IPV6
137 char hbuf[256];
138 #endif
139 sock_addr our_addr;
140 socklen_t our_addr_len;
141
142 our_addr_len = sizeof(our_addr);
143
144 if (-1 == getsockname(con->fd, &(our_addr.plain), &our_addr_len)) {
145 con->http_status = 500;
146
147 log_error_write(srv, __FILE__, __LINE__, "ss",
148 "can't get sockname", strerror(errno));
149
150 buffer_free(o);
151 return 0;
152 }
153
154
155 /* Lookup name: secondly try to get hostname for bind address */
156 switch(our_addr.plain.sa_family) {
157 #ifdef HAVE_IPV6
158 case AF_INET6:
159 if (0 != getnameinfo((const struct sockaddr *)(&our_addr.ipv6),
160 SA_LEN((const struct sockaddr *)&our_addr.ipv6),
161 hbuf, sizeof(hbuf), NULL, 0, 0)) {
162
163 char dst[INET6_ADDRSTRLEN];
164
165 log_error_write(srv, __FILE__, __LINE__,
166 "SSS", "NOTICE: getnameinfo failed: ",
167 strerror(errno), ", using ip-address instead");
168
169 buffer_append_string(o,
170 inet_ntop(AF_INET6, (char *)&our_addr.ipv6.sin6_addr,
171 dst, sizeof(dst)));
172 } else {
173 buffer_append_string(o, hbuf);
174 }
175 break;
176 #endif
177 case AF_INET:
178 if (NULL == (he = gethostbyaddr((char *)&our_addr.ipv4.sin_addr, sizeof(struct in_addr), AF_INET))) {
179 log_error_write(srv, __FILE__, __LINE__,
180 "SdS", "NOTICE: gethostbyaddr failed: ",
181 h_errno, ", using ip-address instead");
182
183 buffer_append_string(o, inet_ntoa(our_addr.ipv4.sin_addr));
184 } else {
185 buffer_append_string(o, he->h_name);
186 }
187 break;
188 default:
189 log_error_write(srv, __FILE__, __LINE__,
190 "S", "ERROR: unsupported address-type");
191
192 buffer_free(o);
193 return -1;
194 }
195
196 if (!((con->conf.is_ssl == 0 && srv->srvconf.port == 80) ||
197 (con->conf.is_ssl == 1 && srv->srvconf.port == 443))) {
198 buffer_append_string_len(o, CONST_STR_LEN(":"));
199 buffer_append_long(o, srv->srvconf.port);
200 }
201 }
202 buffer_append_string_buffer(o, con->uri.path);
203 buffer_append_string_len(o, CONST_STR_LEN("/"));
204 if (!buffer_is_empty(con->uri.query)) {
205 buffer_append_string_len(o, CONST_STR_LEN("?"));
206 buffer_append_string_buffer(o, con->uri.query);
207 }
208
209 response_header_insert(srv, con, CONST_STR_LEN("Location"), CONST_BUF_LEN(o));
210
211 con->http_status = 301;
212 con->file_finished = 1;
213
214 buffer_free(o);
215
216 return 0;
217 }
218
strftime_cache_get(server * srv,time_t last_mod)219 buffer * strftime_cache_get(server *srv, time_t last_mod) {
220 struct tm *tm;
221 size_t i;
222
223 for (i = 0; i < FILE_CACHE_MAX; i++) {
224 /* found cache-entry */
225 if (srv->mtime_cache[i].mtime == last_mod) return srv->mtime_cache[i].str;
226
227 /* found empty slot */
228 if (srv->mtime_cache[i].mtime == 0) break;
229 }
230
231 if (i == FILE_CACHE_MAX) {
232 i = 0;
233 }
234
235 srv->mtime_cache[i].mtime = last_mod;
236 buffer_prepare_copy(srv->mtime_cache[i].str, 1024);
237 tm = gmtime(&(srv->mtime_cache[i].mtime));
238 srv->mtime_cache[i].str->used = strftime(srv->mtime_cache[i].str->ptr,
239 srv->mtime_cache[i].str->size - 1,
240 "%a, %d %b %Y %H:%M:%S GMT", tm);
241 srv->mtime_cache[i].str->used++;
242
243 return srv->mtime_cache[i].str;
244 }
245
246
http_response_handle_cachable(server * srv,connection * con,buffer * mtime)247 int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) {
248 /*
249 * 14.26 If-None-Match
250 * [...]
251 * If none of the entity tags match, then the server MAY perform the
252 * requested method as if the If-None-Match header field did not exist,
253 * but MUST also ignore any If-Modified-Since header field(s) in the
254 * request. That is, if no entity tags match, then the server MUST NOT
255 * return a 304 (Not Modified) response.
256 */
257
258 /* last-modified handling */
259 if (con->request.http_if_none_match) {
260 if (etag_is_equal(con->physical.etag, con->request.http_if_none_match)) {
261 if (con->request.http_method == HTTP_METHOD_GET ||
262 con->request.http_method == HTTP_METHOD_HEAD) {
263
264 /* check if etag + last-modified */
265 if (con->request.http_if_modified_since) {
266 size_t used_len;
267 char *semicolon;
268
269 if (NULL == (semicolon = strchr(con->request.http_if_modified_since, ';'))) {
270 used_len = strlen(con->request.http_if_modified_since);
271 } else {
272 used_len = semicolon - con->request.http_if_modified_since;
273 }
274
275 if (0 == strncmp(con->request.http_if_modified_since, mtime->ptr, used_len)) {
276 if ('\0' == mtime->ptr[used_len]) con->http_status = 304;
277 return HANDLER_FINISHED;
278 } else {
279 char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
280 time_t t_header, t_file;
281 struct tm tm;
282
283 /* check if we can safely copy the string */
284 if (used_len >= sizeof(buf)) {
285 log_error_write(srv, __FILE__, __LINE__, "ssdd",
286 "DEBUG: Last-Modified check failed as the received timestamp was too long:",
287 con->request.http_if_modified_since, used_len, sizeof(buf) - 1);
288
289 con->http_status = 412;
290 con->mode = DIRECT;
291 return HANDLER_FINISHED;
292 }
293
294
295 strncpy(buf, con->request.http_if_modified_since, used_len);
296 buf[used_len] = '\0';
297
298 if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) {
299 con->http_status = 412;
300 con->mode = DIRECT;
301 return HANDLER_FINISHED;
302 }
303 tm.tm_isdst = 0;
304 t_header = mktime(&tm);
305
306 strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm);
307 tm.tm_isdst = 0;
308 t_file = mktime(&tm);
309
310 if (t_file > t_header) return HANDLER_GO_ON;
311
312 con->http_status = 304;
313 return HANDLER_FINISHED;
314 }
315 } else {
316 con->http_status = 304;
317 return HANDLER_FINISHED;
318 }
319 } else {
320 con->http_status = 412;
321 con->mode = DIRECT;
322 return HANDLER_FINISHED;
323 }
324 }
325 } else if (con->request.http_if_modified_since) {
326 size_t used_len;
327 char *semicolon;
328
329 if (NULL == (semicolon = strchr(con->request.http_if_modified_since, ';'))) {
330 used_len = strlen(con->request.http_if_modified_since);
331 } else {
332 used_len = semicolon - con->request.http_if_modified_since;
333 }
334
335 if (0 == strncmp(con->request.http_if_modified_since, mtime->ptr, used_len)) {
336 if ('\0' == mtime->ptr[used_len]) con->http_status = 304;
337 return HANDLER_FINISHED;
338 } else {
339 char buf[sizeof("Sat, 23 Jul 2005 21:20:01 GMT")];
340 time_t t_header, t_file;
341 struct tm tm;
342
343 /* convert to timestamp */
344 if (used_len >= sizeof(buf)) return HANDLER_GO_ON;
345
346 strncpy(buf, con->request.http_if_modified_since, used_len);
347 buf[used_len] = '\0';
348
349 if (NULL == strptime(buf, "%a, %d %b %Y %H:%M:%S GMT", &tm)) {
350 /**
351 * parsing failed, let's get out of here
352 */
353 return HANDLER_GO_ON;
354 }
355 tm.tm_isdst = 0;
356 t_header = mktime(&tm);
357
358 strptime(mtime->ptr, "%a, %d %b %Y %H:%M:%S GMT", &tm);
359 tm.tm_isdst = 0;
360 t_file = mktime(&tm);
361
362 if (t_file > t_header) return HANDLER_GO_ON;
363
364 con->http_status = 304;
365 return HANDLER_FINISHED;
366 }
367 }
368
369 return HANDLER_GO_ON;
370 }
371