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