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