diff options
Diffstat (limited to 'src/main.cc')
| -rw-r--r-- | src/main.cc | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..f9e1af6 --- /dev/null +++ b/src/main.cc @@ -0,0 +1,187 @@ +// -*- mode: c++; c-basic-offset: 2; -*- + +#include "common.hh" + +#include <cstring> +#include <memory> +#include <signal.h> +#include <unistd.h> + +#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(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 <the_jk@yahoo.com>" << std::endl; + return EXIT_SUCCESS; + } + std::unique_ptr<Config> 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(Logger::create_stderr()); + std::unique_ptr<Logger> 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( + 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; +} |
