/* * 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-2017 OpenVPN Technologies, 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 "buffer.h" #include "error.h" #include "win32.h" #include "init.h" #include "status.h" #include "sig.h" #include "occ.h" #include "manage.h" #include "openvpn.h" #include "memdbg.h" /* Handle signals */ struct signal_info siginfo_static; /* GLOBAL */ struct signame { int value; const char *upper; const char *lower; }; static const struct signame signames[] = { { SIGINT, "SIGINT", "sigint"}, { SIGTERM, "SIGTERM", "sigterm" }, { SIGHUP, "SIGHUP", "sighup" }, { SIGUSR1, "SIGUSR1", "sigusr1" }, { SIGUSR2, "SIGUSR2", "sigusr2" } }; int parse_signal(const char *signame) { int i; for (i = 0; i < (int)SIZE(signames); ++i) { if (!strcmp(signame, signames[i].upper)) { return signames[i].value; } } return -1; } const char * signal_name(const int sig, const bool upper) { int i; for (i = 0; i < (int)SIZE(signames); ++i) { if (sig == signames[i].value) { return upper ? signames[i].upper : signames[i].lower; } } return "UNKNOWN"; } const char * signal_description(const int signum, const char *sigtext) { if (sigtext) { return sigtext; } else { return signal_name(signum, false); } } void throw_signal(const int signum) { siginfo_static.signal_received = signum; siginfo_static.source = SIG_SOURCE_HARD; } void throw_signal_soft(const int signum, const char *signal_text) { siginfo_static.signal_received = signum; siginfo_static.source = SIG_SOURCE_SOFT; siginfo_static.signal_text = signal_text; } static void signal_reset(struct signal_info *si) { if (si) { si->signal_received = 0; si->signal_text = NULL; si->source = SIG_SOURCE_SOFT; } } void print_signal(const struct signal_info *si, const char *title, int msglevel) { if (si) { const char *type = (si->signal_text ? si->signal_text : ""); const char *t = (title ? title : "process"); const char *hs = NULL; switch (si->source) { case SIG_SOURCE_SOFT: hs = "soft"; break; case SIG_SOURCE_HARD: hs = "hard"; break; case SIG_SOURCE_CONNECTION_FAILED: hs = "connection failed(soft)"; break; default: ASSERT(0); } switch (si->signal_received) { case SIGINT: case SIGTERM: msg(msglevel, "%s[%s,%s] received, %s exiting", signal_name(si->signal_received, true), hs, type, t); break; case SIGHUP: case SIGUSR1: msg(msglevel, "%s[%s,%s] received, %s restarting", signal_name(si->signal_received, true), hs, type, t); break; default: msg(msglevel, "Unknown signal %d [%s,%s] received by %s", si->signal_received, hs, type, t); break; } } else { msg(msglevel, "Unknown signal received"); } } /* * Call management interface with restart info */ void signal_restart_status(const struct signal_info *si) { #ifdef ENABLE_MANAGEMENT if (management) { int state = -1; switch (si->signal_received) { case SIGINT: case SIGTERM: state = OPENVPN_STATE_EXITING; break; case SIGHUP: case SIGUSR1: state = OPENVPN_STATE_RECONNECTING; break; } if (state >= 0) { management_set_state(management, state, si->signal_text ? si->signal_text : signal_name(si->signal_received, true), NULL, NULL, NULL, NULL); } } #endif /* ifdef ENABLE_MANAGEMENT */ } #ifdef HAVE_SIGNAL_H /* normal signal handler, when we are in event loop */ static void signal_handler(const int signum) { throw_signal(signum); signal(signum, signal_handler); } #endif /* set handlers for unix signals */ #ifdef HAVE_SIGNAL_H #define SM_UNDEF 0 #define SM_PRE_INIT 1 #define SM_POST_INIT 2 static int signal_mode; /* GLOBAL */ #endif void pre_init_signal_catch(void) { #ifndef _WIN32 #ifdef HAVE_SIGNAL_H signal_mode = SM_PRE_INIT; signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); signal(SIGHUP, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); signal(SIGPIPE, SIG_IGN); #endif /* HAVE_SIGNAL_H */ #endif /* _WIN32 */ } void post_init_signal_catch(void) { #ifndef _WIN32 #ifdef HAVE_SIGNAL_H signal_mode = SM_POST_INIT; signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); signal(SIGHUP, signal_handler); signal(SIGUSR1, signal_handler); signal(SIGUSR2, signal_handler); signal(SIGPIPE, SIG_IGN); #endif /* HAVE_SIGNAL_H */ #endif } /* called after daemonization to retain signal settings */ void restore_signal_state(void) { #ifdef HAVE_SIGNAL_H if (signal_mode == SM_PRE_INIT) { pre_init_signal_catch(); } else if (signal_mode == SM_POST_INIT) { post_init_signal_catch(); } #endif } /* * Print statistics. * * Triggered by SIGUSR2 or F2 on Windows. */ void print_status(const struct context *c, struct status_output *so) { struct gc_arena gc = gc_new(); status_reset(so); status_printf(so, "OpenVPN STATISTICS"); status_printf(so, "Updated,%s", time_string(0, 0, false, &gc)); status_printf(so, "TUN/TAP read bytes," counter_format, c->c2.tun_read_bytes); status_printf(so, "TUN/TAP write bytes," counter_format, c->c2.tun_write_bytes); status_printf(so, "TCP/UDP read bytes," counter_format, c->c2.link_read_bytes); status_printf(so, "TCP/UDP write bytes," counter_format, c->c2.link_write_bytes); status_printf(so, "Auth read bytes," counter_format, c->c2.link_read_bytes_auth); #ifdef USE_COMP if (c->c2.comp_context) { comp_print_stats(c->c2.comp_context, so); } #endif #ifdef PACKET_TRUNCATION_CHECK status_printf(so, "TUN read truncations," counter_format, c->c2.n_trunc_tun_read); status_printf(so, "TUN write truncations," counter_format, c->c2.n_trunc_tun_write); status_printf(so, "Pre-encrypt truncations," counter_format, c->c2.n_trunc_pre_encrypt); status_printf(so, "Post-decrypt truncations," counter_format, c->c2.n_trunc_post_decrypt); #endif #ifdef _WIN32 if (tuntap_defined(c->c1.tuntap)) { status_printf(so, "TAP-WIN32 driver status,\"%s\"", tap_win_getinfo(c->c1.tuntap, &gc)); } #endif status_printf(so, "END"); status_flush(so); gc_free(&gc); } #ifdef ENABLE_OCC /* * Handle the triggering and time-wait of explicit * exit notification. */ static void process_explicit_exit_notification_init(struct context *c) { msg(M_INFO, "SIGTERM received, sending exit notification to peer"); event_timeout_init(&c->c2.explicit_exit_notification_interval, 1, 0); reset_coarse_timers(c); signal_reset(c->sig); halt_non_edge_triggered_signals(); c->c2.explicit_exit_notification_time_wait = now; } void process_explicit_exit_notification_timer_wakeup(struct context *c) { if (event_timeout_trigger(&c->c2.explicit_exit_notification_interval, &c->c2.timeval, ETT_DEFAULT)) { ASSERT(c->c2.explicit_exit_notification_time_wait && c->options.ce.explicit_exit_notification); if (now >= c->c2.explicit_exit_notification_time_wait + c->options.ce.explicit_exit_notification) { event_timeout_clear(&c->c2.explicit_exit_notification_interval); c->sig->signal_received = SIGTERM; c->sig->signal_text = "exit-with-notification"; } else { c->c2.occ_op = OCC_EXIT; } } } #endif /* ifdef ENABLE_OCC */ /* * Process signals */ void remap_signal(struct context *c) { if (c->sig->signal_received == SIGUSR1 && c->options.remap_sigusr1) { c->sig->signal_received = c->options.remap_sigusr1; } } static void process_sigusr2(const struct context *c) { struct status_output *so = status_open(NULL, 0, M_INFO, NULL, 0); print_status(c, so); status_close(so); signal_reset(c->sig); } static bool process_sigterm(struct context *c) { bool ret = true; #ifdef ENABLE_OCC if (c->options.ce.explicit_exit_notification && !c->c2.explicit_exit_notification_time_wait) { process_explicit_exit_notification_init(c); ret = false; } #endif return ret; } /** * If a restart signal is received during exit-notification, reset the * signal and return true. If its a soft restart signal from the event loop * which implies the loop cannot continue, remap to SIGTERM to exit promptly. */ static bool ignore_restart_signals(struct context *c) { bool ret = false; #ifdef ENABLE_OCC if ( (c->sig->signal_received == SIGUSR1 || c->sig->signal_received == SIGHUP) && event_timeout_defined(&c->c2.explicit_exit_notification_interval) ) { if (c->sig->source == SIG_SOURCE_HARD) { msg(M_INFO, "Ignoring %s received during exit notification", signal_name(c->sig->signal_received, true)); signal_reset(c->sig); ret = true; } else { msg(M_INFO, "Converting soft %s received during exit notification to SIGTERM", signal_name(c->sig->signal_received, true)); register_signal(c, SIGTERM, "exit-with-notification"); ret = false; } } #endif return ret; } bool process_signal(struct context *c) { bool ret = true; if (ignore_restart_signals(c)) { ret = false; } else if (c->sig->signal_received == SIGTERM || c->sig->signal_received == SIGINT) { ret = process_sigterm(c); } else if (c->sig->signal_received == SIGUSR2) { process_sigusr2(c); ret = false; } return ret; } void register_signal(struct context *c, int sig, const char *text) { if (c->sig->signal_received != SIGTERM) { c->sig->signal_received = sig; } c->sig->signal_text = text; }