Commit Diff


commit - 679cbc0acc9ceb3822cb8574013f0bfcb6460b0d
commit + 5b1ce226061f6be85ef5848a7df637ec2f069704
blob - 313082479108a8f36277c1ba6d8cb506e91fe54f
blob + cb103a9a567aed21307000a84fbdb6a952ee0c64
--- shared/transport.cc
+++ shared/transport.cc
@@ -47,8 +47,9 @@ tcb::span<char> read(not_null<struct tls *> ctx, tcb::
 
 const std::array<char, 2> CRLF{'\r', '\n'};
 const std::array<char, 1> SPACE{' '};
-const std::array<char, 2> STATUS_20_SUCCESS{'2', '0'};
-const std::array<char, 2> STATUS_59_BAD_REQUEST{'5', '9'};
+const status_t STATUS_20_SUCCESS{'2', '0'};
+const status_t STATUS_53_PROXY_REQUEST_REFUSED{'5', '3'};
+const status_t STATUS_59_BAD_REQUEST{'5', '9'};
 
 
 bool init()
@@ -165,7 +166,7 @@ tcb::span<char> read_request(not_null<struct tls *> ct
 }
 
 
-bool send_response(not_null<struct tls *> ctx, std::array<char, 2> status, tcb::span<const char> meta)
+bool send_response(not_null<struct tls *> ctx, status_t status, tcb::span<const char> meta)
 {
     // > <STATUS><SPACE><META><CR><LF>
     std::array<char, status.size() + SPACE.size() + MAX_META_LENGTH + CRLF.size()> buff;
@@ -189,7 +190,7 @@ bool send(not_null<struct tls *> ctx, tcb::span<const 
         if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT)
             continue;
         break;
-        buff = tcb::span<const char>{&buff[ret], buff.size() - ret};
+        buff = decltype(buff){&buff[ret], buff.size() - ret};
     }
     if (ret == -1)
     {
blob - b7518fc07c0e247aef385cbc777f032258efc06c
blob + 137cf9aaf8f4cdfe634162e8481daef491e79f28
--- shared/transport.h
+++ shared/transport.h
@@ -23,8 +23,10 @@ constexpr auto MAX_URL_LENGTH = 1024;
 constexpr auto MAX_META_LENGTH = 1024;
 
 extern const std::array<char, 2> CRLF;
-extern const std::array<char, 2> STATUS_20_SUCCESS;
-extern const std::array<char, 2> STATUS_59_BAD_REQUEST;
+using status_t = std::array<char, 2>;
+extern const status_t STATUS_20_SUCCESS;
+extern const status_t STATUS_53_PROXY_REQUEST_REFUSED;
+extern const status_t STATUS_59_BAD_REQUEST;
 
 
 constexpr auto MAX_REQUEST_LENGTH = MAX_URL_LENGTH + CRLF.size();
@@ -87,7 +89,7 @@ tcb::span<char> read_request(not_null<struct tls *> ct
 
 
 /** Write gemini response */
-bool send_response(not_null<struct tls *> ctx, std::array<char, 2> status, tcb::span<const char> meta);
+bool send_response(not_null<struct tls *> ctx, status_t status, tcb::span<const char> meta);
 
 
 /** Send raw bytes */
blob - 66f41bfe0404e451318143c2be6792916a4a1513
blob + 8244dc2b89c635fe09a9d5489f4765b3ff222a03
--- vostokd/vostokd.cc
+++ vostokd/vostokd.cc
@@ -30,10 +30,14 @@ constexpr tcb::span<const char> cut_null(const char (&
 
 namespace meta
 {
-const auto badrequest = cut_null("Bad request");
+const auto bad_request = cut_null("Bad request");
+const auto url_too_short = cut_null("URL is too short");
+const auto non_gemini = cut_null("No proxying to non-Gemini content");
 }   // namespace meta
 
+const auto gemini_scheme = cut_null("gemini://");
 
+
 struct command_line_arguments
 {
     not_null<czstring> m_addr{"127.0.0.1"};
@@ -111,18 +115,45 @@ void client_thread(transport::accepted_context::value 
     assert(ctx);
 
     std::array<char, transport::MAX_REQUEST_LENGTH> buffer;
-    const auto url = transport::read_request(ctx.get_ctx(), buffer);
+    auto url = transport::read_request(ctx.get_ctx(), buffer);
+    if (!url.size())
+    {
+        transport::send_response(ctx.get_ctx(), transport::STATUS_59_BAD_REQUEST, meta::bad_request);
+        return;
+    }
 
+    // check and skip scheme
+    if (url.size() < gemini_scheme.size())
+    {
+        transport::send_response(ctx.get_ctx(), transport::STATUS_59_BAD_REQUEST, meta::url_too_short);
+        return;
+    }
+    if (!std::equal(gemini_scheme.begin(), gemini_scheme.end(), url.begin()))
+    {
+        transport::send_response(ctx.get_ctx(), transport::STATUS_53_PROXY_REQUEST_REFUSED, meta::non_gemini);
+        return;
+    }
+    url = decltype(url){&url[gemini_scheme.size()], url.size() - gemini_scheme.size()};
+
+    // skip domain[:port]
+    const char *current = url.begin();
+    for (; current != url.end(); ++current)
+    {
+        if (*current == '/')
+        {
+            const auto skip_len = (current + 1) - url.begin();
+            url = decltype(url){&url[skip_len], url.size() - skip_len};
+            break;
+        }
+    }
+    if (current == url.end())
+        url = decltype(url){};
+
     error::g_log << "Requested URL: \"";
     for (auto c : url)
         error::g_log << c;
     error::g_log << "\"" << std::endl;
 
-    if (!url.size())
-    {
-        transport::send_response(ctx.get_ctx(), transport::STATUS_59_BAD_REQUEST, meta::badrequest);
-        return;
-    }
 
     // > If <META> is an empty string, the MIME type MUST default to "text/gemini; charset=utf-8".
     transport::send_response(ctx.get_ctx(), transport::STATUS_20_SUCCESS, tcb::span<const char>{});