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