diff options
Diffstat (limited to 'url_handler/main.cpp')
-rw-r--r-- | url_handler/main.cpp | 159 |
1 files changed, 159 insertions, 0 deletions
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; +} |