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