aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTobias Brunner <tobias@strongswan.org>2012-10-18 12:28:14 +0200
committerTobias Brunner <tobias@strongswan.org>2012-10-18 12:28:14 +0200
commit8bd00205f4f3c0c57eaf49f4d281f52befa9a855 (patch)
treecf6ac06017a3d99ee3f93420072d9902922ff5cf /src
parentbbf90fcc79ce5de807136263713fcf48033a3bc7 (diff)
parent25a413cb96f9711411dd1590d62d323098c267e0 (diff)
downloadstrongswan-8bd00205f4f3c0c57eaf49f4d281f52befa9a855.tar.bz2
strongswan-8bd00205f4f3c0c57eaf49f4d281f52befa9a855.tar.xz
Merge branch 'android-mobility'
This brings support for MOBIKE to the Android app. The app also tries to keep the connection up as long as possible. DNS queries are now handled by a new class that uses independent threads to resolve them, this allows to cancel them e.g. if no network connectivity is available (otherwise the app would block until the DNS query returns).
Diffstat (limited to 'src')
-rw-r--r--src/frontends/android/AndroidManifest.xml1
-rw-r--r--src/frontends/android/jni/Android.mk3
-rw-r--r--src/frontends/android/jni/libandroidbridge/Android.mk1
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_service.c54
-rw-r--r--src/frontends/android/jni/libandroidbridge/backend/android_service.h5
-rw-r--r--src/frontends/android/jni/libandroidbridge/charonservice.c58
-rw-r--r--src/frontends/android/jni/libandroidbridge/charonservice.h11
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c3
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/android_net.c77
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/network_manager.c244
-rw-r--r--src/frontends/android/jni/libandroidbridge/kernel/network_manager.h91
-rw-r--r--src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java54
-rw-r--r--src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java113
-rw-r--r--src/libcharon/bus/bus.h2
-rw-r--r--src/libcharon/daemon.c3
-rw-r--r--src/libcharon/sa/ike_sa.c2
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c14
-rw-r--r--src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c7
-rw-r--r--src/libipsec/ipsec_processor.c4
-rw-r--r--src/libipsec/ipsec_sa.c16
-rw-r--r--src/libipsec/ipsec_sa.h14
-rw-r--r--src/libipsec/ipsec_sa_mgr.c69
-rw-r--r--src/libipsec/ipsec_sa_mgr.h21
-rw-r--r--src/libstrongswan/Android.mk4
-rw-r--r--src/libstrongswan/Makefile.am6
-rw-r--r--src/libstrongswan/host_resolver.c391
-rw-r--r--src/libstrongswan/host_resolver.h60
-rw-r--r--src/libstrongswan/library.c2
-rw-r--r--src/libstrongswan/library.h6
-rw-r--r--src/libstrongswan/threading/mutex.c8
-rw-r--r--src/libstrongswan/threading/rwlock.c7
-rw-r--r--src/libstrongswan/utils.h16
-rw-r--r--src/libstrongswan/utils/host.c50
33 files changed, 1218 insertions, 199 deletions
diff --git a/src/frontends/android/AndroidManifest.xml b/src/frontends/android/AndroidManifest.xml
index f0ed02ff3..4655cd7fc 100644
--- a/src/frontends/android/AndroidManifest.xml
+++ b/src/frontends/android/AndroidManifest.xml
@@ -23,6 +23,7 @@
<uses-sdk android:minSdkVersion="14" />
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:icon="@drawable/ic_launcher"
diff --git a/src/frontends/android/jni/Android.mk b/src/frontends/android/jni/Android.mk
index 326565f2b..40a7a06a3 100644
--- a/src/frontends/android/jni/Android.mk
+++ b/src/frontends/android/jni/Android.mk
@@ -16,11 +16,14 @@ openssl_PATH := $(LOCAL_PATH)/openssl/include
# CFLAGS (partially from a configure run using droid-gcc)
strongswan_CFLAGS := \
+ -Wall \
+ -Wextra \
-Wno-format \
-Wno-pointer-sign \
-Wno-pointer-arith \
-Wno-sign-compare \
-Wno-strict-aliasing \
+ -Wno-unused-parameter \
-DHAVE___BOOL \
-DHAVE_STDBOOL_H \
-DHAVE_ALLOCA_H \
diff --git a/src/frontends/android/jni/libandroidbridge/Android.mk b/src/frontends/android/jni/libandroidbridge/Android.mk
index f47e39656..b1fa4d1cb 100644
--- a/src/frontends/android/jni/libandroidbridge/Android.mk
+++ b/src/frontends/android/jni/libandroidbridge/Android.mk
@@ -11,6 +11,7 @@ backend/android_service.c backend/android_service.h \
charonservice.c charonservice.h \
kernel/android_ipsec.c kernel/android_ipsec.h \
kernel/android_net.c kernel/android_net.h \
+kernel/network_manager.c kernel/network_manager.h \
vpnservice_builder.c vpnservice_builder.h
# build libandroidbridge -------------------------------------------------------
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c
index 2a115d2f9..b00567f60 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c
@@ -59,11 +59,6 @@ struct private_android_service_t {
char *type;
/**
- * local ipv4 address
- */
- char *local_address;
-
- /**
* gateway
*/
char *gateway;
@@ -362,7 +357,6 @@ METHOD(listener_t, child_updown, bool,
{
/* disable the hooks registered to catch initiation failures */
this->public.listener.ike_updown = NULL;
- this->public.listener.ike_state_change = NULL;
if (!setup_tun_device(this, ike_sa, child_sa))
{
DBG1(DBG_DMN, "failed to setup TUN device");
@@ -403,19 +397,6 @@ METHOD(listener_t, ike_updown, bool,
return TRUE;
}
-METHOD(listener_t, ike_state_change, bool,
- private_android_service_t *this, ike_sa_t *ike_sa, ike_sa_state_t state)
-{
- /* this call back is only registered during initiation */
- if (this->ike_sa == ike_sa && state == IKE_DESTROYING)
- {
- charonservice->update_status(charonservice,
- CHARONSERVICE_UNREACHABLE_ERROR);
- return FALSE;
- }
- return TRUE;
-}
-
METHOD(listener_t, alert, bool,
private_android_service_t *this, ike_sa_t *ike_sa, alert_t alert,
va_list args)
@@ -432,6 +413,15 @@ METHOD(listener_t, alert, bool,
charonservice->update_status(charonservice,
CHARONSERVICE_PEER_AUTH_ERROR);
break;
+ case ALERT_PEER_INIT_UNREACHABLE:
+ this->lock->read_lock(this->lock);
+ if (this->tunfd < 0)
+ { /* only handle this if we are not reestablishing the SA */
+ charonservice->update_status(charonservice,
+ CHARONSERVICE_UNREACHABLE_ERROR);
+ }
+ this->lock->unlock(this->lock);
+ break;
default:
break;
}
@@ -455,9 +445,8 @@ METHOD(listener_t, ike_reestablish, bool,
if (this->ike_sa == old)
{
this->ike_sa = new;
- /* re-register hooks to detect initiation failures */
+ /* re-register hook to detect initiation failures */
this->public.listener.ike_updown = _ike_updown;
- this->public.listener.ike_state_change = _ike_state_change;
/* the TUN device will be closed when the new CHILD_SA is established */
}
return TRUE;
@@ -480,13 +469,13 @@ static job_requeue_t initiate(private_android_service_t *this)
}
};
- ike_cfg = ike_cfg_create(TRUE, TRUE, this->local_address, FALSE,
+ ike_cfg = ike_cfg_create(TRUE, TRUE, "0.0.0.0", FALSE,
charon->socket->get_port(charon->socket, FALSE),
this->gateway, FALSE, IKEV2_UDP_PORT);
ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE));
peer_cfg = peer_cfg_create("android", IKEV2, ike_cfg, CERT_SEND_IF_ASKED,
- UNIQUE_REPLACE, 1, /* keyingtries */
+ UNIQUE_REPLACE, 0, /* keyingtries */
36000, 0, /* rekey 10h, reauth none */
600, 600, /* jitter, over 10min */
TRUE, FALSE, /* mobike, aggressive */
@@ -538,10 +527,14 @@ static job_requeue_t initiate(private_android_service_t *this)
peer_cfg->add_auth_cfg(peer_cfg, auth, FALSE);
child_cfg = child_cfg_create("android", &lifetime, NULL, TRUE, MODE_TUNNEL,
- ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
- 0, 0, NULL, NULL, 0);
- child_cfg->add_proposal(child_cfg, proposal_create_default(PROTO_ESP));
- ts = traffic_selector_create_dynamic(0, 0, 65535);
+ ACTION_NONE, ACTION_RESTART, ACTION_RESTART,
+ FALSE, 0, 0, NULL, NULL, 0);
+ /* create an ESP proposal with the algorithms currently supported by
+ * libipsec, no PFS for now */
+ child_cfg->add_proposal(child_cfg, proposal_create_from_string(PROTO_ESP,
+ "aes128-aes192-aes256-sha1-sha256-sha384-sha512"));
+ ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, "0.0.0.0",
+ 0, "255.255.255.255", 65535);
child_cfg->add_traffic_selector(child_cfg, TRUE, ts);
ts = traffic_selector_create_from_string(0, TS_IPV4_ADDR_RANGE, "0.0.0.0",
0, "255.255.255.255", 65535);
@@ -588,7 +581,6 @@ METHOD(android_service_t, destroy, void,
close_tun_device(this);
this->lock->destroy(this->lock);
free(this->type);
- free(this->local_address);
free(this->gateway);
free(this->username);
if (this->password)
@@ -603,8 +595,8 @@ METHOD(android_service_t, destroy, void,
* See header
*/
android_service_t *android_service_create(android_creds_t *creds, char *type,
- char *local_address, char *gateway,
- char *username, char *password)
+ char *gateway, char *username,
+ char *password)
{
private_android_service_t *this;
@@ -614,14 +606,12 @@ android_service_t *android_service_create(android_creds_t *creds, char *type,
.ike_rekey = _ike_rekey,
.ike_reestablish = _ike_reestablish,
.ike_updown = _ike_updown,
- .ike_state_change = _ike_state_change,
.child_updown = _child_updown,
.alert = _alert,
},
.destroy = _destroy,
},
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
- .local_address = local_address,
.username = username,
.password = password,
.gateway = gateway,
diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.h b/src/frontends/android/jni/libandroidbridge/backend/android_service.h
index 52c3dc5c8..1bfdcf994 100644
--- a/src/frontends/android/jni/libandroidbridge/backend/android_service.h
+++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.h
@@ -53,13 +53,12 @@ struct android_service_t {
*
* @param creds Android specific credential set
* @param type VPN type (see VpnType.java)
- * @param local_address local ip address
* @param gateway gateway address
* @param username user name (local identity)
* @param password password (if any)
*/
android_service_t *android_service_create(android_creds_t *creds, char *type,
- char *local_address, char *gateway,
- char *username, char *password);
+ char *gateway, char *username,
+ char *password);
#endif /** ANDROID_SERVICE_H_ @}*/
diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.c b/src/frontends/android/jni/libandroidbridge/charonservice.c
index ef4d42edf..1fde9887b 100644
--- a/src/frontends/android/jni/libandroidbridge/charonservice.c
+++ b/src/frontends/android/jni/libandroidbridge/charonservice.c
@@ -38,7 +38,7 @@
#define ANDROID_DEBUG_LEVEL 1
#define ANDROID_RETRASNMIT_TRIES 3
-#define ANDROID_RETRANSMIT_TIMEOUT 3.0
+#define ANDROID_RETRANSMIT_TIMEOUT 2.0
#define ANDROID_RETRANSMIT_BASE 1.4
typedef struct private_charonservice_t private_charonservice_t;
@@ -74,9 +74,19 @@ struct private_charonservice_t {
vpnservice_builder_t *builder;
/**
+ * NetworkManager instance (accessed via JNI)
+ */
+ network_manager_t *network_manager;
+
+ /**
* CharonVpnService reference
*/
jobject vpn_service;
+
+ /**
+ * Sockets that were bypassed and we keep track for
+ */
+ linked_list_t *sockets;
};
/**
@@ -172,8 +182,10 @@ failed:
return success;
}
-METHOD(charonservice_t, bypass_socket, bool,
- private_charonservice_t *this, int fd, int family)
+/**
+ * Bypass a single socket
+ */
+static bool bypass_single_socket(intptr_t fd, private_charonservice_t *this)
{
JNIEnv *env;
jmethodID method_id;
@@ -188,7 +200,7 @@ METHOD(charonservice_t, bypass_socket, bool,
}
if (!(*env)->CallBooleanMethod(env, this->vpn_service, method_id, fd))
{
- DBG1(DBG_CFG, "VpnService.protect() failed");
+ DBG2(DBG_KNL, "VpnService.protect() failed");
goto failed;
}
androidjni_detach_thread();
@@ -200,6 +212,19 @@ failed:
return FALSE;
}
+METHOD(charonservice_t, bypass_socket, bool,
+ private_charonservice_t *this, int fd, int family)
+{
+ if (fd >= 0)
+ {
+ this->sockets->insert_last(this->sockets, (void*)(intptr_t)fd);
+ return bypass_single_socket((intptr_t)fd, this);
+ }
+ this->sockets->invoke_function(this->sockets, (void*)bypass_single_socket,
+ this);
+ return TRUE;
+}
+
/**
* Converts the given Java array of byte arrays (byte[][]) to a linked list
* of chunk_t objects.
@@ -330,22 +355,26 @@ METHOD(charonservice_t, get_vpnservice_builder, vpnservice_builder_t*,
return this->builder;
}
+METHOD(charonservice_t, get_network_manager, network_manager_t*,
+ private_charonservice_t *this)
+{
+ return this->network_manager;
+}
+
/**
* Initiate a new connection
*
- * @param local local ip address (gets owned)
* @param gateway gateway address (gets owned)
* @param username username (gets owned)
* @param password password (gets owned)
*/
-static void initiate(char *type, char *local, char *gateway,
- char *username, char *password)
+static void initiate(char *type, char *gateway, char *username, char *password)
{
private_charonservice_t *this = (private_charonservice_t*)charonservice;
this->creds->clear(this->creds);
DESTROY_IF(this->service);
- this->service = android_service_create(this->creds, type, local, gateway,
+ this->service = android_service_create(this->creds, type, gateway,
username, password);
}
@@ -400,10 +429,13 @@ static void charonservice_init(JNIEnv *env, jobject service, jobject builder)
.get_user_certificate = _get_user_certificate,
.get_user_key = _get_user_key,
.get_vpnservice_builder = _get_vpnservice_builder,
+ .get_network_manager = _get_network_manager,
},
.attr = android_attr_create(),
.creds = android_creds_create(),
.builder = vpnservice_builder_create(builder),
+ .network_manager = network_manager_create(service),
+ .sockets = linked_list_create(),
.vpn_service = (*env)->NewGlobalRef(env, service),
);
charonservice = &this->public;
@@ -439,6 +471,8 @@ static void charonservice_deinit(JNIEnv *env)
{
private_charonservice_t *this = (private_charonservice_t*)charonservice;
+ this->network_manager->destroy(this->network_manager);
+ this->sockets->destroy(this->sockets);
this->builder->destroy(this->builder);
this->creds->destroy(this->creds);
this->attr->destroy(this->attr);
@@ -555,16 +589,14 @@ JNI_METHOD(CharonVpnService, deinitializeCharon, void)
* Initiate SA
*/
JNI_METHOD(CharonVpnService, initiate, void,
- jstring jtype, jstring jlocal_address, jstring jgateway, jstring jusername,
- jstring jpassword)
+ jstring jtype, jstring jgateway, jstring jusername, jstring jpassword)
{
- char *type, *local_address, *gateway, *username, *password;
+ char *type, *gateway, *username, *password;
type = androidjni_convert_jstring(env, jtype);
- local_address = androidjni_convert_jstring(env, jlocal_address);
gateway = androidjni_convert_jstring(env, jgateway);
username = androidjni_convert_jstring(env, jusername);
password = androidjni_convert_jstring(env, jpassword);
- initiate(type, local_address, gateway, username, password);
+ initiate(type, gateway, username, password);
}
diff --git a/src/frontends/android/jni/libandroidbridge/charonservice.h b/src/frontends/android/jni/libandroidbridge/charonservice.h
index 376f55014..4b5839f5e 100644
--- a/src/frontends/android/jni/libandroidbridge/charonservice.h
+++ b/src/frontends/android/jni/libandroidbridge/charonservice.h
@@ -32,6 +32,7 @@
#define CHARONSERVICE_H_
#include "vpnservice_builder.h"
+#include "kernel/network_manager.h"
#include <library.h>
#include <utils/linked_list.h>
@@ -69,7 +70,9 @@ struct charonservice_t {
/**
* Install a bypass policy for the given socket using the protect() Method
- * of the Android VpnService interface
+ * of the Android VpnService interface.
+ *
+ * Use -1 as fd to re-bypass previously bypassed sockets.
*
* @param fd socket file descriptor
* @param family socket protocol family
@@ -111,6 +114,12 @@ struct charonservice_t {
*/
vpnservice_builder_t *(*get_vpnservice_builder)(charonservice_t *this);
+ /**
+ * Get the current network_manager_t object
+ *
+ * @return NetworkManager instance
+ */
+ network_manager_t *(*get_network_manager)(charonservice_t *this);
};
/**
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c b/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c
index 08cc61610..85fe5d4c1 100644
--- a/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c
+++ b/src/frontends/android/jni/libandroidbridge/kernel/android_ipsec.c
@@ -79,7 +79,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
u_int16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst,
bool encap, bool new_encap, mark_t mark)
{
- return NOT_SUPPORTED;
+ return ipsec->sas->update_sa(ipsec->sas, spi, protocol, cpi, src, dst,
+ new_src, new_dst, encap, new_encap, mark);
}
METHOD(kernel_ipsec_t, query_sa, status_t,
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c
index e29f95510..430c95bc8 100644
--- a/src/frontends/android/jni/libandroidbridge/kernel/android_net.c
+++ b/src/frontends/android/jni/libandroidbridge/kernel/android_net.c
@@ -14,6 +14,15 @@
#include "android_net.h"
+#include "../charonservice.h"
+#include <hydra.h>
+#include <debug.h>
+#include <processing/jobs/callback_job.h>
+#include <threading/mutex.h>
+
+/** delay before firing roam events (ms) */
+#define ROAM_DELAY 100
+
typedef struct private_kernel_android_net_t private_kernel_android_net_t;
struct private_kernel_android_net_t {
@@ -22,8 +31,66 @@ struct private_kernel_android_net_t {
* Public kernel interface
*/
kernel_android_net_t public;
+
+ /**
+ * Reference to NetworkManager object
+ */
+ network_manager_t *network_manager;
+
+ /**
+ * earliest time of the next roam event
+ */
+ timeval_t next_roam;
+
+ /**
+ * mutex to check and update roam event time
+ */
+ mutex_t *mutex;
};
+/**
+ * callback function that raises the delayed roam event
+ */
+static job_requeue_t roam_event()
+{
+ /* this will fail if no connection is up */
+ charonservice->bypass_socket(charonservice, -1, 0);
+ hydra->kernel_interface->roam(hydra->kernel_interface, TRUE);
+ return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Listen for connectivity change events and queue a roam event
+ */
+static void connectivity_cb(private_kernel_android_net_t *this,
+ bool disconnected)
+{
+ timeval_t now;
+ job_t *job;
+
+ time_monotonic(&now);
+ this->mutex->lock(this->mutex);
+ if (!timercmp(&now, &this->next_roam, >))
+ {
+ this->mutex->unlock(this->mutex);
+ return;
+ }
+ timeval_add_ms(&now, ROAM_DELAY);
+ 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);
+}
+
+METHOD(kernel_net_t, get_source_addr, host_t*,
+ private_kernel_android_net_t *this, host_t *dest, host_t *src)
+{
+ return this->network_manager->get_local_address(this->network_manager,
+ dest->get_family(dest) == AF_INET);
+}
+
METHOD(kernel_net_t, add_ip, status_t,
private_kernel_android_net_t *this, host_t *virtual_ip, host_t *iface_ip)
{
@@ -34,6 +101,9 @@ METHOD(kernel_net_t, add_ip, status_t,
METHOD(kernel_net_t, destroy, void,
private_kernel_android_net_t *this)
{
+ this->network_manager->remove_connectivity_cb(this->network_manager,
+ (void*)connectivity_cb);
+ this->mutex->destroy(this->mutex);
free(this);
}
@@ -47,7 +117,7 @@ kernel_android_net_t *kernel_android_net_create()
INIT(this,
.public = {
.interface = {
- .get_source_addr = (void*)return_null,
+ .get_source_addr = _get_source_addr,
.get_nexthop = (void*)return_null,
.get_interface = (void*)return_null,
.create_address_enumerator = (void*)enumerator_create_empty,
@@ -58,7 +128,12 @@ kernel_android_net_t *kernel_android_net_create()
.destroy = _destroy,
},
},
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ .network_manager = charonservice->get_network_manager(charonservice),
);
+ timerclear(&this->next_roam);
+ this->network_manager->add_connectivity_cb(this->network_manager,
+ (void*)connectivity_cb, this);
return &this->public;
};
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c
new file mode 100644
index 000000000..9c97fbb14
--- /dev/null
+++ b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include "network_manager.h"
+
+#include "../android_jni.h"
+#include "../charonservice.h"
+#include <debug.h>
+#include <threading/mutex.h>
+
+typedef struct private_network_manager_t private_network_manager_t;
+
+struct private_network_manager_t {
+
+ /**
+ * Public interface
+ */
+ network_manager_t public;
+
+ /**
+ * Reference to NetworkManager object
+ */
+ jobject obj;
+
+ /**
+ * Java class for NetworkManager
+ */
+ jclass cls;
+
+ /**
+ * Registered callback
+ */
+ struct {
+ connectivity_cb_t cb;
+ void *data;
+ } connectivity_cb;
+
+ /**
+ * Mutex to access callback
+ */
+ mutex_t *mutex;
+};
+
+METHOD(network_manager_t, get_local_address, host_t*,
+ private_network_manager_t *this, bool ipv4)
+{
+ JNIEnv *env;
+ jmethodID method_id;
+ jstring jaddr;
+ char *addr;
+ host_t *host;
+
+ androidjni_attach_thread(&env);
+ method_id = (*env)->GetMethodID(env, this->cls, "getLocalAddress",
+ "(Z)Ljava/lang/String;");
+ if (!method_id)
+ {
+ goto failed;
+ }
+ jaddr = (*env)->CallObjectMethod(env, this->obj, method_id, ipv4);
+ if (!jaddr)
+ {
+ goto failed;
+ }
+ addr = androidjni_convert_jstring(env, jaddr);
+ androidjni_detach_thread();
+ host = host_create_from_string(addr, 0);
+ free(addr);
+ return host;
+
+failed:
+ androidjni_exception_occurred(env);
+ androidjni_detach_thread();
+ return NULL;
+}
+
+JNI_METHOD(NetworkManager, networkChanged, void,
+ bool disconnected)
+{
+ private_network_manager_t *nm;
+
+ nm = (private_network_manager_t*)charonservice->get_network_manager(
+ charonservice);
+ nm->mutex->lock(nm->mutex);
+ if (nm->connectivity_cb.cb)
+ {
+ nm->connectivity_cb.cb(nm->connectivity_cb.data, disconnected);
+ }
+ nm->mutex->unlock(nm->mutex);
+}
+
+METHOD(network_manager_t, add_connectivity_cb, void,
+ private_network_manager_t *this, connectivity_cb_t cb, void *data)
+{
+ this->mutex->lock(this->mutex);
+ if (!this->connectivity_cb.cb)
+ {
+ JNIEnv *env;
+ jmethodID method_id;
+
+ androidjni_attach_thread(&env);
+ method_id = (*env)->GetMethodID(env, this->cls, "Register", "()V");
+ if (!method_id)
+ {
+ androidjni_exception_occurred(env);
+ }
+ else
+ {
+ (*env)->CallVoidMethod(env, this->obj, method_id);
+ if (!androidjni_exception_occurred(env))
+ {
+ this->connectivity_cb.cb = cb;
+ this->connectivity_cb.data = data;
+ }
+ androidjni_detach_thread();
+ }
+ }
+ this->mutex->unlock(this->mutex);
+}
+
+/**
+ * Unregister the NetworkManager via JNI.
+ *
+ * this->mutex has to be locked
+ */
+static void unregister_network_manager(private_network_manager_t *this)
+{
+ JNIEnv *env;
+ jmethodID method_id;
+
+ androidjni_attach_thread(&env);
+ method_id = (*env)->GetMethodID(env, this->cls, "Unregister", "()V");
+ if (!method_id)
+ {
+ androidjni_exception_occurred(env);
+ }
+ else
+ {
+ (*env)->CallVoidMethod(env, this->obj, method_id);
+ androidjni_exception_occurred(env);
+ }
+ androidjni_detach_thread();
+}
+
+METHOD(network_manager_t, remove_connectivity_cb, void,
+ private_network_manager_t *this, connectivity_cb_t cb)
+{
+ this->mutex->lock(this->mutex);
+ if (this->connectivity_cb.cb == cb)
+ {
+ this->connectivity_cb.cb = NULL;
+ unregister_network_manager(this);
+ }
+ this->mutex->unlock(this->mutex);
+}
+
+METHOD(network_manager_t, destroy, void,
+ private_network_manager_t *this)
+{
+ JNIEnv *env;
+
+ this->mutex->lock(this->mutex);
+ if (this->connectivity_cb.cb)
+ {
+ this->connectivity_cb.cb = NULL;
+ unregister_network_manager(this);
+ }
+ this->mutex->unlock(this->mutex);
+
+ androidjni_attach_thread(&env);
+ if (this->obj)
+ {
+ (*env)->DeleteGlobalRef(env, this->obj);
+ }
+ if (this->cls)
+ {
+ (*env)->DeleteGlobalRef(env, this->cls);
+ }
+ androidjni_detach_thread();
+ this->mutex->destroy(this->mutex);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+network_manager_t *network_manager_create(jobject context)
+{
+ private_network_manager_t *this;
+ JNIEnv *env;
+ jmethodID method_id;
+ jobject obj;
+ jclass cls;
+
+ INIT(this,
+ .public = {
+ .get_local_address = _get_local_address,
+ .add_connectivity_cb = _add_connectivity_cb,
+ .remove_connectivity_cb = _remove_connectivity_cb,
+ .destroy = _destroy,
+ },
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ );
+
+ androidjni_attach_thread(&env);
+ cls = (*env)->FindClass(env, JNI_PACKAGE_STRING "/NetworkManager");
+ if (!cls)
+ {
+ goto failed;
+ }
+ this->cls = (*env)->NewGlobalRef(env, cls);
+ method_id = (*env)->GetMethodID(env, cls, "<init>",
+ "(Landroid/content/Context;)V");
+ if (!method_id)
+ {
+ goto failed;
+ }
+ obj = (*env)->NewObject(env, cls, method_id, context);
+ if (!obj)
+ {
+ goto failed;
+ }
+ this->obj = (*env)->NewGlobalRef(env, obj);
+ androidjni_detach_thread();
+ return &this->public;
+
+failed:
+ DBG1(DBG_KNL, "failed to build NetworkManager object");
+ androidjni_exception_occurred(env);
+ androidjni_detach_thread();
+ destroy(this);
+ return NULL;
+};
diff --git a/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h
new file mode 100644
index 000000000..634816405
--- /dev/null
+++ b/src/frontends/android/jni/libandroidbridge/kernel/network_manager.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/**
+ * @defgroup network_manager network_manager
+ * @{ @ingroup kernel_android
+ */
+
+#ifndef NETWORK_MANAGER_H_
+#define NETWORK_MANAGER_H_
+
+#include <jni.h>
+
+#include <library.h>
+#include <utils/host.h>
+
+typedef struct network_manager_t network_manager_t;
+
+/**
+ * Callback called if connectivity changes somehow.
+ *
+ * Implementation should be quick as the call is made by the Java apps main
+ * thread.
+ *
+ * @param data data supplied during registration
+ * @param disconnected TRUE if currently disconnected
+ */
+typedef void (*connectivity_cb_t)(void *data, bool disconnected);
+
+/**
+ * NetworkManager, used to listen for network changes and retrieve local IP
+ * addresses.
+ *
+ * Communicates with NetworkManager via JNI
+ */
+struct network_manager_t {
+
+ /**
+ * Get a local address
+ *
+ * @param ipv4 TRUE to get an IPv4 address
+ * @return the address or NULL if none available
+ */
+ host_t *(*get_local_address)(network_manager_t *this, bool ipv4);
+
+ /**
+ * Register a callback that is called if connectivity changes
+ *
+ * @note Only the first registered callback is currently used
+ *
+ * @param cb callback to register
+ * @param data data provided to callback
+ */
+ void (*add_connectivity_cb)(network_manager_t *this, connectivity_cb_t cb,
+ void *data);
+
+ /**
+ * Unregister a previously registered callback for connectivity changes
+ *
+ * @param cb previously registered callback
+ */
+ void (*remove_connectivity_cb)(network_manager_t *this,
+ connectivity_cb_t cb);
+
+ /**
+ * Destroy a network_manager_t instance
+ */
+ void (*destroy)(network_manager_t *this);
+};
+
+/**
+ * Create a network_manager_t instance
+ *
+ * @param context Context object
+ * @return network_manager_t instance
+ */
+network_manager_t *network_manager_create(jobject context);
+
+#endif /** NETWORK_MANAGER_H_ @}*/
diff --git a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
index 924781948..02db8c494 100644
--- a/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
+++ b/src/frontends/android/src/org/strongswan/android/logic/CharonVpnService.java
@@ -18,14 +18,10 @@
package org.strongswan.android.logic;
import java.io.File;
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
-import java.util.Enumeration;
import org.strongswan.android.data.VpnProfile;
import org.strongswan.android.data.VpnProfileDataSource;
@@ -218,9 +214,7 @@ public class CharonVpnService extends VpnService implements Runnable
initializeCharon(builder, mLogFile);
Log.i(TAG, "charon started");
- String local_address = getLocalIPv4Address();
initiate(mCurrentProfile.getVpnType().getIdentifier(),
- local_address != null ? local_address : "0.0.0.0",
mCurrentProfile.getGateway(), mCurrentProfile.getUsername(),
mCurrentProfile.getPassword());
}
@@ -335,15 +329,7 @@ public class CharonVpnService extends VpnService implements Runnable
switch (status)
{
case STATE_CHILD_SA_DOWN:
- synchronized (mServiceLock)
- {
- /* if we are not actively disconnecting we assume the remote terminated
- * the connection and call disconnect() to deinitialize charon properly */
- if (mService != null && !mIsDisconnecting)
- {
- mService.disconnect();
- }
- }
+ /* we ignore this as we use closeaction=restart */
break;
case STATE_CHILD_SA_UP:
setState(State.CONNECTED);
@@ -486,41 +472,7 @@ public class CharonVpnService extends VpnService implements Runnable
/**
* Initiate VPN, provided by libandroidbridge.so
*/
- public native void initiate(String type, String local_address, String gateway,
- String username, String password);
-
- /**
- * Helper function that retrieves a local IPv4 address.
- *
- * @return string representation of an IPv4 address, or null if none found
- */
- private static String getLocalIPv4Address()
- {
- try
- {
- Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
- while (en.hasMoreElements())
- {
- NetworkInterface intf = en.nextElement();
-
- Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses();
- while (enumIpAddr.hasMoreElements())
- {
- InetAddress inetAddress = enumIpAddr.nextElement();
- if (!inetAddress.isLoopbackAddress() && inetAddress.getAddress().length == 4)
- {
- return inetAddress.getHostAddress().toString();
- }
- }
- }
- }
- catch (SocketException ex)
- {
- ex.printStackTrace();
- return null;
- }
- return null;
- }
+ public native void initiate(String type, String gateway, String username, String password);
/**
* Adapter for VpnService.Builder which is used to access it safely via JNI.
@@ -547,7 +499,7 @@ public class CharonVpnService extends VpnService implements Runnable
Context context = getApplicationContext();
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pending = PendingIntent.getActivity(context, 0, intent,
- Intent.FLAG_ACTIVITY_NEW_TASK);
+ PendingIntent.FLAG_UPDATE_CURRENT);
builder.setConfigureIntent(pending);
return builder;
}
diff --git a/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java b/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java
new file mode 100644
index 000000000..160865fb7
--- /dev/null
+++ b/src/frontends/android/src/org/strongswan/android/logic/NetworkManager.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2012 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.logic;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Enumeration;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+
+public class NetworkManager extends BroadcastReceiver
+{
+ private final Context mContext;
+ private boolean mRegistered;
+
+ public NetworkManager(Context context)
+ {
+ mContext = context;
+ }
+
+ public void Register()
+ {
+ mContext.registerReceiver(this, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+ }
+
+ public void Unregister()
+ {
+ mContext.unregisterReceiver(this);
+ }
+
+ @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());
+ }
+
+ /**
+ * Notify the native parts about a network change
+ *
+ * @param disconnected true if no connection is available at the moment
+ */
+ public native void networkChanged(boolean disconnected);
+
+ /**
+ * Function that retrieves a local address of the given family.
+ *
+ * @param ipv4 true to return an IPv4 address, false for IPv6
+ * @return string representation of an IPv4 address, or null if none found
+ */
+ public String getLocalAddress(boolean ipv4)
+ {
+ try
+ {
+ Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
+ if (en == null)
+ { /* no interfaces at all */
+ return null;
+ }
+ while (en.hasMoreElements())
+ {
+ NetworkInterface intf = en.nextElement();
+ if (intf.isLoopback() || !intf.isUp() ||
+ intf.getName().startsWith("tun"))
+ {
+ continue;
+ }
+ Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses();
+ while (enumIpAddr.hasMoreElements())
+ {
+ InetAddress inetAddress = enumIpAddr.nextElement();
+ if (inetAddress.isLoopbackAddress())
+ {
+ continue;
+ }
+ if ((ipv4 && inetAddress instanceof Inet4Address) ||
+ (!ipv4 && inetAddress instanceof Inet6Address))
+ {
+ return inetAddress.getHostAddress();
+ }
+ }
+ }
+ }
+ catch (SocketException ex)
+ {
+ ex.printStackTrace();
+ return null;
+ }
+ return null;
+ }
+}
diff --git a/src/libcharon/bus/bus.h b/src/libcharon/bus/bus.h
index aba8acdbd..4bde2434b 100644
--- a/src/libcharon/bus/bus.h
+++ b/src/libcharon/bus/bus.h
@@ -90,6 +90,8 @@ enum alert_t {
ALERT_PEER_AUTH_FAILED,
/** failed to resolve peer address, no arguments */
ALERT_PEER_ADDR_FAILED,
+ /** peer did not respond to initial message, current try (int, 0-based) */
+ ALERT_PEER_INIT_UNREACHABLE,
};
/**
diff --git a/src/libcharon/daemon.c b/src/libcharon/daemon.c
index 6e977efc4..9ae56a91d 100644
--- a/src/libcharon/daemon.c
+++ b/src/libcharon/daemon.c
@@ -84,7 +84,8 @@ static void destroy(private_daemon_t *this)
{
/* terminate all idle threads */
lib->processor->set_threads(lib->processor, 0);
-
+ /* make sure nobody waits for a DNS query */
+ lib->hosts->flush(lib->hosts);
/* close all IKE_SAs */
if (this->public.ike_sa_manager)
{
diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c
index 1d49acb52..d9118fc49 100644
--- a/src/libcharon/sa/ike_sa.c
+++ b/src/libcharon/sa/ike_sa.c
@@ -1690,6 +1690,8 @@ METHOD(ike_sa_t, retransmit, status_t,
{
/* retry IKE_SA_INIT/Main Mode if we have multiple keyingtries */
u_int32_t tries = this->peer_cfg->get_keyingtries(this->peer_cfg);
+ charon->bus->alert(charon->bus, ALERT_PEER_INIT_UNREACHABLE,
+ this->keyingtry);
this->keyingtry++;
if (tries == 0 || tries > this->keyingtry)
{
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c
index 3f63a8496..e5f070e13 100644
--- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c
+++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c
@@ -543,12 +543,7 @@ static void queue_route_reinstall(private_kernel_netlink_net_t *this,
time_monotonic(&now);
if (timercmp(&now, &this->last_route_reinstall, >))
{
- now.tv_usec += ROUTE_DELAY * 1000;
- while (now.tv_usec > 1000000)
- {
- now.tv_sec++;
- now.tv_usec -= 1000000;
- }
+ timeval_add_ms(&now, ROUTE_DELAY);
this->last_route_reinstall = now;
job = (job_t*)callback_job_create((callback_job_cb_t)reinstall_routes,
@@ -704,12 +699,7 @@ static void fire_roam_event(private_kernel_netlink_net_t *this, bool address)
this->roam_lock->unlock(this->roam_lock);
return;
}
- now.tv_usec += ROAM_DELAY * 1000;
- while (now.tv_usec > 1000000)
- {
- now.tv_sec++;
- now.tv_usec -= 1000000;
- }
+ timeval_add_ms(&now, ROAM_DELAY);
this->next_roam = now;
this->roam_lock->unlock(this->roam_lock);
diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c
index 16a46bb56..47a8829f7 100644
--- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c
+++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c
@@ -285,12 +285,7 @@ static void fire_roam_event(private_kernel_pfroute_net_t *this, bool address)
time_monotonic(&now);
if (timercmp(&now, &this->last_roam, >))
{
- now.tv_usec += ROAM_DELAY * 1000;
- while (now.tv_usec > 1000000)
- {
- now.tv_sec++;
- now.tv_usec -= 1000000;
- }
+ timeval_add_ms(&now, ROAM_DELAY);
this->last_roam = now;
job = (job_t*)callback_job_create((callback_job_cb_t)roam_event,
diff --git a/src/libipsec/ipsec_processor.c b/src/libipsec/ipsec_processor.c
index a91d9e074..353f0e7e0 100644
--- a/src/libipsec/ipsec_processor.c
+++ b/src/libipsec/ipsec_processor.c
@@ -146,7 +146,9 @@ static job_requeue_t process_inbound(private_ipsec_processor_t *this)
policy->destroy(policy);
break;
}
- DBG1(DBG_ESP, "discarding inbound IP packet due to policy");
+ DBG1(DBG_ESP, "discarding inbound IP packet %H == %H due to "
+ "policy", ip_packet->get_source(ip_packet),
+ ip_packet->get_destination(ip_packet));
/* no matching policy found, fall-through */
}
case IPPROTO_NONE:
diff --git a/src/libipsec/ipsec_sa.c b/src/libipsec/ipsec_sa.c
index cccd16404..66852259c 100644
--- a/src/libipsec/ipsec_sa.c
+++ b/src/libipsec/ipsec_sa.c
@@ -95,6 +95,20 @@ METHOD(ipsec_sa_t, get_destination, host_t*,
return this->dst;
}
+METHOD(ipsec_sa_t, set_source, void,
+ private_ipsec_sa_t *this, host_t *addr)
+{
+ this->src->destroy(this->src);
+ this->src = addr->clone(addr);
+}
+
+METHOD(ipsec_sa_t, set_destination, void,
+ private_ipsec_sa_t *this, host_t *addr)
+{
+ this->dst->destroy(this->dst);
+ this->dst = addr->clone(addr);
+}
+
METHOD(ipsec_sa_t, get_spi, u_int32_t,
private_ipsec_sa_t *this)
{
@@ -202,6 +216,8 @@ ipsec_sa_t *ipsec_sa_create(u_int32_t spi, host_t *src, host_t *dst,
.destroy = _destroy,
.get_source = _get_source,
.get_destination = _get_destination,
+ .set_source = _set_source,
+ .set_destination = _set_destination,
.get_spi = _get_spi,
.get_reqid = _get_reqid,
.get_protocol = _get_protocol,
diff --git a/src/libipsec/ipsec_sa.h b/src/libipsec/ipsec_sa.h
index 5fd03b6e4..271e0129f 100644
--- a/src/libipsec/ipsec_sa.h
+++ b/src/libipsec/ipsec_sa.h
@@ -52,6 +52,20 @@ struct ipsec_sa_t {
host_t *(*get_destination)(ipsec_sa_t *this);
/**
+ * Set the source address for this SA
+ *
+ * @param addr source address of this SA (gets cloned)
+ */
+ void (*set_source)(ipsec_sa_t *this, host_t *addr);
+
+ /**
+ * Set the destination address for this SA
+ *
+ * @param addr destination address of this SA (gets cloned)
+ */
+ void (*set_destination)(ipsec_sa_t *this, host_t *addr);
+
+ /**
* Get the SPI for this SA
*
* @return SPI of this SA
diff --git a/src/libipsec/ipsec_sa_mgr.c b/src/libipsec/ipsec_sa_mgr.c
index e42c77aa5..35ad6c171 100644
--- a/src/libipsec/ipsec_sa_mgr.c
+++ b/src/libipsec/ipsec_sa_mgr.c
@@ -237,29 +237,29 @@ static bool match_entry_by_sa_ptr(ipsec_sa_entry_t *item, ipsec_sa_t *sa)
return item->sa == sa;
}
-static bool match_entry_by_spi_inbound(ipsec_sa_entry_t *item, u_int32_t spi,
- bool inbound)
+static bool match_entry_by_spi_inbound(ipsec_sa_entry_t *item, u_int32_t *spi,
+ bool *inbound)
{
- return item->sa->get_spi(item->sa) == spi &&
- item->sa->is_inbound(item->sa) == inbound;
+ return item->sa->get_spi(item->sa) == *spi &&
+ item->sa->is_inbound(item->sa) == *inbound;
}
-static bool match_entry_by_spi_src_dst(ipsec_sa_entry_t *item, u_int32_t spi,
+static bool match_entry_by_spi_src_dst(ipsec_sa_entry_t *item, u_int32_t *spi,
host_t *src, host_t *dst)
{
- return item->sa->match_by_spi_src_dst(item->sa, spi, src, dst);
+ return item->sa->match_by_spi_src_dst(item->sa, *spi, src, dst);
}
static bool match_entry_by_reqid_inbound(ipsec_sa_entry_t *item,
- u_int32_t reqid, bool inbound)
+ u_int32_t *reqid, bool *inbound)
{
- return item->sa->match_by_reqid(item->sa, reqid, inbound);
+ return item->sa->match_by_reqid(item->sa, *reqid, *inbound);
}
-static bool match_entry_by_spi_dst(ipsec_sa_entry_t *item, u_int32_t spi,
+static bool match_entry_by_spi_dst(ipsec_sa_entry_t *item, u_int32_t *spi,
host_t *dst)
{
- return item->sa->match_by_spi_dst(item->sa, spi, dst);
+ return item->sa->match_by_spi_dst(item->sa, *spi, dst);
}
/**
@@ -381,7 +381,7 @@ static bool allocate_spi(private_ipsec_sa_mgr_t *this, u_int32_t spi)
if (this->allocated_spis->get(this->allocated_spis, &spi) ||
this->sas->find_first(this->sas, (void*)match_entry_by_spi_inbound,
- NULL, spi, TRUE) == SUCCESS)
+ NULL, &spi, TRUE) == SUCCESS)
{
return FALSE;
}
@@ -471,7 +471,7 @@ METHOD(ipsec_sa_mgr_t, add_sa, status_t,
}
if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_src_dst,
- NULL, spi, src, dst) == SUCCESS)
+ NULL, &spi, src, dst) == SUCCESS)
{
this->mutex->unlock(this->mutex);
DBG1(DBG_ESP, "failed to install SAD entry: already installed");
@@ -487,6 +487,44 @@ METHOD(ipsec_sa_mgr_t, add_sa, status_t,
return SUCCESS;
}
+METHOD(ipsec_sa_mgr_t, update_sa, status_t,
+ private_ipsec_sa_mgr_t *this, u_int32_t spi, u_int8_t protocol,
+ u_int16_t cpi, host_t *src, host_t *dst, host_t *new_src, host_t *new_dst,
+ bool encap, bool new_encap, mark_t mark)
+{
+ ipsec_sa_entry_t *entry = NULL;
+
+ DBG2(DBG_ESP, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H",
+ ntohl(spi), src, dst, new_src, new_dst);
+
+ if (!new_encap)
+ {
+ DBG1(DBG_ESP, "failed to update SAD entry: can't deactivate UDP "
+ "encapsulation");
+ return NOT_SUPPORTED;
+ }
+
+ this->mutex->lock(this->mutex);
+ if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_src_dst,
+ (void**)&entry, &spi, src, dst) == SUCCESS &&
+ wait_for_entry(this, entry))
+ {
+ entry->sa->set_source(entry->sa, new_src);
+ entry->sa->set_destination(entry->sa, new_dst);
+ /* checkin the entry */
+ entry->locked = FALSE;
+ entry->condvar->signal(entry->condvar);
+ }
+ this->mutex->unlock(this->mutex);
+
+ if (!entry)
+ {
+ DBG1(DBG_ESP, "failed to update SAD entry: not found");
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
METHOD(ipsec_sa_mgr_t, del_sa, status_t,
private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi,
u_int8_t protocol, u_int16_t cpi, mark_t mark)
@@ -498,7 +536,7 @@ METHOD(ipsec_sa_mgr_t, del_sa, status_t,
enumerator = this->sas->create_enumerator(this->sas);
while (enumerator->enumerate(enumerator, (void**)&current))
{
- if (match_entry_by_spi_src_dst(current, spi, src, dst))
+ if (match_entry_by_spi_src_dst(current, &spi, src, dst))
{
if (wait_remove_entry(this, current))
{
@@ -529,7 +567,7 @@ METHOD(ipsec_sa_mgr_t, checkout_by_reqid, ipsec_sa_t*,
this->mutex->lock(this->mutex);
if (this->sas->find_first(this->sas, (void*)match_entry_by_reqid_inbound,
- (void**)&entry, reqid, inbound) == SUCCESS &&
+ (void**)&entry, &reqid, &inbound) == SUCCESS &&
wait_for_entry(this, entry))
{
sa = entry->sa;
@@ -546,7 +584,7 @@ METHOD(ipsec_sa_mgr_t, checkout_by_spi, ipsec_sa_t*,
this->mutex->lock(this->mutex);
if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_dst,
- (void**)&entry, spi, dst) == SUCCESS &&
+ (void**)&entry, &spi, dst) == SUCCESS &&
wait_for_entry(this, entry))
{
sa = entry->sa;
@@ -609,6 +647,7 @@ ipsec_sa_mgr_t *ipsec_sa_mgr_create()
.public = {
.get_spi = _get_spi,
.add_sa = _add_sa,
+ .update_sa = _update_sa,
.del_sa = _del_sa,
.checkout_by_spi = _checkout_by_spi,
.checkout_by_reqid = _checkout_by_reqid,
diff --git a/src/libipsec/ipsec_sa_mgr.h b/src/libipsec/ipsec_sa_mgr.h
index 303b36f0e..db30a86bb 100644
--- a/src/libipsec/ipsec_sa_mgr.h
+++ b/src/libipsec/ipsec_sa_mgr.h
@@ -86,6 +86,27 @@ struct ipsec_sa_mgr_t {
traffic_selector_t *src_ts, traffic_selector_t *dst_ts);
/**
+ * Update the hosts on an installed SA.
+ *
+ * @param spi SPI of the SA
+ * @param protocol protocol for this SA (ESP/AH)
+ * @param cpi CPI for IPComp, 0 if no IPComp is used
+ * @param src current source address
+ * @param dst current destination address
+ * @param new_src new source address
+ * @param new_dst new destination address
+ * @param encap current use of UDP encapsulation
+ * @param new_encap new use of UDP encapsulation
+ * @param mark optional mark for this SA
+ * @return SUCCESS if operation completed
+ */
+ status_t (*update_sa)(ipsec_sa_mgr_t *this,
+ u_int32_t spi, u_int8_t protocol, u_int16_t cpi,
+ host_t *src, host_t *dst,
+ host_t *new_src, host_t *new_dst,
+ bool encap, bool new_encap, mark_t mark);
+
+ /**
* Delete a previously added SA
*
* @param spi SPI of the SA
diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk
index 4912576df..9c7ef1d0a 100644
--- a/src/libstrongswan/Android.mk
+++ b/src/libstrongswan/Android.mk
@@ -3,8 +3,8 @@ include $(CLEAR_VARS)
# copy-n-paste from Makefile.am
LOCAL_SRC_FILES := \
-library.c chunk.c debug.c enum.c settings.c printf_hook.c asn1/asn1.c \
-asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \
+library.c chunk.c debug.c enum.c host_resolver.c settings.c printf_hook.c \
+asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \
crypto/crypters/crypter.c crypto/hashers/hasher.c crypto/pkcs7.c crypto/pkcs9.c \
crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \
crypto/prfs/prf.c crypto/prfs/mac_prf.c \
diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am
index 463d57d95..4017bfcc2 100644
--- a/src/libstrongswan/Makefile.am
+++ b/src/libstrongswan/Makefile.am
@@ -1,8 +1,8 @@
ipseclib_LTLIBRARIES = libstrongswan.la
libstrongswan_la_SOURCES = \
-library.c chunk.c debug.c enum.c settings.c printf_hook.c asn1/asn1.c \
-asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \
+library.c chunk.c debug.c enum.c host_resolver.c settings.c printf_hook.c \
+asn1/asn1.c asn1/asn1_parser.c asn1/oid.c bio/bio_reader.c bio/bio_writer.c \
crypto/crypters/crypter.c crypto/hashers/hasher.c crypto/pkcs7.c crypto/pkcs9.c \
crypto/proposal/proposal_keywords.c crypto/proposal/proposal_keywords_static.c \
crypto/prfs/prf.c crypto/prfs/mac_prf.c \
@@ -31,7 +31,7 @@ utils/optionsfrom.c utils/capabilities.c utils/backtrace.c utils/tun_device.c
if USE_DEV_HEADERS
strongswan_includedir = ${dev_headers}
nobase_strongswan_include_HEADERS = \
-library.h chunk.h debug.h enum.h settings.h printf_hook.h \
+library.h chunk.h debug.h enum.h host_resolver.h settings.h printf_hook.h \
asn1/asn1.h asn1/asn1_parser.h asn1/oid.h bio/bio_reader.h bio/bio_writer.h \
crypto/crypters/crypter.h crypto/hashers/hasher.h crypto/mac.h \
crypto/pkcs7.h crypto/pkcs9.h crypto/proposal/proposal_keywords.h \
diff --git a/src/libstrongswan/host_resolver.c b/src/libstrongswan/host_resolver.c
new file mode 100644
index 000000000..55b07d318
--- /dev/null
+++ b/src/libstrongswan/host_resolver.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "host_resolver.h"
+
+#include <debug.h>
+#include <library.h>
+#include <threading/condvar.h>
+#include <threading/mutex.h>
+#include <threading/thread.h>
+#include <utils/hashtable.h>
+#include <utils/linked_list.h>
+
+/**
+ * Default minimum and maximum number of threads
+ */
+#define MIN_THREADS_DEFAULT 0
+#define MAX_THREADS_DEFAULT 3
+
+/**
+ * Timeout in seconds to wait for new queries until a thread may be stopped
+ */
+#define NEW_QUERY_WAIT_TIMEOUT 30
+
+typedef struct private_host_resolver_t private_host_resolver_t;
+
+/**
+ * Private data of host_resolver_t
+ */
+struct private_host_resolver_t {
+
+ /**
+ * Public interface
+ */
+ host_resolver_t public;
+
+ /**
+ * Hashtable to check for queued queries, query_t*
+ */
+ hashtable_t *queries;
+
+ /**
+ * Queue for queries, query_t*
+ */
+ linked_list_t *queue;
+
+ /**
+ * Mutex to safely access private data
+ */
+ mutex_t *mutex;
+
+ /**
+ * Condvar to signal arrival of new queries
+ */
+ condvar_t *new_query;
+
+ /**
+ * Minimum number of resolver threads
+ */
+ u_int min_threads;
+
+ /**
+ * Maximum number of resolver threads
+ */
+ u_int max_threads;
+
+ /**
+ * Current number of threads
+ */
+ u_int threads;
+
+ /**
+ * Current number of busy threads
+ */
+ u_int busy_threads;
+
+ /**
+ * Pool of threads, thread_t*
+ */
+ linked_list_t *pool;
+
+ /**
+ * TRUE if no new queries are accepted
+ */
+ bool disabled;
+
+};
+
+typedef struct {
+ /** DNS name we are looking for */
+ char *name;
+ /** address family we request */
+ int family;
+ /** Condvar to signal completion of a query */
+ condvar_t *done;
+ /** refcount */
+ refcount_t refcount;
+ /** the result if successful */
+ host_t *result;
+} query_t;
+
+/**
+ * Destroy the given query_t object if refcount is zero
+ */
+static void query_destroy(query_t *this)
+{
+ if (ref_put(&this->refcount))
+ {
+ DESTROY_IF(this->result);
+ this->done->destroy(this->done);
+ free(this->name);
+ free(this);
+ }
+}
+
+/**
+ * Signals all waiting threads and destroys the query
+ */
+static void query_signal_and_destroy(query_t *this)
+{
+ this->done->broadcast(this->done);
+ query_destroy(this);
+}
+
+/**
+ * Hash a queued query
+ */
+static u_int query_hash(query_t *this)
+{
+ return chunk_hash_inc(chunk_create(this->name, strlen(this->name)),
+ chunk_hash(chunk_from_thing(this->family)));
+}
+
+/**
+ * Compare two queued queries
+ */
+static bool query_equals(query_t *this, query_t *other)
+{
+ return this->family == other->family && streq(this->name, other->name);
+}
+
+/**
+ * Main function of resolver threads
+ */
+static void *resolve_hosts(private_host_resolver_t *this)
+{
+ struct addrinfo hints, *result;
+ query_t *query;
+ int error;
+ bool old, timed_out;
+
+ while (TRUE)
+ {
+ this->mutex->lock(this->mutex);
+ thread_cleanup_push((thread_cleanup_t)this->mutex->unlock, this->mutex);
+ while (this->queue->remove_first(this->queue,
+ (void**)&query) != SUCCESS)
+ {
+ old = thread_cancelability(TRUE);
+ timed_out = this->new_query->timed_wait(this->new_query,
+ this->mutex, NEW_QUERY_WAIT_TIMEOUT * 1000);
+ thread_cancelability(old);
+ if (this->disabled)
+ {
+ thread_cleanup_pop(TRUE);
+ return NULL;
+ }
+ else if (timed_out && (this->threads > this->min_threads))
+ { /* terminate this thread by detaching it */
+ thread_t *thread = thread_current();
+
+ this->threads--;
+ this->pool->remove(this->pool, thread, NULL);
+ thread_cleanup_pop(TRUE);
+ thread->detach(thread);
+ return NULL;
+ }
+ }
+ this->busy_threads++;
+ thread_cleanup_pop(TRUE);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = query->family;
+ hints.ai_socktype = SOCK_DGRAM;
+
+ thread_cleanup_push((thread_cleanup_t)query_signal_and_destroy, query);
+ old = thread_cancelability(TRUE);
+ error = getaddrinfo(query->name, NULL, &hints, &result);
+ thread_cancelability(old);
+ thread_cleanup_pop(FALSE);
+
+ this->mutex->lock(this->mutex);
+ this->busy_threads--;
+ if (error != 0)
+ {
+ DBG1(DBG_LIB, "resolving '%s' failed: %s", query->name,
+ gai_strerror(error));
+ }
+ else
+ { /* result is a linked list, but we use only the first address */
+ query->result = host_create_from_sockaddr(result->ai_addr);
+ freeaddrinfo(result);
+ }
+ this->queries->remove(this->queries, query);
+ query->done->broadcast(query->done);
+ this->mutex->unlock(this->mutex);
+ query_destroy(query);
+ }
+ return NULL;
+}
+
+/**
+ * Try to convert IP addresses directly
+ */
+static host_t *try_numeric_lookup(char *name, int family)
+{
+ struct addrinfo hints, *result;
+ int error;
+ host_t *host;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ error = getaddrinfo(name, NULL, &hints, &result);
+ if (error != 0)
+ { /* not an IP address */
+ return NULL;
+ }
+ else
+ { /* result is a linked list, but we use only the first address */
+ host = host_create_from_sockaddr(result->ai_addr);
+ freeaddrinfo(result);
+ }
+ return host;
+}
+
+METHOD(host_resolver_t, resolve, host_t*,
+ private_host_resolver_t *this, char *name, int family)
+{
+ query_t *query, lookup = {
+ .name = name,
+ .family = family,
+ };
+ host_t *result;
+
+ if (streq(name, "%any") || streq(name, "0.0.0.0"))
+ {
+ return host_create_any(family ? family : AF_INET);
+ }
+ if (streq(name, "%any6") || streq(name, "::"))
+ {
+ return host_create_any(family ? family : AF_INET6);
+ }
+ if (family == AF_INET && strchr(name, ':'))
+ { /* do not try to convert v6 addresses for v4 family */
+ return NULL;
+ }
+ result = try_numeric_lookup(name, family);
+ if (result)
+ { /* shortcut for numeric IP addresses */
+ return result;
+ }
+ this->mutex->lock(this->mutex);
+ if (this->disabled)
+ {
+ this->mutex->unlock(this->mutex);
+ return NULL;
+ }
+ query = this->queries->get(this->queries, &lookup);
+ if (!query)
+ {
+ INIT(query,
+ .name = strdup(name),
+ .family = family,
+ .done = condvar_create(CONDVAR_TYPE_DEFAULT),
+ .refcount = 1,
+ );
+ this->queries->put(this->queries, query, query);
+ this->queue->insert_last(this->queue, query);
+ this->new_query->signal(this->new_query);
+ }
+ ref_get(&query->refcount);
+ if (this->busy_threads == this->threads &&
+ this->threads < this->max_threads)
+ {
+ thread_t *thread;
+
+ thread = thread_create((thread_main_t)resolve_hosts, this);
+ if (thread)
+ {
+ this->threads++;
+ this->pool->insert_last(this->pool, thread);
+ }
+ }
+ query->done->wait(query->done, this->mutex);
+ this->mutex->unlock(this->mutex);
+
+ result = query->result ? query->result->clone(query->result) : NULL;
+ query_destroy(query);
+ return result;
+}
+
+METHOD(host_resolver_t, flush, void,
+ private_host_resolver_t *this)
+{
+ enumerator_t *enumerator;
+ query_t *query;
+
+ this->mutex->lock(this->mutex);
+ enumerator = this->queries->create_enumerator(this->queries);
+ while (enumerator->enumerate(enumerator, &query, NULL))
+ { /* use the hashtable here as we also want to signal dequeued queries */
+ this->queries->remove_at(this->queries, enumerator);
+ query->done->broadcast(query->done);
+ }
+ enumerator->destroy(enumerator);
+ this->queue->destroy_function(this->queue, (void*)query_destroy);
+ this->queue = linked_list_create();
+ this->disabled = TRUE;
+ /* this will already terminate most idle threads */
+ this->new_query->broadcast(this->new_query);
+ this->mutex->unlock(this->mutex);
+}
+
+METHOD(host_resolver_t, destroy, void,
+ private_host_resolver_t *this)
+{
+ thread_t *thread;
+
+ flush(this);
+ this->pool->invoke_offset(this->pool, offsetof(thread_t, cancel));
+ while (this->pool->remove_first(this->pool, (void**)&thread) == SUCCESS)
+ {
+ thread->join(thread);
+ }
+ this->pool->destroy(this->pool);
+ this->queue->destroy(this->queue);
+ this->queries->destroy(this->queries);
+ this->new_query->destroy(this->new_query);
+ this->mutex->destroy(this->mutex);
+ free(this);
+}
+
+/*
+ * Described in header
+ */
+host_resolver_t *host_resolver_create()
+{
+ private_host_resolver_t *this;
+
+ INIT(this,
+ .public = {
+ .resolve = _resolve,
+ .flush = _flush,
+ .destroy = _destroy,
+ },
+ .queries = hashtable_create((hashtable_hash_t)query_hash,
+ (hashtable_equals_t)query_equals, 8),
+ .queue = linked_list_create(),
+ .pool = linked_list_create(),
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ .new_query = condvar_create(CONDVAR_TYPE_DEFAULT),
+ );
+
+ this->min_threads = max(0, lib->settings->get_int(lib->settings,
+ "libstrongswan.host_resolver.min_threads",
+ MIN_THREADS_DEFAULT));
+ this->max_threads = max(this->min_threads ?: 1,
+ lib->settings->get_int(lib->settings,
+ "libstrongswan.host_resolver.max_threads",
+ MAX_THREADS_DEFAULT));
+ return &this->public;
+}
diff --git a/src/libstrongswan/host_resolver.h b/src/libstrongswan/host_resolver.h
new file mode 100644
index 000000000..f7b8c7e9f
--- /dev/null
+++ b/src/libstrongswan/host_resolver.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+/**
+ * @defgroup host_resolver host_resolver
+ * @{ @ingroup libstrongswan
+ */
+
+#ifndef HOST_RESOLVER_H_
+#define HOST_RESOLVER_H_
+
+#include "utils/host.h"
+
+typedef struct host_resolver_t host_resolver_t;
+
+/**
+ * Resolve hosts by DNS name but do so in a separate thread (calling
+ * getaddrinfo(3) directly might block indefinitely, or at least a very long
+ * time if no DNS servers are reachable).
+ */
+struct host_resolver_t {
+
+ /**
+ * Resolve host from the given DNS name.
+ *
+ * @param name name to lookup
+ * @param family requested address family
+ * @return resolved host or NULL if failed or canceled
+ */
+ host_t *(*resolve)(host_resolver_t *this, char *name, int family);
+
+ /**
+ * Flush the queue of queries. No new queries will be accepted afterwards.
+ */
+ void (*flush)(host_resolver_t *this);
+
+ /**
+ * Destroy a host_resolver_t.
+ */
+ void (*destroy)(host_resolver_t *this);
+};
+
+/**
+ * Create a host_resolver_t instance.
+ */
+host_resolver_t *host_resolver_create();
+
+#endif /** HOST_RESOLVER_H_ @}*/
diff --git a/src/libstrongswan/library.c b/src/libstrongswan/library.c
index 1179b468c..a42d68cbc 100644
--- a/src/libstrongswan/library.c
+++ b/src/libstrongswan/library.c
@@ -68,6 +68,7 @@ void library_deinit()
this->public.scheduler->destroy(this->public.scheduler);
this->public.processor->destroy(this->public.processor);
this->public.plugins->destroy(this->public.plugins);
+ this->public.hosts->destroy(this->public.hosts);
this->public.settings->destroy(this->public.settings);
this->public.credmgr->destroy(this->public.credmgr);
this->public.creds->destroy(this->public.creds);
@@ -183,6 +184,7 @@ bool library_init(char *settings)
this->objects = hashtable_create((hashtable_hash_t)hash,
(hashtable_equals_t)equals, 4);
this->public.settings = settings_create(settings);
+ this->public.hosts = host_resolver_create();
this->public.proposal = proposal_keywords_create();
this->public.crypto = crypto_factory_create();
this->public.creds = credential_factory_create();
diff --git a/src/libstrongswan/library.h b/src/libstrongswan/library.h
index b79bd91be..5bd0d67eb 100644
--- a/src/libstrongswan/library.h
+++ b/src/libstrongswan/library.h
@@ -77,6 +77,7 @@
#include "printf_hook.h"
#include "utils.h"
#include "chunk.h"
+#include "host_resolver.h"
#include "settings.h"
#include "integrity_checker.h"
#include "processing/processor.h"
@@ -171,6 +172,11 @@ struct library_t {
scheduler_t *scheduler;
/**
+ * resolve hosts by DNS name
+ */
+ host_resolver_t *hosts;
+
+ /**
* various settings loaded from settings file
*/
settings_t *settings;
diff --git a/src/libstrongswan/threading/mutex.c b/src/libstrongswan/threading/mutex.c
index 2ef918a28..df0cd27bf 100644
--- a/src/libstrongswan/threading/mutex.c
+++ b/src/libstrongswan/threading/mutex.c
@@ -282,13 +282,7 @@ METHOD(condvar_t, timed_wait, bool,
ms = timeout % 1000;
tv.tv_sec += s;
- tv.tv_usec += ms * 1000;
-
- if (tv.tv_usec > 1000000 /* 1s */)
- {
- tv.tv_usec -= 1000000;
- tv.tv_sec++;
- }
+ timeval_add_ms(&tv, ms);
return timed_wait_abs(this, mutex, tv);
}
diff --git a/src/libstrongswan/threading/rwlock.c b/src/libstrongswan/threading/rwlock.c
index 7097a8e8c..9ce9c6a71 100644
--- a/src/libstrongswan/threading/rwlock.c
+++ b/src/libstrongswan/threading/rwlock.c
@@ -433,13 +433,8 @@ METHOD(rwlock_condvar_t, timed_wait, bool,
ms = timeout % 1000;
tv.tv_sec += s;
- tv.tv_usec += ms * 1000;
+ timeval_add_ms(&tv, ms);
- if (tv.tv_usec > 1000000 /* 1s */)
- {
- tv.tv_usec -= 1000000;
- tv.tv_sec++;
- }
return timed_wait_abs(this, lock, tv);
}
diff --git a/src/libstrongswan/utils.h b/src/libstrongswan/utils.h
index f47c65ac1..4e2065f63 100644
--- a/src/libstrongswan/utils.h
+++ b/src/libstrongswan/utils.h
@@ -455,6 +455,22 @@ void closefrom(int lowfd);
time_t time_monotonic(timeval_t *tv);
/**
+ * Add the given number of milliseconds to the given timeval struct
+ *
+ * @param tv timeval struct to modify
+ * @param ms number of milliseconds
+ */
+static inline void timeval_add_ms(timeval_t *tv, u_int ms)
+{
+ tv->tv_usec += ms * 1000;
+ while (tv->tv_usec > 1000000 /* 1s */)
+ {
+ tv->tv_usec -= 1000000;
+ tv->tv_sec++;
+ }
+}
+
+/**
* returns null
*/
void *return_null();
diff --git a/src/libstrongswan/utils/host.c b/src/libstrongswan/utils/host.c
index e17b6ad02..1d0614001 100644
--- a/src/libstrongswan/utils/host.c
+++ b/src/libstrongswan/utils/host.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2009 Tobias Brunner
+ * Copyright (C) 2006-2012 Tobias Brunner
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
@@ -16,14 +16,10 @@
* for more details.
*/
-#define _GNU_SOURCE
-#include <sys/socket.h>
-#include <netdb.h>
-#include <string.h>
-
#include "host.h"
#include <debug.h>
+#include <library.h>
#define IPV4_LEN 4
#define IPV6_LEN 16
@@ -450,48 +446,14 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr)
*/
host_t *host_create_from_dns(char *string, int af, u_int16_t port)
{
- private_host_t *this;
- struct addrinfo hints, *result;
- int error;
-
- if (streq(string, "%any"))
- {
- return host_create_any_port(af ? af : AF_INET, port);
- }
- if (streq(string, "%any6"))
- {
- return host_create_any_port(af ? af : AF_INET6, port);
- }
- if (af == AF_INET && strchr(string, ':'))
- { /* do not try to convert v6 addresses for v4 family */
- return NULL;
- }
+ host_t *this;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = af;
- error = getaddrinfo(string, NULL, &hints, &result);
- if (error != 0)
- {
- DBG1(DBG_LIB, "resolving '%s' failed: %s", string, gai_strerror(error));
- return NULL;
- }
- /* result is a linked list, but we use only the first address */
- this = (private_host_t*)host_create_from_sockaddr(result->ai_addr);
- freeaddrinfo(result);
+ this = lib->hosts->resolve(lib->hosts, string, af);
if (this)
{
- switch (this->address.sa_family)
- {
- case AF_INET:
- this->address4.sin_port = htons(port);
- break;
- case AF_INET6:
- this->address6.sin6_port = htons(port);
- break;
- }
- return &this->public;
+ this->set_port(this, port);
}
- return NULL;
+ return this;
}
/*