/* * OpenVPN -- An application to securely tunnel IP networks * over a single TCP/UDP port, with support for SSL/TLS-based * session authentication and key exchange, * packet encryption, packet authentication, and * packet compression. * * Copyright (C) 2002-2018 OpenVPN Inc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #elif defined(_MSC_VER) #include "config-msvc.h" #endif #include "syshead.h" #include "init.h" #include "forward.h" #include "multi.h" #include "win32.h" #include "platform.h" #include "memdbg.h" #include "forward-inline.h" #define P2P_CHECK_SIG() EVENT_LOOP_CHECK_SIGNAL(c, process_signal_p2p, c); static bool process_signal_p2p(struct context *c) { remap_signal(c); return process_signal(c); } /* Write our PID to a file */ static void write_pid(const char *filename) { if (filename) { unsigned int pid = 0; FILE *fp = platform_fopen(filename, "w"); if (!fp) { msg(M_ERR, "Open error on pid file %s", filename); } pid = platform_getpid(); fprintf(fp, "%u\n", pid); if (fclose(fp)) { msg(M_ERR, "Close error on pid file %s", filename); } } } /**************************************************************************/ /** * Main event loop for OpenVPN in client mode, where only one VPN tunnel * is active. * @ingroup eventloop * * @param c - The context structure of the single active VPN tunnel. */ static void tunnel_point_to_point(struct context *c) { context_clear_2(c); /* set point-to-point mode */ c->mode = CM_P2P; /* initialize tunnel instance */ init_instance_handle_signals(c, c->es, CC_HARD_USR1_TO_HUP); if (IS_SIG(c)) { return; } /* main event loop */ while (true) { perf_push(PERF_EVENT_LOOP); /* process timers, TLS, etc. */ pre_select(c); P2P_CHECK_SIG(); /* set up and do the I/O wait */ io_wait(c, p2p_iow_flags(c)); P2P_CHECK_SIG(); /* timeout? */ if (c->c2.event_set_status == ES_TIMEOUT) { perf_pop(); continue; } /* process the I/O which triggered select */ process_io(c); P2P_CHECK_SIG(); perf_pop(); } uninit_management_callback(); /* tear down tunnel instance (unless --persist-tun) */ close_instance(c); } #undef PROCESS_SIGNAL_P2P /**************************************************************************/ /** * OpenVPN's main init-run-cleanup loop. * @ingroup eventloop * * This function contains the two outer OpenVPN loops. Its structure is * as follows: * - Once-per-process initialization. * - Outer loop, run at startup and then once per \c SIGHUP: * - Level 1 initialization * - Inner loop, run at startup and then once per \c SIGUSR1: * - Call event loop function depending on client or server mode: * - \c tunnel_point_to_point() * - \c tunnel_server() * - Level 1 cleanup * - Once-per-process cleanup. * * @param argc - Commandline argument count. * @param argv - Commandline argument values. */ static int openvpn_main(int argc, char *argv[]) { struct context c; #if PEDANTIC fprintf(stderr, "Sorry, I was built with --enable-pedantic and I am incapable of doing any real work!\n"); return 1; #endif #ifdef _WIN32 SetConsoleOutputCP(CP_UTF8); #endif CLEAR(c); /* signify first time for components which can * only be initialized once per program instantiation. */ c.first_time = true; /* initialize program-wide statics */ if (init_static()) { /* * This loop is initially executed on startup and then * once per SIGHUP. */ do { /* enter pre-initialization mode with regard to signal handling */ pre_init_signal_catch(); /* zero context struct but leave first_time member alone */ context_clear_all_except_first_time(&c); /* static signal info object */ CLEAR(siginfo_static); c.sig = &siginfo_static; /* initialize garbage collector scoped to context object */ gc_init(&c.gc); /* initialize environmental variable store */ c.es = env_set_create(NULL); #ifdef _WIN32 set_win_sys_path_via_env(c.es); #endif #ifdef ENABLE_MANAGEMENT /* initialize management subsystem */ init_management(&c); #endif /* initialize options to default state */ init_options(&c.options, true); /* parse command line options, and read configuration file */ parse_argv(&c.options, argc, argv, M_USAGE, OPT_P_DEFAULT, NULL, c.es); #ifdef ENABLE_PLUGIN /* plugins may contribute options configuration */ init_verb_mute(&c, IVM_LEVEL_1); init_plugins(&c); open_plugins(&c, true, OPENVPN_PLUGIN_INIT_PRE_CONFIG_PARSE); #endif /* init verbosity and mute levels */ init_verb_mute(&c, IVM_LEVEL_1); /* set dev options */ init_options_dev(&c.options); /* openssl print info? */ if (print_openssl_info(&c.options)) { break; } /* --genkey mode? */ if (do_genkey(&c.options)) { break; } /* tun/tap persist command? */ if (do_persist_tuntap(&c.options)) { break; } /* sanity check on options */ options_postprocess(&c.options); /* show all option settings */ show_settings(&c.options); /* print version number */ msg(M_INFO, "%s", title_string); #ifdef _WIN32 show_windows_version(M_INFO); #endif show_library_versions(M_INFO); /* misc stuff */ pre_setup(&c.options); /* test crypto? */ if (do_test_crypto(&c.options)) { break; } /* Query passwords before becoming a daemon if we don't use the * management interface to get them. */ #ifdef ENABLE_MANAGEMENT if (!(c.options.management_flags & MF_QUERY_PASSWORDS)) #endif init_query_passwords(&c); /* become a daemon if --daemon */ if (c.first_time) { c.did_we_daemonize = possibly_become_daemon(&c.options); write_pid(c.options.writepid); } #ifdef ENABLE_MANAGEMENT /* open management subsystem */ if (!open_management(&c)) { break; } /* query for passwords through management interface, if needed */ if (c.options.management_flags & MF_QUERY_PASSWORDS) { init_query_passwords(&c); } #endif /* set certain options as environmental variables */ setenv_settings(c.es, &c.options); /* finish context init */ context_init_1(&c); do { /* run tunnel depending on mode */ switch (c.options.mode) { case MODE_POINT_TO_POINT: tunnel_point_to_point(&c); break; #if P2MP_SERVER case MODE_SERVER: tunnel_server(&c); break; #endif default: ASSERT(0); } /* indicates first iteration -- has program-wide scope */ c.first_time = false; /* any signals received? */ if (IS_SIG(&c)) { print_signal(c.sig, NULL, M_INFO); } /* pass restart status to management subsystem */ signal_restart_status(c.sig); } while (c.sig->signal_received == SIGUSR1); env_set_destroy(c.es); uninit_options(&c.options); gc_reset(&c.gc); } while (c.sig->signal_received == SIGHUP); } context_gc_free(&c); #ifdef ENABLE_MANAGEMENT /* close management interface */ close_management(); #endif /* uninitialize program-wide statics */ uninit_static(); openvpn_exit(OPENVPN_EXIT_STATUS_GOOD); /* exit point */ return 0; /* NOTREACHED */ } #ifdef _WIN32 int wmain(int argc, wchar_t *wargv[]) { char **argv; int ret; int i; if ((argv = calloc(argc+1, sizeof(char *))) == NULL) { return 1; } for (i = 0; i < argc; i++) { int n = WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, NULL, 0, NULL, NULL); argv[i] = malloc(n); WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], n, NULL, NULL); } ret = openvpn_main(argc, argv); for (i = 0; i < argc; i++) { free(argv[i]); } free(argv); return ret; } #else /* ifdef _WIN32 */ int main(int argc, char *argv[]) { return openvpn_main(argc, argv); } #endif /* ifdef _WIN32 */