summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Smeding <tom.smeding@gmail.com>2019-11-05 10:31:02 +0100
committerTom Smeding <tom.smeding@gmail.com>2019-11-05 10:32:46 +0100
commit18417582b74eae55180c2f485784575ad56b3f75 (patch)
tree92dd3f0e80cc3e7b02f46485f1700ef2c598093c
parentd179a5b469d609a7c7f15d841dfb95fe77b3125e (diff)
Add url handler for seqnum_serverHEADmaster
-rw-r--r--.gitignore1
-rw-r--r--Makefile2
-rw-r--r--url_handler/.gitignore1
-rw-r--r--url_handler/Makefile22
-rw-r--r--url_handler/handler.cpp146
-rw-r--r--url_handler/handler.h9
-rw-r--r--url_handler/main.cpp159
-rwxr-xr-xurl_handler/ping_script.py27
8 files changed, 367 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index a781ccc..005f32b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.clangd/
*.o
client
server
diff --git a/Makefile b/Makefile
index b63a27b..518a16f 100644
--- a/Makefile
+++ b/Makefile
@@ -10,9 +10,11 @@ OBJECTS = $(patsubst %.c,%.o,$(SOURCES))
.PHONY: all clean
all: $(TARGETS)
+ @$(MAKE) -C url_handler all
clean:
rm -f $(TARGETS) *.o
+ @$(MAKE) -C url_handler clean
$(TARGETS): %: %.o $(OBJECTS)
diff --git a/url_handler/.gitignore b/url_handler/.gitignore
new file mode 100644
index 0000000..8cffaca
--- /dev/null
+++ b/url_handler/.gitignore
@@ -0,0 +1 @@
+url_handler
diff --git a/url_handler/Makefile b/url_handler/Makefile
new file mode 100644
index 0000000..3c03697
--- /dev/null
+++ b/url_handler/Makefile
@@ -0,0 +1,22 @@
+CXX = g++
+CXXFLAGS = -Wall -Wextra -O2 -g -std=c++17 -fwrapv -pthread
+LDFLAGS = -pthread
+
+TARGET = url_handler
+
+SOURCES = $(wildcard *.cpp)
+OBJECTS = $(SOURCES:.cpp=.o)
+
+.PHONY: all clean
+
+all: $(TARGET)
+
+clean:
+ rm -f $(TARGET) *.o
+
+
+$(TARGET): $(OBJECTS)
+ $(CXX) $^ -o $@ $(LDFLAGS)
+
+%.o: %.cpp $(wildcard *.h)
+ $(CXX) $(CXXFLAGS) -c -o $@ $<
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;
+}
diff --git a/url_handler/handler.h b/url_handler/handler.h
new file mode 100644
index 0000000..135e5b7
--- /dev/null
+++ b/url_handler/handler.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <array>
+#include <cstdint>
+
+using namespace std;
+
+
+uint16_t handle_icmp(uint32_t source_addr, const array<uint8_t, 16> &payload);
diff --git a/url_handler/main.cpp b/url_handler/main.cpp
new file mode 100644
index 0000000..47166db
--- /dev/null
+++ b/url_handler/main.cpp
@@ -0,0 +1,159 @@
+#include <vector>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <cstdio>
+#include <cstring>
+#include <cassert>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+#include "handler.h"
+
+using namespace std;
+
+
+struct Pipes {
+ int rfd, wfd;
+};
+
+static pair<pid_t, Pipes> start_process(const vector<string> &cmd) {
+ int host2child[2], child2host[2];
+ if (pipe(host2child) < 0 || pipe(child2host) < 0) {
+ perror("pipe");
+ exit(1);
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(host2child[1]);
+ close(child2host[0]);
+ dup2(host2child[0], STDIN_FILENO);
+ dup2(child2host[1], STDOUT_FILENO);
+
+ vector<char*> argv(cmd.size() + 1);
+ for (size_t i = 0; i < cmd.size(); i++) {
+ argv[i] = (char*)cmd[i].data();
+ fprintf(stderr, "argv[%zu] = %p (%s)\n", i, cmd[i].data(), cmd[i].data());
+ }
+ argv[cmd.size()] = nullptr;
+
+ execvp(argv[0], argv.data());
+ perror("execvp");
+ exit(1);
+ }
+
+ if (pid < 0) {
+ perror("fork");
+ exit(1);
+ }
+
+ close(host2child[0]);
+ close(child2host[1]);
+ return make_pair(pid, Pipes{child2host[0], host2child[1]});
+}
+
+static bool poll_process_exited(pid_t pid) {
+ int status;
+ pid_t ret = waitpid(pid, &status, WNOHANG);
+ if (ret == 0) return false;
+ if (ret < 0) {
+ if (errno == EINTR) return false; // wut? we said WNOHANG, right?
+ perror("wait");
+ return true;
+ }
+ assert(ret == pid);
+ return WIFEXITED(status);
+}
+
+static void wait_process_exited(pid_t pid) {
+ while (true) {
+ int status;
+ pid_t ret = waitpid(pid, &status, 0);
+ if (ret < 0) {
+ if (errno == EINTR) continue;
+ perror("wait");
+ exit(1);
+ }
+ if (ret == 0) continue; // wut?
+ assert(ret == pid);
+ if (WIFEXITED(pid)) break;
+ }
+}
+
+int main(int argc, char **argv) {
+ vector<string> server_args(argc - 1);
+ for (int i = 0; i < argc - 1; i++) server_args[i] = argv[i + 1];
+
+ // make sure we actually receive the signal
+ signal(SIGCHLD, [](int) {});
+
+ pid_t child_pid;
+ Pipes pipes;
+ tie(child_pid, pipes) = start_process(server_args);
+
+ int exit_code = 0;
+
+ while (true) {
+ fd_set inset;
+ FD_ZERO(&inset);
+ FD_SET(pipes.rfd, &inset);
+
+ int ret = select(pipes.rfd + 1, &inset, nullptr, nullptr, nullptr);
+
+ if (poll_process_exited(child_pid)) {
+ child_pid = -1;
+ goto exit_cleanup;
+ }
+
+ if (ret < 0) {
+ if (errno == EINTR) continue;
+ perror("select");
+ exit_code = 1;
+ goto exit_cleanup;
+ }
+
+ if (FD_ISSET(pipes.rfd, &inset)) {
+ uint8_t buffer[24];
+ size_t cursor = 0;
+ while (cursor < 24) {
+ ssize_t nread = read(pipes.rfd, buffer + cursor, 24 - cursor);
+ if (nread < 0) {
+ perror("read");
+ goto exit_cleanup;
+ }
+ cursor += nread;
+ }
+
+ if (memcmp(buffer, "payl", 4) != 0) {
+ fprintf(stderr, "Invalid event message from server: id bytes '%c%c%c%c'\n",
+ buffer[0], buffer[1], buffer[2], buffer[3]);
+ fprintf(stderr, "<");
+ fwrite(buffer, 1, 24, stderr);
+ fprintf(stderr, ">\n");
+ exit_code = 1;
+ goto exit_cleanup;
+ }
+
+ uint32_t source_addr = *(uint32_t*)&buffer[4];
+ array<uint8_t, 16> payload;
+ memcpy(payload.data(), &buffer[8], 16);
+ uint16_t seqnum = handle_icmp(source_addr, payload);
+
+ if (write(pipes.wfd, &seqnum, 2) != 2) {
+ perror("write");
+ exit_code = 1;
+ goto exit_cleanup;
+ }
+ }
+ }
+
+exit_cleanup:
+ if (child_pid != -1) {
+ kill(child_pid, SIGTERM);
+ wait_process_exited(child_pid);
+ }
+
+ return exit_code;
+}
diff --git a/url_handler/ping_script.py b/url_handler/ping_script.py
new file mode 100755
index 0000000..30f15e5
--- /dev/null
+++ b/url_handler/ping_script.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+import sys, os, time, subprocess
+
+url = sys.argv[1]
+if len(url) % 15 == 0: url += "\0"
+while len(url) % 15 != 0: url += "\0"
+
+for i in range(len(url) // 15):
+ subprocess.check_call(["ping", "-c", "1", "-p", "64" + "".join("{:02X}".format(ord(x)) for x in url[15*i:15*i+15]), "127.0.0.1"], stdout=subprocess.DEVNULL)
+
+while True:
+ out = subprocess.check_output(["ping", "-c", "1", "-p", "65", "127.0.0.1"], stderr=subprocess.STDOUT).decode("utf-8")
+ if out.find("icmp_seq=1 ") != -1:
+ break
+ time.sleep(0.2)
+
+while True:
+ out = subprocess.check_output(["ping", "-c", "1", "-p", "66", "127.0.0.1"], stderr=subprocess.STDOUT).decode("utf-8")
+ idx = out.find("icmp_seq=")
+ idx2 = out.find(" ", idx)
+ seqnum = int(out[idx+9:idx2])
+ n0 = seqnum % 256
+ n1 = seqnum >> 8
+ if n0 == 0: break
+ print(chr(n0), end="")
+ if n1 == 0: break
+ print(chr(n1), end="")