Handle client subscriptions in lookip plugin
[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 <utils/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 *clients;
59
60 /**
61 * Mutex to lock clients list
62 */
63 mutex_t *mutex;
64 };
65
66 /**
67 * Open lookip unix socket
68 */
69 static bool open_socket(private_lookip_socket_t *this)
70 {
71 struct sockaddr_un addr;
72 mode_t old;
73
74 addr.sun_family = AF_UNIX;
75 strcpy(addr.sun_path, LOOKIP_SOCKET);
76
77 this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
78 if (this->socket == -1)
79 {
80 DBG1(DBG_CFG, "creating lookip socket failed");
81 return FALSE;
82 }
83 unlink(addr.sun_path);
84 old = umask(~(S_IRWXU | S_IRWXG));
85 if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0)
86 {
87 DBG1(DBG_CFG, "binding lookip socket failed: %s", strerror(errno));
88 close(this->socket);
89 return FALSE;
90 }
91 umask(old);
92 if (chown(addr.sun_path, charon->caps->get_uid(charon->caps),
93 charon->caps->get_gid(charon->caps)) != 0)
94 {
95 DBG1(DBG_CFG, "changing lookip socket permissions failed: %s",
96 strerror(errno));
97 }
98 if (listen(this->socket, 10) < 0)
99 {
100 DBG1(DBG_CFG, "listening on lookip socket failed: %s", strerror(errno));
101 close(this->socket);
102 unlink(addr.sun_path);
103 return FALSE;
104 }
105 return TRUE;
106 }
107
108 /**
109 * Listener callback entry
110 */
111 typedef struct {
112 /* FD to write to */
113 int fd;
114 /* message type to send */
115 int type;
116 /* back pointer to socket, only for subscriptions */
117 private_lookip_socket_t *this;
118 } entry_t;
119
120 /**
121 * Destroy entry
122 */
123 static void entry_destroy(entry_t *this)
124 {
125 close(this->fd);
126 free(this);
127 }
128
129 /**
130 * Callback function for listener
131 */
132 static bool listener_cb(entry_t *entry, bool up, host_t *vip,
133 host_t *other, identification_t *id, char *name)
134 {
135 lookip_response_t resp = {
136 .type = entry->type,
137 };
138
139 /* filter events */
140 if (up && entry->type == LOOKIP_NOTIFY_DOWN)
141 {
142 return TRUE;
143 }
144 if (!up && entry->type == LOOKIP_NOTIFY_UP)
145 {
146 return TRUE;
147 }
148
149 snprintf(resp.vip, sizeof(resp.vip), "%H", vip);
150 snprintf(resp.ip, sizeof(resp.ip), "%H", other);
151 snprintf(resp.id, sizeof(resp.id), "%Y", id);
152 snprintf(resp.name, sizeof(resp.name), "%s", name);
153
154 switch (send(entry->fd, &resp, sizeof(resp), 0))
155 {
156 case sizeof(resp):
157 return TRUE;
158 case 0:
159 /* client disconnected, adios */
160 break;
161 default:
162 DBG1(DBG_CFG, "sending lookip response failed: %s", strerror(errno));
163 break;
164 }
165 if (entry->this)
166 { /* unregister listener */
167 entry->this->mutex->lock(entry->this->mutex);
168 entry->this->clients->remove(entry->this->clients, entry, NULL);
169 entry->this->mutex->unlock(entry->this->mutex);
170
171 entry_destroy(entry);
172 }
173 return FALSE;
174 }
175
176 /**
177 * Perform a entry lookup
178 */
179 static void query(private_lookip_socket_t *this, int fd, lookip_request_t *req)
180 {
181 entry_t entry = {
182 .fd = fd,
183 .type = LOOKIP_ENTRY,
184 };
185 host_t *vip = NULL;
186
187 if (req)
188 { /* lookup */
189 req->vip[sizeof(req->vip) - 1] = 0;
190 vip = host_create_from_string(req->vip, 0);
191 if (vip)
192 {
193 this->listener->lookup(this->listener, vip,
194 (void*)listener_cb, &entry);
195 vip->destroy(vip);
196 }
197 }
198 else
199 { /* dump */
200 this->listener->lookup(this->listener, NULL,
201 (void*)listener_cb, &entry);
202 }
203 }
204
205 /**
206 * Subscribe to virtual IP events
207 */
208 static void subscribe(private_lookip_socket_t *this, int fd, int type)
209 {
210 entry_t *entry;
211
212 INIT(entry,
213 .fd = fd,
214 .type = type,
215 .this = this,
216 );
217
218 this->mutex->lock(this->mutex);
219 this->clients->insert_last(this->clients, entry);
220 this->mutex->unlock(this->mutex);
221
222 this->listener->add_listener(this->listener, (void*)listener_cb, entry);
223 }
224
225 /**
226 * Accept client connections, dispatch
227 */
228 static job_requeue_t receive(private_lookip_socket_t *this)
229 {
230 struct sockaddr_un addr;
231 int fd, len = sizeof(addr);
232 lookip_request_t req;
233 bool oldstate, subscribed = FALSE;
234
235 oldstate = thread_cancelability(TRUE);
236 fd = accept(this->socket, (struct sockaddr*)&addr, &len);
237 thread_cancelability(oldstate);
238
239 if (fd != -1)
240 {
241 while (TRUE)
242 {
243 oldstate = thread_cancelability(TRUE);
244 len = recv(fd, &req, sizeof(req), 0);
245 thread_cancelability(oldstate);
246
247 if (len == sizeof(req))
248 {
249 switch (req.type)
250 {
251 case LOOKIP_LOOKUP:
252 query(this, fd, &req);
253 continue;
254 case LOOKIP_DUMP:
255 query(this, fd, NULL);
256 continue;
257 case LOOKIP_REGISTER_UP:
258 subscribe(this, fd, LOOKIP_NOTIFY_UP);
259 subscribed = TRUE;
260 continue;
261 case LOOKIP_REGISTER_DOWN:
262 subscribe(this, fd, LOOKIP_NOTIFY_DOWN);
263 subscribed = TRUE;
264 continue;
265 case LOOKIP_END:
266 break;
267 default:
268 DBG1(DBG_CFG, "received unknown lookip command");
269 break;
270 }
271 }
272 else
273 {
274 if (len != 0)
275 {
276 DBG1(DBG_CFG, "receiving lookip request failed: %s",
277 strerror(errno));
278 }
279 break;
280 }
281 break;
282 }
283 if (!subscribed)
284 { /* don't close if we queued the fd */
285 close(fd);
286 }
287 }
288 else
289 {
290 DBG1(DBG_CFG, "accepting lookip connection failed: %s",
291 strerror(errno));
292 }
293 return JOB_REQUEUE_FAIR;
294 }
295
296 METHOD(lookip_socket_t, destroy, void,
297 private_lookip_socket_t *this)
298 {
299 this->clients->destroy_function(this->clients, (void*)entry_destroy);
300 this->mutex->destroy(this->mutex);
301 close(this->socket);
302 free(this);
303 }
304
305 /**
306 * See header
307 */
308 lookip_socket_t *lookip_socket_create(lookip_listener_t *listener)
309 {
310 private_lookip_socket_t *this;
311
312 INIT(this,
313 .public = {
314 .destroy = _destroy,
315 },
316 .listener = listener,
317 .clients = linked_list_create(),
318 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
319 );
320
321 if (!open_socket(this))
322 {
323 free(this);
324 return NULL;
325 }
326
327 lib->processor->queue_job(lib->processor,
328 (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this,
329 NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
330
331 return &this->public;
332 }