summaryrefslogtreecommitdiff
path: root/url_handler/handler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'url_handler/handler.cpp')
-rw-r--r--url_handler/handler.cpp146
1 files changed, 146 insertions, 0 deletions
diff --git a/url_handler/handler.cpp b/url_handler/handler.cpp
new file mode 100644
index 0000000..b202d5b
--- /dev/null
+++ b/url_handler/handler.cpp
@@ -0,0 +1,146 @@
+#include <fstream>
+#include <filesystem>
+#include <unordered_map>
+#include <mutex>
+#include <thread>
+#include <memory>
+#include <cstdlib>
+#include "handler.h"
+
+using namespace std;
+
+
+static string shell_escape(const string &str) {
+ string res;
+ res.reserve(str.size() + 16);
+ res += '\'';
+
+ for (char c : str) {
+ if (c == '\'') res += "'\"'\"'";
+ else res += c;
+ }
+
+ res += '\'';
+ return res;
+}
+
+static string read_file(const string &fname) {
+ ifstream f(fname);
+ if (!f) return {};
+
+ string contents;
+ char buffer[4096];
+ while (true) {
+ f.read(buffer, sizeof buffer);
+ contents.insert(contents.size(), buffer, f.gcount());
+ if (!f) break;
+ }
+
+ return contents;
+}
+
+static string download_url(const string &url) {
+ string tempdir_name = "/tmp/tmp.url_handler.XXXXXX";
+ if (mkdtemp(tempdir_name.data()) == nullptr) {
+ perror("mkdtemp");
+ return {};
+ }
+
+ string tempfile_name = tempdir_name + "/file";
+
+ string cmd = "curl -sL " + shell_escape(url) + " >" + shell_escape(tempfile_name);
+ // fprintf(stderr, "cmd = %s\n", cmd.data());
+
+ system(cmd.data());
+
+ string response = read_file(tempfile_name);
+ filesystem::remove_all(tempdir_name);
+
+ fprintf(stderr, "Received response for url '%s'\n", url.data());
+
+ return response;
+}
+
+struct State {
+ string url;
+
+ thread download_thread;
+
+ // mutex protects 'response' and 'response_present'; the download thread
+ // sets 'response_present' to true and simultaneously 'response' to the
+ // right value. After 'response_present' can be observed to be true by the
+ // main thread, the download thread doesn't touch anything anymore.
+ mutex res_mt;
+ bool response_present = false;
+ string response;
+
+ // owned by host thread
+ size_t rescur = 0;
+};
+
+static unordered_map<uint32_t, unique_ptr<State>> state_map;
+
+uint16_t handle_icmp(uint32_t source_addr, const array<uint8_t, 16> &payload) {
+ auto state_it = state_map.find(source_addr);
+ if (state_it == state_map.end()) {
+ state_it = state_map.emplace(source_addr, make_unique<State>()).first;
+ }
+ State &state = *state_it->second;
+
+ switch (payload[0]) {
+ case 100: // url fragment
+ // fprintf(stderr, "msg(%u): url fragment\n", source_addr);
+ {
+ lock_guard<mutex> guard(state.res_mt);
+ if (state.response_present) return 0;
+ }
+ for (size_t i = 1; i < 16; i++) {
+ if (payload[i] == '\0') {
+ fprintf(stderr, "URL received: <%s>\n", state.url.data());
+ state.download_thread = thread([&state]() {
+ string response = download_url(state.url);
+
+ {
+ lock_guard<mutex> guard(state.res_mt);
+ state.response = move(response);
+ state.response_present = true;
+ }
+
+ // fprintf(stderr, "[dlth] response received: <%s>\n", state.response.data());
+ });
+ break;
+ }
+ state.url.push_back(payload[i]);
+ }
+ return 1;
+
+ case 101: { // query whether response is ready
+ // fprintf(stderr, "msg(%u): ready query\n", source_addr);
+ lock_guard<mutex> guard(state.res_mt);
+ return state.response_present ? 1 : 0;
+ }
+
+ case 102: { // get response
+ // fprintf(stderr, "msg(%u): get_response\n", source_addr);
+ {
+ lock_guard<mutex> guard(state.res_mt);
+ if (!state.response_present) return 0;
+ }
+
+ uint16_t retval = 0;
+ for (size_t i = 0; i < 2 && state.rescur < state.response.size(); i++) {
+ retval |= (uint16_t)state.response[state.rescur] << (8 * i);
+ state.rescur++;
+ }
+
+ if (state.rescur == state.response.size()) {
+ state.download_thread.join(); // should already have exited
+ state_map.erase(state_it);
+ }
+ // fprintf(stderr, " -> retval %016X\n", (unsigned)retval);
+ return retval;
+ }
+ }
+
+ return 0;
+}