diff --git a/darkhttpd.c b/darkhttpd.c index befb07d..780df4c 100644 --- a/darkhttpd.c +++ b/darkhttpd.c @@ -397,6 +397,7 @@ static const char *default_mimetype = octet_stream; static void poll_recv_request(struct connection *conn); static void poll_send_header(struct connection *conn); static void poll_send_reply(struct connection *conn); +static void urlencode(const char *src, char *dest); /* close() that dies on error. */ static void xclose(const int fd) { @@ -473,6 +474,8 @@ static struct apbuf *make_apbuf(void) { return buf; } +static void append_escaped(struct apbuf *dst, const char *src); + /* Append s (of length len) to buf. */ static void appendl(struct apbuf *buf, const char *s, const size_t len) { size_t need = buf->length + len; @@ -1728,24 +1731,30 @@ static void default_reply(struct connection *conn, static void redirect(struct connection *conn, const char *format, ...) __printflike(2, 3); static void redirect(struct connection *conn, const char *format, ...) { - char *where, date[DATE_LEN]; + char* where; + char date[DATE_LEN]; + struct apbuf* reply; va_list va; va_start(va, format); xvasprintf(&where, format, va); va_end(va); - - /* Only really need to calculate the date once. */ rfc1123_date(date, now); - conn->reply_length = xasprintf(&(conn->reply), - "301 Moved Permanently\n" - "

Moved Permanently

\n" - "Moved to: %s\n" /* where x 2 */ - "
\n" - "%s" /* generated on */ - "\n", - where, where, generated_on(date)); + reply = make_apbuf(); + append(reply, "" + "301 Moved Permanently\n" + "

Moved Permanently

\n" + "Moved to: "); + append_escaped(reply, where); + append(reply, "\n
\n"); + append(reply, generated_on(date)); + append(reply, "\n"); + conn->reply = reply->str; + conn->reply_length = reply->length; + free(reply); conn->header_length = xasprintf(&(conn->header), "HTTP/1.1 301 Moved Permanently\r\n" @@ -1833,7 +1842,10 @@ static void redirect_https(struct connection *conn) { return; } - redirect(conn, "https://%s%s", host, url); + char* encoded_url = xmalloc(strlen(url) * 3 + 1); + urlencode(url, encoded_url); + redirect(conn, "https://%s%s", host, encoded_url); + free(encoded_url); free(host); free(url); } @@ -2078,21 +2090,23 @@ static void cleanup_sorted_dirlist(struct dlent **list, const ssize_t size) { } } -/* Is this an unreserved character according to - * https://tools.ietf.org/html/rfc3986#section-2.3 - */ -static int is_unreserved(const unsigned char c) { - if (c >= 'a' && c <= 'z') return 1; - if (c >= 'A' && c <= 'Z') return 1; - if (c >= '0' && c <= '9') return 1; +static int needs_encoding(char c) { + /* Is this an unreserved character according to + * https://tools.ietf.org/html/rfc3986#section-2.3 + */ + if (c >= 'a' && c <= 'z') return 0; + if (c >= 'A' && c <= 'Z') return 0; + if (c >= '0' && c <= '9') return 0; switch (c) { case '-': case '.': case '_': case '~': - return 1; + /* Also allow '/' */ + case '/': + return 0; } - return 0; + return 1; } /* Encode string to be an RFC3986-compliant URL part. @@ -2103,13 +2117,13 @@ static void urlencode(const char *src, char *dest) { int i, j; for (i = j = 0; src[i] != '\0'; i++) { - if (!is_unreserved((unsigned char)src[i])) { + if (needs_encoding(src[i])) { dest[j++] = '%'; dest[j++] = hex[(src[i] >> 4) & 0xF]; dest[j++] = hex[ src[i] & 0xF]; - } - else + } else { dest[j++] = src[i]; + } } dest[j] = '\0'; } @@ -2326,7 +2340,10 @@ static void process_get(struct connection *conn) { forward_to = forward_all_url; } if (forward_to) { - redirect(conn, "%s%s", forward_to, decoded_url); + char* encoded_url = xmalloc(strlen(decoded_url) * 3 + 1); + urlencode(decoded_url, encoded_url); + redirect(conn, "%s%s", forward_to, encoded_url); + free(encoded_url); free(decoded_url); return; }