diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | url_handler/.gitignore | 1 | ||||
| -rw-r--r-- | url_handler/Makefile | 22 | ||||
| -rw-r--r-- | url_handler/handler.cpp | 146 | ||||
| -rw-r--r-- | url_handler/handler.h | 9 | ||||
| -rw-r--r-- | url_handler/main.cpp | 159 | ||||
| -rwxr-xr-x | url_handler/ping_script.py | 27 | 
8 files changed, 367 insertions, 0 deletions
@@ -1,3 +1,4 @@ +.clangd/  *.o  client  server @@ -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="")  | 
