c9c181f878264b119737a560c67bd5fdb8166405
[strongswan.git] / src / libstrongswan / chunk.c
1 /*
2 * Copyright (C) 2008-2009 Tobias Brunner
3 * Copyright (C) 2005-2006 Martin Willi
4 * Copyright (C) 2005 Jan Hutter
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include <stdio.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <errno.h>
22
23 #include "chunk.h"
24
25 #include <debug.h>
26
27 /* required for chunk_hash */
28 #undef get16bits
29 #if (defined(__GNUC__) && defined(__i386__))
30 #define get16bits(d) (*((const u_int16_t*)(d)))
31 #endif
32 #if !defined (get16bits)
33 #define get16bits(d) ((((u_int32_t)(((const u_int8_t*)(d))[1])) << 8)\
34 + (u_int32_t)(((const u_int8_t*)(d))[0]) )
35 #endif
36
37 /**
38 * Empty chunk.
39 */
40 chunk_t chunk_empty = { NULL, 0 };
41
42 /**
43 * Described in header.
44 */
45 chunk_t chunk_create_clone(u_char *ptr, chunk_t chunk)
46 {
47 chunk_t clone = chunk_empty;
48
49 if (chunk.ptr && chunk.len > 0)
50 {
51 clone.ptr = ptr;
52 clone.len = chunk.len;
53 memcpy(clone.ptr, chunk.ptr, chunk.len);
54 }
55
56 return clone;
57 }
58
59 /**
60 * Decribed in header.
61 */
62 size_t chunk_length(const char* mode, ...)
63 {
64 va_list chunks;
65 size_t length = 0;
66
67 va_start(chunks, mode);
68 while (TRUE)
69 {
70 switch (*mode++)
71 {
72 case 'm':
73 case 'c':
74 {
75 chunk_t ch = va_arg(chunks, chunk_t);
76 length += ch.len;
77 continue;
78 }
79 default:
80 break;
81 }
82 break;
83 }
84 va_end(chunks);
85 return length;
86 }
87
88 /**
89 * Decribed in header.
90 */
91 chunk_t chunk_create_cat(u_char *ptr, const char* mode, ...)
92 {
93 va_list chunks;
94 chunk_t construct = chunk_create(ptr, 0);
95
96 va_start(chunks, mode);
97 while (TRUE)
98 {
99 bool free_chunk = FALSE;
100 switch (*mode++)
101 {
102 case 'm':
103 {
104 free_chunk = TRUE;
105 }
106 case 'c':
107 {
108 chunk_t ch = va_arg(chunks, chunk_t);
109 memcpy(ptr, ch.ptr, ch.len);
110 ptr += ch.len;
111 construct.len += ch.len;
112 if (free_chunk)
113 {
114 free(ch.ptr);
115 }
116 continue;
117 }
118 default:
119 break;
120 }
121 break;
122 }
123 va_end(chunks);
124
125 return construct;
126 }
127
128 /**
129 * Decribed in header.
130 */
131 void chunk_split(chunk_t chunk, const char *mode, ...)
132 {
133 va_list chunks;
134 u_int len;
135 chunk_t *ch;
136
137 va_start(chunks, mode);
138 while (TRUE)
139 {
140 if (*mode == '\0')
141 {
142 break;
143 }
144 len = va_arg(chunks, u_int);
145 ch = va_arg(chunks, chunk_t*);
146 /* a null chunk means skip len bytes */
147 if (ch == NULL)
148 {
149 chunk = chunk_skip(chunk, len);
150 continue;
151 }
152 switch (*mode++)
153 {
154 case 'm':
155 {
156 ch->len = min(chunk.len, len);
157 if (ch->len)
158 {
159 ch->ptr = chunk.ptr;
160 }
161 else
162 {
163 ch->ptr = NULL;
164 }
165 chunk = chunk_skip(chunk, ch->len);
166 continue;
167 }
168 case 'a':
169 {
170 ch->len = min(chunk.len, len);
171 if (ch->len)
172 {
173 ch->ptr = malloc(ch->len);
174 memcpy(ch->ptr, chunk.ptr, ch->len);
175 }
176 else
177 {
178 ch->ptr = NULL;
179 }
180 chunk = chunk_skip(chunk, ch->len);
181 continue;
182 }
183 case 'c':
184 {
185 ch->len = min(ch->len, chunk.len);
186 ch->len = min(ch->len, len);
187 if (ch->len)
188 {
189 memcpy(ch->ptr, chunk.ptr, ch->len);
190 }
191 else
192 {
193 ch->ptr = NULL;
194 }
195 chunk = chunk_skip(chunk, ch->len);
196 continue;
197 }
198 default:
199 break;
200 }
201 break;
202 }
203 va_end(chunks);
204 }
205
206 /**
207 * Described in header.
208 */
209 bool chunk_write(chunk_t chunk, char *path, char *label, mode_t mask, bool force)
210 {
211 mode_t oldmask;
212 FILE *fd;
213 bool good = FALSE;
214
215 if (!force && access(path, F_OK) == 0)
216 {
217 DBG1(" %s file '%s' already exists", label, path);
218 return FALSE;
219 }
220 oldmask = umask(mask);
221 fd = fopen(path, "w");
222 if (fd)
223 {
224 if (fwrite(chunk.ptr, sizeof(u_char), chunk.len, fd) == chunk.len)
225 {
226 DBG1(" written %s file '%s' (%d bytes)",
227 label, path, chunk.len);
228 good = TRUE;
229 }
230 else
231 {
232 DBG1(" writing %s file '%s' failed: %s",
233 label, path, strerror(errno));
234 }
235 fclose(fd);
236 }
237 else
238 {
239 DBG1(" could not open %s file '%s': %s", label, path, strerror(errno));
240 }
241 umask(oldmask);
242 return good;
243 }
244
245
246 /** hex conversion digits */
247 static char hexdig_upper[] = "0123456789ABCDEF";
248 static char hexdig_lower[] = "0123456789abcdef";
249
250 /**
251 * Described in header.
252 */
253 chunk_t chunk_to_hex(chunk_t chunk, char *buf, bool uppercase)
254 {
255 int i, len;
256 char *hexdig = hexdig_lower;
257
258 if (uppercase)
259 {
260 hexdig = hexdig_upper;
261 }
262
263 len = chunk.len * 2;
264 if (!buf)
265 {
266 buf = malloc(len + 1);
267 }
268 buf[len] = '\0';
269
270 for (i = 0; i < chunk.len; i++)
271 {
272 buf[i*2] = hexdig[(chunk.ptr[i] >> 4) & 0xF];
273 buf[i*2+1] = hexdig[(chunk.ptr[i] ) & 0xF];
274 }
275 return chunk_create(buf, len);
276 }
277
278 /**
279 * convert a signle hex character to its binary value
280 */
281 static char hex2bin(char hex)
282 {
283 switch (hex)
284 {
285 case '0' ... '9':
286 return hex - '0';
287 case 'A' ... 'F':
288 return hex - 'A' + 10;
289 case 'a' ... 'f':
290 return hex - 'a' + 10;
291 default:
292 return 0;
293 }
294 }
295
296 /**
297 * Described in header.
298 */
299 chunk_t chunk_from_hex(chunk_t hex, char *buf)
300 {
301 int i, len;
302 bool odd = FALSE;
303
304 len = (hex.len / 2);
305 if (hex.len % 2)
306 {
307 odd = TRUE;
308 len++;
309 }
310 if (!buf)
311 {
312 buf = malloc(len);
313 }
314 /* buffer is filled from the right */
315 memset(buf, 0, len);
316 hex.ptr += hex.len;
317 for (i = len - 1; i >= 0; i--)
318 {
319 buf[i] = hex2bin(*(--hex.ptr));
320 if (i > 0 || !odd)
321 {
322 buf[i] |= hex2bin(*(--hex.ptr)) << 4;
323 }
324 }
325 return chunk_create(buf, len);
326 }
327
328 /** base 64 conversion digits */
329 static char b64digits[] =
330 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
331
332 /**
333 * Described in header.
334 */
335 chunk_t chunk_to_base64(chunk_t chunk, char *buf)
336 {
337 int i, len;
338 char *pos;
339
340 len = chunk.len + ((3 - chunk.len % 3) % 3);
341 if (!buf)
342 {
343 buf = malloc(len * 4 / 3 + 1);
344 }
345 pos = buf;
346 for (i = 0; i < len; i+=3)
347 {
348 *pos++ = b64digits[chunk.ptr[i] >> 2];
349 if (i+1 >= chunk.len)
350 {
351 *pos++ = b64digits[(chunk.ptr[i] & 0x03) << 4];
352 *pos++ = '=';
353 *pos++ = '=';
354 break;
355 }
356 *pos++ = b64digits[((chunk.ptr[i] & 0x03) << 4) | (chunk.ptr[i+1] >> 4)];
357 if (i+2 >= chunk.len)
358 {
359 *pos++ = b64digits[(chunk.ptr[i+1] & 0x0F) << 2];
360 *pos++ = '=';
361 break;
362 }
363 *pos++ = b64digits[((chunk.ptr[i+1] & 0x0F) << 2) | (chunk.ptr[i+2] >> 6)];
364 *pos++ = b64digits[chunk.ptr[i+2] & 0x3F];
365 }
366 *pos = '\0';
367 return chunk_create(buf, len * 4 / 3);
368 }
369
370 /**
371 * convert a base 64 digit to its binary form (inversion of b64digits array)
372 */
373 static int b642bin(char b64)
374 {
375 switch (b64)
376 {
377 case 'A' ... 'Z':
378 return b64 - 'A';
379 case 'a' ... 'z':
380 return ('Z' - 'A' + 1) + b64 - 'a';
381 case '0' ... '9':
382 return ('Z' - 'A' + 1) + ('z' - 'a' + 1) + b64 - '0';
383 case '+':
384 case '-':
385 return 62;
386 case '/':
387 case '_':
388 return 63;
389 case '=':
390 return 0;
391 default:
392 return -1;
393 }
394 }
395
396 /**
397 * Described in header.
398 */
399 chunk_t chunk_from_base64(chunk_t base64, char *buf)
400 {
401 u_char *pos, byte[4];
402 int i, j, len, outlen;
403
404 len = base64.len / 4 * 3;
405 if (!buf)
406 {
407 buf = malloc(len);
408 }
409 pos = base64.ptr;
410 outlen = 0;
411 for (i = 0; i < len; i+=3)
412 {
413 outlen += 3;
414 for (j = 0; j < 4; j++)
415 {
416 if (*pos == '=')
417 {
418 outlen--;
419 }
420 byte[j] = b642bin(*pos++);
421 }
422 buf[i] = (byte[0] << 2) | (byte[1] >> 4);
423 buf[i+1] = (byte[1] << 4) | (byte[2] >> 2);
424 buf[i+2] = (byte[2] << 6) | (byte[3]);
425 }
426 return chunk_create(buf, outlen);
427 }
428
429 /**
430 * Described in header.
431 */
432 int chunk_compare(chunk_t a, chunk_t b)
433 {
434 int compare_len = a.len - b.len;
435 int len = (compare_len < 0)? a.len : b.len;
436
437 if (compare_len != 0 || len == 0)
438 {
439 return compare_len;
440 }
441 return memcmp(a.ptr, b.ptr, len);
442 };
443
444 /**
445 * Described in header.
446 *
447 * The implementation is based on Paul Hsieh's SuperFastHash:
448 * http://www.azillionmonkeys.com/qed/hash.html
449 */
450 u_int32_t chunk_hash_inc(chunk_t chunk, u_int32_t hash)
451 {
452 u_char *data = chunk.ptr;
453 size_t len = chunk.len;
454 u_int32_t tmp;
455 int rem;
456
457 if (!len || data == NULL)
458 {
459 return 0;
460 }
461
462 rem = len & 3;
463 len >>= 2;
464
465 /* Main loop */
466 for (; len > 0; --len)
467 {
468 hash += get16bits(data);
469 tmp = (get16bits(data + 2) << 11) ^ hash;
470 hash = (hash << 16) ^ tmp;
471 data += 2 * sizeof(u_int16_t);
472 hash += hash >> 11;
473 }
474
475 /* Handle end cases */
476 switch (rem)
477 {
478 case 3:
479 {
480 hash += get16bits(data);
481 hash ^= hash << 16;
482 hash ^= data[sizeof(u_int16_t)] << 18;
483 hash += hash >> 11;
484 break;
485 }
486 case 2:
487 {
488 hash += get16bits(data);
489 hash ^= hash << 11;
490 hash += hash >> 17;
491 break;
492 }
493 case 1:
494 {
495 hash += *data;
496 hash ^= hash << 10;
497 hash += hash >> 1;
498 break;
499 }
500 }
501
502 /* Force "avalanching" of final 127 bits */
503 hash ^= hash << 3;
504 hash += hash >> 5;
505 hash ^= hash << 4;
506 hash += hash >> 17;
507 hash ^= hash << 25;
508 hash += hash >> 6;
509
510 return hash;
511 }
512
513 /**
514 * Described in header.
515 */
516 u_int32_t chunk_hash(chunk_t chunk)
517 {
518 return chunk_hash_inc(chunk, chunk.len);
519 }
520
521 /**
522 * Described in header.
523 */
524 int chunk_printf_hook(char *dst, size_t len, printf_hook_spec_t *spec,
525 const void *const *args)
526 {
527 chunk_t *chunk = *((chunk_t**)(args[0]));
528 bool first = TRUE;
529 chunk_t copy = *chunk;
530 int written = 0;
531
532 if (!spec->hash)
533 {
534 const void *new_args[] = {&chunk->ptr, &chunk->len};
535 return mem_printf_hook(dst, len, spec, new_args);
536 }
537
538 while (copy.len > 0)
539 {
540 if (first)
541 {
542 first = FALSE;
543 }
544 else
545 {
546 written += print_in_hook(dst, len, ":");
547 }
548 written += print_in_hook(dst, len, "%02x", *copy.ptr++);
549 copy.len--;
550 }
551 return written;
552 }