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