From 01a65b13e3f0bb928f8e00785e11dcff35ecba93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Friedrich=20Scho=CC=88ller?= Date: Mon, 13 May 2013 23:31:27 +0200 Subject: [PATCH] Added cygwin support (client only) --- osflags | 7 +- src/main.cpp | 4 + src/tun.cpp | 30 ++-- src/tun_dev.h | 53 ++++--- src/tun_dev_cygwin.c | 325 +++++++++++++++++++++++++++++++++++++++++++ src/worker.cpp | 2 + 6 files changed, 393 insertions(+), 28 deletions(-) create mode 100644 src/tun_dev_cygwin.c diff --git a/osflags b/osflags index 80b117b..386e444 100644 --- a/osflags +++ b/osflags @@ -20,6 +20,9 @@ c) LINUX) echo $FLAGS -DHAVE_LINUX_IF_TUN_H ;; + CYGWIN*) + echo $FLAGS -DWIN32 + ;; *) echo $FLAGS ;; @@ -43,10 +46,12 @@ dev) echo src/tun_dev_generic.c fi ;; + CYGWIN*) + echo src/tun_dev_cygwin.c + ;; *) echo src/tun_dev_generic.c ;; esac ;; esac - diff --git a/src/main.cpp b/src/main.cpp index 2c74cdf..7fd383c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -170,6 +170,10 @@ int main(int argc, char *argv[]) if (userName != NULL) { +#ifdef WIN32 + syslog(LOG_ERR, "dropping privileges is not supported on Windows"); + return 1; +#endif passwd *pw = getpwnam(userName); if (pw != NULL) diff --git a/src/tun.cpp b/src/tun.cpp index f42ea80..f66b0d1 100644 --- a/src/tun.cpp +++ b/src/tun.cpp @@ -36,6 +36,8 @@ using namespace std; Tun::Tun(const char *device, int mtu) { + char cmdline[512]; + this->mtu = mtu; if (device != NULL) @@ -52,10 +54,15 @@ Tun::Tun(const char *device, int mtu) syslog(LOG_INFO, "opened tunnel device: %s", this->device); - char cmdline[512]; +#ifdef WIN32 + snprintf(cmdline, sizeof(cmdline), "netsh interface ipv4 set subinterface \"%s\" mtu=%d", this->device, mtu); + if (system(cmdline) != 0) + syslog(LOG_ERR, "could not set tun device mtu"); +#else snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s mtu %u", this->device, mtu); if (system(cmdline) != 0) syslog(LOG_ERR, "could not set tun device mtu"); +#endif } Tun::~Tun() @@ -69,16 +76,23 @@ void Tun::setIp(uint32_t ip, uint32_t destIp, bool includeSubnet) string ips = Utility::formatIp(ip); string destIps = Utility::formatIp(destIp); -#ifdef LINUX - snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s %s netmask 255.255.255.0", device, ips.c_str()); -#else - snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s %s %s netmask 255.255.255.255", device, ips.c_str(), destIps.c_str()); -#endif - +#ifdef WIN32 + snprintf(cmdline, sizeof(cmdline), "netsh interface ip set address name=\"%s\" " + "static %s 255.255.255.0", device, ips.c_str()); + if (system(cmdline) != 0) + syslog(LOG_ERR, "could not set tun device ip address"); + + if (!tun_set_ip(fd, ip, ip & 0xffffff00, 0xffffff00)) + syslog(LOG_ERR, "could not set tun device driver ip address: %s", tun_last_error()); +#elif LINUX + snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s %s netmask 255.255.255.0", device, ips.c_str()); + if (system(cmdline) != 0) + syslog(LOG_ERR, "could not set tun device ip address"); +#else + snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s %s %s netmask 255.255.255.255", device, ips.c_str(), destIps.c_str()); if (system(cmdline) != 0) syslog(LOG_ERR, "could not set tun device ip address"); -#ifndef LINUX if (includeSubnet) { snprintf(cmdline, sizeof(cmdline), "/sbin/route add %s/24 %s", destIps.c_str(), destIps.c_str()); diff --git a/src/tun_dev.h b/src/tun_dev.h index c5eac51..c396ee5 100644 --- a/src/tun_dev.h +++ b/src/tun_dev.h @@ -1,28 +1,43 @@ -/* - VTun - Virtual Tunnel over TCP/IP network. - - Copyright (C) 1998-2000 Maxim Krasnyansky - - VTun has been derived from VPPP package by Maxim Krasnyansky. - - 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. +/* + * Hans - IP over ICMP + * Copyright (C) 2013 Friedrich Schöller + * 1998-2000 Maxim Krasnyansky + * + * 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 3 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, see . + * */ -#define VTUN_DEV_LEN 20 +#include +#include + +#define VTUN_DEV_LEN 100 + +#ifdef __cplusplus +extern "C" { +#endif -extern "C" -{ int tun_open(char *dev); int tun_close(int fd, char *dev); int tun_write(int fd, char *buf, int len); int tun_read(int fd, char *buf, int len); + const char *tun_last_error(); + +#ifdef WIN32 + bool tun_set_ip(int fd, uint32_t local, uint32_t network, uint32_t netmask); +#endif + +#ifdef __cplusplus } +#endif diff --git a/src/tun_dev_cygwin.c b/src/tun_dev_cygwin.c new file mode 100644 index 0000000..9ff6330 --- /dev/null +++ b/src/tun_dev_cygwin.c @@ -0,0 +1,325 @@ +/* + * Hans - IP over ICMP + * Copyright (C) 2013 Friedrich Schöller + * 2002-2005 Ivo Timmermans, + * 2002-2011 Guus Sliepen + * + * 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 3 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, see . + * + */ + +#include "tun_dev.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TAP_WIN_CONTROL_CODE(request,method) CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) +#define TAP_WIN_IOCTL_GET_MAC TAP_WIN_CONTROL_CODE (1, METHOD_BUFFERED) +#define TAP_WIN_IOCTL_GET_VERSION TAP_WIN_CONTROL_CODE (2, METHOD_BUFFERED) +#define TAP_WIN_IOCTL_GET_MTU TAP_WIN_CONTROL_CODE (3, METHOD_BUFFERED) +#define TAP_WIN_IOCTL_GET_INFO TAP_WIN_CONTROL_CODE (4, METHOD_BUFFERED) +#define TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT TAP_WIN_CONTROL_CODE (5, METHOD_BUFFERED) +#define TAP_WIN_IOCTL_SET_MEDIA_STATUS TAP_WIN_CONTROL_CODE (6, METHOD_BUFFERED) +#define TAP_WIN_IOCTL_CONFIG_DHCP_MASQ TAP_WIN_CONTROL_CODE (7, METHOD_BUFFERED) +#define TAP_WIN_IOCTL_GET_LOG_LINE TAP_WIN_CONTROL_CODE (8, METHOD_BUFFERED) +#define TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT TAP_WIN_CONTROL_CODE (9, METHOD_BUFFERED) +#define TAP_WIN_IOCTL_CONFIG_TUN TAP_WIN_CONTROL_CODE (10, METHOD_BUFFERED) +#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}" +#define USERMODEDEVICEDIR "\\\\.\\Global\\" +#define SYSDEVICEDIR "\\Device\\" +#define USERDEVICEDIR "\\DosDevices\\Global\\" +#define TAP_WIN_SUFFIX ".tap" + +struct adapter_info +{ + int reader_read_fd, reader_write_fd; + HANDLE reader_thread; + HANDLE adapter_handle; +}; + +#define ERROR_BUFFER_SIZE 1024 + +char error_buffer[ERROR_BUFFER_SIZE]; + +static void error(char *format, ...) +{ + va_list vl; + va_start(vl, format); + vsnprintf(error_buffer, ERROR_BUFFER_SIZE, format, vl); + va_end(vl); +} + +static void noerror(void) +{ + *error_buffer = 0; +} + +static const char *winerror(int err) +{ + static char buf[1024], *ptr; + + ptr = buf + sprintf(buf, "(%d) ", err); + + if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), ptr, sizeof(buf) - (ptr - buf), NULL)) { + strcpy(ptr, "(unable to format errormessage)"); + }; + + if((ptr = strchr(buf, '\r'))) + *ptr = '\0'; + + return buf; +} + +static struct adapter_info *get_adapter_info_from_fd(int fd) +{ + static struct adapter_info single_adapter_info = { + .reader_read_fd = -1, + .reader_write_fd = -1, + .reader_thread = INVALID_HANDLE_VALUE, + .adapter_handle = INVALID_HANDLE_VALUE + }; + return &single_adapter_info; +} + +static HANDLE open_tap_adapter(char *name) +{ + HKEY connections_key, adapter_key; + int adapter_index, error_code; + char regpath[1024]; + char adapter_id[VTUN_DEV_LEN]; + char adapter_path[1024]; + char adapter_name[1024]; + HANDLE adapter_handle = INVALID_HANDLE_VALUE; + DWORD len; + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &connections_key) != ERROR_SUCCESS) + { + error("opening registry: %s", winerror(GetLastError())); + return INVALID_HANDLE_VALUE; + } + + for (adapter_index = 0; ; adapter_index++) + { + len = sizeof(adapter_id); + if (RegEnumKeyEx(connections_key, adapter_index, adapter_id, &len, 0, 0, 0, NULL) != ERROR_SUCCESS) + break; + + snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapter_id); + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &adapter_key) != ERROR_SUCCESS) + continue; + len = sizeof(adapter_name); + if (RegQueryValueEx(adapter_key, "Name", 0, 0, adapter_name, &len) != ERROR_SUCCESS) + { + RegCloseKey(adapter_key); + continue; + } + RegCloseKey(adapter_key); + + if (name && name[0] && strcmp(name, adapter_name) && strcmp(name, adapter_id)) + continue; + + snprintf(adapter_path, sizeof(adapter_path), USERMODEDEVICEDIR "%s" TAP_WIN_SUFFIX, adapter_id); + + adapter_handle = CreateFile(adapter_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0); + if (adapter_handle != INVALID_HANDLE_VALUE) + break; + } + + RegCloseKey(connections_key); + + if (adapter_handle == INVALID_HANDLE_VALUE) + { + error("could not open tap adapter"); + return INVALID_HANDLE_VALUE; + } + + strncpy(name, adapter_name, VTUN_DEV_LEN); + + noerror(); + return adapter_handle; +} + +static __stdcall DWORD reader_thread(LPVOID ptr) +{ + struct adapter_info *adapter_info = ptr; + char buf[0xffff]; // TODO: MTU + OVERLAPPED overlapped; + DWORD len; + int wait_result; + + memset(&overlapped, 0, sizeof(overlapped)); + overlapped.hEvent = CreateEvent(NULL, true, false, NULL); + + while (true) + { + if (!ReadFile(adapter_info->adapter_handle, buf, sizeof(buf), &len, &overlapped)) + { + if (GetLastError() != ERROR_IO_PENDING) + { + syslog(LOG_ERR, "error reading tap: %s", winerror(GetLastError())); + return 1; + } + + wait_result = WaitForSingleObjectEx(overlapped.hEvent, INFINITE, true); // or false? + + if (wait_result == WAIT_IO_COMPLETION) + { + printf("hui?\n"); + continue; + } + + if (wait_result != WAIT_OBJECT_0) + { + syslog(LOG_ERR, "error waiting for tap adapter: %s", winerror(GetLastError())); + return 1; + } + + if (!GetOverlappedResult(adapter_info->adapter_handle, &overlapped, &len, true)) + { + syslog(LOG_ERR, "error waiting for operation to complete: %s", winerror(GetLastError())); + return 1; + } + } + + // printf("success: %d\n", len); + write(adapter_info->reader_write_fd, buf, len); + } +} + +int tun_open(char *dev) +{ + struct adapter_info *adapter_info; + int socket_pair[2]; + + if (socketpair(AF_UNIX, SOCK_DGRAM, PF_UNIX, socket_pair)) + { + error("creating socket pair: %s", strerror(errno)); + return -1; + } + + adapter_info = get_adapter_info_from_fd(socket_pair[0]); + adapter_info->reader_read_fd = socket_pair[0]; + adapter_info->reader_write_fd = socket_pair[1]; + + adapter_info->adapter_handle = open_tap_adapter(dev); + if (adapter_info->adapter_handle == INVALID_HANDLE_VALUE) + { + tun_close(adapter_info->reader_read_fd, NULL); + return -1; + } + + adapter_info->reader_thread = CreateThread(NULL, 0, reader_thread, adapter_info, 0, NULL); + if (adapter_info->reader_thread == INVALID_HANDLE_VALUE) + { + error("reader thread creation: %s", winerror(GetLastError())); + tun_close(adapter_info->reader_read_fd, NULL); + return -1; + } + + return adapter_info->reader_read_fd; +} + +int tun_close(int fd, char *dev) +{ + struct adapter_info *adapter_info = get_adapter_info_from_fd(fd); + + if (adapter_info->reader_thread != INVALID_HANDLE_VALUE) + { + TerminateThread(adapter_info->reader_thread, 0); + adapter_info->reader_thread = INVALID_HANDLE_VALUE; + } + + close(adapter_info->reader_read_fd); + adapter_info->reader_read_fd = -1; + + close(adapter_info->reader_write_fd); + adapter_info->reader_write_fd = -1; + + if (adapter_info->adapter_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(adapter_info->adapter_handle); + adapter_info->adapter_handle = INVALID_HANDLE_VALUE; + } + + return 0; +} + +int tun_write(int fd, char *buf, int len) +{ + struct adapter_info *adapter_info = get_adapter_info_from_fd(fd); + OVERLAPPED overlapped; + DWORD written; + + memset(&overlapped, 0, sizeof(overlapped)); + + if (!WriteFile(adapter_info->adapter_handle, buf, len, &written, &overlapped)) + { + error("tap write: %s", winerror(GetLastError())); + return -1; + } + + return written; +} + +int tun_read(int fd, char *buf, int len) +{ + len = read(fd, buf, len); + if (len == -1) + error("reader read: %s", strerror(errno)); + return len; +} + +const char *tun_last_error() +{ + return error_buffer; +} + +bool tun_set_ip(int fd, uint32_t local, uint32_t network, uint32_t netmask) +{ + struct adapter_info *adapter_info = get_adapter_info_from_fd(fd); + uint32_t addresses[3]; + DWORD status; + DWORD len; + + addresses[0] = htonl(local); + addresses[1] = htonl(network); + addresses[2] = htonl(netmask); + + if (!DeviceIoControl(adapter_info->adapter_handle, TAP_WIN_IOCTL_CONFIG_TUN, + &addresses, sizeof(addresses), &addresses, sizeof(addresses), &len, NULL)) + { + error("configuring tap addresses", winerror(GetLastError())); + return false; + } + + status = true; + if (!DeviceIoControl(adapter_info->adapter_handle, TAP_WIN_IOCTL_SET_MEDIA_STATUS, + &status, sizeof(status), &status, sizeof(status), &len, NULL)) + { + error("enabling tap device: %s", winerror(GetLastError())); + return false; + } + + noerror(); + return true; +} diff --git a/src/worker.cpp b/src/worker.cpp index c9c770d..eed6a63 100644 --- a/src/worker.cpp +++ b/src/worker.cpp @@ -185,6 +185,7 @@ void Worker::run() void Worker::dropPrivileges() { +#ifndef WIN32 if (uid <= 0 || privilegesDropped) return; @@ -197,4 +198,5 @@ void Worker::dropPrivileges() throw Exception("setuid", true); privilegesDropped = true; +#endif }