Moved data structures to new collections subfolder
[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, char *name)
139 {
140 lookip_response_t resp = {
141 .type = entry->type,
142 };
143
144 /* filter events */
145 if (up && entry->type == LOOKIP_NOTIFY_DOWN)
146 {
147 return TRUE;
148 }
149 if (!up && entry->type == LOOKIP_NOTIFY_UP)
150 {
151 return TRUE;
152 }
153
154 snprintf(resp.vip, sizeof(resp.vip), "%H", vip);
155 snprintf(resp.ip, sizeof(resp.ip), "%H", other);
156 snprintf(resp.id, sizeof(resp.id), "%Y", id);
157 snprintf(resp.name, sizeof(resp.name), "%s", name);
158
159 switch (send(entry->fd, &resp, sizeof(resp), 0))
160 {
161 case sizeof(resp):
162 return TRUE;
163 case 0:
164 /* client disconnected, adios */
165 break;
166 default:
167 DBG1(DBG_CFG, "sending lookip response failed: %s", strerror(errno));
168 break;
169 }
170 if (entry->this)
171 { /* unregister listener */
172 entry->this->mutex->lock(entry->this->mutex);
173 entry->this->registered->remove(entry->this->registered, entry, NULL);
174 entry->this->mutex->unlock(entry->this->mutex);
175
176 entry_destroy(entry);
177 }
178 return FALSE;
179 }
180
181 /**
182 * Perform a entry lookup
183 */
184 static void query(private_lookip_socket_t *this, int fd, lookip_request_t *req)
185 {
186 entry_t entry = {
187 .fd = fd,
188 .type = LOOKIP_ENTRY,
189 };
190 host_t *vip = NULL;
191 int matches = 0;
192
193 if (req)
194 { /* lookup */
195 req->vip[sizeof(req->vip) - 1] = 0;
196 vip = host_create_from_string(req->vip, 0);
197 if (vip)
198 {
199 matches = this->listener->lookup(this->listener, vip,
200 (void*)listener_cb, &entry);
201 vip->destroy(vip);
202 }
203 if (matches == 0)
204 {
205 lookip_response_t resp = {
206 .type = LOOKIP_NOT_FOUND,
207 };
208
209 snprintf(resp.vip, sizeof(resp.vip), "%s", req->vip);
210 if (send(fd, &resp, sizeof(resp), 0) < 0)
211 {
212 DBG1(DBG_CFG, "sending lookip not-found failed: %s",
213 strerror(errno));
214 }
215 }
216 }
217 else
218 { /* dump */
219 this->listener->lookup(this->listener, NULL,
220 (void*)listener_cb, &entry);
221 }
222 }
223
224 /**
225 * Subscribe to virtual IP events
226 */
227 static void subscribe(private_lookip_socket_t *this, int fd, int type)
228 {
229 entry_t *entry;
230
231 INIT(entry,
232 .fd = fd,
233 .type = type,
234 .this = this,
235 );
236
237 this->mutex->lock(this->mutex);
238 this->registered->insert_last(this->registered, entry);
239 this->mutex->unlock(this->mutex);
240
241 this->listener->add_listener(this->listener, (void*)listener_cb, entry);
242 }
243
244 /**
245 * Check if a client is subscribed for notifications
246 */
247 static bool subscribed(private_lookip_socket_t *this, int fd)
248 {
249 enumerator_t *enumerator;
250 bool subscribed = FALSE;
251 entry_t *entry;
252
253 this->mutex->lock(this->mutex);
254 enumerator = this->registered->create_enumerator(this->registered);
255 while (enumerator->enumerate(enumerator, &entry))
256 {
257 if (entry->fd == fd)
258 {
259 subscribed = TRUE;
260 break;
261 }
262 }
263 enumerator->destroy(enumerator);
264 this->mutex->unlock(this->mutex);
265
266 return subscribed;
267 }
268
269 /**
270 * Create a fd_set from all bound sockets
271 */
272 static int build_fds(private_lookip_socket_t *this, fd_set *fds)
273 {
274 enumerator_t *enumerator;
275 uintptr_t fd;
276 int maxfd;
277
278 FD_ZERO(fds);
279 FD_SET(this->socket, fds);
280 maxfd = this->socket;
281
282 this->mutex->lock(this->mutex);
283 enumerator = this->connected->create_enumerator(this->connected);
284 while (enumerator->enumerate(enumerator, &fd))
285 {
286 FD_SET(fd, fds);
287 maxfd = max(maxfd, fd);
288 }
289 enumerator->destroy(enumerator);
290 this->mutex->unlock(this->mutex);
291
292 return maxfd + 1;
293 }
294
295 /**
296 * Find the socket select()ed
297 */
298 static int scan_fds(private_lookip_socket_t *this, fd_set *fds)
299 {
300 enumerator_t *enumerator;
301 uintptr_t fd;
302 int selected = -1;
303
304 this->mutex->lock(this->mutex);
305 enumerator = this->connected->create_enumerator(this->connected);
306 while (enumerator->enumerate(enumerator, &fd))
307 {
308 if (FD_ISSET(fd, fds))
309 {
310 selected = fd;
311 break;
312 }
313 }
314 enumerator->destroy(enumerator);
315 this->mutex->unlock(this->mutex);
316
317 return selected;
318 }
319
320 /**
321 * Dispatch from a socket, return TRUE to end communication
322 */
323 static bool dispatch(private_lookip_socket_t *this, int fd)
324 {
325 lookip_request_t req;
326 int len;
327
328 len = recv(fd, &req, sizeof(req), 0);
329 if (len != sizeof(req))
330 {
331 if (len != 0)
332 {
333 DBG1(DBG_CFG, "receiving lookip request failed: %s",
334 strerror(errno));
335 }
336 return TRUE;
337 }
338 switch (req.type)
339 {
340 case LOOKIP_LOOKUP:
341 query(this, fd, &req);
342 return FALSE;
343 case LOOKIP_DUMP:
344 query(this, fd, NULL);
345 return FALSE;
346 case LOOKIP_REGISTER_UP:
347 subscribe(this, fd, LOOKIP_NOTIFY_UP);
348 return FALSE;
349 case LOOKIP_REGISTER_DOWN:
350 subscribe(this, fd, LOOKIP_NOTIFY_DOWN);
351 return FALSE;
352 case LOOKIP_END:
353 return TRUE;
354 default:
355 DBG1(DBG_CFG, "received unknown lookip command");
356 return TRUE;
357 }
358 }
359
360 /**
361 * Accept client connections, dispatch
362 */
363 static job_requeue_t receive(private_lookip_socket_t *this)
364 {
365 struct sockaddr_un addr;
366 int fd, maxfd, len;
367 bool oldstate;
368 fd_set fds;
369
370 while (TRUE)
371 {
372 maxfd = build_fds(this, &fds);
373 oldstate = thread_cancelability(TRUE);
374 if (select(maxfd, &fds, NULL, NULL, NULL) <= 0)
375 {
376 thread_cancelability(oldstate);
377 DBG1(DBG_CFG, "selecting lookip sockets failed: %s",
378 strerror(errno));
379 break;
380 }
381 thread_cancelability(oldstate);
382
383 if (FD_ISSET(this->socket, &fds))
384 { /* new connection, accept() */
385 len = sizeof(addr);
386 fd = accept(this->socket, (struct sockaddr*)&addr, &len);
387 if (fd != -1)
388 {
389 this->mutex->lock(this->mutex);
390 this->connected->insert_last(this->connected,
391 (void*)(uintptr_t)fd);
392 this->mutex->unlock(this->mutex);
393 }
394 else
395 {
396 DBG1(DBG_CFG, "accepting lookip connection failed: %s",
397 strerror(errno));
398 }
399 continue;
400 }
401
402 fd = scan_fds(this, &fds);
403 if (fd == -1)
404 {
405 continue;
406 }
407 if (dispatch(this, fd))
408 {
409 this->mutex->lock(this->mutex);
410 this->connected->remove(this->connected, (void*)(uintptr_t)fd, NULL);
411 this->mutex->unlock(this->mutex);
412 if (!subscribed(this, fd))
413 {
414 close(fd);
415 }
416 }
417 }
418 return JOB_REQUEUE_FAIR;
419 }
420
421 METHOD(lookip_socket_t, destroy, void,
422 private_lookip_socket_t *this)
423 {
424 this->registered->destroy_function(this->registered, (void*)entry_destroy);
425 this->connected->destroy(this->connected);
426 this->mutex->destroy(this->mutex);
427 close(this->socket);
428 free(this);
429 }
430
431 /**
432 * See header
433 */
434 lookip_socket_t *lookip_socket_create(lookip_listener_t *listener)
435 {
436 private_lookip_socket_t *this;
437
438 INIT(this,
439 .public = {
440 .destroy = _destroy,
441 },
442 .listener = listener,
443 .registered = linked_list_create(),
444 .connected = linked_list_create(),
445 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
446 );
447
448 if (!open_socket(this))
449 {
450 free(this);
451 return NULL;
452 }
453
454 lib->processor->queue_job(lib->processor,
455 (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this,
456 NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
457
458 return &this->public;
459 }