httpread.c
Go to the documentation of this file.
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 }


wpa_supplicant
Author(s): Package maintained by Blaise Gassend
autogenerated on Thu Apr 24 2014 15:34:35