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 (*hbp == ' ' || *hbp == '\t')
282 hbp++;
283 /* get version */
284 if (0 == strncmp(hbp, "HTTP/", 5)) {
285 hbp += 5;
286 if (hbp[0] == '1' && hbp[1] == '.' &&
287 isdigit(hbp[2]) && hbp[2] != '0')
288 h->version = 1;
289 }
290 }
291 /* skip rest of line */
292 while (*hbp)
293 if (*hbp++ == '\n')
294 break;
295
296 /* Remainder of lines are options, in any order;
297 * or empty line to terminate
298 */
299 for (;;) {
300 /* Empty line to terminate */
301 if (hbp[0] == '\n' ||
302 (hbp[0] == '\r' && hbp[1] == '\n'))
303 break;
304 if (!isgraph(*hbp))
305 goto bad;
306 if (httpread_hdr_option_analyze(h, hbp))
307 goto bad;
308 /* skip line */
309 while (*hbp)
310 if (*hbp++ == '\n')
311 break;
312 }
313
314 /* chunked overrides content-length always */
315 if (h->chunked)
316 h->got_content_length = 0;
317
318 /* For some types, we should not try to read a body
319 * This is in addition to the application determining
320 * that we should not read a body.
321 */
322 switch (h->hdr_type) {
323 case HTTPREAD_HDR_TYPE_REPLY:
324 /* Some codes can have a body and some not.
325 * For now, just assume that any other than 200
326 * do not...
327 */
328 if (h->reply_code != 200)
329 h->max_bytes = 0;
330 break;
331 case HTTPREAD_HDR_TYPE_GET:
332 case HTTPREAD_HDR_TYPE_HEAD:
333 /* in practice it appears that it is assumed
334 * that GETs have a body length of 0... ?
335 */
336 if (h->chunked == 0 && h->got_content_length == 0)
337 h->max_bytes = 0;
338 break;
339 case HTTPREAD_HDR_TYPE_POST:
340 case HTTPREAD_HDR_TYPE_PUT:
341 case HTTPREAD_HDR_TYPE_DELETE:
342 case HTTPREAD_HDR_TYPE_TRACE:
343 case HTTPREAD_HDR_TYPE_CONNECT:
344 case HTTPREAD_HDR_TYPE_NOTIFY:
345 case HTTPREAD_HDR_TYPE_M_SEARCH:
346 case HTTPREAD_HDR_TYPE_M_POST:
347 case HTTPREAD_HDR_TYPE_SUBSCRIBE:
348 case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
349 default:
350 break;
351 }
352
353 return 0;
354
355 bad:
356 /* Error */
357 return -1;
358 }
359
360
361 /* httpread_read_handler -- called when socket ready to read
362 *
363 * Note: any extra data we read past end of transmitted file is ignored;
364 * if we were to support keeping connections open for multiple files then
365 * this would have to be addressed.
366 */
httpread_read_handler(int sd,void * eloop_ctx,void * sock_ctx)367 static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
368 {
369 struct httpread *h = sock_ctx;
370 int nread;
371 char *rbp; /* pointer into read buffer */
372 char *hbp; /* pointer into header buffer */
373 char *bbp; /* pointer into body buffer */
374 char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */
375
376 /* read some at a time, then search for the interal
377 * boundaries between header and data and etc.
378 */
379 wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h);
380 nread = read(h->sd, readbuf, sizeof(readbuf));
381 if (nread < 0) {
382 wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno));
383 goto bad;
384 }
385 wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread);
386 if (nread == 0) {
387 /* end of transmission... this may be normal
388 * or may be an error... in some cases we can't
389 * tell which so we must assume it is normal then.
390 */
391 if (!h->got_hdr) {
392 /* Must at least have completed header */
393 wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
394 goto bad;
395 }
396 if (h->chunked || h->got_content_length) {
397 /* Premature EOF; e.g. dropped connection */
398 wpa_printf(MSG_DEBUG,
399 "httpread premature eof(%p) %d/%d",
400 h, h->body_nbytes,
401 h->content_length);
402 goto bad;
403 }
404 /* No explicit length, hopefully we have all the data
405 * although dropped connections can cause false
406 * end
407 */
408 wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
409 h->got_body = 1;
410 goto got_file;
411 }
412 rbp = readbuf;
413
414 /* Header consists of text lines (terminated by both CR and LF)
415 * and an empty line (CR LF only).
416 */
417 if (!h->got_hdr) {
418 hbp = h->hdr + h->hdr_nbytes;
419 /* add to headers until:
420 * -- we run out of data in read buffer
421 * -- or, we run out of header buffer room
422 * -- or, we get double CRLF in headers
423 */
424 for (;;) {
425 if (nread == 0)
426 goto get_more;
427 if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
428 wpa_printf(MSG_DEBUG,
429 "httpread: Too long header");
430 goto bad;
431 }
432 *hbp++ = *rbp++;
433 nread--;
434 h->hdr_nbytes++;
435 if (h->hdr_nbytes >= 4 &&
436 hbp[-1] == '\n' &&
437 hbp[-2] == '\r' &&
438 hbp[-3] == '\n' &&
439 hbp[-4] == '\r' ) {
440 h->got_hdr = 1;
441 *hbp = 0; /* null terminate */
442 break;
443 }
444 }
445 /* here we've just finished reading the header */
446 if (httpread_hdr_analyze(h)) {
447 wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
448 goto bad;
449 }
450 if (h->max_bytes == 0) {
451 wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)",
452 h);
453 goto got_file;
454 }
455 if (h->got_content_length && h->content_length == 0) {
456 wpa_printf(MSG_DEBUG,
457 "httpread zero content length(%p)", h);
458 goto got_file;
459 }
460 }
461
462 /* Certain types of requests never have data and so
463 * must be specially recognized.
464 */
465 if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
466 !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
467 !os_strncasecmp(h->hdr, "HEAD", 4) ||
468 !os_strncasecmp(h->hdr, "GET", 3)) {
469 if (!h->got_body) {
470 wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type");
471 }
472 h->got_body = 1;
473 goto got_file;
474 }
475
476 /* Data can be just plain binary data, or if "chunked"
477 * consists of chunks each with a header, ending with
478 * an ending header.
479 */
480 if (nread == 0)
481 goto get_more;
482 if (!h->got_body) {
483 /* Here to get (more of) body */
484 /* ensure we have enough room for worst case for body
485 * plus a null termination character
486 */
487 if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
488 char *new_body;
489 int new_alloc_nbytes;
490
491 if (h->body_nbytes >= h->max_bytes) {
492 wpa_printf(MSG_DEBUG,
493 "httpread: body_nbytes=%d >= max_bytes=%d",
494 h->body_nbytes, h->max_bytes);
495 goto bad;
496 }
497 new_alloc_nbytes = h->body_alloc_nbytes +
498 HTTPREAD_BODYBUF_DELTA;
499 /* For content-length case, the first time
500 * through we allocate the whole amount
501 * we need.
502 */
503 if (h->got_content_length &&
504 new_alloc_nbytes < (h->content_length + 1))
505 new_alloc_nbytes = h->content_length + 1;
506 if (new_alloc_nbytes < h->body_alloc_nbytes ||
507 new_alloc_nbytes > h->max_bytes +
508 HTTPREAD_BODYBUF_DELTA) {
509 wpa_printf(MSG_DEBUG,
510 "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)",
511 new_alloc_nbytes,
512 h->body_alloc_nbytes,
513 h->max_bytes);
514 goto bad;
515 }
516 if ((new_body = os_realloc(h->body, new_alloc_nbytes))
517 == NULL) {
518 wpa_printf(MSG_DEBUG,
519 "httpread: Failed to reallocate buffer (len=%d)",
520 new_alloc_nbytes);
521 goto bad;
522 }
523
524 h->body = new_body;
525 h->body_alloc_nbytes = new_alloc_nbytes;
526 }
527 /* add bytes */
528 bbp = h->body + h->body_nbytes;
529 for (;;) {
530 int ncopy;
531 /* See if we need to stop */
532 if (h->chunked && h->in_chunk_data == 0) {
533 /* in chunk header */
534 char *cbp = h->body + h->chunk_start;
535 if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
536 bbp[-1] == '\n') {
537 /* end of chunk hdr line */
538 /* hdr line consists solely
539 * of a hex numeral and CFLF
540 */
541 if (!isxdigit(*cbp)) {
542 wpa_printf(MSG_DEBUG,
543 "httpread: Unexpected chunk header value (not a hex digit)");
544 goto bad;
545 }
546 h->chunk_size = strtoul(cbp, NULL, 16);
547 if (h->chunk_size < 0 ||
548 h->chunk_size > h->max_bytes) {
549 wpa_printf(MSG_DEBUG,
550 "httpread: Invalid chunk size %d",
551 h->chunk_size);
552 goto bad;
553 }
554 /* throw away chunk header
555 * so we have only real data
556 */
557 h->body_nbytes = h->chunk_start;
558 bbp = cbp;
559 if (h->chunk_size == 0) {
560 /* end of chunking */
561 /* trailer follows */
562 h->in_trailer = 1;
563 wpa_printf(MSG_DEBUG,
564 "httpread end chunks(%p)",
565 h);
566 break;
567 }
568 h->in_chunk_data = 1;
569 /* leave chunk_start alone */
570 }
571 } else if (h->chunked) {
572 /* in chunk data */
573 if ((h->body_nbytes - h->chunk_start) ==
574 (h->chunk_size + 2)) {
575 /* end of chunk reached,
576 * new chunk starts
577 */
578 /* check chunk ended w/ CRLF
579 * which we'll throw away
580 */
581 if (bbp[-1] == '\n' &&
582 bbp[-2] == '\r') {
583 } else {
584 wpa_printf(MSG_DEBUG,
585 "httpread: Invalid chunk end");
586 goto bad;
587 }
588 h->body_nbytes -= 2;
589 bbp -= 2;
590 h->chunk_start = h->body_nbytes;
591 h->in_chunk_data = 0;
592 h->chunk_size = 0; /* just in case */
593 }
594 } else if (h->got_content_length &&
595 h->body_nbytes >= h->content_length) {
596 h->got_body = 1;
597 wpa_printf(MSG_DEBUG,
598 "httpread got content(%p)", h);
599 goto got_file;
600 }
601 if (nread <= 0)
602 break;
603 /* Now transfer. Optimize using memcpy where we can. */
604 if (h->chunked && h->in_chunk_data) {
605 /* copy up to remainder of chunk data
606 * plus the required CR+LF at end
607 */
608 ncopy = (h->chunk_start + h->chunk_size + 2) -
609 h->body_nbytes;
610 } else if (h->chunked) {
611 /*in chunk header -- don't optimize */
612 *bbp++ = *rbp++;
613 nread--;
614 h->body_nbytes++;
615 continue;
616 } else if (h->got_content_length) {
617 ncopy = h->content_length - h->body_nbytes;
618 } else {
619 ncopy = nread;
620 }
621 /* Note: should never be 0 */
622 if (ncopy < 0) {
623 wpa_printf(MSG_DEBUG,
624 "httpread: Invalid ncopy=%d", ncopy);
625 goto bad;
626 }
627 if (ncopy > nread)
628 ncopy = nread;
629 os_memcpy(bbp, rbp, ncopy);
630 bbp += ncopy;
631 h->body_nbytes += ncopy;
632 rbp += ncopy;
633 nread -= ncopy;
634 } /* body copy loop */
635 } /* !got_body */
636 if (h->chunked && h->in_trailer) {
637 /* If "chunked" then there is always a trailer,
638 * consisting of zero or more non-empty lines
639 * ending with CR LF and then an empty line w/ CR LF.
640 * We do NOT support trailers except to skip them --
641 * this is supported (generally) by the http spec.
642 */
643 for (;;) {
644 int c;
645 if (nread <= 0)
646 break;
647 c = *rbp++;
648 nread--;
649 switch (h->trailer_state) {
650 case trailer_line_begin:
651 if (c == '\r')
652 h->trailer_state = trailer_empty_cr;
653 else
654 h->trailer_state = trailer_nonempty;
655 break;
656 case trailer_empty_cr:
657 /* end empty line */
658 if (c == '\n') {
659 h->trailer_state = trailer_line_begin;
660 h->in_trailer = 0;
661 wpa_printf(MSG_DEBUG,
662 "httpread got content(%p)",
663 h);
664 h->got_body = 1;
665 goto got_file;
666 }
667 h->trailer_state = trailer_nonempty;
668 break;
669 case trailer_nonempty:
670 if (c == '\r')
671 h->trailer_state = trailer_nonempty_cr;
672 break;
673 case trailer_nonempty_cr:
674 if (c == '\n')
675 h->trailer_state = trailer_line_begin;
676 else
677 h->trailer_state = trailer_nonempty;
678 break;
679 }
680 }
681 }
682 goto get_more;
683
684 bad:
685 /* Error */
686 wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
687 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
688 return;
689
690 get_more:
691 wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
692 return;
693
694 got_file:
695 wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d",
696 h->body_nbytes, h->hdr_type);
697 wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body",
698 h->body, h->body_nbytes);
699 /* Null terminate for convenience of some applications */
700 if (h->body)
701 h->body[h->body_nbytes] = 0; /* null terminate */
702 h->got_file = 1;
703 /* Assume that we do NOT support keeping connection alive,
704 * and just in case somehow we don't get destroyed right away,
705 * unregister now.
706 */
707 eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
708 /* The application can destroy us whenever they feel like...
709 * cancel timeout.
710 */
711 eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
712 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
713 }
714
715
716 /* httpread_create -- start a new reading session making use of eloop.
717 * The new instance will use the socket descriptor for reading (until
718 * it gets a file and not after) but will not close the socket, even
719 * when the instance is destroyed (the application must do that).
720 * Return NULL on error.
721 *
722 * Provided that httpread_create successfully returns a handle,
723 * the callback fnc is called to handle httpread_event events.
724 * The caller should do destroy on any errors or unknown events.
725 *
726 * Pass max_bytes == 0 to not read body at all (required for e.g.
727 * reply to HEAD request).
728 */
httpread_create(int sd,void (* cb)(struct httpread * handle,void * cookie,enum httpread_event e),void * cookie,int max_bytes,int timeout_seconds)729 struct httpread * httpread_create(
730 int sd, /* descriptor of TCP socket to read from */
731 void (*cb)(struct httpread *handle, void *cookie,
732 enum httpread_event e), /* call on event */
733 void *cookie, /* pass to callback */
734 int max_bytes, /* maximum body size else abort it */
735 int timeout_seconds /* 0; or total duration timeout period */
736 )
737 {
738 struct httpread *h = NULL;
739
740 h = os_zalloc(sizeof(*h));
741 if (h == NULL)
742 goto fail;
743 h->sd = sd;
744 h->cb = cb;
745 h->cookie = cookie;
746 h->max_bytes = max_bytes;
747 h->timeout_seconds = timeout_seconds;
748
749 if (timeout_seconds > 0 &&
750 eloop_register_timeout(timeout_seconds, 0,
751 httpread_timeout_handler, NULL, h)) {
752 /* No way to recover (from malloc failure) */
753 goto fail;
754 }
755 if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
756 NULL, h)) {
757 /* No way to recover (from malloc failure) */
758 goto fail;
759 }
760 return h;
761
762 fail:
763
764 /* Error */
765 httpread_destroy(h);
766 return NULL;
767 }
768
769
770 /* httpread_hdr_type_get -- When file is ready, returns header type. */
httpread_hdr_type_get(struct httpread * h)771 enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
772 {
773 return h->hdr_type;
774 }
775
776
777 /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
778 * or possibly NULL (which would be an error).
779 */
httpread_uri_get(struct httpread * h)780 char * httpread_uri_get(struct httpread *h)
781 {
782 return h->uri;
783 }
784
785
786 /* httpread_reply_code_get -- When reply is ready, returns reply code */
httpread_reply_code_get(struct httpread * h)787 int httpread_reply_code_get(struct httpread *h)
788 {
789 return h->reply_code;
790 }
791
792
793 /* httpread_length_get -- When file is ready, returns file length. */
httpread_length_get(struct httpread * h)794 int httpread_length_get(struct httpread *h)
795 {
796 return h->body_nbytes;
797 }
798
799
800 /* httpread_data_get -- When file is ready, returns file content
801 * with null byte appened.
802 * Might return NULL in some error condition.
803 */
httpread_data_get(struct httpread * h)804 void * httpread_data_get(struct httpread *h)
805 {
806 return h->body ? h->body : "";
807 }
808
809
810 /* httpread_hdr_get -- When file is ready, returns header content
811 * with null byte appended.
812 * Might return NULL in some error condition.
813 */
httpread_hdr_get(struct httpread * h)814 char * httpread_hdr_get(struct httpread *h)
815 {
816 return h->hdr;
817 }
818
819
820 /* httpread_hdr_line_get -- When file is ready, returns pointer
821 * to line within header content matching the given tag
822 * (after the tag itself and any spaces/tabs).
823 *
824 * The tag should end with a colon for reliable matching.
825 *
826 * If not found, returns NULL;
827 */
httpread_hdr_line_get(struct httpread * h,const char * tag)828 char * httpread_hdr_line_get(struct httpread *h, const char *tag)
829 {
830 int tag_len = os_strlen(tag);
831 char *hdr = h->hdr;
832 hdr = os_strchr(hdr, '\n');
833 if (hdr == NULL)
834 return NULL;
835 hdr++;
836 for (;;) {
837 if (!os_strncasecmp(hdr, tag, tag_len)) {
838 hdr += tag_len;
839 while (*hdr == ' ' || *hdr == '\t')
840 hdr++;
841 return hdr;
842 }
843 hdr = os_strchr(hdr, '\n');
844 if (hdr == NULL)
845 return NULL;
846 hdr++;
847 }
848 }
849