#include #include #include #include #include #include #include #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> state_map; uint16_t handle_icmp(uint32_t source_addr, const array &payload) { auto state_it = state_map.find(source_addr); if (state_it == state_map.end()) { state_it = state_map.emplace(source_addr, make_unique()).first; } State &state = *state_it->second; switch (payload[0]) { case 100: // url fragment // fprintf(stderr, "msg(%u): url fragment\n", source_addr); { lock_guard 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 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 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 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; }