Skip to content

Commit

Permalink
Add initial support for Windows
Browse files Browse the repository at this point in the history
- Support by TUN/TAP driver from OpenVPN
- Native Windows API without emulation
- Client up and down sample .bat scripts

Limitations:
    * Only support MinGW, not support Cygwin
      due to the TUN/TAP driver
    * Daemonize not working, can only run in
      the foreground
    * Scripts can only be written in batch file
      format, bash not supported
  • Loading branch information
linusyang committed Oct 9, 2014
1 parent a531768 commit 6d05242
Show file tree
Hide file tree
Showing 15 changed files with 707 additions and 7 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*.status
*.tar.*
*~
*.exe
.DS_Store
.deps
.dirstamp
Expand Down
8 changes: 8 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,18 @@ case "$host" in
AC_DEFINE([TARGET_FREEBSD], [1], [Are we running on FreeBSD?])
AC_CHECK_HEADER([net/if_tun.h],[],[AC_MSG_ERROR([FreeBSD net/if_tun.h not found.])],[])
;;
*-*-mingw*)
AC_DEFINE([TARGET_WIN32], [1], [Are we running on Windows?])
WIN32=yes
CPPFLAGS="${CPPFLAGS} -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=_WIN32_WINNT_WINXP"
LIBS="${LIBS} -lgdi32 -lws2_32"
AC_CHECK_HEADERS([windows.h winsock2.h ws2tcpip.h],[],[AC_MSG_ERROR([Windows headers not found.])],[])
;;
*)
AC_DEFINE([TARGET_UNKNOWN], [1], [Unknown platform ?])
esac

AM_CONDITIONAL([WIN32], [test "${WIN32}" = "yes"])
AC_CHECK_HEADER([android/log.h],[CFLAGS="$CFLAGS -DHAVE_ANDROID_LOG"; LDFLAGS="$LDFLAGS -llog"],[],[])

# Checks for typedefs, structures, and compiler characteristics.
Expand Down
35 changes: 35 additions & 0 deletions samples/windows/client.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# ShadowVPN config example for windows

# notice: do not put space before or after "="

# server listen address
server=127.0.0.1

# server listen port
port=1123

# password to use
password=my_password

# server or client
mode=client

# local tunnel ip address (required)
tunip=10.7.0.2

# the MTU of VPN device
# 1492(Ethernet) - 20(IPv4, or 40 for IPv6) - 8(UDP) - 24(ShadowVPN)
mtu=1440

# tun/tap interface name
intf=Local Area Connection 2

# the script to run after VPN is created
# use this script to set up routes, NAT, etc
# configuration in this file will be set as environment variables
up=client_up.bat

# the script to run before stopping VPN
# use this script to restore routes, NAT, etc
# configuration in this file will be set as environment variables
down=client_down.bat
25 changes: 25 additions & 0 deletions samples/windows/client_down.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@ECHO off
REM example client down script for windows
REM will be executed when client is down

REM all key value pairs in ShadowVPN config file will be passed to this script
REM as environment variables, except password

REM user-defined variables
SET remote_tun_ip=10.7.0.1
SET orig_intf="Local Area Connection"

REM revert ip settings
netsh interface ip set interface %orig_intf% ignoredefaultroutes=disabled > NUL
netsh interface ip set address name="%intf%" dhcp > NUL

REM revert routing table
ECHO reverting default route
route delete 0.0.0.0 mask 128.0.0.0 %remote_tun_ip% > NUL
route delete 128.0.0.0 mask 128.0.0.0 %remote_tun_ip% > NUL
route delete %server% > NUL

REM revert dns server
netsh interface ip set dns name="%intf%" source=dhcp > NUL

ECHO %0 done
31 changes: 31 additions & 0 deletions samples/windows/client_up.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@ECHO off
REM example client up script for windows
REM will be executed when client is up

REM all key value pairs in ShadowVPN config file will be passed to this script
REM as environment variables, except password

REM user-defined variables
SET remote_tun_ip=10.7.0.1
SET dns_server=8.8.8.8
SET orig_intf="Local Area Connection"

REM exclude remote server in routing table
for /F "tokens=3" %%* in ('route print ^| findstr "\<0.0.0.0\>"') do set "orig_gw=%%*"
route add %server% %orig_gw% metric 5 > NUL

REM configure IP address and MTU of VPN interface
netsh interface ip set interface %orig_intf% ignoredefaultroutes=enabled > NUL
netsh interface ip set address name="%intf%" static %tunip% 255.255.255.0 > NUL
netsh interface ipv4 set subinterface "%intf%" mtu=%mtu% > NUL

REM change routing table
ECHO changing default route
netsh interface ipv4 add route 128.0.0.0/1 "%intf%" %remote_tun_ip% metric=6 > NUL
netsh interface ipv4 add route 0.0.0.0/1 "%intf%" %remote_tun_ip% metric=6 > NUL
ECHO default route changed to %remote_tun_ip%

REM change dns server
netsh interface ip set dns name="%intf%" static %dns_server% > NUL

ECHO %0 done
6 changes: 5 additions & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@ libshadowvpn_la_SOURCES = log.c \
daemon.h \
daemon.c \
shadowvpn.h

if WIN32
libshadowvpn_la_SOURCES += win32.c win32.h
endif

libshadowvpn_la_LIBADD = ../libsodium/src/libsodium/libsodium.la

shadowvpn_SOURCES = main.c

shadowvpn_LDADD = libshadowvpn.la

18 changes: 16 additions & 2 deletions src/args.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ static int parse_config_file(shadowvpn_args_t *args, const char *filename) {
char *sp_pos;
lineno++;
sp_pos = strchr(line, '\r');
if (sp_pos) *sp_pos = 0;
if (sp_pos) *sp_pos = '\n';
sp_pos = strchr(line, '\n');
if (sp_pos) {
*sp_pos = 0;
Expand Down Expand Up @@ -108,6 +108,12 @@ static int parse_config_file(shadowvpn_args_t *args, const char *filename) {
errf("password not set in config file");
return -1;
}
#ifdef TARGET_WIN32
if (!args_tun_ip) {
errf("tunip not set in config file");
return -1;
}
#endif
return 0;
}

Expand Down Expand Up @@ -159,7 +165,15 @@ static int process_key_value(shadowvpn_args_t *args, const char *key,
args->up_script = strdup(value);
} else if (strcmp("down", key) == 0) {
args->down_script = strdup(value);
} else {
}
#ifdef TARGET_WIN32
else if (strcmp("tunip", key) == 0) {
args_tun_ip = strdup(value);
} else if (strcmp("tunmask", key) == 0) {
args_tun_mask = (int) atol(value);
}
#endif
else {
errf("warning: unknown key in config file: %s", key);
}
return 0;
Expand Down
19 changes: 18 additions & 1 deletion src/daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,29 @@
*/

#include "shadowvpn.h"

#ifdef TARGET_WIN32

int daemon_start(const shadowvpn_args_t *args) {
printf("started\n");
return 0;
}

int daemon_stop(const shadowvpn_args_t *args) {
printf("stopped\n");
return 0;
}

#else

#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "shadowvpn.h"

#define PID_BUF_SIZE 32

Expand Down Expand Up @@ -180,3 +195,5 @@ int daemon_stop(const shadowvpn_args_t *args) {
}
return 0;
}

#endif
14 changes: 13 additions & 1 deletion src/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*/

#include <time.h>
#include "log.h"
#include "shadowvpn.h"

int verbose_mode;

Expand All @@ -39,6 +39,18 @@ void log_timestamp(FILE *out) {
void perror_timestamp(const char *msg, const char *file, int line) {
log_timestamp(stderr);
fprintf(stderr, "%s:%d ", file, line);
#ifdef TARGET_WIN32
LPVOID *err_str = NULL;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &err_str, 0, NULL);
if (err_str != NULL) {
fprintf(stderr, "%s: %s\n", msg, (char *)err_str);
LocalFree(err_str);
}
#else
perror(msg);
#endif
}

16 changes: 16 additions & 0 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,21 @@

static vpn_ctx_t vpn_ctx;

#ifdef TARGET_WIN32
BOOL WINAPI sig_handler(DWORD signo)
{
if (signo == CTRL_C_EVENT)
vpn_stop(&vpn_ctx);
return TRUE;
}
#else
static void sig_handler(int signo) {
if (signo == SIGINT)
exit(1); // for gprof
else
vpn_stop(&vpn_ctx);
}
#endif

int main(int argc, char **argv) {
shadowvpn_args_t args;
Expand Down Expand Up @@ -78,8 +87,15 @@ int main(int argc, char **argv) {
return EXIT_FAILURE;
}

#ifdef TARGET_WIN32
if (0 == SetConsoleCtrlHandler((PHANDLER_ROUTINE) sig_handler, TRUE)) {
errf("can not set control handler");
return EXIT_FAILURE;
}
#else
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
#endif

if (-1 == vpn_ctx_init(&vpn_ctx, &args)) {
return EXIT_FAILURE;
Expand Down
4 changes: 4 additions & 0 deletions src/shell.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ static int shell_run(shadowvpn_args_t *args, int is_up) {
return 0;
}
buf = malloc(strlen(script) + 8);
#ifdef TARGET_WIN32
sprintf(buf, "cmd /c %s", script);
#else
sprintf(buf, "sh %s", script);
#endif
logf("executing %s", script);
if (0 != (r = system(buf))) {
free(buf);
Expand Down
Loading

0 comments on commit 6d05242

Please sign in to comment.