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