// -*- mode: c++; c-basic-offset: 2; -*- #include "common.hh" #include #include #include #include #include "args.hh" #include "config.hh" #include "io.hh" #include "logger.hh" #include "proxy.hh" namespace { Proxy* g_proxy; void proxy_quit(int UNUSED(sig)) { g_proxy->quit(); } void proxy_reload(int UNUSED(sig)) { g_proxy->reload(); } void setup_signals(Proxy* proxy) { g_proxy = proxy; struct sigaction action; memset(&action, 0, sizeof(action)); action.sa_handler = proxy_quit; action.sa_flags = SA_RESTART; sigaction(SIGINT, &action, nullptr); sigaction(SIGQUIT, &action, nullptr); action.sa_handler = proxy_reload; sigaction(SIGHUP, &action, nullptr); } std::string get_cwd() { std::string ret; char buf[128]; auto ptr = getcwd(buf, 128); if (ptr) { ret.assign(ptr, strlen(ptr)); return ret; } if (errno != ERANGE) return ret; ret.resize(1024); while (true) { ptr = getcwd(&ret[0], ret.size()); if (ptr) { ret.resize(strlen(ret.data())); break; } if (errno != ERANGE) { ret.resize(0); break; } ret.resize(ret.size() * 2); } return ret; } } // namespace int main(int argc, char** argv) { std::unique_ptr args(Args::create()); args->add('C', "config", "FILE", "load config from FILE instead of default"); args->add('F', "foreground", "do not fork into background and log to stdout"); args->add('m', "monitor", "HOST:PORT", "accept monitors on HOST:PORT"); args->add('h', "help", "display this text and exit."); args->add('V', "version", "display version and exit."); if (!args->run(argc, argv)) { std::cerr << "Try `tp --help` for usage." << std::endl; return EXIT_FAILURE; } if (args->is_set('h')) { std::cout << "Usage: `tp [OPTIONS...] [BIND][:PORT]`\n" << "Transparent proxy.\n" << '\n'; args->print_help(); return EXIT_SUCCESS; } if (args->is_set('V')) { std::cout << "TransparentProxy version " VERSION << " written by Joel Klinghed " << std::endl; return EXIT_SUCCESS; } std::unique_ptr config(Config::create()); switch (args->arguments().size()) { case 0: break; case 1: { auto arg = args->arguments().front(); auto colon = arg.find(':'); if (colon == std::string::npos) { config->set("proxy_bind", arg); } else { if (colon > 0) { config->set("proxy_bind", arg.substr(0, colon)); } config->set("proxy_port", arg.substr(colon + 1)); } break; } default: std::cerr << "Too many arguments.\n" << "Try `tp --help` for usage." << std::endl; return EXIT_FAILURE; } if (args->is_set('F')) { config->set("foreground", "true"); } auto monitor = args->arg('m', nullptr); if (monitor) { auto str = std::string(monitor); auto colon = str.find(':'); if (colon == 0 || colon == std::string::npos) { std::cerr << "Invalid argument to monitor, expected HOST:PORT not: " << str << std::endl; return EXIT_FAILURE; } config->set("monitor", "true"); config->set("monitor_bind", str.substr(0, colon)); config->set("monitor_port", str.substr(colon + 1)); } auto configfile = args->arg('C', nullptr); if (configfile) { config->load_file(configfile); } else { config->load_name("tp"); } if (!config->good()) { std::cerr << "Error loading config\n" << config->last_error() << std::endl; return EXIT_FAILURE; } std::unique_ptr logger(Logger::create_stderr()); std::unique_ptr file_logger; auto logfile = args->arg('l', nullptr); bool logfile_from_argument; if (!logfile) { logfile = config->get("logfile", nullptr); logfile_from_argument = false; } else { logfile_from_argument = true; } if (logfile) { if (logfile[0] != '/') { logger->out(Logger::ERR, "Logfile need to be an absolute path, not: %s", logfile); return EXIT_FAILURE; } file_logger.reset(Logger::create_file(logfile)); if (!file_logger) { logger->out(Logger::ERR, "Unable to open %s for logging: %s", logfile, strerror(errno)); return EXIT_FAILURE; } } io::auto_fd accept_socket(Proxy::setup_accept_socket(config.get(), logger.get())); if (!accept_socket) return EXIT_FAILURE; io::auto_fd monitor_socket; if (config->get("monitor", false)) { monitor_socket.reset(Proxy::setup_monitor_socket(config.get(), logger.get())); } auto foreground = config->get("foreground", false); auto cwd = get_cwd(); std::unique_ptr proxy( Proxy::create(config.get(), cwd, configfile, foreground ? "bogus" : (logfile_from_argument ? logfile : nullptr), foreground ? logger.get() : file_logger.get(), accept_socket.release(), monitor_socket.release())); if (!foreground) { if (daemon(0, 0)) { logger->out(Logger::ERR, "Failed to fork: %s", strerror(errno)); return EXIT_FAILURE; } } setup_signals(proxy.get()); return proxy->run() ? EXIT_SUCCESS : EXIT_FAILURE; }