aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2015-07-28 13:29:03 +0200
committerTobias Brunner <tobias@strongswan.org>2015-07-28 13:55:23 +0200
commitf14feed01421384b610f896916a28bd4f73f895a (patch)
tree598a92977c36e3af97755049259c8ac6cbece668
parentfbcac07043a6e98ebba8df8bc2c1eb282ae1a2a3 (diff)
parent1e323dc1b772a42470939ab53ad295b3bc786e30 (diff)
downloadstrongswan-f14feed01421384b610f896916a28bd4f73f895a.tar.bz2
strongswan-f14feed01421384b610f896916a28bd4f73f895a.tar.xz
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.
-rw-r--r--src/frontends/android/AndroidManifest.xml4
-rw-r--r--src/frontends/android/jni/Android.mk2
-rw-r--r--src/frontends/android/jni/Application.mk1
-rw-r--r--src/frontends/android/jni/libandroidbridge/android_jni.c39
-rw-r--r--src/frontends/android/jni/libandroidbridge/android_jni.h2
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_service.c95
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_service.h12
-rw-r--r--src/frontends/android/jni/libandroidbridge/charonservice.c64
-rw-r--r--src/frontends/android/jni/libandroidbridge/charonservice.h5
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/android_net.c224
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/android_net.h23
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/network_manager.c27
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/network_manager.h9
-rw-r--r--src/frontends/android/project.properties2
-rw-r--r--src/frontends/android/res/layout/profile_detail_view.xml61
-rw-r--r--src/frontends/android/res/values-de/strings.xml10
-rw-r--r--src/frontends/android/res/values-pl/strings.xml8
-rw-r--r--src/frontends/android/res/values-ru/strings.xml8
-rw-r--r--src/frontends/android/res/values-ua/strings.xml8
-rw-r--r--src/frontends/android/res/values/strings.xml10
-rw-r--r--src/frontends/android/src/org/strongswan/android/data/VpnProfile.java38
-rw-r--r--src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java41
-rw-r--r--src/frontends/android/src/org/strongswan/android/data/VpnType.java2
-rw-r--r--src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java167
-rw-r--r--src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java17
-rw-r--r--src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java10
-rw-r--r--src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java74
-rw-r--r--src/frontends/android/src/org/strongswan/android/utils/SettingsWriter.java160
-rw-r--r--src/include/Makefile.am3
-rw-r--r--src/include/linux/types.h172
-rw-r--r--src/libstrongswan/settings/settings.c69
-rw-r--r--src/libstrongswan/settings/settings.h54
-rw-r--r--src/libstrongswan/settings/settings_lexer.l19
-rw-r--r--src/libstrongswan/settings/settings_parser.y39
-rw-r--r--src/libstrongswan/tests/suites/test_settings.c146
-rw-r--r--src/starter/parser/lexer.l11
-rw-r--r--src/starter/parser/parser.y2
-rw-r--r--src/starter/tests/suites/test_parser.c3
38 files changed, 1242 insertions, 399 deletions
diff --git a/src/frontends/android/AndroidManifest.xml b/src/frontends/android/AndroidManifest.xml
index 10365985b..5075440e2 100644
--- a/src/frontends/android/AndroidManifest.xml
+++ b/src/frontends/android/AndroidManifest.xml
@@ -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" />
diff --git a/src/frontends/android/jni/Android.mk b/src/frontends/android/jni/Android.mk
index 670e83de1..1fb233b48 100644
--- a/src/frontends/android/jni/Android.mk
+++ b/src/frontends/android/jni/Android.mk
@@ -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),)
diff --git a/src/frontends/android/jni/Application.mk b/src/frontends/android/jni/Application.mk
index cdfb47400..9fa668354 100644
--- a/src/frontends/android/jni/Application.mk
+++ b/src/frontends/android/jni/Application.mk
@@ -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
diff --git a/src/frontends/android/jni/libandroidbridge/android_jni.c b/src/frontends/android/jni/libandroidbridge/android_jni.c
index 7ab9a24bd..a6412bdf7 100644
--- a/src/frontends/android/jni/libandroidbridge/android_jni.c
+++ b/src/frontends/android/jni/libandroidbridge/android_jni.c
@@ -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);
+ }
+ }
}
diff --git a/src/frontends/android/jni/libandroidbridge/android_jni.h b/src/frontends/android/jni/libandroidbridge/android_jni.h
index 99c0bc2cd..b08670f7e 100644
--- a/src/frontends/android/jni/libandroidbridge/android_jni.h
+++ b/src/frontends/android/jni/libandroidbridge/android_jni.h
@@ -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;
/**
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c
index 896bb0940..7ef3913f7 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c
@@ -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
- */
- char *type;
-
- /**
- * gateway
+ * configuration setttings
*/
- char *gateway;
-
- /**
- * username
- */
- char *username;
-
- /**
- * password
- */
- char *password;
+ settings_t *settings;
/**
* lock to safely access the TUN device fd
@@ -85,6 +68,11 @@ struct private_android_service_t {
int tunfd;
/**
+ * MTU of TUN device
+ */
+ int mtu;
+
+ /**
* DNS proxy
*/
android_dns_proxy_t *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);
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.h b/src/frontends/android/jni/libandroidbridge/backend/android_service.h
index 1bfdcf994..1a5175774 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_service.h
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.h
@@ -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_ @}*/
diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.c b/src/frontends/android/jni/libandroidbridge/charonservice.c
index 524630024..2655f7361 100644
--- a/src/frontends/android/jni/libandroidbridge/charonservice.c
+++ b/src/frontends/android/jni/libandroidbridge/charonservice.c
@@ -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;
@@ -84,11 +83,6 @@ struct private_charonservice_t {
network_manager_t *network_manager;
/**
- * Handle network events
- */
- android_net_t *net_handler;
-
- /**
* CharonVpnService reference
*/
jobject vpn_service;
@@ -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;
-
- type = androidjni_convert_jstring(env, jtype);
- gateway = androidjni_convert_jstring(env, jgateway);
- username = androidjni_convert_jstring(env, jusername);
- password = androidjni_convert_jstring(env, jpassword);
+ settings_t *settings;
+ char *config;
- initiate(type, gateway, username, password);
+ config = androidjni_convert_jstring(env, jconfig);
+ settings = settings_create_string(config);
+ initiate(settings);
}
diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.h b/src/frontends/android/jni/libandroidbridge/charonservice.h
index 0c71d876d..8cb68e099 100644
--- a/src/frontends/android/jni/libandroidbridge/charonservice.h
+++ b/src/frontends/android/jni/libandroidbridge/charonservice.h
@@ -45,6 +45,11 @@ 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
*/
enum android_vpn_state_t {
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c
index 653e2738b..2ce1bdfac 100644
--- a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c
+++ b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c
@@ -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;
-};
+}
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_net.h b/src/frontends/android/jni/libandroidbridge/kernel/android_net.h
index ade83f32a..761fa21bc 100644
--- a/src/frontends/android/jni/libandroidbridge/kernel/android_net.h
+++ b/src/frontends/android/jni/libandroidbridge/kernel/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
@@ -22,25 +22,14 @@
#define ANDROID_NET_H_
#include <library.h>
-
-typedef struct android_net_t android_net_t;
-
-/**
- * Handle connectivity events from NetworkManager
- */
-struct android_net_t {
-
- /**
- * Destroy an android_net_t instance.
- */
- void (*destroy)(android_net_t *this);
-};
+#include <kernel/kernel_net.h>
/**
- * 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_ @}*/
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c
index f8e560b56..372b25c55 100644
--- a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c
+++ b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c
@@ -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),
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h
index abca239ea..9a6a715b7 100644
--- a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h
+++ b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.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
@@ -66,6 +66,13 @@ struct network_manager_t {
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
*/
void (*destroy)(network_manager_t *this);
diff --git a/src/frontends/android/project.properties b/src/frontends/android/project.properties
index a5578ba09..bbe203c84 100644
--- a/src/frontends/android/project.properties
+++ b/src/frontends/android/project.properties
@@ -8,4 +8,4 @@
# project structure.
# Project target.
-target=android-19
+target=android-21
diff --git a/src/frontends/android/res/layout/profile_detail_view.xml b/src/frontends/android/res/layout/profile_detail_view.xml
index 91cd345ff..57d5606ff 100644
--- a/src/frontends/android/res/layout/profile_detail_view.xml
+++ b/src/frontends/android/res/layout/profile_detail_view.xml
@@ -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> \ No newline at end of file
diff --git a/src/frontends/android/res/values-de/strings.xml b/src/frontends/android/res/values-de/strings.xml
index 491fe8a8a..6cd5ba50a 100644
--- a/src/frontends/android/res/values-de/strings.xml
+++ b/src/frontends/android/res/values-de/strings.xml
@@ -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>
diff --git a/src/frontends/android/res/values-pl/strings.xml b/src/frontends/android/res/values-pl/strings.xml
index d0cfa48f1..fb2aba003 100644
--- a/src/frontends/android/res/values-pl/strings.xml
+++ b/src/frontends/android/res/values-pl/strings.xml
@@ -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>
diff --git a/src/frontends/android/res/values-ru/strings.xml b/src/frontends/android/res/values-ru/strings.xml
index eb69183db..eabfc084b 100644
--- a/src/frontends/android/res/values-ru/strings.xml
+++ b/src/frontends/android/res/values-ru/strings.xml
@@ -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>
diff --git a/src/frontends/android/res/values-ua/strings.xml b/src/frontends/android/res/values-ua/strings.xml
index e23b9b9b2..d7c238370 100644
--- a/src/frontends/android/res/values-ua/strings.xml
+++ b/src/frontends/android/res/values-ua/strings.xml
@@ -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>
diff --git a/src/frontends/android/res/values/strings.xml b/src/frontends/android/res/values/strings.xml
index 933a80aff..5c8ebab57 100644
--- a/src/frontends/android/res/values/strings.xml
+++ b/src/frontends/android/res/values/strings.xml
@@ -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>
diff --git a/src/frontends/android/src/org/strongswan/android/data/VpnProfile.java b/src/frontends/android/src/org/strongswan/android/data/VpnProfile.java
index 8323826d2..5c64ad0e5 100644
--- a/src/frontends/android/src/org/strongswan/android/data/VpnProfile.java
+++ b/src/frontends/android/src/org/strongswan/android/data/VpnProfile.java
@@ -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()
{
diff --git a/src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java b/src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java
index 6fd68d0c8..45e9b8650 100644
--- a/src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java
+++ b/src/frontends/android/src/org/strongswan/android/data/VpnProfileDataSource.java
@@ -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);
+ }
}
diff --git a/src/frontends/android/src/org/strongswan/android/data/VpnType.java b/src/frontends/android/src/org/strongswan/android/data/VpnType.java
index bffa8384c..bb7fd09f3 100644
--- a/src/frontends/android/src/org/strongswan/android/data/VpnType.java
+++ b/src/frontends/android/src/org/strongswan/android/data/VpnType.java
@@ -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.
diff --git a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
index 7cdaee735..e5241d5a7 100644
--- a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
+++ b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
@@ -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)
+ {
+ 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(route.mAddress, route.mPrefix);
+ 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");
}
}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java b/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java
index 8ea07f4c0..ebe1d0080 100644
--- a/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java
+++ b/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.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
@@ -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());
}
/**
diff --git a/src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java b/src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java
index 39f86b460..5b1799744 100644
--- a/src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java
+++ b/src/frontends/android/src/org/strongswan/android/ui/ImcStateFragment.java
@@ -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())
{
diff --git a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
index 41cd6e98c..a8b3daa06 100644
--- a/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
+++ b/src/frontends/android/src/org/strongswan/android/ui/VpnProfileDetailActivity.java
@@ -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
@@ -315,6 +340,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
diff --git a/src/frontends/android/src/org/strongswan/android/utils/SettingsWriter.java b/src/frontends/android/src/org/strongswan/android/utils/SettingsWriter.java
new file mode 100644
index 000000000..01c0ab8a8
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/utils/SettingsWriter.java
@@ -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>();
+ }
+}
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index 5de713143..0284c094a 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -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
diff --git a/src/include/linux/types.h b/src/include/linux/types.h
deleted file mode 100644
index 22cfdc05e..000000000
--- a/src/include/linux/types.h
+++ /dev/null
@@ -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 */
diff --git a/src/libstrongswan/settings/settings.c b/src/libstrongswan/settings/settings.c
index acf9160d2..305ebe620 100644
--- a/src/libstrongswan/settings/settings.c
+++ b/src/libstrongswan/settings/settings.c
@@ -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;
+}
diff --git a/src/libstrongswan/settings/settings.h b/src/libstrongswan/settings/settings.h
index 3b87c8feb..4ef80d0f6 100644
--- a/src/libstrongswan/settings/settings.h
+++ b/src/libstrongswan/settings/settings.h
@@ -335,6 +335,50 @@ struct settings_t {
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.
*/
void (*destroy)(settings_t *this);
@@ -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_ @}*/
diff --git a/src/libstrongswan/settings/settings_lexer.l b/src/libstrongswan/settings/settings_lexer.l
index 176387f1f..ce9d4eedc 100644
--- a/src/libstrongswan/settings/settings_lexer.l
+++ b/src/libstrongswan/settings/settings_lexer.l
@@ -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);
+}
diff --git a/src/libstrongswan/settings/settings_parser.y b/src/libstrongswan/settings/settings_parser.y
index d95a24b2a..96ab36faf 100644
--- a/src/libstrongswan/settings/settings_parser.y
+++ b/src/libstrongswan/settings/settings_parser.y
@@ -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;
+}
diff --git a/src/libstrongswan/tests/suites/test_settings.c b/src/libstrongswan/tests/suites/test_settings.c
index 9601a34a9..ec79a6288 100644
--- a/src/libstrongswan/tests/suites/test_settings.c
+++ b/src/libstrongswan/tests/suites/test_settings.c
@@ -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");
}
@@ -990,6 +1092,12 @@ START_TEST(test_invalid)
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));
ck_assert(!settings->load_files(settings, path, FALSE));
@@ -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);
diff --git a/src/starter/parser/lexer.l b/src/starter/parser/lexer.l
index d967e745b..f70658e68 100644
--- a/src/starter/parser/lexer.l
+++ b/src/starter/parser/lexer.l
@@ -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);
}
}
diff --git a/src/starter/parser/parser.y b/src/starter/parser/parser.y
index 54dedc12b..0b2b3b09f 100644
--- a/src/starter/parser/parser.y
+++ b/src/starter/parser/parser.y
@@ -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
diff --git a/src/starter/tests/suites/test_parser.c b/src/starter/tests/suites/test_parser.c
index 26a41ba55..4ae7b22fa 100644
--- a/src/starter/tests/suites/test_parser.c
+++ b/src/starter/tests/suites/test_parser.c
@@ -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)