10307af88a66639f2994c1cd32397f15cc78cc46
[strongswan.git] / src / charon / control / interfaces / dbus_interface.c
1 /**
2 * @file dbus_interface.c
3 *
4 * @brief Implementation of dbus_interface_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2007 Martin Willi
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #define DBUS_API_SUBJECT_TO_CHANGE
24 #include <dbus/dbus.h>
25 #include <NetworkManager/NetworkManager.h>
26 #include <NetworkManager/NetworkManagerVPN.h>
27 #include <stdlib.h>
28
29 #include "dbus_interface.h"
30
31 #include <library.h>
32 #include <daemon.h>
33 #include <processing/jobs/callback_job.h>
34
35
36 #define NM_DBUS_SERVICE_STRONG "org.freedesktop.NetworkManager.strongswan"
37 #define NM_DBUS_INTERFACE_STRONG "org.freedesktop.NetworkManager.strongswan"
38 #define NM_DBUS_PATH_STRONG "/org/freedesktop/NetworkManager/strongswan"
39
40 typedef struct private_dbus_interface_t private_dbus_interface_t;
41
42 /**
43 * Private data of an dbus_interface_t object.
44 */
45 struct private_dbus_interface_t {
46
47 /**
48 * Public part of dbus_t object.
49 */
50 dbus_interface_t public;
51
52 /**
53 * DBUS connection
54 */
55 DBusConnection* conn;
56
57 /**
58 * error value used here and there
59 */
60 DBusError err;
61
62 /**
63 * state of the daemon
64 */
65 NMVPNState state;
66
67 /**
68 * job accepting stroke messages
69 */
70 callback_job_t *job;
71
72 /**
73 * name of the currently active connection
74 */
75 char *name;
76 };
77
78 /**
79 * set daemon state and send StateChange signal to the bus
80 */
81 static void set_state(private_dbus_interface_t *this, NMVPNState state)
82 {
83 DBusMessage* msg;
84
85 msg = dbus_message_new_signal(NM_DBUS_PATH_STRONG, NM_DBUS_INTERFACE_STRONG, NM_DBUS_VPN_SIGNAL_STATE_CHANGE);
86
87 if (!dbus_message_append_args(msg, DBUS_TYPE_UINT32, &this->state,
88 DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID) ||
89 !dbus_connection_send(this->conn, msg, NULL))
90 {
91 DBG1(DBG_CFG, "unable to send DBUS StateChange signal");
92 }
93 dbus_connection_flush(this->conn);
94 dbus_message_unref(msg);
95 this->state = state;
96 }
97
98
99 /**
100 * get the child_cfg with the same name as the peer cfg
101 */
102 static child_cfg_t* get_child_from_peer(peer_cfg_t *peer_cfg, char *name)
103 {
104 child_cfg_t *current, *found = NULL;
105 iterator_t *iterator;
106
107 iterator = peer_cfg->create_child_cfg_iterator(peer_cfg);
108 while (iterator->iterate(iterator, (void**)&current))
109 {
110 if (streq(current->get_name(current), name))
111 {
112 found = current;
113 found->get_ref(found);
114 break;
115 }
116 }
117 iterator->destroy(iterator);
118 return found;
119 }
120
121 /**
122 * logging dummy
123 */
124 static bool dbus_log(void *param, signal_t signal, level_t level,
125 ike_sa_t *ike_sa, char *format, va_list args)
126 {
127 return TRUE;
128 }
129
130
131 /**
132 * process NetworkManagers startConnection method call
133 */
134 static bool start_connection(private_dbus_interface_t *this, DBusMessage* msg)
135 {
136 DBusMessage *reply, *signal;
137 char *name, *user, **data, **passwords, **routes;
138 int data_count, passwords_count, routes_count;
139 u_int32_t me, other, p2p, netmask, mss;
140 char *dev, *domain, *banner;
141 const dbus_int32_t array[] = {};
142 const dbus_int32_t *varray = array;
143 peer_cfg_t *peer_cfg;
144 child_cfg_t *child_cfg;
145 status_t status = FAILED;
146
147 dbus_error_free(&this->err);
148
149 if (!dbus_message_get_args(msg, &this->err,
150 DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &user,
151 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &passwords, &passwords_count,
152 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &data, &data_count,
153 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &routes, &routes_count,
154 DBUS_TYPE_INVALID))
155 {
156 return FALSE;
157 }
158 set_state(this, NM_VPN_STATE_STARTING);
159
160 peer_cfg = charon->backends->get_peer_cfg_by_name(charon->backends, name);
161 if (peer_cfg)
162 {
163 free(this->name);
164 this->name = strdup(peer_cfg->get_name(peer_cfg));
165 child_cfg = get_child_from_peer(peer_cfg, name);
166 if (child_cfg)
167 {
168 status = charon->interfaces->initiate(charon->interfaces, peer_cfg,
169 child_cfg, dbus_log, NULL);
170 }
171 else
172 {
173 peer_cfg->destroy(peer_cfg);
174 }
175 }
176 reply = dbus_message_new_method_return(msg);
177 dbus_connection_send(this->conn, reply, NULL);
178 dbus_message_unref(reply);
179
180 if (status == SUCCESS)
181 {
182
183 set_state(this, NM_VPN_STATE_STARTED);
184 signal = dbus_message_new_signal(NM_DBUS_PATH_STRONG,
185 NM_DBUS_INTERFACE_STRONG,
186 NM_DBUS_VPN_SIGNAL_IP4_CONFIG);
187 me = other = p2p = mss = netmask = 0;
188 dev = domain = banner = "";
189 if (dbus_message_append_args(signal,
190 DBUS_TYPE_UINT32, &other,
191 DBUS_TYPE_STRING, &dev,
192 DBUS_TYPE_UINT32, &me,
193 DBUS_TYPE_UINT32, &p2p,
194 DBUS_TYPE_UINT32, &netmask,
195 DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &varray, 0,
196 DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &varray, 0,
197 DBUS_TYPE_UINT32, &mss,
198 DBUS_TYPE_STRING, &domain,
199 DBUS_TYPE_STRING, &banner, DBUS_TYPE_INVALID))
200 {
201 dbus_connection_send(this->conn, signal, NULL);
202 }
203 dbus_message_unref(signal);
204 }
205 else
206 {
207 set_state(this, NM_VPN_STATE_STOPPED);
208 }
209
210 dbus_connection_flush(this->conn);
211 return TRUE;
212 }
213
214 /**
215 * process NetworkManagers stopConnection method call
216 */
217 static bool stop_connection(private_dbus_interface_t *this, DBusMessage* msg)
218 {
219 u_int32_t id;
220 iterator_t *iterator;
221 ike_sa_t *ike_sa;
222
223 if (this->name == NULL)
224 {
225 return FALSE;
226 }
227
228 dbus_error_free(&this->err);
229
230 set_state(this, NM_VPN_STATE_STOPPING);
231
232 iterator = charon->interfaces->create_ike_sa_iterator(charon->interfaces);
233 while (iterator->iterate(iterator, (void**)&ike_sa))
234 {
235 child_sa_t *child_sa;
236 iterator_t *children;
237
238 if (this->name && streq(this->name, ike_sa->get_name(ike_sa)))
239 {
240 id = ike_sa->get_unique_id(ike_sa);
241 iterator->destroy(iterator);
242 charon->interfaces->terminate_ike(charon->interfaces, id, NULL, NULL);
243 set_state(this, NM_VPN_STATE_STOPPED);
244 return TRUE;;
245 }
246 children = ike_sa->create_child_sa_iterator(ike_sa);
247 while (children->iterate(children, (void**)&child_sa))
248 {
249 if (this->name && streq(this->name, child_sa->get_name(child_sa)))
250 {
251 id = child_sa->get_reqid(child_sa);
252 children->destroy(children);
253 iterator->destroy(iterator);
254 charon->interfaces->terminate_child(charon->interfaces, id, NULL, NULL);
255 set_state(this, NM_VPN_STATE_STOPPED);
256 return TRUE;
257 }
258 }
259 children->destroy(children);
260 }
261 iterator->destroy(iterator);
262 set_state(this, NM_VPN_STATE_STOPPED);
263 return TRUE;
264 }
265
266 /**
267 * process NetworkManagers getState method call
268 */
269 static bool get_state(private_dbus_interface_t *this, DBusMessage* msg)
270 {
271 DBusMessage* reply;
272 reply = dbus_message_new_method_return(msg);
273 if (!reply || !dbus_message_append_args(reply,
274 DBUS_TYPE_UINT32, &this->state,
275 DBUS_TYPE_INVALID))
276 {
277 return FALSE;
278 }
279 dbus_connection_send(this->conn, reply, NULL);
280 return TRUE;
281 }
282
283 /**
284 * Handle incoming messages
285 */
286 static DBusHandlerResult message_handler(DBusConnection *con, DBusMessage *msg,
287 private_dbus_interface_t *this)
288 {
289 bool handled;
290
291 if (dbus_message_is_method_call(msg, NM_DBUS_INTERFACE_STRONG,
292 "startConnection"))
293 {
294 handled = start_connection(this, msg);
295 }
296 else if (dbus_message_is_method_call(msg, NM_DBUS_INTERFACE_STRONG,
297 "stopConnection"))
298 {
299 handled = stop_connection(this, msg);
300 }
301 else if (dbus_message_is_method_call(msg, NM_DBUS_INTERFACE_STRONG,
302 "getState"))
303 {
304 handled = get_state(this, msg);
305 }
306 else
307 {
308 DBG1(DBG_CFG, "ignoring DBUS message %s.%s",
309 dbus_message_get_interface(msg), dbus_message_get_member(msg));
310 handled = FALSE;
311 }
312
313 if (handled)
314 {
315 return DBUS_HANDLER_RESULT_HANDLED;
316 }
317 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
318 }
319
320 /**
321 * Handle received signals
322
323 static DBusHandlerResult signal_handler(DBusConnection *con, DBusMessage *msg,
324 private_dbus_interface_t *this)
325 {
326 bool handled;
327
328 if (dbus_message_is_signal(msg, NM_DBUS_INTERFACE, "VPNConnectionStateChange"))
329 {
330 NMVPNState state;
331 char *name;
332
333 if (dbus_message_get_args(msg, &this->err, DBUS_TYPE_STRING, &name,
334 DBUS_TYPE_UINT32, &state, DBUS_TYPE_INVALID))
335 {
336 DBG1(DBG_CFG, "got state %d for %s", state, name);
337 }
338 handled = TRUE;
339 }
340 else
341 {
342 DBG1(DBG_CFG, "ignoring DBUS signal %s.%s",
343 dbus_message_get_interface(msg), dbus_message_get_member(msg));
344 handled = FALSE;
345 }
346 if (handled)
347 {
348 return DBUS_HANDLER_RESULT_HANDLED;
349 }
350 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
351 } */
352
353 /**
354 * dispatcher function processed by a seperate thread
355 */
356 static job_requeue_t dispatch(private_dbus_interface_t *this)
357 {
358 if (dbus_connection_read_write_dispatch(this->conn, -1))
359 {
360 return JOB_REQUEUE_DIRECT;
361 }
362 return JOB_REQUEUE_NONE;
363 }
364
365 /**
366 * Implementation of interface_t.destroy.
367 */
368 static void destroy(private_dbus_interface_t *this)
369 {
370 this->job->cancel(this->job);
371 dbus_connection_close(this->conn);
372 dbus_error_free(&this->err);
373 dbus_shutdown();
374 free(this->name);
375 free(this);
376 }
377
378 /*
379 * Described in header file
380 */
381 interface_t *interface_create()
382 {
383 int ret;
384 DBusObjectPathVTable v = {NULL, (void*)&message_handler, NULL, NULL, NULL, NULL};
385 private_dbus_interface_t *this = malloc_thing(private_dbus_interface_t);
386
387 this->public.interface.destroy = (void (*)(interface_t*))destroy;
388
389 dbus_error_init(&this->err);
390 this->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &this->err);
391 if (dbus_error_is_set(&this->err))
392 {
393 DBG1(DBG_CFG, "unable to open DBUS connection: %s", this->err.message);
394 charon->kill(charon, "DBUS initialization failed");
395 }
396 dbus_connection_set_exit_on_disconnect(this->conn, FALSE);
397
398 ret = dbus_bus_request_name(this->conn, NM_DBUS_SERVICE_STRONG,
399 DBUS_NAME_FLAG_REPLACE_EXISTING , &this->err);
400 if (dbus_error_is_set(&this->err))
401 {
402 DBG1(DBG_CFG, "unable to set DBUS name: %s", this->err.message);
403 charon->kill(charon, "unable to set DBUS name");
404 }
405 if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
406 {
407 charon->kill(charon, "DBUS name already owned");
408 }
409 if (!dbus_connection_register_object_path(this->conn, NM_DBUS_PATH_STRONG, &v, this))
410 {
411 charon->kill(charon, "unable to register DBUS message handler");
412 }
413 /*
414 if (!dbus_connection_add_filter(this->conn, (void*)signal_handler, this, NULL))
415 {
416 charon->kill(charon, "unable to register DBUS signal handler");
417 }
418
419 dbus_bus_add_match(this->conn, "type='signal', "
420 "interface='" NM_DBUS_INTERFACE_VPN "',"
421 "path='" NM_DBUS_PATH_VPN "'", &this->err);
422 if (dbus_error_is_set (&this->err))
423 {
424 charon->kill(charon, "unable to add DBUS signal match");
425 }*/
426
427 this->name = NULL;
428 this->state = NM_VPN_STATE_INIT;
429 set_state(this, NM_VPN_STATE_STOPPED);
430
431 this->job = callback_job_create((callback_job_cb_t)dispatch, this, NULL, NULL);
432 charon->processor->queue_job(charon->processor, (job_t*)this->job);
433
434 return &this->public.interface;
435 }
436