source: rtems/cpukit/shttpd/shttpd.c @ c648ca5

4.104.11
Last change on this file since c648ca5 was c648ca5, checked in by Ralf Corsepius <ralf.corsepius@…>, on Mar 26, 2010 at 5:42:24 PM

Add HAVE_STRINGS_H for better POSIX compliance.

  • Property mode set to 100644
File size: 29.1 KB
Line 
1/*
2 * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
3 * All rights reserved
4 *
5 * "THE BEER-WARE LICENSE" (Revision 42):
6 * Sergey Lyubka wrote this file.  As long as you retain this notice you
7 * can do whatever you want with this stuff. If we meet some day, and you think
8 * this stuff is worth it, you can buy me a beer in return.
9 */
10
11/*
12 * Small and portable HTTP server, http://shttpd.sourceforge.net
13 * $Id$
14 */
15
16#include "defs.h"
17
18#ifdef HAVE_STRINGS_H
19#include <strings.h>
20#endif
21
22time_t          current_time;   /* Current UTC time             */
23int             tz_offset;      /* Time zone offset from UTC    */
24
25static LL_HEAD(listeners);      /* List of listening sockets    */
26
27const struct vec known_http_methods[] = {
28        {"GET",         3},
29        {"POST",        4},
30        {"PUT",         3},
31        {"DELETE",      6},
32        {"HEAD",        4},
33        {NULL,          0}
34};
35
36/*
37 * This structure tells how HTTP headers must be parsed.
38 * Used by parse_headers() function.
39 */
40#define OFFSET(x)       offsetof(struct headers, x)
41static const struct http_header http_headers[] = {
42        {16, HDR_INT,    OFFSET(cl),            "Content-Length: "      },
43        {14, HDR_STRING, OFFSET(ct),            "Content-Type: "        },
44        {12, HDR_STRING, OFFSET(useragent),     "User-Agent: "          },
45        {19, HDR_DATE,   OFFSET(ims),           "If-Modified-Since: "   },
46        {15, HDR_STRING, OFFSET(auth),          "Authorization: "       },
47        {9,  HDR_STRING, OFFSET(referer),       "Referer: "             },
48        {8,  HDR_STRING, OFFSET(cookie),        "Cookie: "              },
49        {10, HDR_STRING, OFFSET(location),      "Location: "            },
50        {8,  HDR_INT,    OFFSET(status),        "Status: "              },
51        {7,  HDR_STRING, OFFSET(range),         "Range: "               },
52        {12, HDR_STRING, OFFSET(connection),    "Connection: "          },
53        {19, HDR_STRING, OFFSET(transenc),      "Transfer-Encoding: "   },
54        {0,  HDR_INT,    0,                     NULL                    }
55};
56
57struct shttpd_ctx *init_ctx(const char *config_file, int argc, char *argv[]);
58
59void
60decode_url_encoded_string(const char *src, int src_len, char *dst, int dst_len)
61{
62        int     i, j, a, b;
63#define HEXTOI(x)  (isdigit(x) ? x - '0' : x - 'W')
64
65        for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++)
66                switch (src[i]) {
67                case '%':
68                        if (isxdigit(((unsigned char *) src)[i + 1]) &&
69                            isxdigit(((unsigned char *) src)[i + 2])) {
70                                a = tolower(((unsigned char *)src)[i + 1]);
71                                b = tolower(((unsigned char *)src)[i + 2]);
72                                dst[j] = (HEXTOI(a) << 4) | HEXTOI(b);
73                                i += 2;
74                        } else {
75                                dst[j] = '%';
76                        }
77                        break;
78                case '+':
79                        dst[j] = ' ';
80                        break;
81                default:
82                        dst[j] = src[i];
83                        break;
84                }
85
86        dst[j] = '\0';  /* Null-terminate the destination */
87}
88
89void
90shttpd_add_mime_type(struct shttpd_ctx *ctx, const char *ext, const char *mime)
91{
92        struct mime_type_link   *e;
93        const char              *error_msg = "shttpd_add_mime_type: no memory";
94
95        if ((e = malloc(sizeof(*e))) == NULL) {
96                elog(E_FATAL, 0, error_msg);
97        } else if ((e->ext= my_strdup(ext)) == NULL) {
98                elog(E_FATAL, 0, error_msg);
99        } else if ((e->mime = my_strdup(mime)) == NULL) {
100                elog(E_FATAL, 0, error_msg);
101        } else {
102                e->ext_len = strlen(ext);
103                LL_TAIL(&ctx->mime_types, &e->link);
104        }
105}
106
107
108static const char *
109is_alias(struct shttpd_ctx *ctx, const char *uri,
110                struct vec *a_uri, struct vec *a_path)
111{
112        const char      *p, *s = ctx->aliases;
113        size_t          len;
114
115        DBG(("is_alias: aliases [%s]", s == NULL ? "" : s));
116
117        FOR_EACH_WORD_IN_LIST(s, len) {
118                if ((p = memchr(s, '=', len)) != NULL &&
119                    memcmp(uri, s, p - s) == 0) {
120                        a_uri->ptr = s;
121                        a_uri->len = p - s;
122                        a_path->ptr = ++p;
123                        a_path->len = (s + len) - p;
124                        return (s);
125                }
126        }
127
128        return (NULL);
129}
130
131void
132stop_stream(struct stream *stream)
133{
134        if (stream->io_class != NULL && stream->io_class->close != NULL)
135                stream->io_class->close(stream);
136
137        stream->io_class= NULL;
138        stream->flags |= FLAG_CLOSED;
139        stream->flags &= ~(FLAG_R | FLAG_W | FLAG_ALWAYS_READY);
140
141        DBG(("%d %s stopped. %lu of content data, %d now in a buffer",
142            stream->conn->rem.chan.sock,
143            stream->io_class ? stream->io_class->name : "(null)",
144            (unsigned long) stream->io.total, io_data_len(&stream->io)));
145}
146
147/*
148 * Setup listening socket on given port, return socket
149 */
150static int
151open_listening_port(int port)
152{
153        int             sock, on = 1;
154        struct usa      sa;
155
156#ifdef _WIN32
157        {WSADATA data;  WSAStartup(MAKEWORD(2,2), &data);}
158#endif /* _WIN32 */
159
160        sa.len                          = sizeof(sa.u.sin);
161        sa.u.sin.sin_family             = AF_INET;
162        sa.u.sin.sin_port               = htons((uint16_t) port);
163        sa.u.sin.sin_addr.s_addr        = htonl(INADDR_ANY);
164
165        if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
166                goto fail;
167        if (set_non_blocking_mode(sock) != 0)
168                goto fail;
169        if (setsockopt(sock, SOL_SOCKET,
170            SO_REUSEADDR,(char *) &on, sizeof(on)) != 0)
171                goto fail;
172        if (bind(sock, &sa.u.sa, sa.len) < 0)
173                goto fail;
174        if (listen(sock, 128) != 0)
175                goto fail;
176
177#ifndef _WIN32
178        (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
179#endif /* !_WIN32 */
180
181        return (sock);
182fail:
183        if (sock != -1)
184                (void) closesocket(sock);
185        elog(E_LOG, NULL, "open_listening_port(%d): %s", port, strerror(errno));
186        return (-1);
187}
188
189/*
190 * Check whether full request is buffered Return headers length, or 0
191 */
192int
193get_headers_len(const char *buf, size_t buflen)
194{
195        const char      *s, *e;
196        int             len = 0;
197
198        for (s = buf, e = s + buflen - 1; len == 0 && s < e; s++)
199                /* Control characters are not allowed but >=128 is. */
200                if (!isprint(*(unsigned char *)s) && *s != '\r' && *s != '\n' && *(unsigned char *)s < 128)
201                        len = -1;
202                else if (s[0] == '\n' && s[1] == '\n')
203                        len = s - buf + 2;
204                else if (s[0] == '\n' && &s[1] < e &&
205                    s[1] == '\r' && s[2] == '\n')
206                        len = s - buf + 3;
207
208        return (len);
209}
210
211/*
212 * Send error message back to a client.
213 */
214void
215send_server_error(struct conn *c, int status, const char *reason)
216{
217#ifdef EMBEDDED
218        struct llhead           *lp;
219        struct error_handler    *e;
220
221        LL_FOREACH(&c->ctx->error_handlers, lp) {
222                e = LL_ENTRY(lp, struct error_handler, link);
223
224                if (e->code == status) {
225                        if (c->loc.io_class != NULL &&
226                            c->loc.io_class->close != NULL)
227                                c->loc.io_class->close(&c->loc);
228                        io_clear(&c->loc.io);
229                        setup_embedded_stream(c, e->callback, NULL);
230                        return;
231                }
232        }
233#endif /* EMBEDDED */
234
235        io_clear(&c->loc.io);
236        c->loc.headers_len = c->loc.io.head = my_snprintf(c->loc.io.buf,
237            c->loc.io.size, "HTTP/1.1 %d %s\r\n\r\n%d %s",
238            status, reason, status, reason);
239        c->status = status;
240        stop_stream(&c->loc);
241}
242
243/*
244 * Convert month to the month number. Return -1 on error, or month number
245 */
246static int
247montoi(const char *s)
248{
249        static const char *ar[] = {
250                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
251                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
252        };
253        size_t  i;
254
255        for (i = 0; i < sizeof(ar) / sizeof(ar[0]); i++)
256                if (!strcmp(s, ar[i]))
257                        return (i);
258
259        return (-1);
260}
261
262/*
263 * Parse date-time string, and return the corresponding time_t value
264 */
265static time_t
266date_to_epoch(const char *s)
267{
268        struct tm       tm, *tmp;
269        char            mon[32];
270        int             sec, min, hour, mday, month, year;
271
272        (void) memset(&tm, 0, sizeof(tm));
273        sec = min = hour = mday = month = year = 0;
274
275        if (((sscanf(s, "%d/%3s/%d %d:%d:%d",
276            &mday, mon, &year, &hour, &min, &sec) == 6) ||
277            (sscanf(s, "%d %3s %d %d:%d:%d",
278            &mday, mon, &year, &hour, &min, &sec) == 6) ||
279            (sscanf(s, "%*3s, %d %3s %d %d:%d:%d",
280            &mday, mon, &year, &hour, &min, &sec) == 6) ||
281            (sscanf(s, "%d-%3s-%d %d:%d:%d",
282            &mday, mon, &year, &hour, &min, &sec) == 6)) &&
283            (month = montoi(mon)) != -1) {
284                tm.tm_mday      = mday;
285                tm.tm_mon       = month;
286                tm.tm_year      = year;
287                tm.tm_hour      = hour;
288                tm.tm_min       = min;
289                tm.tm_sec       = sec;
290        }
291
292        if (tm.tm_year > 1900)
293                tm.tm_year -= 1900;
294        else if (tm.tm_year < 70)
295                tm.tm_year += 100;
296
297        /* Set Daylight Saving Time field */
298        tmp = localtime(&current_time);
299        tm.tm_isdst = tmp->tm_isdst;
300
301        return (mktime(&tm));
302}
303
304static void
305remove_double_dots(char *s)
306{
307        char    *p = s;
308
309        while (*s != '\0') {
310                *p++ = *s++;
311                if (s[-1] == '/')
312                        while (*s == '.' || *s == '/')
313                                s++;
314        }
315        *p = '\0';
316}
317
318void
319parse_headers(const char *s, int len, struct headers *parsed)
320{
321        const struct http_header        *h;
322        union variant                   *v;
323        const char                      *p, *e = s + len;
324
325        DBG(("parsing headers (len %d): [%.*s]", len, len, s));
326
327        /* Loop through all headers in the request */
328        while (s < e) {
329
330                /* Find where this header ends */
331                for (p = s; p < e && *p != '\n'; ) p++;
332
333                /* Is this header known to us ? */
334                for (h = http_headers; h->len != 0; h++)
335                        if (e - s > h->len &&
336                            !my_strncasecmp(s, h->name, h->len))
337                                break;
338
339                /* If the header is known to us, store its value */
340                if (h->len != 0) {
341
342                        /* Shift to where value starts */
343                        s += h->len;
344
345                        /* Find place to store the value */
346                        v = (union variant *) ((char *) parsed + h->offset);
347
348                        /* Fetch header value into the connection structure */
349                        if (h->type == HDR_STRING) {
350                                v->v_vec.ptr = s;
351                                v->v_vec.len = p - s;
352                                if (p[-1] == '\r' && v->v_vec.len > 0)
353                                        v->v_vec.len--;
354                        } else if (h->type == HDR_INT) {
355                                v->v_big_int = strtoul(s, NULL, 10);
356                        } else if (h->type == HDR_DATE) {
357                                v->v_time = date_to_epoch(s);
358                        }
359                }
360
361                s = p + 1;      /* Shift to the next header */
362        }
363}
364
365/*
366 * For given directory path, substitute it to valid index file.
367 * Return 0 if index file has been found, -1 if not found
368 */
369static int
370find_index_file(struct conn *c, char *path, size_t maxpath, struct stat *stp)
371{
372        char            buf[FILENAME_MAX];
373        const char      *s = c->ctx->index_files;
374        size_t          len;
375
376        FOR_EACH_WORD_IN_LIST(s, len) {
377                my_snprintf(buf, sizeof(buf), "%s%c%.*s",path, DIRSEP, len, s);
378                if (my_stat(buf, stp) == 0) {
379                        my_strlcpy(path, buf, maxpath);
380                        c->mime_type = get_mime_type(c->ctx, s, len);
381                        return (0);
382                }
383        }
384
385        return (-1);
386}
387
388/*
389 * Try to open requested file, return 0 if OK, -1 if error.
390 * If the file is given arguments using PATH_INFO mechanism,
391 * initialize pathinfo pointer.
392 */
393static int
394get_path_info(struct conn *c, char *path, struct stat *stp)
395{
396        char    *p, *e;
397
398        if (my_stat(path, stp) == 0)
399                return (0);
400
401        p = path + strlen(path);
402        e = path + strlen(c->ctx->document_root) + 2;
403       
404        /* Strip directory parts of the path one by one */
405        for (; p > e; p--)
406                if (*p == '/') {
407                        *p = '\0';
408                        if (!my_stat(path, stp) && !S_ISDIR(stp->st_mode)) {
409                                c->path_info = p + 1;
410                                return (0);
411                        } else {
412                                *p = '/';
413                        }
414                }
415
416        return (-1);
417}
418
419
420static void
421decide_what_to_do(struct conn *c)
422{
423        char            path[URI_MAX], buf[1024];
424        struct vec      alias_uri, alias_path;
425        struct stat     st;
426        int             rc;
427#ifdef EMBEDDED
428        struct registered_uri   *ruri;
429#endif /* EMBEDDED */
430
431        DBG(("decide_what_to_do: [%s]", c->uri));
432
433        if ((c->query = strchr(c->uri, '?')) != NULL)
434                *c->query++ = '\0';
435
436        decode_url_encoded_string(c->uri, strlen(c->uri),
437            c->uri, strlen(c->uri) + 1);
438        remove_double_dots(c->uri);
439       
440        if (strlen(c->uri) + strlen(c->ctx->document_root) >= sizeof(path)) {
441                send_server_error(c, 400, "URI is too long");
442                return;
443        }
444
445        (void) my_snprintf(path, sizeof(path), "%s%s",
446            c->ctx->document_root, c->uri);
447
448        /* User may use the aliases - check URI for mount point */
449        if (is_alias(c->ctx, c->uri, &alias_uri, &alias_path) != NULL) {
450                (void) my_snprintf(path, sizeof(path), "%.*s%s",
451                    alias_path.len, alias_path.ptr, c->uri + alias_uri.len);
452                DBG(("using alias %.*s -> %.*s", alias_uri.len, alias_uri.ptr,
453                    alias_path.len, alias_path.ptr));
454        }
455
456#if !defined(NO_AUTH)
457        if (check_authorization(c, path) != 1) {
458                send_authorization_request(c);
459        } else
460#endif /* NO_AUTH */
461#ifdef EMBEDDED
462        if ((ruri = is_registered_uri(c->ctx, c->uri)) != NULL) {
463                setup_embedded_stream(c, ruri->callback, ruri->callback_data);
464        } else
465#endif /* EMBEDDED */
466        if (strstr(path, HTPASSWD)) {
467                /* Do not allow to view passwords files */
468                send_server_error(c, 403, "Forbidden");
469        } else
470#if !defined(NO_AUTH)
471        if ((c->method == METHOD_PUT || c->method == METHOD_DELETE) &&
472            (c->ctx->put_auth_file == NULL || !is_authorized_for_put(c))) {
473                send_authorization_request(c);
474        } else
475#endif /* NO_AUTH */
476        if (c->method == METHOD_PUT) {
477                c->status = my_stat(path, &st) == 0 ? 200 : 201;
478
479                if (c->ch.range.v_vec.len > 0) {
480                        send_server_error(c, 501, "PUT Range Not Implemented");
481                } else if ((rc = put_dir(path)) == 0) {
482                        send_server_error(c, 200, "OK");
483                } else if (rc == -1) {
484                        send_server_error(c, 500, "PUT Directory Error");
485                } else if (c->rem.content_len == 0) {
486                        send_server_error(c, 411, "Length Required");
487                } else if ((c->loc.chan.fd = my_open(path, O_WRONLY | O_BINARY |
488                    O_CREAT | O_NONBLOCK | O_TRUNC, 0644)) == -1) {
489                        send_server_error(c, 500, "PUT Error");
490                } else {
491                        DBG(("PUT file [%s]", c->uri));
492                        c->loc.io_class = &io_file;
493                        c->loc.flags |= FLAG_W | FLAG_ALWAYS_READY ;
494                }
495        } else if (c->method == METHOD_DELETE) {
496                DBG(("DELETE [%s]", c->uri));
497                if (my_remove(path) == 0)
498                        send_server_error(c, 200, "OK");
499                else
500                        send_server_error(c, 500, "DELETE Error");
501        } else if (get_path_info(c, path, &st) != 0) {
502                send_server_error(c, 404, "Not Found");
503        } else if (S_ISDIR(st.st_mode) && path[strlen(path) - 1] != '/') {
504                (void) my_snprintf(buf, sizeof(buf),
505                        "Moved Permanently\r\nLocation: %s/", c->uri);
506                send_server_error(c, 301, buf);
507        } else if (S_ISDIR(st.st_mode) &&
508            find_index_file(c, path, sizeof(path) - 1, &st) == -1 &&
509            c->ctx->dirlist == 0) {
510                send_server_error(c, 403, "Directory Listing Denied");
511        } else if (S_ISDIR(st.st_mode) && c->ctx->dirlist) {
512                if ((c->loc.chan.dir.path = my_strdup(path)) != NULL)
513                        get_dir(c);
514                else
515                        send_server_error(c, 500, "GET Directory Error");
516        } else if (S_ISDIR(st.st_mode) && c->ctx->dirlist == 0) {
517                send_server_error(c, 403, "Directory listing denied");
518#ifndef NO_CGI
519        } else if (is_cgi(c->ctx, path)) {
520                if (c->method != METHOD_POST && c->method != METHOD_GET) {
521                        send_server_error(c, 501, "Bad method ");
522                } else if ((run_cgi(c, path)) == -1) {
523                        send_server_error(c, 500, "Cannot exec CGI");
524                } else {
525                        do_cgi(c);
526                }
527#endif /* NO_CGI */
528        } else if (c->ch.ims.v_time && st.st_mtime <= c->ch.ims.v_time) {
529                send_server_error(c, 304, "Not Modified");
530        } else if ((c->loc.chan.fd = my_open(path,
531            O_RDONLY | O_BINARY, 0644)) != -1) {
532                get_file(c, &st);
533        } else {
534                send_server_error(c, 500, "Internal Error");
535        }
536}
537
538static int
539set_request_method(struct conn *c)
540{
541        const struct vec        *v;
542
543        assert(c->rem.io.head >= MIN_REQ_LEN);
544
545        /* Set the request method */
546        for (v = known_http_methods; v->ptr != NULL; v++)
547                if (!memcmp(c->rem.io.buf, v->ptr, v->len)) {
548                        c->method = v - known_http_methods;
549                        break;
550                }
551
552        return (v->ptr == NULL);
553}
554
555static void
556parse_http_request(struct conn *c)
557{
558        char    *s, *e, *p, *start;
559        char    *end_number;
560        int     uri_len, req_len;
561
562        s = c->rem.io.buf;
563        req_len = c->rem.headers_len = get_headers_len(s, c->rem.io.head);
564
565        if (req_len == 0 && io_space_len(&c->rem.io) == 0)
566                send_server_error(c, 400, "Request is too big");
567
568        if (req_len == 0)
569                return;
570        else if (req_len < MIN_REQ_LEN)
571                send_server_error(c, 400, "Bad request");
572        else if (set_request_method(c))
573                send_server_error(c, 501, "Method Not Implemented");
574        else if ((c->request = my_strndup(s, req_len)) == NULL)
575                send_server_error(c, 500, "Cannot allocate request");
576
577        if (c->loc.flags & FLAG_CLOSED)
578                return;
579
580        DBG(("Conn %d: parsing request: [%.*s]", c->rem.chan.sock, req_len, s));
581        c->rem.flags |= FLAG_HEADERS_PARSED;
582
583        /* Set headers pointer. Headers follow the request line */
584        c->headers = memchr(c->request, '\n', req_len);
585        assert(c->headers != NULL);
586        assert(c->headers < c->request + req_len);
587        if (c->headers > c->request && c->headers[-1] == '\r')
588                c->headers[-1] = '\0';
589        *c->headers++ = '\0';
590
591        /*
592         * Now make a copy of the URI, because it will be URL-decoded,
593         * and we need a copy of unmodified URI for the access log.
594         * First, we skip the REQUEST_METHOD and shift to the URI.
595         */
596        for (p = c->request, e = p + req_len; *p != ' ' && p < e; p++);
597        while (p < e && *p == ' ') p++;
598
599        /* Now remember where URI starts, and shift to the end of URI */
600        for (start = p; p < e && !isspace((unsigned char)*p); ) p++;
601        uri_len = p - start;
602        /* Skip space following the URI */
603        while (p < e && *p == ' ') p++;
604
605        /* Now comes the HTTP-Version in the form HTTP/<major>.<minor> */
606        if (strncmp(p, "HTTP/", 5) != 0) {
607                send_server_error(c, 400, "Bad HTTP version");
608                return;
609        }
610        p += 5;
611        /* Parse the HTTP major version number */
612        c->major_version = strtoul(p, &end_number, 10);
613        if (end_number == p || *end_number != '.') {
614                send_server_error(c, 400, "Bad HTTP major version");
615                return;
616        }
617        p = end_number + 1;
618        /* Parse the minor version number */
619        c->minor_version = strtoul(p, &end_number, 10);
620        if (end_number == p || *end_number != '\0') {
621                send_server_error(c, 400, "Bad HTTP minor version");
622                return;
623        }
624        /* Version must be <=1.1 */
625        if (c->major_version > 1 ||
626            (c->major_version == 1 && c->minor_version > 1)) {
627                send_server_error(c, 505, "HTTP version not supported");
628                return;
629        }
630
631        if (uri_len <= 0) {
632                send_server_error(c, 400, "Bad URI");
633        } else if ((c->uri = malloc(uri_len + 1)) == NULL) {
634                send_server_error(c, 500, "Cannot allocate URI");
635        } else {
636                my_strlcpy(c->uri, (char *) start, uri_len + 1);
637                parse_headers(c->headers,
638                    (c->request + req_len) - c->headers, &c->ch);
639
640                /* Remove the length of request from total, count only data */
641                assert(c->rem.io.total >= (big_int_t) req_len);
642                c->rem.io.total -= req_len;
643
644                c->rem.content_len = c->ch.cl.v_big_int;
645                io_inc_tail(&c->rem.io, req_len);
646
647                decide_what_to_do(c);
648        }
649}
650
651void
652shttpd_add_socket(struct shttpd_ctx *ctx, int sock)
653{
654        struct conn     *c;
655        struct usa      sa;
656        int             l = ctx->inetd_mode ? E_FATAL : E_LOG;
657#if !defined(NO_SSL)
658        SSL             *ssl = NULL;
659#endif /* NO_SSL */
660
661        sa.len = sizeof(sa.u.sin);
662        (void) set_non_blocking_mode(sock);
663
664        if (getpeername(sock, &sa.u.sa, &sa.len)) {
665                elog(l, NULL, "add_socket: %s", strerror(errno));
666#if !defined(NO_SSL)
667        } else if (ctx->ssl_ctx && (ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
668                elog(l, NULL, "add_socket: SSL_new: %s", strerror(ERRNO));
669                (void) closesocket(sock);
670        } else if (ctx->ssl_ctx && SSL_set_fd(ssl, sock) == 0) {
671                elog(l, NULL, "add_socket: SSL_set_fd: %s", strerror(ERRNO));
672                (void) closesocket(sock);
673                SSL_free(ssl);
674#endif /* NO_SSL */
675        } else if ((c = calloc(1, sizeof(*c) + 2 * ctx->io_buf_size)) == NULL) {
676
677#if !defined(NO_SSL)
678                if (ssl)
679                        SSL_free(ssl);
680#endif /* NO_SSL */
681                (void) closesocket(sock);
682                elog(l, NULL, "add_socket: calloc: %s", strerror(ERRNO));
683        } else {
684                ctx->nrequests++;
685                c->rem.conn = c->loc.conn = c;
686                c->ctx          = ctx;
687                c->sa           = sa;
688                c->birth_time   = current_time;
689                c->expire_time  = current_time + EXPIRE_TIME;
690
691                set_close_on_exec(sock);
692
693                c->loc.io_class = NULL;
694       
695                c->rem.io_class = &io_socket;
696                c->rem.chan.sock = sock;
697
698                /* Set IO buffers */
699                c->loc.io.buf   = (char *) (c + 1);
700                c->rem.io.buf   = c->loc.io.buf + ctx->io_buf_size;
701                c->loc.io.size  = c->rem.io.size = ctx->io_buf_size;
702
703#if !defined(NO_SSL)
704                if (ssl) {
705                        c->rem.io_class = &io_ssl;
706                        c->rem.chan.ssl.sock = sock;
707                        c->rem.chan.ssl.ssl = ssl;
708                        ssl_handshake(&c->rem);
709                }
710#endif /* NO_SSL */
711
712                EnterCriticalSection(&ctx->mutex);
713                LL_TAIL(&ctx->connections, &c->link);
714                ctx->nactive++;
715                LeaveCriticalSection(&ctx->mutex);
716               
717                DBG(("%s:%hu connected (socket %d)",
718                    inet_ntoa(* (struct in_addr *) &sa.u.sin.sin_addr.s_addr),
719                    ntohs(sa.u.sin.sin_port), sock));
720        }
721}
722
723int
724shttpd_active(struct shttpd_ctx *ctx)
725{
726        return (ctx->nactive);
727}
728
729/*
730 * Setup a listening socket on given port. Return opened socket or -1
731 */
732int
733shttpd_listen(struct shttpd_ctx *ctx, int port)
734{
735        struct listener *l;
736        int             sock;
737
738        if ((sock = open_listening_port(port)) == -1) {
739        } else if ((l = calloc(1, sizeof(*l))) == NULL) {
740                (void) closesocket(sock);
741        } else {
742                l->sock = sock;
743                l->ctx  = ctx;
744                LL_TAIL(&listeners, &l->link);
745                DBG(("shttpd_listen: added socket %d", sock));
746        }
747
748        return (sock);
749}
750
751int
752shttpd_accept(int lsn_sock, int milliseconds)
753{
754        struct timeval  tv;
755        struct usa      sa;
756        fd_set          read_set;
757        int             sock = -1;
758       
759        tv.tv_sec       = milliseconds / 1000;
760        tv.tv_usec      = milliseconds % 1000;
761        sa.len          = sizeof(sa.u.sin);
762        FD_ZERO(&read_set);
763        FD_SET(lsn_sock, &read_set);
764       
765        if (select(lsn_sock + 1, &read_set, NULL, NULL, &tv) == 1)
766                sock = accept(lsn_sock, &sa.u.sa, &sa.len);
767
768        return (sock);
769}
770
771static void
772read_stream(struct stream *stream)
773{
774        int     n, len;
775
776        len = io_space_len(&stream->io);
777        assert(len > 0);
778
779        /* Do not read more that needed */
780        if (stream->content_len > 0 &&
781            stream->io.total + len > stream->content_len)
782                len = stream->content_len - stream->io.total;
783
784        /* Read from underlying channel */
785        n = stream->nread_last = stream->io_class->read(stream,
786            io_space(&stream->io), len);
787
788        if (n > 0)
789                io_inc_head(&stream->io, n);
790        else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
791                n = n;  /* Ignore EINTR and EAGAIN */
792        else if (!(stream->flags & FLAG_DONT_CLOSE))
793                stop_stream(stream);
794
795        DBG(("read_stream (%d %s): read %d/%d/%lu bytes (errno %d)",
796            stream->conn->rem.chan.sock,
797            stream->io_class ? stream->io_class->name : "(null)",
798            n, len, (unsigned long) stream->io.total, ERRNO));
799
800        /*
801         * Close the local stream if everything was read
802         * XXX We do not close the remote stream though! It may be
803         * a POST data completed transfer, we do not want the socket
804         * to be closed.
805         */
806        if (stream->content_len > 0 && stream == &stream->conn->loc) {
807                assert(stream->io.total <= stream->content_len);
808                if (stream->io.total == stream->content_len)
809                        stop_stream(stream);
810        }
811
812        stream->conn->expire_time = current_time + EXPIRE_TIME;
813}
814
815static void
816write_stream(struct stream *from, struct stream *to)
817{
818        int     n, len;
819
820        len = io_data_len(&from->io);
821        assert(len > 0);
822
823        /* TODO: should be assert on CAN_WRITE flag */
824        n = to->io_class->write(to, io_data(&from->io), len);
825        to->conn->expire_time = current_time + EXPIRE_TIME;
826        DBG(("write_stream (%d %s): written %d/%d bytes (errno %d)",
827            to->conn->rem.chan.sock,
828            to->io_class ? to->io_class->name : "(null)", n, len, ERRNO));
829
830        if (n > 0)
831                io_inc_tail(&from->io, n);
832        else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
833                n = n;  /* Ignore EINTR and EAGAIN */
834        else if (!(to->flags & FLAG_DONT_CLOSE))
835                stop_stream(to);
836}
837
838
839static void
840disconnect(struct conn *c)
841{
842        static const struct vec ka = {"keep-alive", 10};
843        int                     dont_close;
844
845        DBG(("Disconnecting %d (%.*s)", c->rem.chan.sock,
846            c->ch.connection.v_vec.len, c->ch.connection.v_vec.ptr));
847
848#if !defined(_WIN32) || defined(NO_GUI)
849        if (c->ctx->access_log != NULL)
850#endif /* _WIN32 */
851                        log_access(c->ctx->access_log, c);
852
853        /* In inetd mode, exit if request is finished. */
854        if (c->ctx->inetd_mode)
855                exit(0);
856
857        if (c->loc.io_class != NULL && c->loc.io_class->close != NULL)
858                c->loc.io_class->close(&c->loc);
859
860        /*
861         * Check the "Connection: " header before we free c->request
862         * If it its 'keep-alive', then do not close the connection
863         */
864        dont_close =  c->ch.connection.v_vec.len >= ka.len &&
865            !my_strncasecmp(ka.ptr, c->ch.connection.v_vec.ptr, ka.len);
866
867        if (c->request)
868                free(c->request);
869        if (c->uri)
870                free(c->uri);
871
872        /* Handle Keep-Alive */
873        dont_close = 0;
874        if (dont_close) {
875                c->loc.io_class = NULL;
876                c->loc.flags = c->rem.flags = 0;
877                c->query = c->request = c->uri = c->path_info = NULL;
878                c->mime_type = NULL;
879                (void) memset(&c->ch, 0, sizeof(c->ch));
880                io_clear(&c->rem.io);
881                io_clear(&c->loc.io);
882                c->rem.io.total = c->loc.io.total = 0;
883        } else {
884                if (c->rem.io_class != NULL)
885                        c->rem.io_class->close(&c->rem);
886
887                EnterCriticalSection(&c->ctx->mutex);
888                LL_DEL(&c->link);
889                c->ctx->nactive--;
890                assert(c->ctx->nactive >= 0);
891                LeaveCriticalSection(&c->ctx->mutex);
892
893                free(c);
894        }
895}
896
897static void
898add_to_set(int fd, fd_set *set, int *max_fd)
899{
900        FD_SET(fd, set);
901        if (fd > *max_fd)
902                *max_fd = fd;
903}
904
905/*
906 * One iteration of server loop. This is the core of the data exchange.
907 */
908void
909shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
910{
911        struct llhead   *lp, *tmp;
912        struct listener *l;
913        struct conn     *c;
914        struct timeval  tv;                     /* Timeout for select() */
915        fd_set          read_set, write_set;
916        int             sock, max_fd = -1, msec = milliseconds;
917        struct usa      sa;
918
919        current_time = time(0);
920        FD_ZERO(&read_set);
921        FD_ZERO(&write_set);
922
923        /* Add listening sockets to the read set */
924        LL_FOREACH(&listeners, lp) {
925                l = LL_ENTRY(lp, struct listener, link);
926                FD_SET(l->sock, &read_set);
927                if (l->sock > max_fd)
928                        max_fd = l->sock;
929                DBG(("FD_SET(%d) (listening)", l->sock));
930        }
931
932        /* Multiplex streams */
933        LL_FOREACH(&ctx->connections, lp) {
934                c = LL_ENTRY(lp, struct conn, link);
935               
936                /* If there is a space in remote IO, check remote socket */
937                if (io_space_len(&c->rem.io))
938                        add_to_set(c->rem.chan.fd, &read_set, &max_fd);
939
940#if !defined(NO_CGI)
941                /*
942                 * If there is a space in local IO, and local endpoint is
943                 * CGI, check local socket for read availability
944                 */
945                if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
946                    c->loc.io_class == &io_cgi)
947                        add_to_set(c->loc.chan.fd, &read_set, &max_fd);
948
949                /*
950                 * If there is some data read from remote socket, and
951                 * local endpoint is CGI, check local for write availability
952                 */
953                if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
954                    c->loc.io_class == &io_cgi)
955                        add_to_set(c->loc.chan.fd, &write_set, &max_fd);
956#endif /* NO_CGI */
957
958                /*
959                 * If there is some data read from local endpoint, check the
960                 * remote socket for write availability
961                 */
962                if (io_data_len(&c->loc.io))
963                        add_to_set(c->rem.chan.fd, &write_set, &max_fd);
964
965                if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
966                    (c->loc.flags & FLAG_ALWAYS_READY))
967                        msec = 0;
968               
969                if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
970                    (c->loc.flags & FLAG_ALWAYS_READY))
971                        msec = 0;
972        }
973
974        tv.tv_sec = msec / 1000;
975        tv.tv_usec = msec % 1000;
976
977        /* Check IO readiness */
978        if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) < 0) {
979#ifdef _WIN32
980                /*
981                 * On windows, if read_set and write_set are empty,
982                 * select() returns "Invalid parameter" error
983                 * (at least on my Windows XP Pro). So in this case,
984                 * we sleep here.
985                 */
986                Sleep(milliseconds);
987#endif /* _WIN32 */
988                DBG(("select: %d", ERRNO));
989                return;
990        }
991
992        /* Check for incoming connections on listener sockets */
993        LL_FOREACH(&listeners, lp) {
994                l = LL_ENTRY(lp, struct listener, link);
995                if (!FD_ISSET(l->sock, &read_set))
996                        continue;
997                do {
998                        sa.len = sizeof(sa.u.sin);
999                        if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1) {
1000#if defined(_WIN32)
1001                                shttpd_add_socket(ctx, sock);
1002#else
1003                                if (sock < FD_SETSIZE) {
1004                                        shttpd_add_socket(ctx, sock);
1005                                } else {
1006                                        elog(E_LOG, NULL,
1007                                           "shttpd_poll: ctx %p: disarding "
1008                                           "socket %d, too busy", ctx, sock);
1009                                        (void) closesocket(sock);
1010                                }
1011#endif /* _WIN32 */
1012                        }
1013                } while (sock != -1);
1014        }
1015
1016        /* Process all connections */
1017        LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
1018                c = LL_ENTRY(lp, struct conn, link);
1019
1020                /* Read from remote end if it is ready */
1021                if (FD_ISSET(c->rem.chan.fd, &read_set) &&
1022                    io_space_len(&c->rem.io))
1023                        read_stream(&c->rem);
1024
1025                /* If the request is not parsed yet, do so */
1026                if (!(c->rem.flags & FLAG_HEADERS_PARSED))
1027                        parse_http_request(c);
1028
1029                DBG(("loc: %u [%.*s]", io_data_len(&c->loc.io),
1030                    io_data_len(&c->loc.io), io_data(&c->loc.io)));
1031                DBG(("rem: %u [%.*s]", io_data_len(&c->rem.io),
1032                    io_data_len(&c->rem.io), io_data(&c->rem.io)));
1033
1034                /* Read from the local end if it is ready */
1035                if (io_space_len(&c->loc.io) &&
1036                    ((c->loc.flags & FLAG_ALWAYS_READY)
1037               
1038#if !defined(NO_CGI)
1039                    ||(c->loc.io_class == &io_cgi &&
1040                     FD_ISSET(c->loc.chan.fd, &read_set))
1041#endif /* NO_CGI */
1042                    ))
1043                        read_stream(&c->loc);
1044
1045                if (io_data_len(&c->rem.io) > 0 && (c->loc.flags & FLAG_W) &&
1046                    c->loc.io_class != NULL && c->loc.io_class->write != NULL)
1047                        write_stream(&c->rem, &c->loc);
1048
1049                if (io_data_len(&c->loc.io) > 0 && c->rem.io_class != NULL)
1050                        write_stream(&c->loc, &c->rem);
1051
1052                if (c->rem.nread_last > 0)
1053                        c->ctx->in += c->rem.nread_last;
1054                if (c->loc.nread_last > 0)
1055                        c->ctx->out += c->loc.nread_last;
1056
1057                /* Check whether we should close this connection */
1058                if ((current_time > c->expire_time) ||
1059                    (c->rem.flags & FLAG_CLOSED) ||
1060                    ((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
1061                        disconnect(c);
1062        }
1063}
1064
1065/*
1066 * Deallocate shttpd object, free up the resources
1067 */
1068void
1069shttpd_fini(struct shttpd_ctx *ctx)
1070{
1071        struct llhead           *lp, *tmp;
1072        struct mime_type_link   *mtl;
1073        struct conn             *c;
1074        struct listener         *l;
1075        struct registered_uri   *ruri;
1076
1077        /* Free configured mime types */
1078        LL_FOREACH_SAFE(&ctx->mime_types, lp, tmp) {
1079                mtl = LL_ENTRY(lp, struct mime_type_link, link);
1080                free(mtl->mime);
1081                free(mtl->ext);
1082                free(mtl);
1083        }
1084
1085#if 0
1086        struct opt              *opt;
1087        /* Free strdup-ed temporary vars */
1088        for (opt = options; opt->sw != 0; opt++)
1089                if (opt->tmp) {
1090                        free(opt->tmp);
1091                        opt->tmp = NULL;
1092                }
1093#endif
1094       
1095        /* Free all connections */
1096        LL_FOREACH_SAFE(&ctx->connections, lp, tmp) {
1097                c = LL_ENTRY(lp, struct conn, link);
1098                disconnect(c);
1099        }
1100
1101        /* Free registered URIs (must be done after disconnect()) */
1102        LL_FOREACH_SAFE(&ctx->registered_uris, lp, tmp) {
1103                ruri = LL_ENTRY(lp, struct registered_uri, link);
1104                free((void *)ruri->uri);
1105                free(ruri);
1106        }
1107
1108        /* Free listener sockets for this context */
1109        LL_FOREACH_SAFE(&listeners, lp, tmp) {
1110                l = LL_ENTRY(lp, struct listener, link);
1111                (void) closesocket(l->sock);
1112                LL_DEL(&l->link);
1113                free(l);
1114        }
1115
1116        if (ctx->access_log)            (void) fclose(ctx->access_log);
1117        if (ctx->error_log)             (void) fclose(ctx->error_log);
1118        if (ctx->put_auth_file)         free(ctx->put_auth_file);
1119        if (ctx->document_root)         free(ctx->document_root);
1120        if (ctx->index_files)           free(ctx->index_files);
1121        if (ctx->aliases)               free(ctx->aliases);
1122#if !defined(NO_CGI)
1123        if (ctx->cgi_vars)              free(ctx->cgi_vars);
1124        if (ctx->cgi_extensions)        free(ctx->cgi_extensions);
1125        if (ctx->cgi_interpreter)       free(ctx->cgi_interpreter);
1126#endif /* NO_CGI */
1127        if (ctx->auth_realm)            free(ctx->auth_realm);
1128        if (ctx->global_passwd_file)    free(ctx->global_passwd_file);
1129        if (ctx->uid)                   free(ctx->uid);
1130
1131        /* TODO: free SSL context */
1132
1133        free(ctx);
1134}
Note: See TracBrowser for help on using the repository browser.