source: rtems/cpukit/shttpd/shttpd.c @ 086442b7

4.104.11
Last change on this file since 086442b7 was 086442b7, checked in by Ralf Corsepius <ralf.corsepius@…>, on Dec 2, 2009 at 11:00:38 AM

Whitespace removal.

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