07688e36c55ce90fa864ad9fa443662095e6a007
[crypto.git] / lib / silcutil / silcutil.c
1 /*
2
3   silcutil.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2007 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /*
20  * These are general utility functions that doesn't belong to any specific
21  * group of routines.
22  */
23 /* $Id$ */
24
25 #include "silc.h"
26
27 /* Gets line from a buffer. Stops reading when a newline or EOF occurs.
28    This doesn't remove the newline sign from the destination buffer. The
29    argument begin is returned and should be passed again for the function. */
30
31 int silc_gets(char *dest, int destlen, const char *src, int srclen, int begin)
32 {
33   static int start = 0;
34   int i;
35
36   memset(dest, 0, destlen);
37
38   if (begin != start)
39     start = 0;
40
41   i = 0;
42   for ( ; start <= srclen; i++, start++) {
43     if (i > destlen) {
44       silc_set_errno(SILC_ERR_OVERFLOW);
45       return -1;
46     }
47
48     dest[i] = src[start];
49
50     if (dest[i] == EOF) {
51       silc_set_errno(SILC_ERR_EOF);
52       return EOF;
53     }
54
55     if (dest[i] == '\n')
56       break;
57   }
58   start++;
59
60   return start;
61 }
62
63 /* Converts string to capital characters. */
64
65 SilcBool silc_to_upper(const char *string, char *dest, SilcUInt32 dest_size)
66 {
67   int i;
68
69   if (strlen(string) > dest_size) {
70     silc_set_errno(SILC_ERR_OVERFLOW);
71     return FALSE;
72   }
73
74   for (i = 0; i < strlen(string); i++)
75     dest[i] = (char)toupper((int)string[i]);
76
77   return TRUE;
78 }
79
80 /* Converts string to lower letter characters. */
81
82 SilcBool silc_to_lower(const char *string, char *dest, SilcUInt32 dest_size)
83 {
84   int i;
85
86   if (strlen(string) > dest_size) {
87     silc_set_errno(SILC_ERR_OVERFLOW);
88     return FALSE;
89   }
90
91   for (i = 0; i < strlen(string); i++)
92     dest[i] = (char)tolower((int)string[i]);
93
94   return TRUE;
95 }
96
97 /* Parse userfqdn string which is in user@fqdn format. */
98
99 int silc_parse_userfqdn(const char *string,
100                         char *user, SilcUInt32 user_size,
101                         char *fqdn, SilcUInt32 fqdn_size)
102 {
103   SilcUInt32 tlen;
104
105   if (!user && !fqdn) {
106     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
107     return 0;
108   }
109
110   memset(user, 0, user_size);
111   memset(fqdn, 0, fqdn_size);
112
113   if (!string) {
114     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
115     return 0;
116   }
117
118   if (string[0] == '@') {
119     if (user)
120       silc_strncat(user, user_size, string, strlen(string));
121
122     return 1;
123   }
124
125   if (strchr(string, '@')) {
126     tlen = strcspn(string, "@");
127
128     if (user)
129       silc_strncat(user, user_size, string, tlen);
130
131     if (fqdn)
132       silc_strncat(fqdn, fqdn_size, string + tlen + 1,
133                    strlen(string) - tlen - 1);
134
135     return 2;
136   }
137
138   if (user)
139     silc_strncat(user, user_size, string, strlen(string));
140
141   return 1;
142 }
143
144 /* Parses command line. At most `max_args' is taken. Rest of the line
145    will be allocated as the last argument if there are more than `max_args'
146    arguments in the line. Note that the command name is counted as one
147    argument and is saved. */
148
149 void silc_parse_command_line(unsigned char *buffer,
150                              unsigned char ***parsed,
151                              SilcUInt32 **parsed_lens,
152                              SilcUInt32 **parsed_types,
153                              SilcUInt32 *parsed_num,
154                              SilcUInt32 max_args)
155 {
156   int i, len = 0;
157   int argc = 0;
158   const char *cp = (const char *)buffer;
159   char *tmp;
160
161   *parsed = silc_calloc(1, sizeof(**parsed));
162   *parsed_lens = silc_calloc(1, sizeof(**parsed_lens));
163
164   /* Get the command first */
165   len = strcspn(cp, " ");
166   tmp = silc_calloc(strlen(cp) + 1, sizeof(*tmp));
167   if (!tmp)
168     return;
169   silc_to_upper(cp, tmp, strlen(cp));
170   (*parsed)[0] = silc_calloc(len + 1, sizeof(char));
171   memcpy((*parsed)[0], tmp, len);
172   silc_free(tmp);
173   (*parsed_lens)[0] = len;
174   cp += len;
175   while (*cp == ' ')
176     cp++;
177   argc++;
178
179   /* Parse arguments */
180   if (strchr(cp, ' ') || strlen(cp) != 0) {
181     for (i = 1; i < max_args; i++) {
182
183       if (i != max_args - 1)
184         len = strcspn(cp, " ");
185       else
186         len = strlen(cp);
187       while (len && cp[len - 1] == ' ')
188         len--;
189       if (!len)
190         break;
191
192       *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1));
193       *parsed_lens = silc_realloc(*parsed_lens,
194                                   sizeof(**parsed_lens) * (argc + 1));
195       (*parsed)[argc] = silc_calloc(len + 1, sizeof(char));
196       memcpy((*parsed)[argc], cp, len);
197       (*parsed_lens)[argc] = len;
198       argc++;
199
200       cp += len;
201       if (strlen(cp) == 0)
202         break;
203       else
204         while (*cp == ' ')
205           cp++;
206     }
207   }
208
209   /* Save argument types. Protocol defines all argument types but
210      this implementation makes sure that they are always in correct
211      order hence this simple code. */
212   *parsed_types = silc_calloc(argc, sizeof(**parsed_types));
213   for (i = 0; i < argc; i++)
214     (*parsed_types)[i] = i;
215
216   *parsed_num = argc;
217 }
218
219 /* Formats arguments to a string and returns it after allocating memory
220    for it. It must be remembered to free it later. */
221
222 char *silc_format(char *fmt, ...)
223 {
224   va_list args;
225   char buf[8192];
226
227   memset(buf, 0, sizeof(buf));
228   va_start(args, fmt);
229   silc_vsnprintf(buf, sizeof(buf) - 1, fmt, args);
230   va_end(args);
231
232   return silc_strdup(buf);
233 }
234
235 /* Hash a ID. The `user_context' is the ID type. */
236
237 SilcUInt32 silc_hash_id(void *key, void *user_context)
238 {
239   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
240   SilcUInt32 h = 0;
241   int i;
242
243   switch (id_type) {
244   case SILC_ID_CLIENT:
245     {
246       SilcClientID *id = (SilcClientID *)key;
247
248       /* The client ID is hashed by hashing the hash of the ID
249          (which is a truncated MD5 hash of the nickname) so that we
250          can access the entry from the cache with both Client ID but
251          with just a hash from the ID as well. */
252       return silc_hash_client_id_hash(id->hash, NULL);
253     }
254     break;
255   case SILC_ID_SERVER:
256     {
257       SilcServerID *id = (SilcServerID *)key;
258
259       h = id->port * id->rnd;
260       for (i = 0; i < id->ip.data_len; i++)
261         h ^= id->ip.data[i];
262
263       return h;
264     }
265     break;
266   case SILC_ID_CHANNEL:
267     {
268       SilcChannelID *id = (SilcChannelID *)key;
269
270       h = id->port * id->rnd;
271       for (i = 0; i < id->ip.data_len; i++)
272         h ^= id->ip.data[i];
273
274       return h;
275     }
276     break;
277   default:
278     break;
279   }
280
281   return h;
282 }
283
284 /* Hash Client ID's hash. */
285
286 SilcUInt32 silc_hash_client_id_hash(void *key, void *user_context)
287 {
288   int i;
289   unsigned char *hash = key;
290   SilcUInt32 h = 0, g;
291
292   for (i = 0; i < CLIENTID_HASH_LEN; i++) {
293     h = (h << 4) + hash[i];
294     if ((g = h & 0xf0000000)) {
295       h = h ^ (g >> 24);
296       h = h ^ g;
297     }
298   }
299
300   return h;
301 }
302
303 /* Compares two ID's. May be used as SilcHashTable comparison function.
304    The Client ID's compares only the hash of the Client ID not any other
305    part of the Client ID. Other ID's are fully compared. */
306
307 SilcBool silc_hash_id_compare(void *key1, void *key2, void *user_context)
308 {
309   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
310   return (id_type == SILC_ID_CLIENT ?
311           SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
312           SILC_ID_COMPARE_TYPE(key1, key2, id_type));
313 }
314
315 /* Compares two ID's. Compares full IDs. */
316
317 SilcBool silc_hash_id_compare_full(void *key1, void *key2, void *user_context)
318 {
319   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
320   return SILC_ID_COMPARE_TYPE(key1, key2, id_type);
321 }
322
323 /* Compare two Client ID's entirely and not just the hash from the ID. */
324
325 SilcBool silc_hash_client_id_compare(void *key1, void *key2,
326                                      void *user_context)
327 {
328   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
329 }
330
331 /* Creates fingerprint from data, usually used with SHA1 digests */
332
333 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
334 {
335   unsigned char *fingerprint, *cp;
336   unsigned int len, blocks, i;
337
338   if (!data || !data_len) {
339     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
340     return NULL;
341   }
342
343   if (data_len >= 256)
344     data_len = 255;
345
346   /* Align and calculate total length */
347   len = ((data_len + 19) / 20) * 20;
348   blocks = (len / 10);
349   len = (len * 2) + ((blocks - 1) * 2) + (4 * blocks) + 2 + 1;
350
351   cp = fingerprint = silc_calloc(len, sizeof(*fingerprint));
352   if (!cp)
353     return NULL;
354
355   for (i = 0; i < data_len; i++) {
356     silc_snprintf(cp, len, "%02X", data[i]);
357     cp += 2;
358     len -= 2;
359
360     if ((i + 1) % 2 == 0)
361       silc_snprintf(cp++, len--, " ");
362     if ((i + 1) % 10 == 0)
363       silc_snprintf(cp++, len--, " ");
364   }
365   i--;
366   if ((i + 1) % 10 == 0)
367     *(--cp) = '\0';
368   if ((i + 1) % 2 == 0)
369     *(--cp) = '\0';
370
371   return fingerprint;
372 }
373
374 /* Return TRUE if the `data' is ASCII string. */
375
376 SilcBool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
377 {
378   int i;
379
380   for (i = 0; i < data_len; i++) {
381     if (!isascii(data[i]))
382       return FALSE;
383   }
384
385   return TRUE;
386 }
387
388 /* Displays input prompt on command line and takes input data from user */
389
390 char *silc_get_input(const char *prompt, SilcBool echo_off)
391 {
392 #ifdef SILC_UNIX
393   int fd;
394   char input[2048];
395
396   if (echo_off) {
397     char *ret = NULL;
398 #ifdef HAVE_TERMIOS_H
399     struct termios to;
400     struct termios to_old;
401
402     fd = open("/dev/tty", O_RDONLY);
403     if (fd < 0) {
404       silc_set_errno_posix(errno);
405       return NULL;
406     }
407
408     signal(SIGINT, SIG_IGN);
409
410     /* Get terminal info */
411     tcgetattr(fd, &to);
412     to_old = to;
413
414     /* Echo OFF, and assure we can prompt and get input */
415     to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
416     to.c_lflag |= ICANON;
417     to.c_cc[VMIN] = 255;
418     tcsetattr(fd, TCSANOW, &to);
419
420     memset(input, 0, sizeof(input));
421
422     printf("%s", prompt);
423     fflush(stdout);
424
425     if ((read(fd, input, sizeof(input))) < 0) {
426       silc_set_errno_posix(errno);
427       tcsetattr(fd, TCSANOW, &to_old);
428       return NULL;
429     }
430
431     if (strlen(input) <= 1) {
432       tcsetattr(fd, TCSANOW, &to_old);
433       silc_set_errno(SILC_ERR_EOF);
434       return NULL;
435     }
436
437     if (strchr(input, '\n'))
438       *strchr(input, '\n') = '\0';
439
440     /* Restore old terminfo */
441     tcsetattr(fd, TCSANOW, &to_old);
442     signal(SIGINT, SIG_DFL);
443
444     ret = silc_memdup(input, strlen(input));
445     memset(input, 0, sizeof(input));
446 #endif /* HAVE_TERMIOS_H */
447     return ret;
448   } else {
449     fd = open("/dev/tty", O_RDONLY);
450     if (fd < 0) {
451       silc_set_errno_posix(errno);
452       return NULL;
453     }
454
455     memset(input, 0, sizeof(input));
456
457     printf("%s", prompt);
458     fflush(stdout);
459
460     if ((read(fd, input, sizeof(input))) < 0) {
461       silc_set_errno_posix(errno);
462       return NULL;
463     }
464
465     if (strlen(input) <= 1) {
466       silc_set_errno(SILC_ERR_EOF);
467       return NULL;
468     }
469
470     if (strchr(input, '\n'))
471       *strchr(input, '\n') = '\0';
472
473     return silc_strdup(input);
474   }
475 #else
476   return NULL;
477 #endif /* SILC_UNIX */
478 }
479
480 /* Hexdump */
481
482 void silc_hexdump(const unsigned char *data, SilcUInt32 data_len,
483                   FILE *output)
484 {
485   int i, k;
486   int off, pos, count;
487   int len = data_len;
488
489   k = 0;
490   pos = 0;
491   count = 16;
492   off = len % 16;
493   while (1) {
494     if (off) {
495       if ((len - pos) < 16 && (len - pos <= len - off))
496         count = off;
497     } else {
498       if (pos == len)
499         count = 0;
500     }
501     if (off == len)
502       count = len;
503
504     if (count)
505       fprintf(output, "%08X  ", k++ * 16);
506
507     for (i = 0; i < count; i++) {
508       fprintf(output, "%02X ", data[pos + i]);
509
510       if ((i + 1) % 4 == 0)
511         fprintf(output, " ");
512     }
513
514     if (count && count < 16) {
515       int j;
516
517       for (j = 0; j < 16 - count; j++) {
518         fprintf(output, "   ");
519
520         if ((j + count + 1) % 4 == 0)
521           fprintf(output, " ");
522       }
523     }
524
525     for (i = 0; i < count; i++) {
526       char ch;
527
528       if (data[pos] < 32 || data[pos] >= 127)
529         ch = '.';
530       else
531         ch = data[pos];
532
533       fprintf(output, "%c", ch);
534       pos++;
535     }
536
537     if (count)
538       fprintf(output, "\n");
539
540     if (count < 16)
541       break;
542   }
543 }
544
545 /* Convert hex string to data.  Each hex number must have two characters. */
546
547 SilcBool silc_hex2data(const char *hex, unsigned char *data,
548                        SilcUInt32 data_size, SilcUInt32 *ret_data_len)
549 {
550   char *cp = (char *)hex;
551   unsigned char l, h;
552   int i;
553
554   if (data_size < strlen(hex) / 2) {
555     silc_set_errno(SILC_ERR_OVERFLOW);
556     return FALSE;
557   }
558
559   for (i = 0; i < strlen(hex) / 2; i++) {
560     h = *cp++;
561     l = *cp++;
562
563     h -= h < 'A' ? '0' : 'A' - 10;
564     l -= l < 'A' ? '0' : 'A' - 10;
565
566     data[i] = (h << 4) | (l & 0xf);
567   }
568
569   if (ret_data_len)
570     *ret_data_len = i;
571
572   SILC_LOG_HEXDUMP(("len %d", i), data, i);
573
574   return TRUE;
575 }
576
577 /* Converts binary data to HEX string */
578
579 SilcBool silc_data2hex(const unsigned char *data, SilcUInt32 data_len,
580                        char *hex, SilcUInt32 hex_size)
581 {
582   unsigned char l, h;
583   char *cp = hex;
584   int i;
585
586   if (hex_size - 1 < data_len * 2) {
587     silc_set_errno(SILC_ERR_OVERFLOW);
588     return FALSE;
589   }
590
591   memset(hex, 0, hex_size);
592
593   for (i = 0; i < data_len; i++) {
594     l = data[i];
595     h = l >> 4;
596     l &= 0xf;
597
598     *cp++ = h + (h > 9 ? 'A' - 10 : '0');
599     *cp++ = l + (l > 9 ? 'A' - 10 : '0');
600   }
601
602   SILC_LOG_DEBUG(("HEX string: '%s'", hex));
603
604   return TRUE;
605 }