blob: 3d244fc977c798147dede19f8402ad30dbe9f708 [file] [log] [blame]
/*
* 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 <sales@openvpn.net>
*
* 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();
#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 */