source: trunk/third-party/shttpd/shttpd.c @ 5841

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