Moved generic string and data hashing and comparison functions
[silc.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   char fingerprint[64], *cp;
336   int i;
337
338   memset(fingerprint, 0, sizeof(fingerprint));
339   cp = fingerprint;
340   for (i = 0; i < data_len; i++) {
341     silc_snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
342     cp += 2;
343
344     if ((i + 1) % 2 == 0)
345       silc_snprintf(cp++, sizeof(fingerprint), " ");
346
347     if ((i + 1) % 10 == 0)
348       silc_snprintf(cp++, sizeof(fingerprint), " ");
349   }
350   i--;
351   if ((i + 1) % 2 == 0)
352     cp[-2] = 0;
353   if ((i + 1) % 10 == 0)
354     cp[-1] = 0;
355
356   return silc_strdup(fingerprint);
357 }
358
359 /* Return TRUE if the `data' is ASCII string. */
360
361 SilcBool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
362 {
363   int i;
364
365   for (i = 0; i < data_len; i++) {
366     if (!isascii(data[i]))
367       return FALSE;
368   }
369
370   return TRUE;
371 }
372
373 /* Displays input prompt on command line and takes input data from user */
374
375 char *silc_get_input(const char *prompt, SilcBool echo_off)
376 {
377 #ifdef SILC_UNIX
378   int fd;
379   char input[2048];
380
381   if (echo_off) {
382     char *ret = NULL;
383 #ifdef HAVE_TERMIOS_H
384     struct termios to;
385     struct termios to_old;
386
387     fd = open("/dev/tty", O_RDONLY);
388     if (fd < 0) {
389       silc_set_errno_posix(errno);
390       return NULL;
391     }
392
393     signal(SIGINT, SIG_IGN);
394
395     /* Get terminal info */
396     tcgetattr(fd, &to);
397     to_old = to;
398
399     /* Echo OFF, and assure we can prompt and get input */
400     to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
401     to.c_lflag |= ICANON;
402     to.c_cc[VMIN] = 255;
403     tcsetattr(fd, TCSANOW, &to);
404
405     memset(input, 0, sizeof(input));
406
407     printf("%s", prompt);
408     fflush(stdout);
409
410     if ((read(fd, input, sizeof(input))) < 0) {
411       silc_set_errno_posix(errno);
412       tcsetattr(fd, TCSANOW, &to_old);
413       return NULL;
414     }
415
416     if (strlen(input) <= 1) {
417       tcsetattr(fd, TCSANOW, &to_old);
418       silc_set_errno(SILC_ERR_EOF);
419       return NULL;
420     }
421
422     if (strchr(input, '\n'))
423       *strchr(input, '\n') = '\0';
424
425     /* Restore old terminfo */
426     tcsetattr(fd, TCSANOW, &to_old);
427     signal(SIGINT, SIG_DFL);
428
429     ret = silc_memdup(input, strlen(input));
430     memset(input, 0, sizeof(input));
431 #endif /* HAVE_TERMIOS_H */
432     return ret;
433   } else {
434     fd = open("/dev/tty", O_RDONLY);
435     if (fd < 0) {
436       silc_set_errno_posix(errno);
437       return NULL;
438     }
439
440     memset(input, 0, sizeof(input));
441
442     printf("%s", prompt);
443     fflush(stdout);
444
445     if ((read(fd, input, sizeof(input))) < 0) {
446       silc_set_errno_posix(errno);
447       return NULL;
448     }
449
450     if (strlen(input) <= 1) {
451       silc_set_errno(SILC_ERR_EOF);
452       return NULL;
453     }
454
455     if (strchr(input, '\n'))
456       *strchr(input, '\n') = '\0';
457
458     return silc_strdup(input);
459   }
460 #else
461   return NULL;
462 #endif /* SILC_UNIX */
463 }
464
465 /* Hexdump */
466
467 void silc_hexdump(const unsigned char *data, SilcUInt32 data_len,
468                   FILE *output)
469 {
470   int i, k;
471   int off, pos, count;
472   int len = data_len;
473
474   k = 0;
475   pos = 0;
476   count = 16;
477   off = len % 16;
478   while (1) {
479     if (off) {
480       if ((len - pos) < 16 && (len - pos <= len - off))
481         count = off;
482     } else {
483       if (pos == len)
484         count = 0;
485     }
486     if (off == len)
487       count = len;
488
489     if (count)
490       fprintf(output, "%08X  ", k++ * 16);
491
492     for (i = 0; i < count; i++) {
493       fprintf(output, "%02X ", data[pos + i]);
494
495       if ((i + 1) % 4 == 0)
496         fprintf(output, " ");
497     }
498
499     if (count && count < 16) {
500       int j;
501
502       for (j = 0; j < 16 - count; j++) {
503         fprintf(output, "   ");
504
505         if ((j + count + 1) % 4 == 0)
506           fprintf(output, " ");
507       }
508     }
509
510     for (i = 0; i < count; i++) {
511       char ch;
512
513       if (data[pos] < 32 || data[pos] >= 127)
514         ch = '.';
515       else
516         ch = data[pos];
517
518       fprintf(output, "%c", ch);
519       pos++;
520     }
521
522     if (count)
523       fprintf(output, "\n");
524
525     if (count < 16)
526       break;
527   }
528 }
529
530 /* Convert hex string to data.  Each hex number must have two characters. */
531
532 SilcBool silc_hex2data(const char *hex, unsigned char *data,
533                        SilcUInt32 data_size, SilcUInt32 *ret_data_len)
534 {
535   char *cp = (char *)hex;
536   unsigned char l, h;
537   int i;
538
539   if (data_size < strlen(hex) / 2) {
540     silc_set_errno(SILC_ERR_OVERFLOW);
541     return FALSE;
542   }
543
544   for (i = 0; i < strlen(hex) / 2; i++) {
545     h = *cp++;
546     l = *cp++;
547
548     h -= h < 'A' ? '0' : 'A' - 10;
549     l -= l < 'A' ? '0' : 'A' - 10;
550
551     data[i] = (h << 4) | (l & 0xf);
552   }
553
554   if (ret_data_len)
555     *ret_data_len = i;
556
557   SILC_LOG_HEXDUMP(("len %d", i), data, i);
558
559   return TRUE;
560 }
561
562 /* Converts binary data to HEX string */
563
564 SilcBool silc_data2hex(const unsigned char *data, SilcUInt32 data_len,
565                        char *hex, SilcUInt32 hex_size)
566 {
567   unsigned char l, h;
568   char *cp = hex;
569   int i;
570
571   if (hex_size - 1 < data_len * 2) {
572     silc_set_errno(SILC_ERR_OVERFLOW);
573     return FALSE;
574   }
575
576   memset(hex, 0, hex_size);
577
578   for (i = 0; i < data_len; i++) {
579     l = data[i];
580     h = l >> 4;
581     l &= 0xf;
582
583     *cp++ = h + (h > 9 ? 'A' - 10 : '0');
584     *cp++ = l + (l > 9 ? 'A' - 10 : '0');
585   }
586
587   SILC_LOG_DEBUG(("HEX string: '%s'", hex));
588
589   return TRUE;
590 }