Add a lookip server side UNIX socket processing LOOKUP and DUMP requests
[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 <processing/jobs/callback_job.h>
28
29 #include "lookip_msg.h"
30
31 typedef struct private_lookip_socket_t private_lookip_socket_t;
32
33 /**
34 * Private data of an lookip_socket_t object.
35 */
36 struct private_lookip_socket_t {
37
38 /**
39 * Public lookip_socket_t interface.
40 */
41 lookip_socket_t public;
42
43 /**
44 * lookip
45 */
46 lookip_listener_t *listener;
47
48 /**
49 * lookip unix socket file descriptor
50 */
51 int socket;
52 };
53
54 /**
55 * Open lookip unix socket
56 */
57 static bool open_socket(private_lookip_socket_t *this)
58 {
59 struct sockaddr_un addr;
60 mode_t old;
61
62 addr.sun_family = AF_UNIX;
63 strcpy(addr.sun_path, LOOKIP_SOCKET);
64
65 this->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
66 if (this->socket == -1)
67 {
68 DBG1(DBG_CFG, "creating lookip socket failed");
69 return FALSE;
70 }
71 unlink(addr.sun_path);
72 old = umask(~(S_IRWXU | S_IRWXG));
73 if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) < 0)
74 {
75 DBG1(DBG_CFG, "binding lookip socket failed: %s", strerror(errno));
76 close(this->socket);
77 return FALSE;
78 }
79 umask(old);
80 if (chown(addr.sun_path, charon->caps->get_uid(charon->caps),
81 charon->caps->get_gid(charon->caps)) != 0)
82 {
83 DBG1(DBG_CFG, "changing lookip socket permissions failed: %s",
84 strerror(errno));
85 }
86 if (listen(this->socket, 10) < 0)
87 {
88 DBG1(DBG_CFG, "listening on lookip socket failed: %s", strerror(errno));
89 close(this->socket);
90 unlink(addr.sun_path);
91 return FALSE;
92 }
93 return TRUE;
94 }
95
96 /**
97 * Listener callback data
98 */
99 typedef struct {
100 /* FD to write to */
101 int fd;
102 /* message type to send */
103 int type;
104 } cb_data_t;
105
106 /**
107 * Callback function for listener
108 */
109 static bool listener_cb(cb_data_t *data, bool up, host_t *vip,
110 host_t *other, identification_t *id, char *name)
111 {
112 lookip_response_t resp = {
113 .type = data->type,
114 };
115
116 snprintf(resp.vip, sizeof(resp.vip), "%H", vip);
117 snprintf(resp.ip, sizeof(resp.ip), "%H", other);
118 snprintf(resp.id, sizeof(resp.id), "%Y", id);
119 snprintf(resp.name, sizeof(resp.name), "%s", name);
120
121 switch (send(data->fd, &resp, sizeof(resp), 0))
122 {
123 case sizeof(resp):
124 return TRUE;
125 case 0:
126 /* client disconnected, adios */
127 return FALSE;
128 default:
129 DBG1(DBG_CFG, "sending lookip response failed: %s", strerror(errno));
130 return FALSE;
131 }
132 }
133
134 /**
135 * Perform a entry lookup
136 */
137 static void query(private_lookip_socket_t *this, int fd, lookip_request_t *req)
138 {
139 cb_data_t data = {
140 .fd = fd,
141 .type = LOOKIP_ENTRY,
142 };
143 host_t *vip = NULL;
144
145 if (req)
146 { /* lookup */
147 req->vip[sizeof(req->vip) - 1] = 0;
148 vip = host_create_from_string(req->vip, 0);
149 if (vip)
150 {
151 this->listener->lookup(this->listener, vip,
152 (void*)listener_cb, &data);
153 vip->destroy(vip);
154 }
155 }
156 else
157 { /* dump */
158 this->listener->lookup(this->listener, NULL,
159 (void*)listener_cb, &data);
160 }
161 }
162
163 /**
164 * Accept client connections, dispatch
165 */
166 static job_requeue_t receive(private_lookip_socket_t *this)
167 {
168 struct sockaddr_un addr;
169 int fd, len = sizeof(addr);
170 lookip_request_t req;
171 bool oldstate;
172
173 oldstate = thread_cancelability(TRUE);
174 fd = accept(this->socket, (struct sockaddr*)&addr, &len);
175 thread_cancelability(oldstate);
176
177 if (fd != -1)
178 {
179 while (TRUE)
180 {
181 oldstate = thread_cancelability(TRUE);
182 len = recv(fd, &req, sizeof(req), 0);
183 thread_cancelability(oldstate);
184
185 if (len == sizeof(req))
186 {
187 switch (req.type)
188 {
189 case LOOKIP_LOOKUP:
190 query(this, fd, &req);
191 continue;
192 case LOOKIP_DUMP:
193 query(this, fd, NULL);
194 continue;
195 case LOOKIP_END:
196 break;
197 default:
198 DBG1(DBG_CFG, "received unknown lookip command");
199 break;
200 }
201 }
202 else
203 {
204 if (len != 0)
205 {
206 DBG1(DBG_CFG, "receiving lookip request failed: %s",
207 strerror(errno));
208 }
209 break;
210 }
211 break;
212 }
213 close(fd);
214 }
215 else
216 {
217 DBG1(DBG_CFG, "accepting lookip connection failed: %s",
218 strerror(errno));
219 }
220 return JOB_REQUEUE_FAIR;
221 }
222
223 METHOD(lookip_socket_t, destroy, void,
224 private_lookip_socket_t *this)
225 {
226 close(this->socket);
227 free(this);
228 }
229
230 /**
231 * See header
232 */
233 lookip_socket_t *lookip_socket_create(lookip_listener_t *listener)
234 {
235 private_lookip_socket_t *this;
236
237 INIT(this,
238 .public = {
239 .destroy = _destroy,
240 },
241 .listener = listener,
242 );
243
244 if (!open_socket(this))
245 {
246 free(this);
247 return NULL;
248 }
249
250 lib->processor->queue_job(lib->processor,
251 (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this,
252 NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
253
254 return &this->public;
255 }