]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Add support for register-dns through interactive service
authorSelva Nair <selva.nair@gmail.com>
Fri, 11 Mar 2016 04:47:26 +0000 (23:47 -0500)
committerGert Doering <gert@greenie.muc.de>
Mon, 16 May 2016 15:44:45 +0000 (17:44 +0200)
The call to the service returns promptly after delegating the job to
a thread, before the task is completed. In the thread, "net stop dnscache",
"net start dnscache", "ipconfig /flushdns" and "ipconfig /register-dns"
are executed in that order.

Parallel execution of these commands is prevented by a lock that is
common to all connections started by the service.

Note: "net stop .." is used instead of "sc stop.." as the latter can
return before the service has fully stopped (in STOP_PENDING state),
causing the subsequent start to fail.

Signed-off-by: Selva Nair <selva.nair@gmail.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <1457671646-4322-1-git-send-email-selva.nair@gmail.com>
URL: http://article.gmane.org/gmane.network.openvpn.devel/11354
Signed-off-by: Gert Doering <gert@greenie.muc.de>
include/openvpn-msg.h
src/openvpn/tun.c
src/openvpnserv/interactive.c

index 7470512a7e849f9b5964f56d817f248d8e97df16..4c13acf3532876411e3c871c1d6ab59698a6af2a 100644 (file)
@@ -37,7 +37,8 @@ typedef enum {
   msg_del_nbt_cfg,
   msg_flush_neighbors,
   msg_add_block_dns,
-  msg_del_block_dns
+  msg_del_block_dns,
+  msg_register_dns
 } message_type_t;
 
 typedef struct {
index f79d067a311771bb6e23a65bc8073c866d8b74ff..b7a29f718c1330f57d51c79cd05e1f34c2081c19 100644 (file)
@@ -5101,10 +5101,43 @@ fork_dhcp_action (struct tuntap *tt)
     }
 }
 
+static void
+register_dns_service (const struct tuntap *tt)
+{
+  DWORD len;
+  HANDLE msg_channel = tt->options.msg_channel;
+  ack_message_t ack;
+  struct gc_arena gc = gc_new ();
+
+  message_header_t rdns = { msg_register_dns, sizeof(message_header_t), 0 };
+
+  if (!WriteFile (msg_channel, &rdns, sizeof (rdns), &len, NULL) ||
+      !ReadFile (msg_channel, &ack, sizeof (ack), &len, NULL))
+    {
+      msg (M_WARN, "Register_dns: could not talk to service: %s [status=0x%lx]",
+           strerror_win32 (GetLastError (), &gc), GetLastError ());
+    }
+
+  else if (ack.error_number != NO_ERROR)
+    {
+      msg (M_WARN, "Register_dns failed using service: %s [status=0x%x]",
+           strerror_win32 (ack.error_number, &gc), ack.error_number);
+    }
+
+  else
+      msg (M_INFO, "Register_dns request sent to the service");
+
+  gc_free (&gc);
+}
+
 void
 fork_register_dns_action (struct tuntap *tt)
 {
-  if (tt && tt->options.register_dns)
+  if (tt && tt->options.register_dns && tt->options.msg_channel)
+    {
+       register_dns_service (tt);
+    }
+  else if (tt && tt->options.register_dns)
     {
       struct gc_arena gc = gc_new ();
       struct buffer cmd = alloc_buf_gc (256, &gc);
index d83ea656050d3f2ab89f056f8930173d2d0c7829..df30ad7ca9e399398233f0b07b2a480e1ddcd6ec 100644 (file)
@@ -50,6 +50,9 @@ static SERVICE_STATUS_HANDLE service;
 static SERVICE_STATUS status;
 static HANDLE exit_event = NULL;
 static settings_t settings;
+static HANDLE rdns_semaphore = NULL;
+#define RDNS_TIMEOUT 600  /* seconds to wait for the semaphore */
+
 
 openvpn_service_t interactive_service = {
   interactive,
@@ -803,6 +806,147 @@ HandleBlockDNSMessage (const block_dns_message_t *msg, undo_lists_t *lists)
   return err;
 }
 
+/*
+ * Execute a command and return its exit code. If timeout > 0, terminate
+ * the process if still running after timeout milliseconds. In that case
+ * the return value is the windows error code WAIT_TIMEOUT = 0x102
+ */
+static DWORD
+ExecCommand (const WCHAR *argv0, const WCHAR *cmdline, DWORD timeout)
+{
+  DWORD exit_code;
+  STARTUPINFOW si;
+  PROCESS_INFORMATION pi;
+  DWORD proc_flags = CREATE_NO_WINDOW|CREATE_UNICODE_ENVIRONMENT;
+  WCHAR *cmdline_dup = NULL;
+
+  ZeroMemory (&si, sizeof(si));
+  ZeroMemory (&pi, sizeof(pi));
+
+  si.cb = sizeof(si);
+
+  /* CreateProcess needs a modifiable cmdline: make a copy */
+  cmdline_dup = wcsdup (cmdline);
+  if ( cmdline_dup && CreateProcessW (argv0, cmdline_dup, NULL, NULL, FALSE,
+                                      proc_flags, NULL, NULL, &si, &pi) )
+    {
+      WaitForSingleObject (pi.hProcess, timeout ? timeout : INFINITE);
+      if (!GetExitCodeProcess (pi.hProcess, &exit_code))
+        {
+          MsgToEventLog (M_SYSERR, TEXT("ExecCommand: Error getting exit_code:"));
+          exit_code = GetLastError();
+        }
+      else if (exit_code == STILL_ACTIVE)
+        {
+          exit_code = WAIT_TIMEOUT;  /* Windows error code 0x102 */
+
+          /* kill without impunity */
+          TerminateProcess (pi.hProcess, exit_code);
+          MsgToEventLog (M_ERR, TEXT("ExecCommand: \"%s %s\" killed after timeout"),
+            argv0, cmdline);
+        }
+      else if (exit_code)
+          MsgToEventLog (M_ERR, TEXT("ExecCommand: \"%s %s\" exited with status = %lu"),
+                          argv0, cmdline, exit_code);
+      else
+          MsgToEventLog (M_INFO, TEXT("ExecCommand: \"%s %s\" completed"), argv0, cmdline);
+
+      CloseHandle(pi.hProcess);
+      CloseHandle(pi.hThread);
+    }
+  else
+    {
+      exit_code = GetLastError();
+      MsgToEventLog (M_SYSERR, TEXT("ExecCommand: could not run \"%s %s\" :"),
+                                      argv0, cmdline);
+    }
+
+  free (cmdline_dup);
+  return exit_code;
+}
+
+/*
+ * Entry point for register-dns thread.
+ */
+static DWORD WINAPI
+RegisterDNS (LPVOID unused)
+{
+  DWORD err;
+  DWORD i;
+  WCHAR sys_path[MAX_PATH];
+  DWORD timeout = RDNS_TIMEOUT * 1000; /* in milliseconds */
+
+  /* default paths of net and ipconfig commands */
+  WCHAR net[MAX_PATH]   = L"C:\\Windows\\system32\\net.exe";
+  WCHAR ipcfg[MAX_PATH] = L"C:\\Windows\\system32\\ipconfig.exe";
+
+  struct
+    {
+      WCHAR *argv0;
+      WCHAR *cmdline;
+      DWORD timeout;
+    } cmds [] = {
+                  { net,   L"net stop dnscache",     timeout },
+                  { net,   L"net start dnscache",    timeout },
+                  { ipcfg, L"ipconfig /flushdns",    timeout },
+                  { ipcfg, L"ipconfig /registerdns", timeout },
+                };
+  int ncmds = sizeof (cmds) / sizeof (cmds[0]);
+
+  HANDLE wait_handles[2] = {rdns_semaphore, exit_event};
+
+  if(GetSystemDirectory(sys_path, MAX_PATH))
+    {
+      _snwprintf (net, MAX_PATH, L"%s\\%s", sys_path, L"net.exe");
+      net[MAX_PATH-1] = L'\0';
+
+      _snwprintf (ipcfg, MAX_PATH, L"%s\\%s", sys_path, L"ipconfig.exe");
+      ipcfg[MAX_PATH-1] = L'\0';
+    }
+
+  if (WaitForMultipleObjects (2, wait_handles, FALSE, timeout) == WAIT_OBJECT_0)
+    {
+      /* Semaphore locked */
+      for (i = 0; i < ncmds; ++i)
+        {
+          ExecCommand (cmds[i].argv0, cmds[i].cmdline, cmds[i].timeout);
+        }
+      err = 0;
+      if ( !ReleaseSemaphore (rdns_semaphore, 1, NULL) )
+        err = MsgToEventLog (M_SYSERR, TEXT("RegisterDNS: Failed to release regsiter-dns semaphore:"));
+    }
+  else
+    {
+      MsgToEventLog (M_ERR, TEXT("RegisterDNS: Failed to lock register-dns semaphore"));
+      err = ERROR_SEM_TIMEOUT;  /* Windows error code 0x79 */
+    }
+  return err;
+}
+
+static DWORD
+HandleRegisterDNSMessage (void)
+{
+  DWORD err;
+  HANDLE thread = NULL;
+
+  /* Delegate this job to a sub-thread */
+  thread = CreateThread (NULL, 0, RegisterDNS, NULL, 0, NULL);
+
+  /*
+   * We don't add these thread handles to the undo list -- the thread and
+   * processes it spawns are all supposed to terminate or timeout by themselves.
+   */
+  if (thread)
+    {
+      err = 0;
+      CloseHandle (thread);
+    }
+  else
+    err = GetLastError();
+
+  return err;
+}
+
 static VOID
 HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_lists_t *lists)
 {
@@ -854,6 +998,10 @@ HandleMessage (HANDLE pipe, DWORD bytes, DWORD count, LPHANDLE events, undo_list
         ack.error_number = HandleBlockDNSMessage (&msg.block_dns, lists);
       break;
 
+    case msg_register_dns:
+        ack.error_number = HandleRegisterDNSMessage ();
+        break;
+
     default:
       ack.error_number = ERROR_MESSAGE_TYPE;
       MsgToEventLog (MSG_FLAGS_ERROR, TEXT("Unknown message type %d"), msg.header.type);
@@ -1381,6 +1529,13 @@ ServiceStartInteractive (DWORD dwArgc, LPTSTR *lpszArgv)
       goto out;
     }
 
+  rdns_semaphore = CreateSemaphoreW (NULL, 1, 1, NULL);
+  if (!rdns_semaphore)
+  {
+      error = MsgToEventLog (M_SYSERR, TEXT("Could not create semaphore for register-dns"));
+      goto out;
+  }
+
   error = UpdateWaitHandles (&handles, &handle_count, io_event, exit_event, threads);
   if (error != NO_ERROR)
     goto out;
@@ -1458,6 +1613,7 @@ out:
   FreeWaitHandles (handles);
   CloseHandleEx (&io_event);
   CloseHandleEx (&exit_event);
+  CloseHandleEx (&rdns_semaphore);
 
   status.dwCurrentState = SERVICE_STOPPED;
   status.dwWin32ExitCode = error;