Merge branch 'android-updates'

Fixes the roaming behavior on Android 5+, a linker issue on Android M,
a few bugs, and adds several new advanced options for VPN profile (MTU,
server port, split tunneling).

Also adds methods and a constructor to parse settings_t from a string
instead of a file.

Fixes #782, #847, #865.
This commit is contained in:
Tobias Brunner
2015-07-28 13:55:23 +02:00
38 changed files with 1242 additions and 399 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2012-2014 Tobias Brunner
Copyright (C) 2012-2015 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager
Hochschule fuer Technik Rapperswil
@@ -20,7 +20,7 @@
android:versionCode="26"
android:versionName="1.4.5" >
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="22" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+1 -1
View File
@@ -6,7 +6,7 @@ include $(CLEAR_VARS)
strongswan_USE_BYOD := true
strongswan_CHARON_PLUGINS := android-log openssl fips-prf random nonce pubkey \
pkcs1 pkcs8 pem xcbc hmac socket-default kernel-netlink \
pkcs1 pkcs8 pem xcbc hmac socket-default \
eap-identity eap-mschapv2 eap-md5 eap-gtc eap-tls
ifneq ($(strongswan_USE_BYOD),)
+1
View File
@@ -1,2 +1,3 @@
# select the ABI(s) to build for (see CPU-ARCH-ABIS.html in the NDK docs).
APP_ABI := armeabi x86 mips
APP_PLATFORM := android-19
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -15,6 +15,8 @@
* for more details.
*/
#include <dlfcn.h>
#include "android_jni.h"
#include <library.h>
@@ -25,6 +27,21 @@
*/
static JavaVM *android_jvm;
static struct {
char name[32];
void *handle;
} libs[] = {
{ "libstrongswan.so", NULL },
#ifdef USE_BYOD
{ "libtncif.so", NULL },
{ "libtnccs.so", NULL },
{ "libimcv.so", NULL },
#endif
{ "libhydra.so", NULL },
{ "libcharon.so", NULL },
{ "libipsec.so", NULL },
};
jclass *android_charonvpnservice_class;
jclass *android_charonvpnservice_builder_class;
android_sdk_version_t android_sdk_version;
@@ -79,6 +96,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
JNIEnv *env;
jclass jversion;
jfieldID jsdk_int;
int i;
android_jvm = vm;
@@ -87,6 +105,15 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
return -1;
}
for (i = 0; i < countof(libs); i++)
{
libs[i].handle = dlopen(libs[i].name, RTLD_GLOBAL);
if (!libs[i].handle)
{
return -1;
}
}
androidjni_threadlocal = thread_value_create(attached_thread_cleanup);
android_charonvpnservice_class =
@@ -109,6 +136,16 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved)
*/
void JNI_OnUnload(JavaVM *vm, void *reserved)
{
int i;
androidjni_threadlocal->destroy(androidjni_threadlocal);
for (i = countof(libs) - 1; i >= 0; i--)
{
if (libs[i].handle)
{
dlclose(libs[i].handle);
}
}
}
@@ -54,6 +54,8 @@ typedef enum {
ANDROID_ICE_CREAM_SANDWICH = 14,
ANDROID_ICE_CREAM_SANDWICH_MR1 = 15,
ANDROID_JELLY_BEAN = 16,
ANDROID_JELLY_BEAN_MR1 = 17,
ANDROID_JELLY_BEAN_MR2 = 18,
} android_sdk_version_t;
/**
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2014 Tobias Brunner
* Copyright (C) 2010-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -32,8 +32,6 @@
typedef struct private_android_service_t private_android_service_t;
#define TUN_DEFAULT_MTU 1400
/**
* private data of Android service
*/
@@ -55,24 +53,9 @@ struct private_android_service_t {
ike_sa_t *ike_sa;
/**
* the type of VPN
* configuration setttings
*/
char *type;
/**
* gateway
*/
char *gateway;
/**
* username
*/
char *username;
/**
* password
*/
char *password;
settings_t *settings;
/**
* lock to safely access the TUN device fd
@@ -84,6 +67,11 @@ struct private_android_service_t {
*/
int tunfd;
/**
* MTU of TUN device
*/
int mtu;
/**
* DNS proxy
*/
@@ -191,7 +179,7 @@ static job_requeue_t handle_plain(private_android_service_t *this)
return JOB_REQUEUE_DIRECT;
}
raw = chunk_alloc(TUN_DEFAULT_MTU);
raw = chunk_alloc(this->mtu);
len = read(tunfd, raw.ptr, raw.len);
if (len < 0)
{
@@ -309,7 +297,7 @@ static bool setup_tun_device(private_android_service_t *this,
return FALSE;
}
if (!add_routes(builder, child_sa) ||
!builder->set_mtu(builder, TUN_DEFAULT_MTU))
!builder->set_mtu(builder, this->mtu))
{
return FALSE;
}
@@ -621,6 +609,7 @@ static void add_auth_cfg_pw(private_android_service_t *this,
{
identification_t *user;
auth_cfg_t *auth;
char *username, *password;
auth = auth_cfg_create();
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
@@ -629,12 +618,14 @@ static void add_auth_cfg_pw(private_android_service_t *this,
auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_TTLS);
}
user = identification_create_from_string(this->username);
username = this->settings->get_str(this->settings, "connection.username",
NULL);
password = this->settings->get_str(this->settings, "connection.password",
NULL);
user = identification_create_from_string(username);
auth->add(auth, AUTH_RULE_IDENTITY, user);
this->creds->add_username_password(this->creds, this->username,
this->password);
memwipe(this->password, strlen(this->password));
this->creds->add_username_password(this->creds, username, password);
peer_cfg->add_auth_cfg(peer_cfg, auth, TRUE);
}
@@ -644,6 +635,7 @@ static bool add_auth_cfg_cert(private_android_service_t *this,
certificate_t *cert;
identification_t *id;
auth_cfg_t *auth;
char *type;
cert = this->creds->load_user_certificate(this->creds);
if (!cert)
@@ -651,8 +643,9 @@ static bool add_auth_cfg_cert(private_android_service_t *this,
return FALSE;
}
type = this->settings->get_str(this->settings, "connection.type", NULL);
auth = auth_cfg_create();
if (strpfx("ikev2-eap-tls", this->type))
if (strpfx("ikev2-eap-tls", type))
{
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP);
auth->add(auth, AUTH_RULE_EAP_TYPE, EAP_TLS);
@@ -687,11 +680,15 @@ static job_requeue_t initiate(private_android_service_t *this)
.jitter = 300 /* 5min */
}
};
char *type, *server;
int port;
server = this->settings->get_str(this->settings, "connection.server", NULL);
port = this->settings->get_int(this->settings, "connection.port",
IKEV2_UDP_PORT);
ike_cfg = ike_cfg_create(IKEV2, TRUE, TRUE, "0.0.0.0",
charon->socket->get_port(charon->socket, FALSE),
this->gateway, IKEV2_UDP_PORT,
FRAGMENTATION_YES, 0);
server, port, FRAGMENTATION_YES, 0);
ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
ike_cfg->add_proposal(ike_cfg, proposal_create_default_aead(PROTO_IKE));
@@ -705,10 +702,11 @@ static job_requeue_t initiate(private_android_service_t *this)
peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET));
peer_cfg->add_virtual_ip(peer_cfg, host_create_any(AF_INET6));
type = this->settings->get_str(this->settings, "connection.type", NULL);
/* local auth config */
if (streq("ikev2-cert", this->type) ||
streq("ikev2-cert-eap", this->type) ||
streq("ikev2-eap-tls", this->type))
if (streq("ikev2-cert", type) ||
streq("ikev2-cert-eap", type) ||
streq("ikev2-eap-tls", type))
{
if (!add_auth_cfg_cert(this, peer_cfg))
{
@@ -718,16 +716,16 @@ static job_requeue_t initiate(private_android_service_t *this)
return JOB_REQUEUE_NONE;
}
}
if (streq("ikev2-eap", this->type) ||
streq("ikev2-cert-eap", this->type) ||
streq("ikev2-byod-eap", this->type))
if (streq("ikev2-eap", type) ||
streq("ikev2-cert-eap", type) ||
streq("ikev2-byod-eap", type))
{
add_auth_cfg_pw(this, peer_cfg, strpfx(this->type, "ikev2-byod"));
add_auth_cfg_pw(this, peer_cfg, strpfx(type, "ikev2-byod"));
}
/* remote auth config */
auth = auth_cfg_create();
gateway = identification_create_from_string(this->gateway);
gateway = identification_create_from_string(server);
auth->add(auth, AUTH_RULE_IDENTITY, gateway);
auth->add(auth, AUTH_RULE_IDENTITY_LOOSE, TRUE);
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
@@ -806,23 +804,15 @@ METHOD(android_service_t, destroy, void,
close_tun_device(this);
this->dns_proxy->destroy(this->dns_proxy);
this->lock->destroy(this->lock);
free(this->type);
free(this->gateway);
free(this->username);
if (this->password)
{
memwipe(this->password, strlen(this->password));
free(this->password);
}
this->settings->destroy(this->settings);
free(this);
}
/**
* See header
*/
android_service_t *android_service_create(android_creds_t *creds, char *type,
char *gateway, char *username,
char *password)
android_service_t *android_service_create(android_creds_t *creds,
settings_t *settings)
{
private_android_service_t *this;
@@ -840,15 +830,14 @@ android_service_t *android_service_create(android_creds_t *creds, char *type,
},
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
.dns_proxy = android_dns_proxy_create(),
.username = username,
.password = password,
.gateway = gateway,
.settings = settings,
.creds = creds,
.type = type,
.tunfd = -1,
.mtu = settings->get_int(settings, "global.mtu", ANDROID_DEFAULT_MTU),
);
/* only allow queries for the VPN gateway */
this->dns_proxy->add_hostname(this->dns_proxy, gateway);
this->dns_proxy->add_hostname(this->dns_proxy,
this->settings->get_str(this->settings, "connection.server", NULL));
charon->bus->add_listener(charon->bus, &this->public.listener);
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2012 Tobias Brunner
* Copyright (C) 2010-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -52,13 +52,9 @@ struct android_service_t {
* new IKE SA.
*
* @param creds Android specific credential set
* @param type VPN type (see VpnType.java)
* @param gateway gateway address
* @param username user name (local identity)
* @param password password (if any)
* @param settings configuration settings (gets adopted)
*/
android_service_t *android_service_create(android_creds_t *creds, char *type,
char *gateway, char *username,
char *password);
android_service_t *android_service_create(android_creds_t *creds,
settings_t *settings);
#endif /** ANDROID_SERVICE_H_ @}*/
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -44,7 +44,6 @@
#define ANDROID_RETRASNMIT_TRIES 3
#define ANDROID_RETRANSMIT_TIMEOUT 2.0
#define ANDROID_RETRANSMIT_BASE 1.4
#define ANDROID_FRAGMENT_SIZE 1400
typedef struct private_charonservice_t private_charonservice_t;
@@ -83,11 +82,6 @@ struct private_charonservice_t {
*/
network_manager_t *network_manager;
/**
* Handle network events
*/
android_net_t *net_handler;
/**
* CharonVpnService reference
*/
@@ -400,18 +394,27 @@ METHOD(charonservice_t, get_network_manager, network_manager_t*,
/**
* Initiate a new connection
*
* @param gateway gateway address (gets owned)
* @param username username (gets owned)
* @param password password (gets owned)
* @param settings configuration settings (gets owned)
*/
static void initiate(char *type, char *gateway, char *username, char *password)
static void initiate(settings_t *settings)
{
private_charonservice_t *this = (private_charonservice_t*)charonservice;
lib->settings->set_str(lib->settings,
"charon.plugins.tnc-imc.preferred_language",
settings->get_str(settings, "global.language", "en"));
/* this is actually the size of the complete IKE/IP packet, so if the MTU
* for the TUN devices has to be reduced to pass traffic the IKE packets
* will be a bit smaller than necessary as there is no IPsec overhead like
* for the tunneled traffic (but compensating that seems like overkill) */
lib->settings->set_int(lib->settings,
"charon.fragment_size",
settings->get_int(settings, "global.mtu",
ANDROID_DEFAULT_MTU));
this->creds->clear(this->creds);
DESTROY_IF(this->service);
this->service = android_service_create(this->creds, type, gateway,
username, password);
this->service = android_service_create(this->creds, settings);
}
/**
@@ -423,14 +426,12 @@ static bool charonservice_register(plugin_t *plugin, plugin_feature_t *feature,
private_charonservice_t *this = (private_charonservice_t*)charonservice;
if (reg)
{
this->net_handler = android_net_create();
lib->credmgr->add_set(lib->credmgr, &this->creds->set);
charon->attributes->add_handler(charon->attributes,
&this->attr->handler);
}
else
{
this->net_handler->destroy(this->net_handler);
lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
charon->attributes->remove_handler(charon->attributes,
&this->attr->handler);
@@ -466,8 +467,8 @@ static void set_options(char *logfile)
"charon.retransmit_timeout", ANDROID_RETRANSMIT_TIMEOUT);
lib->settings->set_double(lib->settings,
"charon.retransmit_base", ANDROID_RETRANSMIT_BASE);
lib->settings->set_int(lib->settings,
"charon.fragment_size", ANDROID_FRAGMENT_SIZE);
lib->settings->set_bool(lib->settings,
"charon.initiator_only", TRUE);
lib->settings->set_bool(lib->settings,
"charon.close_ike_on_child_failure", TRUE);
/* setting the source address breaks the VpnService.protect() function which
@@ -483,19 +484,6 @@ static void set_options(char *logfile)
* so lets disable IPv6 for now to avoid issues with dual-stack gateways */
lib->settings->set_bool(lib->settings,
"charon.plugins.socket-default.use_ipv6", FALSE);
/* don't install virtual IPs via kernel-netlink */
lib->settings->set_bool(lib->settings,
"charon.install_virtual_ip", FALSE);
/* kernel-netlink should not trigger roam events, we use Android's
* ConnectivityManager for that, much less noise */
lib->settings->set_bool(lib->settings,
"charon.plugins.kernel-netlink.roam_events", FALSE);
/* ignore tun devices (it's mostly tun0 but it may already be taken, ignore
* some others too), also ignore lo as a default route points to it when
* no connectivity is available */
lib->settings->set_str(lib->settings,
"charon.interfaces_ignore", "lo, tun0, tun1, tun2, tun3, "
"tun4");
#ifdef USE_BYOD
lib->settings->set_str(lib->settings,
@@ -519,6 +507,8 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder,
static plugin_feature_t features[] = {
PLUGIN_CALLBACK(kernel_ipsec_register, kernel_android_ipsec_create),
PLUGIN_PROVIDE(CUSTOM, "kernel-ipsec"),
PLUGIN_CALLBACK(kernel_net_register, kernel_android_net_create),
PLUGIN_PROVIDE(CUSTOM, "kernel-net"),
PLUGIN_CALLBACK(charonservice_register, NULL),
PLUGIN_PROVIDE(CUSTOM, "android-backend"),
PLUGIN_DEPENDS(CUSTOM, "libcharon"),
@@ -705,14 +695,12 @@ JNI_METHOD(CharonVpnService, deinitializeCharon, void)
* Initiate SA
*/
JNI_METHOD(CharonVpnService, initiate, void,
jstring jtype, jstring jgateway, jstring jusername, jstring jpassword)
jstring jconfig)
{
char *type, *gateway, *username, *password;
settings_t *settings;
char *config;
type = androidjni_convert_jstring(env, jtype);
gateway = androidjni_convert_jstring(env, jgateway);
username = androidjni_convert_jstring(env, jusername);
password = androidjni_convert_jstring(env, jpassword);
initiate(type, gateway, username, password);
config = androidjni_convert_jstring(env, jconfig);
settings = settings_create_string(config);
initiate(settings);
}
@@ -44,6 +44,11 @@ typedef enum android_vpn_state_t android_vpn_state_t;
typedef enum android_imc_state_t android_imc_state_t;
typedef struct charonservice_t charonservice_t;
/**
* Default value for the MTU of TUN device and the size of IKE fragments
*/
#define ANDROID_DEFAULT_MTU 1400
/**
* VPN status codes. As defined in CharonVpnService.java
*/
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -11,9 +11,14 @@
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include "android_net.h"
#include "../android_jni.h"
#include "../charonservice.h"
#include <hydra.h>
#include <processing/jobs/callback_job.h>
@@ -21,6 +26,7 @@
/** delay before firing roam events (ms) */
#define ROAM_DELAY 100
#define ROAM_DELAY_RECHECK 1000
typedef struct private_android_net_t private_android_net_t;
@@ -29,7 +35,7 @@ struct private_android_net_t {
/**
* Public kernel interface
*/
android_net_t public;
kernel_net_t public;
/**
* Reference to NetworkManager object
@@ -37,14 +43,29 @@ struct private_android_net_t {
network_manager_t *network_manager;
/**
* earliest time of the next roam event
* Earliest time of the next roam event
*/
timeval_t next_roam;
/**
* mutex to check and update roam event time
* Mutex to check and update roam event time, and other private members
*/
mutex_t *mutex;
/**
* List of virtual IPs
*/
linked_list_t *vips;
/**
* Socket used to determine source address
*/
int socket_v4;
/**
* Whether the device is currently connected
*/
bool connected;
};
/**
@@ -69,6 +90,7 @@ static void connectivity_cb(private_android_net_t *this,
time_monotonic(&now);
this->mutex->lock(this->mutex);
this->connected = !disconnected;
if (!timercmp(&now, &this->next_roam, >))
{
this->mutex->unlock(this->mutex);
@@ -83,32 +105,210 @@ static void connectivity_cb(private_android_net_t *this,
lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY);
}
METHOD(android_net_t, destroy, void,
METHOD(kernel_net_t, get_source_addr, host_t*,
private_android_net_t *this, host_t *dest, host_t *src)
{
union {
struct sockaddr sockaddr;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} addr;
socklen_t addrlen;
timeval_t now;
job_t *job;
addrlen = *dest->get_sockaddr_len(dest);
addr.sockaddr.sa_family = AF_UNSPEC;
if (connect(this->socket_v4, &addr.sockaddr, addrlen) < 0)
{
DBG1(DBG_KNL, "failed to disconnect socket: %s", strerror(errno));
return NULL;
}
if (android_sdk_version <= ANDROID_JELLY_BEAN_MR2)
{ /* this seems to help avoiding the VIP, unless there is no connectivity
* at all */
charonservice->bypass_socket(charonservice, -1, 0);
}
if (connect(this->socket_v4, dest->get_sockaddr(dest), addrlen) < 0)
{
/* don't report an error if we are not connected (ENETUNREACH) */
if (errno != ENETUNREACH)
{
DBG1(DBG_KNL, "failed to connect socket: %s", strerror(errno));
}
else
{
time_monotonic(&now);
this->mutex->lock(this->mutex);
if (this->connected && timercmp(&now, &this->next_roam, >))
{ /* we were not able to find a source address but reportedly are
* connected, trigger a recheck in case an IP address appears
* delayed but the callback is not triggered again */
timeval_add_ms(&now, ROAM_DELAY_RECHECK);
this->next_roam = now;
this->mutex->unlock(this->mutex);
job = (job_t*)callback_job_create((callback_job_cb_t)roam_event,
NULL, NULL, NULL);
lib->scheduler->schedule_job_ms(lib->scheduler, job,
ROAM_DELAY_RECHECK);
}
else
{
this->mutex->unlock(this->mutex);
}
}
return NULL;
}
if (getsockname(this->socket_v4, &addr.sockaddr, &addrlen) < 0)
{
DBG1(DBG_KNL, "failed to determine src address: %s", strerror(errno));
return NULL;
}
return host_create_from_sockaddr((sockaddr_t*)&addr);
}
METHOD(kernel_net_t, get_source_addr_old, host_t*,
private_android_net_t *this, host_t *dest, host_t *src)
{
host_t *host;
/* on older Android versions we might get the virtual IP back because
* the protect() implementation there and connect() don't properly work
* together, on newer releases (using fwmarks) that's not a problem */
host = get_source_addr(this, dest, src);
if (host)
{
this->mutex->lock(this->mutex);
if (this->vips->find_first(this->vips, (void*)host->ip_equals,
NULL, host) == SUCCESS)
{
host->destroy(host);
host = NULL;
}
this->mutex->unlock(this->mutex);
}
return host;
}
METHOD(kernel_net_t, get_nexthop, host_t*,
private_android_net_t *this, host_t *dest, int prefix, host_t *src)
{
return NULL;
}
METHOD(kernel_net_t, get_interface, bool,
private_android_net_t *this, host_t *host, char **name)
{
if (name)
{ /* the actual name does not matter in our case */
*name = strdup("strongswan");
}
return TRUE;
}
METHOD(kernel_net_t, create_address_enumerator, enumerator_t*,
private_android_net_t *this, kernel_address_type_t which)
{
/* return virtual IPs if requested, nothing otherwise */
if (which & ADDR_TYPE_VIRTUAL)
{
this->mutex->lock(this->mutex);
return enumerator_create_cleaner(
this->vips->create_enumerator(this->vips),
(void*)this->mutex->unlock, this->mutex);
}
return enumerator_create_empty();
}
METHOD(kernel_net_t, add_ip, status_t,
private_android_net_t *this, host_t *virtual_ip, int prefix, char *iface)
{
this->mutex->lock(this->mutex);
this->vips->insert_last(this->vips, virtual_ip->clone(virtual_ip));
this->mutex->unlock(this->mutex);
return SUCCESS;
}
METHOD(kernel_net_t, del_ip, status_t,
private_android_net_t *this, host_t *virtual_ip, int prefix, bool wait)
{
host_t *vip;
this->mutex->lock(this->mutex);
if (this->vips->find_first(this->vips, (void*)virtual_ip->ip_equals,
(void**)&vip, virtual_ip) == SUCCESS)
{
this->vips->remove(this->vips, vip, NULL);
vip->destroy(vip);
}
this->mutex->unlock(this->mutex);
return SUCCESS;
}
METHOD(kernel_net_t, add_route, status_t,
private_android_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
host_t *gateway, host_t *src_ip, char *if_name)
{
return NOT_SUPPORTED;
}
METHOD(kernel_net_t, del_route, status_t,
private_android_net_t *this, chunk_t dst_net, u_int8_t prefixlen,
host_t *gateway, host_t *src_ip, char *if_name)
{
return NOT_SUPPORTED;
}
METHOD(kernel_net_t, destroy, void,
private_android_net_t *this)
{
this->network_manager->remove_connectivity_cb(this->network_manager,
(void*)connectivity_cb);
this->mutex->destroy(this->mutex);
this->vips->destroy(this->vips);
close(this->socket_v4);
free(this);
}
/*
* Described in header.
*/
android_net_t *android_net_create()
kernel_net_t *kernel_android_net_create()
{
private_android_net_t *this;
INIT(this,
.public = {
.get_source_addr = _get_source_addr,
.get_nexthop = _get_nexthop,
.get_interface = _get_interface,
.create_address_enumerator = _create_address_enumerator,
.add_ip = _add_ip,
.del_ip = _del_ip,
.add_route = _add_route,
.del_route = _del_route,
.destroy = _destroy,
},
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
.vips = linked_list_create(),
.network_manager = charonservice->get_network_manager(charonservice),
);
timerclear(&this->next_roam);
this->network_manager->add_connectivity_cb(this->network_manager,
(void*)connectivity_cb, this);
if (android_sdk_version <= ANDROID_JELLY_BEAN_MR2)
{
this->public.get_source_addr = _get_source_addr_old;
}
this->socket_v4 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (this->socket_v4 < 0)
{
DBG1(DBG_KNL, "failed to create socket to lookup src addresses: %s",
strerror(errno));
}
charonservice->bypass_socket(charonservice, this->socket_v4, AF_INET);
this->mutex->lock(this->mutex);
this->network_manager->add_connectivity_cb(
this->network_manager, (void*)connectivity_cb, this);
this->connected = this->network_manager->is_connected(this->network_manager);
this->mutex->unlock(this->mutex);
return &this->public;
};
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -22,25 +22,14 @@
#define ANDROID_NET_H_
#include <library.h>
typedef struct android_net_t android_net_t;
#include <kernel/kernel_net.h>
/**
* Handle connectivity events from NetworkManager
*/
struct android_net_t {
/**
* Destroy an android_net_t instance.
*/
void (*destroy)(android_net_t *this);
};
/**
* Create an android_net_t instance.
* Create an Android-specific kernel_net_t instance.
*
* @return android_net_t instance
* @return kernel_net_t instance
*/
android_net_t *android_net_create();
kernel_net_t *kernel_android_net_create();
#endif /** ANDROID_NET_H_ @}*/
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -90,8 +90,8 @@ METHOD(network_manager_t, add_connectivity_cb, void,
this->connectivity_cb.cb = cb;
this->connectivity_cb.data = data;
}
androidjni_detach_thread();
}
androidjni_detach_thread();
}
this->mutex->unlock(this->mutex);
}
@@ -132,6 +132,28 @@ METHOD(network_manager_t, remove_connectivity_cb, void,
this->mutex->unlock(this->mutex);
}
METHOD(network_manager_t, is_connected, bool,
private_network_manager_t *this)
{
JNIEnv *env;
jmethodID method_id;
bool connected = FALSE;
androidjni_attach_thread(&env);
method_id = (*env)->GetMethodID(env, this->cls, "isConnected", "()Z");
if (!method_id)
{
androidjni_exception_occurred(env);
}
else
{
connected = (*env)->CallBooleanMethod(env, this->obj, method_id);
connected = !androidjni_exception_occurred(env) && connected;
}
androidjni_detach_thread();
return connected;
}
METHOD(network_manager_t, destroy, void,
private_network_manager_t *this)
{
@@ -174,6 +196,7 @@ network_manager_t *network_manager_create(jobject context)
.public = {
.add_connectivity_cb = _add_connectivity_cb,
.remove_connectivity_cb = _remove_connectivity_cb,
.is_connected = _is_connected,
.destroy = _destroy,
},
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -65,6 +65,13 @@ struct network_manager_t {
void (*remove_connectivity_cb)(network_manager_t *this,
connectivity_cb_t cb);
/**
* Check whether we currently have connectivity
*
* @return TRUE if currently connected
*/
bool (*is_connected)(network_manager_t *this);
/**
* Destroy a network_manager_t instance
*/
+1 -1
View File
@@ -8,4 +8,4 @@
# project structure.
# Project target.
target=android-19
target=android-21
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2012 Tobias Brunner
Copyright (C) 2012-2015 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager
Hochschule fuer Technik Rapperswil
@@ -139,6 +139,65 @@
android:id="@+id/select_certificate"
layout="@layout/two_line_button" />
<CheckBox
android:id="@+id/show_advanced"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/profile_show_advanced_label" />
<LinearLayout
android:id="@+id/advanced_settings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/profile_mtu_label" />
<EditText
android:id="@+id/mtu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:inputType="number|textNoSuggestions"
android:hint="@string/profile_use_default_hint" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/profile_port_label" />
<EditText
android:id="@+id/port"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:inputType="number|textNoSuggestions"
android:hint="@string/profile_use_default_hint" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/profile_split_tunneling_label" />
<CheckBox
android:id="@+id/split_tunneling_v4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/profile_split_tunnelingv4_title" />
<CheckBox
android:id="@+id/split_tunneling_v6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/profile_split_tunnelingv6_title" />
</LinearLayout>
</LinearLayout>
</ScrollView>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2012-2013 Tobias Brunner
Copyright (C) 2012-2015 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager
Hochschule fuer Technik Rapperswil
@@ -63,11 +63,19 @@
<string name="profile_ca_auto_label">Automatisch wählen</string>
<string name="profile_ca_select_certificate_label">CA-Zertifikat auswählen</string>
<string name="profile_ca_select_certificate">Wählen Sie ein bestimmtes CA-Zertifikat</string>
<string name="profile_show_advanced_label">Erweiterte Einstellungen anzeigen</string>
<string name="profile_mtu_label">MTU:</string>
<string name="profile_port_label">Server Port:</string>
<string name="profile_use_default_hint">(Standardwert verwenden)</string>
<string name="profile_split_tunneling_label">Split-Tunneling:</string>
<string name="profile_split_tunnelingv4_title">Blockiere IPv4 Verkehr der nicht für das VPN bestimmt ist</string>
<string name="profile_split_tunnelingv6_title">Blockiere IPv6 Verkehr der nicht für das VPN bestimmt ist</string>
<!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Bitte geben Sie hier die Gateway-Adresse ein</string>
<string name="alert_text_no_input_username">Bitte geben Sie hier Ihren Benutzernamen ein</string>
<string name="alert_text_nocertfound_title">Kein CA-Zertifikat ausgewählt</string>
<string name="alert_text_nocertfound">Bitte wählen Sie eines aus oder aktivieren Sie <i>Automatisch wählen</i></string>
<string name="alert_text_out_of_range">Bitte geben Sie eine Nummer von %1$d - %2$d ein</string>
<string name="tnc_notice_title">EAP-TNC kann Ihre Privatsphäre beeinträchtigen</string>
<string name="tnc_notice_subtitle">Gerätedaten werden an den Gateway-Betreiber gesendet</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) erlaubt Gateway-Betreibern den Gesundheitszustand von Endgeräten zu prüfen.&lt;/p>&lt;p>Dazu kann der Betreiber Daten verlangen, wie etwa eine eindeutige Identifikationsnummer, eine Liste der installierten Pakete, Systemeinstellungen oder kryptografische Prüfsummen von Dateien.&lt;/p>&lt;b>Solche Daten werden nur übermittelt nachdem die Identität des Gateways geprüft wurde.&lt;/b></string>
@@ -63,11 +63,19 @@
<string name="profile_ca_auto_label">Wybierz automatycznie</string>
<string name="profile_ca_select_certificate_label">Wybierz certyfikat CA</string>
<string name="profile_ca_select_certificate">Wybierz określony certyfikat CA</string>
<string name="profile_show_advanced_label">Show advanced settings</string>
<string name="profile_mtu_label">MTU:</string>
<string name="profile_port_label">Server port:</string>
<string name="profile_use_default_hint">(use default)</string>
<string name="profile_split_tunneling_label">Split tunneling:</string>
<string name="profile_split_tunnelingv4_title">Block IPv4 traffic not destined for the VPN</string>
<string name="profile_split_tunnelingv6_title">Block IPv6 traffic not destined for the VPN</string>
<!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Wprowadź adres bramki</string>
<string name="alert_text_no_input_username">Wprowadź swoją nazwę użytkownika</string>
<string name="alert_text_nocertfound_title">Nie wybrano żadnego certyfikatu CA</string>
<string name="alert_text_nocertfound">Wybierz lub uaktywnij jeden <i>Wybierz automatycznie</i></string>
<string name="alert_text_out_of_range">Please enter a number in the range from %1$d - %2$d</string>
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
<string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
@@ -60,11 +60,19 @@
<string name="profile_ca_auto_label">Выбрать автоматически</string>
<string name="profile_ca_select_certificate_label">Выбрать сертификат CA</string>
<string name="profile_ca_select_certificate">Выбрать CA сертификат</string>
<string name="profile_show_advanced_label">Show advanced settings</string>
<string name="profile_mtu_label">MTU:</string>
<string name="profile_port_label">Server port:</string>
<string name="profile_use_default_hint">(use default)</string>
<string name="profile_split_tunneling_label">Split tunneling:</string>
<string name="profile_split_tunnelingv4_title">Block IPv4 traffic not destined for the VPN</string>
<string name="profile_split_tunnelingv6_title">Block IPv6 traffic not destined for the VPN</string>
<!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Пожалуйста введите адрес шлюза</string>
<string name="alert_text_no_input_username">Пожалуйста введите имя пользователя</string>
<string name="alert_text_nocertfound_title">Не выбран сертификат CA</string>
<string name="alert_text_nocertfound">Пожалуйста выберите один <i>Выбрать автоматически</i></string>
<string name="alert_text_out_of_range">Please enter a number in the range from %1$d - %2$d</string>
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
<string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
@@ -61,11 +61,19 @@
<string name="profile_ca_auto_label">Вибрати автоматично</string>
<string name="profile_ca_select_certificate_label">Вибрати сертифікат CA</string>
<string name="profile_ca_select_certificate">Вибрати спеціальний сертифікат CA</string>
<string name="profile_show_advanced_label">Show advanced settings</string>
<string name="profile_mtu_label">MTU:</string>
<string name="profile_port_label">Server port:</string>
<string name="profile_use_default_hint">(use default)</string>
<string name="profile_split_tunneling_label">Split tunneling:</string>
<string name="profile_split_tunnelingv4_title">Block IPv4 traffic not destined for the VPN</string>
<string name="profile_split_tunnelingv6_title">Block IPv6 traffic not destined for the VPN</string>
<!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Введіть адресу шлюза тут</string>
<string name="alert_text_no_input_username">Введіть ім\'я користувача тут</string>
<string name="alert_text_nocertfound_title">Не вибрано сертифікат CA</string>
<string name="alert_text_nocertfound">Будь ласка виберіть один <i>Вибрати автоматично</i></string>
<string name="alert_text_out_of_range">Please enter a number in the range from %1$d - %2$d</string>
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
<string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
+9 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2012-2013 Tobias Brunner
Copyright (C) 2012-2015 Tobias Brunner
Copyright (C) 2012 Giuliano Grassi
Copyright (C) 2012 Ralf Sager
Hochschule fuer Technik Rapperswil
@@ -63,11 +63,19 @@
<string name="profile_ca_auto_label">Select automatically</string>
<string name="profile_ca_select_certificate_label">Select CA certificate</string>
<string name="profile_ca_select_certificate">Select a specific CA certificate</string>
<string name="profile_show_advanced_label">Show advanced settings</string>
<string name="profile_mtu_label">MTU:</string>
<string name="profile_port_label">Server port:</string>
<string name="profile_use_default_hint">(use default)</string>
<string name="profile_split_tunneling_label">Split tunneling:</string>
<string name="profile_split_tunnelingv4_title">Block IPv4 traffic not destined for the VPN</string>
<string name="profile_split_tunnelingv6_title">Block IPv6 traffic not destined for the VPN</string>
<!-- Warnings/Notifications in the details view -->
<string name="alert_text_no_input_gateway">Please enter the gateway address here</string>
<string name="alert_text_no_input_username">Please enter your username here</string>
<string name="alert_text_nocertfound_title">No CA certificate selected</string>
<string name="alert_text_nocertfound">Please select one or activate <i>Select automatically</i></string>
<string name="alert_text_out_of_range">Please enter a number in the range from %1$d - %2$d</string>
<string name="tnc_notice_title">EAP-TNC may affect your privacy</string>
<string name="tnc_notice_subtitle">Device data is sent to the gateway operator</string>
<string name="tnc_notice_details">&lt;p>Trusted Network Connect (TNC) allows gateway operators to assess the health of a client device.&lt;/p>&lt;p>For that purpose the gateway operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.&lt;/p>&lt;b>Any data will be sent only after verifying the gateway\'s identity.&lt;/b></string>
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -17,9 +17,15 @@
package org.strongswan.android.data;
public class VpnProfile implements Cloneable
{
/* While storing this as EnumSet would be nicer this simplifies storing it in a database */
public static final int SPLIT_TUNNELING_BLOCK_IPV4 = 1;
public static final int SPLIT_TUNNELING_BLOCK_IPV6 = 2;
private String mName, mGateway, mUsername, mPassword, mCertificate, mUserCertificate;
private Integer mMTU, mPort, mSplitTunneling;
private VpnType mVpnType;
private long mId = -1;
@@ -103,6 +109,36 @@ public class VpnProfile implements Cloneable
this.mUserCertificate = alias;
}
public Integer getMTU()
{
return mMTU;
}
public void setMTU(Integer mtu)
{
this.mMTU = mtu;
}
public Integer getPort()
{
return mPort;
}
public void setPort(Integer port)
{
this.mPort = port;
}
public Integer getSplitTunneling()
{
return mSplitTunneling;
}
public void setSplitTunneling(Integer splitTunneling)
{
this.mSplitTunneling = splitTunneling;
}
@Override
public String toString()
{
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012 Tobias Brunner
* Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -40,6 +40,9 @@ public class VpnProfileDataSource
public static final String KEY_PASSWORD = "password";
public static final String KEY_CERTIFICATE = "certificate";
public static final String KEY_USER_CERTIFICATE = "user_certificate";
public static final String KEY_MTU = "mtu";
public static final String KEY_PORT = "port";
public static final String KEY_SPLIT_TUNNELING = "split_tunneling";
private DatabaseHelper mDbHelper;
private SQLiteDatabase mDatabase;
@@ -48,7 +51,7 @@ public class VpnProfileDataSource
private static final String DATABASE_NAME = "strongswan.db";
private static final String TABLE_VPNPROFILE = "vpnprofile";
private static final int DATABASE_VERSION = 4;
private static final int DATABASE_VERSION = 7;
public static final String DATABASE_CREATE =
"CREATE TABLE " + TABLE_VPNPROFILE + " (" +
@@ -59,7 +62,10 @@ public class VpnProfileDataSource
KEY_USERNAME + " TEXT," +
KEY_PASSWORD + " TEXT," +
KEY_CERTIFICATE + " TEXT," +
KEY_USER_CERTIFICATE + " TEXT" +
KEY_USER_CERTIFICATE + " TEXT," +
KEY_MTU + " INTEGER," +
KEY_PORT + " INTEGER," +
KEY_SPLIT_TUNNELING + " INTEGER" +
");";
private static final String[] ALL_COLUMNS = new String[] {
KEY_ID,
@@ -70,6 +76,9 @@ public class VpnProfileDataSource
KEY_PASSWORD,
KEY_CERTIFICATE,
KEY_USER_CERTIFICATE,
KEY_MTU,
KEY_PORT,
KEY_SPLIT_TUNNELING,
};
private static class DatabaseHelper extends SQLiteOpenHelper
@@ -104,6 +113,21 @@ public class VpnProfileDataSource
{ /* remove NOT NULL constraint from username column */
updateColumns(db);
}
if (oldVersion < 5)
{
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_MTU +
" INTEGER;");
}
if (oldVersion < 6)
{
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_PORT +
" INTEGER;");
}
if (oldVersion < 7)
{
db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_SPLIT_TUNNELING +
" INTEGER;");
}
}
private void updateColumns(SQLiteDatabase db)
@@ -255,6 +279,9 @@ public class VpnProfileDataSource
profile.setPassword(cursor.getString(cursor.getColumnIndex(KEY_PASSWORD)));
profile.setCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_CERTIFICATE)));
profile.setUserCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_USER_CERTIFICATE)));
profile.setMTU(getInt(cursor, cursor.getColumnIndex(KEY_MTU)));
profile.setPort(getInt(cursor, cursor.getColumnIndex(KEY_PORT)));
profile.setSplitTunneling(getInt(cursor, cursor.getColumnIndex(KEY_SPLIT_TUNNELING)));
return profile;
}
@@ -268,6 +295,14 @@ public class VpnProfileDataSource
values.put(KEY_PASSWORD, profile.getPassword());
values.put(KEY_CERTIFICATE, profile.getCertificateAlias());
values.put(KEY_USER_CERTIFICATE, profile.getUserCertificateAlias());
values.put(KEY_MTU, profile.getMTU());
values.put(KEY_PORT, profile.getPort());
values.put(KEY_SPLIT_TUNNELING, profile.getSplitTunneling());
return values;
}
private Integer getInt(Cursor cursor, int columnIndex)
{
return cursor.isNull(columnIndex) ? null : cursor.getInt(columnIndex);
}
}
@@ -24,7 +24,7 @@ public enum VpnType
IKEV2_CERT("ikev2-cert", EnumSet.of(VpnTypeFeature.CERTIFICATE)),
IKEV2_CERT_EAP("ikev2-cert-eap", EnumSet.of(VpnTypeFeature.USER_PASS, VpnTypeFeature.CERTIFICATE)),
IKEV2_EAP_TLS("ikev2-eap-tls", EnumSet.of(VpnTypeFeature.CERTIFICATE)),
IKEV2_BYOD_EAP("ikev2-byod-eap", EnumSet.of(VpnTypeFeature.USER_PASS, VpnTypeFeature.CERTIFICATE, VpnTypeFeature.BYOD));
IKEV2_BYOD_EAP("ikev2-byod-eap", EnumSet.of(VpnTypeFeature.USER_PASS, VpnTypeFeature.BYOD));
/**
* Features of a VPN type.
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2012-2015 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -18,11 +18,16 @@
package org.strongswan.android.logic;
import java.io.File;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.strongswan.android.data.VpnProfile;
import org.strongswan.android.data.VpnProfileDataSource;
@@ -32,7 +37,9 @@ import org.strongswan.android.logic.VpnStateService.State;
import org.strongswan.android.logic.imc.ImcState;
import org.strongswan.android.logic.imc.RemediationInstruction;
import org.strongswan.android.ui.MainActivity;
import org.strongswan.android.utils.SettingsWriter;
import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
@@ -40,11 +47,13 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.VpnService;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.security.KeyChain;
import android.security.KeyChainException;
import android.system.OsConstants;
import android.util.Log;
public class CharonVpnService extends VpnService implements Runnable
@@ -211,13 +220,19 @@ public class CharonVpnService extends VpnService implements Runnable
startConnection(mCurrentProfile);
mIsDisconnecting = false;
BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName());
BuilderAdapter builder = new BuilderAdapter(mCurrentProfile.getName(), mCurrentProfile.getSplitTunneling());
if (initializeCharon(builder, mLogFile, mCurrentProfile.getVpnType().has(VpnTypeFeature.BYOD)))
{
Log.i(TAG, "charon started");
initiate(mCurrentProfile.getVpnType().getIdentifier(),
mCurrentProfile.getGateway(), mCurrentProfile.getUsername(),
mCurrentProfile.getPassword());
SettingsWriter writer = new SettingsWriter();
writer.setValue("global.language", Locale.getDefault().getLanguage());
writer.setValue("global.mtu", mCurrentProfile.getMTU());
writer.setValue("connection.type", mCurrentProfile.getVpnType().getIdentifier());
writer.setValue("connection.server", mCurrentProfile.getGateway());
writer.setValue("connection.port", mCurrentProfile.getPort());
writer.setValue("connection.username", mCurrentProfile.getUsername());
writer.setValue("connection.password", mCurrentProfile.getPassword());
initiate(writer.serialize());
}
else
{
@@ -497,7 +512,6 @@ public class CharonVpnService extends VpnService implements Runnable
private PrivateKey getUserKey() throws KeyChainException, InterruptedException
{
return KeyChain.getPrivateKey(getApplicationContext(), mCurrentUserCertificateAlias);
}
/**
@@ -518,7 +532,7 @@ public class CharonVpnService extends VpnService implements Runnable
/**
* Initiate VPN, provided by libandroidbridge.so
*/
public native void initiate(String type, String gateway, String username, String password);
public native void initiate(String config);
/**
* Adapter for VpnService.Builder which is used to access it safely via JNI.
@@ -527,15 +541,17 @@ public class CharonVpnService extends VpnService implements Runnable
public class BuilderAdapter
{
private final String mName;
private final Integer mSplitTunneling;
private VpnService.Builder mBuilder;
private BuilderCache mCache;
private BuilderCache mEstablishedCache;
public BuilderAdapter(String name)
public BuilderAdapter(String name, Integer splitTunneling)
{
mName = name;
mSplitTunneling = splitTunneling;
mBuilder = createBuilder(name);
mCache = new BuilderCache();
mCache = new BuilderCache(mSplitTunneling);
}
private VpnService.Builder createBuilder(String name)
@@ -557,7 +573,6 @@ public class CharonVpnService extends VpnService implements Runnable
{
try
{
mBuilder.addAddress(address, prefixLength);
mCache.addAddress(address, prefixLength);
}
catch (IllegalArgumentException ex)
@@ -572,6 +587,7 @@ public class CharonVpnService extends VpnService implements Runnable
try
{
mBuilder.addDnsServer(address);
mCache.recordAddressFamily(address);
}
catch (IllegalArgumentException ex)
{
@@ -584,7 +600,6 @@ public class CharonVpnService extends VpnService implements Runnable
{
try
{
mBuilder.addRoute(address, prefixLength);
mCache.addRoute(address, prefixLength);
}
catch (IllegalArgumentException ex)
@@ -611,7 +626,6 @@ public class CharonVpnService extends VpnService implements Runnable
{
try
{
mBuilder.setMtu(mtu);
mCache.setMtu(mtu);
}
catch (IllegalArgumentException ex)
@@ -626,6 +640,7 @@ public class CharonVpnService extends VpnService implements Runnable
ParcelFileDescriptor fd;
try
{
mCache.applyData(mBuilder);
fd = mBuilder.establish();
}
catch (Exception ex)
@@ -641,7 +656,7 @@ public class CharonVpnService extends VpnService implements Runnable
* builder anymore, but we might need another when reestablishing */
mBuilder = createBuilder(mName);
mEstablishedCache = mCache;
mCache = new BuilderCache();
mCache = new BuilderCache(mSplitTunneling);
return fd.detachFd();
}
@@ -679,17 +694,40 @@ public class CharonVpnService extends VpnService implements Runnable
public class BuilderCache
{
private final List<PrefixedAddress> mAddresses = new ArrayList<PrefixedAddress>();
private final List<PrefixedAddress> mRoutes = new ArrayList<PrefixedAddress>();
private final List<PrefixedAddress> mRoutesIPv4 = new ArrayList<PrefixedAddress>();
private final List<PrefixedAddress> mRoutesIPv6 = new ArrayList<PrefixedAddress>();
private final int mSplitTunneling;
private int mMtu;
private boolean mIPv4Seen, mIPv6Seen;
public BuilderCache(Integer splitTunneling)
{
mSplitTunneling = splitTunneling != null ? splitTunneling : 0;
}
public void addAddress(String address, int prefixLength)
{
mAddresses.add(new PrefixedAddress(address, prefixLength));
recordAddressFamily(address);
}
public void addRoute(String address, int prefixLength)
{
mRoutes.add(new PrefixedAddress(address, prefixLength));
try
{
if (isIPv6(address))
{
mRoutesIPv6.add(new PrefixedAddress(address, prefixLength));
}
else
{
mRoutesIPv4.add(new PrefixedAddress(address, prefixLength));
}
}
catch (UnknownHostException ex)
{
ex.printStackTrace();
}
}
public void setMtu(int mtu)
@@ -697,19 +735,89 @@ public class CharonVpnService extends VpnService implements Runnable
mMtu = mtu;
}
public void recordAddressFamily(String address)
{
try
{
if (isIPv6(address))
{
mIPv6Seen = true;
}
else
{
mIPv4Seen = true;
}
}
catch (UnknownHostException ex)
{
ex.printStackTrace();
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void applyData(VpnService.Builder builder)
{
for (PrefixedAddress address : mAddresses)
{
builder.addAddress(address.mAddress, address.mPrefix);
}
for (PrefixedAddress route : mRoutes)
/* add routes depending on whether split tunneling is allowed or not,
* that is, whether we have to handle and block non-VPN traffic */
if ((mSplitTunneling & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) == 0)
{
builder.addRoute(route.mAddress, route.mPrefix);
if (mIPv4Seen)
{ /* split tunneling is used depending on the routes */
for (PrefixedAddress route : mRoutesIPv4)
{
builder.addRoute(route.mAddress, route.mPrefix);
}
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{ /* allow traffic that would otherwise be blocked to bypass the VPN */
builder.allowFamily(OsConstants.AF_INET);
}
}
else if (mIPv4Seen)
{ /* only needed if we've seen any addresses. otherwise, traffic
* is blocked by default (we also install no routes in that case) */
builder.addRoute("0.0.0.0", 0);
}
/* same thing for IPv6 */
if ((mSplitTunneling & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) == 0)
{
if (mIPv6Seen)
{
for (PrefixedAddress route : mRoutesIPv6)
{
builder.addRoute(route.mAddress, route.mPrefix);
}
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
builder.allowFamily(OsConstants.AF_INET6);
}
}
else if (mIPv6Seen)
{
builder.addRoute("::", 0);
}
builder.setMtu(mMtu);
}
private boolean isIPv6(String address) throws UnknownHostException
{
InetAddress addr = InetAddress.getByName(address);
if (addr instanceof Inet4Address)
{
return false;
}
else if (addr instanceof Inet6Address)
{
return true;
}
return false;
}
private class PrefixedAddress
{
public String mAddress;
@@ -725,22 +833,25 @@ public class CharonVpnService extends VpnService implements Runnable
/*
* The libraries are extracted to /data/data/org.strongswan.android/...
* during installation.
* during installation. On newer releases most are loaded in JNI_OnLoad.
*/
static
{
System.loadLibrary("strongswan");
if (MainActivity.USE_BYOD)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2)
{
System.loadLibrary("tncif");
System.loadLibrary("tnccs");
System.loadLibrary("imcv");
}
System.loadLibrary("strongswan");
System.loadLibrary("hydra");
System.loadLibrary("charon");
System.loadLibrary("ipsec");
if (MainActivity.USE_BYOD)
{
System.loadLibrary("tncif");
System.loadLibrary("tnccs");
System.loadLibrary("imcv");
}
System.loadLibrary("hydra");
System.loadLibrary("charon");
System.loadLibrary("ipsec");
}
System.loadLibrary("androidbridge");
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2012-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -42,12 +42,21 @@ public class NetworkManager extends BroadcastReceiver
mContext.unregisterReceiver(this);
}
public boolean isConnected()
{
ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = null;
if (cm != null)
{
info = cm.getActiveNetworkInfo();
}
return info != null && info.isConnected();
}
@Override
public void onReceive(Context context, Intent intent)
{
ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
networkChanged(info == null || !info.isConnected());
networkChanged(!isConnected());
}
/**
@@ -24,6 +24,7 @@ import org.strongswan.android.logic.imc.ImcState;
import org.strongswan.android.logic.imc.RemediationInstruction;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.Service;
import android.content.ComponentName;
@@ -82,7 +83,7 @@ public class ImcStateFragment extends Fragment implements VpnStateListener
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.imc_state_fragment, null);
View view = inflater.inflate(R.layout.imc_state_fragment, container, false);
mButton = (LinearLayout)view.findViewById(R.id.imc_state_button);
mButton.setOnClickListener(new OnClickListener() {
@@ -174,7 +175,12 @@ public class ImcStateFragment extends Fragment implements VpnStateListener
public void updateView()
{
FragmentTransaction ft = getFragmentManager().beginTransaction();
FragmentManager fm = getFragmentManager();
if (fm == null)
{
return;
}
FragmentTransaction ft = fm.beginTransaction();
switch (mService.getImcState())
{
@@ -60,6 +60,8 @@ import android.widget.TextView;
public class VpnProfileDetailActivity extends Activity
{
private static final int SELECT_TRUSTED_CERTIFICATE = 0;
private static final int MTU_MIN = 1280;
private static final int MTU_MAX = 1500;
private VpnProfileDataSource mDataSource;
private Long mId;
@@ -79,6 +81,12 @@ public class VpnProfileDetailActivity extends Activity
private CheckBox mCheckAuto;
private RelativeLayout mSelectCert;
private RelativeLayout mTncNotice;
private CheckBox mShowAdvanced;
private ViewGroup mAdvancedSettings;
private EditText mMTU;
private EditText mPort;
private CheckBox mBlockIPv4;
private CheckBox mBlockIPv6;
@Override
public void onCreate(Bundle savedInstanceState)
@@ -108,6 +116,14 @@ public class VpnProfileDetailActivity extends Activity
mCheckAuto = (CheckBox)findViewById(R.id.ca_auto);
mSelectCert = (RelativeLayout)findViewById(R.id.select_certificate);
mShowAdvanced = (CheckBox)findViewById(R.id.show_advanced);
mAdvancedSettings = (ViewGroup)findViewById(R.id.advanced_settings);
mMTU = (EditText)findViewById(R.id.mtu);
mPort = (EditText)findViewById(R.id.port);
mBlockIPv4 = (CheckBox)findViewById(R.id.split_tunneling_v4);
mBlockIPv6 = (CheckBox)findViewById(R.id.split_tunneling_v6);
mSelectVpnType.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
@@ -154,6 +170,14 @@ public class VpnProfileDetailActivity extends Activity
}
});
mShowAdvanced.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
updateAdvancedSettings();
}
});
mId = savedInstanceState == null ? null : savedInstanceState.getLong(VpnProfileDataSource.KEY_ID);
if (mId == null)
{
@@ -165,6 +189,7 @@ public class VpnProfileDetailActivity extends Activity
updateCredentialView();
updateCertificateSelector();
updateAdvancedSettings();
}
@Override
@@ -314,6 +339,22 @@ public class VpnProfileDetailActivity extends Activity
}
}
/**
* Update the advanced settings UI depending on whether any advanced
* settings have already been made.
*/
private void updateAdvancedSettings()
{
boolean show = mShowAdvanced.isChecked();
if (!show && mProfile != null)
{
Integer st = mProfile.getSplitTunneling();
show = mProfile.getMTU() != null || mProfile.getPort() != null || (st != null && st != 0);
}
mShowAdvanced.setVisibility(!show ? View.VISIBLE : View.GONE);
mAdvancedSettings.setVisibility(show ? View.VISIBLE : View.GONE);
}
/**
* Save or update the profile depending on whether we actually have a
* profile object or not (this was created in updateProfileData)
@@ -368,6 +409,18 @@ public class VpnProfileDetailActivity extends Activity
showCertificateAlert();
valid = false;
}
Integer mtu = getInteger(mMTU);
if (mtu != null && (mtu < MTU_MIN || mtu > MTU_MAX))
{
mMTU.setError(String.format(getString(R.string.alert_text_out_of_range), MTU_MIN, MTU_MAX));
valid = false;
}
Integer port = getInteger(mPort);
if (port != null && (port < 1 || port > 65535))
{
mPort.setError(String.format(getString(R.string.alert_text_out_of_range), 1, 65535));
valid = false;
}
return valid;
}
@@ -395,6 +448,12 @@ public class VpnProfileDetailActivity extends Activity
}
String certAlias = mCheckAuto.isChecked() ? null : mCertEntry.getAlias();
mProfile.setCertificateAlias(certAlias);
mProfile.setMTU(getInteger(mMTU));
mProfile.setPort(getInteger(mPort));
int st = 0;
st |= mBlockIPv4.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4 : 0;
st |= mBlockIPv6.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6 : 0;
mProfile.setSplitTunneling(st == 0 ? null : st);
}
/**
@@ -417,6 +476,10 @@ public class VpnProfileDetailActivity extends Activity
mVpnType = mProfile.getVpnType();
mUsername.setText(mProfile.getUsername());
mPassword.setText(mProfile.getPassword());
mMTU.setText(mProfile.getMTU() != null ? mProfile.getMTU().toString() : null);
mPort.setText(mProfile.getPort() != null ? mProfile.getPort().toString() : null);
mBlockIPv4.setChecked(mProfile.getSplitTunneling() != null ? (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) != 0 : false);
mBlockIPv6.setChecked(mProfile.getSplitTunneling() != null ? (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) != 0 : false);
useralias = mProfile.getUserCertificateAlias();
alias = mProfile.getCertificateAlias();
getActionBar().setTitle(mProfile.getName());
@@ -458,6 +521,17 @@ public class VpnProfileDetailActivity extends Activity
}
}
/**
* Get the integer value in the given text box or null if empty
*
* @param view text box (numeric entry assumed)
*/
private Integer getInteger(EditText view)
{
String value = view.getText().toString().trim();
return value.isEmpty() ? null : Integer.valueOf(value);
}
private class SelectUserCertOnClickListener implements OnClickListener, KeyChainAliasCallback
{
@Override
@@ -0,0 +1,160 @@
/*
* Copyright (C) 2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package org.strongswan.android.utils;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.regex.Pattern;
/**
* Simple generator for data/files that may be parsed by libstrongswan's
* settings_t class.
*/
public class SettingsWriter
{
/**
* Top-level section
*/
private final SettingsSection mTop = new SettingsSection();
/**
* Set a string value
* @param key
* @param value
* @return the writer
*/
public SettingsWriter setValue(String key, String value)
{
Pattern pattern = Pattern.compile("[^#{}=\"\\n\\t ]+");
if (key == null || !pattern.matcher(key).matches())
{
return this;
}
String[] keys = key.split("\\.");
SettingsSection section = mTop;
section = findOrCreateSection(Arrays.copyOfRange(keys, 0, keys.length-1));
section.Settings.put(keys[keys.length-1], value);
return this;
}
/**
* Set an integer value
* @param key
* @param value
* @return the writer
*/
public SettingsWriter setValue(String key, Integer value)
{
return setValue(key, value == null ? null : value.toString());
}
/**
* Set a boolean value
* @param key
* @param value
* @return the writer
*/
public SettingsWriter setValue(String key, Boolean value)
{
return setValue(key, value == null ? null : value ? "1" : "0");
}
/**
* Serializes the settings to a string in the format understood by
* libstrongswan's settings_t parser.
* @return serialized settings
*/
public String serialize()
{
StringBuilder builder = new StringBuilder();
serializeSection(mTop, builder);
return builder.toString();
}
/**
* Serialize the settings in a section and recursively serialize sub-sections
* @param section
* @param builder
*/
private void serializeSection(SettingsSection section, StringBuilder builder)
{
for (Entry<String, String> setting : section.Settings.entrySet())
{
builder.append(setting.getKey()).append('=');
if (setting.getValue() != null)
{
builder.append("\"").append(escapeValue(setting.getValue())).append("\"");
}
builder.append('\n');
}
for (Entry<String, SettingsSection> subsection : section.Sections.entrySet())
{
builder.append(subsection.getKey()).append(" {\n");
serializeSection(subsection.getValue(), builder);
builder.append("}\n");
}
}
/**
* Escape value so it may be wrapped in "
* @param value
* @return
*/
private String escapeValue(String value)
{
return value.replace("\"", "\\\"");
}
/**
* Find or create the nested sections with the given names
* @param sections list of section names
* @return final section
*/
private SettingsSection findOrCreateSection(String[] sections)
{
SettingsSection section = mTop;
for (String name : sections)
{
SettingsSection subsection = section.Sections.get(name);
if (subsection == null)
{
subsection = new SettingsSection();
section.Sections.put(name, subsection);
}
section = subsection;
}
return section;
}
/**
* A section containing sub-sections and settings.
*/
private class SettingsSection
{
/**
* Assigned key/value pairs
*/
LinkedHashMap<String,String> Settings = new LinkedHashMap<String, String>();
/**
* Assigned sub-sections
*/
LinkedHashMap<String,SettingsSection> Sections = new LinkedHashMap<String, SettingsWriter.SettingsSection>();
}
}
+1 -2
View File
@@ -1,3 +1,2 @@
EXTRA_DIST = linux/if_alg.h linux/ipsec.h linux/netlink.h linux/rtnetlink.h \
linux/pfkeyv2.h linux/udp.h linux/xfrm.h linux/types.h \
sys/queue.h
linux/pfkeyv2.h linux/udp.h linux/xfrm.h sys/queue.h
-172
View File
@@ -1,172 +0,0 @@
#ifndef _LINUX_TYPES_H
#define _LINUX_TYPES_H
#include <linux/posix_types.h>
#include <asm/types.h>
#ifndef __KERNEL_STRICT_NAMES
typedef __u32 __kernel_dev_t;
typedef __kernel_fd_set fd_set;
typedef __kernel_dev_t dev_t;
typedef __kernel_ino_t ino_t;
typedef __kernel_mode_t mode_t;
typedef __kernel_nlink_t nlink_t;
typedef __kernel_off_t off_t;
typedef __kernel_pid_t pid_t;
typedef __kernel_daddr_t daddr_t;
typedef __kernel_key_t key_t;
typedef __kernel_suseconds_t suseconds_t;
typedef __kernel_timer_t timer_t;
typedef __kernel_clockid_t clockid_t;
typedef __kernel_mqd_t mqd_t;
typedef __kernel_uid_t uid_t;
typedef __kernel_gid_t gid_t;
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
typedef __kernel_loff_t loff_t;
#endif
/*
* The following typedefs are also protected by individual ifdefs for
* historical reasons:
*/
#ifndef _SIZE_T
#define _SIZE_T
typedef __kernel_size_t size_t;
#endif
#ifndef _SSIZE_T
#define _SSIZE_T
typedef __kernel_ssize_t ssize_t;
#endif
#ifndef _PTRDIFF_T
#define _PTRDIFF_T
typedef __kernel_ptrdiff_t ptrdiff_t;
#endif
#ifndef _TIME_T
#define _TIME_T
typedef __kernel_time_t time_t;
#endif
#ifndef _CLOCK_T
#define _CLOCK_T
typedef __kernel_clock_t clock_t;
#endif
#ifndef _CADDR_T
#define _CADDR_T
typedef __kernel_caddr_t caddr_t;
#endif
/* bsd */
typedef unsigned char u_char;
typedef unsigned short u_short;
typedef unsigned int u_int;
typedef unsigned long u_long;
/* sysv */
typedef unsigned char unchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
#ifndef __BIT_TYPES_DEFINED__
#define __BIT_TYPES_DEFINED__
typedef __u8 u_int8_t;
typedef __s8 int8_t;
typedef __u16 u_int16_t;
typedef __s16 int16_t;
typedef __u32 u_int32_t;
typedef __s32 int32_t;
#endif /* !(__BIT_TYPES_DEFINED__) */
typedef __u8 uint8_t;
typedef __u16 uint16_t;
typedef __u32 uint32_t;
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
typedef __u64 uint64_t;
typedef __u64 u_int64_t;
typedef __s64 int64_t;
#endif
/* this is a special 64bit data type that is 8-byte aligned */
#define aligned_u64 unsigned long long __attribute__((aligned(8)))
#define aligned_be64 __be64 __attribute__((aligned(8)))
#define aligned_le64 __le64 __attribute__((aligned(8)))
/**
* The type used for indexing onto a disc or disc partition.
*
* Linux always considers sectors to be 512 bytes long independently
* of the devices real block size.
*/
#ifdef CONFIG_LBD
typedef u64 sector_t;
#else
typedef unsigned long sector_t;
#endif
/*
* The type of the inode's block count.
*/
#ifdef CONFIG_LSF
typedef u64 blkcnt_t;
#else
typedef unsigned long blkcnt_t;
#endif
/*
* The type of an index into the pagecache. Use a #define so asm/types.h
* can override it.
*/
#ifndef pgoff_t
#define pgoff_t unsigned long
#endif
#endif /* __KERNEL_STRICT_NAMES */
/*
* Below are truly Linux-specific types that should never collide with
* any application/library that wants linux/types.h.
*/
#ifdef __CHECKER__
#define __bitwise__ __attribute__((bitwise))
#else
#define __bitwise__
#endif
#ifdef __CHECK_ENDIAN__
#define __bitwise __bitwise__
#else
#define __bitwise
#endif
typedef __u16 __bitwise __le16;
typedef __u16 __bitwise __be16;
typedef __u32 __bitwise __le32;
typedef __u32 __bitwise __be32;
#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
typedef __u64 __bitwise __le64;
typedef __u64 __bitwise __be64;
#endif
typedef __u16 __bitwise __sum16;
typedef __u32 __bitwise __wsum;
struct ustat {
__kernel_daddr_t f_tfree;
__kernel_ino_t f_tinode;
char f_fname[6];
char f_fpack[6];
};
#endif /* _LINUX_TYPES_H */
+58 -11
View File
@@ -37,9 +37,10 @@
typedef struct private_settings_t private_settings_t;
/**
* Parse function provided by the generated parser.
* Parse functions provided by the generated parser.
*/
bool settings_parser_parse_file(section_t *root, char *name);
bool settings_parser_parse_string(section_t *root, char *settings);
/**
* Private data of settings
@@ -843,16 +844,17 @@ METHOD(settings_t, add_fallback, void,
}
/**
* Load settings from files matching the given file pattern.
* Load settings from files matching the given file pattern or from a string.
* All sections and values are added relative to "parent".
* All files (even included ones) have to be loaded successfully.
* If merge is FALSE the contents of parent are replaced with the parsed
* contents, otherwise they are merged together.
*/
static bool load_files_internal(private_settings_t *this, section_t *parent,
char *pattern, bool merge)
static bool load_internal(private_settings_t *this, section_t *parent,
char *pattern, bool merge, bool string)
{
section_t *section;
bool loaded;
if (pattern == NULL || !pattern[0])
{ /* TODO: Clear parent if merge is FALSE? */
@@ -860,7 +862,9 @@ static bool load_files_internal(private_settings_t *this, section_t *parent,
}
section = settings_section_create(NULL);
if (!settings_parser_parse_file(section, pattern))
loaded = string ? settings_parser_parse_string(section, pattern) :
settings_parser_parse_file(section, pattern);
if (!loaded)
{
settings_section_destroy(section, NULL);
return FALSE;
@@ -877,7 +881,7 @@ static bool load_files_internal(private_settings_t *this, section_t *parent,
METHOD(settings_t, load_files, bool,
private_settings_t *this, char *pattern, bool merge)
{
return load_files_internal(this, this->top, pattern, merge);
return load_internal(this, this->top, pattern, merge, FALSE);
}
METHOD(settings_t, load_files_section, bool,
@@ -894,7 +898,30 @@ METHOD(settings_t, load_files_section, bool,
{
return FALSE;
}
return load_files_internal(this, section, pattern, merge);
return load_internal(this, section, pattern, merge, FALSE);
}
METHOD(settings_t, load_string, bool,
private_settings_t *this, char *settings, bool merge)
{
return load_internal(this, this->top, settings, merge, TRUE);
}
METHOD(settings_t, load_string_section, bool,
private_settings_t *this, char *settings, bool merge, char *key, ...)
{
section_t *section;
va_list args;
va_start(args, key);
section = ensure_section(this, this->top, key, args);
va_end(args);
if (!section)
{
return FALSE;
}
return load_internal(this, section, settings, merge, TRUE);
}
METHOD(settings_t, destroy, void,
@@ -906,10 +933,7 @@ METHOD(settings_t, destroy, void,
free(this);
}
/*
* see header file
*/
settings_t *settings_create(char *file)
static private_settings_t *settings_create_base()
{
private_settings_t *this;
@@ -931,14 +955,37 @@ settings_t *settings_create(char *file)
.add_fallback = _add_fallback,
.load_files = _load_files,
.load_files_section = _load_files_section,
.load_string = _load_string,
.load_string_section = _load_string_section,
.destroy = _destroy,
},
.top = settings_section_create(NULL),
.contents = array_create(0, 0),
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
);
return this;
}
/*
* see header file
*/
settings_t *settings_create(char *file)
{
private_settings_t *this = settings_create_base();
load_files(this, file, FALSE);
return &this->public;
}
/*
* see header file
*/
settings_t *settings_create_string(char *settings)
{
private_settings_t *this = settings_create_base();
load_string(this, settings, FALSE);
return &this->public;
}
+54
View File
@@ -334,6 +334,50 @@ struct settings_t {
bool (*load_files_section)(settings_t *this, char *pattern, bool merge,
char *section, ...);
/**
* Load settings from the given string.
*
* If merge is TRUE, existing sections are extended, existing values
* replaced, by those found in the string. If it is FALSE, existing
* sections are purged before reading the new config.
*
* @note If the string contains _include_ statements they should be
* absolute paths.
*
* @note If any failures occur, no settings are added at all. So, it's all
* or nothing.
*
* @param settings string to parse
* @param merge TRUE to merge config with existing values
* @return TRUE, if settings were loaded successfully
*/
bool (*load_string)(settings_t *this, char *settings, bool merge);
/**
* Load settings from the given string.
*
* If merge is TRUE, existing sections are extended, existing values
* replaced, by those found in the string. If it is FALSE, existing
* sections are purged before reading the new config.
*
* All settings are loaded relative to the given section. The section is
* created, if it does not yet exist.
*
* @note If the string contains _include_ statements they should be
* absolute paths.
*
* @note If any failures occur, no settings are added at all. So, it's all
* or nothing.
*
* @param settings string to parse
* @param merge TRUE to merge config with existing values
* @param section section name of parent section, printf style
* @param ... argument list for section
* @return TRUE, if settings were loaded successfully
*/
bool (*load_string_section)(settings_t *this, char *settings, bool merge,
char *section, ...);
/**
* Destroy a settings instance.
*/
@@ -350,4 +394,14 @@ struct settings_t {
*/
settings_t *settings_create(char *file);
/**
* Load settings from a string.
*
* @note If parsing the file fails the object is still created.
*
* @param settings string to read settings from
* @return settings object, or NULL
*/
settings_t *settings_create_string(char *settings);
#endif /** SETTINGS_H_ @}*/
+10 -9
View File
@@ -119,16 +119,11 @@ static void include_files(parser_helper_t *ctx);
<str>{
"\"" |
<<EOF>> |
\n |
\\ {
if (!streq(yytext, "\""))
{
if (streq(yytext, "\n"))
{ /* put the newline back to fix the line numbers */
unput('\n');
yy_set_bol(0);
}
PARSER_DBG1(yyextra, "unterminated string detected");
return STRING_ERROR;
}
if (yy_top_state(yyscanner) == inc)
{ /* string include */
@@ -146,11 +141,9 @@ static void include_files(parser_helper_t *ctx);
\\n yyextra->string_add(yyextra, "\n");
\\r yyextra->string_add(yyextra, "\r");
\\t yyextra->string_add(yyextra, "\t");
\\b yyextra->string_add(yyextra, "\b");
\\f yyextra->string_add(yyextra, "\f");
\\\r?\n /* merge lines that end with EOL characters */
\\. yyextra->string_add(yyextra, yytext+1);
[^\\\n"]+ {
[^\\"]+ {
yyextra->string_add(yyextra, yytext);
}
}
@@ -198,3 +191,11 @@ static void include_files(parser_helper_t *ctx)
settings_parser_open_next_file(ctx);
}
/**
* Load the given string to be parsed next
*/
void settings_parser_load_string(parser_helper_t *ctx, const char *content)
{
settings_parser__scan_string(content, ctx->scanner);
}
+38 -1
View File
@@ -39,6 +39,7 @@ int settings_parser_get_leng(void *scanner);
int settings_parser_get_lineno(void *scanner);
/* Custom functions in lexer */
bool settings_parser_open_next_file(parser_helper_t *ctx);
bool settings_parser_load_string(parser_helper_t *ctx, const char *content);
/**
* Forward declarations
@@ -79,7 +80,7 @@ static int yylex(YYSTYPE *lvalp, parser_helper_t *ctx)
struct kv_t *kv;
}
%token <s> NAME STRING
%token NEWLINE
%token NEWLINE STRING_ERROR
/* ...and other symbols */
%type <s> value valuepart
@@ -286,3 +287,39 @@ bool settings_parser_parse_file(section_t *root, char *name)
helper->destroy(helper);
return success;
}
/**
* Parse the given string and add all sections and key/value pairs to the
* given section.
*/
bool settings_parser_parse_string(section_t *root, char *settings)
{
parser_helper_t *helper;
array_t *sections = NULL;
bool success = FALSE;
array_insert_create(&sections, ARRAY_TAIL, root);
helper = parser_helper_create(sections);
helper->get_lineno = settings_parser_get_lineno;
if (settings_parser_lex_init_extra(helper, &helper->scanner) != 0)
{
helper->destroy(helper);
array_destroy(sections);
return FALSE;
}
settings_parser_load_string(helper, settings);
if (getenv("DEBUG_SETTINGS_PARSER"))
{
yydebug = 1;
settings_parser_set_debug(1, helper->scanner);
}
success = yyparse(helper) == 0;
if (!success)
{
DBG1(DBG_CFG, "failed to parse settings '%s'", settings);
}
array_destroy(sections);
settings_parser_lex_destroy(helper->scanner);
helper->destroy(helper);
return success;
}
+130 -16
View File
@@ -58,6 +58,10 @@ START_SETUP(setup_base_config)
" }\n"
" key2 = with space\n"
" key3 = \"string with\\nnewline\"\n"
" key4 = \"multi line\n"
"string\"\n"
" key5 = \"escaped \\\n"
"newline\"\n"
"}\n"
"out = side\n"
"other {\n"
@@ -88,6 +92,8 @@ START_TEST(test_get_str)
verify_string("", "main.empty");
verify_string("with space", "main.key2");
verify_string("string with\nnewline", "main.key3");
verify_string("multi line\nstring", "main.key4");
verify_string("escaped newline", "main.key5");
verify_string("value", "main.sub1.key");
verify_string("value2", "main.sub1.key2");
verify_string("bar", "main.sub1.subsub.foo");
@@ -97,7 +103,7 @@ START_TEST(test_get_str)
verify_string("other val", "other.key1");
verify_null("main.none");
verify_null("main.key4");
verify_null("main.key6");
verify_null("other.sub");
}
END_TEST
@@ -131,7 +137,7 @@ START_TEST(test_get_str_printf)
* probably document it at least */
verify_null("main.%s%u.key%d", "sub", 1, 2);
verify_null("%s.%s%d", "main", "key", 4);
verify_null("%s.%s%d", "main", "key", 6);
}
END_TEST
@@ -529,9 +535,7 @@ END_TEST
# define include2 "/tmp/strongswan-settings-test-include2"
#endif
START_SETUP(setup_include_config)
{
chunk_t inc1 = chunk_from_str(
static char *include_content1 =
"main {\n"
" key1 = n1\n"
" key2 = n2\n"
@@ -544,14 +548,17 @@ START_SETUP(setup_include_config)
" sub3 = val3\n"
" }\n"
" include " include2 "\n"
"}");
chunk_t inc2 = chunk_from_str(
"}";
static char *include_content2 =
"key2 = v2\n"
"sub1 {\n"
" key = val\n"
"}");
ck_assert(chunk_write(inc1, include1, 0022, TRUE));
ck_assert(chunk_write(inc2, include2, 0022, TRUE));
"}";
START_SETUP(setup_include_config)
{
ck_assert(chunk_write(chunk_from_str(include_content1), include1, 0022, TRUE));
ck_assert(chunk_write(chunk_from_str(include_content2), include2, 0022, TRUE));
}
END_SETUP
@@ -784,6 +791,104 @@ START_TEST(test_order_section)
}
END_TEST
START_TEST(test_load_string)
{
char *content =
"main {\n"
" key1 = val1\n"
" key2 = val2\n"
" key3 = val3\n"
" none = x\n"
" sub1 {\n"
" include = value\n"
" key2 = v2\n"
" sub1 {\n"
" key = val\n"
" }\n"
" }\n"
"}";
char *val1, *val2, *val3;
settings = settings_create_string(content);
val1 = settings->get_str(settings, "main.key1", NULL);
val2 = settings->get_str(settings, "main.sub1.key2", NULL);
/* loading the same content twice should not change anything, with... */
ck_assert(settings->load_string(settings, content, TRUE));
ck_assert(val1 == settings->get_str(settings, "main.key1", NULL));
ck_assert(val2 == settings->get_str(settings, "main.sub1.key2", NULL));
/* ...or without merging */
ck_assert(settings->load_string(settings, content, FALSE));
ck_assert(val1 == settings->get_str(settings, "main.key1", NULL));
ck_assert(val2 == settings->get_str(settings, "main.sub1.key2", NULL));
val1 = settings->get_str(settings, "main.key2", NULL);
val2 = settings->get_str(settings, "main.key3", NULL);
val3 = settings->get_str(settings, "main.none", NULL);
/* only pointers for modified settings should change, but still be valid */
ck_assert(settings->load_string(settings, include_content1, FALSE));
ck_assert(val1 != settings->get_str(settings, "main.key2", NULL));
ck_assert_str_eq(val1, "val2");
ck_assert(val2 == settings->get_str(settings, "main.key3", NULL));
ck_assert(val3 != settings->get_str(settings, "main.none", NULL));
ck_assert_str_eq(val3, "x");
settings->destroy(settings);
settings = settings_create_string(content);
ck_assert(settings);
ck_assert(settings->load_string(settings, include_content1, TRUE));
verify_include();
ck_assert(settings->load_string(settings, include_content2, FALSE));
verify_null("main.key1");
verify_string("v2", "key2");
verify_string("val", "sub1.key");
verify_null("main.sub1.key3");
}
END_TEST
START_TEST(test_load_string_section)
{
char *content =
"main {\n"
" key1 = val1\n"
" key2 = val2\n"
" none = x\n"
" sub1 {\n"
" include = value\n"
" key2 = value2\n"
" }\n"
"}";
settings = settings_create_string(content);
ck_assert(settings->load_string_section(settings, include_content1, TRUE, ""));
ck_assert(settings->load_string_section(settings, include_content2, TRUE, "main.sub1"));
verify_include();
/* invalid strings are a failure */
ck_assert(!settings->load_string_section(settings, "conf {", TRUE, ""));
/* NULL or empty strings are OK though */
ck_assert(settings->load_string_section(settings, "", TRUE, ""));
ck_assert(settings->load_string_section(settings, NULL, TRUE, ""));
verify_include();
ck_assert(settings->load_string_section(settings, include_content2, FALSE, "main"));
verify_null("main.key1");
verify_string("v2", "main.key2");
verify_string("val", "main.sub1.key");
verify_null("main.sub1.key3");
verify_null("main.sub2.sub3");
ck_assert(settings->load_string_section(settings, include_content2, TRUE, "main.sub2"));
verify_string("v2", "main.sub2.key2");
verify_string("val", "main.sub2.sub1.key");
}
END_TEST
START_SETUP(setup_fallback_config)
{
create_settings(chunk_from_str(
@@ -906,9 +1011,8 @@ START_SETUP(setup_string_config)
create_settings(chunk_from_str(
"string = \" with accurate\twhitespace\"\n"
"special = \"all { special } characters # can be used.\"\n"
"unterminated = \"is fine\n"
"but = produces a warning\n"
"newlines = \"can either be encoded\\nor \\\n"
"newlines = \"can be encoded explicitly\\nor implicitly\n"
"or \\\n"
"escaped\"\n"
"quotes = \"\\\"and\\\" slashes \\\\ can \\\\ be\" # escaped too\n"
"multiple = \"strings\" are \"combined\"\n"
@@ -920,9 +1024,7 @@ START_TEST(test_strings)
{
verify_string(" with accurate\twhitespace", "string");
verify_string("all { special } characters # can be used.", "special");
verify_string("is fine", "unterminated");
verify_string("produces a warning", "but");
verify_string("can either be encoded\nor escaped", "newlines");
verify_string("can be encoded explicitly\nor implicitly\nor escaped", "newlines");
verify_string("\"and\" slashes \\ can \\ be", "quotes");
verify_string("strings are combined", "multiple");
}
@@ -989,6 +1091,12 @@ START_TEST(test_invalid)
ck_assert(chunk_write(contents, path, 0022, TRUE));
ck_assert(!settings->load_files(settings, path, FALSE));
contents = chunk_from_str(
"unterminated {\n"
" strings = \"are invalid\n");
ck_assert(chunk_write(contents, path, 0022, TRUE));
ck_assert(!settings->load_files(settings, path, FALSE));
contents = chunk_from_str(
"spaces in name {}");
ck_assert(chunk_write(contents, path, 0022, TRUE));
@@ -1060,6 +1168,12 @@ Suite *settings_suite_create()
tcase_add_test(tc, test_order_section);
suite_add_tcase(s, tc);
tc = tcase_create("load_string[_section]");
tcase_add_checked_fixture(tc, setup_include_config, teardown_config);
tcase_add_test(tc, test_load_string);
tcase_add_test(tc, test_load_string_section);
suite_add_tcase(s, tc);
tc = tcase_create("fallback");
tcase_add_checked_fixture(tc, setup_fallback_config, teardown_config);
tcase_add_test(tc, test_add_fallback);
+2 -9
View File
@@ -123,16 +123,11 @@ static void include_files(parser_helper_t *ctx);
<str>{
"\"" |
<<EOF>> |
\n |
\\ {
if (!streq(yytext, "\""))
{
if (streq(yytext, "\n"))
{ /* put the newline back to fix the line numbers */
unput('\n');
yy_set_bol(0);
}
PARSER_DBG1(yyextra, "unterminated string detected");
return STRING_ERROR;
}
if (yy_top_state(yyscanner) == inc)
{ /* string include */
@@ -150,11 +145,9 @@ static void include_files(parser_helper_t *ctx);
\\n yyextra->string_add(yyextra, "\n");
\\r yyextra->string_add(yyextra, "\r");
\\t yyextra->string_add(yyextra, "\t");
\\b yyextra->string_add(yyextra, "\b");
\\f yyextra->string_add(yyextra, "\f");
\\\r?\n /* merge lines that end with EOL characters */
\\. yyextra->string_add(yyextra, yytext+1);
[^\\\n"]+ {
[^\\"]+ {
yyextra->string_add(yyextra, yytext);
}
}
+1 -1
View File
@@ -73,7 +73,7 @@ static int yylex(YYSTYPE *lvalp, parser_helper_t *ctx)
conf_parser_section_t t;
}
%token <s> STRING
%token EQ SPACES NEWLINE CONFIG_SETUP CONN CA
%token EQ SPACES NEWLINE CONFIG_SETUP CONN CA STRING_ERROR
/* ...and other symbols */
%type <t> section_type
+3
View File
@@ -328,6 +328,9 @@ static struct {
{ TRUE, "conn foo\n\tkey=val ue", "foo", "val ue" },
{ TRUE, "conn foo\n\tkey=\"val ue\"", "foo", "val ue" },
{ TRUE, "conn foo\n\tkey=\"val\\nue\"", "foo", "val\nue" },
{ TRUE, "conn foo\n\tkey=\"val\nue\"", "foo", "val\nue" },
{ TRUE, "conn foo\n\tkey=\"val\\\nue\"", "foo", "value" },
{ FALSE, "conn foo\n\tkey=\"unterminated", "foo", NULL },
};
START_TEST(test_strings)