2 * Copyright (C) 2012 Tobias Brunner
3 * Copyright (C) 2009 Martin Willi
4 * Hochschule fuer Technik Rapperswil
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>.
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
17 #include "resolve_handler.h"
19 #include <sys/types.h>
24 #include <utils/debug.h>
25 #include <threading/mutex.h>
27 /* path to resolvconf executable */
28 #define RESOLVCONF_EXEC "/sbin/resolvconf"
30 /* default prefix used for resolvconf interfaces (should have high prio) */
31 #define RESOLVCONF_PREFIX "lo.inet.ipsec."
33 typedef struct private_resolve_handler_t private_resolve_handler_t
;
36 * Private data of an resolve_handler_t object.
38 struct private_resolve_handler_t
{
41 * Public resolve_handler_t interface.
43 resolve_handler_t
public;
46 * resolv.conf file to use
51 * use resolvconf instead of writing directly to resolv.conf
56 * prefix to be used for interface names sent to resolvconf
61 * Mutex to access file exclusively
67 * Writes the given nameserver to resolv.conf
69 static bool write_nameserver(private_resolve_handler_t
*this,
70 identification_t
*server
, host_t
*addr
)
77 in
= fopen(this->file
, "r");
78 /* allows us to stream from in to out */
80 out
= fopen(this->file
, "w");
83 fprintf(out
, "nameserver %H # by strongSwan, from %Y\n", addr
,
85 DBG1(DBG_IKE
, "installing DNS server %H to %s", addr
, this->file
);
88 /* copy rest of the file */
91 while ((len
= fread(buf
, 1, sizeof(buf
), in
)))
93 ignore_result(fwrite(buf
, 1, len
, out
));
106 * Removes the given nameserver from resolv.conf
108 static void remove_nameserver(private_resolve_handler_t
*this,
109 identification_t
*server
, host_t
*addr
)
112 char line
[1024], matcher
[512];
114 in
= fopen(this->file
, "r");
117 /* allows us to stream from in to out */
119 out
= fopen(this->file
, "w");
122 snprintf(matcher
, sizeof(matcher
),
123 "nameserver %H # by strongSwan, from %Y\n",
126 /* copy all, but matching line */
127 while (fgets(line
, sizeof(line
), in
))
129 if (strpfx(line
, matcher
))
131 DBG1(DBG_IKE
, "removing DNS server %H from %s",
146 * Add or remove the given nameserver by invoking resolvconf.
148 static bool invoke_resolvconf(private_resolve_handler_t
*this,
149 identification_t
*server
, host_t
*addr
,
155 /* we use the nameserver's IP address as part of the interface name to
156 * make them unique */
157 if (snprintf(cmd
, sizeof(cmd
), "%s %s %s%H", RESOLVCONF_EXEC
,
158 install
? "-a" : "-d", this->iface_prefix
, addr
) >= sizeof(cmd
))
167 out
= popen(cmd
, "w");
172 DBG1(DBG_IKE
, "installing DNS server %H via resolvconf", addr
);
173 fprintf(out
, "nameserver %H\n", addr
);
174 success
= !ferror(out
);
182 ignore_result(system(cmd
));
187 METHOD(attribute_handler_t
, handle
, bool,
188 private_resolve_handler_t
*this, ike_sa_t
*ike_sa
,
189 configuration_attribute_type_t type
, chunk_t data
)
191 identification_t
*server
;
197 case INTERNAL_IP4_DNS
:
198 addr
= host_create_from_chunk(AF_INET
, data
, 0);
200 case INTERNAL_IP6_DNS
:
201 addr
= host_create_from_chunk(AF_INET6
, data
, 0);
207 if (!addr
|| addr
->is_anyaddr(addr
))
212 server
= ike_sa
->get_other_id(ike_sa
);
214 this->mutex
->lock(this->mutex
);
215 if (this->use_resolvconf
)
217 handled
= invoke_resolvconf(this, server
, addr
, TRUE
);
221 handled
= write_nameserver(this, server
, addr
);
223 this->mutex
->unlock(this->mutex
);
228 DBG1(DBG_IKE
, "adding DNS server failed");
233 METHOD(attribute_handler_t
, release
, void,
234 private_resolve_handler_t
*this, ike_sa_t
*ike_sa
,
235 configuration_attribute_type_t type
, chunk_t data
)
237 identification_t
*server
;
243 case INTERNAL_IP4_DNS
:
246 case INTERNAL_IP6_DNS
:
252 addr
= host_create_from_chunk(family
, data
, 0);
253 server
= ike_sa
->get_other_id(ike_sa
);
255 this->mutex
->lock(this->mutex
);
256 if (this->use_resolvconf
)
258 invoke_resolvconf(this, server
, addr
, FALSE
);
262 remove_nameserver(this, server
, addr
);
264 this->mutex
->unlock(this->mutex
);
270 * Attribute enumerator implementation
273 /** implements enumerator_t interface */
275 /** request IPv4 DNS? */
277 /** request IPv6 DNS? */
279 } attribute_enumerator_t
;
281 static bool attribute_enumerate(attribute_enumerator_t
*this,
282 configuration_attribute_type_t
*type
,
287 *type
= INTERNAL_IP4_DNS
;
294 *type
= INTERNAL_IP6_DNS
;
303 * Check if a list has a host of given family
305 static bool has_host_family(linked_list_t
*list
, int family
)
307 enumerator_t
*enumerator
;
311 enumerator
= list
->create_enumerator(list
);
312 while (enumerator
->enumerate(enumerator
, &host
))
314 if (host
->get_family(host
) == family
)
320 enumerator
->destroy(enumerator
);
325 METHOD(attribute_handler_t
, create_attribute_enumerator
, enumerator_t
*,
326 private_resolve_handler_t
*this, ike_sa_t
*ike_sa
,
329 attribute_enumerator_t
*enumerator
;
333 .enumerate
= (void*)attribute_enumerate
,
334 .destroy
= (void*)free
,
336 .v4
= has_host_family(vips
, AF_INET
),
337 .v6
= has_host_family(vips
, AF_INET6
),
339 return &enumerator
->public;
342 METHOD(resolve_handler_t
, destroy
, void,
343 private_resolve_handler_t
*this)
345 this->mutex
->destroy(this->mutex
);
352 resolve_handler_t
*resolve_handler_create()
354 private_resolve_handler_t
*this;
362 .create_attribute_enumerator
= _create_attribute_enumerator
,
366 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
367 .file
= lib
->settings
->get_str(lib
->settings
, "%s.plugins.resolve.file",
368 RESOLV_CONF
, lib
->ns
),
371 if (stat(RESOLVCONF_EXEC
, &st
) == 0)
373 this->use_resolvconf
= TRUE
;
374 this->iface_prefix
= lib
->settings
->get_str(lib
->settings
,
375 "%s.plugins.resolve.resolvconf.iface_prefix",
376 RESOLVCONF_PREFIX
, lib
->ns
);
379 return &this->public;