libhydra: Move kernel interface to libcharon
[strongswan.git] / src / libcharon / plugins / resolve / resolve_handler.c
1 /*
2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2009 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "resolve_handler.h"
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22
23 #include <utils/debug.h>
24 #include <threading/mutex.h>
25
26 /* path to resolvconf executable */
27 #define RESOLVCONF_EXEC "/sbin/resolvconf"
28
29 /* default prefix used for resolvconf interfaces (should have high prio) */
30 #define RESOLVCONF_PREFIX "lo.inet.ipsec."
31
32 typedef struct private_resolve_handler_t private_resolve_handler_t;
33
34 /**
35 * Private data of an resolve_handler_t object.
36 */
37 struct private_resolve_handler_t {
38
39 /**
40 * Public resolve_handler_t interface.
41 */
42 resolve_handler_t public;
43
44 /**
45 * resolv.conf file to use
46 */
47 char *file;
48
49 /**
50 * use resolvconf instead of writing directly to resolv.conf
51 */
52 bool use_resolvconf;
53
54 /**
55 * prefix to be used for interface names sent to resolvconf
56 */
57 char *iface_prefix;
58
59 /**
60 * Mutex to access file exclusively
61 */
62 mutex_t *mutex;
63 };
64
65 /**
66 * Writes the given nameserver to resolv.conf
67 */
68 static bool write_nameserver(private_resolve_handler_t *this,
69 identification_t *server, host_t *addr)
70 {
71 FILE *in, *out;
72 char buf[1024];
73 size_t len;
74 bool handled = FALSE;
75
76 in = fopen(this->file, "r");
77 /* allows us to stream from in to out */
78 unlink(this->file);
79 out = fopen(this->file, "w");
80 if (out)
81 {
82 fprintf(out, "nameserver %H # by strongSwan, from %Y\n", addr,
83 server);
84 DBG1(DBG_IKE, "installing DNS server %H to %s", addr, this->file);
85 handled = TRUE;
86
87 /* copy rest of the file */
88 if (in)
89 {
90 while ((len = fread(buf, 1, sizeof(buf), in)))
91 {
92 ignore_result(fwrite(buf, 1, len, out));
93 }
94 }
95 fclose(out);
96 }
97 if (in)
98 {
99 fclose(in);
100 }
101 return handled;
102 }
103
104 /**
105 * Removes the given nameserver from resolv.conf
106 */
107 static void remove_nameserver(private_resolve_handler_t *this,
108 identification_t *server, host_t *addr)
109 {
110 FILE *in, *out;
111 char line[1024], matcher[512];
112
113 in = fopen(this->file, "r");
114 if (in)
115 {
116 /* allows us to stream from in to out */
117 unlink(this->file);
118 out = fopen(this->file, "w");
119 if (out)
120 {
121 snprintf(matcher, sizeof(matcher),
122 "nameserver %H # by strongSwan, from %Y\n",
123 addr, server);
124
125 /* copy all, but matching line */
126 while (fgets(line, sizeof(line), in))
127 {
128 if (strpfx(line, matcher))
129 {
130 DBG1(DBG_IKE, "removing DNS server %H from %s",
131 addr, this->file);
132 }
133 else
134 {
135 fputs(line, out);
136 }
137 }
138 fclose(out);
139 }
140 fclose(in);
141 }
142 }
143
144 /**
145 * Add or remove the given nameserver by invoking resolvconf.
146 */
147 static bool invoke_resolvconf(private_resolve_handler_t *this,
148 identification_t *server, host_t *addr,
149 bool install)
150 {
151 char cmd[128];
152 bool success = TRUE;
153
154 /* we use the nameserver's IP address as part of the interface name to
155 * make them unique */
156 if (snprintf(cmd, sizeof(cmd), "%s %s %s%H", RESOLVCONF_EXEC,
157 install ? "-a" : "-d", this->iface_prefix, addr) >= sizeof(cmd))
158 {
159 return FALSE;
160 }
161
162 if (install)
163 {
164 FILE *out;
165
166 out = popen(cmd, "w");
167 if (!out)
168 {
169 return FALSE;
170 }
171 DBG1(DBG_IKE, "installing DNS server %H via resolvconf", addr);
172 fprintf(out, "nameserver %H\n", addr);
173 success = !ferror(out);
174 if (pclose(out))
175 {
176 return FALSE;
177 }
178 }
179 else
180 {
181 ignore_result(system(cmd));
182 }
183 return success;
184 }
185
186 METHOD(attribute_handler_t, handle, bool,
187 private_resolve_handler_t *this, ike_sa_t *ike_sa,
188 configuration_attribute_type_t type, chunk_t data)
189 {
190 identification_t *server;
191 host_t *addr;
192 bool handled;
193
194 switch (type)
195 {
196 case INTERNAL_IP4_DNS:
197 addr = host_create_from_chunk(AF_INET, data, 0);
198 break;
199 case INTERNAL_IP6_DNS:
200 addr = host_create_from_chunk(AF_INET6, data, 0);
201 break;
202 default:
203 return FALSE;
204 }
205
206 if (!addr || addr->is_anyaddr(addr))
207 {
208 DESTROY_IF(addr);
209 return FALSE;
210 }
211 server = ike_sa->get_other_id(ike_sa);
212
213 this->mutex->lock(this->mutex);
214 if (this->use_resolvconf)
215 {
216 handled = invoke_resolvconf(this, server, addr, TRUE);
217 }
218 else
219 {
220 handled = write_nameserver(this, server, addr);
221 }
222 this->mutex->unlock(this->mutex);
223 addr->destroy(addr);
224
225 if (!handled)
226 {
227 DBG1(DBG_IKE, "adding DNS server failed");
228 }
229 return handled;
230 }
231
232 METHOD(attribute_handler_t, release, void,
233 private_resolve_handler_t *this, ike_sa_t *ike_sa,
234 configuration_attribute_type_t type, chunk_t data)
235 {
236 identification_t *server;
237 host_t *addr;
238 int family;
239
240 switch (type)
241 {
242 case INTERNAL_IP4_DNS:
243 family = AF_INET;
244 break;
245 case INTERNAL_IP6_DNS:
246 family = AF_INET6;
247 break;
248 default:
249 return;
250 }
251 addr = host_create_from_chunk(family, data, 0);
252 server = ike_sa->get_other_id(ike_sa);
253
254 this->mutex->lock(this->mutex);
255 if (this->use_resolvconf)
256 {
257 invoke_resolvconf(this, server, addr, FALSE);
258 }
259 else
260 {
261 remove_nameserver(this, server, addr);
262 }
263 this->mutex->unlock(this->mutex);
264
265 addr->destroy(addr);
266 }
267
268 /**
269 * Attribute enumerator implementation
270 */
271 typedef struct {
272 /** implements enumerator_t interface */
273 enumerator_t public;
274 /** request IPv4 DNS? */
275 bool v4;
276 /** request IPv6 DNS? */
277 bool v6;
278 } attribute_enumerator_t;
279
280 static bool attribute_enumerate(attribute_enumerator_t *this,
281 configuration_attribute_type_t *type,
282 chunk_t *data)
283 {
284 if (this->v4)
285 {
286 *type = INTERNAL_IP4_DNS;
287 *data = chunk_empty;
288 this->v4 = FALSE;
289 return TRUE;
290 }
291 if (this->v6)
292 {
293 *type = INTERNAL_IP6_DNS;
294 *data = chunk_empty;
295 this->v6 = FALSE;
296 return TRUE;
297 }
298 return FALSE;
299 }
300
301 /**
302 * Check if a list has a host of given family
303 */
304 static bool has_host_family(linked_list_t *list, int family)
305 {
306 enumerator_t *enumerator;
307 host_t *host;
308 bool found = FALSE;
309
310 enumerator = list->create_enumerator(list);
311 while (enumerator->enumerate(enumerator, &host))
312 {
313 if (host->get_family(host) == family)
314 {
315 found = TRUE;
316 break;
317 }
318 }
319 enumerator->destroy(enumerator);
320
321 return found;
322 }
323
324 METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*,
325 private_resolve_handler_t *this, ike_sa_t *ike_sa,
326 linked_list_t *vips)
327 {
328 attribute_enumerator_t *enumerator;
329
330 INIT(enumerator,
331 .public = {
332 .enumerate = (void*)attribute_enumerate,
333 .destroy = (void*)free,
334 },
335 .v4 = has_host_family(vips, AF_INET),
336 .v6 = has_host_family(vips, AF_INET6),
337 );
338 return &enumerator->public;
339 }
340
341 METHOD(resolve_handler_t, destroy, void,
342 private_resolve_handler_t *this)
343 {
344 this->mutex->destroy(this->mutex);
345 free(this);
346 }
347
348 /**
349 * See header
350 */
351 resolve_handler_t *resolve_handler_create()
352 {
353 private_resolve_handler_t *this;
354 struct stat st;
355
356 INIT(this,
357 .public = {
358 .handler = {
359 .handle = _handle,
360 .release = _release,
361 .create_attribute_enumerator = _create_attribute_enumerator,
362 },
363 .destroy = _destroy,
364 },
365 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
366 .file = lib->settings->get_str(lib->settings, "%s.plugins.resolve.file",
367 RESOLV_CONF, lib->ns),
368 );
369
370 if (stat(RESOLVCONF_EXEC, &st) == 0)
371 {
372 this->use_resolvconf = TRUE;
373 this->iface_prefix = lib->settings->get_str(lib->settings,
374 "%s.plugins.resolve.resolvconf.iface_prefix",
375 RESOLVCONF_PREFIX, lib->ns);
376 }
377
378 return &this->public;
379 }