./.ci/build.sh "$@"
# Which paths to ignore.
-paths='src/solaris src/mingw src/gcrypt'
+paths='src/solaris src/windows src/gcrypt'
case "$(uname -s)" in
Linux)
free(localhost);
fclose(f);
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
int unix_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(unix_fd < 0) {
}
void exit_control(void) {
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
unlink(unixsocketname);
io_del(&unix_socket);
close(unix_socket.fd);
#ifndef HAVE_GETTIMEOFDAY
int gettimeofday(struct timeval *tv, void *tz) {
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
uint64_t lt = (uint64_t)ft.dwLowDateTime | ((uint64_t)ft.dwHighDateTime << 32);
} while (0)
#endif
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
#define mkdir(a, b) mkdir(a)
#ifndef SHUT_RDWR
#define SHUT_RDWR SD_BOTH
#include "net.h"
struct timeval now;
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
#ifdef HAVE_SYS_EPOLL_H
static int epollset = 0;
#endif
static int io_compare(const io_t *a, const io_t *b) {
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
return a->fd - b->fd;
#else
}
io->fd = fd;
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
if(io->fd != -1) {
io->event = WSACreateEvent();
#endif
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
void io_add_event(io_t *io, io_cb_t cb, void *data, WSAEVENT event) {
io->event = event;
io_add(io, cb, data, -1, 0);
return;
}
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
#ifdef HAVE_SYS_EPOLL_H
epoll_ctl(epollset, EPOLL_CTL_DEL, io->fd, NULL);
}
io_set(io, 0);
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
if(io->fd != -1 && WSACloseEvent(io->event) == FALSE) {
abort();
};
}
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
// From Matz's Ruby
#ifndef NSIG
bool event_loop(void) {
running = true;
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
#ifdef HAVE_SYS_EPOLL_H
typedef struct io_t {
int fd;
int flags;
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
WSAEVENT event;
#endif
io_cb_t cb;
extern struct timeval now;
extern void io_add(io_t *io, io_cb_t cb, void *data, int fd, int flags);
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
extern void io_add_event(io_t *io, io_cb_t cb, void *data, WSAEVENT event);
#endif
extern void io_del(io_t *io);
splay_empty_tree(&config);
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
typedef int uid_t;
static uid_t getuid(void) {
}
}
}
-#endif // HAVE_MINGW
+#endif // HAVE_WINDOWS
static char *read_node_name(void) {
if(access(tinc_conf, R_OK) == 0) {
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
#define WINVER 0x0600
#define _WIN32_WINNT 0x0600
#define WIN32_LEAN_AND_MEAN
#define alloca(size) __builtin_alloca(size)
#endif
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
#ifdef HAVE_W32API_H
#include <w32api.h>
#endif
#include <process.h>
#include <direct.h>
#endif
-#endif // HAVE_MINGW
+#endif // HAVE_WINDOWS
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#undef STATUS
#endif
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
#define SLASH "\\"
#else
#define SLASH "/"
static long start;
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
void ifconfig_header(FILE *out) {
fprintf(out, "#!/bin/sh\n");
start = ftell(out);
return;
}
-#elif defined(HAVE_MINGW)
+#elif defined(HAVE_WINDOWS)
switch(address.type) {
case SUBNET_MAC:
}
}
-#elif defined(HAVE_MINGW)
+#elif defined(HAVE_WINDOWS)
if(*gateway_str) {
switch(subnet.type) {
char filename2[PATH_MAX];
snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up.invitation", confbase);
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
snprintf(filename2, sizeof(filename2), "%s" SLASH "tinc-up.bat", confbase);
#else
snprintf(filename2, sizeof(filename2), "%s" SLASH "tinc-up", confbase);
if(response == 'e') {
char *command;
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
const char *editor = getenv("VISUAL");
if(!editor) {
continue;
}
-#if HAVE_MINGW
+#if HAVE_WINDOWS
// If socket has been shut down, recv() on Windows returns -1 and sets sockerrno
// to WSAESHUTDOWN, while on UNIX-like operating systems recv() returns 0, so we
return false;
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
// We cannot atomically replace files on Windows.
char bakfile[PATH_MAX] = "";
snprintf(bakfile, sizeof(bakfile), "%s.bak", filename);
return false;
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
unlink(bakfile);
#endif
fprintf(stderr, "Warning: old key(s) found and disabled.\n");
return NULL;
}
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
struct stat s;
if(fstat(fileno(fp), &s)) {
return NULL;
}
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
struct stat s;
if(fstat(fileno(fp), &s)) {
static logmode_t logmode = LOGMODE_STDERR;
static pid_t logpid;
static FILE *logfile = NULL;
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
static HANDLE loghandle = NULL;
#endif
static const char *logident = NULL;
break;
case LOGMODE_SYSLOG:
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
{
const char *messages[] = {message};
ReportEvent(loghandle, priority, 0, 0, NULL, 1, 0, messages, NULL);
break;
case LOGMODE_SYSLOG:
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
loghandle = RegisterEventSource(NULL, logident);
if(!loghandle) {
break;
case LOGMODE_SYSLOG:
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
DeregisterEventSource(loghandle);
break;
#else
LOGMODE_SYSLOG
} logmode_t;
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
#define LOG_EMERG EVENTLOG_ERROR_TYPE
#define LOG_ALERT EVENTLOG_ERROR_TYPE
#define LOG_CRIT EVENTLOG_ERROR_TYPE
elif os_name == 'sunos'
subdir('solaris')
elif os_name == 'windows'
- subdir('mingw')
+ subdir('windows')
endif
foreach h : check_headers
endif
endif
-if opt_curses.auto() and os_name == 'windows'
- message('curses does not link under MinGW')
-else
+if not opt_curses.disabled()
# The meta-dependency covers more alternatives, but is only available in 0.54+
curses_name = meson_version.version_compare('>=0.54') ? 'curses' : 'ncurses'
dep_curses = dependency(curses_name, required: opt_curses, static: static)
# Some distributions do not supply pkg-config files for readline
if opt_readline.auto() and os_name == 'windows'
- message('readline does not link under MinGW')
+ message('readline not available on Windows')
else
dep_readline = dependency('readline', required: opt_readline, static: static)
if not dep_readline.found()
+++ /dev/null
-#ifndef TINC_MINGW_COMMON_H
-#define TINC_MINGW_COMMON_H
-
-/*
- * TAP-Win32 -- A kernel driver to provide virtual tap device functionality
- * on Windows. Originally derived from the CIPE-Win32
- * project by Damion K. Wilson, with extensive modifications by
- * James Yonan.
- *
- * All source code which derives from the CIPE-Win32 project is
- * Copyright (C) Damion K. Wilson, 2003, and is released under the
- * GPL version 2 (see below).
- *
- * All other source code is Copyright (C) James Yonan, 2003-2004,
- * and is released under the GPL version 2 (see below).
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-//===============================================
-// This file is included both by OpenVPN and
-// the TAP-Win32 driver and contains definitions
-// common to both.
-//===============================================
-
-//=============
-// TAP IOCTLs
-//=============
-
-#define TAP_CONTROL_CODE(request,method) \
- CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
-
-#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED)
-#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED)
-#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED)
-#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED)
-#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
-#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED)
-#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED)
-#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED)
-#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED)
-
-//=================
-// Registry keys
-//=================
-
-#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
-
-#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
-
-//======================
-// Filesystem prefixes
-//======================
-
-#define USERMODEDEVICEDIR "\\\\.\\Global\\"
-#define SYSDEVICEDIR "\\Device\\"
-#define USERDEVICEDIR "\\DosDevices\\Global\\"
-#define TAPSUFFIX ".tap"
-
-//=========================================================
-// TAP_COMPONENT_ID -- This string defines the TAP driver
-// type -- different component IDs can reside in the system
-// simultaneously.
-//=========================================================
-
-#define TAP_COMPONENT_ID "tap0801"
-
-#endif
+++ /dev/null
-/*
- device.c -- Interaction with Windows tap driver in a MinGW environment
- Copyright (C) 2002-2005 Ivo Timmermans,
- 2002-2022 Guus Sliepen <guus@tinc-vpn.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "../system.h"
-
-#include <windows.h>
-#include <winioctl.h>
-
-#include "../conf.h"
-#include "../device.h"
-#include "../logger.h"
-#include "../names.h"
-#include "../net.h"
-#include "../route.h"
-#include "../utils.h"
-#include "../xalloc.h"
-
-#include "common.h"
-
-int device_fd = -1;
-static HANDLE device_handle = INVALID_HANDLE_VALUE;
-static io_t device_read_io;
-static OVERLAPPED device_read_overlapped;
-static OVERLAPPED device_write_overlapped;
-static vpn_packet_t device_read_packet;
-static vpn_packet_t device_write_packet;
-char *device = NULL;
-char *iface = NULL;
-static const char *device_info = "Windows tap device";
-
-static void device_issue_read(void) {
- int status;
-
- for(;;) {
- ResetEvent(device_read_overlapped.hEvent);
-
- DWORD len;
- status = ReadFile(device_handle, (void *)device_read_packet.data, MTU, &len, &device_read_overlapped);
-
- if(!status) {
- if(GetLastError() != ERROR_IO_PENDING)
- logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
- device, strerror(errno));
-
- break;
- }
-
- device_read_packet.len = len;
- device_read_packet.priority = 0;
- route(myself, &device_read_packet);
- }
-}
-
-static void device_handle_read(void *data, int flags) {
- (void)data;
- (void)flags;
-
- DWORD len;
-
- if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, FALSE)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Error getting read result from %s %s: %s", device_info,
- device, strerror(errno));
-
- if(GetLastError() != ERROR_IO_INCOMPLETE) {
- /* Must reset event or it will keep firing. */
- ResetEvent(device_read_overlapped.hEvent);
- }
-
- return;
- }
-
- device_read_packet.len = len;
- device_read_packet.priority = 0;
- route(myself, &device_read_packet);
- device_issue_read();
-}
-
-static bool setup_device(void) {
- HKEY key, key2;
- int i;
-
- char regpath[1024];
- char adapterid[1024];
- char adaptername[1024];
- char tapname[1024];
- DWORD len;
-
- bool found = false;
-
- int err;
-
- get_config_string(lookup_config(&config_tree, "Device"), &device);
- get_config_string(lookup_config(&config_tree, "Interface"), &iface);
-
- if(device && iface) {
- logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: both Device and Interface specified, results may not be as expected");
- }
-
- /* Open registry and look for network adapters */
-
- if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
- return false;
- }
-
- for(i = 0; ; i++) {
- len = sizeof(adapterid);
-
- if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL)) {
- break;
- }
-
- /* Find out more about this adapter */
-
- snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
-
- if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2)) {
- continue;
- }
-
- len = sizeof(adaptername);
- err = RegQueryValueEx(key2, "Name", 0, 0, (LPBYTE)adaptername, &len);
-
- RegCloseKey(key2);
-
- if(err) {
- continue;
- }
-
- if(device) {
- if(!strcmp(device, adapterid)) {
- found = true;
- break;
- } else {
- continue;
- }
- }
-
- if(iface) {
- if(!strcmp(iface, adaptername)) {
- found = true;
- break;
- } else {
- continue;
- }
- }
-
- snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
- device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
-
- if(device_handle != INVALID_HANDLE_VALUE) {
- found = true;
- break;
- }
- }
-
- RegCloseKey(key);
-
- if(!found) {
- logger(DEBUG_ALWAYS, LOG_ERR, "No Windows tap device found!");
- return false;
- }
-
- if(!device) {
- device = xstrdup(adapterid);
- }
-
- if(!iface) {
- iface = xstrdup(adaptername);
- }
-
- /* Try to open the corresponding tap device */
-
- if(device_handle == INVALID_HANDLE_VALUE) {
- snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
- device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
- }
-
- if(device_handle == INVALID_HANDLE_VALUE) {
- logger(DEBUG_ALWAYS, LOG_ERR, "%s (%s) is not a usable Windows tap device: %s", device, iface, winerror(GetLastError()));
- return false;
- }
-
- /* Get version information from tap device */
-
- {
- ULONG info[3] = {0};
- DWORD len;
-
- if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_VERSION, &info, sizeof(info), &info, sizeof(info), &len, NULL)) {
- logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get version information from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
- } else {
- logger(DEBUG_ALWAYS, LOG_INFO, "TAP-Windows driver version: %lu.%lu%s", info[0], info[1], info[2] ? " (DEBUG)" : "");
-
- /* Warn if using >=9.21. This is because starting from 9.21, TAP-Win32 seems to use a different, less efficient write path. */
- if(info[0] == 9 && info[1] >= 21)
- logger(DEBUG_ALWAYS, LOG_WARNING,
- "You are using the newer (>= 9.0.0.21, NDIS6) series of TAP-Win32 drivers. "
- "Using these drivers with tinc is not recommended as it can result in poor performance. "
- "You might want to revert back to 9.0.0.9 instead.");
- }
- }
-
- /* Get MAC address from tap device */
-
- if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
- return false;
- }
-
- if(routing_mode == RMODE_ROUTER) {
- overwrite_mac = 1;
- }
-
- device_info = "Windows tap device";
-
- logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
-
- device_read_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- device_write_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
-
- return true;
-}
-
-static void enable_device(void) {
- logger(DEBUG_ALWAYS, LOG_INFO, "Enabling %s", device_info);
-
- ULONG status = 1;
- DWORD len;
- DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL);
-
- /* We don't use the write event directly, but GetOverlappedResult() does, internally. */
-
- io_add_event(&device_read_io, device_handle_read, NULL, device_read_overlapped.hEvent);
- device_issue_read();
-}
-
-static void disable_device(void) {
- logger(DEBUG_ALWAYS, LOG_INFO, "Disabling %s", device_info);
-
- io_del(&device_read_io);
-
- ULONG status = 0;
- DWORD len;
- DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL);
-
- /* Note that we don't try to cancel ongoing I/O here - we just stop listening.
- This is because some TAP-Win32 drivers don't seem to handle cancellation very well,
- especially when combined with other events such as the computer going to sleep - cases
- were observed where the GetOverlappedResult() would just block indefinitely and never
- return in that case. */
-}
-
-static void close_device(void) {
- CancelIo(device_handle);
-
- /* According to MSDN, CancelIo() does not necessarily wait for the operation to complete.
- To prevent race conditions, make sure the operation is complete
- before we close the event it's referencing. */
-
- DWORD len;
-
- if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s read to cancel: %s", device_info, device, winerror(GetLastError()));
- }
-
- if(device_write_packet.len > 0 && !GetOverlappedResult(device_handle, &device_write_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s write to cancel: %s", device_info, device, winerror(GetLastError()));
- }
-
- device_write_packet.len = 0;
-
- CloseHandle(device_read_overlapped.hEvent);
- CloseHandle(device_write_overlapped.hEvent);
-
- CloseHandle(device_handle);
- device_handle = INVALID_HANDLE_VALUE;
-
- free(device);
- device = NULL;
- free(iface);
- iface = NULL;
- device_info = NULL;
-}
-
-static bool read_packet(vpn_packet_t *packet) {
- (void)packet;
- return false;
-}
-
-static bool write_packet(vpn_packet_t *packet) {
- DWORD outlen;
-
- logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
- packet->len, device_info);
-
- if(device_write_packet.len > 0) {
- /* Make sure the previous write operation is finished before we start the next one;
- otherwise we end up with multiple write ops referencing the same OVERLAPPED structure,
- which according to MSDN is a no-no. */
-
- if(!GetOverlappedResult(device_handle, &device_write_overlapped, &outlen, FALSE)) {
- if(GetLastError() != ERROR_IO_INCOMPLETE) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Error completing previously queued write to %s %s: %s", device_info, device, winerror(GetLastError()));
- } else {
- logger(DEBUG_TRAFFIC, LOG_ERR, "Previous overlapped write to %s %s still in progress", device_info, device);
- // drop this packet
- return true;
- }
- }
- }
-
- /* Copy the packet, since the write operation might still be ongoing after we return. */
-
- memcpy(&device_write_packet, packet, sizeof(*packet));
-
- ResetEvent(device_write_overlapped.hEvent);
-
- if(WriteFile(device_handle, DATA(&device_write_packet), device_write_packet.len, &outlen, &device_write_overlapped)) {
- // Write was completed immediately.
- device_write_packet.len = 0;
- } else if(GetLastError() != ERROR_IO_PENDING) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
- device_write_packet.len = 0;
- return false;
- }
-
- return true;
-}
-
-const devops_t os_devops = {
- .setup = setup_device,
- .close = close_device,
- .read = read_packet,
- .write = write_packet,
- .enable = enable_device,
- .disable = disable_device,
-};
+++ /dev/null
-check_headers += 'w32api.h'
-
-win_common_libs = ['ws2_32', 'iphlpapi', 'threads']
-
-if opt_harden and cc_name != 'msvc'
- win_common_libs += 'ssp'
-endif
-
-foreach libname : win_common_libs
- dep = dependency(libname, required: false)
- if not dep.found()
- dep = cc.find_library(libname)
- endif
- deps_common += dep
-endforeach
-
-src_tincd += files('device.c')
-
-cdata.set('HAVE_MINGW', 1)
-
Set all files and paths according to netname
*/
void make_names(bool daemon) {
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
HKEY key;
char installdir[1024] = "";
DWORD len = sizeof(installdir);
identname = xstrdup("tinc");
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\tinc", 0, KEY_READ, &key)) {
if(!RegQueryValueEx(key, NULL, 0, 0, (LPBYTE)installdir, &len)) {
}
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
(void)daemon;
if(!logfilename) {
do_outgoing_connection(outgoing);
}
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
/* Clean up dead proxy processes */
while(waitpid(-1, NULL, WNOHANG) > 0);
}
}
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
static void sigterm_handler(void *data) {
logger(DEBUG_ALWAYS, LOG_NOTICE, "Got %s signal", strsignal(((signal_t *)data)->signum));
event_exit();
0, 0
});
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
signal_t sighup = {0};
signal_t sigterm = {0};
signal_t sigquit = {0};
return 1;
}
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
signal_del(&sighup);
signal_del(&sigterm);
signal_del(&sigquit);
extern void try_tx(struct node_t *n, bool mtu);
extern void tarpit(int fd);
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
#define closesocket(s) close(s)
#endif
listen_socket_t listen_socket[MAXSOCKETS];
int listen_sockets;
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
io_t unix_socket;
#endif
}
static void do_outgoing_pipe(connection_t *c, const char *command) {
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
int fd[2];
if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd)) {
c->allow_request = ID;
}
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
/*
accept a new UNIX socket connection
*/
#include "../crypto.h"
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
static int random_fd = -1;
#include "../crypto.h"
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
static int random_fd = -1;
#include "process.h"
#include "version.h"
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
#include "utils.h"
#endif
/* Some functions the less gifted operating systems might lack... */
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
static SC_HANDLE manager = NULL;
static SC_HANDLE service = NULL;
static SERVICE_STATUS status = {0};
bool detach(void) {
logmode_t logmode;
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
signal(SIGPIPE, SIG_IGN);
signal(SIGUSR1, SIG_IGN);
signal(SIGUSR2, SIG_IGN);
#endif
if(do_detach) {
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
if(daemon(1, 0)) {
logger(DEBUG_ALWAYS, LOG_ERR, "Couldn't detach from terminal: %s", strerror(errno));
extern bool detach(void);
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
#include "event.h"
extern io_t stop_io;
ptrdiff_t len = e - p;
#ifndef HAVE_UNSETENV
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
// Windows requires putenv("FOO=") to unset %FOO%
len++;
#endif
/* First check if there is a script */
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
if(!*scriptextension) {
const char *pathext = getenv("PATHEXT");
#include <getopt.h>
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
#include <pthread.h>
#endif
#include "utils.h"
#include "names.h"
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
#define closesocket(s) close(s)
#endif
fprintf(stderr, message, program_name);
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
int stdin_sock_fd = -1;
return -1;
}
-#endif // HAVE_MINGW
+#endif // HAVE_WINDOWS
int main(int argc, char *argv[]) {
program_name = argv[0];
#endif
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
static struct WSAData wsa_state;
if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
return 1;
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
if(!readonly) {
in = start_input_reader();
}
if(FD_ISSET(in, &fds)) {
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
ssize_t len = recv(in, buf, readsize, 0);
#else
ssize_t len = read(in, buf, readsize);
}
if(len == 0) {
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
shutdown(in, SD_SEND);
closesocket(in);
#endif
}
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
#else
return true;
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
static bool remove_service(void) {
SC_HANDLE manager = NULL;
SC_HANDLE service = NULL;
fclose(f);
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
if((pid == 0) || (kill(pid, 0) && (errno == ESRCH))) {
fprintf(stderr, "Could not find tincd running at pid %d\n", pid);
char *c;
char *slash = strrchr(program_name, '/');
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
if((c = strrchr(program_name, '\\')) > slash) {
slash = c;
char **nargv = xzalloc((optind + argc) * sizeof(*nargv));
char *arg0 = c;
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
/*
Windows has no real concept of an "argv array". A command line is just one string.
The CRT of the new process will decode the command line string to generate argv before calling main(), and (by convention)
nargv[nargc++] = argv[i];
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
int status = spawnvp(_P_WAIT, c, nargv);
free(nargv);
return 1;
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
return remove_service() ? EXIT_SUCCESS : EXIT_FAILURE;
#else
}
// Replace the configuration file with the new one
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
if(remove(filename)) {
fprintf(stderr, "Error replacing file %s: %s\n", filename, strerror(errno));
check_port(name);
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
char filename[PATH_MAX];
snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up", confbase);
}
char *command;
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
const char *editor = getenv("VISUAL");
if(!editor) {
return 0;
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
static struct WSAData wsa_state;
if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
#include <lz4.h>
#endif
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
#include <pwd.h>
#include <grp.h>
#include <time.h>
static bool do_mlock = false;
#endif
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
/* If nonzero, chroot to netdir after startup. */
static bool do_chroot = false;
{NULL, 0, NULL, 0}
};
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
static struct WSAData wsa_state;
int main2(int argc, char **argv);
#endif
" --pidfile=FILENAME Write PID and control socket cookie to FILENAME.\n"
" --bypass-security Disables meta protocol security, for debugging.\n"
" -o, --option[HOST.]KEY=VALUE Set global/host configuration value.\n"
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
" -R, --chroot chroot to NET dir at startup.\n"
" -U, --user=USER setuid to given USER at startup.\n"
#endif
list_insert_tail(&cmdline_conf, cfg);
break;
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
case 'R':
case 'U':
}
static bool drop_privs(void) {
-#ifndef HAVE_MINGW
+#ifndef HAVE_WINDOWS
uid_t uid = 0;
if(switchuser) {
return true;
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
# define setpriority(level) !SetPriorityClass(GetCurrentProcess(), (level))
static void stop_handler(void *data, int flags) {
return 1;
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {
logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "WSAStartup", winerror(GetLastError()));
#endif
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
io_add_event(&stop_io, stop_handler, NULL, WSACreateEvent());
if(stop_io.event == FALSE) {
return b64encode_tinc_internal(src, dst, length, base64_urlsafe);
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
const char *winerror(int err) {
static char buf[1024], *ptr;
extern size_t b64encode_tinc_urlsafe(const void *src, char *dst, size_t length);
extern size_t b64decode_tinc(const char *src, void *dst, size_t length);
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
extern const char *winerror(int);
#define strerror(x) ((x)>0?strerror(x):winerror(GetLastError()))
#define sockerrno WSAGetLastError()
--- /dev/null
+#ifndef TINC_WINDOWS_COMMON_H
+#define TINC_WINDOWS_COMMON_H
+
+/*
+ * TAP-Win32 -- A kernel driver to provide virtual tap device functionality
+ * on Windows. Originally derived from the CIPE-Win32
+ * project by Damion K. Wilson, with extensive modifications by
+ * James Yonan.
+ *
+ * All source code which derives from the CIPE-Win32 project is
+ * Copyright (C) Damion K. Wilson, 2003, and is released under the
+ * GPL version 2 (see below).
+ *
+ * All other source code is Copyright (C) James Yonan, 2003-2004,
+ * and is released under the GPL version 2 (see below).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+//===============================================
+// This file is included both by OpenVPN and
+// the TAP-Win32 driver and contains definitions
+// common to both.
+//===============================================
+
+//=============
+// TAP IOCTLs
+//=============
+
+#define TAP_CONTROL_CODE(request,method) \
+ CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
+
+#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE (1, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE (2, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE (3, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE (4, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
+#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE (6, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE (7, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE (8, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE (9, METHOD_BUFFERED)
+
+//=================
+// Registry keys
+//=================
+
+#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+//======================
+// Filesystem prefixes
+//======================
+
+#define USERMODEDEVICEDIR "\\\\.\\Global\\"
+#define SYSDEVICEDIR "\\Device\\"
+#define USERDEVICEDIR "\\DosDevices\\Global\\"
+#define TAPSUFFIX ".tap"
+
+//=========================================================
+// TAP_COMPONENT_ID -- This string defines the TAP driver
+// type -- different component IDs can reside in the system
+// simultaneously.
+//=========================================================
+
+#define TAP_COMPONENT_ID "tap0801"
+
+#endif
--- /dev/null
+/*
+ device.c -- Interaction with the TAP-Windows driver
+ Copyright (C) 2002-2005 Ivo Timmermans,
+ 2002-2022 Guus Sliepen <guus@tinc-vpn.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "../system.h"
+
+#include <windows.h>
+#include <winioctl.h>
+
+#include "../conf.h"
+#include "../device.h"
+#include "../logger.h"
+#include "../names.h"
+#include "../net.h"
+#include "../route.h"
+#include "../utils.h"
+#include "../xalloc.h"
+
+#include "common.h"
+
+int device_fd = -1;
+static HANDLE device_handle = INVALID_HANDLE_VALUE;
+static io_t device_read_io;
+static OVERLAPPED device_read_overlapped;
+static OVERLAPPED device_write_overlapped;
+static vpn_packet_t device_read_packet;
+static vpn_packet_t device_write_packet;
+char *device = NULL;
+char *iface = NULL;
+static const char *device_info = "Windows tap device";
+
+static void device_issue_read(void) {
+ int status;
+
+ for(;;) {
+ ResetEvent(device_read_overlapped.hEvent);
+
+ DWORD len;
+ status = ReadFile(device_handle, (void *)device_read_packet.data, MTU, &len, &device_read_overlapped);
+
+ if(!status) {
+ if(GetLastError() != ERROR_IO_PENDING)
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
+ device, strerror(errno));
+
+ break;
+ }
+
+ device_read_packet.len = len;
+ device_read_packet.priority = 0;
+ route(myself, &device_read_packet);
+ }
+}
+
+static void device_handle_read(void *data, int flags) {
+ (void)data;
+ (void)flags;
+
+ DWORD len;
+
+ if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, FALSE)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error getting read result from %s %s: %s", device_info,
+ device, strerror(errno));
+
+ if(GetLastError() != ERROR_IO_INCOMPLETE) {
+ /* Must reset event or it will keep firing. */
+ ResetEvent(device_read_overlapped.hEvent);
+ }
+
+ return;
+ }
+
+ device_read_packet.len = len;
+ device_read_packet.priority = 0;
+ route(myself, &device_read_packet);
+ device_issue_read();
+}
+
+static bool setup_device(void) {
+ HKEY key, key2;
+ int i;
+
+ char regpath[1024];
+ char adapterid[1024];
+ char adaptername[1024];
+ char tapname[1024];
+ DWORD len;
+
+ bool found = false;
+
+ int err;
+
+ get_config_string(lookup_config(&config_tree, "Device"), &device);
+ get_config_string(lookup_config(&config_tree, "Interface"), &iface);
+
+ if(device && iface) {
+ logger(DEBUG_ALWAYS, LOG_WARNING, "Warning: both Device and Interface specified, results may not be as expected");
+ }
+
+ /* Open registry and look for network adapters */
+
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &key)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Unable to read registry: %s", winerror(GetLastError()));
+ return false;
+ }
+
+ for(i = 0; ; i++) {
+ len = sizeof(adapterid);
+
+ if(RegEnumKeyEx(key, i, adapterid, &len, 0, 0, 0, NULL)) {
+ break;
+ }
+
+ /* Find out more about this adapter */
+
+ snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapterid);
+
+ if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &key2)) {
+ continue;
+ }
+
+ len = sizeof(adaptername);
+ err = RegQueryValueEx(key2, "Name", 0, 0, (LPBYTE)adaptername, &len);
+
+ RegCloseKey(key2);
+
+ if(err) {
+ continue;
+ }
+
+ if(device) {
+ if(!strcmp(device, adapterid)) {
+ found = true;
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ if(iface) {
+ if(!strcmp(iface, adaptername)) {
+ found = true;
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, adapterid);
+ device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
+
+ if(device_handle != INVALID_HANDLE_VALUE) {
+ found = true;
+ break;
+ }
+ }
+
+ RegCloseKey(key);
+
+ if(!found) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "No Windows tap device found!");
+ return false;
+ }
+
+ if(!device) {
+ device = xstrdup(adapterid);
+ }
+
+ if(!iface) {
+ iface = xstrdup(adaptername);
+ }
+
+ /* Try to open the corresponding tap device */
+
+ if(device_handle == INVALID_HANDLE_VALUE) {
+ snprintf(tapname, sizeof(tapname), USERMODEDEVICEDIR "%s" TAPSUFFIX, device);
+ device_handle = CreateFile(tapname, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
+ }
+
+ if(device_handle == INVALID_HANDLE_VALUE) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "%s (%s) is not a usable Windows tap device: %s", device, iface, winerror(GetLastError()));
+ return false;
+ }
+
+ /* Get version information from tap device */
+
+ {
+ ULONG info[3] = {0};
+ DWORD len;
+
+ if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_VERSION, &info, sizeof(info), &info, sizeof(info), &len, NULL)) {
+ logger(DEBUG_ALWAYS, LOG_WARNING, "Could not get version information from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
+ } else {
+ logger(DEBUG_ALWAYS, LOG_INFO, "TAP-Windows driver version: %lu.%lu%s", info[0], info[1], info[2] ? " (DEBUG)" : "");
+
+ /* Warn if using >=9.21. This is because starting from 9.21, TAP-Win32 seems to use a different, less efficient write path. */
+ if(info[0] == 9 && info[1] >= 21)
+ logger(DEBUG_ALWAYS, LOG_WARNING,
+ "You are using the newer (>= 9.0.0.21, NDIS6) series of TAP-Win32 drivers. "
+ "Using these drivers with tinc is not recommended as it can result in poor performance. "
+ "You might want to revert back to 9.0.0.9 instead.");
+ }
+ }
+
+ /* Get MAC address from tap device */
+
+ if(!DeviceIoControl(device_handle, TAP_IOCTL_GET_MAC, mymac.x, sizeof(mymac.x), mymac.x, sizeof(mymac.x), &len, 0)) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not get MAC address from Windows tap device %s (%s): %s", device, iface, winerror(GetLastError()));
+ return false;
+ }
+
+ if(routing_mode == RMODE_ROUTER) {
+ overwrite_mac = 1;
+ }
+
+ device_info = "Windows tap device";
+
+ logger(DEBUG_ALWAYS, LOG_INFO, "%s (%s) is a %s", device, iface, device_info);
+
+ device_read_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ device_write_overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ return true;
+}
+
+static void enable_device(void) {
+ logger(DEBUG_ALWAYS, LOG_INFO, "Enabling %s", device_info);
+
+ ULONG status = 1;
+ DWORD len;
+ DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL);
+
+ /* We don't use the write event directly, but GetOverlappedResult() does, internally. */
+
+ io_add_event(&device_read_io, device_handle_read, NULL, device_read_overlapped.hEvent);
+ device_issue_read();
+}
+
+static void disable_device(void) {
+ logger(DEBUG_ALWAYS, LOG_INFO, "Disabling %s", device_info);
+
+ io_del(&device_read_io);
+
+ ULONG status = 0;
+ DWORD len;
+ DeviceIoControl(device_handle, TAP_IOCTL_SET_MEDIA_STATUS, &status, sizeof(status), &status, sizeof(status), &len, NULL);
+
+ /* Note that we don't try to cancel ongoing I/O here - we just stop listening.
+ This is because some TAP-Win32 drivers don't seem to handle cancellation very well,
+ especially when combined with other events such as the computer going to sleep - cases
+ were observed where the GetOverlappedResult() would just block indefinitely and never
+ return in that case. */
+}
+
+static void close_device(void) {
+ CancelIo(device_handle);
+
+ /* According to MSDN, CancelIo() does not necessarily wait for the operation to complete.
+ To prevent race conditions, make sure the operation is complete
+ before we close the event it's referencing. */
+
+ DWORD len;
+
+ if(!GetOverlappedResult(device_handle, &device_read_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s read to cancel: %s", device_info, device, winerror(GetLastError()));
+ }
+
+ if(device_write_packet.len > 0 && !GetOverlappedResult(device_handle, &device_write_overlapped, &len, TRUE) && GetLastError() != ERROR_OPERATION_ABORTED) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Could not wait for %s %s write to cancel: %s", device_info, device, winerror(GetLastError()));
+ }
+
+ device_write_packet.len = 0;
+
+ CloseHandle(device_read_overlapped.hEvent);
+ CloseHandle(device_write_overlapped.hEvent);
+
+ CloseHandle(device_handle);
+ device_handle = INVALID_HANDLE_VALUE;
+
+ free(device);
+ device = NULL;
+ free(iface);
+ iface = NULL;
+ device_info = NULL;
+}
+
+static bool read_packet(vpn_packet_t *packet) {
+ (void)packet;
+ return false;
+}
+
+static bool write_packet(vpn_packet_t *packet) {
+ DWORD outlen;
+
+ logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
+ packet->len, device_info);
+
+ if(device_write_packet.len > 0) {
+ /* Make sure the previous write operation is finished before we start the next one;
+ otherwise we end up with multiple write ops referencing the same OVERLAPPED structure,
+ which according to MSDN is a no-no. */
+
+ if(!GetOverlappedResult(device_handle, &device_write_overlapped, &outlen, FALSE)) {
+ if(GetLastError() != ERROR_IO_INCOMPLETE) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error completing previously queued write to %s %s: %s", device_info, device, winerror(GetLastError()));
+ } else {
+ logger(DEBUG_TRAFFIC, LOG_ERR, "Previous overlapped write to %s %s still in progress", device_info, device);
+ // drop this packet
+ return true;
+ }
+ }
+ }
+
+ /* Copy the packet, since the write operation might still be ongoing after we return. */
+
+ memcpy(&device_write_packet, packet, sizeof(*packet));
+
+ ResetEvent(device_write_overlapped.hEvent);
+
+ if(WriteFile(device_handle, DATA(&device_write_packet), device_write_packet.len, &outlen, &device_write_overlapped)) {
+ // Write was completed immediately.
+ device_write_packet.len = 0;
+ } else if(GetLastError() != ERROR_IO_PENDING) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Error while writing to %s %s: %s", device_info, device, winerror(GetLastError()));
+ device_write_packet.len = 0;
+ return false;
+ }
+
+ return true;
+}
+
+const devops_t os_devops = {
+ .setup = setup_device,
+ .close = close_device,
+ .read = read_packet,
+ .write = write_packet,
+ .enable = enable_device,
+ .disable = disable_device,
+};
--- /dev/null
+check_headers += 'w32api.h'
+
+win_common_libs = ['ws2_32', 'iphlpapi', 'threads']
+
+if opt_harden and cc_name != 'msvc'
+ win_common_libs += 'ssp'
+endif
+
+foreach libname : win_common_libs
+ dep = dependency(libname, required: false)
+ if not dep.found()
+ dep = cc.find_library(libname)
+ endif
+ deps_common += dep
+endforeach
+
+src_tincd += files('device.c')
+
}
static inline int xvasprintf(char **strp, const char *fmt, va_list ap) {
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
char buf[1024];
int result = vsnprintf(buf, sizeof(buf), fmt, ap);
#include "../../src/system.h"
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
static const char *winerror(int err) {
static char buf[1024], *ptr;
protocol = "17.7";
}
-#ifdef HAVE_MINGW
+#ifdef HAVE_WINDOWS
static struct WSAData wsa_state;
if(WSAStartup(MAKEWORD(2, 2), &wsa_state)) {