manage.c
6fbf66fa
 /*
  *  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.
  *
564a2109
  *  Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
6fbf66fa
  *
  *  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 (see the file COPYING included with this
  *  distribution); if not, write to the Free Software Foundation, Inc.,
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include "syshead.h"
 
 #ifdef ENABLE_MANAGEMENT
 
 #include "error.h"
 #include "fdmisc.h"
 #include "options.h"
 #include "sig.h"
 #include "event.h"
 #include "otime.h"
 #include "integer.h"
984cf003
 #include "misc.h"
b1f27cb0
 #include "ssl.h"
16322c78
 #include "common.h"
6fbf66fa
 #include "manage.h"
 
 #include "memdbg.h"
 
1bda73a7
 #ifdef ENABLE_PKCS11
 #include "pkcs11.h"
 #endif
 
657ecf14
 #define MANAGEMENT_ECHO_PULL_INFO 0
3c7f2f55
 
 #if MANAGEMENT_ECHO_PULL_INFO
 #define MANAGEMENT_ECHO_FLAGS LOG_PRINT_INTVAL
 #else
 #define MANAGEMENT_ECHO_FLAGS 0
 #endif
 
d19b079b
 /* tag for blank username/password */
 static const char blank_up[] = "[[BLANK]]";
 
6fbf66fa
 struct management *management; /* GLOBAL */
 
 /* static forward declarations */
 static void man_output_standalone (struct management *man, volatile int *signal_received);
4f404ad3
 static void man_reset_client_socket (struct management *man, const bool exiting);
6fbf66fa
 
 static void
 man_help ()
 {
   msg (M_CLIENT, "Management Interface for %s", title_string);
   msg (M_CLIENT, "Commands:");
   msg (M_CLIENT, "auth-retry t           : Auth failure retry mode (none,interact,nointeract).");
34a507c9
   msg (M_CLIENT, "bytecount n            : Show bytes in/out, update every n secs (0=off).");
6fbf66fa
   msg (M_CLIENT, "echo [on|off] [N|all]  : Like log, but only show messages in echo buffer.");
   msg (M_CLIENT, "exit|quit              : Close management session.");
b1f27cb0
   msg (M_CLIENT, "forget-passwords       : Forget passwords entered so far.");
6fbf66fa
   msg (M_CLIENT, "help                   : Print this message.");
   msg (M_CLIENT, "hold [on|off|release]  : Set/show hold flag to on/off state, or"); 
   msg (M_CLIENT, "                         release current hold and start tunnel."); 
   msg (M_CLIENT, "kill cn                : Kill the client instance(s) having common name cn.");
   msg (M_CLIENT, "kill IP:port           : Kill the client instance connecting from IP:port.");
16322c78
   msg (M_CLIENT, "load-stats             : Show global server load stats.");
6fbf66fa
   msg (M_CLIENT, "log [on|off] [N|all]   : Turn on/off realtime log display");
   msg (M_CLIENT, "                         + show last N lines or 'all' for entire history.");
   msg (M_CLIENT, "mute [n]               : Set log mute level to n, or show level if n is absent.");
dd1047f5
   msg (M_CLIENT, "needok type action     : Enter confirmation for NEED-OK request of 'type',");
   msg (M_CLIENT, "                         where action = 'ok' or 'cancel'.");
1bda73a7
   msg (M_CLIENT, "needstr type action    : Enter confirmation for NEED-STR request of 'type',");
   msg (M_CLIENT, "                         where action is reply string.");
6fbf66fa
   msg (M_CLIENT, "net                    : (Windows only) Show network info and routing table.");
   msg (M_CLIENT, "password type p        : Enter password p for a queried OpenVPN password.");
2e913885
   msg (M_CLIENT, "pid                    : Show process ID of the current OpenVPN process.");
1bda73a7
 #ifdef ENABLE_PKCS11
   msg (M_CLIENT, "pkcs11-id-count        : Get number of available PKCS#11 identities.");
   msg (M_CLIENT, "pkcs11-id-get index    : Get PKCS#11 identity at index.");
 #endif
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
   msg (M_CLIENT, "client-auth CID KID    : Authenticate client-id/key-id CID/KID (MULTILINE)");
   msg (M_CLIENT, "client-auth-nt CID KID : Authenticate client-id/key-id CID/KID");
5733ef66
   msg (M_CLIENT, "client-deny CID KID R [CR] : Deny auth client-id/key-id CID/KID with log reason");
   msg (M_CLIENT, "                             text R and optional client reason text CR");
90efcacb
   msg (M_CLIENT, "client-kill CID        : Kill client instance CID");
7e1c085d
   msg (M_CLIENT, "env-filter [level]     : Set env-var filter level");
90efcacb
 #ifdef MANAGEMENT_PF
   msg (M_CLIENT, "client-pf CID          : Define packet filter for client CID (MULTILINE)");
 #endif
 #endif
6fbf66fa
   msg (M_CLIENT, "signal s               : Send signal s to daemon,");
   msg (M_CLIENT, "                         s = SIGHUP|SIGTERM|SIGUSR1|SIGUSR2.");
   msg (M_CLIENT, "state [on|off] [N|all] : Like log, but show state history.");
   msg (M_CLIENT, "status [n]             : Show current daemon status info using format #n.");
   msg (M_CLIENT, "test n                 : Produce n lines of output for testing/debugging.");
   msg (M_CLIENT, "username type u        : Enter username u for a queried OpenVPN username.");
   msg (M_CLIENT, "verb [n]               : Set log verbosity level to n, or show if n is absent.");
   msg (M_CLIENT, "version                : Show current version number.");
3cf6c932
 #if HTTP_PROXY_FALLBACK
   msg (M_CLIENT, "http-proxy-fallback <server> <port> [flags] : Enter dynamic HTTP proxy fallback info.");
   msg (M_CLIENT, "http-proxy-fallback-disable : Disable HTTP proxy fallback.");
 #endif
6fbf66fa
   msg (M_CLIENT, "END");
 }
 
 static const char *
 man_state_name (const int state)
 {
   switch (state)
     {
     case OPENVPN_STATE_INITIAL:
       return "INITIAL";
     case OPENVPN_STATE_CONNECTING:
       return "CONNECTING";
     case OPENVPN_STATE_WAIT:
       return "WAIT";
     case OPENVPN_STATE_AUTH:
       return "AUTH";
     case OPENVPN_STATE_GET_CONFIG:
       return "GET_CONFIG";
     case OPENVPN_STATE_ASSIGN_IP:
       return "ASSIGN_IP";
     case OPENVPN_STATE_ADD_ROUTES:
       return "ADD_ROUTES";
     case OPENVPN_STATE_CONNECTED:
       return "CONNECTED";
     case OPENVPN_STATE_RECONNECTING:
       return "RECONNECTING";
     case OPENVPN_STATE_EXITING:
       return "EXITING";
e482a632
     case OPENVPN_STATE_RESOLVE:
       return "RESOLVE";
     case OPENVPN_STATE_TCP_CONNECT:
       return "TCP_CONNECT";
6fbf66fa
     default:
       return "?";
     }
 }
 
 static void
 man_welcome (struct management *man)
 {
   msg (M_CLIENT, ">INFO:OpenVPN Management Interface Version %d -- type 'help' for more info",
        MANAGEMENT_VERSION);
   if (man->persist.special_state_msg)
     msg (M_CLIENT, "%s", man->persist.special_state_msg);
 }
 
 static inline bool
 man_password_needed (struct management *man)
 {
   return man->settings.up.defined && !man->connection.password_verified;
 }
 
 static void
 man_check_password (struct management *man, const char *line)
 {
   if (man_password_needed (man))
     {
       if (streq (line, man->settings.up.password))
 	{
 	  man->connection.password_verified = true;
 	  msg (M_CLIENT, "SUCCESS: password is correct");
 	  man_welcome (man);
 	}
       else
 	{
 	  man->connection.password_verified = false;
 	  msg (M_CLIENT, "ERROR: bad password");
 	  if (++man->connection.password_tries >= MANAGEMENT_N_PASSWORD_RETRIES)
 	    {
 	      msg (M_WARN, "MAN: client connection rejected after %d failed password attempts",
 		   MANAGEMENT_N_PASSWORD_RETRIES);
 	      man->connection.halt = true;
 	    }
 	}
     }
 }
 
 static void
 man_update_io_state (struct management *man)
 {
   if (socket_defined (man->connection.sd_cli))
     {
90efcacb
       if (buffer_list_defined (man->connection.out))
6fbf66fa
 	{
 	  man->connection.state = MS_CC_WAIT_WRITE;
 	}
       else
 	{
 	  man->connection.state = MS_CC_WAIT_READ;
 	}
     }
 }
 
 static void
3cf6c932
 man_output_list_push_finalize (struct management *man)
6fbf66fa
 {
   if (management_connected (man))
     {
       man_update_io_state (man);
       if (!man->persist.standalone_disabled)
8d33c060
 	{
 	  volatile int signal_received = 0;
 	  man_output_standalone (man, &signal_received);
 	}
6fbf66fa
     }
 }
 
 static void
3cf6c932
 man_output_list_push_str (struct management *man, const char *str)
 {
   if (management_connected (man) && str)
     {
       buffer_list_push (man->connection.out, (const unsigned char *) str);
     }
 }
 
 static void
 man_output_list_push (struct management *man, const char *str)
 {
   man_output_list_push_str (man, str);
   man_output_list_push_finalize (man);
 }
 
 static void
6fbf66fa
 man_prompt (struct management *man)
 {
   if (man_password_needed (man))
     man_output_list_push (man, "ENTER PASSWORD:");
 #if 0 /* should we use prompt? */
   else
74fce85e
     man_output_list_push (man, ">");
6fbf66fa
 #endif
 }
 
 static void
bb564a59
 man_delete_unix_socket (struct management *man)
 {
 #if UNIX_SOCK_SUPPORT
86f5c7c9
   if ((man->settings.flags & (MF_UNIX_SOCK|MF_CONNECT_AS_CLIENT)) == MF_UNIX_SOCK)
bb564a59
     socket_delete_unix (&man->settings.local_unix);
 #endif
 }
 
 static void
6fbf66fa
 man_close_socket (struct management *man, const socket_descriptor_t sd)
 {
 #ifndef WIN32
   /*
    * Windows doesn't need this because the ne32 event is permanently
    * enabled at struct management scope.
    */
   if (man->persist.callback.delete_event)
     (*man->persist.callback.delete_event) (man->persist.callback.arg, sd);
 #endif
   openvpn_close_socket (sd);
 }
 
 static void
 virtual_output_callback_func (void *arg, const unsigned int flags, const char *str)
 {
3cf6c932
   struct management *man = (struct management *) arg;
6fbf66fa
   static int recursive_level = 0; /* GLOBAL */
3cf6c932
   bool did_push = false;
6fbf66fa
 
   if (!recursive_level) /* don't allow recursion */
     {
       struct gc_arena gc = gc_new ();
       struct log_entry e;
       const char *out = NULL;
 
       ++recursive_level;
 
       CLEAR (e);
       update_time ();
       e.timestamp = now;
       e.u.msg_flags = flags;
       e.string = str;
 
       if (flags & M_FATAL)
 	man->persist.standalone_disabled = false;
 
       if (flags != M_CLIENT)
 	log_history_add (man->persist.log, &e);
 
       if (!man_password_needed (man))
 	{
 	  if (flags == M_CLIENT)
 	    out = log_entry_print (&e, LOG_PRINT_CRLF, &gc);
 	  else if (man->connection.log_realtime)
 	    out = log_entry_print (&e, LOG_PRINT_INT_DATE
 				   |   LOG_PRINT_MSG_FLAGS
 				   |   LOG_PRINT_LOG_PREFIX
 				   |   LOG_PRINT_CRLF, &gc);
 	  if (out)
3cf6c932
 	    {
 	      man_output_list_push_str (man, out);
 	      did_push = true;
 	    }
6fbf66fa
 	  if (flags & M_FATAL)
 	    {
 	      out = log_entry_print (&e, LOG_FATAL_NOTIFY|LOG_PRINT_CRLF, &gc);
 	      if (out)
 		{
3cf6c932
 		  man_output_list_push_str (man, out);
 		  did_push = true;
4f404ad3
 		  man_reset_client_socket (man, true);
6fbf66fa
 		}
 	    }
 	}
 
       --recursive_level;
       gc_free (&gc);
     }
3cf6c932
 
   if (did_push)
     man_output_list_push_finalize (man);
6fbf66fa
 }
 
2c21891e
 /*
  * Given a signal, return the signal with possible remapping applied,
  * or -1 if the signal should be ignored.
  */
 static int
 man_mod_signal (const struct management *man, const int signum)
 {
   const unsigned int flags = man->settings.mansig;
   int s = signum;
   if (s == SIGUSR1)
     {
       if (flags & MANSIG_MAP_USR1_TO_HUP)
 	s = SIGHUP;
       if (flags & MANSIG_MAP_USR1_TO_TERM)
 	s = SIGTERM;
     }
   if (flags & MANSIG_IGNORE_USR1_HUP)
     {
       if (s == SIGHUP || s == SIGUSR1)
 	s = -1;
     }
   return s;
 }
 
6fbf66fa
 static void
 man_signal (struct management *man, const char *name)
 {
   const int sig = parse_signal (name);
   if (sig >= 0)
     {
2c21891e
       const int sig_mod = man_mod_signal (man, sig);
       if (sig_mod >= 0)
 	{
 	  throw_signal (sig_mod);
 	  msg (M_CLIENT, "SUCCESS: signal %s thrown", signal_name (sig_mod, true));
 	}
       else
 	{
 	  if (man->persist.special_state_msg)
 	    msg (M_CLIENT, "%s", man->persist.special_state_msg);
 	  else
 	    msg (M_CLIENT, "ERROR: signal '%s' is currently ignored", name);
 	}
6fbf66fa
     }
   else
     {
       msg (M_CLIENT, "ERROR: signal '%s' is not a known signal type", name);
     }
 }
 
 static void
 man_status (struct management *man, const int version, struct status_output *so)
 {
   if (man->persist.callback.status)
     {
       (*man->persist.callback.status) (man->persist.callback.arg, version, so);
     }
   else
     {
       msg (M_CLIENT, "ERROR: The 'status' command is not supported by the current daemon mode");
     }
 }
 
 static void
34a507c9
 man_bytecount (struct management *man, const int update_seconds)
 {
5f435d64
   if (update_seconds >= 0)
     man->connection.bytecount_update_seconds = update_seconds;
   else
     man->connection.bytecount_update_seconds = 0;
   msg (M_CLIENT, "SUCCESS: bytecount interval changed");
34a507c9
 }
 
 void
5f435d64
 man_bytecount_output_client (struct management *man)
34a507c9
 {
   char in[32];
   char out[32];
   /* do in a roundabout way to work around possible mingw or mingw-glibc bug */
   openvpn_snprintf (in, sizeof (in), counter_format, man->persist.bytes_in);
   openvpn_snprintf (out, sizeof (out), counter_format, man->persist.bytes_out);
   msg (M_CLIENT, ">BYTECOUNT:%s,%s", in, out);
   man->connection.bytecount_last_update = now;
 }
 
5f435d64
 #ifdef MANAGEMENT_DEF_AUTH
 
 void
 man_bytecount_output_server (struct management *man,
 			     const counter_type *bytes_in_total,
 			     const counter_type *bytes_out_total,
 			     struct man_def_auth_context *mdac)
 {
   char in[32];
   char out[32];
   /* do in a roundabout way to work around possible mingw or mingw-glibc bug */
   openvpn_snprintf (in, sizeof (in), counter_format, *bytes_in_total);
   openvpn_snprintf (out, sizeof (out), counter_format, *bytes_out_total);
   msg (M_CLIENT, ">BYTECOUNT_CLI:%lu,%s,%s", mdac->cid, in, out);
   mdac->bytecount_last_update = now;
 }
 
 #endif
 
34a507c9
 static void
6fbf66fa
 man_kill (struct management *man, const char *victim)
 {
   struct gc_arena gc = gc_new ();
 
   if (man->persist.callback.kill_by_cn && man->persist.callback.kill_by_addr)
     {
       struct buffer buf;
       char p1[128];
       char p2[128];
       int n_killed;
 
       buf_set_read (&buf, (uint8_t*) victim, strlen (victim) + 1);
       buf_parse (&buf, ':', p1, sizeof (p1));
       buf_parse (&buf, ':', p2, sizeof (p2));
 
       if (strlen (p1) && strlen (p2))
 	{
 	  /* IP:port specified */
 	  bool status;
 	  const in_addr_t addr = getaddr (GETADDR_HOST_ORDER|GETADDR_MSG_VIRT_OUT, p1, 0, &status, NULL);
 	  if (status)
 	    {
 	      const int port = atoi (p2);
 	      if (port > 0 && port < 65536)
 		{
 		  n_killed = (*man->persist.callback.kill_by_addr) (man->persist.callback.arg, addr, port);
 		  if (n_killed > 0)
 		    {
 		      msg (M_CLIENT, "SUCCESS: %d client(s) at address %s:%d killed",
 			   n_killed,
 			   print_in_addr_t (addr, 0, &gc),
 			   port);
 		    }
 		  else
 		    {
 		      msg (M_CLIENT, "ERROR: client at address %s:%d not found",
 			   print_in_addr_t (addr, 0, &gc),
 			   port);
 		    }
 		}
 	      else
 		{
 		  msg (M_CLIENT, "ERROR: port number is out of range: %s", p2);
 		}
 	    }
 	  else
 	    {
 	      msg (M_CLIENT, "ERROR: error parsing IP address: %s", p1);
 	    }
 	}
       else if (strlen (p1))
 	{
 	  /* common name specified */
 	  n_killed = (*man->persist.callback.kill_by_cn) (man->persist.callback.arg, p1);
 	  if (n_killed > 0)
 	    {
 	      msg (M_CLIENT, "SUCCESS: common name '%s' found, %d client(s) killed", p1, n_killed);
 	    }
 	  else
 	    {
 	      msg (M_CLIENT, "ERROR: common name '%s' not found", p1);
 	    }
 	}
       else
 	{
 	  msg (M_CLIENT, "ERROR: kill parse");
 	}
     }
   else
     {
       msg (M_CLIENT, "ERROR: The 'kill' command is not supported by the current daemon mode");
     }
 
   gc_free (&gc);
 }
 
 /*
  * General-purpose history command handler
  * for the log and echo commands.
  */
 static void
 man_history (struct management *man,
 	     const char *parm,
 	     const char *type,
 	     struct log_history *log,
 	     bool *realtime,
 	     const unsigned int lep_flags)
 {
   struct gc_arena gc = gc_new ();
   int n = 0;
 
   if (streq (parm, "on"))
     {
       *realtime = true;
       msg (M_CLIENT, "SUCCESS: real-time %s notification set to ON", type);
     }
   else if (streq (parm, "off"))
     {
       *realtime = false;
       msg (M_CLIENT, "SUCCESS: real-time %s notification set to OFF", type);
     }
   else if (streq (parm, "all") || (n = atoi (parm)) > 0)
     {
       const int size = log_history_size (log);
       const int start = (n ? n : size) - 1;
       int i;
 
       for (i = start; i >= 0; --i)
 	{
 	  const struct log_entry *e = log_history_ref (log, i);
 	  if (e)
 	    {
 	      const char *out = log_entry_print (e, lep_flags, &gc);
 	      virtual_output_callback_func (man, M_CLIENT, out);
 	    }
 	}
       msg (M_CLIENT, "END");
     }
   else
     {
       msg (M_CLIENT, "ERROR: %s parameter must be 'on' or 'off' or some number n or 'all'", type);
     }
 
   gc_free (&gc);
 }
 
 static void
 man_log (struct management *man, const char *parm)
 {
   man_history (man,
 	       parm,
 	       "log",
 	       man->persist.log,
 	       &man->connection.log_realtime,
 	       LOG_PRINT_INT_DATE|LOG_PRINT_MSG_FLAGS);
 }
 
 static void
 man_echo (struct management *man, const char *parm)
 {
   man_history (man,
 	       parm,
 	       "echo",
 	       man->persist.echo,
 	       &man->connection.echo_realtime,
3c7f2f55
 	       LOG_PRINT_INT_DATE|MANAGEMENT_ECHO_FLAGS);
6fbf66fa
 }
 
 static void
 man_state (struct management *man, const char *parm)
 {
   man_history (man,
 	       parm,
 	       "state",
 	       man->persist.state,
 	       &man->connection.state_realtime,
5ad84585
 	       LOG_PRINT_INT_DATE|LOG_PRINT_STATE|
 	       LOG_PRINT_LOCAL_IP|LOG_PRINT_REMOTE_IP);
6fbf66fa
 }
 
 static void
 man_up_finalize (struct management *man)
 {
   switch (man->connection.up_query_mode)
     {
     case UP_QUERY_DISABLED:
       man->connection.up_query.defined = false;
       break;
     case UP_QUERY_USER_PASS:
       if (strlen (man->connection.up_query.username) && strlen (man->connection.up_query.password))
 	man->connection.up_query.defined = true;
       break;
     case UP_QUERY_PASS:
       if (strlen (man->connection.up_query.password))
 	man->connection.up_query.defined = true;
       break;
984cf003
     case UP_QUERY_NEED_OK:
       if (strlen (man->connection.up_query.password))
 	man->connection.up_query.defined = true;
       break;
1bda73a7
     case UP_QUERY_NEED_STR:
       if (strlen (man->connection.up_query.password))
 	man->connection.up_query.defined = true;
       break;
6fbf66fa
     default:
       ASSERT (0);
     }
 }
 
 static void
 man_query_user_pass (struct management *man,
 		     const char *type,
 		     const char *string,
 		     const bool needed,
 		     const char *prompt,
 		     char *dest,
 		     int len)
 {
   if (needed)
     {
       ASSERT (man->connection.up_query_type);
       if (streq (man->connection.up_query_type, type))
 	{
 	  strncpynt (dest, string, len);
 	  man_up_finalize (man);
 	  msg (M_CLIENT, "SUCCESS: '%s' %s entered, but not yet verified",
 	       type,
 	       prompt);
 	}
       else
 	msg (M_CLIENT, "ERROR: %s of type '%s' entered, but we need one of type '%s'",
 	     prompt,
 	     type,
 	     man->connection.up_query_type);
     }
   else
     {
       msg (M_CLIENT, "ERROR: no %s is currently needed at this time", prompt);
     }
 }
 
 static void
 man_query_username (struct management *man, const char *type, const char *string)
 {
   const bool needed = (man->connection.up_query_mode == UP_QUERY_USER_PASS && man->connection.up_query_type);
   man_query_user_pass (man, type, string, needed, "username", man->connection.up_query.username, USER_PASS_LEN);
 }
 
 static void
 man_query_password (struct management *man, const char *type, const char *string)
 {
   const bool needed = ((man->connection.up_query_mode == UP_QUERY_USER_PASS
 			|| man->connection.up_query_mode == UP_QUERY_PASS)
 		       && man->connection.up_query_type);
d19b079b
   if (!string[0]) /* allow blank passwords to be passed through using the blank_up tag */
     string = blank_up;
6fbf66fa
   man_query_user_pass (man, type, string, needed, "password", man->connection.up_query.password, USER_PASS_LEN);
 }
 
 static void
dd1047f5
 man_query_need_ok (struct management *man, const char *type, const char *action)
984cf003
 {
   const bool needed = ((man->connection.up_query_mode == UP_QUERY_NEED_OK) && man->connection.up_query_type);
dd1047f5
   man_query_user_pass (man, type, action, needed, "needok-confirmation", man->connection.up_query.password, USER_PASS_LEN);
984cf003
 }
 
 static void
1bda73a7
 man_query_need_str (struct management *man, const char *type, const char *action)
 {
   const bool needed = ((man->connection.up_query_mode == UP_QUERY_NEED_STR) && man->connection.up_query_type);
   man_query_user_pass (man, type, action, needed, "needstr-string", man->connection.up_query.password, USER_PASS_LEN);
 }
 
 static void
b1f27cb0
 man_forget_passwords (struct management *man)
 {
5acd2baf
 #if defined(USE_CRYPTO) && defined(USE_SSL)
b1f27cb0
   ssl_purge_auth ();
   msg (M_CLIENT, "SUCCESS: Passwords were forgotten");
5acd2baf
 #endif
b1f27cb0
 }
 
 static void
6fbf66fa
 man_net (struct management *man)
 {
   if (man->persist.callback.show_net)
     {
       (*man->persist.callback.show_net) (man->persist.callback.arg, M_CLIENT);
     }
   else
     {
       msg (M_CLIENT, "ERROR: The 'net' command is not supported by the current daemon mode");
     }
 }
 
1bda73a7
 #ifdef ENABLE_PKCS11
 
 static void
 man_pkcs11_id_count (struct management *man)
 {
   msg (M_CLIENT, ">PKCS11ID-COUNT:%d", pkcs11_management_id_count ());
 }
 
 static void
 man_pkcs11_id_get (struct management *man, const int index)
 {
   char *id = NULL;
   char *base64 = NULL;
 
   if (pkcs11_management_id_get (index, &id, &base64))
     msg (M_CLIENT, ">PKCS11ID-ENTRY:'%d', ID:'%s', BLOB:'%s'", index, id, base64);
   else
     msg (M_CLIENT, ">PKCS11ID-ENTRY:'%d'", index);
 
   if (id != NULL)
     free (id);
   if (base64 != NULL)
     free (base64);
 }
 
 #endif
 
6fbf66fa
 static void
 man_hold (struct management *man, const char *cmd)
 {
   if (cmd)
     {
       if (streq (cmd, "on"))
 	{
90efcacb
 	  man->settings.flags |= MF_HOLD;
6fbf66fa
 	  msg (M_CLIENT, "SUCCESS: hold flag set to ON");
 	}
       else if (streq (cmd, "off"))
 	{
90efcacb
 	  man->settings.flags &= ~MF_HOLD;
6fbf66fa
 	  msg (M_CLIENT, "SUCCESS: hold flag set to OFF");
 	}
       else if (streq (cmd, "release"))
 	{
 	  man->persist.hold_release = true;
 	  msg (M_CLIENT, "SUCCESS: hold release succeeded");
 	}
       else
 	{
 	  msg (M_CLIENT, "ERROR: bad hold command parameter");
 	}
     }
   else
90efcacb
     msg (M_CLIENT, "SUCCESS: hold=%d", BOOL_CAST(man->settings.flags & MF_HOLD));
 }
 
 #ifdef MANAGEMENT_DEF_AUTH
 
 static bool
 parse_cid (const char *str, unsigned long *cid)
 {
   if (sscanf (str, "%lu", cid) == 1)
     return true;
   else
     {
       msg (M_CLIENT, "ERROR: cannot parse CID");
       return false;
     }
 }
 
 static bool
 parse_kid (const char *str, unsigned int *kid)
 {
   if (sscanf (str, "%u", kid) == 1)
     return true;
   else
     {
       msg (M_CLIENT, "ERROR: cannot parse KID");
       return false;
     }
 }
 
 static void
 in_extra_reset (struct man_connection *mc, const bool new)
 {
   if (mc)
     {
       if (!new)
 	{
 	  mc->in_extra_cmd = IEC_UNDEF;
 	  mc->in_extra_cid = 0;
 	  mc->in_extra_kid = 0;
 	}
       if (mc->in_extra)
 	{
 	  buffer_list_free (mc->in_extra);
 	  mc->in_extra = NULL;
 	}
       if (new)
 	mc->in_extra = buffer_list_new (0);
     }
 }
 
 static void
 in_extra_dispatch (struct management *man)
 {
    switch (man->connection.in_extra_cmd)
     {
     case IEC_CLIENT_AUTH:
        if (man->persist.callback.client_auth)
 	{
 	  const bool status = (*man->persist.callback.client_auth)
 	    (man->persist.callback.arg,
 	     man->connection.in_extra_cid,
 	     man->connection.in_extra_kid,
 	     true,
 	     NULL,
5733ef66
 	     NULL,
90efcacb
 	     man->connection.in_extra);
 	  man->connection.in_extra = NULL;
 	  if (status)
 	    {
 	      msg (M_CLIENT, "SUCCESS: client-auth command succeeded");
 	    }
 	  else
 	    {
 	      msg (M_CLIENT, "ERROR: client-auth command failed");
 	    }
 	}
       else
 	{
 	  msg (M_CLIENT, "ERROR: The client-auth command is not supported by the current daemon mode");
 	}
       break;
 #ifdef MANAGEMENT_PF
     case IEC_CLIENT_PF:
       if (man->persist.callback.client_pf)
 	{
 	  const bool status = (*man->persist.callback.client_pf)
 	    (man->persist.callback.arg,
 	     man->connection.in_extra_cid,
 	     man->connection.in_extra);
 	  man->connection.in_extra = NULL;
 	  if (status)
 	    {
 	      msg (M_CLIENT, "SUCCESS: client-pf command succeeded");
 	    }
 	  else
 	    {
 	      msg (M_CLIENT, "ERROR: client-pf command failed");
 	    }
 	}
       else
 	{
 	  msg (M_CLIENT, "ERROR: The client-pf command is not supported by the current daemon mode");
 	}
       break;
 #endif
     }
    in_extra_reset (&man->connection, false);
6fbf66fa
 }
 
90efcacb
 static void
 man_client_auth (struct management *man, const char *cid_str, const char *kid_str, const bool extra)
 {
   struct man_connection *mc = &man->connection;
   mc->in_extra_cid = 0;
   mc->in_extra_kid = 0;
   if (parse_cid (cid_str, &mc->in_extra_cid)
       && parse_kid (kid_str, &mc->in_extra_kid))
     {
       mc->in_extra_cmd = IEC_CLIENT_AUTH;
       in_extra_reset (mc, true);
       if (!extra)
 	in_extra_dispatch (man);
     }
 }
 
 static void
5733ef66
 man_client_deny (struct management *man, const char *cid_str, const char *kid_str, const char *reason, const char *client_reason)
90efcacb
 {
   unsigned long cid = 0;
   unsigned int kid = 0;
   if (parse_cid (cid_str, &cid) && parse_kid (kid_str, &kid))
     {
       if (man->persist.callback.client_auth)
 	{
 	  const bool status = (*man->persist.callback.client_auth)
 	    (man->persist.callback.arg,
 	     cid,
 	     kid,
 	     false,
 	     reason,
5733ef66
 	     client_reason,
90efcacb
 	     NULL);
 	  if (status)
 	    {
 	      msg (M_CLIENT, "SUCCESS: client-deny command succeeded");
 	    }
 	  else
 	    {
 	      msg (M_CLIENT, "ERROR: client-deny command failed");
 	    }
 	}
       else
 	{
 	  msg (M_CLIENT, "ERROR: The client-deny command is not supported by the current daemon mode");
 	}
     }
 }
 
 static void
 man_client_kill (struct management *man, const char *cid_str)
 {
   unsigned long cid = 0;
   if (parse_cid (cid_str, &cid))
     {
       if (man->persist.callback.kill_by_cid)
 	{
 	  const bool status = (*man->persist.callback.kill_by_cid) (man->persist.callback.arg, cid);
 	  if (status)
 	    {
 	      msg (M_CLIENT, "SUCCESS: client-kill command succeeded");
 	    }
 	  else
 	    {
 	      msg (M_CLIENT, "ERROR: client-kill command failed");
 	    }
 	}
       else
 	{
 	  msg (M_CLIENT, "ERROR: The client-kill command is not supported by the current daemon mode");
 	}
     }
 }
 
4fb4ce12
 static void
 man_client_n_clients (struct management *man)
 {
   if (man->persist.callback.n_clients)
     {
       const int nclients = (*man->persist.callback.n_clients) (man->persist.callback.arg);
       msg (M_CLIENT, "SUCCESS: nclients=%d", nclients);
     }
   else
     {
       msg (M_CLIENT, "ERROR: The nclients command is not supported by the current daemon mode");
     }
 }
 
7e1c085d
 static void
 man_env_filter (struct management *man, const int level)
 {
   man->connection.env_filter_level = level;
   msg (M_CLIENT, "SUCCESS: env_filter_level=%d", level);
 }
 
90efcacb
 #ifdef MANAGEMENT_PF
 
 static void
 man_client_pf (struct management *man, const char *cid_str)
 {
   struct man_connection *mc = &man->connection;
   mc->in_extra_cid = 0;
   mc->in_extra_kid = 0;
   if (parse_cid (cid_str, &mc->in_extra_cid))
     {
       mc->in_extra_cmd = IEC_CLIENT_PF;
       in_extra_reset (mc, true);
     }
 }
 
 #endif
 #endif
 
16322c78
 static void
 man_load_stats (struct management *man)
 {
   extern counter_type link_read_bytes_global;
   extern counter_type link_write_bytes_global;
   int nclients = 0;
 
   if (man->persist.callback.n_clients)
     nclients = (*man->persist.callback.n_clients) (man->persist.callback.arg);
   msg (M_CLIENT, "SUCCESS: nclients=%d,bytesin=" counter_format ",bytesout=" counter_format,
        nclients,
        link_read_bytes_global,
        link_write_bytes_global);
 }
 
6fbf66fa
 #define MN_AT_LEAST (1<<0)
 
 static bool
 man_need (struct management *man, const char **p, const int n, unsigned int flags)
 {
   int i;
   ASSERT (p[0]);
   for (i = 1; i <= n; ++i)
     {
       if (!p[i])
 	{
 	  msg (M_CLIENT, "ERROR: the '%s' command requires %s%d parameter%s",
 	       p[0],
 	       (flags & MN_AT_LEAST) ? "at least " : "",
 	       n,
 	       n > 1 ? "s" : "");
 	  return false;
 	}
     }
   return true;
 }
 
3cf6c932
 #if HTTP_PROXY_FALLBACK
 
 static void
 man_http_proxy_fallback (struct management *man, const char *server, const char *port, const char *flags)
 {
   if (man->persist.callback.http_proxy_fallback_cmd)
     {
       const bool status = (*man->persist.callback.http_proxy_fallback_cmd)(man->persist.callback.arg, server, port, flags);
       if (status)
 	{
 	  msg (M_CLIENT, "SUCCESS: proxy-fallback command succeeded");
 	}
       else
 	{
 	  msg (M_CLIENT, "ERROR: proxy-fallback command failed");
 	}
     }
   else
     {
       msg (M_CLIENT, "ERROR: The proxy-fallback command is not supported by the current daemon mode");
     }
 }
 
 #endif
 
6fbf66fa
 static void
 man_dispatch_command (struct management *man, struct status_output *so, const char **p, const int nparms)
 {
   struct gc_arena gc = gc_new ();
 
   ASSERT (p[0]);
   if (streq (p[0], "exit") || streq (p[0], "quit"))
     {
       man->connection.halt = true;
       goto done;
     }
   else if (streq (p[0], "help"))
     {
       man_help ();
     }
   else if (streq (p[0], "version"))
     {
       msg (M_CLIENT, "OpenVPN Version: %s", title_string);
       msg (M_CLIENT, "Management Version: %d", MANAGEMENT_VERSION);
       msg (M_CLIENT, "END");
     }
2e913885
   else if (streq (p[0], "pid"))
     {
       msg (M_CLIENT, "SUCCESS: pid=%d", openvpn_getpid ());
     }
7d0f3877
 #ifdef MANAGEMENT_DEF_AUTH
4fb4ce12
   else if (streq (p[0], "nclients"))
     {
       man_client_n_clients (man);
     }
7e1c085d
   else if (streq (p[0], "env-filter"))
     {
       int level = 0;
       if (p[1])
 	level = atoi (p[1]);
       man_env_filter (man, level);
     }
7d0f3877
 #endif
6fbf66fa
   else if (streq (p[0], "signal"))
     {
       if (man_need (man, p, 1, 0))
 	man_signal (man, p[1]);
     }
16322c78
   else if (streq (p[0], "load-stats"))
     {
       man_load_stats (man);
     }
6fbf66fa
   else if (streq (p[0], "status"))
     {
       int version = 0;
       if (p[1])
 	version = atoi (p[1]);
       man_status (man, version, so);
     }
   else if (streq (p[0], "kill"))
     {
       if (man_need (man, p, 1, 0))
 	man_kill (man, p[1]);
     }
   else if (streq (p[0], "verb"))
     {
       if (p[1])
 	{
 	  const int level = atoi(p[1]);
 	  if (set_debug_level (level, 0))
 	    msg (M_CLIENT, "SUCCESS: verb level changed");
 	  else
 	    msg (M_CLIENT, "ERROR: verb level is out of range");
 	}
       else
 	msg (M_CLIENT, "SUCCESS: verb=%d", get_debug_level ());
     }
   else if (streq (p[0], "mute"))
     {
       if (p[1])
 	{
 	  const int level = atoi(p[1]);
 	  if (set_mute_cutoff (level))
 	    msg (M_CLIENT, "SUCCESS: mute level changed");
 	  else
 	    msg (M_CLIENT, "ERROR: mute level is out of range");
 	}
       else
 	msg (M_CLIENT, "SUCCESS: mute=%d", get_mute_cutoff ());
     }
   else if (streq (p[0], "auth-retry"))
     {
 #if P2MP
       if (p[1])
 	{
 	  if (auth_retry_set (M_CLIENT, p[1]))
 	    msg (M_CLIENT, "SUCCESS: auth-retry parameter changed");
 	  else
 	    msg (M_CLIENT, "ERROR: bad auth-retry parameter");
 	}
       else
 	msg (M_CLIENT, "SUCCESS: auth-retry=%s", auth_retry_print ());	
 #else
       msg (M_CLIENT, "ERROR: auth-retry feature is unavailable");
 #endif
     }
   else if (streq (p[0], "state"))
     {
       if (!p[1])
 	{
 	  man_state (man, "1");
 	}
       else
 	{
 	  if (p[1])
 	    man_state (man, p[1]);
 	  if (p[2])
 	    man_state (man, p[2]);
 	}
     }
   else if (streq (p[0], "log"))
     {
       if (man_need (man, p, 1, MN_AT_LEAST))
 	{
 	  if (p[1])
 	    man_log (man, p[1]);
 	  if (p[2])
 	    man_log (man, p[2]);
 	}
     }
   else if (streq (p[0], "echo"))
     {
       if (man_need (man, p, 1, MN_AT_LEAST))
 	{
 	  if (p[1])
 	    man_echo (man, p[1]);
 	  if (p[2])
 	    man_echo (man, p[2]);
 	}
     }
   else if (streq (p[0], "username"))
     {
       if (man_need (man, p, 2, 0))
 	man_query_username (man, p[1], p[2]);
     }
   else if (streq (p[0], "password"))
     {
       if (man_need (man, p, 2, 0))
 	man_query_password (man, p[1], p[2]);
     }
b1f27cb0
   else if (streq (p[0], "forget-passwords"))
     {
       man_forget_passwords (man);
     }
dd1047f5
   else if (streq (p[0], "needok"))
984cf003
     {
dd1047f5
       if (man_need (man, p, 2, 0))
 	man_query_need_ok (man, p[1], p[2]);
984cf003
     }
1bda73a7
   else if (streq (p[0], "needstr"))
     {
       if (man_need (man, p, 2, 0))
 	man_query_need_str (man, p[1], p[2]);
     }
6fbf66fa
   else if (streq (p[0], "net"))
     {
       man_net (man);
     }
   else if (streq (p[0], "hold"))
     {
       man_hold (man, p[1]);
     }
34a507c9
   else if (streq (p[0], "bytecount"))
     {
       if (man_need (man, p, 1, 0))
 	man_bytecount (man, atoi(p[1]));
     }
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
   else if (streq (p[0], "client-kill"))
     {
       if (man_need (man, p, 1, 0))
 	man_client_kill (man, p[1]);
     }
   else if (streq (p[0], "client-deny"))
     {
5733ef66
       if (man_need (man, p, 3, MN_AT_LEAST))
 	man_client_deny (man, p[1], p[2], p[3], p[4]);
90efcacb
     }
   else if (streq (p[0], "client-auth-nt"))
     {
       if (man_need (man, p, 2, 0))
 	man_client_auth (man, p[1], p[2], false);
     }
   else if (streq (p[0], "client-auth"))
     {
       if (man_need (man, p, 2, 0))
 	man_client_auth (man, p[1], p[2], true);
     }
 #ifdef MANAGEMENT_PF
   else if (streq (p[0], "client-pf"))
     {
       if (man_need (man, p, 1, 0))
 	man_client_pf (man, p[1]);
     }
 #endif
 #endif
1bda73a7
 #ifdef ENABLE_PKCS11
   else if (streq (p[0], "pkcs11-id-count"))
     {
       man_pkcs11_id_count (man);
     }
   else if (streq (p[0], "pkcs11-id-get"))
     {
       if (man_need (man, p, 1, 0))
 	man_pkcs11_id_get (man, atoi(p[1]));
     }
 #endif
3cf6c932
 #if HTTP_PROXY_FALLBACK
   else if (streq (p[0], "http-proxy-fallback"))
     {
       if (man_need (man, p, 2, MN_AT_LEAST))
 	man_http_proxy_fallback (man, p[1], p[2], p[3]);
     }
   else if (streq (p[0], "http-proxy-fallback-disable"))
     {
       man_http_proxy_fallback (man, NULL, NULL, NULL);
     }
 #endif
6fbf66fa
 #if 1
   else if (streq (p[0], "test"))
     {
       if (man_need (man, p, 1, 0))
 	{
 	  int i;
 	  const int n = atoi (p[1]);
 	  for (i = 0; i < n; ++i)
 	    {
 	      msg (M_CLIENT, "[%d] The purpose of this command is to generate large amounts of output.", i);
 	    }
 	}
     }
 #endif
   else
     {
       msg (M_CLIENT, "ERROR: unknown command, enter 'help' for more options");
     }
 
  done:
   gc_free (&gc);
 }
 
 #ifdef WIN32
 
 static void
 man_start_ne32 (struct management *man)
 {
   switch (man->connection.state)
     {
     case MS_LISTEN:
       net_event_win32_start (&man->connection.ne32, FD_ACCEPT, man->connection.sd_top);
       break;
     case MS_CC_WAIT_READ:
     case MS_CC_WAIT_WRITE:
       net_event_win32_start (&man->connection.ne32, FD_READ|FD_WRITE|FD_CLOSE, man->connection.sd_cli);
       break;
     default:
       ASSERT (0);
     }  
 }
 
 static void
 man_stop_ne32 (struct management *man)
 {
   net_event_win32_stop (&man->connection.ne32);
 }
 
 #endif
 
 static void
8d33c060
 man_record_peer_info (struct management *man)
 {
   struct gc_arena gc = gc_new ();
   if (man->settings.write_peer_info_file)
     {
       bool success = false;
 #ifdef HAVE_GETSOCKNAME
       if (socket_defined (man->connection.sd_cli))
 	{
 	  struct sockaddr_in addr;
 	  socklen_t addrlen = sizeof (addr);
 	  int status;
 
 	  CLEAR (addr);
 	  status = getsockname (man->connection.sd_cli, (struct sockaddr *)&addr, &addrlen);
 	  if (!status && addrlen == sizeof (addr))
 	    {
 	      const in_addr_t a = ntohl (addr.sin_addr.s_addr);
 	      const int p = ntohs (addr.sin_port);
 	      FILE *fp = fopen (man->settings.write_peer_info_file, "w");
 	      if (fp)
 		{
 		  fprintf (fp, "%s\n%d\n", print_in_addr_t (a, 0, &gc), p);
 		  if (!fclose (fp))
 		    success = true;
 		}
 	    }
 	}
 #endif
       if (!success)
 	{
 	  msg (D_MANAGEMENT, "MANAGEMENT: failed to write peer info to file %s",
 	       man->settings.write_peer_info_file);
 	  throw_signal_soft (SIGTERM, "management-connect-failed");
 	}
     }
   gc_free (&gc);
 }
 
 static void
4f404ad3
 man_connection_settings_reset (struct management *man)
 {
   man->connection.state_realtime = false;
   man->connection.log_realtime = false;
   man->connection.echo_realtime = false;
34a507c9
   man->connection.bytecount_update_seconds = 0;
4f404ad3
   man->connection.password_verified = false;
   man->connection.password_tries = 0;
   man->connection.halt = false;
   man->connection.state = MS_CC_WAIT_WRITE;
 }
 
 static void
 man_new_connection_post (struct management *man, const char *description)
6fbf66fa
 {
   struct gc_arena gc = gc_new ();
4f404ad3
 
   set_nonblock (man->connection.sd_cli);
   set_cloexec (man->connection.sd_cli);
 
   man_connection_settings_reset (man);
 
 #ifdef WIN32
   man_start_ne32 (man);
 #endif
 
bb564a59
 #if UNIX_SOCK_SUPPORT
86f5c7c9
   if (man->settings.flags & MF_UNIX_SOCK)
bb564a59
     {
       msg (D_MANAGEMENT, "MANAGEMENT: %s %s",
 	   description,
 	   sockaddr_unix_name (&man->settings.local_unix, "NULL"));
     }
   else
 #endif
     msg (D_MANAGEMENT, "MANAGEMENT: %s %s",
 	 description,
 	 print_sockaddr (&man->settings.local, &gc));
4f404ad3
 
90efcacb
   buffer_list_reset (man->connection.out);
4f404ad3
 
   if (!man_password_needed (man))
     man_welcome (man);
   man_prompt (man);
   man_update_io_state (man);
 
   gc_free (&gc);
 }
 
86f5c7c9
 #if UNIX_SOCK_SUPPORT
 static bool
 man_verify_unix_peer_uid_gid (struct management *man, const socket_descriptor_t sd)
 {
   if (socket_defined (sd) && (man->settings.client_uid != -1 || man->settings.client_gid != -1))
     {
       static const char err_prefix[] = "MANAGEMENT: unix domain socket client connection rejected --";
       int uid, gid;
       if (unix_socket_get_peer_uid_gid (man->connection.sd_cli, &uid, &gid))
 	{
 	  if (man->settings.client_uid != -1 && man->settings.client_uid != uid)
 	    {
 	      msg (D_MANAGEMENT, "%s UID of socket peer (%d) doesn't match required value (%d) as given by --management-client-user",
 		   err_prefix, uid, man->settings.client_uid);
 	      return false;
 	    }
 	  if (man->settings.client_gid != -1 && man->settings.client_gid != gid)
 	    {
 	      msg (D_MANAGEMENT, "%s GID of socket peer (%d) doesn't match required value (%d) as given by --management-client-group",
 		   err_prefix, gid, man->settings.client_gid);
 	      return false;
 	    }
 	}
       else
 	{
 	  msg (D_MANAGEMENT, "%s cannot get UID/GID of socket peer", err_prefix);
 	  return false;
 	}
     }
   return true;
 }
 #endif
 
4f404ad3
 static void
 man_accept (struct management *man)
 {
8bc93d7f
   struct link_socket_actual act;
bb564a59
   CLEAR (act);
6fbf66fa
 
   /*
bb564a59
    * Accept the TCP or Unix domain socket client.
6fbf66fa
    */
bb564a59
 #if UNIX_SOCK_SUPPORT
86f5c7c9
   if (man->settings.flags & MF_UNIX_SOCK)
bb564a59
     {
       struct sockaddr_un remote;
       man->connection.sd_cli = socket_accept_unix (man->connection.sd_top, &remote);
86f5c7c9
       if (!man_verify_unix_peer_uid_gid (man, man->connection.sd_cli))
 	sd_close (&man->connection.sd_cli);
bb564a59
     }
   else
 #endif
     man->connection.sd_cli = socket_do_accept (man->connection.sd_top, &act, false);
 
6fbf66fa
   if (socket_defined (man->connection.sd_cli))
     {
8bc93d7f
       man->connection.remote = act.dest;
 
6fbf66fa
       if (socket_defined (man->connection.sd_top))
 	{
 #ifdef WIN32
 	  man_stop_ne32 (man);
 #endif
 	}
 
4f404ad3
       man_new_connection_post (man, "Client connected from");
6fbf66fa
     }
 }
 
 static void
 man_listen (struct management *man)
 {
   struct gc_arena gc = gc_new ();
 
   /*
    * Initialize state
    */
   man->connection.state = MS_LISTEN;
   man->connection.sd_cli = SOCKET_UNDEFINED;
 
   /*
    * Initialize listening socket
    */
   if (man->connection.sd_top == SOCKET_UNDEFINED)
     {
bb564a59
 #if UNIX_SOCK_SUPPORT
86f5c7c9
       if (man->settings.flags & MF_UNIX_SOCK)
bb564a59
 	{
 	  man_delete_unix_socket (man);
 	  man->connection.sd_top = create_socket_unix ();
 	  socket_bind_unix (man->connection.sd_top, &man->settings.local_unix, "MANAGEMENT");
 	}
       else
 #endif
 	{
 	  man->connection.sd_top = create_socket_tcp ();
 	  socket_bind (man->connection.sd_top, &man->settings.local, "MANAGEMENT");
 	}
6fbf66fa
 
       /*
        * Listen for connection
        */
       if (listen (man->connection.sd_top, 1))
 	msg (M_SOCKERR, "MANAGEMENT: listen() failed");
 
       /*
        * Set misc socket properties
        */
       set_nonblock (man->connection.sd_top);
       set_cloexec (man->connection.sd_top);
 
bb564a59
 #if UNIX_SOCK_SUPPORT
86f5c7c9
       if (man->settings.flags & MF_UNIX_SOCK)
bb564a59
 	{
 	  msg (D_MANAGEMENT, "MANAGEMENT: unix domain socket listening on %s",
 	       sockaddr_unix_name (&man->settings.local_unix, "NULL"));
 	}
       else
 #endif
 	msg (D_MANAGEMENT, "MANAGEMENT: TCP Socket listening on %s",
 	     print_sockaddr (&man->settings.local, &gc));
6fbf66fa
     }
 
 #ifdef WIN32
   man_start_ne32 (man);
 #endif
   
   gc_free (&gc);
 }
 
 static void
4f404ad3
 man_connect (struct management *man)
 {
   struct gc_arena gc = gc_new ();
   int status;
   int signal_received = 0;
 
   /*
    * Initialize state
    */
   man->connection.state = MS_INITIAL;
   man->connection.sd_top = SOCKET_UNDEFINED;
 
86f5c7c9
 #if UNIX_SOCK_SUPPORT
   if (man->settings.flags & MF_UNIX_SOCK)
     {
       man->connection.sd_cli = create_socket_unix ();
       status = socket_connect_unix (man->connection.sd_cli, &man->settings.local_unix);
       if (!status && !man_verify_unix_peer_uid_gid (man, man->connection.sd_cli))
 	  {
 #ifdef EPERM
 	    status = EPERM;
 #else
 	    status = 1;
 #endif
 	    sd_close (&man->connection.sd_cli);
 	  }
     }
   else
 #endif
     {
       man->connection.sd_cli = create_socket_tcp ();
       status = openvpn_connect (man->connection.sd_cli,
 				&man->settings.local,
 				5,
 				&signal_received);
     }
4f404ad3
 
   if (signal_received)
     {
       throw_signal (signal_received);
       goto done;
     }
 
   if (status)
     {
86f5c7c9
 #if UNIX_SOCK_SUPPORT
       if (man->settings.flags & MF_UNIX_SOCK)
 	{
 	  msg (D_LINK_ERRORS,
 	       "MANAGEMENT: connect to unix socket %s failed: %s",
 	       sockaddr_unix_name (&man->settings.local_unix, "NULL"),
 	       strerror_ts (status, &gc));
 	}
       else
 #endif
4f404ad3
       msg (D_LINK_ERRORS,
 	   "MANAGEMENT: connect to %s failed: %s",
 	   print_sockaddr (&man->settings.local, &gc),
 	   strerror_ts (status, &gc));
       throw_signal_soft (SIGTERM, "management-connect-failed");
       goto done;
     }
 
8d33c060
   man_record_peer_info (man);
4f404ad3
   man_new_connection_post (man, "Connected to management server at");
 
  done:
   gc_free (&gc);
 }
 
 static void
 man_reset_client_socket (struct management *man, const bool exiting)
6fbf66fa
 {
   if (socket_defined (man->connection.sd_cli))
     {
 #ifdef WIN32
       man_stop_ne32 (man);
 #endif
       man_close_socket (man, man->connection.sd_cli);
4f404ad3
       man->connection.sd_cli = SOCKET_UNDEFINED;
3a37ffbe
       man->connection.state = MS_INITIAL;
6fbf66fa
       command_line_reset (man->connection.in);
90efcacb
       buffer_list_reset (man->connection.out);
 #ifdef MANAGEMENT_DEF_AUTH
       in_extra_reset (&man->connection, false);
 #endif
3a37ffbe
       msg (D_MANAGEMENT, "MANAGEMENT: Client disconnected");
6fbf66fa
     }
4f404ad3
   if (!exiting)
     {
5acd2baf
 #if defined(USE_CRYPTO) && defined(USE_SSL)
90efcacb
       if (man->settings.flags & MF_FORGET_DISCONNECT)
5acd2baf
 	ssl_purge_auth ();
 #endif
90efcacb
       if (man->settings.flags & MF_SIGNAL) {
1184b824
       	  int mysig = man_mod_signal (man, SIGUSR1);
 	  if (mysig >= 0)
 	    {
 	      msg (D_MANAGEMENT, "MANAGEMENT: Triggering management signal");
 	      throw_signal_soft (mysig, "management-disconnect");
 	    }
       }
 
90efcacb
       if (man->settings.flags & MF_CONNECT_AS_CLIENT)
8d33c060
 	{
 	  msg (D_MANAGEMENT, "MANAGEMENT: Triggering management exit");
 	  throw_signal_soft (SIGTERM, "management-exit");
 	}
4f404ad3
       else
 	man_listen (man);
     }
6fbf66fa
 }
 
 static void
 man_process_command (struct management *man, const char *line)
 {
   struct gc_arena gc = gc_new ();
   struct status_output *so;
   int nparms;
   char *parms[MAX_PARMS+1];
 
   CLEAR (parms);
   so = status_open (NULL, 0, -1, &man->persist.vout, 0);
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
   in_extra_reset (&man->connection, false);
 #endif
6fbf66fa
 
   if (man_password_needed (man))
     {
       man_check_password (man, line);
     }
   else
     {
       nparms = parse_line (line, parms, MAX_PARMS, "TCP", 0, M_CLIENT, &gc);
       if (parms[0] && streq (parms[0], "password"))
 	msg (D_MANAGEMENT_DEBUG, "MANAGEMENT: CMD 'password [...]'");
7e6f2d6f
       else if (!streq (line, "load-stats"))
6fbf66fa
 	msg (D_MANAGEMENT_DEBUG, "MANAGEMENT: CMD '%s'", line);
 
 #if 0
       /* DEBUGGING -- print args */
       {
 	int i;
 	for (i = 0; i < nparms; ++i)
 	  msg (M_INFO, "[%d] '%s'", i, parms[i]);
       }
 #endif
 
       if (nparms > 0)
 	man_dispatch_command (man, so, (const char **)parms, nparms);
     }
 
   CLEAR (parms);
   status_close (so);
   gc_free (&gc);
 }
 
 static bool
 man_io_error (struct management *man, const char *prefix)
 {
   const int err = openvpn_errno_socket ();
 
   if (!ignore_sys_error (err))
     {
       struct gc_arena gc = gc_new ();
       msg (D_MANAGEMENT, "MANAGEMENT: TCP %s error: %s",
 	   prefix,
 	   strerror_ts (err, &gc));
       gc_free (&gc);
       return true;
     }
   else
     return false;
 }
 
 static int
 man_read (struct management *man)
 {
   /*
    * read command line from socket
    */
   unsigned char buf[256];
   int len = 0;
 
   len = recv (man->connection.sd_cli, buf, sizeof (buf), MSG_NOSIGNAL);
   if (len == 0)
     {
4f404ad3
       man_reset_client_socket (man, false);
6fbf66fa
     }
   else if (len > 0)
     {
       bool processed_command = false;
 
       ASSERT (len <= (int) sizeof (buf));
       command_line_add (man->connection.in, buf, len);
 
       /*
        * Reset output object
        */
90efcacb
       buffer_list_reset (man->connection.out);
6fbf66fa
 
       /*
        * process command line if complete
        */
       {
 	const unsigned char *line;
 	while ((line = command_line_get (man->connection.in)))
 	  {
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
 	    if (man->connection.in_extra)
 	      {
 		if (!strcmp ((char *)line, "END"))
 		  {
 		    in_extra_dispatch (man);
 		    in_extra_reset (&man->connection, false);
 		  }
 		else
 		  {
 		    buffer_list_push (man->connection.in_extra, line);
 		  }
 	      }
 	    else
 #endif
 	      man_process_command (man, (char *) line);
6fbf66fa
 	    if (man->connection.halt)
 	      break;
 	    command_line_next (man->connection.in);
 	    processed_command = true;
 	  }
       }
 
       /*
        * Reset output state to MS_CC_WAIT_(READ|WRITE)
        */
       if (man->connection.halt)
 	{
4f404ad3
 	  man_reset_client_socket (man, false);
6fbf66fa
 	  len = 0;
 	}
       else
 	{
 	  if (processed_command)
 	    man_prompt (man);
 	  man_update_io_state (man);
 	}
     }
   else /* len < 0 */
     {
       if (man_io_error (man, "recv"))
4f404ad3
 	man_reset_client_socket (man, false);
6fbf66fa
     }
   return len;
 }
 
 static int
 man_write (struct management *man)
 {
7e1c085d
   const int size_hint = 1024;
6fbf66fa
   int sent = 0;
7e1c085d
   const struct buffer *buf;
6fbf66fa
 
7e1c085d
   buffer_list_aggregate(man->connection.out, size_hint);
   buf = buffer_list_peek (man->connection.out);
6fbf66fa
   if (buf && BLEN (buf))
     {
7e1c085d
       const int len = min_int (size_hint, BLEN (buf));
6fbf66fa
       sent = send (man->connection.sd_cli, BPTR (buf), len, MSG_NOSIGNAL);
       if (sent >= 0)
 	{
90efcacb
 	  buffer_list_advance (man->connection.out, sent);
6fbf66fa
 	}
       else if (sent < 0)
 	{
 	  if (man_io_error (man, "send"))
4f404ad3
 	    man_reset_client_socket (man, false);
6fbf66fa
 	}
     }
 
   /*
    * Reset output state to MS_CC_WAIT_(READ|WRITE)
    */
   man_update_io_state (man);
 
   return sent;
 }
 
 static void
 man_connection_clear (struct man_connection *mc)
 {
   CLEAR (*mc);
 
   /* set initial state */
   mc->state = MS_INITIAL;
 
   /* clear socket descriptors */
   mc->sd_top = SOCKET_UNDEFINED;
   mc->sd_cli = SOCKET_UNDEFINED;
 }
 
 static void
 man_persist_init (struct management *man,
 		  const int log_history_cache,
 		  const int echo_buffer_size,
 		  const int state_buffer_size)
 {
   struct man_persist *mp = &man->persist;
   if (!mp->defined)
     {
       CLEAR (*mp);
 
       /* initialize log history store */
       mp->log = log_history_init (log_history_cache);  
 
       /*
        * Initialize virtual output object, so that functions
        * which write to a virtual_output object can be redirected
        * here to the management object.
        */
       mp->vout.func = virtual_output_callback_func;
       mp->vout.arg = man;
       mp->vout.flags_default = M_CLIENT;
       msg_set_virtual_output (&mp->vout);
 
       /*
        * Initialize --echo list
        */
       man->persist.echo = log_history_init (echo_buffer_size);
 
       /*
        * Initialize --state list
        */
       man->persist.state = log_history_init (state_buffer_size);
 
       mp->defined = true;
     }
 }
 
 static void
 man_persist_close (struct man_persist *mp)
 {
   if (mp->log)
     {
       msg_set_virtual_output (NULL);
       log_history_close (mp->log);
     }
 
   if (mp->echo)
     log_history_close (mp->echo);
 
   if (mp->state)
     log_history_close (mp->state);
 
   CLEAR (*mp);
 }
       
 static void
 man_settings_init (struct man_settings *ms,
 		   const char *addr,
 		   const int port,
 		   const char *pass_file,
bb564a59
 		   const char *client_user,
 		   const char *client_group,
6fbf66fa
 		   const int log_history_cache,
 		   const int echo_buffer_size,
 		   const int state_buffer_size,
2c21891e
 		   const char *write_peer_info_file,
90efcacb
 		   const int remap_sigusr1,
 		   const unsigned int flags)
6fbf66fa
 {
   if (!ms->defined)
     {
       CLEAR (*ms);
 
90efcacb
       ms->flags = flags;
bb564a59
       ms->client_uid = -1;
       ms->client_gid = -1;
6fbf66fa
 
       /*
        * Get username/password
        */
       if (pass_file)
984cf003
 	get_user_pass (&ms->up, pass_file, "Management", GET_USER_PASS_PASSWORD_ONLY);
6fbf66fa
 
       /*
bb564a59
        * lookup client UID/GID if specified
6fbf66fa
        */
bb564a59
       if (client_user)
 	{
 	  struct user_state s;
 	  get_user (client_user, &s);
 	  ms->client_uid = user_state_uid (&s);
 	  msg (D_MANAGEMENT, "MANAGEMENT: client_uid=%d", ms->client_uid);
 	  ASSERT (ms->client_uid >= 0);
 	}
       if (client_group)
6fbf66fa
 	{
bb564a59
 	  struct group_state s;
 	  get_group (client_group, &s);
 	  ms->client_gid = group_state_gid (&s);
 	  msg (D_MANAGEMENT, "MANAGEMENT: client_gid=%d", ms->client_gid);
 	  ASSERT (ms->client_gid >= 0);
6fbf66fa
 	}
bb564a59
 
       ms->write_peer_info_file = string_alloc (write_peer_info_file, NULL);
 
 #if UNIX_SOCK_SUPPORT
86f5c7c9
       if (ms->flags & MF_UNIX_SOCK)
bb564a59
 	sockaddr_unix_init (&ms->local_unix, addr);
6fbf66fa
       else
bb564a59
 #endif
6fbf66fa
 	{
bb564a59
 	  /*
 	   * Initialize socket address
 	   */
 	  ms->local.sa.sin_family = AF_INET;
 	  ms->local.sa.sin_addr.s_addr = 0;
 	  ms->local.sa.sin_port = htons (port);
 
 	  /*
 	   * Run management over tunnel, or
 	   * separate channel?
 	   */
 	  if (streq (addr, "tunnel") && !(flags & MF_CONNECT_AS_CLIENT))
 	    {
 	      ms->management_over_tunnel = true;
 	    }
 	  else
 	    {
 	      ms->local.sa.sin_addr.s_addr = getaddr
 		(GETADDR_RESOLVE|GETADDR_WARN_ON_SIGNAL|GETADDR_FATAL, addr, 0, NULL, NULL);
 	    }
6fbf66fa
 	}
       
       /*
        * Log history and echo buffer may need to be resized
        */
       ms->log_history_cache = log_history_cache;
       ms->echo_buffer_size = echo_buffer_size;
       ms->state_buffer_size = state_buffer_size;
 
2c21891e
       /*
        * Set remap sigusr1 flags
        */
       if (remap_sigusr1 == SIGHUP)
 	ms->mansig |= MANSIG_MAP_USR1_TO_HUP;
       else if (remap_sigusr1 == SIGTERM)
 	ms->mansig |= MANSIG_MAP_USR1_TO_TERM;
 
6fbf66fa
       ms->defined = true;
     }
 }
 
 static void
 man_settings_close (struct man_settings *ms)
 {
8d33c060
   free (ms->write_peer_info_file);
6fbf66fa
   CLEAR (*ms);
 }
 
 
 static void
 man_connection_init (struct management *man)
 {
   if (man->connection.state == MS_INITIAL)
     {
 #ifdef WIN32
       /*
        * This object is a sort of TCP/IP helper
        * for Windows.
        */
       net_event_win32_init (&man->connection.ne32);
 #endif
 
       /*
        * Allocate helper objects for command line input and
        * command output from/to the socket.
        */
1b3842c6
       man->connection.in = command_line_new (1024);
90efcacb
       man->connection.out = buffer_list_new (0);
6fbf66fa
 
       /*
        * Initialize event set for standalone usage, when we are
        * running outside of the primary event loop.
        */
       {
 	int maxevents = 1;
 	man->connection.es = event_set_init (&maxevents, EVENT_METHOD_FAST);
       }
 
       /*
4f404ad3
        * Listen/connect socket
6fbf66fa
        */
90efcacb
       if (man->settings.flags & MF_CONNECT_AS_CLIENT)
4f404ad3
 	man_connect (man);
       else
 	man_listen (man);
6fbf66fa
     }
 }
 
 static void
 man_connection_close (struct management *man)
 {
   struct man_connection *mc = &man->connection;
 
   if (mc->es)
     event_free (mc->es);
 #ifdef WIN32
   net_event_win32_close (&mc->ne32);
 #endif
   if (socket_defined (mc->sd_top))
bb564a59
     {
       man_close_socket (man, mc->sd_top);
       man_delete_unix_socket (man);
     }
6fbf66fa
   if (socket_defined (mc->sd_cli))
     man_close_socket (man, mc->sd_cli);
   if (mc->in)
     command_line_free (mc->in);
   if (mc->out)
90efcacb
     buffer_list_free (mc->out);
 #ifdef MANAGEMENT_DEF_AUTH
   in_extra_reset (&man->connection, false);
 #endif
6fbf66fa
   man_connection_clear (mc);
 }
 
 struct management *
 management_init (void)
 {
   struct management *man;
   ALLOC_OBJ_CLEAR (man, struct management);
 
   man_persist_init (man,
 		    MANAGEMENT_LOG_HISTORY_INITIAL_SIZE,
 		    MANAGEMENT_ECHO_BUFFER_SIZE,
 		    MANAGEMENT_STATE_BUFFER_SIZE);
 
   man_connection_clear (&man->connection);
 
   return man;
 }
 
 bool
 management_open (struct management *man,
 		 const char *addr,
 		 const int port,
 		 const char *pass_file,
bb564a59
 		 const char *client_user,
 		 const char *client_group,
6fbf66fa
 		 const int log_history_cache,
 		 const int echo_buffer_size,
 		 const int state_buffer_size,
2c21891e
 		 const char *write_peer_info_file,
90efcacb
 		 const int remap_sigusr1,
 		 const unsigned int flags)
6fbf66fa
 {
   bool ret = false;
 
   /*
    * Save the settings only if they have not
    * been saved before.
    */
   man_settings_init (&man->settings,
 		     addr,
 		     port,
 		     pass_file,
bb564a59
 		     client_user,
 		     client_group,
6fbf66fa
 		     log_history_cache,
 		     echo_buffer_size,
 		     state_buffer_size,
2c21891e
 		     write_peer_info_file,
90efcacb
 		     remap_sigusr1,
 		     flags);
6fbf66fa
 
   /*
    * The log is initially sized to MANAGEMENT_LOG_HISTORY_INITIAL_SIZE,
    * but may be changed here.  Ditto for echo and state buffers.
    */
   log_history_resize (man->persist.log, man->settings.log_history_cache);
   log_history_resize (man->persist.echo, man->settings.echo_buffer_size);
   log_history_resize (man->persist.state, man->settings.state_buffer_size);
 
   /*
    * If connection object is uninitialized and we are not doing
    * over-the-tunnel management, then open (listening) connection.
    */
   if (man->connection.state == MS_INITIAL)
     {
       if (!man->settings.management_over_tunnel)
 	{
 	  man_connection_init (man);
 	  ret = true;
 	}
     }
 
   return ret;
 }
 
 void
 management_close (struct management *man)
 {
   man_connection_close (man);
   man_settings_close (&man->settings);
   man_persist_close (&man->persist);
   free (man);
 }
 
 void
 management_set_callback (struct management *man,
 			 const struct management_callback *cb)
 {
   man->persist.standalone_disabled = true;
   man->persist.callback = *cb;
 }
 
 void
 management_clear_callback (struct management *man)
 {
   man->persist.standalone_disabled = false;
   man->persist.hold_release = false;
   CLEAR (man->persist.callback);
3cf6c932
   man_output_list_push_finalize (man); /* flush output queue */
6fbf66fa
 }
 
 void
 management_set_state (struct management *man,
 		      const int state,
 		      const char *detail,
5ad84585
 		      const in_addr_t tun_local_ip,
 		      const in_addr_t tun_remote_ip)
6fbf66fa
 {
90efcacb
   if (man->persist.state && (!(man->settings.flags & MF_SERVER) || state < OPENVPN_STATE_CLIENT_BASE))
6fbf66fa
     {
       struct gc_arena gc = gc_new ();
       struct log_entry e;
       const char *out = NULL;
 
       update_time ();
       CLEAR (e);
       e.timestamp = now;
       e.u.state = state;
       e.string = detail;
       e.local_ip = tun_local_ip;
5ad84585
       e.remote_ip = tun_remote_ip;
6fbf66fa
       
       log_history_add (man->persist.state, &e);
 
       if (man->connection.state_realtime)
 	out = log_entry_print (&e, LOG_PRINT_STATE_PREFIX
 			       |   LOG_PRINT_INT_DATE
                                |   LOG_PRINT_STATE
 			       |   LOG_PRINT_LOCAL_IP
5ad84585
 			       |   LOG_PRINT_REMOTE_IP
e482a632
                                |   LOG_PRINT_CRLF
 			       |   LOG_ECHO_TO_LOG, &gc);
6fbf66fa
 
       if (out)
 	man_output_list_push (man, out);
 
       gc_free (&gc);
     }
 }
 
90efcacb
 #ifdef MANAGEMENT_DEF_AUTH
 
7e1c085d
 static bool
 env_filter_match (const char *env_str, const int env_filter_level)
 {
   static const char *env_names[] = {
     "username=",
     "password=",
     "X509_0_CN=",
     "tls_serial_0=",
     "untrusted_ip=",
     "ifconfig_local=",
     "ifconfig_netmask=",
     "daemon_start_time=",
     "daemon_pid=",
     "dev=",
     "ifconfig_pool_remote_ip=",
     "ifconfig_pool_netmask=",
     "time_duration=",
     "bytes_sent=",
     "bytes_received="
   };
   if (env_filter_level >= 1)
     {
       size_t i;
       for (i = 0; i < SIZE(env_names); ++i)
 	{
 	  const char *en = env_names[i];
 	  const size_t len = strlen(en);
 	  if (strncmp(env_str, en, len) == 0)
 	    return true;
 	}
       return false;
     }
   else
     return true;
 }
 
90efcacb
 static void
7e1c085d
 man_output_env (const struct env_set *es, const bool tail, const int env_filter_level)
90efcacb
 {
   if (es)
     {
       struct env_item *e;
       for (e = es->list; e != NULL; e = e->next)
 	{
7e1c085d
 	  if (e->string && (!env_filter_level || env_filter_match(e->string, env_filter_level)))
90efcacb
 	    msg (M_CLIENT, ">CLIENT:ENV,%s", e->string);
 	}
     }
6ce4a658
   if (tail)
     msg (M_CLIENT, ">CLIENT:ENV,END");
 }
 
 static void
 man_output_extra_env (struct management *man)
 {
   struct gc_arena gc = gc_new ();
   struct env_set *es = env_set_create (&gc);
   if (man->persist.callback.n_clients)
     {
       const int nclients = (*man->persist.callback.n_clients) (man->persist.callback.arg);
       setenv_int (es, "n_clients", nclients);
     }
7e1c085d
   man_output_env (es, false, man->connection.env_filter_level);
6ce4a658
   gc_free (&gc);
90efcacb
 }
 
aaf72974
 static bool
 validate_peer_info_line(const char *line)
 {
   uint8_t c;
   int state = 0;
   while ((c=*line++))
     {
       switch (state)
 	{
 	case 0:
 	case 1:
 	  if (c == '=' && state == 1)
 	    state = 2;
 	  else if (isalnum(c) || c == '_')
 	    state = 1;
 	  else
 	    return false;
 	case 2:
 	  if (isprint(c))
 	    ;
 	  else
 	    return false;
 	}
     }
   return (state == 2);
 }
 
 static void
 man_output_peer_info_env (struct management *man, struct man_def_auth_context *mdac)
 {
   char line[256];
   if (man->persist.callback.get_peer_info)
     {
       const char *peer_info = (*man->persist.callback.get_peer_info) (man->persist.callback.arg, mdac->cid);
       if (peer_info)
 	{
 	  struct buffer buf;
 	  buf_set_read (&buf, (const uint8_t *) peer_info, strlen(peer_info));
 	  while (buf_parse (&buf, '\n', line, sizeof (line)))
 	    {
 	      chomp (line);
 	      if (validate_peer_info_line(line))
 		{
 		  msg (M_CLIENT, ">CLIENT:ENV,%s", line);
 		}
 	      else
 		msg (D_MANAGEMENT, "validation failed on peer_info line received from client");
 	    }
 	}
     }
 }
 
90efcacb
 void
 management_notify_client_needing_auth (struct management *management,
 				       const unsigned int mda_key_id,
 				       struct man_def_auth_context *mdac,
 				       const struct env_set *es)
 {
   if (!(mdac->flags & DAF_CONNECTION_CLOSED))
     {
       const char *mode = "CONNECT";
       if (mdac->flags & DAF_CONNECTION_ESTABLISHED)
 	mode = "REAUTH";
       msg (M_CLIENT, ">CLIENT:%s,%lu,%u", mode, mdac->cid, mda_key_id);
6ce4a658
       man_output_extra_env (management);
aaf72974
       man_output_peer_info_env(management, mdac);
7e1c085d
       man_output_env (es, true, management->connection.env_filter_level);
90efcacb
       mdac->flags |= DAF_INITIAL_AUTH;
     }
 }
 
 void
 management_connection_established (struct management *management,
60d9e137
 				   struct man_def_auth_context *mdac,
 				   const struct env_set *es)
90efcacb
 {
   mdac->flags |= DAF_CONNECTION_ESTABLISHED;
60d9e137
   msg (M_CLIENT, ">CLIENT:ESTABLISHED,%lu", mdac->cid);
   man_output_extra_env (management);
7e1c085d
   man_output_env (es, true, management->connection.env_filter_level);
90efcacb
 }
 
 void
 management_notify_client_close (struct management *management,
 				struct man_def_auth_context *mdac,
 				const struct env_set *es)
 {
   if ((mdac->flags & DAF_INITIAL_AUTH) && !(mdac->flags & DAF_CONNECTION_CLOSED))
     {
       msg (M_CLIENT, ">CLIENT:DISCONNECT,%lu", mdac->cid);
7e1c085d
       man_output_env (es, true, management->connection.env_filter_level);
90efcacb
       mdac->flags |= DAF_CONNECTION_CLOSED;
     }
 }
 
 void
 management_learn_addr (struct management *management,
 		       struct man_def_auth_context *mdac,
 		       const struct mroute_addr *addr,
 		       const bool primary)
 {
   struct gc_arena gc = gc_new ();
   if ((mdac->flags & DAF_INITIAL_AUTH) && !(mdac->flags & DAF_CONNECTION_CLOSED))
     {
       msg (M_CLIENT, ">CLIENT:ADDRESS,%lu,%s,%d",
 	   mdac->cid,
 	   mroute_addr_print_ex (addr, MAPF_SUBNET, &gc),
 	   BOOL_CAST (primary));
     }
   gc_free (&gc);
 }
 
 #endif
 
6fbf66fa
 void
3c7f2f55
 management_echo (struct management *man, const char *string, const bool pull)
6fbf66fa
 {
   if (man->persist.echo)
     {
       struct gc_arena gc = gc_new ();
       struct log_entry e;
       const char *out = NULL;
 
       update_time ();
       CLEAR (e);
       e.timestamp = now;
       e.string = string;
3c7f2f55
       e.u.intval = BOOL_CAST (pull);
 
6fbf66fa
       log_history_add (man->persist.echo, &e);
 
       if (man->connection.echo_realtime)
3c7f2f55
 	out = log_entry_print (&e, LOG_PRINT_INT_DATE|LOG_PRINT_ECHO_PREFIX|LOG_PRINT_CRLF|MANAGEMENT_ECHO_FLAGS, &gc);
6fbf66fa
 
       if (out)
 	man_output_list_push (man, out);
 
       gc_free (&gc);
     }
 }
 
 void
 management_post_tunnel_open (struct management *man, const in_addr_t tun_local_ip)
 {
   /*
    * If we are running management over the tunnel,
    * this is the place to initialize the connection.
    */
   if (man->settings.management_over_tunnel
       && man->connection.state == MS_INITIAL)
     {
       /* listen on our local TUN/TAP IP address */
8bc93d7f
       man->settings.local.sa.sin_addr.s_addr = htonl (tun_local_ip);
6fbf66fa
       man_connection_init (man);
     }
 
 }
 
 void
 management_pre_tunnel_close (struct management *man)
 {
   if (man->settings.management_over_tunnel)
     man_connection_close (man);
 }
 
 void
8c7c6be4
 management_auth_failure (struct management *man, const char *type, const char *reason)
6fbf66fa
 {
8c7c6be4
   if (reason)
     msg (M_CLIENT, ">PASSWORD:Verification Failed: '%s' ['%s']", type, reason);
   else
     msg (M_CLIENT, ">PASSWORD:Verification Failed: '%s'", type);
6fbf66fa
 }
 
 static inline bool
 man_persist_state (unsigned int *persistent, const int n)
 {
   if (persistent)
     {
       if (*persistent == (unsigned int)n)
 	return false;
       *persistent = n;
     }
   return true;
 }
 
 #ifdef WIN32
 
 void
 management_socket_set (struct management *man,
 		       struct event_set *es,
 		       void *arg,
 		       unsigned int *persistent)
 {
   if (man->connection.state != MS_INITIAL)
     {
       event_t ev = net_event_win32_get_event (&man->connection.ne32);
       net_event_win32_reset_write (&man->connection.ne32);
 
       switch (man->connection.state)
 	{
 	case MS_LISTEN:
 	  if (man_persist_state (persistent, 1))
 	    event_ctl (es, ev, EVENT_READ, arg);
 	  break;
 	case MS_CC_WAIT_READ:
 	  if (man_persist_state (persistent, 2))
 	    event_ctl (es, ev, EVENT_READ, arg);
 	  break;
 	case MS_CC_WAIT_WRITE:
 	  if (man_persist_state (persistent, 3))
 	    event_ctl (es, ev, EVENT_READ|EVENT_WRITE, arg);
 	  break;
 	default:
 	  ASSERT (0);
 	}
     }
 }
 
 void
 management_io (struct management *man)
 {
   if (man->connection.state != MS_INITIAL)
     {
       long net_events;
       net_event_win32_reset (&man->connection.ne32);
       net_events = net_event_win32_get_event_mask (&man->connection.ne32);
 
       if (net_events & FD_CLOSE)
 	{
4f404ad3
 	  man_reset_client_socket (man, false);
6fbf66fa
 	}
       else
 	{
 	  if (man->connection.state == MS_LISTEN)
 	    {
 	      if (net_events & FD_ACCEPT)
 		{
 		  man_accept (man);
 		  net_event_win32_clear_selected_events (&man->connection.ne32, FD_ACCEPT);
 		}
 	    }
3cf6c932
 	  else if (man->connection.state == MS_CC_WAIT_READ || man->connection.state == MS_CC_WAIT_WRITE)
6fbf66fa
 	    {
 	      if (net_events & FD_READ)
 		{
1b3842c6
 		  while (man_read (man) > 0)
 		    ;
6fbf66fa
 		  net_event_win32_clear_selected_events (&man->connection.ne32, FD_READ);
 		}
 
 	      if (net_events & FD_WRITE)
 		{
 		  int status;
 		  status = man_write (man);
 		  if (status < 0 && WSAGetLastError() == WSAEWOULDBLOCK)
 		    {
 		      net_event_win32_clear_selected_events (&man->connection.ne32, FD_WRITE);
 		    }
 		}
 	    }
 	}
     }
 }
 
 #else
 
 void
 management_socket_set (struct management *man,
 		       struct event_set *es,
 		       void *arg,
 		       unsigned int *persistent)
 {
   switch (man->connection.state)
     {
     case MS_LISTEN:
       if (man_persist_state (persistent, 1))
 	event_ctl (es, man->connection.sd_top, EVENT_READ, arg);
       break;
     case MS_CC_WAIT_READ:
       if (man_persist_state (persistent, 2))
 	event_ctl (es, man->connection.sd_cli, EVENT_READ, arg);
       break;
     case MS_CC_WAIT_WRITE:
       if (man_persist_state (persistent, 3))
 	event_ctl (es, man->connection.sd_cli, EVENT_WRITE, arg);
       break;
     case MS_INITIAL:
       break;
     default:
       ASSERT (0);
     }
 }
 
 void
 management_io (struct management *man)
 {
   switch (man->connection.state)
     {
     case MS_LISTEN:
       man_accept (man);
       break;
     case MS_CC_WAIT_READ:
       man_read (man);
       break;
     case MS_CC_WAIT_WRITE:
       man_write (man);
       break;
     case MS_INITIAL:
       break;
     default:
       ASSERT (0);
     }
 }
 
 #endif
 
5acb3a79
 static inline bool
6fbf66fa
 man_standalone_ok (const struct management *man)
 {
   return !man->settings.management_over_tunnel && man->connection.state != MS_INITIAL;
 }
 
8d33c060
 static bool
 man_check_for_signals (volatile int *signal_received)
 {
   if (signal_received)
     {
       get_signal (signal_received);
       if (*signal_received)
 	return true;
     }
   return false;
 }
 
6fbf66fa
 /*
  * Wait for socket I/O when outside primary event loop
  */
 static int
 man_block (struct management *man, volatile int *signal_received, const time_t expire)
 {
   struct timeval tv;
   struct event_set_return esr;
   int status = -1;
   
   if (man_standalone_ok (man))
     {
3cf6c932
       while (true)
6fbf66fa
 	{
 	  event_reset (man->connection.es);
 	  management_socket_set (man, man->connection.es, NULL, NULL);
 	  tv.tv_usec = 0;
 	  tv.tv_sec = 1;
8d33c060
 	  if (man_check_for_signals (signal_received))
 	    {
 	      status = -1;
 	      break;
 	    }
6fbf66fa
 	  status = event_wait (man->connection.es, &tv, &esr, 1);
 	  update_time ();
8d33c060
 	  if (man_check_for_signals (signal_received))
6fbf66fa
 	    {
8d33c060
 	      status = -1;
 	      break;
6fbf66fa
 	    }
3cf6c932
 
 	  if (status > 0)
 	    break;
 	  else if (expire && now >= expire)
6fbf66fa
 	    {
3cf6c932
 	      /* set SIGINT signal if expiration time exceeded */
6fbf66fa
 	      status = 0;
 	      if (signal_received)
 		*signal_received = SIGINT;
 	      break;
 	    }
3cf6c932
 	}
6fbf66fa
     }
   return status;
 }
 
 /*
  * Perform management socket output outside primary event loop
  */
 static void
 man_output_standalone (struct management *man, volatile int *signal_received)
 {
   if (man_standalone_ok (man))
     {
       while (man->connection.state == MS_CC_WAIT_WRITE)
 	{
 	  management_io (man);
 	  if (man->connection.state == MS_CC_WAIT_WRITE)
 	    man_block (man, signal_received, 0);
 	  if (signal_received && *signal_received)
 	    break;
 	}
     }
 }
 
 /*
  * Process management event loop outside primary event loop
  */
 static int
 man_standalone_event_loop (struct management *man, volatile int *signal_received, const time_t expire)
 {
3a37ffbe
   int status = -1;
   if (man_standalone_ok (man))
     {
       status = man_block (man, signal_received, expire);
       if (status > 0)
 	management_io (man);
     }
6fbf66fa
   return status;
 }
 
 #define MWCC_PASSWORD_WAIT (1<<0)
 #define MWCC_HOLD_WAIT     (1<<1)
 
 /*
  * Block until client connects
  */
 static void
 man_wait_for_client_connection (struct management *man,
 				volatile int *signal_received,
 				const time_t expire,
 				unsigned int flags)
 {
   ASSERT (man_standalone_ok (man));
   if (man->connection.state == MS_LISTEN)
     {
       if (flags & MWCC_PASSWORD_WAIT)
 	msg (D_MANAGEMENT, "Need password(s) from management interface, waiting...");
       if (flags & MWCC_HOLD_WAIT)
 	msg (D_MANAGEMENT, "Need hold release from management interface, waiting...");
       do {
 	man_standalone_event_loop (man, signal_received, expire);
 	if (signal_received && *signal_received)
 	  break;
       } while (man->connection.state == MS_LISTEN || man_password_needed (man));
     }
 }
 
 /*
  * Process the management event loop for sec seconds
  */
 void
 management_event_loop_n_seconds (struct management *man, int sec)
 {
   if (man_standalone_ok (man))
     {
       volatile int signal_received = 0;
       const bool standalone_disabled_save = man->persist.standalone_disabled;
3cf6c932
       time_t expire = 0;
6fbf66fa
 
       man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
 
       /* set expire time */
       update_time ();
3cf6c932
       if (sec)
 	expire = now + sec;
6fbf66fa
 
       /* if no client connection, wait for one */
       man_wait_for_client_connection (man, &signal_received, expire, 0);
       if (signal_received)
 	return;
 
3cf6c932
       /* run command processing event loop */
       do
6fbf66fa
 	{
 	  man_standalone_event_loop (man, &signal_received, expire);
3a37ffbe
 	  if (!signal_received)
 	    man_check_for_signals (&signal_received);
6fbf66fa
 	  if (signal_received)
 	    return;
3cf6c932
 	} while (expire);
6fbf66fa
 
       /* revert state */
       man->persist.standalone_disabled = standalone_disabled_save;
     }
   else
     {
       sleep (sec);
     }
 }
 
 /*
  * Get a username/password from management channel in standalone mode.
  */
 bool
 management_query_user_pass (struct management *man,
 			    struct user_pass *up,
 			    const char *type,
984cf003
 			    const unsigned int flags)
6fbf66fa
 {
   struct gc_arena gc = gc_new ();
   bool ret = false;
 
   if (man_standalone_ok (man))
     {
       volatile int signal_received = 0;
       const bool standalone_disabled_save = man->persist.standalone_disabled;
       struct buffer alert_msg = alloc_buf_gc (128, &gc);
984cf003
       const char *alert_type = NULL;
       const char *prefix = NULL;
       unsigned int up_query_mode = 0;
6fbf66fa
 
       ret = true;
       man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
       man->persist.special_state_msg = NULL;
 
       CLEAR (man->connection.up_query);
 
984cf003
       if (flags & GET_USER_PASS_NEED_OK)
 	{
 	  up_query_mode = UP_QUERY_NEED_OK;
 	  prefix= "NEED-OK";
 	  alert_type = "confirmation";
 	}
1bda73a7
       else if (flags & GET_USER_PASS_NEED_STR)
         {
 	  up_query_mode = UP_QUERY_NEED_STR;
 	  prefix= "NEED-STR";
 	  alert_type = "string";
 	}
984cf003
       else if (flags & GET_USER_PASS_PASSWORD_ONLY)
 	{
 	  up_query_mode = UP_QUERY_PASS;
 	  prefix = "PASSWORD";
 	  alert_type = "password";
 	}
       else
 	{
 	  up_query_mode = UP_QUERY_USER_PASS;
 	  prefix = "PASSWORD";
 	  alert_type = "username/password";
 	}
       buf_printf (&alert_msg, ">%s:Need '%s' %s",
 		  prefix,
6fbf66fa
 		  type,
984cf003
 		  alert_type);
6fbf66fa
 
1bda73a7
       if (flags & (GET_USER_PASS_NEED_OK | GET_USER_PASS_NEED_STR))
dd1047f5
 	buf_printf (&alert_msg, " MSG:%s", up->username);
 
6fbf66fa
       man_wait_for_client_connection (man, &signal_received, 0, MWCC_PASSWORD_WAIT);
       if (signal_received)
 	ret = false;
 
       if (ret)
 	{
 	  man->persist.special_state_msg = BSTR (&alert_msg);
 	  msg (M_CLIENT, "%s", man->persist.special_state_msg);
 
 	  /* tell command line parser which info we need */
984cf003
 	  man->connection.up_query_mode = up_query_mode;
6fbf66fa
 	  man->connection.up_query_type = type;
 
 	  /* run command processing event loop until we get our username/password */
 	  do
 	    {
 	      man_standalone_event_loop (man, &signal_received, 0);
3a37ffbe
 	      if (!signal_received)
 		man_check_for_signals (&signal_received);
6fbf66fa
 	      if (signal_received)
 		{
 		  ret = false;
 		  break;
 		}
 	    } while (!man->connection.up_query.defined);
 	}
 
       /* revert state */
       man->connection.up_query_mode = UP_QUERY_DISABLED;
       man->connection.up_query_type = NULL;
       man->persist.standalone_disabled = standalone_disabled_save;
       man->persist.special_state_msg = NULL;
 
d19b079b
       /* pass through blank passwords */
       if (!strcmp (man->connection.up_query.password, blank_up))
 	CLEAR (man->connection.up_query.password);
 
6fbf66fa
       /*
        * Transfer u/p to return object, zero any record
        * we hold in the management object.
        */
       if (ret)
 	{
 	  man->connection.up_query.nocache = up->nocache; /* preserve caller's nocache setting */
 	  *up = man->connection.up_query;
 	}
       CLEAR (man->connection.up_query);
     }
 
   gc_free (&gc);
   return ret;
 }
 
 /*
  * Return true if management_hold() would block
  */
 bool
 management_would_hold (struct management *man)
 {
90efcacb
   return (man->settings.flags & MF_HOLD) && !man->persist.hold_release && man_standalone_ok (man);
6fbf66fa
 }
 
 /*
92bbb061
  * Return true if (from the management interface's perspective) OpenVPN should
  * daemonize.
  */
 bool
 management_should_daemonize (struct management *man)
 {
90efcacb
   return management_would_hold (man) || (man->settings.flags & MF_QUERY_PASSWORDS);
92bbb061
 }
 
 /*
6fbf66fa
  * If the hold flag is enabled, hibernate until a management client releases the hold.
  * Return true if the caller should not sleep for an additional time interval.
  */
 bool
 management_hold (struct management *man)
 {
   if (management_would_hold (man))
     {
       volatile int signal_received = 0;
       const bool standalone_disabled_save = man->persist.standalone_disabled;
 
       man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
       man->persist.special_state_msg = NULL;
2c21891e
       man->settings.mansig |= MANSIG_IGNORE_USR1_HUP;
6fbf66fa
 
       man_wait_for_client_connection (man, &signal_received, 0, MWCC_HOLD_WAIT);
 
       if (!signal_received)
 	{
 	  man->persist.special_state_msg = ">HOLD:Waiting for hold release";
 	  msg (M_CLIENT, "%s", man->persist.special_state_msg);
 
 	  /* run command processing event loop until we get our username/password */
 	  do
 	    {
 	      man_standalone_event_loop (man, &signal_received, 0);
3a37ffbe
 	      if (!signal_received)
 		man_check_for_signals (&signal_received);
6fbf66fa
 	      if (signal_received)
 		break;
 	    } while (!man->persist.hold_release);
 	}
 
       /* revert state */
       man->persist.standalone_disabled = standalone_disabled_save;
       man->persist.special_state_msg = NULL;
2c21891e
       man->settings.mansig &= ~MANSIG_IGNORE_USR1_HUP;
6fbf66fa
 
       return true;
     }
   return false;
 }
 
 /*
  * struct command_line
  */
 
 struct command_line *
 command_line_new (const int buf_len)
 {
   struct command_line *cl;
   ALLOC_OBJ_CLEAR (cl, struct command_line);
   cl->buf = alloc_buf (buf_len);
   cl->residual = alloc_buf (buf_len);
   return cl;
 }
 
 void
 command_line_reset (struct command_line *cl)
 {
   buf_clear (&cl->buf);
   buf_clear (&cl->residual);
 }
 
 void
 command_line_free (struct command_line *cl)
 {
   command_line_reset (cl);
   free_buf (&cl->buf);
   free_buf (&cl->residual);
   free (cl);
 }
 
 void
 command_line_add (struct command_line *cl, const unsigned char *buf, const int len)
 {
   int i;
   for (i = 0; i < len; ++i)
     {
       if (buf[i] && (isprint(buf[i]) || buf[i] == '\n'))
 	{
 	  if (!buf_write_u8 (&cl->buf, buf[i]))
 	    buf_clear (&cl->buf);
 	}
     }
 }
 
 const unsigned char *
 command_line_get (struct command_line *cl)
 {
   int i;
   const unsigned char *ret = NULL;
 
   i = buf_substring_len (&cl->buf, '\n');
   if (i >= 0)
     {
       buf_copy_excess (&cl->residual, &cl->buf, i);
       buf_chomp (&cl->buf);
       ret = (const unsigned char *) BSTR (&cl->buf);
     }
   return ret;
 }
 
 void
 command_line_next (struct command_line *cl)
 {
   buf_clear (&cl->buf);
   buf_copy (&cl->buf, &cl->residual);
   buf_clear (&cl->residual);
 }
 
 /*
  * struct log_entry
  */
 
 const char *
 log_entry_print (const struct log_entry *e, unsigned int flags, struct gc_arena *gc)
 {
   struct buffer out = alloc_buf_gc (ERR_BUF_SIZE, gc);
   if (flags & LOG_FATAL_NOTIFY)
     buf_printf (&out, ">FATAL:");    
   if (flags & LOG_PRINT_LOG_PREFIX)
     buf_printf (&out, ">LOG:");
   if (flags & LOG_PRINT_ECHO_PREFIX)
     buf_printf (&out, ">ECHO:");
   if (flags & LOG_PRINT_STATE_PREFIX)
     buf_printf (&out, ">STATE:");
   if (flags & LOG_PRINT_INT_DATE)
     buf_printf (&out, "%u,", (unsigned int)e->timestamp);
   if (flags & LOG_PRINT_MSG_FLAGS)
     buf_printf (&out, "%s,", msg_flags_string (e->u.msg_flags, gc));
   if (flags & LOG_PRINT_STATE)
     buf_printf (&out, "%s,", man_state_name (e->u.state));
3c7f2f55
   if (flags & LOG_PRINT_INTVAL)
     buf_printf (&out, "%d,", e->u.intval);
6fbf66fa
   if (e->string)
     buf_printf (&out, "%s", e->string);
   if (flags & LOG_PRINT_LOCAL_IP)
     buf_printf (&out, ",%s", print_in_addr_t (e->local_ip, IA_EMPTY_IF_UNDEF, gc));
5ad84585
   if (flags & LOG_PRINT_REMOTE_IP)
     buf_printf (&out, ",%s", print_in_addr_t (e->remote_ip, IA_EMPTY_IF_UNDEF, gc));
e482a632
   if (flags & LOG_ECHO_TO_LOG)
     msg (D_MANAGEMENT, "MANAGEMENT: %s", BSTR (&out));
6fbf66fa
   if (flags & LOG_PRINT_CRLF)
     buf_printf (&out, "\r\n");
   return BSTR (&out);
 }
 
 static void
 log_entry_free_contents (struct log_entry *e)
 {
   if (e->string)
     free ((char *)e->string);
   CLEAR (*e);
 }
 
 /*
  * struct log_history
  */
 
 static inline int
 log_index (const struct log_history *h, int i)
 {
   return modulo_add (h->base, i, h->capacity);
 }
 
 static void
 log_history_obj_init (struct log_history *h, int capacity)
 {
   CLEAR (*h);
   h->capacity = capacity;
   ALLOC_ARRAY_CLEAR (h->array, struct log_entry, capacity);
 }
 
 struct log_history *
 log_history_init (const int capacity)
 {
   struct log_history *h;
   ASSERT (capacity > 0);
   ALLOC_OBJ (h, struct log_history);
   log_history_obj_init (h, capacity);
   return h;
 }
 
 static void
 log_history_free_contents (struct log_history *h)
 {
   int i;
   for (i = 0; i < h->size; ++i)
     log_entry_free_contents (&h->array[log_index(h, i)]);
   free (h->array);
 }
 
 void
 log_history_close (struct log_history *h)
 {
   log_history_free_contents (h);
   free (h);
 }
 
 void
 log_history_add (struct log_history *h, const struct log_entry *le)
 {
   struct log_entry *e;
   ASSERT (h->size >= 0 && h->size <= h->capacity);
   if (h->size == h->capacity)
     {
       e = &h->array[h->base];
       log_entry_free_contents (e);
       h->base = log_index (h, 1);
     }
   else
     {
       e = &h->array[log_index(h, h->size)];
       ++h->size;
     }
 
   *e = *le;
   e->string = string_alloc (le->string, NULL);
 }
 
 void
 log_history_resize (struct log_history *h, const int capacity)
 {
   if (capacity != h->capacity)
     {
       struct log_history newlog;
       int i;
 
       ASSERT (capacity > 0);
       log_history_obj_init (&newlog, capacity);
 
       for (i = 0; i < h->size; ++i)
 	log_history_add (&newlog, &h->array[log_index(h, i)]);
 			 
       log_history_free_contents (h);
       *h = newlog;
     }
 }
 
 const struct log_entry *
 log_history_ref (const struct log_history *h, const int index)
 {
   if (index >= 0 && index < h->size)
     return &h->array[log_index(h, (h->size - 1) - index)];
   else
     return NULL;
 }
 
3cf6c932
 #if HTTP_PROXY_FALLBACK
 
 void
 management_http_proxy_fallback_notify (struct management *man, const char *type, const char *remote_ip_hint)
 {
   if (remote_ip_hint)
     msg (M_CLIENT, ">PROXY:%s,%s", type, remote_ip_hint);
   else
     msg (M_CLIENT, ">PROXY:%s", type);
 }
 
 #endif /* HTTP_PROXY_FALLBACK */
 
6fbf66fa
 #else
 static void dummy(void) {}
 #endif /* ENABLE_MANAGEMENT */