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