added a dispatcher class to receive HA sync messages
[strongswan.git] / src / charon / plugins / ha_sync / ha_sync_message.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 * $Id$
16 */
17
18 #define _GNU_SOURCE
19 #include <string.h>
20 #include <arpa/inet.h>
21
22 #include "ha_sync_message.h"
23
24 #include <daemon.h>
25
26 #define ALLOCATION_BLOCK 64
27
28 typedef struct private_ha_sync_message_t private_ha_sync_message_t;
29
30 /**
31 * Private data of an ha_sync_message_t object.
32 */
33 struct private_ha_sync_message_t {
34
35 /**
36 * Public ha_sync_message_t interface.
37 */
38 ha_sync_message_t public;
39
40 /**
41 * Allocated size of buf
42 */
43 size_t allocated;
44
45 /**
46 * Buffer containing encoded data
47 */
48 chunk_t buf;
49 };
50
51 typedef struct ike_sa_id_encoding_t ike_sa_id_encoding_t;
52
53 /**
54 * Encoding if an ike_sa_id_t
55 */
56 struct ike_sa_id_encoding_t {
57 u_int64_t initiator_spi;
58 u_int64_t responder_spi;
59 u_int8_t initiator;
60 } __attribute__((packed));
61
62 typedef struct identification_encoding_t identification_encoding_t;
63
64 /**
65 * Encoding of a identification_t
66 */
67 struct identification_encoding_t {
68 u_int8_t type;
69 u_int8_t len;
70 char encoding[];
71 } __attribute__((packed));
72
73 typedef struct host_encoding_t host_encoding_t;
74
75 /**
76 * encoding of a host_t
77 */
78 struct host_encoding_t {
79 u_int16_t port;
80 u_int8_t family;
81 char encoding[];
82 } __attribute__((packed));
83
84 /**
85 * Implementation of ha_sync_message_t.get_type
86 */
87 static ha_sync_message_type_t get_type(private_ha_sync_message_t *this)
88 {
89 return this->buf.ptr[1];
90 }
91
92 /**
93 * check for space in buffer, increase if necessary
94 */
95 static void check_buf(private_ha_sync_message_t *this, size_t len)
96 {
97 int increased = 0;
98
99 while (this->buf.len + len > this->allocated)
100 { /* double size */
101 this->allocated += ALLOCATION_BLOCK;
102 increased++;
103 }
104 if (increased)
105 {
106 this->buf.ptr = realloc(this->buf.ptr, this->allocated);
107 }
108 }
109
110 /**
111 * Implementation of ha_sync_message_t.add_attribute
112 */
113 static void add_attribute(private_ha_sync_message_t *this,
114 ha_sync_message_attribute_t attribute, ...)
115 {
116 size_t len;
117 va_list args;
118
119 check_buf(this, sizeof(u_int8_t));
120 this->buf.ptr[this->buf.len] = attribute;
121 this->buf.len += sizeof(u_int8_t);
122
123 va_start(args, attribute);
124 switch (attribute)
125 {
126 /* ike_sa_id_t* */
127 case HA_SYNC_IKE_ID:
128 case HA_SYNC_IKE_REKEY_ID:
129 {
130 ike_sa_id_encoding_t *enc;
131 ike_sa_id_t *id;
132
133 id = va_arg(args, ike_sa_id_t*);
134 check_buf(this, sizeof(ike_sa_id_encoding_t));
135 enc = (ike_sa_id_encoding_t*)(this->buf.ptr + this->buf.len);
136 this->buf.len += sizeof(ike_sa_id_encoding_t);
137 enc->initiator = id->is_initiator(id);
138 enc->initiator_spi = id->get_initiator_spi(id);
139 enc->responder_spi = id->get_responder_spi(id);
140 break;
141 }
142 /* identification_t* */
143 case HA_SYNC_LOCAL_ID:
144 case HA_SYNC_REMOTE_ID:
145 case HA_SYNC_EAP_ID:
146 {
147 identification_encoding_t *enc;
148 identification_t *id;
149 chunk_t data;
150
151 id = va_arg(args, identification_t*);
152 data = id->get_encoding(id);
153 check_buf(this, sizeof(identification_encoding_t) + data.len);
154 enc = (identification_encoding_t*)(this->buf.ptr + this->buf.len);
155 this->buf.len += sizeof(identification_encoding_t) + data.len;
156 enc->type = id->get_type(id);
157 enc->len = data.len;
158 memcpy(enc->encoding, data.ptr, data.len);
159 break;
160 }
161 /* host_t* */
162 case HA_SYNC_LOCAL_ADDR:
163 case HA_SYNC_REMOTE_ADDR:
164 case HA_SYNC_LOCAL_VIP:
165 case HA_SYNC_REMOTE_VIP:
166 case HA_SYNC_ADDITIONAL_ADDR:
167 {
168 host_encoding_t *enc;
169 host_t *host;
170 chunk_t data;
171
172 host = va_arg(args, host_t*);
173 data = host->get_address(host);
174 check_buf(this, sizeof(host_encoding_t) + data.len);
175 enc = (host_encoding_t*)(this->buf.ptr + this->buf.len);
176 this->buf.len += sizeof(host_encoding_t) + data.len;
177 enc->family = host->get_family(host);
178 enc->port = htons(host->get_port(host));
179 memcpy(enc->encoding, data.ptr, data.len);
180 break;
181 }
182 /* char* */
183 case HA_SYNC_CONFIG_NAME:
184 {
185 char *str;
186
187 str = va_arg(args, char*);
188 len = strlen(str) + 1;
189 check_buf(this, len);
190 memcpy(this->buf.ptr + this->buf.len, str, len);
191 this->buf.len += len;
192 break;
193 }
194 /** u_int32_t */
195 case HA_SYNC_CONDITIONS:
196 case HA_SYNC_EXTENSIONS:
197 {
198 u_int32_t val;
199
200 val = va_arg(args, u_int32_t);
201 check_buf(this, sizeof(val));
202 this->buf.ptr[this->buf.len] = htonl(val);
203 this->buf.len += sizeof(val);
204 break;
205 }
206 /** chunk_t */
207 case HA_SYNC_NONCE_I:
208 case HA_SYNC_NONCE_R:
209 case HA_SYNC_SECRET:
210 {
211 chunk_t chunk;
212
213 chunk = va_arg(args, chunk_t);
214 check_buf(this, chunk.len + sizeof(u_int16_t));
215 *(u_int16_t*)(this->buf.ptr + this->buf.len) = htons(chunk.len);
216 memcpy(this->buf.ptr + this->buf.len + sizeof(u_int16_t),
217 chunk.ptr, chunk.len);
218 this->buf.len += chunk.len + sizeof(u_int16_t);;
219 break;
220 }
221 default:
222 {
223 DBG1(DBG_CFG, "unable to encode, attribute %d unknown", attribute);
224 this->buf.len -= sizeof(u_int8_t);
225 break;
226 }
227 }
228 va_end(args);
229 }
230
231 /**
232 * Attribute enumerator implementation
233 */
234 typedef struct {
235 /** implementes enumerator_t */
236 enumerator_t public;
237 /** position in message */
238 chunk_t buf;
239 /** cleanup handler of current element, if any */
240 void (*cleanup)(void* data);
241 /** data to pass to cleanup handler */
242 void *cleanup_data;
243 } attribute_enumerator_t;
244
245 /**
246 * Implementation of create_attribute_enumerator().enumerate
247 */
248 static bool attribute_enumerate(attribute_enumerator_t *this,
249 ha_sync_message_attribute_t *attr_out,
250 ha_sync_message_value_t *value)
251 {
252 ha_sync_message_attribute_t attr;
253
254 if (this->cleanup)
255 {
256 this->cleanup(this->cleanup_data);
257 this->cleanup = NULL;
258 }
259 if (this->buf.len < 1)
260 {
261 return FALSE;
262 }
263 attr = this->buf.ptr[0];
264 this->buf = chunk_skip(this->buf, 1);
265 switch (attr)
266 {
267 /* ike_sa_id_t* */
268 case HA_SYNC_IKE_ID:
269 case HA_SYNC_IKE_REKEY_ID:
270 {
271 ike_sa_id_encoding_t *enc;
272
273 if (this->buf.len < sizeof(ike_sa_id_encoding_t))
274 {
275 return FALSE;
276 }
277 enc = (ike_sa_id_encoding_t*)(this->buf.ptr);
278 value->ike_sa_id = ike_sa_id_create(enc->initiator_spi,
279 enc->responder_spi, enc->initiator);
280 *attr_out = attr;
281 this->cleanup = (void*)value->ike_sa_id->destroy;
282 this->cleanup_data = value->ike_sa_id;
283 this->buf = chunk_skip(this->buf, sizeof(ike_sa_id_encoding_t));
284 return TRUE;
285 }
286 /* identification_t* */
287 case HA_SYNC_LOCAL_ID:
288 case HA_SYNC_REMOTE_ID:
289 case HA_SYNC_EAP_ID:
290 {
291 identification_encoding_t *enc;
292
293 enc = (identification_encoding_t*)(this->buf.ptr);
294 if (this->buf.len < sizeof(identification_encoding_t) ||
295 this->buf.len < sizeof(identification_encoding_t) + enc->len)
296 {
297 return FALSE;
298 }
299 value->id = identification_create_from_encoding(enc->type,
300 chunk_create(enc->encoding, enc->len));
301 *attr_out = attr;
302 this->cleanup = (void*)value->id->destroy;
303 this->cleanup_data = value->id;
304 this->buf = chunk_skip(this->buf,
305 sizeof(identification_encoding_t) + enc->len);
306 return TRUE;
307 }
308 /* host_t* */
309 case HA_SYNC_LOCAL_ADDR:
310 case HA_SYNC_REMOTE_ADDR:
311 case HA_SYNC_LOCAL_VIP:
312 case HA_SYNC_REMOTE_VIP:
313 case HA_SYNC_ADDITIONAL_ADDR:
314 {
315 host_encoding_t *enc;
316
317 enc = (host_encoding_t*)(this->buf.ptr);
318 if (this->buf.len < sizeof(host_encoding_t))
319 {
320 return FALSE;
321 }
322 value->host = host_create_from_chunk(enc->family,
323 chunk_create(enc->encoding,
324 this->buf.len - sizeof(host_encoding_t)),
325 ntohs(enc->port));
326 if (!value->host)
327 {
328 return FALSE;
329 }
330 *attr_out = attr;
331 this->cleanup = (void*)value->host->destroy;
332 this->cleanup_data = value->host;
333 this->buf = chunk_skip(this->buf, sizeof(host_encoding_t) +
334 value->host->get_address(value->host).len);
335 return TRUE;
336 }
337 /* char* */
338 case HA_SYNC_CONFIG_NAME:
339 {
340 size_t len;
341
342 len = strnlen(this->buf.ptr, this->buf.len);
343 if (len >= this->buf.len)
344 {
345 return FALSE;
346 }
347 value->str = this->buf.ptr;
348 *attr_out = attr;
349 this->buf = chunk_skip(this->buf, len + 1);
350 return TRUE;
351 }
352 /** u_int32_t */
353 case HA_SYNC_CONDITIONS:
354 case HA_SYNC_EXTENSIONS:
355 {
356 if (this->buf.len < sizeof(u_int32_t))
357 {
358 return FALSE;
359 }
360 value->u32 = ntohl(*(u_int32_t*)this->buf.ptr);
361 *attr_out = attr;
362 this->buf = chunk_skip(this->buf, sizeof(u_int32_t));
363 return TRUE;
364 }
365 /** chunk_t */
366 case HA_SYNC_NONCE_I:
367 case HA_SYNC_NONCE_R:
368 case HA_SYNC_SECRET:
369 {
370 size_t len;
371
372 if (this->buf.len < sizeof(u_int16_t))
373 {
374 return FALSE;
375 }
376 len = ntohs(*(u_int16_t*)this->buf.ptr);
377 this->buf = chunk_skip(this->buf, sizeof(u_int16_t));
378 if (this->buf.len < len)
379 {
380 return FALSE;
381 }
382 value->chunk.len = len;
383 value->chunk.ptr = this->buf.ptr;
384 *attr_out = attr;
385 this->buf = chunk_skip(this->buf, len);
386 return TRUE;
387 }
388 default:
389 {
390 return FALSE;
391 }
392 }
393 }
394
395 /**
396 * Implementation of create_attribute_enumerator().destroy
397 */
398 static void enum_destroy(attribute_enumerator_t *this)
399 {
400 if (this->cleanup)
401 {
402 this->cleanup(this->cleanup_data);
403 }
404 free(this);
405 }
406
407 /**
408 * Implementation of ha_sync_message_t.create_attribute_enumerator
409 */
410 static enumerator_t* create_attribute_enumerator(private_ha_sync_message_t *this)
411 {
412 attribute_enumerator_t *e = malloc_thing(attribute_enumerator_t);
413
414 e->public.enumerate = (void*)attribute_enumerate;
415 e->public.destroy = (void*)enum_destroy;
416
417 e->buf = chunk_skip(this->buf, 2);
418 e->cleanup = NULL;
419 e->cleanup_data = NULL;
420
421 return &e->public;
422 }
423
424 /**
425 * Implementation of ha_sync_message_t.get_encoding
426 */
427 static chunk_t get_encoding(private_ha_sync_message_t *this)
428 {
429 return this->buf;
430 }
431
432 /**
433 * Implementation of ha_sync_message_t.destroy.
434 */
435 static void destroy(private_ha_sync_message_t *this)
436 {
437 free(this->buf.ptr);
438 free(this);
439 }
440
441
442 static private_ha_sync_message_t *ha_sync_message_create_generic()
443 {
444 private_ha_sync_message_t *this = malloc_thing(private_ha_sync_message_t);
445
446 this->public.get_type = (ha_sync_message_type_t(*)(ha_sync_message_t*))get_type;
447 this->public.add_attribute = (void(*)(ha_sync_message_t*, ha_sync_message_attribute_t attribute, ...))add_attribute;
448 this->public.create_attribute_enumerator = (enumerator_t*(*)(ha_sync_message_t*))create_attribute_enumerator;
449 this->public.get_encoding = (chunk_t(*)(ha_sync_message_t*))get_encoding;
450 this->public.destroy = (void(*)(ha_sync_message_t*))destroy;
451
452 return this;
453 }
454
455 /**
456 * See header
457 */
458 ha_sync_message_t *ha_sync_message_create(ha_sync_message_type_t type)
459 {
460 private_ha_sync_message_t *this = ha_sync_message_create_generic();
461
462 this->allocated = ALLOCATION_BLOCK;
463 this->buf.ptr = malloc(this->allocated);
464 this->buf.len = 2;
465 this->buf.ptr[0] = HA_SYNC_MESSAGE_VERSION;
466 this->buf.ptr[1] = type;
467
468 return &this->public;
469 }
470
471 /**
472 * See header
473 */
474 ha_sync_message_t *ha_sync_message_parse(chunk_t data)
475 {
476 private_ha_sync_message_t *this;
477
478 if (data.len < 2)
479 {
480 DBG1(DBG_CFG, "HA sync message too short");
481 return NULL;
482 }
483 if (data.ptr[0] != HA_SYNC_MESSAGE_VERSION)
484 {
485 DBG1(DBG_CFG, "HA sync message has version %d, expected %d",
486 data.ptr[0], HA_SYNC_MESSAGE_VERSION);
487 return NULL;
488 }
489
490 this = ha_sync_message_create_generic();
491 this->buf = chunk_clone(data);
492 this->allocated = this->buf.len;
493
494 return &this->public;
495 }
496