d6c2b62ae1b6c64b454b647f4b7f50648238be63
[mech_eap.git] / src / wps / httpread.c
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 (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
357 bad:
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  */
369 static 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                             HTTPREAD_BODYBUF_DELTA) {
511                                 wpa_printf(MSG_DEBUG,
512                                            "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)",
513                                            new_alloc_nbytes,
514                                            h->body_alloc_nbytes,
515                                            h->max_bytes);
516                                 goto bad;
517                         }
518                         if ((new_body = os_realloc(h->body, new_alloc_nbytes))
519                             == NULL) {
520                                 wpa_printf(MSG_DEBUG,
521                                            "httpread: Failed to reallocate buffer (len=%d)",
522                                            new_alloc_nbytes);
523                                 goto bad;
524                         }
525
526                         h->body = new_body;
527                         h->body_alloc_nbytes = new_alloc_nbytes;
528                 }
529                 /* add bytes */
530                 bbp = h->body + h->body_nbytes;
531                 for (;;) {
532                         int ncopy;
533                         /* See if we need to stop */
534                         if (h->chunked && h->in_chunk_data == 0) {
535                                 /* in chunk header */
536                                 char *cbp = h->body + h->chunk_start;
537                                 if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
538                                     bbp[-1] == '\n') {
539                                         /* end of chunk hdr line */
540                                         /* hdr line consists solely
541                                          * of a hex numeral and CFLF
542                                          */
543                                         if (!isxdigit(*cbp)) {
544                                                 wpa_printf(MSG_DEBUG,
545                                                            "httpread: Unexpected chunk header value (not a hex digit)");
546                                                 goto bad;
547                                         }
548                                         h->chunk_size = strtoul(cbp, NULL, 16);
549                                         if (h->chunk_size < 0 ||
550                                             h->chunk_size > h->max_bytes) {
551                                                 wpa_printf(MSG_DEBUG,
552                                                            "httpread: Invalid chunk size %d",
553                                                            h->chunk_size);
554                                                 goto bad;
555                                         }
556                                         /* throw away chunk header
557                                          * so we have only real data
558                                          */
559                                         h->body_nbytes = h->chunk_start;
560                                         bbp = cbp;
561                                         if (h->chunk_size == 0) {
562                                                 /* end of chunking */
563                                                 /* trailer follows */
564                                                 h->in_trailer = 1;
565                                                 wpa_printf(MSG_DEBUG,
566                                                            "httpread end chunks(%p)",
567                                                            h);
568                                                 break;
569                                         }
570                                         h->in_chunk_data = 1;
571                                         /* leave chunk_start alone */
572                                 }
573                         } else if (h->chunked) {
574                                 /* in chunk data */
575                                 if ((h->body_nbytes - h->chunk_start) ==
576                                     (h->chunk_size + 2)) {
577                                         /* end of chunk reached,
578                                          * new chunk starts
579                                          */
580                                         /* check chunk ended w/ CRLF
581                                          * which we'll throw away
582                                          */
583                                         if (bbp[-1] == '\n' &&
584                                             bbp[-2] == '\r') {
585                                         } else {
586                                                 wpa_printf(MSG_DEBUG,
587                                                            "httpread: Invalid chunk end");
588                                                 goto bad;
589                                         }
590                                         h->body_nbytes -= 2;
591                                         bbp -= 2;
592                                         h->chunk_start = h->body_nbytes;
593                                         h->in_chunk_data = 0;
594                                         h->chunk_size = 0; /* just in case */
595                                 }
596                         } else if (h->got_content_length &&
597                                    h->body_nbytes >= h->content_length) {
598                                 h->got_body = 1;
599                                 wpa_printf(MSG_DEBUG,
600                                            "httpread got content(%p)", h);
601                                 goto got_file;
602                         }
603                         if (nread <= 0)
604                                 break;
605                         /* Now transfer. Optimize using memcpy where we can. */
606                         if (h->chunked && h->in_chunk_data) {
607                                 /* copy up to remainder of chunk data
608                                  * plus the required CR+LF at end
609                                  */
610                                 ncopy = (h->chunk_start + h->chunk_size + 2) -
611                                         h->body_nbytes;
612                         } else if (h->chunked) {
613                                 /*in chunk header -- don't optimize */
614                                 *bbp++ = *rbp++;
615                                 nread--;
616                                 h->body_nbytes++;
617                                 continue;
618                         } else if (h->got_content_length) {
619                                 ncopy = h->content_length - h->body_nbytes;
620                         } else {
621                                 ncopy = nread;
622                         }
623                         /* Note: should never be 0 */
624                         if (ncopy < 0) {
625                                 wpa_printf(MSG_DEBUG,
626                                            "httpread: Invalid ncopy=%d", ncopy);
627                                 goto bad;
628                         }
629                         if (ncopy > nread)
630                                 ncopy = nread;
631                         os_memcpy(bbp, rbp, ncopy);
632                         bbp += ncopy;
633                         h->body_nbytes += ncopy;
634                         rbp += ncopy;
635                         nread -= ncopy;
636                 }       /* body copy loop */
637         }       /* !got_body */
638         if (h->chunked && h->in_trailer) {
639                 /* If "chunked" then there is always a trailer,
640                  * consisting of zero or more non-empty lines
641                  * ending with CR LF and then an empty line w/ CR LF.
642                  * We do NOT support trailers except to skip them --
643                  * this is supported (generally) by the http spec.
644                  */
645                 for (;;) {
646                         int c;
647                         if (nread <= 0)
648                                 break;
649                         c = *rbp++;
650                         nread--;
651                         switch (h->trailer_state) {
652                         case trailer_line_begin:
653                                 if (c == '\r')
654                                         h->trailer_state = trailer_empty_cr;
655                                 else
656                                         h->trailer_state = trailer_nonempty;
657                                 break;
658                         case trailer_empty_cr:
659                                 /* end empty line */
660                                 if (c == '\n') {
661                                         h->trailer_state = trailer_line_begin;
662                                         h->in_trailer = 0;
663                                         wpa_printf(MSG_DEBUG,
664                                                    "httpread got content(%p)",
665                                                    h);
666                                         h->got_body = 1;
667                                         goto got_file;
668                                 }
669                                 h->trailer_state = trailer_nonempty;
670                                 break;
671                         case trailer_nonempty:
672                                 if (c == '\r')
673                                         h->trailer_state = trailer_nonempty_cr;
674                                 break;
675                         case trailer_nonempty_cr:
676                                 if (c == '\n')
677                                         h->trailer_state = trailer_line_begin;
678                                 else
679                                         h->trailer_state = trailer_nonempty;
680                                 break;
681                         }
682                 }
683         }
684         goto get_more;
685
686 bad:
687         /* Error */
688         wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
689         (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
690         return;
691
692 get_more:
693         wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
694         return;
695
696 got_file:
697         wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d",
698                    h->body_nbytes, h->hdr_type);
699         wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body",
700                           h->body, h->body_nbytes);
701         /* Null terminate for convenience of some applications */
702         if (h->body)
703                 h->body[h->body_nbytes] = 0; /* null terminate */
704         h->got_file = 1;
705         /* Assume that we do NOT support keeping connection alive,
706          * and just in case somehow we don't get destroyed right away,
707          * unregister now.
708          */
709         eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
710         /* The application can destroy us whenever they feel like...
711          * cancel timeout.
712          */
713         eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
714         (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
715 }
716
717
718 /* httpread_create -- start a new reading session making use of eloop.
719  * The new instance will use the socket descriptor for reading (until
720  * it gets a file and not after) but will not close the socket, even
721  * when the instance is destroyed (the application must do that).
722  * Return NULL on error.
723  *
724  * Provided that httpread_create successfully returns a handle,
725  * the callback fnc is called to handle httpread_event events.
726  * The caller should do destroy on any errors or unknown events.
727  *
728  * Pass max_bytes == 0 to not read body at all (required for e.g.
729  * reply to HEAD request).
730  */
731 struct httpread * httpread_create(
732         int sd,  /* descriptor of TCP socket to read from */
733         void (*cb)(struct httpread *handle, void *cookie,
734                    enum httpread_event e),  /* call on event */
735         void *cookie,    /* pass to callback */
736         int max_bytes,    /* maximum body size else abort it */
737         int timeout_seconds     /* 0; or total duration timeout period */
738         )
739 {
740         struct httpread *h = NULL;
741
742         h = os_zalloc(sizeof(*h));
743         if (h == NULL)
744                 goto fail;
745         h->sd = sd;
746         h->cb = cb;
747         h->cookie = cookie;
748         h->max_bytes = max_bytes;
749         h->timeout_seconds = timeout_seconds;
750
751         if (timeout_seconds > 0 &&
752             eloop_register_timeout(timeout_seconds, 0,
753                                    httpread_timeout_handler, NULL, h)) {
754                 /* No way to recover (from malloc failure) */
755                 goto fail;
756         }
757         if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
758                                 NULL, h)) {
759                 /* No way to recover (from malloc failure) */
760                 goto fail;
761         }
762         return h;
763
764 fail:
765
766         /* Error */
767         httpread_destroy(h);
768         return NULL;
769 }
770
771
772 /* httpread_hdr_type_get -- When file is ready, returns header type. */
773 enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
774 {
775         return h->hdr_type;
776 }
777
778
779 /* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
780  * or possibly NULL (which would be an error).
781  */
782 char * httpread_uri_get(struct httpread *h)
783 {
784         return h->uri;
785 }
786
787
788 /* httpread_reply_code_get -- When reply is ready, returns reply code */
789 int httpread_reply_code_get(struct httpread *h)
790 {
791         return h->reply_code;
792 }
793
794
795 /* httpread_length_get -- When file is ready, returns file length. */
796 int httpread_length_get(struct httpread *h)
797 {
798         return h->body_nbytes;
799 }
800
801
802 /* httpread_data_get -- When file is ready, returns file content
803  * with null byte appened.
804  * Might return NULL in some error condition.
805  */
806 void * httpread_data_get(struct httpread *h)
807 {
808         return h->body ? h->body : "";
809 }
810
811
812 /* httpread_hdr_get -- When file is ready, returns header content
813  * with null byte appended.
814  * Might return NULL in some error condition.
815  */
816 char * httpread_hdr_get(struct httpread *h)
817 {
818         return h->hdr;
819 }
820
821
822 /* httpread_hdr_line_get -- When file is ready, returns pointer
823  * to line within header content matching the given tag
824  * (after the tag itself and any spaces/tabs).
825  *
826  * The tag should end with a colon for reliable matching.
827  *
828  * If not found, returns NULL;
829  */
830 char * httpread_hdr_line_get(struct httpread *h, const char *tag)
831 {
832         int tag_len = os_strlen(tag);
833         char *hdr = h->hdr;
834         hdr = os_strchr(hdr, '\n');
835         if (hdr == NULL)
836                 return NULL;
837         hdr++;
838         for (;;) {
839                 if (!os_strncasecmp(hdr, tag, tag_len)) {
840                         hdr += tag_len;
841                         while (*hdr == ' ' || *hdr == '\t')
842                                 hdr++;
843                         return hdr;
844                 }
845                 hdr = os_strchr(hdr, '\n');
846                 if (hdr == NULL)
847                         return NULL;
848                 hdr++;
849         }
850 }