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