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 :
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 : */
98 24055 : static int word_eq(char *s1, char *s2)
99 : {
100 : int c1;
101 : int c2;
102 24055 : int end1 = 0;
103 24055 : int end2 = 0;
104 : for (;;) {
105 59083 : c1 = *s1++;
106 59083 : c2 = *s2++;
107 59083 : if (isalpha(c1) && isupper(c1))
108 30283 : c1 = tolower(c1);
109 59083 : if (isalpha(c2) && isupper(c2))
110 53585 : c2 = tolower(c2);
111 59083 : end1 = !isgraph(c1);
112 59083 : end2 = !isgraph(c2);
113 59083 : if (end1 || end2 || c1 != c2)
114 : break;
115 35028 : }
116 24055 : 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 : */
127 1548 : void httpread_destroy(struct httpread *h)
128 : {
129 1548 : wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h);
130 1548 : if (!h)
131 1683 : return;
132 :
133 1413 : eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
134 1413 : eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
135 1413 : os_free(h->body);
136 1413 : os_free(h->uri);
137 1413 : os_memset(h, 0, sizeof(*h)); /* aid debugging */
138 1413 : h->sd = -1; /* aid debugging */
139 1413 : os_free(h);
140 : }
141 :
142 :
143 : /* httpread_timeout_handler -- called on excessive total duration
144 : */
145 1 : static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
146 : {
147 1 : struct httpread *h = user_ctx;
148 1 : wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
149 1 : (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
150 1 : }
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 : */
156 7864 : static int httpread_hdr_option_analyze(
157 : struct httpread *h,
158 : char *hbp /* pointer to current line in header buffer */
159 : )
160 : {
161 7864 : if (word_eq(hbp, "CONTENT-LENGTH:")) {
162 21862 : while (isgraph(*hbp))
163 19290 : hbp++;
164 3858 : while (*hbp == ' ' || *hbp == '\t')
165 1286 : hbp++;
166 1286 : if (!isdigit(*hbp))
167 2 : return -1;
168 1284 : h->content_length = atol(hbp);
169 1284 : if (h->content_length < 0 || h->content_length > h->max_bytes) {
170 2 : wpa_printf(MSG_DEBUG,
171 : "httpread: Unacceptable Content-Length %d",
172 : h->content_length);
173 2 : return -1;
174 : }
175 1282 : h->got_content_length = 1;
176 1282 : return 0;
177 : }
178 13156 : if (word_eq(hbp, "TRANSFER_ENCODING:") ||
179 6578 : word_eq(hbp, "TRANSFER-ENCODING:")) {
180 220 : while (isgraph(*hbp))
181 198 : hbp++;
182 33 : while (*hbp == ' ' || *hbp == '\t')
183 11 : hbp++;
184 : /* There should (?) be no encodings of interest
185 : * other than chunked...
186 : */
187 11 : if (word_eq(hbp, "CHUNKED")) {
188 10 : h->chunked = 1;
189 10 : h->in_chunk_data = 0;
190 : /* ignore possible ;<parameters> */
191 : }
192 11 : return 0;
193 : }
194 : /* skip anything we don't know, which is a lot */
195 6567 : return 0;
196 : }
197 :
198 :
199 1401 : static int httpread_hdr_analyze(struct httpread *h)
200 : {
201 1401 : char *hbp = h->hdr; /* pointer into h->hdr */
202 1401 : int standard_first_line = 1;
203 :
204 : /* First line is special */
205 1401 : h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
206 1401 : if (!isgraph(*hbp))
207 1 : goto bad;
208 1400 : if (os_strncmp(hbp, "HTTP/", 5) == 0) {
209 888 : h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
210 888 : standard_first_line = 0;
211 888 : hbp += 5;
212 1775 : if (hbp[0] == '1' && hbp[1] == '.' &&
213 1774 : isdigit(hbp[2]) && hbp[2] != '0')
214 887 : h->version = 1;
215 4437 : while (isgraph(*hbp))
216 2661 : hbp++;
217 2663 : while (*hbp == ' ' || *hbp == '\t')
218 887 : hbp++;
219 888 : if (!isdigit(*hbp))
220 1 : goto bad;
221 887 : h->reply_code = atol(hbp);
222 512 : } else if (word_eq(hbp, "GET"))
223 52 : h->hdr_type = HTTPREAD_HDR_TYPE_GET;
224 460 : else if (word_eq(hbp, "HEAD"))
225 8 : h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
226 452 : else if (word_eq(hbp, "POST"))
227 192 : h->hdr_type = HTTPREAD_HDR_TYPE_POST;
228 260 : else if (word_eq(hbp, "PUT"))
229 1 : h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
230 259 : else if (word_eq(hbp, "DELETE"))
231 1 : h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
232 258 : else if (word_eq(hbp, "TRACE"))
233 1 : h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
234 257 : else if (word_eq(hbp, "CONNECT"))
235 1 : h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
236 256 : else if (word_eq(hbp, "NOTIFY"))
237 163 : h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
238 93 : else if (word_eq(hbp, "M-SEARCH"))
239 1 : h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
240 92 : else if (word_eq(hbp, "M-POST"))
241 1 : h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
242 91 : else if (word_eq(hbp, "SUBSCRIBE"))
243 57 : h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
244 34 : else if (word_eq(hbp, "UNSUBSCRIBE"))
245 31 : h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
246 : else {
247 : }
248 :
249 1399 : if (standard_first_line) {
250 : char *rawuri;
251 : char *uri;
252 : /* skip type */
253 3866 : while (isgraph(*hbp))
254 2842 : hbp++;
255 1536 : while (*hbp == ' ' || *hbp == '\t')
256 512 : 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 512 : rawuri = hbp;
263 10640 : while (isgraph(*hbp))
264 9616 : hbp++;
265 512 : h->uri = os_malloc((hbp - rawuri) + 1);
266 512 : if (h->uri == NULL)
267 1 : goto bad;
268 511 : uri = h->uri;
269 10635 : while (rawuri < hbp) {
270 9613 : int c = *rawuri;
271 9617 : if (c == '%' &&
272 6 : isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
273 1 : *uri++ = hex2byte(rawuri + 1);
274 1 : rawuri += 3;
275 : } else {
276 9612 : *uri++ = c;
277 9612 : rawuri++;
278 : }
279 : }
280 511 : *uri = 0; /* null terminate */
281 1533 : while (*hbp == ' ' || *hbp == '\t')
282 511 : hbp++;
283 : /* get version */
284 511 : if (0 == strncmp(hbp, "HTTP/", 5)) {
285 511 : hbp += 5;
286 1022 : if (hbp[0] == '1' && hbp[1] == '.' &&
287 1022 : isdigit(hbp[2]) && hbp[2] != '0')
288 503 : h->version = 1;
289 : }
290 : }
291 : /* skip rest of line */
292 11080 : while (*hbp)
293 9682 : if (*hbp++ == '\n')
294 1398 : 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 18516 : if (hbp[0] == '\n' ||
302 10651 : (hbp[0] == '\r' && hbp[1] == '\n'))
303 : break;
304 7865 : if (!isgraph(*hbp))
305 1 : goto bad;
306 7864 : if (httpread_hdr_option_analyze(h, hbp))
307 4 : goto bad;
308 : /* skip line */
309 260650 : while (*hbp)
310 252790 : if (*hbp++ == '\n')
311 7860 : break;
312 7860 : }
313 :
314 : /* chunked overrides content-length always */
315 1393 : if (h->chunked)
316 10 : 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 1393 : 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 887 : if (h->reply_code != 200)
329 9 : h->max_bytes = 0;
330 887 : 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 55 : if (h->chunked == 0 && h->got_content_length == 0)
337 53 : h->max_bytes = 0;
338 55 : 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 451 : break;
351 : }
352 :
353 1393 : return 0;
354 :
355 : bad:
356 : /* Error */
357 8 : 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 : */
367 1523 : static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
368 : {
369 1523 : 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 1523 : wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h);
380 1523 : nread = read(h->sd, readbuf, sizeof(readbuf));
381 1523 : if (nread < 0) {
382 1 : wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno));
383 1 : goto bad;
384 : }
385 1522 : wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread);
386 1522 : 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 13 : if (!h->got_hdr) {
392 : /* Must at least have completed header */
393 3 : wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
394 3 : goto bad;
395 : }
396 10 : if (h->chunked || h->got_content_length) {
397 : /* Premature EOF; e.g. dropped connection */
398 3 : wpa_printf(MSG_DEBUG,
399 : "httpread premature eof(%p) %d/%d",
400 : h, h->body_nbytes,
401 : h->content_length);
402 3 : goto bad;
403 : }
404 : /* No explicit length, hopefully we have all the data
405 : * although dropped connections can cause false
406 : * end
407 : */
408 7 : wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
409 7 : h->got_body = 1;
410 7 : goto got_file;
411 : }
412 1509 : 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 1509 : if (!h->got_hdr) {
418 1414 : 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 297637 : if (nread == 0)
426 11 : goto get_more;
427 297626 : if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
428 2 : wpa_printf(MSG_DEBUG,
429 : "httpread: Too long header");
430 2 : goto bad;
431 : }
432 297624 : *hbp++ = *rbp++;
433 297624 : nread--;
434 297624 : h->hdr_nbytes++;
435 591038 : if (h->hdr_nbytes >= 4 &&
436 304082 : hbp[-1] == '\n' &&
437 21336 : hbp[-2] == '\r' &&
438 12069 : hbp[-3] == '\n' &&
439 1401 : hbp[-4] == '\r' ) {
440 1401 : h->got_hdr = 1;
441 1401 : *hbp = 0; /* null terminate */
442 1401 : break;
443 : }
444 296223 : }
445 : /* here we've just finished reading the header */
446 1401 : if (httpread_hdr_analyze(h)) {
447 8 : wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
448 8 : goto bad;
449 : }
450 1393 : if (h->max_bytes == 0) {
451 690 : wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)",
452 : h);
453 690 : goto got_file;
454 : }
455 703 : if (h->got_content_length && h->content_length == 0) {
456 52 : wpa_printf(MSG_DEBUG,
457 : "httpread zero content length(%p)", h);
458 52 : goto got_file;
459 : }
460 : }
461 :
462 : /* Certain types of requests never have data and so
463 : * must be specially recognized.
464 : */
465 1435 : if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
466 1348 : !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
467 1316 : !os_strncasecmp(h->hdr, "HEAD", 4) ||
468 657 : !os_strncasecmp(h->hdr, "GET", 3)) {
469 89 : if (!h->got_body) {
470 89 : wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type");
471 : }
472 89 : h->got_body = 1;
473 89 : 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 657 : if (nread == 0)
481 7 : goto get_more;
482 650 : 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 650 : if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
488 : char *new_body;
489 : int new_alloc_nbytes;
490 :
491 559 : if (h->body_nbytes >= h->max_bytes) {
492 1 : wpa_printf(MSG_DEBUG,
493 : "httpread: body_nbytes=%d >= max_bytes=%d",
494 : h->body_nbytes, h->max_bytes);
495 1 : goto bad;
496 : }
497 558 : 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 1102 : if (h->got_content_length &&
504 544 : new_alloc_nbytes < (h->content_length + 1))
505 1 : new_alloc_nbytes = h->content_length + 1;
506 1116 : if (new_alloc_nbytes < h->body_alloc_nbytes ||
507 558 : new_alloc_nbytes > h->max_bytes +
508 : HTTPREAD_BODYBUF_DELTA) {
509 1 : 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 1 : goto bad;
515 : }
516 557 : if ((new_body = os_realloc(h->body, new_alloc_nbytes))
517 : == NULL) {
518 1 : wpa_printf(MSG_DEBUG,
519 : "httpread: Failed to reallocate buffer (len=%d)",
520 : new_alloc_nbytes);
521 1 : goto bad;
522 : }
523 :
524 556 : h->body = new_body;
525 556 : h->body_alloc_nbytes = new_alloc_nbytes;
526 : }
527 : /* add bytes */
528 647 : bbp = h->body + h->body_nbytes;
529 : for (;;) {
530 : int ncopy;
531 : /* See if we need to stop */
532 52726 : if (h->chunked && h->in_chunk_data == 0) {
533 : /* in chunk header */
534 22090 : char *cbp = h->body + h->chunk_start;
535 29434 : if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
536 7344 : bbp[-1] == '\n') {
537 : /* end of chunk hdr line */
538 : /* hdr line consists solely
539 : * of a hex numeral and CFLF
540 : */
541 7344 : if (!isxdigit(*cbp)) {
542 1 : wpa_printf(MSG_DEBUG,
543 : "httpread: Unexpected chunk header value (not a hex digit)");
544 1 : goto bad;
545 : }
546 7343 : h->chunk_size = strtoul(cbp, NULL, 16);
547 14685 : if (h->chunk_size < 0 ||
548 7342 : h->chunk_size > h->max_bytes) {
549 2 : wpa_printf(MSG_DEBUG,
550 : "httpread: Invalid chunk size %d",
551 : h->chunk_size);
552 2 : goto bad;
553 : }
554 : /* throw away chunk header
555 : * so we have only real data
556 : */
557 7341 : h->body_nbytes = h->chunk_start;
558 7341 : bbp = cbp;
559 7341 : if (h->chunk_size == 0) {
560 : /* end of chunking */
561 : /* trailer follows */
562 4 : h->in_trailer = 1;
563 4 : wpa_printf(MSG_DEBUG,
564 : "httpread end chunks(%p)",
565 : h);
566 4 : break;
567 : }
568 7337 : h->in_chunk_data = 1;
569 : /* leave chunk_start alone */
570 : }
571 8553 : } else if (h->chunked) {
572 : /* in chunk data */
573 14734 : if ((h->body_nbytes - h->chunk_start) ==
574 7367 : (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 14673 : if (bbp[-1] == '\n' &&
582 7336 : bbp[-2] == '\r') {
583 : } else {
584 1 : wpa_printf(MSG_DEBUG,
585 : "httpread: Invalid chunk end");
586 1 : goto bad;
587 : }
588 7336 : h->body_nbytes -= 2;
589 7336 : bbp -= 2;
590 7336 : h->chunk_start = h->body_nbytes;
591 7336 : h->in_chunk_data = 0;
592 7336 : h->chunk_size = 0; /* just in case */
593 : }
594 2356 : } else if (h->got_content_length &&
595 1170 : h->body_nbytes >= h->content_length) {
596 543 : h->got_body = 1;
597 543 : wpa_printf(MSG_DEBUG,
598 : "httpread got content(%p)", h);
599 543 : goto got_file;
600 : }
601 30092 : if (nread <= 0)
602 96 : break;
603 : /* Now transfer. Optimize using memcpy where we can. */
604 29996 : 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 14704 : ncopy = (h->chunk_start + h->chunk_size + 2) -
609 7352 : h->body_nbytes;
610 22644 : } else if (h->chunked) {
611 : /*in chunk header -- don't optimize */
612 22051 : *bbp++ = *rbp++;
613 22051 : nread--;
614 22051 : h->body_nbytes++;
615 22051 : continue;
616 593 : } else if (h->got_content_length) {
617 585 : ncopy = h->content_length - h->body_nbytes;
618 : } else {
619 8 : ncopy = nread;
620 : }
621 : /* Note: should never be 0 */
622 7945 : if (ncopy < 0) {
623 0 : wpa_printf(MSG_DEBUG,
624 : "httpread: Invalid ncopy=%d", ncopy);
625 0 : goto bad;
626 : }
627 7945 : if (ncopy > nread)
628 57 : ncopy = nread;
629 7945 : os_memcpy(bbp, rbp, ncopy);
630 7945 : bbp += ncopy;
631 7945 : h->body_nbytes += ncopy;
632 7945 : rbp += ncopy;
633 7945 : nread -= ncopy;
634 29996 : } /* body copy loop */
635 : } /* !got_body */
636 100 : 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 14 : if (nread <= 0)
646 2 : break;
647 12 : c = *rbp++;
648 12 : nread--;
649 12 : switch (h->trailer_state) {
650 : case trailer_line_begin:
651 4 : if (c == '\r')
652 3 : h->trailer_state = trailer_empty_cr;
653 : else
654 1 : h->trailer_state = trailer_nonempty;
655 4 : break;
656 : case trailer_empty_cr:
657 : /* end empty line */
658 3 : if (c == '\n') {
659 2 : h->trailer_state = trailer_line_begin;
660 2 : h->in_trailer = 0;
661 2 : wpa_printf(MSG_DEBUG,
662 : "httpread got content(%p)",
663 : h);
664 2 : h->got_body = 1;
665 2 : goto got_file;
666 : }
667 1 : h->trailer_state = trailer_nonempty;
668 1 : break;
669 : case trailer_nonempty:
670 3 : if (c == '\r')
671 2 : h->trailer_state = trailer_nonempty_cr;
672 3 : break;
673 : case trailer_nonempty_cr:
674 2 : if (c == '\n')
675 1 : h->trailer_state = trailer_line_begin;
676 : else
677 1 : h->trailer_state = trailer_nonempty;
678 2 : break;
679 : }
680 10 : }
681 : }
682 98 : goto get_more;
683 :
684 : bad:
685 : /* Error */
686 24 : wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
687 24 : (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
688 24 : return;
689 :
690 : get_more:
691 116 : wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
692 116 : return;
693 :
694 : got_file:
695 1383 : wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d",
696 1383 : h->body_nbytes, h->hdr_type);
697 2766 : wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body",
698 2766 : h->body, h->body_nbytes);
699 : /* Null terminate for convenience of some applications */
700 1383 : if (h->body)
701 545 : h->body[h->body_nbytes] = 0; /* null terminate */
702 1383 : 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 1383 : eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
708 : /* The application can destroy us whenever they feel like...
709 : * cancel timeout.
710 : */
711 1383 : eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
712 1383 : (*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 : */
729 1415 : 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 1415 : struct httpread *h = NULL;
739 :
740 1415 : h = os_zalloc(sizeof(*h));
741 1415 : if (h == NULL)
742 2 : goto fail;
743 1413 : h->sd = sd;
744 1413 : h->cb = cb;
745 1413 : h->cookie = cookie;
746 1413 : h->max_bytes = max_bytes;
747 1413 : h->timeout_seconds = timeout_seconds;
748 :
749 2826 : if (timeout_seconds > 0 &&
750 1413 : eloop_register_timeout(timeout_seconds, 0,
751 : httpread_timeout_handler, NULL, h)) {
752 : /* No way to recover (from malloc failure) */
753 1 : goto fail;
754 : }
755 1412 : if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
756 : NULL, h)) {
757 : /* No way to recover (from malloc failure) */
758 1 : goto fail;
759 : }
760 1411 : return h;
761 :
762 : fail:
763 :
764 : /* Error */
765 4 : httpread_destroy(h);
766 4 : return NULL;
767 : }
768 :
769 :
770 : /* httpread_hdr_type_get -- When file is ready, returns header type. */
771 1383 : enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
772 : {
773 1383 : 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 : */
780 657 : char * httpread_uri_get(struct httpread *h)
781 : {
782 657 : return h->uri;
783 : }
784 :
785 :
786 : /* httpread_reply_code_get -- When reply is ready, returns reply code */
787 886 : int httpread_reply_code_get(struct httpread *h)
788 : {
789 886 : return h->reply_code;
790 : }
791 :
792 :
793 : /* httpread_length_get -- When file is ready, returns file length. */
794 128 : int httpread_length_get(struct httpread *h)
795 : {
796 128 : 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 : */
804 623 : void * httpread_data_get(struct httpread *h)
805 : {
806 623 : 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 : */
814 88 : char * httpread_hdr_get(struct httpread *h)
815 : {
816 88 : 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 : */
828 210 : char * httpread_hdr_line_get(struct httpread *h, const char *tag)
829 : {
830 210 : int tag_len = os_strlen(tag);
831 210 : char *hdr = h->hdr;
832 210 : hdr = os_strchr(hdr, '\n');
833 210 : if (hdr == NULL)
834 0 : return NULL;
835 210 : hdr++;
836 : for (;;) {
837 853 : if (!os_strncasecmp(hdr, tag, tag_len)) {
838 208 : hdr += tag_len;
839 594 : while (*hdr == ' ' || *hdr == '\t')
840 178 : hdr++;
841 208 : return hdr;
842 : }
843 645 : hdr = os_strchr(hdr, '\n');
844 645 : if (hdr == NULL)
845 2 : return NULL;
846 643 : hdr++;
847 643 : }
848 : }
|