2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
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>.
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
16 #include "lookip_socket.h"
18 #include <sys/types.h>
20 #include <sys/socket.h>
26 #include <threading/thread.h>
27 #include <threading/mutex.h>
28 #include <collections/linked_list.h>
29 #include <processing/jobs/callback_job.h>
31 #include "lookip_msg.h"
33 typedef struct private_lookip_socket_t private_lookip_socket_t
;
36 * Private data of an lookip_socket_t object.
38 struct private_lookip_socket_t
{
41 * Public lookip_socket_t interface.
43 lookip_socket_t
public;
48 lookip_listener_t
*listener
;
51 * lookip unix socket file descriptor
56 * List of registered listeners, as entry_t
58 linked_list_t
*registered
;
61 * List of connected clients, as uintptr_t FD
63 linked_list_t
*connected
;
66 * Mutex to lock clients list
72 * Open lookip unix socket
74 static bool open_socket(private_lookip_socket_t
*this)
76 struct sockaddr_un addr
;
79 addr
.sun_family
= AF_UNIX
;
80 strcpy(addr
.sun_path
, LOOKIP_SOCKET
);
82 this->socket
= socket(AF_UNIX
, SOCK_SEQPACKET
, 0);
83 if (this->socket
== -1)
85 DBG1(DBG_CFG
, "creating lookip socket failed");
88 unlink(addr
.sun_path
);
89 old
= umask(~(S_IRWXU
| S_IRWXG
));
90 if (bind(this->socket
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0)
92 DBG1(DBG_CFG
, "binding lookip socket failed: %s", strerror(errno
));
97 if (chown(addr
.sun_path
, charon
->caps
->get_uid(charon
->caps
),
98 charon
->caps
->get_gid(charon
->caps
)) != 0)
100 DBG1(DBG_CFG
, "changing lookip socket permissions failed: %s",
103 if (listen(this->socket
, 10) < 0)
105 DBG1(DBG_CFG
, "listening on lookip socket failed: %s", strerror(errno
));
107 unlink(addr
.sun_path
);
114 * Listener callback entry
119 /* message type to send */
121 /* back pointer to socket, only for subscriptions */
122 private_lookip_socket_t
*this;
128 static void entry_destroy(entry_t
*this)
135 * Callback function for listener
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
)
141 lookip_response_t resp
= {
146 if (up
&& entry
->type
== LOOKIP_NOTIFY_DOWN
)
150 if (!up
&& entry
->type
== LOOKIP_NOTIFY_UP
)
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
);
160 switch (send(entry
->fd
, &resp
, sizeof(resp
), 0))
165 /* client disconnected, adios */
168 DBG1(DBG_CFG
, "sending lookip response failed: %s", strerror(errno
));
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
);
177 entry_destroy(entry
);
183 * Perform a entry lookup
185 static void query(private_lookip_socket_t
*this, int fd
, lookip_request_t
*req
)
189 .type
= LOOKIP_ENTRY
,
196 req
->vip
[sizeof(req
->vip
) - 1] = 0;
197 vip
= host_create_from_string(req
->vip
, 0);
200 matches
= this->listener
->lookup(this->listener
, vip
,
201 (void*)listener_cb
, &entry
);
206 lookip_response_t resp
= {
207 .type
= LOOKIP_NOT_FOUND
,
210 snprintf(resp
.vip
, sizeof(resp
.vip
), "%s", req
->vip
);
211 if (send(fd
, &resp
, sizeof(resp
), 0) < 0)
213 DBG1(DBG_CFG
, "sending lookip not-found failed: %s",
220 this->listener
->lookup(this->listener
, NULL
,
221 (void*)listener_cb
, &entry
);
226 * Subscribe to virtual IP events
228 static void subscribe(private_lookip_socket_t
*this, int fd
, int type
)
238 this->mutex
->lock(this->mutex
);
239 this->registered
->insert_last(this->registered
, entry
);
240 this->mutex
->unlock(this->mutex
);
242 this->listener
->add_listener(this->listener
, (void*)listener_cb
, entry
);
246 * Check if a client is subscribed for notifications
248 static bool subscribed(private_lookip_socket_t
*this, int fd
)
250 enumerator_t
*enumerator
;
251 bool subscribed
= FALSE
;
254 this->mutex
->lock(this->mutex
);
255 enumerator
= this->registered
->create_enumerator(this->registered
);
256 while (enumerator
->enumerate(enumerator
, &entry
))
264 enumerator
->destroy(enumerator
);
265 this->mutex
->unlock(this->mutex
);
271 * Create a fd_set from all bound sockets
273 static int build_fds(private_lookip_socket_t
*this, fd_set
*fds
)
275 enumerator_t
*enumerator
;
280 FD_SET(this->socket
, fds
);
281 maxfd
= this->socket
;
283 this->mutex
->lock(this->mutex
);
284 enumerator
= this->connected
->create_enumerator(this->connected
);
285 while (enumerator
->enumerate(enumerator
, &fd
))
288 maxfd
= max(maxfd
, fd
);
290 enumerator
->destroy(enumerator
);
291 this->mutex
->unlock(this->mutex
);
297 * Find the socket select()ed
299 static int scan_fds(private_lookip_socket_t
*this, fd_set
*fds
)
301 enumerator_t
*enumerator
;
305 this->mutex
->lock(this->mutex
);
306 enumerator
= this->connected
->create_enumerator(this->connected
);
307 while (enumerator
->enumerate(enumerator
, &fd
))
309 if (FD_ISSET(fd
, fds
))
315 enumerator
->destroy(enumerator
);
316 this->mutex
->unlock(this->mutex
);
322 * Dispatch from a socket, return TRUE to end communication
324 static bool dispatch(private_lookip_socket_t
*this, int fd
)
326 lookip_request_t req
;
329 len
= recv(fd
, &req
, sizeof(req
), 0);
330 if (len
!= sizeof(req
))
334 DBG1(DBG_CFG
, "receiving lookip request failed: %s",
342 query(this, fd
, &req
);
345 query(this, fd
, NULL
);
347 case LOOKIP_REGISTER_UP
:
348 subscribe(this, fd
, LOOKIP_NOTIFY_UP
);
350 case LOOKIP_REGISTER_DOWN
:
351 subscribe(this, fd
, LOOKIP_NOTIFY_DOWN
);
356 DBG1(DBG_CFG
, "received unknown lookip command");
362 * Accept client connections, dispatch
364 static job_requeue_t
receive(private_lookip_socket_t
*this)
366 struct sockaddr_un addr
;
373 maxfd
= build_fds(this, &fds
);
374 oldstate
= thread_cancelability(TRUE
);
375 if (select(maxfd
, &fds
, NULL
, NULL
, NULL
) <= 0)
377 thread_cancelability(oldstate
);
378 DBG1(DBG_CFG
, "selecting lookip sockets failed: %s",
382 thread_cancelability(oldstate
);
384 if (FD_ISSET(this->socket
, &fds
))
385 { /* new connection, accept() */
387 fd
= accept(this->socket
, (struct sockaddr
*)&addr
, &len
);
390 this->mutex
->lock(this->mutex
);
391 this->connected
->insert_last(this->connected
,
392 (void*)(uintptr_t)fd
);
393 this->mutex
->unlock(this->mutex
);
397 DBG1(DBG_CFG
, "accepting lookip connection failed: %s",
403 fd
= scan_fds(this, &fds
);
408 if (dispatch(this, fd
))
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
))
419 return JOB_REQUEUE_FAIR
;
422 METHOD(lookip_socket_t
, destroy
, void,
423 private_lookip_socket_t
*this)
425 this->registered
->destroy_function(this->registered
, (void*)entry_destroy
);
426 this->connected
->destroy(this->connected
);
427 this->mutex
->destroy(this->mutex
);
435 lookip_socket_t
*lookip_socket_create(lookip_listener_t
*listener
)
437 private_lookip_socket_t
*this;
443 .listener
= listener
,
444 .registered
= linked_list_create(),
445 .connected
= linked_list_create(),
446 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
449 if (!open_socket(this))
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
));
459 return &this->public;