Line data Source code
1 : /*
2 : * httpread - Manage reading file(s) from HTTP/TCP socket
3 : * Author: Ted Merrill
4 : * Copyright 2008 Atheros Communications
5 : *
6 : * This software may be distributed under the terms of the BSD license.
7 : * See README for more details.
8 : *
9 : * The files are buffered via internal callbacks from eloop, then presented to
10 : * an application callback routine when completely read into memory. May also
11 : * be used if no file is expected but just to get the header, including HTTP
12 : * replies (e.g. HTTP/1.1 200 OK etc.).
13 : *
14 : * This does not attempt to be an optimally efficient implementation, but does
15 : * attempt to be of reasonably small size and memory consumption; assuming that
16 : * only small files are to be read. A maximum file size is provided by
17 : * application and enforced.
18 : *
19 : * It is assumed that the application does not expect any of the following:
20 : * -- transfer encoding other than chunked
21 : * -- trailer fields
22 : * It is assumed that, even if the other side requested that the connection be
23 : * kept open, that we will close it (thus HTTP messages sent by application
24 : * should have the connection closed field); this is allowed by HTTP/1.1 and
25 : * simplifies things for us.
26 : *
27 : * Other limitations:
28 : * -- HTTP header may not exceed a hard-coded size.
29 : *
30 : * Notes:
31 : * This code would be massively simpler without some of the new features of
32 : * HTTP/1.1, especially chunked data.
33 : */
34 :
35 : #include "includes.h"
36 :
37 : #include "common.h"
38 : #include "eloop.h"
39 : #include "httpread.h"
40 :
41 :
42 : /* Tunable parameters */
43 : #define HTTPREAD_READBUF_SIZE 1024 /* read in chunks of this size */
44 : #define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */
45 : #define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */
46 :
47 : #if 0
48 : /* httpread_debug -- set this global variable > 0 e.g. from debugger
49 : * to enable debugs (larger numbers for more debugs)
50 : * Make this a #define of 0 to eliminate the debugging code.
51 : */
52 : int httpread_debug = 99;
53 : #else
54 : #define httpread_debug 0 /* eliminates even the debugging code */
55 : #endif
56 :
57 :
58 : /* control instance -- actual definition (opaque to application)
59 : */
60 : struct httpread {
61 : /* information from creation */
62 : int sd; /* descriptor of TCP socket to read from */
63 : void (*cb)(struct httpread *handle, void *cookie,
64 : enum httpread_event e); /* call on event */
65 : void *cookie; /* pass to callback */
66 : int max_bytes; /* maximum file size else abort it */
67 : int timeout_seconds; /* 0 or total duration timeout period */
68 :
69 : /* dynamically used information follows */
70 :
71 : int got_hdr; /* nonzero when header is finalized */
72 : char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */
73 : int hdr_nbytes;
74 :
75 : enum httpread_hdr_type hdr_type;
76 : int version; /* 1 if we've seen 1.1 */
77 : int reply_code; /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
78 : int got_content_length; /* true if we know content length for sure */
79 : int content_length; /* body length, iff got_content_length */
80 : int chunked; /* nonzero for chunked data */
81 : char *uri;
82 :
83 : int got_body; /* nonzero when body is finalized */
84 : char *body;
85 : int body_nbytes;
86 : int body_alloc_nbytes; /* amount allocated */
87 :
88 : int got_file; /* here when we are done */
89 :
90 : /* The following apply if data is chunked: */
91 : int in_chunk_data; /* 0=in/at header, 1=in the data or tail*/
92 : int chunk_start; /* offset in body of chunk hdr or data */
93 : int chunk_size; /* data of chunk (not hdr or ending CRLF)*/
94 : int in_trailer; /* in header fields after data (chunked only)*/
95 : enum trailer_state {
96 : trailer_line_begin = 0,
97 : trailer_empty_cr, /* empty line + CR */
98 : trailer_nonempty,
99 : trailer_nonempty_cr,
100 : } trailer_state;
101 : };
102 :
103 :
104 : /* Check words for equality, where words consist of graphical characters
105 : * delimited by whitespace
106 : * Returns nonzero if "equal" doing case insensitive comparison.
107 : */
108 5116 : static int word_eq(char *s1, char *s2)
109 : {
110 : int c1;
111 : int c2;
112 5116 : int end1 = 0;
113 5116 : int end2 = 0;
114 : for (;;) {
115 12533 : c1 = *s1++;
116 12533 : c2 = *s2++;
117 12533 : if (isalpha(c1) && isupper(c1))
118 7284 : c1 = tolower(c1);
119 12533 : if (isalpha(c2) && isupper(c2))
120 11297 : c2 = tolower(c2);
121 12533 : end1 = !isgraph(c1);
122 12533 : end2 = !isgraph(c2);
123 12533 : if (end1 || end2 || c1 != c2)
124 : break;
125 7417 : }
126 5116 : return end1 && end2; /* reached end of both words? */
127 : }
128 :
129 :
130 : static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
131 :
132 : /* httpread_destroy -- if h is non-NULL, clean up
133 : * This must eventually be called by the application following
134 : * call of the application's callback and may be called
135 : * earlier if desired.
136 : */
137 330 : void httpread_destroy(struct httpread *h)
138 : {
139 : if (httpread_debug >= 10)
140 : wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
141 330 : if (!h)
142 340 : return;
143 :
144 320 : eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
145 320 : eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
146 320 : os_free(h->body);
147 320 : os_free(h->uri);
148 320 : os_memset(h, 0, sizeof(*h)); /* aid debugging */
149 320 : h->sd = -1; /* aid debugging */
150 320 : os_free(h);
151 : }
152 :
153 :
154 : /* httpread_timeout_handler -- called on excessive total duration
155 : */
156 0 : static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
157 : {
158 0 : struct httpread *h = user_ctx;
159 0 : wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
160 0 : (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
161 0 : }
162 :
163 :
164 : /* Analyze options only so far as is needed to correctly obtain the file.
165 : * The application can look at the raw header to find other options.
166 : */
167 1528 : static int httpread_hdr_option_analyze(
168 : struct httpread *h,
169 : char *hbp /* pointer to current line in header buffer */
170 : )
171 : {
172 1528 : if (word_eq(hbp, "CONTENT-LENGTH:")) {
173 4896 : while (isgraph(*hbp))
174 4320 : hbp++;
175 864 : while (*hbp == ' ' || *hbp == '\t')
176 288 : hbp++;
177 288 : if (!isdigit(*hbp))
178 0 : return -1;
179 288 : h->content_length = atol(hbp);
180 288 : h->got_content_length = 1;
181 288 : return 0;
182 : }
183 2480 : if (word_eq(hbp, "TRANSFER_ENCODING:") ||
184 1240 : word_eq(hbp, "TRANSFER-ENCODING:")) {
185 0 : while (isgraph(*hbp))
186 0 : hbp++;
187 0 : while (*hbp == ' ' || *hbp == '\t')
188 0 : hbp++;
189 : /* There should (?) be no encodings of interest
190 : * other than chunked...
191 : */
192 0 : if (word_eq(hbp, "CHUNKED")) {
193 0 : h->chunked = 1;
194 0 : h->in_chunk_data = 0;
195 : /* ignore possible ;<parameters> */
196 : }
197 0 : return 0;
198 : }
199 : /* skip anything we don't know, which is a lot */
200 1240 : return 0;
201 : }
202 :
203 :
204 319 : static int httpread_hdr_analyze(struct httpread *h)
205 : {
206 319 : char *hbp = h->hdr; /* pointer into h->hdr */
207 319 : int standard_first_line = 1;
208 :
209 : /* First line is special */
210 319 : h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
211 319 : if (!isgraph(*hbp))
212 0 : goto bad;
213 319 : if (os_strncmp(hbp, "HTTP/", 5) == 0) {
214 141 : h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
215 141 : standard_first_line = 0;
216 141 : hbp += 5;
217 282 : if (hbp[0] == '1' && hbp[1] == '.' &&
218 282 : isdigit(hbp[2]) && hbp[2] != '0')
219 141 : h->version = 1;
220 705 : while (isgraph(*hbp))
221 423 : hbp++;
222 423 : while (*hbp == ' ' || *hbp == '\t')
223 141 : hbp++;
224 141 : if (!isdigit(*hbp))
225 0 : goto bad;
226 141 : h->reply_code = atol(hbp);
227 178 : } else if (word_eq(hbp, "GET"))
228 13 : h->hdr_type = HTTPREAD_HDR_TYPE_GET;
229 165 : else if (word_eq(hbp, "HEAD"))
230 0 : h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
231 165 : else if (word_eq(hbp, "POST"))
232 72 : h->hdr_type = HTTPREAD_HDR_TYPE_POST;
233 93 : else if (word_eq(hbp, "PUT"))
234 0 : h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
235 93 : else if (word_eq(hbp, "DELETE"))
236 0 : h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
237 93 : else if (word_eq(hbp, "TRACE"))
238 0 : h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
239 93 : else if (word_eq(hbp, "CONNECT"))
240 0 : h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
241 93 : else if (word_eq(hbp, "NOTIFY"))
242 54 : h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
243 39 : else if (word_eq(hbp, "M-SEARCH"))
244 0 : h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
245 39 : else if (word_eq(hbp, "M-POST"))
246 0 : h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
247 39 : else if (word_eq(hbp, "SUBSCRIBE"))
248 21 : h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
249 18 : else if (word_eq(hbp, "UNSUBSCRIBE"))
250 17 : h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
251 : else {
252 : }
253 :
254 319 : if (standard_first_line) {
255 : char *rawuri;
256 : char *uri;
257 : /* skip type */
258 1390 : while (isgraph(*hbp))
259 1034 : hbp++;
260 534 : while (*hbp == ' ' || *hbp == '\t')
261 178 : hbp++;
262 : /* parse uri.
263 : * Find length, allocate memory for translated
264 : * copy, then translate by changing %<hex><hex>
265 : * into represented value.
266 : */
267 178 : rawuri = hbp;
268 2714 : while (isgraph(*hbp))
269 2358 : hbp++;
270 178 : h->uri = os_malloc((hbp - rawuri) + 1);
271 178 : if (h->uri == NULL)
272 0 : goto bad;
273 178 : uri = h->uri;
274 2714 : while (rawuri < hbp) {
275 2358 : int c = *rawuri;
276 2358 : if (c == '%' &&
277 0 : isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
278 0 : *uri++ = hex2byte(rawuri + 1);
279 0 : rawuri += 3;
280 : } else {
281 2358 : *uri++ = c;
282 2358 : rawuri++;
283 : }
284 : }
285 178 : *uri = 0; /* null terminate */
286 356 : while (isgraph(*hbp))
287 0 : hbp++;
288 534 : while (*hbp == ' ' || *hbp == '\t')
289 178 : hbp++;
290 : /* get version */
291 178 : if (0 == strncmp(hbp, "HTTP/", 5)) {
292 178 : hbp += 5;
293 356 : if (hbp[0] == '1' && hbp[1] == '.' &&
294 356 : isdigit(hbp[2]) && hbp[2] != '0')
295 174 : h->version = 1;
296 : }
297 : }
298 : /* skip rest of line */
299 2340 : while (*hbp)
300 2021 : if (*hbp++ == '\n')
301 319 : break;
302 :
303 : /* Remainder of lines are options, in any order;
304 : * or empty line to terminate
305 : */
306 : for (;;) {
307 : /* Empty line to terminate */
308 3694 : if (hbp[0] == '\n' ||
309 2166 : (hbp[0] == '\r' && hbp[1] == '\n'))
310 : break;
311 1528 : if (!isgraph(*hbp))
312 0 : goto bad;
313 1528 : if (httpread_hdr_option_analyze(h, hbp))
314 0 : goto bad;
315 : /* skip line */
316 50455 : while (*hbp)
317 48927 : if (*hbp++ == '\n')
318 1528 : break;
319 1528 : }
320 :
321 : /* chunked overrides content-length always */
322 319 : if (h->chunked)
323 0 : h->got_content_length = 0;
324 :
325 : /* For some types, we should not try to read a body
326 : * This is in addition to the application determining
327 : * that we should not read a body.
328 : */
329 319 : switch (h->hdr_type) {
330 : case HTTPREAD_HDR_TYPE_REPLY:
331 : /* Some codes can have a body and some not.
332 : * For now, just assume that any other than 200
333 : * do not...
334 : */
335 141 : if (h->reply_code != 200)
336 1 : h->max_bytes = 0;
337 141 : break;
338 : case HTTPREAD_HDR_TYPE_GET:
339 : case HTTPREAD_HDR_TYPE_HEAD:
340 : /* in practice it appears that it is assumed
341 : * that GETs have a body length of 0... ?
342 : */
343 13 : if (h->chunked == 0 && h->got_content_length == 0)
344 13 : h->max_bytes = 0;
345 13 : break;
346 : case HTTPREAD_HDR_TYPE_POST:
347 : case HTTPREAD_HDR_TYPE_PUT:
348 : case HTTPREAD_HDR_TYPE_DELETE:
349 : case HTTPREAD_HDR_TYPE_TRACE:
350 : case HTTPREAD_HDR_TYPE_CONNECT:
351 : case HTTPREAD_HDR_TYPE_NOTIFY:
352 : case HTTPREAD_HDR_TYPE_M_SEARCH:
353 : case HTTPREAD_HDR_TYPE_M_POST:
354 : case HTTPREAD_HDR_TYPE_SUBSCRIBE:
355 : case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
356 : default:
357 165 : break;
358 : }
359 :
360 319 : return 0;
361 :
362 : bad:
363 : /* Error */
364 0 : return -1;
365 : }
366 :
367 :
368 : /* httpread_read_handler -- called when socket ready to read
369 : *
370 : * Note: any extra data we read past end of transmitted file is ignored;
371 : * if we were to support keeping connections open for multiple files then
372 : * this would have to be addressed.
373 : */
374 329 : static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
375 : {
376 329 : struct httpread *h = sock_ctx;
377 : int nread;
378 : char *rbp; /* pointer into read buffer */
379 : char *hbp; /* pointer into header buffer */
380 : char *bbp; /* pointer into body buffer */
381 : char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */
382 :
383 : if (httpread_debug >= 20)
384 : wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
385 :
386 : /* read some at a time, then search for the interal
387 : * boundaries between header and data and etc.
388 : */
389 329 : nread = read(h->sd, readbuf, sizeof(readbuf));
390 329 : if (nread < 0)
391 0 : goto bad;
392 329 : if (nread == 0) {
393 : /* end of transmission... this may be normal
394 : * or may be an error... in some cases we can't
395 : * tell which so we must assume it is normal then.
396 : */
397 1 : if (!h->got_hdr) {
398 : /* Must at least have completed header */
399 1 : wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
400 1 : goto bad;
401 : }
402 0 : if (h->chunked || h->got_content_length) {
403 : /* Premature EOF; e.g. dropped connection */
404 0 : wpa_printf(MSG_DEBUG,
405 : "httpread premature eof(%p) %d/%d",
406 : h, h->body_nbytes,
407 : h->content_length);
408 0 : goto bad;
409 : }
410 : /* No explicit length, hopefully we have all the data
411 : * although dropped connections can cause false
412 : * end
413 : */
414 : if (httpread_debug >= 10)
415 : wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
416 0 : h->got_body = 1;
417 0 : goto got_file;
418 : }
419 328 : rbp = readbuf;
420 :
421 : /* Header consists of text lines (terminated by both CR and LF)
422 : * and an empty line (CR LF only).
423 : */
424 328 : if (!h->got_hdr) {
425 319 : hbp = h->hdr + h->hdr_nbytes;
426 : /* add to headers until:
427 : * -- we run out of data in read buffer
428 : * -- or, we run out of header buffer room
429 : * -- or, we get double CRLF in headers
430 : */
431 : for (;;) {
432 57493 : if (nread == 0)
433 0 : goto get_more;
434 57493 : if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
435 0 : goto bad;
436 : }
437 57493 : *hbp++ = *rbp++;
438 57493 : nread--;
439 57493 : h->hdr_nbytes++;
440 114029 : if (h->hdr_nbytes >= 4 &&
441 58702 : hbp[-1] == '\n' &&
442 4332 : hbp[-2] == '\r' &&
443 2485 : hbp[-3] == '\n' &&
444 319 : hbp[-4] == '\r' ) {
445 319 : h->got_hdr = 1;
446 319 : *hbp = 0; /* null terminate */
447 319 : break;
448 : }
449 57174 : }
450 : /* here we've just finished reading the header */
451 319 : if (httpread_hdr_analyze(h)) {
452 0 : wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
453 0 : goto bad;
454 : }
455 319 : if (h->max_bytes == 0) {
456 : if (httpread_debug >= 10)
457 : wpa_printf(MSG_DEBUG,
458 : "httpread no body hdr end(%p)", h);
459 68 : goto got_file;
460 : }
461 251 : if (h->got_content_length && h->content_length == 0) {
462 : if (httpread_debug >= 10)
463 : wpa_printf(MSG_DEBUG,
464 : "httpread zero content length(%p)",
465 : h);
466 18 : goto got_file;
467 : }
468 : }
469 :
470 : /* Certain types of requests never have data and so
471 : * must be specially recognized.
472 : */
473 463 : if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
474 425 : !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
475 408 : !os_strncasecmp(h->hdr, "HEAD", 4) ||
476 204 : !os_strncasecmp(h->hdr, "GET", 3)) {
477 38 : if (!h->got_body) {
478 : if (httpread_debug >= 10)
479 : wpa_printf(MSG_DEBUG,
480 : "httpread NO BODY for sp. type");
481 : }
482 38 : h->got_body = 1;
483 38 : goto got_file;
484 : }
485 :
486 : /* Data can be just plain binary data, or if "chunked"
487 : * consists of chunks each with a header, ending with
488 : * an ending header.
489 : */
490 204 : if (nread == 0)
491 0 : goto get_more;
492 204 : if (!h->got_body) {
493 : /* Here to get (more of) body */
494 : /* ensure we have enough room for worst case for body
495 : * plus a null termination character
496 : */
497 204 : if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
498 : char *new_body;
499 : int new_alloc_nbytes;
500 :
501 195 : if (h->body_nbytes >= h->max_bytes)
502 0 : goto bad;
503 195 : new_alloc_nbytes = h->body_alloc_nbytes +
504 : HTTPREAD_BODYBUF_DELTA;
505 : /* For content-length case, the first time
506 : * through we allocate the whole amount
507 : * we need.
508 : */
509 390 : if (h->got_content_length &&
510 195 : new_alloc_nbytes < (h->content_length + 1))
511 0 : new_alloc_nbytes = h->content_length + 1;
512 195 : if ((new_body = os_realloc(h->body, new_alloc_nbytes))
513 : == NULL)
514 0 : goto bad;
515 :
516 195 : h->body = new_body;
517 195 : h->body_alloc_nbytes = new_alloc_nbytes;
518 : }
519 : /* add bytes */
520 204 : bbp = h->body + h->body_nbytes;
521 : for (;;) {
522 : int ncopy;
523 : /* See if we need to stop */
524 408 : if (h->chunked && h->in_chunk_data == 0) {
525 : /* in chunk header */
526 0 : char *cbp = h->body + h->chunk_start;
527 0 : if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
528 0 : bbp[-1] == '\n') {
529 : /* end of chunk hdr line */
530 : /* hdr line consists solely
531 : * of a hex numeral and CFLF
532 : */
533 0 : if (!isxdigit(*cbp))
534 0 : goto bad;
535 0 : h->chunk_size = strtoul(cbp, NULL, 16);
536 : /* throw away chunk header
537 : * so we have only real data
538 : */
539 0 : h->body_nbytes = h->chunk_start;
540 0 : bbp = cbp;
541 0 : if (h->chunk_size == 0) {
542 : /* end of chunking */
543 : /* trailer follows */
544 0 : h->in_trailer = 1;
545 : if (httpread_debug >= 20)
546 : wpa_printf(
547 : MSG_DEBUG,
548 : "httpread end chunks(%p)", h);
549 0 : break;
550 : }
551 0 : h->in_chunk_data = 1;
552 : /* leave chunk_start alone */
553 : }
554 408 : } else if (h->chunked) {
555 : /* in chunk data */
556 0 : if ((h->body_nbytes - h->chunk_start) ==
557 0 : (h->chunk_size + 2)) {
558 : /* end of chunk reached,
559 : * new chunk starts
560 : */
561 : /* check chunk ended w/ CRLF
562 : * which we'll throw away
563 : */
564 0 : if (bbp[-1] == '\n' &&
565 0 : bbp[-2] == '\r') {
566 : } else
567 : goto bad;
568 0 : h->body_nbytes -= 2;
569 0 : bbp -= 2;
570 0 : h->chunk_start = h->body_nbytes;
571 0 : h->in_chunk_data = 0;
572 0 : h->chunk_size = 0; /* just in case */
573 : }
574 816 : } else if (h->got_content_length &&
575 408 : h->body_nbytes >= h->content_length) {
576 195 : h->got_body = 1;
577 : if (httpread_debug >= 10)
578 : wpa_printf(
579 : MSG_DEBUG,
580 : "httpread got content(%p)", h);
581 195 : goto got_file;
582 : }
583 213 : if (nread <= 0)
584 9 : break;
585 : /* Now transfer. Optimize using memcpy where we can. */
586 204 : if (h->chunked && h->in_chunk_data) {
587 : /* copy up to remainder of chunk data
588 : * plus the required CR+LF at end
589 : */
590 0 : ncopy = (h->chunk_start + h->chunk_size + 2) -
591 0 : h->body_nbytes;
592 204 : } else if (h->chunked) {
593 : /*in chunk header -- don't optimize */
594 0 : *bbp++ = *rbp++;
595 0 : nread--;
596 0 : h->body_nbytes++;
597 0 : continue;
598 204 : } else if (h->got_content_length) {
599 204 : ncopy = h->content_length - h->body_nbytes;
600 : } else {
601 0 : ncopy = nread;
602 : }
603 : /* Note: should never be 0 */
604 204 : if (ncopy > nread)
605 9 : ncopy = nread;
606 204 : os_memcpy(bbp, rbp, ncopy);
607 204 : bbp += ncopy;
608 204 : h->body_nbytes += ncopy;
609 204 : rbp += ncopy;
610 204 : nread -= ncopy;
611 204 : } /* body copy loop */
612 : } /* !got_body */
613 9 : if (h->chunked && h->in_trailer) {
614 : /* If "chunked" then there is always a trailer,
615 : * consisting of zero or more non-empty lines
616 : * ending with CR LF and then an empty line w/ CR LF.
617 : * We do NOT support trailers except to skip them --
618 : * this is supported (generally) by the http spec.
619 : */
620 : for (;;) {
621 : int c;
622 0 : if (nread <= 0)
623 0 : break;
624 0 : c = *rbp++;
625 0 : nread--;
626 0 : switch (h->trailer_state) {
627 : case trailer_line_begin:
628 0 : if (c == '\r')
629 0 : h->trailer_state = trailer_empty_cr;
630 : else
631 0 : h->trailer_state = trailer_nonempty;
632 0 : break;
633 : case trailer_empty_cr:
634 : /* end empty line */
635 0 : if (c == '\n') {
636 0 : h->trailer_state = trailer_line_begin;
637 0 : h->in_trailer = 0;
638 : if (httpread_debug >= 10)
639 : wpa_printf(
640 : MSG_DEBUG,
641 : "httpread got content(%p)", h);
642 0 : h->got_body = 1;
643 0 : goto got_file;
644 : }
645 0 : h->trailer_state = trailer_nonempty;
646 0 : break;
647 : case trailer_nonempty:
648 0 : if (c == '\r')
649 0 : h->trailer_state = trailer_nonempty_cr;
650 0 : break;
651 : case trailer_nonempty_cr:
652 0 : if (c == '\n')
653 0 : h->trailer_state = trailer_line_begin;
654 : else
655 0 : h->trailer_state = trailer_nonempty;
656 0 : break;
657 : }
658 0 : }
659 : }
660 9 : goto get_more;
661 :
662 : bad:
663 : /* Error */
664 1 : wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
665 1 : (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
666 1 : return;
667 :
668 : get_more:
669 9 : return;
670 :
671 : got_file:
672 : if (httpread_debug >= 10)
673 : wpa_printf(MSG_DEBUG,
674 : "httpread got file %d bytes type %d",
675 : h->body_nbytes, h->hdr_type);
676 : /* Null terminate for convenience of some applications */
677 319 : if (h->body)
678 195 : h->body[h->body_nbytes] = 0; /* null terminate */
679 319 : h->got_file = 1;
680 : /* Assume that we do NOT support keeping connection alive,
681 : * and just in case somehow we don't get destroyed right away,
682 : * unregister now.
683 : */
684 319 : eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
685 : /* The application can destroy us whenever they feel like...
686 : * cancel timeout.
687 : */
688 319 : eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
689 319 : (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
690 : }
691 :
692 :
693 : /* httpread_create -- start a new reading session making use of eloop.
694 : * The new instance will use the socket descriptor for reading (until
695 : * it gets a file and not after) but will not close the socket, even
696 : * when the instance is destroyed (the application must do that).
697 : * Return NULL on error.
698 : *
699 : * Provided that httpread_create successfully returns a handle,
700 : * the callback fnc is called to handle httpread_event events.
701 : * The caller should do destroy on any errors or unknown events.
702 : *
703 : * Pass max_bytes == 0 to not read body at all (required for e.g.
704 : * reply to HEAD request).
705 : */
706 320 : struct httpread * httpread_create(
707 : int sd, /* descriptor of TCP socket to read from */
708 : void (*cb)(struct httpread *handle, void *cookie,
709 : enum httpread_event e), /* call on event */
710 : void *cookie, /* pass to callback */
711 : int max_bytes, /* maximum body size else abort it */
712 : int timeout_seconds /* 0; or total duration timeout period */
713 : )
714 : {
715 320 : struct httpread *h = NULL;
716 :
717 320 : h = os_zalloc(sizeof(*h));
718 320 : if (h == NULL)
719 0 : goto fail;
720 320 : h->sd = sd;
721 320 : h->cb = cb;
722 320 : h->cookie = cookie;
723 320 : h->max_bytes = max_bytes;
724 320 : h->timeout_seconds = timeout_seconds;
725 :
726 640 : if (timeout_seconds > 0 &&
727 320 : eloop_register_timeout(timeout_seconds, 0,
728 : httpread_timeout_handler, NULL, h)) {
729 : /* No way to recover (from malloc failure) */
730 0 : goto fail;
731 : }
732 320 : if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
733 : NULL, h)) {
734 : /* No way to recover (from malloc failure) */
735 0 : goto fail;
736 : }
737 320 : return h;
738 :
739 : fail:
740 :
741 : /* Error */
742 0 : httpread_destroy(h);
743 0 : return NULL;
744 : }
745 :
746 :
747 : /* httpread_hdr_type_get -- When file is ready, returns header type. */
748 319 : enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
749 : {
750 319 : return h->hdr_type;
751 : }
752 :
753 :
754 : /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
755 : * or possibly NULL (which would be an error).
756 : */
757 232 : char * httpread_uri_get(struct httpread *h)
758 : {
759 232 : return h->uri;
760 : }
761 :
762 :
763 : /* httpread_reply_code_get -- When reply is ready, returns reply code */
764 141 : int httpread_reply_code_get(struct httpread *h)
765 : {
766 141 : return h->reply_code;
767 : }
768 :
769 :
770 : /* httpread_length_get -- When file is ready, returns file length. */
771 27 : int httpread_length_get(struct httpread *h)
772 : {
773 27 : return h->body_nbytes;
774 : }
775 :
776 :
777 : /* httpread_data_get -- When file is ready, returns file content
778 : * with null byte appened.
779 : * Might return NULL in some error condition.
780 : */
781 207 : void * httpread_data_get(struct httpread *h)
782 : {
783 207 : return h->body ? h->body : "";
784 : }
785 :
786 :
787 : /* httpread_hdr_get -- When file is ready, returns header content
788 : * with null byte appended.
789 : * Might return NULL in some error condition.
790 : */
791 38 : char * httpread_hdr_get(struct httpread *h)
792 : {
793 38 : return h->hdr;
794 : }
795 :
796 :
797 : /* httpread_hdr_line_get -- When file is ready, returns pointer
798 : * to line within header content matching the given tag
799 : * (after the tag itself and any spaces/tabs).
800 : *
801 : * The tag should end with a colon for reliable matching.
802 : *
803 : * If not found, returns NULL;
804 : */
805 80 : char * httpread_hdr_line_get(struct httpread *h, const char *tag)
806 : {
807 80 : int tag_len = os_strlen(tag);
808 80 : char *hdr = h->hdr;
809 80 : hdr = os_strchr(hdr, '\n');
810 80 : if (hdr == NULL)
811 0 : return NULL;
812 80 : hdr++;
813 : for (;;) {
814 322 : if (!os_strncasecmp(hdr, tag, tag_len)) {
815 79 : hdr += tag_len;
816 228 : while (*hdr == ' ' || *hdr == '\t')
817 70 : hdr++;
818 79 : return hdr;
819 : }
820 243 : hdr = os_strchr(hdr, '\n');
821 243 : if (hdr == NULL)
822 1 : return NULL;
823 242 : hdr++;
824 242 : }
825 : }
|