Commit fc34c007 authored by Kevin Cernekee's avatar Kevin Cernekee

tun: Export setup_tun() functionality

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: default avatarKevin Cernekee <cernekee@gmail.com>
parent 73ca062a
......@@ -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 {
......
......@@ -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);
......
......@@ -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;
......@@ -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;
......@@ -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;
......@@ -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);
......@@ -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);
}
......
......@@ -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;
......@@ -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);
......
......@@ -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,
......
......@@ -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;
......@@ -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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment