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 24219 : static int word_eq(char *s1, char *s2)
99 : {
100 : int c1;
101 : int c2;
102 24219 : int end1 = 0;
103 24219 : int end2 = 0;
104 : for (;;) {
105 59558 : c1 = *s1++;
106 59558 : c2 = *s2++;
107 59558 : if (isalpha(c1) && isupper(c1))
108 30403 : c1 = tolower(c1);
109 59558 : if (isalpha(c2) && isupper(c2))
110 54031 : c2 = tolower(c2);
111 59558 : end1 = !isgraph(c1);
112 59558 : end2 = !isgraph(c2);
113 59558 : if (end1 || end2 || c1 != c2)
114 : break;
115 35339 : }
116 24219 : 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 1554 : void httpread_destroy(struct httpread *h)
128 : {
129 1554 : wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h);
130 1554 : if (!h)
131 1691 : return;
132 :
133 1417 : eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
134 1417 : eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
135 1417 : os_free(h->body);
136 1417 : os_free(h->uri);
137 1417 : os_memset(h, 0, sizeof(*h)); /* aid debugging */
138 1417 : h->sd = -1; /* aid debugging */
139 1417 : 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 7956 : static int httpread_hdr_option_analyze(
157 : struct httpread *h,
158 : char *hbp /* pointer to current line in header buffer */
159 : )
160 : {
161 7956 : if (word_eq(hbp, "CONTENT-LENGTH:")) {
162 22049 : while (isgraph(*hbp))
163 19455 : hbp++;
164 3891 : while (*hbp == ' ' || *hbp == '\t')
165 1297 : hbp++;
166 1297 : if (!isdigit(*hbp))
167 2 : return -1;
168 1295 : h->content_length = atol(hbp);
169 1295 : 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 1293 : h->got_content_length = 1;
176 1293 : return 0;
177 : }
178 13318 : if (word_eq(hbp, "TRANSFER_ENCODING:") ||
179 6659 : 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 6648 : return 0;
196 : }
197 :
198 :
199 1406 : static int httpread_hdr_analyze(struct httpread *h)
200 : {
201 1406 : char *hbp = h->hdr; /* pointer into h->hdr */
202 1406 : int standard_first_line = 1;
203 :
204 : /* First line is special */
205 1406 : h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
206 1406 : if (!isgraph(*hbp))
207 1 : goto bad;
208 1405 : if (os_strncmp(hbp, "HTTP/", 5) == 0) {
209 913 : h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
210 913 : standard_first_line = 0;
211 913 : hbp += 5;
212 1825 : if (hbp[0] == '1' && hbp[1] == '.' &&
213 1824 : isdigit(hbp[2]) && hbp[2] != '0')
214 912 : h->version = 1;
215 4562 : while (isgraph(*hbp))
216 2736 : hbp++;
217 2738 : while (*hbp == ' ' || *hbp == '\t')
218 912 : hbp++;
219 913 : if (!isdigit(*hbp))
220 1 : goto bad;
221 912 : h->reply_code = atol(hbp);
222 492 : } else if (word_eq(hbp, "GET"))
223 48 : h->hdr_type = HTTPREAD_HDR_TYPE_GET;
224 444 : else if (word_eq(hbp, "HEAD"))
225 7 : h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
226 437 : else if (word_eq(hbp, "POST"))
227 182 : h->hdr_type = HTTPREAD_HDR_TYPE_POST;
228 255 : else if (word_eq(hbp, "PUT"))
229 1 : h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
230 254 : else if (word_eq(hbp, "DELETE"))
231 1 : h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
232 253 : else if (word_eq(hbp, "TRACE"))
233 1 : h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
234 252 : else if (word_eq(hbp, "CONNECT"))
235 1 : h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
236 251 : else if (word_eq(hbp, "NOTIFY"))
237 162 : h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
238 89 : else if (word_eq(hbp, "M-SEARCH"))
239 1 : h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
240 88 : else if (word_eq(hbp, "M-POST"))
241 1 : h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
242 87 : else if (word_eq(hbp, "SUBSCRIBE"))
243 55 : h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
244 32 : else if (word_eq(hbp, "UNSUBSCRIBE"))
245 29 : h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
246 : else {
247 : }
248 :
249 1404 : if (standard_first_line) {
250 : char *rawuri;
251 : char *uri;
252 : /* skip type */
253 3724 : while (isgraph(*hbp))
254 2740 : hbp++;
255 1476 : while (*hbp == ' ' || *hbp == '\t')
256 492 : 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 492 : rawuri = hbp;
263 10345 : while (isgraph(*hbp))
264 9361 : hbp++;
265 492 : h->uri = os_malloc((hbp - rawuri) + 1);
266 492 : if (h->uri == NULL)
267 1 : goto bad;
268 491 : uri = h->uri;
269 10340 : while (rawuri < hbp) {
270 9358 : int c = *rawuri;
271 9362 : if (c == '%' &&
272 6 : isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
273 1 : *uri++ = hex2byte(rawuri + 1);
274 1 : rawuri += 3;
275 : } else {
276 9357 : *uri++ = c;
277 9357 : rawuri++;
278 : }
279 : }
280 491 : *uri = 0; /* null terminate */
281 1473 : while (*hbp == ' ' || *hbp == '\t')
282 491 : hbp++;
283 : /* get version */
284 491 : if (0 == strncmp(hbp, "HTTP/", 5)) {
285 491 : hbp += 5;
286 982 : if (hbp[0] == '1' && hbp[1] == '.' &&
287 982 : isdigit(hbp[2]) && hbp[2] != '0')
288 484 : h->version = 1;
289 : }
290 : }
291 : /* skip rest of line */
292 11185 : while (*hbp)
293 9782 : if (*hbp++ == '\n')
294 1403 : 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 18710 : if (hbp[0] == '\n' ||
302 10753 : (hbp[0] == '\r' && hbp[1] == '\n'))
303 : break;
304 7957 : if (!isgraph(*hbp))
305 1 : goto bad;
306 7956 : if (httpread_hdr_option_analyze(h, hbp))
307 4 : goto bad;
308 : /* skip line */
309 263753 : while (*hbp)
310 255801 : if (*hbp++ == '\n')
311 7952 : break;
312 7952 : }
313 :
314 : /* chunked overrides content-length always */
315 1398 : 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 1398 : 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 912 : if (h->reply_code != 200)
329 9 : h->max_bytes = 0;
330 912 : 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 50 : if (h->chunked == 0 && h->got_content_length == 0)
337 48 : h->max_bytes = 0;
338 50 : 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 436 : break;
351 : }
352 :
353 1398 : 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 1526 : static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
368 : {
369 1526 : 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 1526 : wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h);
380 1526 : nread = read(h->sd, readbuf, sizeof(readbuf));
381 1526 : if (nread < 0) {
382 1 : wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno));
383 1 : goto bad;
384 : }
385 1525 : wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread);
386 1525 : 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 1512 : 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 1512 : if (!h->got_hdr) {
418 1419 : 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 300486 : if (nread == 0)
426 11 : goto get_more;
427 300475 : 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 300473 : *hbp++ = *rbp++;
433 300473 : nread--;
434 300473 : h->hdr_nbytes++;
435 596721 : if (h->hdr_nbytes >= 4 &&
436 307018 : hbp[-1] == '\n' &&
437 21540 : hbp[-2] == '\r' &&
438 12176 : hbp[-3] == '\n' &&
439 1406 : hbp[-4] == '\r' ) {
440 1406 : h->got_hdr = 1;
441 1406 : *hbp = 0; /* null terminate */
442 1406 : break;
443 : }
444 299067 : }
445 : /* here we've just finished reading the header */
446 1406 : if (httpread_hdr_analyze(h)) {
447 8 : wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
448 8 : goto bad;
449 : }
450 1398 : if (h->max_bytes == 0) {
451 711 : wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)",
452 : h);
453 711 : goto got_file;
454 : }
455 687 : if (h->got_content_length && h->content_length == 0) {
456 51 : wpa_printf(MSG_DEBUG,
457 : "httpread zero content length(%p)", h);
458 51 : goto got_file;
459 : }
460 : }
461 :
462 : /* Certain types of requests never have data and so
463 : * must be specially recognized.
464 : */
465 1403 : if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
466 1320 : !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
467 1290 : !os_strncasecmp(h->hdr, "HEAD", 4) ||
468 644 : !os_strncasecmp(h->hdr, "GET", 3)) {
469 85 : if (!h->got_body) {
470 85 : wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type");
471 : }
472 85 : h->got_body = 1;
473 85 : 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 644 : if (nread == 0)
481 7 : goto get_more;
482 637 : 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 637 : if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
488 : char *new_body;
489 : int new_alloc_nbytes;
490 :
491 548 : 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 547 : 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 1080 : if (h->got_content_length &&
504 533 : new_alloc_nbytes < (h->content_length + 1))
505 1 : new_alloc_nbytes = h->content_length + 1;
506 1094 : if (new_alloc_nbytes < h->body_alloc_nbytes ||
507 547 : 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 546 : 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 545 : h->body = new_body;
525 545 : h->body_alloc_nbytes = new_alloc_nbytes;
526 : }
527 : /* add bytes */
528 634 : bbp = h->body + h->body_nbytes;
529 : for (;;) {
530 : int ncopy;
531 : /* See if we need to stop */
532 51660 : if (h->chunked && h->in_chunk_data == 0) {
533 : /* in chunk header */
534 21643 : char *cbp = h->body + h->chunk_start;
535 28839 : if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
536 7196 : bbp[-1] == '\n') {
537 : /* end of chunk hdr line */
538 : /* hdr line consists solely
539 : * of a hex numeral and CFLF
540 : */
541 7196 : 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 7195 : h->chunk_size = strtoul(cbp, NULL, 16);
547 14389 : if (h->chunk_size < 0 ||
548 7194 : 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 7193 : h->body_nbytes = h->chunk_start;
558 7193 : bbp = cbp;
559 7193 : 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 7189 : h->in_chunk_data = 1;
569 : /* leave chunk_start alone */
570 : }
571 8381 : } else if (h->chunked) {
572 : /* in chunk data */
573 14434 : if ((h->body_nbytes - h->chunk_start) ==
574 7217 : (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 14377 : if (bbp[-1] == '\n' &&
582 7188 : bbp[-2] == '\r') {
583 : } else {
584 1 : wpa_printf(MSG_DEBUG,
585 : "httpread: Invalid chunk end");
586 1 : goto bad;
587 : }
588 7188 : h->body_nbytes -= 2;
589 7188 : bbp -= 2;
590 7188 : h->chunk_start = h->body_nbytes;
591 7188 : h->in_chunk_data = 0;
592 7188 : h->chunk_size = 0; /* just in case */
593 : }
594 2312 : } else if (h->got_content_length &&
595 1148 : h->body_nbytes >= h->content_length) {
596 532 : h->got_body = 1;
597 532 : wpa_printf(MSG_DEBUG,
598 : "httpread got content(%p)", h);
599 532 : goto got_file;
600 : }
601 29484 : if (nread <= 0)
602 94 : break;
603 : /* Now transfer. Optimize using memcpy where we can. */
604 29390 : 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 14406 : ncopy = (h->chunk_start + h->chunk_size + 2) -
609 7203 : h->body_nbytes;
610 22187 : } else if (h->chunked) {
611 : /*in chunk header -- don't optimize */
612 21605 : *bbp++ = *rbp++;
613 21605 : nread--;
614 21605 : h->body_nbytes++;
615 21605 : continue;
616 582 : } else if (h->got_content_length) {
617 574 : ncopy = h->content_length - h->body_nbytes;
618 : } else {
619 8 : ncopy = nread;
620 : }
621 : /* Note: should never be 0 */
622 7785 : if (ncopy < 0) {
623 0 : wpa_printf(MSG_DEBUG,
624 : "httpread: Invalid ncopy=%d", ncopy);
625 0 : goto bad;
626 : }
627 7785 : if (ncopy > nread)
628 56 : ncopy = nread;
629 7785 : os_memcpy(bbp, rbp, ncopy);
630 7785 : bbp += ncopy;
631 7785 : h->body_nbytes += ncopy;
632 7785 : rbp += ncopy;
633 7785 : nread -= ncopy;
634 29390 : } /* body copy loop */
635 : } /* !got_body */
636 98 : 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 96 : 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 114 : wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
692 114 : return;
693 :
694 : got_file:
695 1388 : wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d",
696 1388 : h->body_nbytes, h->hdr_type);
697 2776 : wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body",
698 2776 : h->body, h->body_nbytes);
699 : /* Null terminate for convenience of some applications */
700 1388 : if (h->body)
701 534 : h->body[h->body_nbytes] = 0; /* null terminate */
702 1388 : 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 1388 : eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
708 : /* The application can destroy us whenever they feel like...
709 : * cancel timeout.
710 : */
711 1388 : eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
712 1388 : (*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 1419 : 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 1419 : struct httpread *h = NULL;
739 :
740 1419 : h = os_zalloc(sizeof(*h));
741 1419 : if (h == NULL)
742 2 : goto fail;
743 1417 : h->sd = sd;
744 1417 : h->cb = cb;
745 1417 : h->cookie = cookie;
746 1417 : h->max_bytes = max_bytes;
747 1417 : h->timeout_seconds = timeout_seconds;
748 :
749 2834 : if (timeout_seconds > 0 &&
750 1417 : 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 1416 : 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 1415 : 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 1388 : enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
772 : {
773 1388 : 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 636 : char * httpread_uri_get(struct httpread *h)
781 : {
782 636 : return h->uri;
783 : }
784 :
785 :
786 : /* httpread_reply_code_get -- When reply is ready, returns reply code */
787 911 : int httpread_reply_code_get(struct httpread *h)
788 : {
789 911 : 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 611 : void * httpread_data_get(struct httpread *h)
805 : {
806 611 : 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 84 : char * httpread_hdr_get(struct httpread *h)
815 : {
816 84 : 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 200 : char * httpread_hdr_line_get(struct httpread *h, const char *tag)
829 : {
830 200 : int tag_len = os_strlen(tag);
831 200 : char *hdr = h->hdr;
832 200 : hdr = os_strchr(hdr, '\n');
833 200 : if (hdr == NULL)
834 0 : return NULL;
835 200 : hdr++;
836 : for (;;) {
837 813 : if (!os_strncasecmp(hdr, tag, tag_len)) {
838 198 : hdr += tag_len;
839 564 : while (*hdr == ' ' || *hdr == '\t')
840 168 : hdr++;
841 198 : return hdr;
842 : }
843 615 : hdr = os_strchr(hdr, '\n');
844 615 : if (hdr == NULL)
845 2 : return NULL;
846 613 : hdr++;
847 613 : }
848 : }
|