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