Fix passing of IKE_SA unique_id over lookip socket
[strongswan.git] / src / libcharon / plugins / lookip / lookip_socket.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "lookip_socket.h"
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/socket.h>
21 #include <sys/un.h>
22 #include <unistd.h>
23 #include <errno.h>
24
25 #include <daemon.h>
26 #include <threading/thread.h>
27 #include <threading/mutex.h>
28 #include <collections/linked_list.h>
29 #include <processing/jobs/callback_job.h>
30
31 #include "lookip_msg.h"
32
33 typedef struct private_lookip_socket_t private_lookip_socket_t;
34
35 /**
36 * Private data of an lookip_socket_t object.
37 */
38 struct private_lookip_socket_t {
39
40 /**
41 * Public lookip_socket_t interface.
42 */
43 lookip_socket_t public;
44
45 /**
46 * lookip
47 */
48 lookip_listener_t *listener;
49
50 /**
51 * lookip unix socket file descriptor
52 */
53 int socket;
54
55 /**
56 * List of registered listeners, as entry_t
57 */
58 linked_list_t *registered;
59
60 /**
61 * List of connected clients, as uintptr_t FD
62 */
63 linked_list_t *connected;
64
65 /**
66 * Mutex to lock clients list
67 */
68 mutex_t *mutex;
69 };
70
71 /**
72 * Open lookip unix socket
73 */
74 static bool open_socket(private_lookip_socket_t *this)
75 {
76 struct sockaddr_un addr;
77 mode_t old;
78
79 addr.sun_family = AF_UNIX;
80 strcpy(addr.sun_path, LOOKIP_SOCKET);
81
82 this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
83 if (this->socket == -1)
84 {
85 DBG1(DBG_CFG, "creating lookip socket failed");
86 return FALSE;
87 }
88 unlink(addr.sun_path);
89 old = umask(~(S_IRWXU | S_IRWXG));
90 if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0)
91 {
92 DBG1(DBG_CFG, "binding lookip socket failed: %s", strerror(errno));
93 close(this->socket);
94 return FALSE;
95 }
96 umask(old);
97 if (chown(addr.sun_path, charon->caps->get_uid(charon->caps),
98 charon->caps->get_gid(charon->caps)) != 0)
99 {
100 DBG1(DBG_CFG, "changing lookip socket permissions failed: %s",
101 strerror(errno));
102 }
103 if (listen(this->socket, 10) < 0)
104 {
105 DBG1(DBG_CFG, "listening on lookip socket failed: %s", strerror(errno));
106 close(this->socket);
107 unlink(addr.sun_path);
108 return FALSE;
109 }
110 return TRUE;
111 }
112
113 /**
114 * Listener callback entry
115 */
116 typedef struct {
117 /* FD to write to */
118 int fd;
119 /* message type to send */
120 int type;
121 /* back pointer to socket, only for subscriptions */
122 private_lookip_socket_t *this;
123 } entry_t;
124
125 /**
126 * Destroy entry
127 */
128 static void entry_destroy(entry_t *this)
129 {
130 close(this->fd);
131 free(this);
132 }
133
134 /**
135 * Callback function for listener
136 */
137 static bool listener_cb(entry_t *entry, bool up, host_t *vip,
138 host_t *other, identification_t *id,
139 char *name, u_int unique_id)
140 {
141 lookip_response_t resp = {
142 .type = entry->type,
143 .unique_id = unique_id,
144 };
145
146 /* filter events */
147 if (up && entry->type == LOOKIP_NOTIFY_DOWN)
148 {
149 return TRUE;
150 }
151 if (!up && entry->type == LOOKIP_NOTIFY_UP)
152 {
153 return TRUE;
154 }
155
156 snprintf(resp.vip, sizeof(resp.vip), "%H", vip);
157 snprintf(resp.ip, sizeof(resp.ip), "%H", other);
158 snprintf(resp.id, sizeof(resp.id), "%Y", id);
159 snprintf(resp.name, sizeof(resp.name), "%s", name);
160
161 switch (send(entry->fd, &resp, sizeof(resp), 0))
162 {
163 case sizeof(resp):
164 return TRUE;
165 case 0:
166 /* client disconnected, adios */
167 break;
168 default:
169 DBG1(DBG_CFG, "sending lookip response failed: %s", strerror(errno));
170 break;
171 }
172 if (entry->this)
173 { /* unregister listener */
174 entry->this->mutex->lock(entry->this->mutex);
175 entry->this->registered->remove(entry->this->registered, entry, NULL);
176 entry->this->mutex->unlock(entry->this->mutex);
177
178 entry_destroy(entry);
179 }
180 return FALSE;
181 }
182
183 /**
184 * Perform a entry lookup
185 */
186 static void query(private_lookip_socket_t *this, int fd, lookip_request_t *req)
187 {
188 entry_t entry = {
189 .fd = fd,
190 .type = LOOKIP_ENTRY,
191 };
192 host_t *vip = NULL;
193 int matches = 0;
194
195 if (req)
196 { /* lookup */
197 req->vip[sizeof(req->vip) - 1] = 0;
198 vip = host_create_from_string(req->vip, 0);
199 if (vip)
200 {
201 matches = this->listener->lookup(this->listener, vip,
202 (void*)listener_cb, &entry);
203 vip->destroy(vip);
204 }
205 if (matches == 0)
206 {
207 lookip_response_t resp = {
208 .type = LOOKIP_NOT_FOUND,
209 };
210
211 snprintf(resp.vip, sizeof(resp.vip), "%s", req->vip);
212 if (send(fd, &resp, sizeof(resp), 0) < 0)
213 {
214 DBG1(DBG_CFG, "sending lookip not-found failed: %s",
215 strerror(errno));
216 }
217 }
218 }
219 else
220 { /* dump */
221 this->listener->lookup(this->listener, NULL,
222 (void*)listener_cb, &entry);
223 }
224 }
225
226 /**
227 * Subscribe to virtual IP events
228 */
229 static void subscribe(private_lookip_socket_t *this, int fd, int type)
230 {
231 entry_t *entry;
232
233 INIT(entry,
234 .fd = fd,
235 .type = type,
236 .this = this,
237 );
238
239 this->mutex->lock(this->mutex);
240 this->registered->insert_last(this->registered, entry);
241 this->mutex->unlock(this->mutex);
242
243 this->listener->add_listener(this->listener, (void*)listener_cb, entry);
244 }
245
246 /**
247 * Check if a client is subscribed for notifications
248 */
249 static bool subscribed(private_lookip_socket_t *this, int fd)
250 {
251 enumerator_t *enumerator;
252 bool subscribed = FALSE;
253 entry_t *entry;
254
255 this->mutex->lock(this->mutex);
256 enumerator = this->registered->create_enumerator(this->registered);
257 while (enumerator->enumerate(enumerator, &entry))
258 {
259 if (entry->fd == fd)
260 {
261 subscribed = TRUE;
262 break;
263 }
264 }
265 enumerator->destroy(enumerator);
266 this->mutex->unlock(this->mutex);
267
268 return subscribed;
269 }
270
271 /**
272 * Create a fd_set from all bound sockets
273 */
274 static int build_fds(private_lookip_socket_t *this, fd_set *fds)
275 {
276 enumerator_t *enumerator;
277 uintptr_t fd;
278 int maxfd;
279
280 FD_ZERO(fds);
281 FD_SET(this->socket, fds);
282 maxfd = this->socket;
283
284 this->mutex->lock(this->mutex);
285 enumerator = this->connected->create_enumerator(this->connected);
286 while (enumerator->enumerate(enumerator, &fd))
287 {
288 FD_SET(fd, fds);
289 maxfd = max(maxfd, fd);
290 }
291 enumerator->destroy(enumerator);
292 this->mutex->unlock(this->mutex);
293
294 return maxfd + 1;
295 }
296
297 /**
298 * Find the socket select()ed
299 */
300 static int scan_fds(private_lookip_socket_t *this, fd_set *fds)
301 {
302 enumerator_t *enumerator;
303 uintptr_t fd;
304 int selected = -1;
305
306 this->mutex->lock(this->mutex);
307 enumerator = this->connected->create_enumerator(this->connected);
308 while (enumerator->enumerate(enumerator, &fd))
309 {
310 if (FD_ISSET(fd, fds))
311 {
312 selected = fd;
313 break;
314 }
315 }
316 enumerator->destroy(enumerator);
317 this->mutex->unlock(this->mutex);
318
319 return selected;
320 }
321
322 /**
323 * Dispatch from a socket, return TRUE to end communication
324 */
325 static bool dispatch(private_lookip_socket_t *this, int fd)
326 {
327 lookip_request_t req;
328 int len;
329
330 len = recv(fd, &req, sizeof(req), 0);
331 if (len != sizeof(req))
332 {
333 if (len != 0)
334 {
335 DBG1(DBG_CFG, "receiving lookip request failed: %s",
336 strerror(errno));
337 }
338 return TRUE;
339 }
340 switch (req.type)
341 {
342 case LOOKIP_LOOKUP:
343 query(this, fd, &req);
344 return FALSE;
345 case LOOKIP_DUMP:
346 query(this, fd, NULL);
347 return FALSE;
348 case LOOKIP_REGISTER_UP:
349 subscribe(this, fd, LOOKIP_NOTIFY_UP);
350 return FALSE;
351 case LOOKIP_REGISTER_DOWN:
352 subscribe(this, fd, LOOKIP_NOTIFY_DOWN);
353 return FALSE;
354 case LOOKIP_END:
355 return TRUE;
356 default:
357 DBG1(DBG_CFG, "received unknown lookip command");
358 return TRUE;
359 }
360 }
361
362 /**
363 * Accept client connections, dispatch
364 */
365 static job_requeue_t receive(private_lookip_socket_t *this)
366 {
367 struct sockaddr_un addr;
368 int fd, maxfd, len;
369 bool oldstate;
370 fd_set fds;
371
372 while (TRUE)
373 {
374 maxfd = build_fds(this, &fds);
375 oldstate = thread_cancelability(TRUE);
376 if (select(maxfd, &fds, NULL, NULL, NULL) <= 0)
377 {
378 thread_cancelability(oldstate);
379 DBG1(DBG_CFG, "selecting lookip sockets failed: %s",
380 strerror(errno));
381 break;
382 }
383 thread_cancelability(oldstate);
384
385 if (FD_ISSET(this->socket, &fds))
386 { /* new connection, accept() */
387 len = sizeof(addr);
388 fd = accept(this->socket, (struct sockaddr*)&addr, &len);
389 if (fd != -1)
390 {
391 this->mutex->lock(this->mutex);
392 this->connected->insert_last(this->connected,
393 (void*)(uintptr_t)fd);
394 this->mutex->unlock(this->mutex);
395 }
396 else
397 {
398 DBG1(DBG_CFG, "accepting lookip connection failed: %s",
399 strerror(errno));
400 }
401 continue;
402 }
403
404 fd = scan_fds(this, &fds);
405 if (fd == -1)
406 {
407 continue;
408 }
409 if (dispatch(this, fd))
410 {
411 this->mutex->lock(this->mutex);
412 this->connected->remove(this->connected, (void*)(uintptr_t)fd, NULL);
413 this->mutex->unlock(this->mutex);
414 if (!subscribed(this, fd))
415 {
416 close(fd);
417 }
418 }
419 }
420 return JOB_REQUEUE_FAIR;
421 }
422
423 METHOD(lookip_socket_t, destroy, void,
424 private_lookip_socket_t *this)
425 {
426 this->registered->destroy_function(this->registered, (void*)entry_destroy);
427 this->connected->destroy(this->connected);
428 this->mutex->destroy(this->mutex);
429 close(this->socket);
430 free(this);
431 }
432
433 /**
434 * See header
435 */
436 lookip_socket_t *lookip_socket_create(lookip_listener_t *listener)
437 {
438 private_lookip_socket_t *this;
439
440 INIT(this,
441 .public = {
442 .destroy = _destroy,
443 },
444 .listener = listener,
445 .registered = linked_list_create(),
446 .connected = linked_list_create(),
447 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
448 );
449
450 if (!open_socket(this))
451 {
452 free(this);
453 return NULL;
454 }
455
456 lib->processor->queue_job(lib->processor,
457 (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this,
458 NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
459
460 return &this->public;
461 }