// g++ -std=c++17 -march=native -O3 -lcrypto -lpthread gewalt.cpp -o gewalt #include #include #include #include #include #include #include #include #include #include #include const unsigned num_threads = std::thread::hardware_concurrency(); #include static std::string hash(std::string const& s) { SHA_CTX ctx; if (!SHA1_Init(&ctx)) throw; if (!SHA1_Update(&ctx, s.data(), s.length())) throw; std::string d(SHA_DIGEST_LENGTH, 0); if (!SHA1_Final((uint8_t *) &d[0], &ctx)) throw; return d; } static std::u32string kapot(std::string const& s) { std::u32string r(s.size(), 0); size_t o = 0; for (size_t i = 0; i < s.length(); ) { auto T = [](uint8_t c) { return (c < 0x80) ? 1 /* ASCII */ : (c & 0xc0) == 0x80 ? 0 /* continuation */ : (c & 0xe0) == 0xc0 ? 2 /* 2-byte chunk */ : (c & 0xf0) == 0xe0 ? 3 /* 3-byte chunk */ : (c & 0xf8) == 0xf0 ? 4 /* 4-byte chunk */ : -1; }; uint32_t c = s[i++]; auto cont = [&]() { c = (c << 6) | (s[i++] & 0x3f); }; switch (T(c)) { case -1: case 0: invalid: c = 0xfffd; /* fall through */ case 1: valid: r[o++] = c; break; case 2: if (c &= 0x1f, i+0 >= s.size() || T(s[i+0])) goto invalid; goto one; case 3: if (c &= 0x1f, i+1 >= s.size() || T(s[i+0]) || T(s[i+1])) goto invalid; goto two; case 4: if (c &= 0x1f, i+2 >= s.size() || T(s[i+0]) || T(s[i+1]) || T(s[i+2])) goto invalid; cont(); two: cont(); one: cont(); goto valid; } } r.resize(o); return r; } std::atomic hcount = 0, kcount = 0; typedef std::unordered_map tab_t; tab_t tab0, tab1; std::mutex mtx; std::array ip; std::string cmd0, cmd1; class stuffer_t { private: std::array cnts; size_t step; std::string cmd; public: stuffer_t(size_t t, size_t s, std::string c) : cnts{t}, step(s), cmd(c) {} std::string operator()() { //XXX this is by far not the most efficient way of doing this, but yeah if (++cnts[3] >= cnts[0]) { cnts[3] = 0; if (++cnts[2] >= cnts[0]) { cnts[2] = 0; if (++cnts[1] >= cnts[0]) { cnts[1] = 0; cnts[0] += step; } } } std::stringstream o; for (size_t i = 0; i < 4; ++i) o << (i ? "." : "") << std::string(cnts[i], '0') << (unsigned) ip[i]; o << "|" << cmd; return o.str(); } }; void go(size_t tid) { //XXX tid stuff is a hack, but YOLO bool one = tid & 1; stuffer_t next(tid >> 1, (num_threads + 1) >> 1, one ? cmd1 : cmd0); tab_t& mytab = one ? tab1 : tab0; tab_t& thtab = one ? tab0 : tab1; uint64_t myhcount = 0, mykcount = 0; while (1) { std::string r = next(); { ++myhcount; auto h = hash(r); if ((h.size()+3)/4 < (size_t) std::count_if(h.begin(), h.end(), [](unsigned char c) { return c < 0x80; })) continue; ++mykcount; auto k = kapot(h); if (k.size() > 3 + (size_t) std::count(k.begin(), k.end(), 0xfffd)) continue; std::lock_guard lck(mtx); hcount += myhcount, myhcount = 0; kcount += mykcount, mykcount = 0; if (thtab.find(k) != thtab.end()) { mytab[k] = r; std::cerr << "\r\x1b[K" << "\x1b[32m"; std::cout << tab0[k] << std::endl << tab1[k] << std::endl; std::cerr << "\x1b[0m"; std::cerr << std::hex; bool first = true; for (uint32_t c: k) std::cerr << (first ? first = false, "" : " ") << c; std::cerr << std::endl; std::cerr << std::dec << "hash count: \x1b[35m" << hcount << "\x1b[0m"; { std::stringstream s; s << std::fixed << std::setprecision(2) << log(hcount|1)/log(2); std::cerr << " (2^\x1b[35m" << std::setw(5) << s.str() << "\x1b[0m" << ")" << std::endl; } std::cerr << "kapot count: " << "\x1b[35m" << kcount << "\x1b[0m"; { std::stringstream s; s << std::fixed << std::setprecision(2) << log(kcount|1)/log(2); std::cerr << " (2^\x1b[35m" << std::setw(5) << s.str() << "\x1b[0m)" << std::endl; } std::cerr << "table sizes: \x1b[35m" << tab0.size() << "\x1b[0m \x1b[35m" << tab1.size() << "\x1b[0m" << std::endl; exit(0); } if (mytab.size() < (1 << 20)) mytab[k] = r; } hcount += myhcount; kcount += mykcount; } } void status() { while (1) { { std::lock_guard lck(mtx); std::cerr << "\r\x1b[K"; std::cerr << "hash count: \x1b[35m" << std::setw(12) << hcount << "\x1b[0m "; { std::stringstream s; s << std::fixed << std::setprecision(2) << log(hcount|1)/log(2); std::cerr << "(2^\x1b[35m" << std::setw(5) << s.str() << "\x1b[0m) | "; } std::cerr << "kapot count: \x1b[35m" << std::setw(12) << kcount << "\x1b[0m "; { std::stringstream s; s << std::fixed << std::setprecision(2) << log(kcount|1)/log(2); std::cerr << "(2^\x1b[35m" << std::setw(5) << s.str() << "\x1b[0m) | "; } std::cerr << "tables: \x1b[35m" << std::setw(9) << tab0.size() << " " << std::setw(9) << tab1.size() << "\x1b[0m " << std::flush; } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } int main(int argc, char **argv) { if (argc < 2) { std::cerr << "\x1b[31mneed IPv4 in argv[1]\x1b[0m" << std::endl; exit(1); } { std::stringstream ss(argv[1]); for (auto& v: ip) { std::string s; std::getline(ss, s, '.'); int n = std::atoi(s.c_str()); if (n < std::numeric_limits::min() || n > std::numeric_limits::max()) goto bad_ip; v = n; } if (!ss) { bad_ip: std::cerr << "\x1b[31mbad IPv4 given?\x1b[0m" << std::endl; exit(2); } } if (argc < 4) { std::cerr << "\x1b[31mneed commands in argv[2] and argv[3]\x1b[0m" << std::endl; exit(2); } cmd0 = argv[2]; cmd1 = argv[3]; std::thread status_thread(status); std::vector ts; for (unsigned i = 0; i < num_threads; ++i) ts.push_back(std::thread(go, i)); for (auto& t: ts) t.join(); }