Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Finally add tun handling for Windows
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
David Woodhouse authored and David Woodhouse committed Feb 11, 2014
1 parent 1edb49e commit a12ac9e
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 11 deletions.
5 changes: 4 additions & 1 deletion library.c
Expand Up @@ -47,7 +47,10 @@ struct openconnect_info *openconnect_vpninfo_new(char *useragent,
if (!vpninfo)
return NULL;

vpninfo->tun_fd = vpninfo->ssl_fd = vpninfo->dtls_fd = -1;
#ifndef _WIN32
vpninfo->tun_fd = -1;
#endif
vpninfo->ssl_fd = vpninfo->dtls_fd = -1;
vpninfo->cmd_fd = vpninfo->cmd_fd_write = -1;
vpninfo->cert_expire_warning = 60 * 86400;
vpninfo->deflate = 1;
Expand Down
3 changes: 3 additions & 0 deletions mainloop.c
Expand Up @@ -137,6 +137,9 @@ int openconnect_mainloop(struct openconnect_info *vpninfo,
WSAEventSelect(vpninfo->cmd_fd, vpninfo->cmd_event, vpninfo->cmd_monitored);
events[nr_events++] = vpninfo->cmd_event;
}
if (vpninfo->tun_monitored) {
events[nr_events++] = vpninfo->tun_rd_overlap.hEvent;
}
if (WaitForMultipleObjects(nr_events, events, FALSE, timeout) == WAIT_FAILED) {
vpn_progress(vpninfo, PRG_ERR,
_("WaitForMultipleObjects failed: %lx\n"),
Expand Down
6 changes: 4 additions & 2 deletions openconnect-internal.h
Expand Up @@ -274,7 +274,7 @@ struct openconnect_info {

#ifdef _WIN32
long dtls_monitored, ssl_monitored, cmd_monitored, tun_monitored;
HANDLE dtls_event, ssl_event, cmd_event, tun_event;
HANDLE dtls_event, ssl_event, cmd_event;
#else
int _select_nfds;
fd_set _select_rfds;
Expand All @@ -288,8 +288,10 @@ struct openconnect_info {
#endif
#ifdef _WIN32
HANDLE tun_fh;
#endif
OVERLAPPED tun_rd_overlap, tun_wr_overlap;
#else
int tun_fd;
#endif
int ssl_fd;
int dtls_fd;

Expand Down
7 changes: 6 additions & 1 deletion tun-win32.c
Expand Up @@ -124,7 +124,9 @@ static int open_tun(struct openconnect_info *vpninfo, char *guid, char *name)

snprintf(devname, sizeof(devname), DEVTEMPLATE, guid);
tun_fh = CreateFile(devname, GENERIC_WRITE|GENERIC_READ, 0, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
OPEN_EXISTING,
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
0);
if (tun_fh == INVALID_HANDLE_VALUE) {
vpn_progress(vpninfo, PRG_ERR, _("Failed to open %s\n"),
devname);
Expand Down Expand Up @@ -176,6 +178,9 @@ static int open_tun(struct openconnect_info *vpninfo, char *guid, char *name)
vpninfo->ifname = strdup(name);

vpninfo->tun_fh = tun_fh;
vpninfo->tun_rd_overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
monitor_read_fd(vpninfo, tun);

return 1;
}

Expand Down
75 changes: 70 additions & 5 deletions tun.c
Expand Up @@ -635,6 +635,12 @@ static int os_setup_tun(struct openconnect_info *vpninfo)
return tun_fd;
}

#ifdef _WIN32
int openconnect_setup_tun_fd(struct openconnect_info *vpninfo, int tun_fd)
{
return 0;
}
#else
int openconnect_setup_tun_fd(struct openconnect_info *vpninfo, int tun_fd)
{
set_fd_cloexec(tun_fd);
Expand All @@ -652,7 +658,6 @@ int openconnect_setup_tun_fd(struct openconnect_info *vpninfo, int tun_fd)
return 0;
}

#ifndef _WIN32
int openconnect_setup_tun_script(struct openconnect_info *vpninfo, char *tun_script)
{
pid_t child;
Expand Down Expand Up @@ -716,6 +721,9 @@ int tun_mainloop(struct openconnect_info *vpninfo, int *timeout)
{
int work_done = 0;
int prefix_size = 0;
#ifdef _WIN32
DWORD pkt_size = 0;
#endif

#ifdef TUN_HAS_AF_PREFIX
if (!vpninfo->script_tun)
Expand All @@ -732,9 +740,34 @@ int tun_mainloop(struct openconnect_info *vpninfo, int *timeout)
vpn_progress(vpninfo, PRG_ERR, "Allocation failed\n");
break;
}
#ifdef _WIN32
if (!ReadFile(vpninfo->tun_fh, out_pkt->data, len, &pkt_size, &vpninfo->tun_rd_overlap)) {
DWORD err = GetLastError();
if (err != ERROR_IO_PENDING)
vpn_progress(vpninfo, PRG_ERR,
_("Failed to read from TAP device: %lx\n"),
err);
break;
}
len = pkt_size;
} else {
/* if out_pkt was already non-NULL then there was lready a pending read on it. */
if (!GetOverlappedResult(vpninfo->tun_fh, &vpninfo->tun_rd_overlap, &pkt_size, FALSE)) {
DWORD err = GetLastError();

if (err != ERROR_IO_INCOMPLETE)
vpn_progress(vpninfo, PRG_ERR,
_("Failed to complete read from TAP device: %lx\n"),
err);
break;
}
len = pkt_size;
#endif /* _WIN32 */
}

#ifndef _WIN32
/* Sanity. Just non-blocking reads on a select()able file descriptor... */
len = read(vpninfo->tun_fd, out_pkt->data - prefix_size, len + prefix_size);
#endif
if (len <= prefix_size)
break;
out_pkt->len = len - prefix_size;
Expand Down Expand Up @@ -793,7 +826,32 @@ int tun_mainloop(struct openconnect_info *vpninfo, int *timeout)
}
#endif
vpninfo->incoming_queue = this->next;

#ifdef _WIN32
if (!WriteFile(vpninfo->tun_fh, data, len, &pkt_size, &vpninfo->tun_wr_overlap)) {
DWORD err = GetLastError();

if (err == ERROR_IO_PENDING) {
/* Theoretically we should let the mainloop handle this blocking,
but that's non-trivial and it doesn't ever seem to happen in
practice anyway. */
vpn_progress(vpninfo, PRG_TRACE,
_("Waiting for tun write...\n"));
if (!GetOverlappedResult(vpninfo->tun_fh, &vpninfo->tun_wr_overlap, &pkt_size, TRUE)) {
err = GetLastError();
goto report_write_err;
}
vpn_progress(vpninfo, PRG_TRACE,
_("Wrote %ld bytes to tun after waiting\n"), pkt_size);
} else {
report_write_err:
vpn_progress(vpninfo, PRG_ERR,
_("Failed to write to TAP device: %lx\n"), err);
}
} else {
vpn_progress(vpninfo, PRG_TRACE,
_("Wrote %ld bytes to tun\n"), pkt_size);
}
#else
if (write(vpninfo->tun_fd, data, len) < 0) {
/* Handle death of "script" socket */
if (vpninfo->script_tun && errno == ENOTCONN) {
Expand All @@ -804,6 +862,7 @@ int tun_mainloop(struct openconnect_info *vpninfo, int *timeout)
_("Failed to write incoming packet: %s\n"),
strerror(errno));
}
#endif
free(this);
}
/* Work is not done if we just got rid of packets off the queue */
Expand All @@ -812,11 +871,16 @@ int tun_mainloop(struct openconnect_info *vpninfo, int *timeout)

void shutdown_tun(struct openconnect_info *vpninfo)
{
#ifdef _WIN32
script_config_tun(vpninfo, "disconnect");
CloseHandle(vpninfo->tun_fh);
vpninfo->tun_fh = NULL;
CloseHandle(vpninfo->tun_rd_overlap.hEvent);
vpninfo->tun_rd_overlap.hEvent = NULL;
#else
if (vpninfo->script_tun) {
#ifndef _WIN32
/* nuke the whole process group */
kill(-vpninfo->script_tun, SIGHUP);
#endif
} else {
script_config_tun(vpninfo, "disconnect");
#ifdef __sun__
Expand All @@ -832,4 +896,5 @@ void shutdown_tun(struct openconnect_info *vpninfo)
if (vpninfo->vpnc_script)
close(vpninfo->tun_fd);
vpninfo->tun_fd = -1;
#endif
}
6 changes: 4 additions & 2 deletions www/platforms.xml
Expand Up @@ -38,8 +38,10 @@ Platform support for new UNIX systems is relatively simple to add
&#8212; most of the difference is in the TUN/TAP device handling, and
the major variants of that are already supported.</p>
<p>
OpenConnect builds for Windows using MinGW, although support for the TUN/TAP
device has not yet been completed.</p>
OpenConnect builds for Windows using MinGW and should work with the
TAP-Windows driver shipped with OpenVPN <i>(driver version 9.9 or later)</i>.
However, support for automatically configuring IP adddress on the network interface
is not yet implemented, and it is only tested with Legacy IP not IPv6.</p>
<p>
A port to Symbian, to provide VPN connectivity on phone handsets,
would be very useful. Any volunteers?</p>
Expand Down

0 comments on commit a12ac9e

Please sign in to comment.