From b7968faa2a6dac1bd9641309ccf4c9a387bca26c Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Tue, 21 Jan 2014 20:37:31 +0100 Subject: Add to code that allows excluding routes from the VPN --HG-- extra : rebase_source : 7e20e643cb0949520b92f7ab7b623d6856ea4ef7 --- icsopenvpn.iml | 2 +- openvpn/config.h | 2 +- openvpn/configure.ac | 6 + openvpn/doc/openvpn.8 | 10 + openvpn/src/openvpn/ps.c | 3 +- openvpn/src/openvpn/route.c | 140 +++--------- openvpn/src/openvpn/route.h | 10 +- openvpn/src/openvpn/socket.c | 3 +- openvpn/src/openvpn/ssl_polarssl.c | 13 +- openvpn/src/openvpn/ssl_verify_polarssl.c | 2 - openvpn/src/openvpn/syshead.h | 7 + openvpn/src/openvpn/tun.c | 6 +- openvpn/src/openvpn/win32.c | 2 +- res/values/strings.xml | 5 +- src/de/blinkt/openvpn/VpnProfile.java | 4 +- src/de/blinkt/openvpn/core/ConfigParser.java | 2 +- src/de/blinkt/openvpn/core/NetworkSpace.java | 244 +++++++++++++++++++++ .../openvpn/core/OpenVpnManagementThread.java | 12 +- src/de/blinkt/openvpn/core/OpenVpnService.java | 109 +++++---- src/de/blinkt/openvpn/core/VpnStatus.java | 7 +- 20 files changed, 402 insertions(+), 187 deletions(-) create mode 100644 src/de/blinkt/openvpn/core/NetworkSpace.java diff --git a/icsopenvpn.iml b/icsopenvpn.iml index 62aab261..866601d3 100644 --- a/icsopenvpn.iml +++ b/icsopenvpn.iml @@ -1,5 +1,5 @@ - + diff --git a/openvpn/config.h b/openvpn/config.h index d281d33d..993a46f1 100644 --- a/openvpn/config.h +++ b/openvpn/config.h @@ -450,7 +450,7 @@ #define PACKAGE_NAME "OpenVPN" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "OpenVPN 2.4-icsopenvpn64" +#define PACKAGE_STRING "OpenVPN 2.4-icsopenvpn" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "openvpn" diff --git a/openvpn/configure.ac b/openvpn/configure.ac index 636d4e25..380dcdbb 100644 --- a/openvpn/configure.ac +++ b/openvpn/configure.ac @@ -500,6 +500,12 @@ AC_CHECK_TYPE( , [[${SOCKET_INCLUDES}]] ) +AC_CHECK_TYPE( + [sa_family_t], + [AC_DEFINE([HAVE_SA_FAMILY_T], [1], [sa_family_t, needed to hold AF_* info])], + , + [[${SOCKET_INCLUDES}]] +) AC_CHECK_TYPE( [struct sockaddr_in6], , diff --git a/openvpn/doc/openvpn.8 b/openvpn/doc/openvpn.8 index 0235c2c8..9eebf93e 100644 --- a/openvpn/doc/openvpn.8 +++ b/openvpn/doc/openvpn.8 @@ -2097,6 +2097,16 @@ In many cases, the parameter can point to an empty directory, however complications can result when scripts or restarts are executed after the chroot operation. + +Note: if OpenVPN is built using the PolarSSL SSL +library, +.B \-\-chroot +will only work if a /dev/urandom device node is available +inside the chroot directory +.B dir. +This is due to the way PolarSSL works (it wants to open +/dev/urandom every time randomness is needed, not just once +at startup) and nothing OpenVPN can influence. .\"********************************************************* .TP .B \-\-setcon context diff --git a/openvpn/src/openvpn/ps.c b/openvpn/src/openvpn/ps.c index b22653b4..901a094f 100644 --- a/openvpn/src/openvpn/ps.c +++ b/openvpn/src/openvpn/ps.c @@ -340,7 +340,8 @@ journal_add (const char *journal_dir, struct proxy_connection *pc, struct proxy_ fd = platform_open (jfn, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP); if (fd != -1) { - write(fd, f, strlen(f)); + if (write(fd, f, strlen(f)) != strlen(f)) + msg(M_WARN, "PORT SHARE: writing to journal file (%s) failed", jfn); close (fd); cp->jfn = jfn; } diff --git a/openvpn/src/openvpn/route.c b/openvpn/src/openvpn/route.c index 81ffa876..fa8221d4 100644 --- a/openvpn/src/openvpn/route.c +++ b/openvpn/src/openvpn/route.c @@ -49,7 +49,7 @@ #define METRIC_NOT_USED ((DWORD)-1) #endif -static void delete_route (struct route *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, const struct env_set *es); +static void delete_route (struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, const struct env_set *es); static void get_bypass_addresses (struct route_bypass *rb, const unsigned int flags); @@ -150,7 +150,7 @@ struct route_list * new_route_list (const int max_routes, struct gc_arena *a) { struct route_list *ret; - ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_list, struct route, max_routes, a); + ALLOC_VAR_ARRAY_CLEAR_GC (ret, struct route_list, struct route_ipv4, max_routes, a); ret->capacity = max_routes; return ret; } @@ -165,7 +165,7 @@ new_route_ipv6_list (const int max_routes, struct gc_arena *a) } static const char * -route_string (const struct route *r, struct gc_arena *gc) +route_string (const struct route_ipv4 *r, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (256, gc); buf_printf (&out, "ROUTE network %s netmask %s gateway %s", @@ -267,7 +267,7 @@ is_special_addr (const char *addr_str) } static bool -init_route (struct route *r, +init_route (struct route_ipv4 *r, struct addrinfo **network_list, const struct route_option *ro, const struct route_list *rl) @@ -484,7 +484,7 @@ void clear_route_list (struct route_list *rl) { const int capacity = rl->capacity; - const size_t rl_size = array_mult_safe (sizeof(struct route), capacity, sizeof(struct route_list)); + const size_t rl_size = array_mult_safe (sizeof(struct route_ipv4), capacity, sizeof(struct route_list)); memset(rl, 0, rl_size); rl->capacity = capacity; } @@ -519,7 +519,7 @@ add_block_local_item (struct route_list *rl, && rl->rgi.gateway.netmask < 0xFFFFFFFF && (rl->n)+2 <= rl->capacity) { - struct route r; + struct route_ipv4 r; unsigned int l2; /* split a route into two smaller blocking routes, and direct them to target */ @@ -649,7 +649,7 @@ init_route_list (struct route_list *rl, for (i = 0; i < opt->n; ++i) { struct addrinfo* netlist; - struct route r; + struct route_ipv4 r; if (!init_route (&r, &netlist, @@ -760,7 +760,7 @@ add_route3 (in_addr_t network, const struct route_gateway_info *rgi, const struct env_set *es) { - struct route r; + struct route_ipv4 r; CLEAR (r); r.flags = RT_DEFINED; r.network = network; @@ -778,7 +778,7 @@ del_route3 (in_addr_t network, const struct route_gateway_info *rgi, const struct env_set *es) { - struct route r; + struct route_ipv4 r; CLEAR (r); r.flags = RT_DEFINED|RT_ADDED; r.network = network; @@ -1028,7 +1028,7 @@ add_routes (struct route_list *rl, struct route_ipv6_list *rl6, const struct tun for (i = 0; i < rl->n; ++i) { - struct route *r = &rl->routes[i]; + struct route_ipv4 *r = &rl->routes[i]; check_subnet_conflict (r->network, r->netmask, "route"); if (flags & ROUTE_DELETE_FIRST) delete_route (r, tt, flags, &rl->rgi, es); @@ -1060,7 +1060,7 @@ delete_routes (struct route_list *rl, struct route_ipv6_list *rl6, int i; for (i = rl->n - 1; i >= 0; --i) { - struct route * r = &rl->routes[i]; + struct route_ipv4 * r = &rl->routes[i]; delete_route (r, tt, flags, &rl->rgi, es); } rl->iflags &= ~RL_ROUTES_ADDED; @@ -1154,7 +1154,7 @@ print_default_gateway(const int msglevel, const struct route_gateway_info *rgi) #endif static void -print_route (const struct route *r, int level) +print_route (const struct route_ipv4 *r, int level) { struct gc_arena gc = gc_new (); if (r->flags & RT_DEFINED) @@ -1171,7 +1171,7 @@ print_routes (const struct route_list *rl, int level) } static void -setenv_route (struct env_set *es, const struct route *r, int i) +setenv_route (struct env_set *es, const struct route_ipv4 *r, int i) { struct gc_arena gc = gc_new (); if (r->flags & RT_DEFINED) @@ -1288,7 +1288,7 @@ is_on_link (const int is_local_route, const unsigned int flags, const struct rou } void -add_route (struct route *r, +add_route (struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, /* may be NULL */ @@ -1344,9 +1344,12 @@ add_route (struct route *r, status = openvpn_execve_check (&argv, es, 0, "ERROR: Linux route add command failed"); #elif defined (TARGET_ANDROID) - struct buffer out = alloc_buf_gc (64, &gc); + struct buffer out = alloc_buf_gc (128, &gc); - buf_printf (&out, "%s %s", network, netmask); + if (rgi) + buf_printf (&out, "%s %s %s dev %s", network, netmask, gateway, rgi->iface); + else + buf_printf (&out, "%s %s %s", network, netmask, gateway); management_android_control (management, "ROUTE", buf_bptr(&out)); #elif defined (WIN32) @@ -1626,7 +1629,7 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla #elif defined (TARGET_ANDROID) struct buffer out = alloc_buf_gc (64, &gc); - buf_printf (&out, "%s/%d", network, r6->netbits); + buf_printf (&out, "%s/%d %s", network, r6->netbits, device); management_android_control (management, "ROUTE6", buf_bptr(&out)); @@ -1741,7 +1744,7 @@ add_route_ipv6 (struct route_ipv6 *r6, const struct tuntap *tt, unsigned int fla } static void -delete_route (struct route *r, +delete_route (struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, @@ -2246,7 +2249,7 @@ get_default_gateway (struct route_gateway_info *rgi) } static DWORD -windows_route_find_if_index (const struct route *r, const struct tuntap *tt) +windows_route_find_if_index (const struct route_ipv4 *r, const struct tuntap *tt) { struct gc_arena gc = gc_new (); DWORD ret = TUN_ADAPTER_INDEX_INVALID; @@ -2291,7 +2294,7 @@ windows_route_find_if_index (const struct route *r, const struct tuntap *tt) } bool -add_route_ipapi (const struct route *r, const struct tuntap *tt, DWORD adapter_index) +add_route_ipapi (const struct route_ipv4 *r, const struct tuntap *tt, DWORD adapter_index) { struct gc_arena gc = gc_new (); bool ret = false; @@ -2365,7 +2368,7 @@ add_route_ipapi (const struct route *r, const struct tuntap *tt, DWORD adapter_i } bool -del_route_ipapi (const struct route *r, const struct tuntap *tt) +del_route_ipapi (const struct route_ipv4 *r, const struct tuntap *tt) { struct gc_arena gc = gc_new (); bool ret = false; @@ -2611,53 +2614,7 @@ get_default_gateway (struct route_gateway_info *rgi) #include #include #include - -/* all of this is taken from in FreeBSD */ -#define RTA_DST 0x1 -#define RTA_GATEWAY 0x2 -#define RTA_NETMASK 0x4 - -#define RTM_GET 0x4 -#define RTM_VERSION 5 - -#define RTF_UP 0x1 -#define RTF_GATEWAY 0x2 - -/* - * These numbers are used by reliable protocols for determining - * retransmission behavior and are included in the routing structure. - */ -struct rt_metrics { - u_long rmx_locks; /* Kernel must leave these values alone */ - u_long rmx_mtu; /* MTU for this path */ - u_long rmx_hopcount; /* max hops expected */ - u_long rmx_expire; /* lifetime for route, e.g. redirect */ - u_long rmx_recvpipe; /* inbound delay-bandwidth product */ - u_long rmx_sendpipe; /* outbound delay-bandwidth product */ - u_long rmx_ssthresh; /* outbound gateway buffer limit */ - u_long rmx_rtt; /* estimated round trip time */ - u_long rmx_rttvar; /* estimated rtt variance */ - u_long rmx_pksent; /* packets sent using this route */ - u_long rmx_filler[4]; /* will be used for T/TCP later */ -}; - -/* - * Structures for routing messages. - */ -struct rt_msghdr { - u_short rtm_msglen; /* to skip over non-understood messages */ - u_char rtm_version; /* future binary compatibility */ - u_char rtm_type; /* message type */ - u_short rtm_index; /* index for associated ifp */ - int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ - int rtm_addrs; /* bitmask identifying sockaddrs in msg */ - pid_t rtm_pid; /* identify sender */ - int rtm_seq; /* for sender to identify action */ - int rtm_errno; /* why failed */ - int rtm_use; /* from rtentry */ - u_long rtm_inits; /* which metrics we are initializing */ - struct rt_metrics rtm_rmx; /* metrics themselves */ -}; +#include struct { struct rt_msghdr m_rtm; @@ -2976,52 +2933,7 @@ get_default_gateway (struct route_gateway_info *rgi) #include #include #include - -/* all of this is taken from in OpenBSD 3.6 */ -#define RTA_DST 0x1 /* destination sockaddr present */ -#define RTA_GATEWAY 0x2 /* gateway sockaddr present */ -#define RTA_NETMASK 0x4 /* netmask sockaddr present */ - -#define RTM_GET 0x4 /* Report Metrics */ - -#define RTM_VERSION 3 /* Up the ante and ignore older versions */ - -#define RTF_UP 0x1 /* route usable */ -#define RTF_GATEWAY 0x2 /* destination is a gateway */ - -/* - * Huge version for userland compatibility. - */ -struct rt_metrics { - u_long rmx_locks; /* Kernel must leave these values alone */ - u_long rmx_mtu; /* MTU for this path */ - u_long rmx_hopcount; /* max hops expected */ - u_long rmx_expire; /* lifetime for route, e.g. redirect */ - u_long rmx_recvpipe; /* inbound delay-bandwidth product */ - u_long rmx_sendpipe; /* outbound delay-bandwidth product */ - u_long rmx_ssthresh; /* outbound gateway buffer limit */ - u_long rmx_rtt; /* estimated round trip time */ - u_long rmx_rttvar; /* estimated rtt variance */ - u_long rmx_pksent; /* packets sent using this route */ -}; - -/* - * Structures for routing messages. - */ -struct rt_msghdr { - u_short rtm_msglen; /* to skip over non-understood messages */ - u_char rtm_version; /* future binary compatibility */ - u_char rtm_type; /* message type */ - u_short rtm_index; /* index for associated ifp */ - int rtm_flags; /* flags, incl. kern & message, e.g. DONE */ - int rtm_addrs; /* bitmask identifying sockaddrs in msg */ - pid_t rtm_pid; /* identify sender */ - int rtm_seq; /* for sender to identify action */ - int rtm_errno; /* why failed */ - int rtm_use; /* from rtentry */ - u_long rtm_inits; /* which metrics we are initializing */ - struct rt_metrics rtm_rmx; /* metrics themselves */ -}; +#include struct { struct rt_msghdr m_rtm; diff --git a/openvpn/src/openvpn/route.h b/openvpn/src/openvpn/route.h index a40de32f..fe9b4616 100644 --- a/openvpn/src/openvpn/route.h +++ b/openvpn/src/openvpn/route.h @@ -110,7 +110,7 @@ struct route_ipv6_option_list { struct route_ipv6_option routes_ipv6[EMPTY_ARRAY_SIZE]; }; -struct route { +struct route_ipv4 { # define RT_DEFINED (1<<0) # define RT_ADDED (1<<1) # define RT_METRIC_DEFINED (1<<2) @@ -190,7 +190,7 @@ struct route_list { unsigned int flags; /* RG_x flags */ int capacity; int n; - struct route routes[EMPTY_ARRAY_SIZE]; + struct route_ipv4 routes[EMPTY_ARRAY_SIZE]; }; #if P2MP @@ -223,7 +223,7 @@ struct route_ipv6_list *new_route_ipv6_list (const int max_routes, struct gc_are void add_route_ipv6 (struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es); void delete_route_ipv6 (const struct route_ipv6 *r, const struct tuntap *tt, unsigned int flags, const struct env_set *es); -void add_route (struct route *r, +void add_route (struct route_ipv4 *r, const struct tuntap *tt, unsigned int flags, const struct route_gateway_info *rgi, @@ -301,8 +301,8 @@ void print_routes (const struct route_list *rl, int level); void show_routes (int msglev); bool test_routes (const struct route_list *rl, const struct tuntap *tt); -bool add_route_ipapi (const struct route *r, const struct tuntap *tt, DWORD adapter_index); -bool del_route_ipapi (const struct route *r, const struct tuntap *tt); +bool add_route_ipapi (const struct route_ipv4 *r, const struct tuntap *tt, DWORD adapter_index); +bool del_route_ipapi (const struct route_ipv4 *r, const struct tuntap *tt); #else static inline bool test_routes (const struct route_list *rl, const struct tuntap *tt) { return true; } diff --git a/openvpn/src/openvpn/socket.c b/openvpn/src/openvpn/socket.c index 716512df..97f67563 100644 --- a/openvpn/src/openvpn/socket.c +++ b/openvpn/src/openvpn/socket.c @@ -1981,7 +1981,8 @@ link_socket_init_phase2 (struct link_socket *sock, #endif } #ifdef TARGET_ANDROID - protect_fd_nonlocal (sock->sd, &sock->info.lsa->actual.dest.addr.sa); + if (sock->sd != -1) + protect_fd_nonlocal (sock->sd, &sock->info.lsa->actual.dest.addr.sa); #endif if (sig_info && sig_info->signal_received) goto done; diff --git a/openvpn/src/openvpn/ssl_polarssl.c b/openvpn/src/openvpn/ssl_polarssl.c index 47fb62a5..9dc4e879 100644 --- a/openvpn/src/openvpn/ssl_polarssl.c +++ b/openvpn/src/openvpn/ssl_polarssl.c @@ -49,6 +49,7 @@ #include #include "ssl_verify_polarssl.h" +#include #include void @@ -284,7 +285,7 @@ tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file, pem_password_callback(passbuf, 512, 0, NULL); status = x509parse_key(ctx->priv_key, priv_key_file_inline, strlen(priv_key_file_inline), - passbuf, strlen(passbuf)); + (unsigned char *) passbuf, strlen(passbuf)); } } else @@ -481,7 +482,8 @@ void tls_ctx_load_ca (struct tls_root_ctx *ctx, const char *ca_file, if (ca_file && !strcmp (ca_file, INLINE_FILE_TAG) && ca_file_inline) { - if (0 != x509parse_crt(ctx->ca_chain, ca_file_inline, strlen(ca_file_inline))) + if (0 != x509parse_crt(ctx->ca_chain, (unsigned char *) ca_file_inline, + strlen(ca_file_inline))) msg (M_FATAL, "Cannot load inline CA certificates"); } else @@ -501,8 +503,9 @@ tls_ctx_load_extra_certs (struct tls_root_ctx *ctx, const char *extra_certs_file if (!strcmp (extra_certs_file, INLINE_FILE_TAG) && extra_certs_file_inline) { - if (0 != x509parse_crt(ctx->crt_chain, extra_certs_file_inline, - strlen(extra_certs_file_inline))) + if (0 != x509parse_crt(ctx->crt_chain, + (unsigned char *) extra_certs_file_inline, + strlen(extra_certs_file_inline))) msg (M_FATAL, "Cannot load inline extra-certs file"); } else @@ -625,7 +628,7 @@ static void my_debug( void *ctx, int level, const char *str ) void tls_ctx_personalise_random(struct tls_root_ctx *ctx) { static char old_sha256_hash[32] = {0}; - char sha256_hash[32] = {0}; + unsigned char sha256_hash[32] = {0}; ctr_drbg_context *cd_ctx = rand_ctx_get(); if (NULL != ctx->crt_chain) diff --git a/openvpn/src/openvpn/ssl_verify_polarssl.c b/openvpn/src/openvpn/ssl_verify_polarssl.c index 5db4f027..e5ccd904 100644 --- a/openvpn/src/openvpn/ssl_verify_polarssl.c +++ b/openvpn/src/openvpn/ssl_verify_polarssl.c @@ -125,8 +125,6 @@ x509_get_username (char *cn, int cn_len, char * x509_get_serial (x509_cert *cert, struct gc_arena *gc) { - int ret = 0; - int i = 0; char *buf = NULL; size_t len = cert->serial.len * 3 + 1; diff --git a/openvpn/src/openvpn/syshead.h b/openvpn/src/openvpn/syshead.h index ab6fa01f..4050d548 100644 --- a/openvpn/src/openvpn/syshead.h +++ b/openvpn/src/openvpn/syshead.h @@ -425,6 +425,13 @@ #define SOL_IP IPPROTO_IP #endif +/* + * Define type sa_family_t if it isn't defined in the socket headers + */ +#ifndef HAVE_SA_FAMILY_T +typedef unsigned short sa_family_t; +#endif + /* * Disable ESEC */ diff --git a/openvpn/src/openvpn/tun.c b/openvpn/src/openvpn/tun.c index 6460a369..4df271d5 100644 --- a/openvpn/src/openvpn/tun.c +++ b/openvpn/src/openvpn/tun.c @@ -909,7 +909,7 @@ do_ifconfig (struct tuntap *tt, if (!tun && tt->topology == TOP_SUBNET) { /* Add a network route for the local tun interface */ - struct route r; + struct route_ipv4 r; CLEAR (r); r.flags = RT_DEFINED | RT_METRIC_DEFINED; r.network = tt->local & tt->remote_netmask; @@ -1106,7 +1106,7 @@ do_ifconfig (struct tuntap *tt, /* Add a network route for the local tun interface */ if (!tun && tt->topology == TOP_SUBNET) { - struct route r; + struct route_ipv4 r; CLEAR (r); r.flags = RT_DEFINED; r.network = tt->local & tt->remote_netmask; @@ -1172,7 +1172,7 @@ do_ifconfig (struct tuntap *tt, /* Add a network route for the local tun interface */ if (!tun && tt->topology == TOP_SUBNET) { - struct route r; + struct route_ipv4 r; CLEAR (r); r.flags = RT_DEFINED; r.network = tt->local & tt->remote_netmask; diff --git a/openvpn/src/openvpn/win32.c b/openvpn/src/openvpn/win32.c index f35c96be..7c89a5a9 100644 --- a/openvpn/src/openvpn/win32.c +++ b/openvpn/src/openvpn/win32.c @@ -517,7 +517,7 @@ win32_signal_get (struct win32_signal *ws) if (ret) { siginfo_static.signal_received = ret; - siginfo_static.hard = true; + siginfo_static.source = SIG_SOURCE_HARD; } } return ret; diff --git a/res/values/strings.xml b/res/values/strings.xml index 792a5f89..34721515 100755 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -97,8 +97,9 @@ Opening tun interface: Local IPv4: %1$s/%2$d IPv6: %3$s MTU: %4$d DNS Server: %1$s, Domain: %2$s - Routes: %s - Routes IPv6: %s + Routes: %1$s %2$s + Routes excluded: %1$s %2$s + VpnService routes installed: %1$s %2$s Got interface information %1$s and %2$s, assuming second address is peer address of remote. Using /32 netmask for local IP. Mode given by OpenVPN is \"%3$s\". Cannot make sense of %1$s and %2$s as IP route with CIDR netmask, using /32 as netmask. Corrected route %1$s/%2$s to %3$s/%2$s diff --git a/src/de/blinkt/openvpn/VpnProfile.java b/src/de/blinkt/openvpn/VpnProfile.java index d580829d..b016fb64 100644 --- a/src/de/blinkt/openvpn/VpnProfile.java +++ b/src/de/blinkt/openvpn/VpnProfile.java @@ -305,10 +305,10 @@ public class VpnProfile implements Serializable { String routes = ""; int numroutes = 0; if (mUseDefaultRoute) - routes += "route 0.0.0.0 0.0.0.0\n"; + routes += "route 0.0.0.0 0.0.0.0 vpn_gateway\n"; else for (String route : getCustomRoutes()) { - routes += "route " + route + "\n"; + routes += "route " + route + "vpn_gateway\n"; numroutes++; } diff --git a/src/de/blinkt/openvpn/core/ConfigParser.java b/src/de/blinkt/openvpn/core/ConfigParser.java index 103c208b..895f048e 100644 --- a/src/de/blinkt/openvpn/core/ConfigParser.java +++ b/src/de/blinkt/openvpn/core/ConfigParser.java @@ -12,7 +12,7 @@ import java.util.Vector; //! Openvpn Config FIle Parser, probably not 100% accurate but close enough -// And rember, this is valid :) +// And remember, this is valid :) // -- // bar // diff --git a/src/de/blinkt/openvpn/core/NetworkSpace.java b/src/de/blinkt/openvpn/core/NetworkSpace.java new file mode 100644 index 00000000..3701c43d --- /dev/null +++ b/src/de/blinkt/openvpn/core/NetworkSpace.java @@ -0,0 +1,244 @@ +package de.blinkt.openvpn.core; + +import android.text.TextUtils; + +import java.math.BigInteger; +import java.net.Inet6Address; +import java.util.*; + +public class NetworkSpace { + + + static class ipAddress implements Comparable { + private BigInteger netAddress; + public int networkMask; + private boolean included; + private boolean isV4; + + + @Override + public int compareTo(ipAddress another) { + int comp = getFirstAddress().compareTo(another.getFirstAddress()); + if (comp != 0) + return comp; + + // bigger mask means smaller address block + if (networkMask > another.networkMask) + return -1; + else if (another.networkMask == networkMask) + return 0; + else + return 1; + + + } + + public ipAddress(CIDRIP ip, boolean include) { + included = include; + netAddress = BigInteger.valueOf(ip.getInt()); + networkMask = ip.len; + isV4 = true; + } + + public ipAddress(Inet6Address address, int mask, boolean include) { + networkMask = mask; + included = include; + + int s = 128; + + netAddress = BigInteger.ZERO; + for (byte b : address.getAddress()) { + s -= 16; + netAddress = netAddress.add(BigInteger.valueOf(b).shiftLeft(s)); + } + } + + public BigInteger getLastAddress() { + return getMaskedAddress(true); + } + + + public BigInteger getFirstAddress() { + return getMaskedAddress(false); + } + + + private BigInteger getMaskedAddress(boolean one) { + BigInteger numAddress = netAddress; + + int numBits; + if (isV4) { + numBits = 32 - networkMask; + } else { + numBits = 128 - networkMask; + } + + for (int i = 0; i < numBits; i++) { + if (one) + numAddress = numAddress.setBit(i); + else + numAddress = numAddress.clearBit(i); + } + return numAddress; + } + + + @Override + public String toString() { + //String in = included ? "+" : "-"; + if (isV4) + return String.format("%s/%d", getIPv4Address(), networkMask); + else + return String.format("%s/%d", getIPv6Address(), networkMask); + } + + ipAddress(BigInteger baseAddress, int mask, boolean included, boolean isV4) { + this.netAddress = baseAddress; + this.networkMask = mask; + this.included = included; + this.isV4 = isV4; + } + + + public ipAddress[] split() { + ipAddress firsthalf = new ipAddress(getFirstAddress(), networkMask + 1, included, isV4); + ipAddress secondhalf = new ipAddress(firsthalf.getLastAddress().add(BigInteger.ONE), networkMask + 1, included, isV4); + assert secondhalf.getLastAddress().equals(getLastAddress()); + return new ipAddress[]{firsthalf, secondhalf}; + } + + String getIPv4Address() { + assert (isV4); + assert (netAddress.longValue() <= 0xffffffffl); + assert (netAddress.longValue() >= 0); + long ip = netAddress.longValue(); + return String.format("%d.%d.%d.%d", (ip >> 24) % 256, (ip >> 16) % 256, (ip >> 8) % 256, ip % 256); + } + + String getIPv6Address() { + assert (!isV4); + BigInteger r = netAddress; + if (r.longValue() == 0) + return "::"; + + Vector parts = new Vector(); + while (r.compareTo(BigInteger.ZERO) == 1) { + parts.add(0, String.format("%x", r.mod(BigInteger.valueOf(256)).longValue())); + r = r.shiftRight(16); + } + + return TextUtils.join(":", parts); + } + + public boolean containsNet(ipAddress network) { + return getFirstAddress().compareTo(network.getFirstAddress()) != 1 && + getLastAddress().compareTo(network.getLastAddress()) != -1; + } + } + + + TreeSet ipAddresses = new TreeSet(); + + + public Collection getNetworks(boolean included) { + Vector ips = new Vector(); + for (ipAddress ip : ipAddresses) { + if (ip.included == included) + ips.add(ip); + } + return ips; + } + + public void clear() { + ipAddresses.clear(); + } + + + void addIP(CIDRIP cidrIp, boolean include) { + + ipAddresses.add(new ipAddress(cidrIp, include)); + } + + void addIPv6(Inet6Address address, int mask, boolean included) { + ipAddresses.add(new ipAddress(address, mask, included)); + } + + TreeSet generateIPList() { + TreeSet ipsSorted = new TreeSet(ipAddresses); + Iterator it = ipsSorted.iterator(); + + ipAddress currentNet = null; + if (it.hasNext()) + currentNet = it.next(); + while (it.hasNext()) { + // Check if it and the next of it are compatbile + ipAddress nextNet = it.next(); + + assert currentNet != null; + if (currentNet.getLastAddress().compareTo(nextNet.getFirstAddress()) == -1) { + // Everything good, no overlapping nothing to do + currentNet = nextNet; + } else { + // This network is smaller or equal to the next but has the same base address + if (currentNet.getFirstAddress().equals(nextNet.getFirstAddress()) && currentNet.networkMask >= nextNet.networkMask) { + if (currentNet.included == nextNet.included) { + ipsSorted.remove(currentNet); + } else { + + // our currentnet is included in next and nextnet needs to be split + ipsSorted.remove(nextNet); + ipAddress[] newNets = nextNet.split(); + + if (newNets[0].getLastAddress().equals(currentNet.getLastAddress())) { + assert (newNets[0].networkMask == currentNet.networkMask); + // Don't add the lower half that would conflict with currentNet + } else { + ipsSorted.add(newNets[0]); + } + + ipsSorted.add(newNets[1]); + } + } else { + assert (currentNet.networkMask < nextNet.networkMask); + assert (nextNet.getFirstAddress().compareTo(currentNet.getFirstAddress()) == 1); + // This network is bigger than the next and last ip of current >= next + assert (currentNet.getLastAddress().compareTo(nextNet.getLastAddress()) != -1); + + if (currentNet.included == nextNet.included) { + ipsSorted.remove(nextNet); + } else { + ipsSorted.remove(currentNet); + ipAddress[] newNets = currentNet.split(); + + ipsSorted.add(newNets[0]); + + if (newNets[1].networkMask == nextNet.networkMask) { + assert (newNets[1].getFirstAddress().equals(nextNet.getFirstAddress())); + assert (newNets[1].getLastAddress().equals(currentNet.getLastAddress())); + } else { + ipsSorted.add(newNets[1]); + } + } + } + // Reset iterator + it = ipsSorted.iterator(); + currentNet = it.next(); + } + + } + + return ipsSorted; + } + + Collection getPositiveIPList() { + TreeSet ipsSorted = generateIPList(); + + Vector ips = new Vector(); + for (ipAddress ia : ipsSorted) { + if (ia.included) + ips.add(ia); + } + return ips; + } + +} diff --git a/src/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/src/de/blinkt/openvpn/core/OpenVpnManagementThread.java index 2fa407ca..5fa70cc8 100644 --- a/src/de/blinkt/openvpn/core/OpenVpnManagementThread.java +++ b/src/de/blinkt/openvpn/core/OpenVpnManagementThread.java @@ -379,9 +379,17 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement { mOpenVPNService.setDomain(extra); } else if (needed.equals("ROUTE")) { String[] routeparts = extra.split(" "); - mOpenVPNService.addRoute(routeparts[0], routeparts[1]); + + if(routeparts.length>3) { + assert(routeparts[3].equals("dev")); + mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], routeparts[4]); + } else { + mOpenVPNService.addRoute(routeparts[0], routeparts[1], routeparts[2], null); + } + } else if (needed.equals("ROUTE6")) { - mOpenVPNService.addRoutev6(extra); + String[] routeparts = extra.split(" "); + mOpenVPNService.addRoutev6(routeparts[0],routeparts[1]); } else if (needed.equals("IFCONFIG")) { String[] ifconfigparts = extra.split(" "); int mtu = Integer.parseInt(ifconfigparts[2]); diff --git a/src/de/blinkt/openvpn/core/OpenVpnService.java b/src/de/blinkt/openvpn/core/OpenVpnService.java index 3de701b1..e485ee73 100644 --- a/src/de/blinkt/openvpn/core/OpenVpnService.java +++ b/src/de/blinkt/openvpn/core/OpenVpnService.java @@ -25,10 +25,15 @@ import de.blinkt.openvpn.core.VpnStatus.StateListener; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.HashMap; import java.util.Locale; import java.util.Vector; +import static de.blinkt.openvpn.core.NetworkSpace.*; import static de.blinkt.openvpn.core.VpnStatus.ConnectionStatus.*; public class OpenVpnService extends VpnService implements StateListener, Callback, ByteCountListener { @@ -41,8 +46,8 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac private static final int OPENVPN_STATUS = 1; private static boolean mNotificationAlwaysVisible = false; private final Vector mDnslist = new Vector(); - private final Vector mRoutes = new Vector(); - private final Vector mRoutesv6 = new Vector(); + private final NetworkSpace mRoutes = new NetworkSpace(); + private final NetworkSpace mRoutesv6 = new NetworkSpace(); private final IBinder mBinder = new LocalBinder(); private Thread mProcessThread = null; private VpnProfile mProfile; @@ -255,8 +260,7 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac mDeviceStateReceiver = null; } - public void userPause (boolean shouldBePaused) - { + public void userPause(boolean shouldBePaused) { if (mDeviceStateReceiver != null) mDeviceStateReceiver.userPause(shouldBePaused); } @@ -349,7 +353,6 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac } - Runnable processThread; if (mOvpn3) { @@ -396,21 +399,21 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac } - private String getTunConfigString() - { + private String getTunConfigString() { // The format of the string is not important, only that // two identical configurations produce the same result - String cfg="TUNCFG UNQIUE STRING ips:"; - - if (mLocalIP!=null) - cfg+=mLocalIP.toString(); - if (mLocalIPv6!=null) - cfg+=mLocalIPv6.toString(); - - cfg+= "routes: " + TextUtils.join("|",mRoutes) + TextUtils.join("|",mRoutesv6); - cfg+= "dns: " + TextUtils.join("|",mDnslist); - cfg+= "domain: " + mDomain; - cfg+= "mtu: " + mMtu; + String cfg = "TUNCFG UNQIUE STRING ips:"; + + if (mLocalIP != null) + cfg += mLocalIP.toString(); + if (mLocalIPv6 != null) + cfg += mLocalIPv6.toString(); + + cfg += "routes: " + TextUtils.join("|", mRoutes.getNetworks(true)) + TextUtils.join("|", mRoutesv6.getNetworks(true)); + cfg += "excl. routes:" + TextUtils.join("|", mRoutes.getNetworks(false)) + TextUtils.join("|", mRoutesv6.getNetworks(false)); + cfg += "dns: " + TextUtils.join("|", mDnslist); + cfg += "domain: " + mDomain; + cfg += "mtu: " + mMtu; return cfg; } @@ -455,20 +458,19 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac builder.setMtu(mMtu); - for (CIDRIP route : mRoutes) { + for (NetworkSpace.ipAddress route : mRoutes.getPositiveIPList()) { try { - builder.addRoute(route.mIp, route.len); + builder.addRoute(route.getIPv4Address(), route.networkMask); } catch (IllegalArgumentException ia) { VpnStatus.logError(getString(R.string.route_rejected) + route + " " + ia.getLocalizedMessage()); } } - for (String v6route : mRoutesv6) { + for (NetworkSpace.ipAddress route6 : mRoutesv6.getPositiveIPList()) { try { - String[] v6parts = v6route.split("/"); - builder.addRoute(v6parts[0], Integer.parseInt(v6parts[1])); + builder.addRoute(route6.getIPv6Address(), route6.networkMask); } catch (IllegalArgumentException ia) { - VpnStatus.logError(getString(R.string.route_rejected) + v6route + " " + ia.getLocalizedMessage()); + VpnStatus.logError(getString(R.string.route_rejected) + route6 + " " + ia.getLocalizedMessage()); } } @@ -477,9 +479,10 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac VpnStatus.logInfo(R.string.last_openvpn_tun_config); VpnStatus.logInfo(R.string.local_ip_info, mLocalIP.mIp, mLocalIP.len, mLocalIPv6, mMtu); - VpnStatus.logInfo(R.string.dns_server_info, joinString(mDnslist), mDomain); - VpnStatus.logInfo(R.string.routes_info, joinString(mRoutes)); - VpnStatus.logInfo(R.string.routes_info6, joinString(mRoutesv6)); + VpnStatus.logInfo(R.string.dns_server_info, TextUtils.join(", ", mDnslist), mDomain); + VpnStatus.logInfo(R.string.routes_info_incl, TextUtils.join(", ", mRoutes.getNetworks(true)), TextUtils.join(", ", mRoutesv6.getNetworks(true))); + VpnStatus.logInfo(R.string.routes_info_excl, TextUtils.join(", ", mRoutes.getNetworks(false)),TextUtils.join(", ", mRoutesv6.getNetworks(false))); + VpnStatus.logDebug(R.string.routes_debug, TextUtils.join(", ", mRoutes.getPositiveIPList()), TextUtils.join(", ", mRoutesv6.getPositiveIPList())); String session = mProfile.mName; if (mLocalIP != null && mLocalIPv6 != null) @@ -518,18 +521,6 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac } - // Ugly, but java has no such method - private String joinString(Vector vec) { - String ret = ""; - if (vec.size() > 0) { - ret = vec.get(0).toString(); - for (int i = 1; i < vec.size(); i++) { - ret = ret + ", " + vec.get(i).toString(); - } - } - return ret; - } - public void addDNS(String dns) { mDnslist.add(dns); } @@ -540,8 +531,17 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac } } - public void addRoute(String dest, String mask) { + public void addRoute(String dest, String mask, String gateway, String device) { CIDRIP route = new CIDRIP(dest, mask); + boolean include = isAndroidTunDevice(device); + + NetworkSpace.ipAddress gatewayIP = new NetworkSpace.ipAddress(new CIDRIP(gateway, 32),false); + + NetworkSpace.ipAddress localNet = new NetworkSpace.ipAddress(mLocalIP,true); + if (localNet.containsNet(gatewayIP)) + include =true; + + if (route.len == 32 && !mask.equals("255.255.255.255")) { VpnStatus.logWarning(R.string.route_not_cidr, dest, mask); } @@ -549,11 +549,30 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac if (route.normalise()) VpnStatus.logWarning(R.string.route_not_netip, dest, route.len, route.mIp); - mRoutes.add(route); + mRoutes.addIP(route, include); + } + + public void addRoutev6(String network, String device) { + String[] v6parts = network.split("/"); + boolean included = isAndroidTunDevice(device); + + // Tun is opened after ROUTE6, no device name may be present + + try { + Inet6Address ip = (Inet6Address) InetAddress.getAllByName(v6parts[0])[0]; + int mask = Integer.parseInt(v6parts[1]); + mRoutesv6.addIPv6(ip, mask, included); + + } catch (UnknownHostException e) { + VpnStatus.logException(e); + } + + } - public void addRoutev6(String extra) { - mRoutesv6.add(extra); + private boolean isAndroidTunDevice(String device) { + return device!=null && + (device.startsWith("tun") || "(null)".equals(device) || "vpnservice-tun".equals(device)); } public void setMtu(int mtu) { @@ -657,9 +676,9 @@ public class OpenVpnService extends VpnService implements StateListener, Callbac public String getTunReopenStatus() { String currentConfiguration = getTunConfigString(); - if(currentConfiguration.equals(mLastTunCfg)) + if (currentConfiguration.equals(mLastTunCfg)) return "NOACTION"; - else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) return "OPEN_AFTER_CLOSE"; else return "OPEN_BEFORE_CLOSE"; diff --git a/src/de/blinkt/openvpn/core/VpnStatus.java b/src/de/blinkt/openvpn/core/VpnStatus.java index f2953240..d146aef8 100644 --- a/src/de/blinkt/openvpn/core/VpnStatus.java +++ b/src/de/blinkt/openvpn/core/VpnStatus.java @@ -479,7 +479,12 @@ public class VpnStatus { newLogItem(new LogItem(LogLevel.INFO, resourceId, args)); } - private synchronized static void newLogItem(LogItem logItem) { + public static void logDebug(int resourceId, Object... args) { + newLogItem(new LogItem(LogLevel.DEBUG, resourceId, args)); + } + + + private synchronized static void newLogItem(LogItem logItem) { logbuffer.addLast(logItem); if(logbuffer.size()>MAXLOGENTRIES) logbuffer.removeFirst(); -- cgit v1.2.3