mirror of
https://github.com/OpenVPN/openvpn.git
synced 2026-05-28 04:03:29 -04:00
Add support for register-dns through interactive service
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>
This commit is contained in:
parent
e3420d5683
commit
3e42a55810
3 changed files with 192 additions and 2 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue