Skip to content

Commit

Permalink
tun: Export setup_tun() functionality
Browse files Browse the repository at this point in the history
Create three new setup_tun() library functions:

    openconnect_setup_tun_device - create a tun device
    openconnect_setup_tun_script - pass traffic through a script
    openconnect_setup_tun_fd - pass traffic through an existing fd

The latter is needed to support Android's VpnService API.  If a fd is
passed in, the library will not close it.

In all cases, the caller no longer needs to directly manipulate
vpninfo->vpnc_script or vpninfo->script_tun.  The former is treated as
a standard caller-allocated, library-freed string.

Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
  • Loading branch information
cernekee committed Jan 15, 2014
1 parent 73ca062 commit fc34c00
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 52 deletions.
3 changes: 3 additions & 0 deletions libopenconnect.map.in
Expand Up @@ -39,6 +39,9 @@ OPENCONNECT_3.1 {
global:
openconnect_setup_cmd_pipe;
openconnect_mainloop;
openconnect_setup_tun_device;
openconnect_setup_tun_script;
openconnect_setup_tun_fd;
} OPENCONNECT_3.0;

OPENCONNECT_PRIVATE {
Expand Down
2 changes: 2 additions & 0 deletions library.c
Expand Up @@ -123,6 +123,8 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo)
free(vpninfo->redirect_url);
free(vpninfo->proxy_type);
free(vpninfo->proxy);
free(vpninfo->vpnc_script);
free(vpninfo->ifname);

if (vpninfo->csd_scriptname) {
unlink(vpninfo->csd_scriptname);
Expand Down
18 changes: 13 additions & 5 deletions main.c
Expand Up @@ -508,6 +508,8 @@ int main(int argc, char **argv)
int use_syslog = 0;
char *urlpath = NULL;
char *proxy = getenv("https_proxy");
int script_tun = 0;
char *vpnc_script = NULL, *ifname = NULL;
int autoproxy = 0;
uid_t uid = getuid();
int opt;
Expand Down Expand Up @@ -554,7 +556,6 @@ int main(int argc, char **argv)
vpninfo->process_auth_form = process_auth_form_cb;
vpninfo->cbdata = vpninfo;
vpninfo->cert_expire_warning = 60 * 86400;
vpninfo->vpnc_script = DEFAULT_VPNCSCRIPT;
vpninfo->cmd_fd = -1;
vpninfo->xmlpost = 1;

Expand Down Expand Up @@ -659,7 +660,7 @@ int main(int argc, char **argv)
case 'h':
usage();
case 'i':
vpninfo->ifname = keep_config_arg();
ifname = xstrdup(config_arg);
break;
case 'l':
use_syslog = 1;
Expand Down Expand Up @@ -703,10 +704,10 @@ int main(int argc, char **argv)
nocertcheck = 1;
break;
case 's':
vpninfo->vpnc_script = keep_config_arg();
vpnc_script = xstrdup(config_arg);
break;
case 'S':
vpninfo->script_tun = 1;
script_tun = 1;
break;
case 'u':
free(username);
Expand Down Expand Up @@ -922,7 +923,14 @@ int main(int argc, char **argv)
exit(1);
}

if (setup_tun(vpninfo)) {
if (!vpnc_script)
vpnc_script = xstrdup(DEFAULT_VPNCSCRIPT);
if (script_tun) {
if (openconnect_setup_tun_script(vpninfo, vpnc_script)) {
fprintf(stderr, _("Set up tun script failed\n"));
exit(1);
}
} else if (openconnect_setup_tun_device(vpninfo, vpnc_script, ifname)) {
fprintf(stderr, _("Set up tun device failed\n"));
exit(1);
}
Expand Down
3 changes: 1 addition & 2 deletions openconnect-internal.h
Expand Up @@ -263,7 +263,7 @@ struct openconnect_info {
unsigned char dtls_secret[48];

char *dtls_cipher;
const char *vpnc_script;
char *vpnc_script;
int script_tun;
char *ifname;

Expand Down Expand Up @@ -371,7 +371,6 @@ char *openconnect__strcasestr(const char *haystack, const char *needle);
/****************************************************************************/

/* tun.c */
int setup_tun(struct openconnect_info *vpninfo);
int tun_mainloop(struct openconnect_info *vpninfo, int *timeout);
void shutdown_tun(struct openconnect_info *vpninfo);
int script_config_tun(struct openconnect_info *vpninfo, const char *reason);
Expand Down
10 changes: 10 additions & 0 deletions openconnect.h
Expand Up @@ -261,6 +261,16 @@ int openconnect_setup_cmd_pipe(struct openconnect_info *vpninfo);

const char *openconnect_get_version(void);

/* Create a tun device through the OS kernel (typical use case). Both
strings are optional and can be NULL if desired. */
int openconnect_setup_tun_device(struct openconnect_info *vpninfo, char *vpnc_script, char *ifname);

/* Pass traffic to a script program (no tun device). */
int openconnect_setup_tun_script(struct openconnect_info *vpninfo, char *tun_script);

/* Caller will provide a file descriptor for the tunnel traffic. */
int openconnect_setup_tun_fd(struct openconnect_info *vpninfo, int tun_fd);

/* Start the main loop; exits if OC_CMD_CANCEL is received on cmd_fd or
the remote site aborts. */
int openconnect_mainloop(struct openconnect_info *vpninfo,
Expand Down
105 changes: 60 additions & 45 deletions tun.c
Expand Up @@ -631,64 +631,78 @@ static int os_setup_tun(struct openconnect_info *vpninfo)
return tun_fd;
}

/* Set up a tuntap device. */
int setup_tun(struct openconnect_info *vpninfo)
int openconnect_setup_tun_fd(struct openconnect_info *vpninfo, int tun_fd)
{
int tun_fd;
fcntl(tun_fd, F_SETFD, FD_CLOEXEC);

set_script_env(vpninfo);
if (vpninfo->tun_fd != -1)
FD_CLR(vpninfo->tun_fd, &vpninfo->select_rfds);
vpninfo->tun_fd = tun_fd;

if (vpninfo->script_tun) {
pid_t child;
int fds[2];
if (vpninfo->select_nfds <= tun_fd)
vpninfo->select_nfds = tun_fd + 1;

if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds)) {
perror(_("socketpair"));
exit(1);
}
tun_fd = fds[0];
child = fork();
if (child < 0) {
perror(_("fork"));
exit(1);
} else if (!child) {
if (setpgid(0, getpid()) < 0)
perror(_("setpgid"));
close(tun_fd);
setenv_int("VPNFD", fds[1]);
execl("/bin/sh", "/bin/sh", "-c", vpninfo->vpnc_script, NULL);
perror(_("execl"));
exit(1);
}
close(fds[1]);
vpninfo->script_tun = child;
vpninfo->ifname = strdup(_("(script)"));
} else {
script_config_tun(vpninfo, "pre-init");
FD_SET(tun_fd, &vpninfo->select_rfds);

tun_fd = os_setup_tun(vpninfo);
if (tun_fd < 0)
return tun_fd;
fcntl(vpninfo->tun_fd, F_SETFL, fcntl(vpninfo->tun_fd, F_GETFL) | O_NONBLOCK);

setenv("TUNDEV", vpninfo->ifname, 1);
script_config_tun(vpninfo, "connect");
return 0;
}

int openconnect_setup_tun_script(struct openconnect_info *vpninfo, char *tun_script)
{
pid_t child;
int fds[2];

/* Ancient vpnc-scripts might not get this right */
set_tun_mtu(vpninfo);
vpninfo->vpnc_script = tun_script;
vpninfo->script_tun = 1;

set_script_env(vpninfo);
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds)) {
perror(_("socketpair"));
exit(1);
}
child = fork();
if (child < 0) {
perror(_("fork"));
exit(1);
} else if (!child) {
if (setpgid(0, getpid()) < 0)
perror(_("setpgid"));
close(fds[0]);
setenv_int("VPNFD", fds[1]);
execl("/bin/sh", "/bin/sh", "-c", vpninfo->vpnc_script, NULL);
perror(_("execl"));
exit(1);
}
close(fds[1]);
vpninfo->script_tun = child;
vpninfo->ifname = strdup(_("(script)"));

fcntl(tun_fd, F_SETFD, FD_CLOEXEC);
return openconnect_setup_tun_fd(vpninfo, fds[0]);
}

vpninfo->tun_fd = tun_fd;
int openconnect_setup_tun_device(struct openconnect_info *vpninfo, char *vpnc_script, char *ifname)
{
int tun_fd;

if (vpninfo->select_nfds <= tun_fd)
vpninfo->select_nfds = tun_fd + 1;
vpninfo->vpnc_script = vpnc_script;
vpninfo->ifname = ifname;

FD_SET(tun_fd, &vpninfo->select_rfds);
set_script_env(vpninfo);
script_config_tun(vpninfo, "pre-init");

fcntl(vpninfo->tun_fd, F_SETFL, fcntl(vpninfo->tun_fd, F_GETFL) | O_NONBLOCK);
tun_fd = os_setup_tun(vpninfo);
if (tun_fd < 0)
return tun_fd;

return 0;
setenv("TUNDEV", vpninfo->ifname, 1);
script_config_tun(vpninfo, "connect");

/* Ancient vpnc-scripts might not get this right */
set_tun_mtu(vpninfo);

return openconnect_setup_tun_fd(vpninfo, tun_fd);
}

static struct pkt *out_pkt;
Expand Down Expand Up @@ -802,6 +816,7 @@ void shutdown_tun(struct openconnect_info *vpninfo)
#endif
}

close(vpninfo->tun_fd);
if (vpninfo->vpnc_script)
close(vpninfo->tun_fd);
vpninfo->tun_fd = -1;
}

0 comments on commit fc34c00

Please sign in to comment.