$search
00001 /* 00002 * httpread - Manage reading file(s) from HTTP/TCP socket 00003 * Author: Ted Merrill 00004 * Copyright 2008 Atheros Communications 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License version 2 as 00008 * published by the Free Software Foundation. 00009 * 00010 * Alternatively, this software may be distributed under the terms of BSD 00011 * license. 00012 * 00013 * See README and COPYING for more details. 00014 * 00015 * The files are buffered via internal callbacks from eloop, then presented to 00016 * an application callback routine when completely read into memory. May also 00017 * be used if no file is expected but just to get the header, including HTTP 00018 * replies (e.g. HTTP/1.1 200 OK etc.). 00019 * 00020 * This does not attempt to be an optimally efficient implementation, but does 00021 * attempt to be of reasonably small size and memory consumption; assuming that 00022 * only small files are to be read. A maximum file size is provided by 00023 * application and enforced. 00024 * 00025 * It is assumed that the application does not expect any of the following: 00026 * -- transfer encoding other than chunked 00027 * -- trailer fields 00028 * It is assumed that, even if the other side requested that the connection be 00029 * kept open, that we will close it (thus HTTP messages sent by application 00030 * should have the connection closed field); this is allowed by HTTP/1.1 and 00031 * simplifies things for us. 00032 * 00033 * Other limitations: 00034 * -- HTTP header may not exceed a hard-coded size. 00035 * 00036 * Notes: 00037 * This code would be massively simpler without some of the new features of 00038 * HTTP/1.1, especially chunked data. 00039 */ 00040 00041 #include "includes.h" 00042 00043 #include "common.h" 00044 #include "eloop.h" 00045 #include "httpread.h" 00046 00047 00048 /* Tunable parameters */ 00049 #define HTTPREAD_READBUF_SIZE 1024 /* read in chunks of this size */ 00050 #define HTTPREAD_HEADER_MAX_SIZE 4096 /* max allowed for headers */ 00051 #define HTTPREAD_BODYBUF_DELTA 4096 /* increase allocation by this */ 00052 00053 #if 0 00054 /* httpread_debug -- set this global variable > 0 e.g. from debugger 00055 * to enable debugs (larger numbers for more debugs) 00056 * Make this a #define of 0 to eliminate the debugging code. 00057 */ 00058 int httpread_debug = 99; 00059 #else 00060 #define httpread_debug 0 /* eliminates even the debugging code */ 00061 #endif 00062 00063 00064 /* control instance -- actual definition (opaque to application) 00065 */ 00066 struct httpread { 00067 /* information from creation */ 00068 int sd; /* descriptor of TCP socket to read from */ 00069 void (*cb)(struct httpread *handle, void *cookie, 00070 enum httpread_event e); /* call on event */ 00071 void *cookie; /* pass to callback */ 00072 int max_bytes; /* maximum file size else abort it */ 00073 int timeout_seconds; /* 0 or total duration timeout period */ 00074 00075 /* dynamically used information follows */ 00076 int sd_registered; /* nonzero if we need to unregister socket */ 00077 int to_registered; /* nonzero if we need to unregister timeout */ 00078 00079 int got_hdr; /* nonzero when header is finalized */ 00080 char hdr[HTTPREAD_HEADER_MAX_SIZE+1]; /* headers stored here */ 00081 int hdr_nbytes; 00082 00083 enum httpread_hdr_type hdr_type; 00084 int version; /* 1 if we've seen 1.1 */ 00085 int reply_code; /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */ 00086 int got_content_length; /* true if we know content length for sure */ 00087 int content_length; /* body length, iff got_content_length */ 00088 int chunked; /* nonzero for chunked data */ 00089 char *uri; 00090 00091 int got_body; /* nonzero when body is finalized */ 00092 char *body; 00093 int body_nbytes; 00094 int body_alloc_nbytes; /* amount allocated */ 00095 00096 int got_file; /* here when we are done */ 00097 00098 /* The following apply if data is chunked: */ 00099 int in_chunk_data; /* 0=in/at header, 1=in the data or tail*/ 00100 int chunk_start; /* offset in body of chunk hdr or data */ 00101 int chunk_size; /* data of chunk (not hdr or ending CRLF)*/ 00102 int in_trailer; /* in header fields after data (chunked only)*/ 00103 enum trailer_state { 00104 trailer_line_begin = 0, 00105 trailer_empty_cr, /* empty line + CR */ 00106 trailer_nonempty, 00107 trailer_nonempty_cr, 00108 } trailer_state; 00109 }; 00110 00111 00112 /* Check words for equality, where words consist of graphical characters 00113 * delimited by whitespace 00114 * Returns nonzero if "equal" doing case insensitive comparison. 00115 */ 00116 static int word_eq(char *s1, char *s2) 00117 { 00118 int c1; 00119 int c2; 00120 int end1 = 0; 00121 int end2 = 0; 00122 for (;;) { 00123 c1 = *s1++; 00124 c2 = *s2++; 00125 if (isalpha(c1) && isupper(c1)) 00126 c1 = tolower(c1); 00127 if (isalpha(c2) && isupper(c2)) 00128 c2 = tolower(c2); 00129 end1 = !isgraph(c1); 00130 end2 = !isgraph(c2); 00131 if (end1 || end2 || c1 != c2) 00132 break; 00133 } 00134 return end1 && end2; /* reached end of both words? */ 00135 } 00136 00137 00138 /* convert hex to binary 00139 * Requires that c have been previously tested true with isxdigit(). 00140 */ 00141 static int hex_value(int c) 00142 { 00143 if (isdigit(c)) 00144 return c - '0'; 00145 if (islower(c)) 00146 return 10 + c - 'a'; 00147 return 10 + c - 'A'; 00148 } 00149 00150 00151 static void httpread_timeout_handler(void *eloop_data, void *user_ctx); 00152 00153 /* httpread_destroy -- if h is non-NULL, clean up 00154 * This must eventually be called by the application following 00155 * call of the application's callback and may be called 00156 * earlier if desired. 00157 */ 00158 void httpread_destroy(struct httpread *h) 00159 { 00160 if (httpread_debug >= 10) 00161 wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h); 00162 if (!h) 00163 return; 00164 00165 if (h->to_registered) 00166 eloop_cancel_timeout(httpread_timeout_handler, NULL, h); 00167 h->to_registered = 0; 00168 if (h->sd_registered) 00169 eloop_unregister_sock(h->sd, EVENT_TYPE_READ); 00170 h->sd_registered = 0; 00171 os_free(h->body); 00172 os_free(h->uri); 00173 os_memset(h, 0, sizeof(*h)); /* aid debugging */ 00174 h->sd = -1; /* aid debugging */ 00175 os_free(h); 00176 } 00177 00178 00179 /* httpread_timeout_handler -- called on excessive total duration 00180 */ 00181 static void httpread_timeout_handler(void *eloop_data, void *user_ctx) 00182 { 00183 struct httpread *h = user_ctx; 00184 wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h); 00185 h->to_registered = 0; /* is self-cancelling */ 00186 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT); 00187 } 00188 00189 00190 /* Analyze options only so far as is needed to correctly obtain the file. 00191 * The application can look at the raw header to find other options. 00192 */ 00193 static int httpread_hdr_option_analyze( 00194 struct httpread *h, 00195 char *hbp /* pointer to current line in header buffer */ 00196 ) 00197 { 00198 if (word_eq(hbp, "CONTENT-LENGTH:")) { 00199 while (isgraph(*hbp)) 00200 hbp++; 00201 while (*hbp == ' ' || *hbp == '\t') 00202 hbp++; 00203 if (!isdigit(*hbp)) 00204 return -1; 00205 h->content_length = atol(hbp); 00206 h->got_content_length = 1; 00207 return 0; 00208 } 00209 if (word_eq(hbp, "TRANSFER_ENCODING:") || 00210 word_eq(hbp, "TRANSFER-ENCODING:")) { 00211 while (isgraph(*hbp)) 00212 hbp++; 00213 while (*hbp == ' ' || *hbp == '\t') 00214 hbp++; 00215 /* There should (?) be no encodings of interest 00216 * other than chunked... 00217 */ 00218 if (word_eq(hbp, "CHUNKED")) { 00219 h->chunked = 1; 00220 h->in_chunk_data = 0; 00221 /* ignore possible ;<parameters> */ 00222 } 00223 return 0; 00224 } 00225 /* skip anything we don't know, which is a lot */ 00226 return 0; 00227 } 00228 00229 00230 static int httpread_hdr_analyze(struct httpread *h) 00231 { 00232 char *hbp = h->hdr; /* pointer into h->hdr */ 00233 int standard_first_line = 1; 00234 00235 /* First line is special */ 00236 h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN; 00237 if (!isgraph(*hbp)) 00238 goto bad; 00239 if (os_strncmp(hbp, "HTTP/", 5) == 0) { 00240 h->hdr_type = HTTPREAD_HDR_TYPE_REPLY; 00241 standard_first_line = 0; 00242 hbp += 5; 00243 if (hbp[0] == '1' && hbp[1] == '.' && 00244 isdigit(hbp[2]) && hbp[2] != '0') 00245 h->version = 1; 00246 while (isgraph(*hbp)) 00247 hbp++; 00248 while (*hbp == ' ' || *hbp == '\t') 00249 hbp++; 00250 if (!isdigit(*hbp)) 00251 goto bad; 00252 h->reply_code = atol(hbp); 00253 } else if (word_eq(hbp, "GET")) 00254 h->hdr_type = HTTPREAD_HDR_TYPE_GET; 00255 else if (word_eq(hbp, "HEAD")) 00256 h->hdr_type = HTTPREAD_HDR_TYPE_HEAD; 00257 else if (word_eq(hbp, "POST")) 00258 h->hdr_type = HTTPREAD_HDR_TYPE_POST; 00259 else if (word_eq(hbp, "PUT")) 00260 h->hdr_type = HTTPREAD_HDR_TYPE_PUT; 00261 else if (word_eq(hbp, "DELETE")) 00262 h->hdr_type = HTTPREAD_HDR_TYPE_DELETE; 00263 else if (word_eq(hbp, "TRACE")) 00264 h->hdr_type = HTTPREAD_HDR_TYPE_TRACE; 00265 else if (word_eq(hbp, "CONNECT")) 00266 h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT; 00267 else if (word_eq(hbp, "NOTIFY")) 00268 h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY; 00269 else if (word_eq(hbp, "M-SEARCH")) 00270 h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH; 00271 else if (word_eq(hbp, "M-POST")) 00272 h->hdr_type = HTTPREAD_HDR_TYPE_M_POST; 00273 else if (word_eq(hbp, "SUBSCRIBE")) 00274 h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE; 00275 else if (word_eq(hbp, "UNSUBSCRIBE")) 00276 h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE; 00277 else { 00278 } 00279 00280 if (standard_first_line) { 00281 char *rawuri; 00282 char *uri; 00283 /* skip type */ 00284 while (isgraph(*hbp)) 00285 hbp++; 00286 while (*hbp == ' ' || *hbp == '\t') 00287 hbp++; 00288 /* parse uri. 00289 * Find length, allocate memory for translated 00290 * copy, then translate by changing %<hex><hex> 00291 * into represented value. 00292 */ 00293 rawuri = hbp; 00294 while (isgraph(*hbp)) 00295 hbp++; 00296 h->uri = os_malloc((hbp - rawuri) + 1); 00297 if (h->uri == NULL) 00298 goto bad; 00299 uri = h->uri; 00300 while (rawuri < hbp) { 00301 int c = *rawuri; 00302 if (c == '%' && 00303 isxdigit(rawuri[1]) && isxdigit(rawuri[2])) { 00304 *uri++ = (hex_value(rawuri[1]) << 4) | 00305 hex_value(rawuri[2]); 00306 rawuri += 3; 00307 } else { 00308 *uri++ = c; 00309 rawuri++; 00310 } 00311 } 00312 *uri = 0; /* null terminate */ 00313 while (isgraph(*hbp)) 00314 hbp++; 00315 while (*hbp == ' ' || *hbp == '\t') 00316 hbp++; 00317 /* get version */ 00318 if (0 == strncmp(hbp, "HTTP/", 5)) { 00319 hbp += 5; 00320 if (hbp[0] == '1' && hbp[1] == '.' && 00321 isdigit(hbp[2]) && hbp[2] != '0') 00322 h->version = 1; 00323 } 00324 } 00325 /* skip rest of line */ 00326 while (*hbp) 00327 if (*hbp++ == '\n') 00328 break; 00329 00330 /* Remainder of lines are options, in any order; 00331 * or empty line to terminate 00332 */ 00333 for (;;) { 00334 /* Empty line to terminate */ 00335 if (hbp[0] == '\n' || 00336 (hbp[0] == '\r' && hbp[1] == '\n')) 00337 break; 00338 if (!isgraph(*hbp)) 00339 goto bad; 00340 if (httpread_hdr_option_analyze(h, hbp)) 00341 goto bad; 00342 /* skip line */ 00343 while (*hbp) 00344 if (*hbp++ == '\n') 00345 break; 00346 } 00347 00348 /* chunked overrides content-length always */ 00349 if (h->chunked) 00350 h->got_content_length = 0; 00351 00352 /* For some types, we should not try to read a body 00353 * This is in addition to the application determining 00354 * that we should not read a body. 00355 */ 00356 switch (h->hdr_type) { 00357 case HTTPREAD_HDR_TYPE_REPLY: 00358 /* Some codes can have a body and some not. 00359 * For now, just assume that any other than 200 00360 * do not... 00361 */ 00362 if (h->reply_code != 200) 00363 h->max_bytes = 0; 00364 break; 00365 case HTTPREAD_HDR_TYPE_GET: 00366 case HTTPREAD_HDR_TYPE_HEAD: 00367 /* in practice it appears that it is assumed 00368 * that GETs have a body length of 0... ? 00369 */ 00370 if (h->chunked == 0 && h->got_content_length == 0) 00371 h->max_bytes = 0; 00372 break; 00373 case HTTPREAD_HDR_TYPE_POST: 00374 case HTTPREAD_HDR_TYPE_PUT: 00375 case HTTPREAD_HDR_TYPE_DELETE: 00376 case HTTPREAD_HDR_TYPE_TRACE: 00377 case HTTPREAD_HDR_TYPE_CONNECT: 00378 case HTTPREAD_HDR_TYPE_NOTIFY: 00379 case HTTPREAD_HDR_TYPE_M_SEARCH: 00380 case HTTPREAD_HDR_TYPE_M_POST: 00381 case HTTPREAD_HDR_TYPE_SUBSCRIBE: 00382 case HTTPREAD_HDR_TYPE_UNSUBSCRIBE: 00383 default: 00384 break; 00385 } 00386 00387 return 0; 00388 00389 bad: 00390 /* Error */ 00391 return -1; 00392 } 00393 00394 00395 /* httpread_read_handler -- called when socket ready to read 00396 * 00397 * Note: any extra data we read past end of transmitted file is ignored; 00398 * if we were to support keeping connections open for multiple files then 00399 * this would have to be addressed. 00400 */ 00401 static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx) 00402 { 00403 struct httpread *h = sock_ctx; 00404 int nread; 00405 char *rbp; /* pointer into read buffer */ 00406 char *hbp; /* pointer into header buffer */ 00407 char *bbp; /* pointer into body buffer */ 00408 char readbuf[HTTPREAD_READBUF_SIZE]; /* temp use to read into */ 00409 00410 if (httpread_debug >= 20) 00411 wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h); 00412 00413 /* read some at a time, then search for the interal 00414 * boundaries between header and data and etc. 00415 */ 00416 nread = read(h->sd, readbuf, sizeof(readbuf)); 00417 if (nread < 0) 00418 goto bad; 00419 if (nread == 0) { 00420 /* end of transmission... this may be normal 00421 * or may be an error... in some cases we can't 00422 * tell which so we must assume it is normal then. 00423 */ 00424 if (!h->got_hdr) { 00425 /* Must at least have completed header */ 00426 wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h); 00427 goto bad; 00428 } 00429 if (h->chunked || h->got_content_length) { 00430 /* Premature EOF; e.g. dropped connection */ 00431 wpa_printf(MSG_DEBUG, 00432 "httpread premature eof(%p) %d/%d", 00433 h, h->body_nbytes, 00434 h->content_length); 00435 goto bad; 00436 } 00437 /* No explicit length, hopefully we have all the data 00438 * although dropped connections can cause false 00439 * end 00440 */ 00441 if (httpread_debug >= 10) 00442 wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h); 00443 h->got_body = 1; 00444 goto got_file; 00445 } 00446 rbp = readbuf; 00447 00448 /* Header consists of text lines (terminated by both CR and LF) 00449 * and an empty line (CR LF only). 00450 */ 00451 if (!h->got_hdr) { 00452 hbp = h->hdr + h->hdr_nbytes; 00453 /* add to headers until: 00454 * -- we run out of data in read buffer 00455 * -- or, we run out of header buffer room 00456 * -- or, we get double CRLF in headers 00457 */ 00458 for (;;) { 00459 if (nread == 0) 00460 goto get_more; 00461 if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) { 00462 goto bad; 00463 } 00464 *hbp++ = *rbp++; 00465 nread--; 00466 h->hdr_nbytes++; 00467 if (h->hdr_nbytes >= 4 && 00468 hbp[-1] == '\n' && 00469 hbp[-2] == '\r' && 00470 hbp[-3] == '\n' && 00471 hbp[-4] == '\r' ) { 00472 h->got_hdr = 1; 00473 *hbp = 0; /* null terminate */ 00474 break; 00475 } 00476 } 00477 /* here we've just finished reading the header */ 00478 if (httpread_hdr_analyze(h)) { 00479 wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h); 00480 goto bad; 00481 } 00482 if (h->max_bytes == 0) { 00483 if (httpread_debug >= 10) 00484 wpa_printf(MSG_DEBUG, 00485 "httpread no body hdr end(%p)", h); 00486 goto got_file; 00487 } 00488 if (h->got_content_length && h->content_length == 0) { 00489 if (httpread_debug >= 10) 00490 wpa_printf(MSG_DEBUG, 00491 "httpread zero content length(%p)", 00492 h); 00493 goto got_file; 00494 } 00495 } 00496 00497 /* Certain types of requests never have data and so 00498 * must be specially recognized. 00499 */ 00500 if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) || 00501 !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) || 00502 !os_strncasecmp(h->hdr, "HEAD", 4) || 00503 !os_strncasecmp(h->hdr, "GET", 3)) { 00504 if (!h->got_body) { 00505 if (httpread_debug >= 10) 00506 wpa_printf(MSG_DEBUG, 00507 "httpread NO BODY for sp. type"); 00508 } 00509 h->got_body = 1; 00510 goto got_file; 00511 } 00512 00513 /* Data can be just plain binary data, or if "chunked" 00514 * consists of chunks each with a header, ending with 00515 * an ending header. 00516 */ 00517 if (nread == 0) 00518 goto get_more; 00519 if (!h->got_body) { 00520 /* Here to get (more of) body */ 00521 /* ensure we have enough room for worst case for body 00522 * plus a null termination character 00523 */ 00524 if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) { 00525 char *new_body; 00526 int new_alloc_nbytes; 00527 00528 if (h->body_nbytes >= h->max_bytes) 00529 goto bad; 00530 new_alloc_nbytes = h->body_alloc_nbytes + 00531 HTTPREAD_BODYBUF_DELTA; 00532 /* For content-length case, the first time 00533 * through we allocate the whole amount 00534 * we need. 00535 */ 00536 if (h->got_content_length && 00537 new_alloc_nbytes < (h->content_length + 1)) 00538 new_alloc_nbytes = h->content_length + 1; 00539 if ((new_body = os_realloc(h->body, new_alloc_nbytes)) 00540 == NULL) 00541 goto bad; 00542 00543 h->body = new_body; 00544 h->body_alloc_nbytes = new_alloc_nbytes; 00545 } 00546 /* add bytes */ 00547 bbp = h->body + h->body_nbytes; 00548 for (;;) { 00549 int ncopy; 00550 /* See if we need to stop */ 00551 if (h->chunked && h->in_chunk_data == 0) { 00552 /* in chunk header */ 00553 char *cbp = h->body + h->chunk_start; 00554 if (bbp-cbp >= 2 && bbp[-2] == '\r' && 00555 bbp[-1] == '\n') { 00556 /* end of chunk hdr line */ 00557 /* hdr line consists solely 00558 * of a hex numeral and CFLF 00559 */ 00560 if (!isxdigit(*cbp)) 00561 goto bad; 00562 h->chunk_size = strtoul(cbp, NULL, 16); 00563 /* throw away chunk header 00564 * so we have only real data 00565 */ 00566 h->body_nbytes = h->chunk_start; 00567 bbp = cbp; 00568 if (h->chunk_size == 0) { 00569 /* end of chunking */ 00570 /* trailer follows */ 00571 h->in_trailer = 1; 00572 if (httpread_debug >= 20) 00573 wpa_printf( 00574 MSG_DEBUG, 00575 "httpread end chunks(%p)", h); 00576 break; 00577 } 00578 h->in_chunk_data = 1; 00579 /* leave chunk_start alone */ 00580 } 00581 } else if (h->chunked) { 00582 /* in chunk data */ 00583 if ((h->body_nbytes - h->chunk_start) == 00584 (h->chunk_size + 2)) { 00585 /* end of chunk reached, 00586 * new chunk starts 00587 */ 00588 /* check chunk ended w/ CRLF 00589 * which we'll throw away 00590 */ 00591 if (bbp[-1] == '\n' && 00592 bbp[-2] == '\r') { 00593 } else 00594 goto bad; 00595 h->body_nbytes -= 2; 00596 bbp -= 2; 00597 h->chunk_start = h->body_nbytes; 00598 h->in_chunk_data = 0; 00599 h->chunk_size = 0; /* just in case */ 00600 } 00601 } else if (h->got_content_length && 00602 h->body_nbytes >= h->content_length) { 00603 h->got_body = 1; 00604 if (httpread_debug >= 10) 00605 wpa_printf( 00606 MSG_DEBUG, 00607 "httpread got content(%p)", h); 00608 goto got_file; 00609 } 00610 if (nread <= 0) 00611 break; 00612 /* Now transfer. Optimize using memcpy where we can. */ 00613 if (h->chunked && h->in_chunk_data) { 00614 /* copy up to remainder of chunk data 00615 * plus the required CR+LF at end 00616 */ 00617 ncopy = (h->chunk_start + h->chunk_size + 2) - 00618 h->body_nbytes; 00619 } else if (h->chunked) { 00620 /*in chunk header -- don't optimize */ 00621 *bbp++ = *rbp++; 00622 nread--; 00623 h->body_nbytes++; 00624 continue; 00625 } else if (h->got_content_length) { 00626 ncopy = h->content_length - h->body_nbytes; 00627 } else { 00628 ncopy = nread; 00629 } 00630 /* Note: should never be 0 */ 00631 if (ncopy > nread) 00632 ncopy = nread; 00633 os_memcpy(bbp, rbp, ncopy); 00634 bbp += ncopy; 00635 h->body_nbytes += ncopy; 00636 rbp += ncopy; 00637 nread -= ncopy; 00638 } /* body copy loop */ 00639 } /* !got_body */ 00640 if (h->chunked && h->in_trailer) { 00641 /* If "chunked" then there is always a trailer, 00642 * consisting of zero or more non-empty lines 00643 * ending with CR LF and then an empty line w/ CR LF. 00644 * We do NOT support trailers except to skip them -- 00645 * this is supported (generally) by the http spec. 00646 */ 00647 bbp = h->body + h->body_nbytes; 00648 for (;;) { 00649 int c; 00650 if (nread <= 0) 00651 break; 00652 c = *rbp++; 00653 nread--; 00654 switch (h->trailer_state) { 00655 case trailer_line_begin: 00656 if (c == '\r') 00657 h->trailer_state = trailer_empty_cr; 00658 else 00659 h->trailer_state = trailer_nonempty; 00660 break; 00661 case trailer_empty_cr: 00662 /* end empty line */ 00663 if (c == '\n') { 00664 h->trailer_state = trailer_line_begin; 00665 h->in_trailer = 0; 00666 if (httpread_debug >= 10) 00667 wpa_printf( 00668 MSG_DEBUG, 00669 "httpread got content(%p)", h); 00670 h->got_body = 1; 00671 goto got_file; 00672 } 00673 h->trailer_state = trailer_nonempty; 00674 break; 00675 case trailer_nonempty: 00676 if (c == '\r') 00677 h->trailer_state = trailer_nonempty_cr; 00678 break; 00679 case trailer_nonempty_cr: 00680 if (c == '\n') 00681 h->trailer_state = trailer_line_begin; 00682 else 00683 h->trailer_state = trailer_nonempty; 00684 break; 00685 } 00686 } 00687 } 00688 goto get_more; 00689 00690 bad: 00691 /* Error */ 00692 wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h); 00693 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR); 00694 return; 00695 00696 get_more: 00697 return; 00698 00699 got_file: 00700 if (httpread_debug >= 10) 00701 wpa_printf(MSG_DEBUG, 00702 "httpread got file %d bytes type %d", 00703 h->body_nbytes, h->hdr_type); 00704 /* Null terminate for convenience of some applications */ 00705 if (h->body) 00706 h->body[h->body_nbytes] = 0; /* null terminate */ 00707 h->got_file = 1; 00708 /* Assume that we do NOT support keeping connection alive, 00709 * and just in case somehow we don't get destroyed right away, 00710 * unregister now. 00711 */ 00712 if (h->sd_registered) 00713 eloop_unregister_sock(h->sd, EVENT_TYPE_READ); 00714 h->sd_registered = 0; 00715 /* The application can destroy us whenever they feel like... 00716 * cancel timeout. 00717 */ 00718 if (h->to_registered) 00719 eloop_cancel_timeout(httpread_timeout_handler, NULL, h); 00720 h->to_registered = 0; 00721 (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY); 00722 } 00723 00724 00725 /* httpread_create -- start a new reading session making use of eloop. 00726 * The new instance will use the socket descriptor for reading (until 00727 * it gets a file and not after) but will not close the socket, even 00728 * when the instance is destroyed (the application must do that). 00729 * Return NULL on error. 00730 * 00731 * Provided that httpread_create successfully returns a handle, 00732 * the callback fnc is called to handle httpread_event events. 00733 * The caller should do destroy on any errors or unknown events. 00734 * 00735 * Pass max_bytes == 0 to not read body at all (required for e.g. 00736 * reply to HEAD request). 00737 */ 00738 struct httpread * httpread_create( 00739 int sd, /* descriptor of TCP socket to read from */ 00740 void (*cb)(struct httpread *handle, void *cookie, 00741 enum httpread_event e), /* call on event */ 00742 void *cookie, /* pass to callback */ 00743 int max_bytes, /* maximum body size else abort it */ 00744 int timeout_seconds /* 0; or total duration timeout period */ 00745 ) 00746 { 00747 struct httpread *h = NULL; 00748 00749 h = os_zalloc(sizeof(*h)); 00750 if (h == NULL) 00751 goto fail; 00752 h->sd = sd; 00753 h->cb = cb; 00754 h->cookie = cookie; 00755 h->max_bytes = max_bytes; 00756 h->timeout_seconds = timeout_seconds; 00757 00758 if (timeout_seconds > 0) { 00759 if (eloop_register_timeout(timeout_seconds, 0, 00760 httpread_timeout_handler, 00761 NULL, h)) { 00762 /* No way to recover (from malloc failure) */ 00763 goto fail; 00764 } 00765 h->to_registered = 1; 00766 } 00767 if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler, 00768 NULL, h)) { 00769 /* No way to recover (from malloc failure) */ 00770 goto fail; 00771 } 00772 h->sd_registered = 1; 00773 return h; 00774 00775 fail: 00776 00777 /* Error */ 00778 httpread_destroy(h); 00779 return NULL; 00780 } 00781 00782 00783 /* httpread_hdr_type_get -- When file is ready, returns header type. */ 00784 enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h) 00785 { 00786 return h->hdr_type; 00787 } 00788 00789 00790 /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI 00791 * or possibly NULL (which would be an error). 00792 */ 00793 char * httpread_uri_get(struct httpread *h) 00794 { 00795 return h->uri; 00796 } 00797 00798 00799 /* httpread_reply_code_get -- When reply is ready, returns reply code */ 00800 int httpread_reply_code_get(struct httpread *h) 00801 { 00802 return h->reply_code; 00803 } 00804 00805 00806 /* httpread_length_get -- When file is ready, returns file length. */ 00807 int httpread_length_get(struct httpread *h) 00808 { 00809 return h->body_nbytes; 00810 } 00811 00812 00813 /* httpread_data_get -- When file is ready, returns file content 00814 * with null byte appened. 00815 * Might return NULL in some error condition. 00816 */ 00817 void * httpread_data_get(struct httpread *h) 00818 { 00819 return h->body ? h->body : ""; 00820 } 00821 00822 00823 /* httpread_hdr_get -- When file is ready, returns header content 00824 * with null byte appended. 00825 * Might return NULL in some error condition. 00826 */ 00827 char * httpread_hdr_get(struct httpread *h) 00828 { 00829 return h->hdr; 00830 } 00831 00832 00833 /* httpread_hdr_line_get -- When file is ready, returns pointer 00834 * to line within header content matching the given tag 00835 * (after the tag itself and any spaces/tabs). 00836 * 00837 * The tag should end with a colon for reliable matching. 00838 * 00839 * If not found, returns NULL; 00840 */ 00841 char * httpread_hdr_line_get(struct httpread *h, const char *tag) 00842 { 00843 int tag_len = os_strlen(tag); 00844 char *hdr = h->hdr; 00845 hdr = os_strchr(hdr, '\n'); 00846 if (hdr == NULL) 00847 return NULL; 00848 hdr++; 00849 for (;;) { 00850 if (!os_strncasecmp(hdr, tag, tag_len)) { 00851 hdr += tag_len; 00852 while (*hdr == ' ' || *hdr == '\t') 00853 hdr++; 00854 return hdr; 00855 } 00856 hdr = os_strchr(hdr, '\n'); 00857 if (hdr == NULL) 00858 return NULL; 00859 hdr++; 00860 } 00861 }