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