Add a DNS attribute handler to updown, passing servers to updown script
[strongswan.git] / src / libcharon / plugins / updown / updown_handler.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 "updown_handler.h"
17
18 #include <daemon.h>
19 #include <utils/linked_list.h>
20 #include <threading/rwlock.h>
21
22 typedef struct private_updown_handler_t private_updown_handler_t;
23
24 /**
25 * Private data of an updown_handler_t object.
26 */
27 struct private_updown_handler_t {
28
29 /**
30 * Public updown_handler_t interface.
31 */
32 updown_handler_t public;
33
34 /**
35 * List of connection specific attributes, as attributes_t
36 */
37 linked_list_t *attrs;
38
39 /**
40 * rwlock to lock access to pools
41 */
42 rwlock_t *lock;
43 };
44
45 /**
46 * Attributes assigned to an IKE_SA
47 */
48 typedef struct {
49 /** unique IKE_SA identifier */
50 u_int id;
51 /** list of DNS attributes, as host_t */
52 linked_list_t *dns;
53 } attributes_t;
54
55 /**
56 * Destroy an attributes_t entry
57 */
58 static void attributes_destroy(attributes_t *this)
59 {
60 this->dns->destroy_offset(this->dns, offsetof(host_t, destroy));
61 free(this);
62 }
63
64 METHOD(attribute_handler_t, handle, bool,
65 private_updown_handler_t *this, identification_t *server,
66 configuration_attribute_type_t type, chunk_t data)
67 {
68 attributes_t *current, *attr = NULL;
69 enumerator_t *enumerator;
70 ike_sa_t *ike_sa;
71 host_t *host;
72
73 ike_sa = charon->bus->get_sa(charon->bus);
74 if (!ike_sa)
75 {
76 return FALSE;
77 }
78 switch (type)
79 {
80 case INTERNAL_IP4_DNS:
81 host = host_create_from_chunk(AF_INET, data, 0);
82 break;
83 case INTERNAL_IP6_DNS:
84 host = host_create_from_chunk(AF_INET6, data, 0);
85 break;
86 default:
87 return FALSE;
88 }
89 if (!host)
90 {
91 return FALSE;
92 }
93
94 this->lock->write_lock(this->lock);
95 enumerator = this->attrs->create_enumerator(this->attrs);
96 while (enumerator->enumerate(enumerator, &current))
97 {
98 if (current->id == ike_sa->get_unique_id(ike_sa))
99 {
100 attr = current;
101 }
102 }
103 enumerator->destroy(enumerator);
104
105 if (!attr)
106 {
107 INIT(attr,
108 .id = ike_sa->get_unique_id(ike_sa),
109 .dns = linked_list_create(),
110 );
111 this->attrs->insert_last(this->attrs, attr);
112 }
113 attr->dns->insert_last(attr->dns, host);
114 this->lock->unlock(this->lock);
115
116 return TRUE;
117 }
118
119 METHOD(attribute_handler_t, release, void,
120 private_updown_handler_t *this, identification_t *server,
121 configuration_attribute_type_t type, chunk_t data)
122 {
123 attributes_t *attr;
124 enumerator_t *enumerator, *servers;
125 ike_sa_t *ike_sa;
126 host_t *host;
127 bool found = FALSE;
128 int family;
129
130 switch (type)
131 {
132 case INTERNAL_IP4_DNS:
133 family = AF_INET;
134 break;
135 case INTERNAL_IP6_DNS:
136 family = AF_INET6;
137 break;
138 default:
139 return;
140 }
141
142 ike_sa = charon->bus->get_sa(charon->bus);
143 if (ike_sa)
144 {
145 this->lock->write_lock(this->lock);
146 enumerator = this->attrs->create_enumerator(this->attrs);
147 while (enumerator->enumerate(enumerator, &attr))
148 {
149 if (attr->id == ike_sa->get_unique_id(ike_sa))
150 {
151 servers = attr->dns->create_enumerator(attr->dns);
152 while (servers->enumerate(servers, &host))
153 {
154 if (host->get_family(host) == family &&
155 chunk_equals(data, host->get_address(host)))
156 {
157 attr->dns->remove_at(attr->dns, servers);
158 host->destroy(host);
159 found = TRUE;
160 break;
161 }
162 }
163 servers->destroy(servers);
164 if (attr->dns->get_count(attr->dns) == 0)
165 {
166 this->attrs->remove_at(this->attrs, enumerator);
167 attributes_destroy(attr);
168 break;
169 }
170 }
171 if (found)
172 {
173 break;
174 }
175 }
176 enumerator->destroy(enumerator);
177 this->lock->unlock(this->lock);
178 }
179 }
180
181 METHOD(updown_handler_t, create_dns_enumerator, enumerator_t*,
182 private_updown_handler_t *this, u_int id)
183 {
184 attributes_t *attr;
185 enumerator_t *enumerator;
186 ike_sa_t *ike_sa;
187
188 ike_sa = charon->bus->get_sa(charon->bus);
189 if (!ike_sa)
190 {
191 return FALSE;
192 }
193
194 this->lock->read_lock(this->lock);
195 enumerator = this->attrs->create_enumerator(this->attrs);
196 while (enumerator->enumerate(enumerator, &attr))
197 {
198 if (attr->id == ike_sa->get_unique_id(ike_sa))
199 {
200 enumerator->destroy(enumerator);
201 return enumerator_create_cleaner(
202 attr->dns->create_enumerator(attr->dns),
203 (void*)this->lock->unlock, this->lock);
204 }
205 }
206 enumerator->destroy(enumerator);
207 this->lock->unlock(this->lock);
208
209 return enumerator_create_empty();
210 }
211
212
213 METHOD(updown_handler_t, destroy, void,
214 private_updown_handler_t *this)
215 {
216 this->lock->destroy(this->lock);
217 this->attrs->destroy_function(this->attrs, (void*)attributes_destroy);
218 free(this);
219 }
220
221 /**
222 * See header
223 */
224 updown_handler_t *updown_handler_create()
225 {
226 private_updown_handler_t *this;
227
228 INIT(this,
229 .public = {
230 .handler = {
231 .handle = _handle,
232 .release = _release,
233 .create_attribute_enumerator = enumerator_create_empty,
234 },
235 .create_dns_enumerator = _create_dns_enumerator,
236 .destroy = _destroy,
237 },
238 .attrs = linked_list_create(),
239 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
240 );
241
242 return &this->public;
243 }