a89195b6da637e4172be3e83decc9c6f8c0ef670
[strongswan.git] / src / libstrongswan / plugins / attr_sql / sql_attribute.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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 <time.h>
17
18 #include <debug.h>
19 #include <library.h>
20
21 #include "sql_attribute.h"
22
23 typedef struct private_sql_attribute_t private_sql_attribute_t;
24
25 /**
26 * private data of sql_attribute
27 */
28 struct private_sql_attribute_t {
29
30 /**
31 * public functions
32 */
33 sql_attribute_t public;
34
35 /**
36 * database connection
37 */
38 database_t *db;
39
40 /**
41 * wheter to record lease history in lease table
42 */
43 bool history;
44 };
45
46 /**
47 * lookup/insert an identity
48 */
49 static u_int get_identity(private_sql_attribute_t *this, identification_t *id)
50 {
51 enumerator_t *e;
52 u_int row;
53
54 /* look for peer identity in the identities table */
55 e = this->db->query(this->db,
56 "SELECT id FROM identities WHERE type = ? AND data = ?",
57 DB_INT, id->get_type(id), DB_BLOB, id->get_encoding(id),
58 DB_UINT);
59
60 if (e && e->enumerate(e, &row))
61 {
62 e->destroy(e);
63 return row;
64 }
65 DESTROY_IF(e);
66 /* not found, insert new one */
67 if (this->db->execute(this->db, &row,
68 "INSERT INTO identities (type, data) VALUES (?, ?)",
69 DB_INT, id->get_type(id), DB_BLOB, id->get_encoding(id)) == 1)
70 {
71 return row;
72 }
73 return 0;
74 }
75
76 /**
77 * Lookup pool by name
78 */
79 static u_int get_pool(private_sql_attribute_t *this, char *name, u_int *timeout)
80 {
81 enumerator_t *e;
82 u_int pool;
83
84 e = this->db->query(this->db, "SELECT id, timeout FROM pools WHERE name = ?",
85 DB_TEXT, name, DB_UINT, DB_UINT);
86 if (e && e->enumerate(e, &pool, timeout))
87 {
88 e->destroy(e);
89 return pool;
90 }
91 DESTROY_IF(e);
92 return 0;
93 }
94
95 /**
96 * Look up an existing lease
97 */
98 static host_t* check_lease(private_sql_attribute_t *this, char *name,
99 u_int pool, u_int identity)
100 {
101 while (TRUE)
102 {
103 u_int id;
104 chunk_t address;
105 enumerator_t *e;
106 time_t now = time(NULL);
107
108 e = this->db->query(this->db,
109 "SELECT id, address FROM addresses "
110 "WHERE pool = ? AND identity = ? AND released != 0 LIMIT 1",
111 DB_UINT, pool, DB_UINT, identity, DB_UINT, DB_BLOB);
112 if (!e || !e->enumerate(e, &id, &address))
113 {
114 DESTROY_IF(e);
115 break;
116 }
117 address = chunk_clonea(address);
118 e->destroy(e);
119
120 if (this->db->execute(this->db, NULL,
121 "UPDATE addresses SET acquired = ?, released = 0 "
122 "WHERE id = ? AND identity = ? AND released != 0",
123 DB_UINT, now, DB_UINT, id, DB_UINT, identity) > 0)
124 {
125 host_t *host;
126
127 host = host_create_from_chunk(AF_UNSPEC, address, 0);
128 if (host)
129 {
130 DBG1("acquired existing lease for address %H in pool '%s'",
131 host, name);
132 return host;
133 }
134 }
135 }
136 return NULL;
137 }
138
139 /**
140 * We check for unallocated addresses or expired leases. First we select an
141 * address as a candidate, but double check later on if it is still available
142 * during the update operation. This allows us to work without locking.
143 */
144 static host_t* get_lease(private_sql_attribute_t *this, char *name,
145 u_int pool, u_int timeout, u_int identity)
146 {
147 while (TRUE)
148 {
149 u_int id;
150 chunk_t address;
151 enumerator_t *e;
152 time_t now = time(NULL);
153 int hits;
154
155 if (timeout)
156 {
157 /* check for an expired lease */
158 e = this->db->query(this->db,
159 "SELECT id, address FROM addresses "
160 "WHERE pool = ? AND released != 0 AND released < ? LIMIT 1",
161 DB_UINT, pool, DB_UINT, now - timeout, DB_UINT, DB_BLOB);
162 }
163 else
164 {
165 /* with static leases, check for an unallocated address */
166 e = this->db->query(this->db,
167 "SELECT id, address FROM addresses "
168 "WHERE pool = ? AND identity = 0 LIMIT 1",
169 DB_UINT, pool, DB_UINT, DB_BLOB);
170
171 }
172
173 if (!e || !e->enumerate(e, &id, &address))
174 {
175 DESTROY_IF(e);
176 break;
177 }
178 address = chunk_clonea(address);
179 e->destroy(e);
180
181 if (timeout)
182 {
183 hits = this->db->execute(this->db, NULL,
184 "UPDATE addresses SET "
185 "acquired = ?, released = 0, identity = ? "
186 "WHERE id = ? AND released != 0 AND released < ?",
187 DB_UINT, now, DB_UINT, identity,
188 DB_UINT, id, DB_UINT, now - timeout);
189 }
190 else
191 {
192 hits = this->db->execute(this->db, NULL,
193 "UPDATE addresses SET "
194 "acquired = ?, released = 0, identity = ? "
195 "WHERE id = ? AND identity = 0",
196 DB_UINT, now, DB_UINT, identity, DB_UINT, id);
197 }
198 if (hits > 0)
199 {
200 host_t *host;
201
202 host = host_create_from_chunk(AF_UNSPEC, address, 0);
203 if (host)
204 {
205 DBG1("acquired new lease for address %H in pool '%s'",
206 host, name);
207 return host;
208 }
209 }
210 }
211 DBG1("no available address found in pool '%s'", name);
212 return NULL;
213 }
214
215 /**
216 * Implementation of attribute_provider_t.acquire_address
217 */
218 static host_t* acquire_address(private_sql_attribute_t *this,
219 char *names, identification_t *id,
220 host_t *requested)
221 {
222 host_t *address = NULL;
223 u_int identity, pool, timeout;
224
225 identity = get_identity(this, id);
226 if (identity)
227 {
228 /* check for a single pool first (no concatenation and enumeration) */
229 if (strchr(names, ',') == NULL)
230 {
231 pool = get_pool(this, names, &timeout);
232 if (pool)
233 {
234 /* check for an existing lease */
235 address = check_lease(this, names, pool, identity);
236 if (address == NULL)
237 {
238 /* get an unallocated address or expired lease */
239 address = get_lease(this, names, pool, timeout, identity);
240 }
241 }
242 }
243 else
244 {
245 enumerator_t *enumerator;
246 char *name;
247
248 /* in a first step check for an existing lease over all pools */
249 enumerator = enumerator_create_token(names, ",", " ");
250 while (enumerator->enumerate(enumerator, &name))
251 {
252 pool = get_pool(this, name, &timeout);
253 if (pool)
254 {
255 address = check_lease(this, name, pool, identity);
256 if (address)
257 {
258 enumerator->destroy(enumerator);
259 return address;
260 }
261 }
262 }
263 enumerator->destroy(enumerator);
264
265 /* in a second step get an unallocated address or expired lease */
266 enumerator = enumerator_create_token(names, ",", " ");
267 while (enumerator->enumerate(enumerator, &name))
268 {
269 pool = get_pool(this, name, &timeout);
270 if (pool)
271 {
272 address = get_lease(this, name, pool, timeout, identity);
273 if (address)
274 {
275 break;
276 }
277 }
278 }
279 enumerator->destroy(enumerator);
280 }
281 }
282 return address;
283 }
284
285 /**
286 * Implementation of attribute_provider_t.release_address
287 */
288 static bool release_address(private_sql_attribute_t *this,
289 char *name, host_t *address, identification_t *id)
290 {
291 enumerator_t *enumerator;
292 bool found = FALSE;
293 time_t now = time(NULL);
294
295 enumerator = enumerator_create_token(name, ",", " ");
296 while (enumerator->enumerate(enumerator, &name))
297 {
298 u_int pool, timeout;
299
300 pool = get_pool(this, name, &timeout);
301 if (pool)
302 {
303 if (this->history)
304 {
305 this->db->execute(this->db, NULL,
306 "INSERT INTO leases (address, identity, acquired, released)"
307 " SELECT id, identity, acquired, ? FROM addresses "
308 " WHERE pool = ? AND address = ?",
309 DB_UINT, now, DB_UINT, pool,
310 DB_BLOB, address->get_address(address));
311 }
312 if (this->db->execute(this->db, NULL,
313 "UPDATE addresses SET released = ? WHERE "
314 "pool = ? AND address = ?", DB_UINT, time(NULL),
315 DB_UINT, pool, DB_BLOB, address->get_address(address)) > 0)
316 {
317 found = TRUE;
318 break;
319 }
320 }
321 }
322 enumerator->destroy(enumerator);
323 return found;
324 }
325
326 /**
327 * Implementation of sql_attribute_t.create_attribute_enumerator
328 */
329 static enumerator_t* create_attribute_enumerator(private_sql_attribute_t *this,
330 identification_t *id, host_t *vip)
331 {
332 if (vip)
333 {
334 enumerator_t *enumerator;
335
336 enumerator = this->db->query(this->db,
337 "SELECT type, value FROM attributes", DB_INT, DB_BLOB);
338 if (enumerator)
339 {
340 return enumerator;
341 }
342 }
343 return enumerator_create_empty();
344 }
345
346 /**
347 * Implementation of sql_attribute_t.destroy
348 */
349 static void destroy(private_sql_attribute_t *this)
350 {
351 free(this);
352 }
353
354 /*
355 * see header file
356 */
357 sql_attribute_t *sql_attribute_create(database_t *db)
358 {
359 private_sql_attribute_t *this = malloc_thing(private_sql_attribute_t);
360 time_t now = time(NULL);
361
362 this->public.provider.acquire_address = (host_t*(*)(attribute_provider_t *this, char*, identification_t *, host_t *))acquire_address;
363 this->public.provider.release_address = (bool(*)(attribute_provider_t *this, char*,host_t *, identification_t*))release_address;
364 this->public.provider.create_attribute_enumerator = (enumerator_t*(*)(attribute_provider_t*, identification_t *id, host_t *host))create_attribute_enumerator;
365 this->public.destroy = (void(*)(sql_attribute_t*))destroy;
366
367 this->db = db;
368 this->history = lib->settings->get_bool(lib->settings,
369 "libstrongswan.plugins.attr-sql.lease_history", TRUE);
370
371 /* close any "online" leases in the case we crashed */
372 if (this->history)
373 {
374 this->db->execute(this->db, NULL,
375 "INSERT INTO leases (address, identity, acquired, released)"
376 " SELECT id, identity, acquired, ? FROM addresses "
377 " WHERE released = 0", DB_UINT, now);
378 }
379 this->db->execute(this->db, NULL,
380 "UPDATE addresses SET released = ? WHERE released = 0",
381 DB_UINT, now);
382 return &this->public;
383 }
384