]>
Commit | Line | Data |
---|---|---|
c4ec8c9d MW |
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. | |
c4ec8c9d MW |
14 | */ |
15 | ||
479f2950 MW |
16 | #include <time.h> |
17 | ||
24779482 AS |
18 | #include <debug.h> |
19 | #include <library.h> | |
20 | ||
21 | #include "sql_attribute.h" | |
c4ec8c9d MW |
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; | |
7daf5226 | 34 | |
c4ec8c9d MW |
35 | /** |
36 | * database connection | |
37 | */ | |
38 | database_t *db; | |
7daf5226 | 39 | |
3bc5a137 MW |
40 | /** |
41 | * wheter to record lease history in lease table | |
42 | */ | |
43 | bool history; | |
c4ec8c9d MW |
44 | }; |
45 | ||
c4ec8c9d | 46 | /** |
f7198e7e | 47 | * lookup/insert an identity |
c4ec8c9d | 48 | */ |
f7198e7e | 49 | static u_int get_identity(private_sql_attribute_t *this, identification_t *id) |
c4ec8c9d | 50 | { |
f7198e7e MW |
51 | enumerator_t *e; |
52 | u_int row; | |
7daf5226 | 53 | |
f7198e7e MW |
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); | |
7daf5226 | 59 | |
f7198e7e | 60 | if (e && e->enumerate(e, &row)) |
c4ec8c9d | 61 | { |
f7198e7e MW |
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; | |
c4ec8c9d | 72 | } |
f7198e7e | 73 | return 0; |
c4ec8c9d MW |
74 | } |
75 | ||
76 | /** | |
f7198e7e | 77 | * Lookup pool by name |
c4ec8c9d | 78 | */ |
f7198e7e | 79 | static u_int get_pool(private_sql_attribute_t *this, char *name, u_int *timeout) |
c4ec8c9d MW |
80 | { |
81 | enumerator_t *e; | |
f7198e7e MW |
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)) | |
c4ec8c9d | 87 | { |
c4ec8c9d | 88 | e->destroy(e); |
f7198e7e | 89 | return pool; |
c4ec8c9d | 90 | } |
d9ad73d8 | 91 | DESTROY_IF(e); |
f7198e7e | 92 | return 0; |
c4ec8c9d MW |
93 | } |
94 | ||
95 | /** | |
7f522b5f | 96 | * Look up an existing lease |
c4ec8c9d | 97 | */ |
7f522b5f AS |
98 | static host_t* check_lease(private_sql_attribute_t *this, char *name, |
99 | u_int pool, u_int identity) | |
c4ec8c9d | 100 | { |
f7198e7e | 101 | while (TRUE) |
c4ec8c9d | 102 | { |
7f522b5f AS |
103 | u_int id; |
104 | chunk_t address; | |
105 | enumerator_t *e; | |
106 | time_t now = time(NULL); | |
107 | ||
fa3fe3c1 | 108 | e = this->db->query(this->db, |
f7198e7e MW |
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)) | |
fa3fe3c1 | 113 | { |
fa3fe3c1 | 114 | DESTROY_IF(e); |
7daf5226 | 115 | break; |
f7198e7e MW |
116 | } |
117 | address = chunk_clonea(address); | |
118 | e->destroy(e); | |
7f522b5f | 119 | |
f7198e7e MW |
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 | { | |
7f522b5f AS |
125 | host_t *host; |
126 | ||
b8cbb645 | 127 | host = host_create_from_chunk(AF_UNSPEC, address, 0); |
f7198e7e MW |
128 | if (host) |
129 | { | |
24779482 AS |
130 | DBG1("acquired existing lease for address %H in pool '%s'", |
131 | host, name); | |
f7198e7e MW |
132 | return host; |
133 | } | |
fa3fe3c1 | 134 | } |
c4ec8c9d | 135 | } |
7f522b5f AS |
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 | { | |
f7198e7e | 147 | while (TRUE) |
fa3fe3c1 | 148 | { |
7f522b5f AS |
149 | u_int id; |
150 | chunk_t address; | |
151 | enumerator_t *e; | |
152 | time_t now = time(NULL); | |
07be083b AS |
153 | int hits; |
154 | ||
155 | if (timeout) | |
156 | { | |
157 | /* check for an expired lease */ | |
158 | e = this->db->query(this->db, | |
f7198e7e MW |
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); | |
07be083b AS |
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 | ||
f7198e7e MW |
173 | if (!e || !e->enumerate(e, &id, &address)) |
174 | { | |
175 | DESTROY_IF(e); | |
7daf5226 | 176 | break; |
f7198e7e MW |
177 | } |
178 | address = chunk_clonea(address); | |
fa3fe3c1 | 179 | e->destroy(e); |
7daf5226 | 180 | |
07be083b AS |
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) | |
c4ec8c9d | 199 | { |
7f522b5f AS |
200 | host_t *host; |
201 | ||
b8cbb645 | 202 | host = host_create_from_chunk(AF_UNSPEC, address, 0); |
f7198e7e | 203 | if (host) |
fa3fe3c1 | 204 | { |
24779482 AS |
205 | DBG1("acquired new lease for address %H in pool '%s'", |
206 | host, name); | |
f7198e7e | 207 | return host; |
c4ec8c9d MW |
208 | } |
209 | } | |
210 | } | |
24779482 | 211 | DBG1("no available address found in pool '%s'", name); |
7f522b5f | 212 | return NULL; |
c4ec8c9d MW |
213 | } |
214 | ||
215 | /** | |
216 | * Implementation of attribute_provider_t.acquire_address | |
217 | */ | |
218 | static host_t* acquire_address(private_sql_attribute_t *this, | |
7f522b5f | 219 | char *names, identification_t *id, |
a44bb934 | 220 | host_t *requested) |
c4ec8c9d | 221 | { |
f7198e7e | 222 | host_t *address = NULL; |
7f522b5f AS |
223 | u_int identity, pool, timeout; |
224 | ||
f7198e7e MW |
225 | identity = get_identity(this, id); |
226 | if (identity) | |
c4ec8c9d | 227 | { |
848133ff AS |
228 | /* check for a single pool first (no concatenation and enumeration) */ |
229 | if (strchr(names, ',') == NULL) | |
7f522b5f | 230 | { |
848133ff | 231 | pool = get_pool(this, names, &timeout); |
7f522b5f AS |
232 | if (pool) |
233 | { | |
848133ff AS |
234 | /* check for an existing lease */ |
235 | address = check_lease(this, names, pool, identity); | |
236 | if (address == NULL) | |
7f522b5f | 237 | { |
848133ff AS |
238 | /* get an unallocated address or expired lease */ |
239 | address = get_lease(this, names, pool, timeout, identity); | |
7f522b5f AS |
240 | } |
241 | } | |
242 | } | |
848133ff | 243 | else |
d932435e | 244 | { |
848133ff AS |
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)) | |
f7198e7e | 268 | { |
848133ff AS |
269 | pool = get_pool(this, name, &timeout); |
270 | if (pool) | |
f7198e7e | 271 | { |
848133ff AS |
272 | address = get_lease(this, name, pool, timeout, identity); |
273 | if (address) | |
274 | { | |
275 | break; | |
276 | } | |
f7198e7e MW |
277 | } |
278 | } | |
848133ff | 279 | enumerator->destroy(enumerator); |
d932435e | 280 | } |
c4ec8c9d | 281 | } |
f7198e7e | 282 | return address; |
c4ec8c9d MW |
283 | } |
284 | ||
285 | /** | |
286 | * Implementation of attribute_provider_t.release_address | |
287 | */ | |
288 | static bool release_address(private_sql_attribute_t *this, | |
876d5c63 | 289 | char *name, host_t *address, identification_t *id) |
c4ec8c9d | 290 | { |
d932435e | 291 | enumerator_t *enumerator; |
f7198e7e MW |
292 | bool found = FALSE; |
293 | time_t now = time(NULL); | |
7daf5226 | 294 | |
d932435e MW |
295 | enumerator = enumerator_create_token(name, ",", " "); |
296 | while (enumerator->enumerate(enumerator, &name)) | |
c4ec8c9d | 297 | { |
f7198e7e | 298 | u_int pool, timeout; |
7daf5226 | 299 | |
f7198e7e MW |
300 | pool = get_pool(this, name, &timeout); |
301 | if (pool) | |
d932435e | 302 | { |
3bc5a137 MW |
303 | if (this->history) |
304 | { | |
305 | this->db->execute(this->db, NULL, | |
f7198e7e MW |
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, | |
3bc5a137 MW |
310 | DB_BLOB, address->get_address(address)); |
311 | } | |
312 | if (this->db->execute(this->db, NULL, | |
f7198e7e MW |
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 | } | |
d932435e | 320 | } |
c4ec8c9d | 321 | } |
d932435e | 322 | enumerator->destroy(enumerator); |
f7198e7e | 323 | return found; |
c4ec8c9d MW |
324 | } |
325 | ||
a461e20d AS |
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, | |
3747f0f2 | 337 | "SELECT type, value FROM attributes", DB_INT, DB_BLOB); |
a461e20d AS |
338 | if (enumerator) |
339 | { | |
340 | return enumerator; | |
341 | } | |
342 | } | |
343 | return enumerator_create_empty(); | |
344 | } | |
345 | ||
c4ec8c9d MW |
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); | |
f7198e7e | 360 | time_t now = time(NULL); |
7daf5226 | 361 | |
a44bb934 | 362 | this->public.provider.acquire_address = (host_t*(*)(attribute_provider_t *this, char*, identification_t *, host_t *))acquire_address; |
876d5c63 | 363 | this->public.provider.release_address = (bool(*)(attribute_provider_t *this, char*,host_t *, identification_t*))release_address; |
a461e20d | 364 | this->public.provider.create_attribute_enumerator = (enumerator_t*(*)(attribute_provider_t*, identification_t *id, host_t *host))create_attribute_enumerator; |
c4ec8c9d | 365 | this->public.destroy = (void(*)(sql_attribute_t*))destroy; |
7daf5226 | 366 | |
c4ec8c9d | 367 | this->db = db; |
3bc5a137 | 368 | this->history = lib->settings->get_bool(lib->settings, |
d38eb335 | 369 | "libstrongswan.plugins.attr-sql.lease_history", TRUE); |
7daf5226 | 370 | |
5373f2a6 | 371 | /* close any "online" leases in the case we crashed */ |
3bc5a137 MW |
372 | if (this->history) |
373 | { | |
374 | this->db->execute(this->db, NULL, | |
f7198e7e MW |
375 | "INSERT INTO leases (address, identity, acquired, released)" |
376 | " SELECT id, identity, acquired, ? FROM addresses " | |
377 | " WHERE released = 0", DB_UINT, now); | |
3bc5a137 | 378 | } |
f7198e7e MW |
379 | this->db->execute(this->db, NULL, |
380 | "UPDATE addresses SET released = ? WHERE released = 0", | |
381 | DB_UINT, now); | |
c4ec8c9d MW |
382 | return &this->public; |
383 | } | |
384 |