5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2002 Pekka Riikonen
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.
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.
20 * These are general utility functions that doesn't belong to any specific
25 #include "silcincludes.h"
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. */
31 int silc_gets(char *dest, int destlen, const char *src, int srclen, int begin)
36 memset(dest, 0, destlen);
42 for ( ; start <= srclen; i++, start++) {
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. */
63 int silc_check_line(char *buf)
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;
81 /* Returns current time as string. */
89 return_time = ctime(&curtime);
90 return_time[strlen(return_time) - 1] = '\0';
95 /* Converts string to capital characters. */
97 char *silc_to_upper(char *string)
100 char *ret = silc_calloc(strlen(string) + 1, sizeof(char));
102 for (i = 0; i < strlen(string); i++)
103 ret[i] = toupper(string[i]);
108 static unsigned char pem_enc[64] =
109 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
111 /* Encodes data into PEM encoding. Returns NULL terminated PEM encoded
112 data string. Note: This is originally public domain code and is
115 char *silc_encode_pem(unsigned char *data, SilcUInt32 len)
118 SilcUInt32 bits, c, char_count;
125 pem = silc_calloc(((len * 8 + 5) / 6) + 5, sizeof(*pem));
127 for (i = 0; i < len; i++) {
132 if (char_count == 3) {
133 pem[j++] = pem_enc[bits >> 18];
134 pem[j++] = pem_enc[(bits >> 12) & 0x3f];
135 pem[j++] = pem_enc[(bits >> 6) & 0x3f];
136 pem[j++] = pem_enc[bits & 0x3f];
144 if (char_count != 0) {
145 bits <<= 16 - (8 * char_count);
146 pem[j++] = pem_enc[bits >> 18];
147 pem[j++] = pem_enc[(bits >> 12) & 0x3f];
149 if (char_count == 1) {
153 pem[j++] = pem_enc[(bits >> 6) & 0x3f];
161 /* Same as above but puts newline ('\n') every 72 characters. */
163 char *silc_encode_pem_file(unsigned char *data, SilcUInt32 data_len)
166 SilcUInt32 len, cols;
169 pem = silc_encode_pem(data, data_len);
172 pem2 = silc_calloc(len + (len / 72) + 1, sizeof(*pem2));
174 for (i = 0, j = 0, cols = 1; i < len; i++, cols++) {
189 /* Decodes PEM into data. Returns the decoded data. Note: This is
190 originally public domain code and is still PD. */
192 unsigned char *silc_decode_pem(unsigned char *pem, SilcUInt32 pem_len,
196 SilcUInt32 len, c, char_count, bits;
198 static char ialpha[256], decoder[256];
200 for (i = 64 - 1; i >= 0; i--) {
201 ialpha[pem_enc[i]] = 1;
202 decoder[pem_enc[i]] = i;
214 data = silc_calloc(((len * 6) / 8), sizeof(*data));
216 for (i = 0; i < len; i++) {
222 if (c > 127 || !ialpha[c])
228 if (char_count == 4) {
229 data[j++] = bits >> 16;
230 data[j++] = (bits >> 8) & 0xff;
231 data[j++] = bits & 0xff;
245 data[j++] = bits >> 10;
248 data[j++] = bits >> 16;
249 data[j++] = (bits >> 8) & 0xff;
259 /* Parse userfqdn string which is in user@fqdn format. */
261 bool silc_parse_userfqdn(const char *string, char **left, char **right)
268 if (string[0] == '@') {
270 *left = strdup(string);
274 if (strchr(string, '@')) {
275 tlen = strcspn(string, "@");
278 *left = silc_calloc(tlen + 1, sizeof(char));
279 memcpy(*left, string, tlen);
283 *right = silc_calloc((strlen(string) - tlen) + 1, sizeof(char));
284 memcpy(*right, string + tlen + 1, strlen(string) - tlen - 1);
288 *left = strdup(string);
294 /* Parses command line. At most `max_args' is taken. Rest of the line
295 will be allocated as the last argument if there are more than `max_args'
296 arguments in the line. Note that the command name is counted as one
297 argument and is saved. */
299 void silc_parse_command_line(unsigned char *buffer,
300 unsigned char ***parsed,
301 SilcUInt32 **parsed_lens,
302 SilcUInt32 **parsed_types,
303 SilcUInt32 *parsed_num,
308 const char *cp = buffer;
311 *parsed = silc_calloc(1, sizeof(**parsed));
312 *parsed_lens = silc_calloc(1, sizeof(**parsed_lens));
314 /* Get the command first */
315 len = strcspn(cp, " ");
316 tmp = silc_to_upper((char *)cp);
317 (*parsed)[0] = silc_calloc(len + 1, sizeof(char));
318 memcpy((*parsed)[0], tmp, len);
320 (*parsed_lens)[0] = len;
326 /* Parse arguments */
327 if (strchr(cp, ' ') || strlen(cp) != 0) {
328 for (i = 1; i < max_args; i++) {
330 if (i != max_args - 1)
331 len = strcspn(cp, " ");
334 while (len && cp[len - 1] == ' ')
339 *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1));
340 *parsed_lens = silc_realloc(*parsed_lens,
341 sizeof(**parsed_lens) * (argc + 1));
342 (*parsed)[argc] = silc_calloc(len + 1, sizeof(char));
343 memcpy((*parsed)[argc], cp, len);
344 (*parsed_lens)[argc] = len;
356 /* Save argument types. Protocol defines all argument types but
357 this implementation makes sure that they are always in correct
358 order hence this simple code. */
359 *parsed_types = silc_calloc(argc, sizeof(**parsed_types));
360 for (i = 0; i < argc; i++)
361 (*parsed_types)[i] = i;
366 /* Formats arguments to a string and returns it after allocating memory
367 for it. It must be remembered to free it later. */
369 char *silc_format(char *fmt, ...)
372 static char buf[8192];
374 memset(buf, 0, sizeof(buf));
376 vsnprintf(buf, sizeof(buf) - 1, fmt, args);
382 /* Renders ID to suitable to print for example to log file. */
384 static char rid[256];
385 #define _PUT_STRING(__d__, __s__) \
387 int __sp = sizeof(__d__) - 1 - strlen(__d__); \
388 if (__sp < strlen(__s__)) { \
390 strncat(__d__, __s__, (sizeof(__d__) - 1) - strlen(__d__)); \
392 strncat(__d__, __s__, strlen(__s__)); \
396 char *silc_id_render(void *id, SilcUInt16 type)
399 unsigned char tmps[2];
402 memset(rid, 0, sizeof(rid));
406 SilcServerID *server_id = (SilcServerID *)id;
407 if (server_id->ip.data_len > 4) {
409 struct sockaddr_in6 ipv6;
410 memset(&ipv6, 0, sizeof(ipv6));
411 ipv6.sin6_family = AF_INET6;
412 memmove(&ipv6.sin6_addr, server_id->ip.data, sizeof(ipv6.sin6_addr));
413 if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
414 tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
415 _PUT_STRING(rid, tmp);
419 memmove(&ipv4.s_addr, server_id->ip.data, 4);
420 cp = inet_ntoa(ipv4);
422 _PUT_STRING(rid, cp);
425 memset(tmp, 0, sizeof(tmp));
426 snprintf(tmp, sizeof(tmp) - 1, ",%d,", ntohs(server_id->port));
427 _PUT_STRING(rid, tmp);
428 SILC_PUT16_MSB(server_id->rnd, tmps);
429 memset(tmp, 0, sizeof(tmp));
430 snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x]", tmps[0], tmps[1]);
431 _PUT_STRING(rid, tmp);
436 SilcClientID *client_id = (SilcClientID *)id;
437 if (client_id->ip.data_len > 4) {
439 struct sockaddr_in6 ipv6;
440 memset(&ipv6, 0, sizeof(ipv6));
441 ipv6.sin6_family = AF_INET6;
442 memmove(&ipv6.sin6_addr, client_id->ip.data, sizeof(ipv6.sin6_addr));
443 if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
444 tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
445 _PUT_STRING(rid, tmp);
449 memmove(&ipv4.s_addr, client_id->ip.data, 4);
450 cp = inet_ntoa(ipv4);
452 _PUT_STRING(rid, cp);
455 memset(tmp, 0, sizeof(tmp));
456 snprintf(tmp, sizeof(tmp) - 1, ",%02x,", client_id->rnd);
457 _PUT_STRING(rid, tmp);
458 memset(tmp, 0, sizeof(tmp));
459 snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x %02x %02x...]",
460 client_id->hash[0], client_id->hash[1],
461 client_id->hash[2], client_id->hash[3]);
462 _PUT_STRING(rid, tmp);
465 case SILC_ID_CHANNEL:
467 SilcChannelID *channel_id = (SilcChannelID *)id;
468 if (channel_id->ip.data_len > 4) {
470 struct sockaddr_in6 ipv6;
471 memset(&ipv6, 0, sizeof(ipv6));
472 ipv6.sin6_family = AF_INET6;
473 memmove(&ipv6.sin6_addr, channel_id->ip.data, sizeof(ipv6.sin6_addr));
474 if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
475 tmp, sizeof(tmp) - 1, NULL, 0, NI_NUMERICHOST))
476 _PUT_STRING(rid, tmp);
480 memmove(&ipv4.s_addr, channel_id->ip.data, 4);
481 cp = inet_ntoa(ipv4);
483 _PUT_STRING(rid, cp);
486 memset(tmp, 0, sizeof(tmp));
487 snprintf(tmp, sizeof(tmp) - 1, ",%d,", ntohs(channel_id->port));
488 _PUT_STRING(rid, tmp);
489 SILC_PUT16_MSB(channel_id->rnd, tmps);
490 memset(tmp, 0, sizeof(tmp));
491 snprintf(tmp, sizeof(tmp) - 1, "[%02x %02x]", tmps[0], tmps[1]);
492 _PUT_STRING(rid, tmp);
501 /* Compares two strings. Strings may include wildcards '*' and '?'.
502 Returns TRUE if strings match. */
504 int silc_string_compare(char *string1, char *string2)
509 char *tmpstr1, *tmpstr2;
511 if (!string1 || !string2)
514 slen1 = strlen(string1);
515 slen2 = strlen(string2);
517 /* See if they are same already */
518 if (!strncmp(string1, string2, strlen(string2)))
522 if (!strchr(string1, '*'))
525 /* Take copies of the original strings as we will change them */
526 tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
527 memcpy(tmpstr1, string1, slen1);
528 tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
529 memcpy(tmpstr2, string2, slen2);
531 for (i = 0; i < slen1; i++) {
533 /* * wildcard. Only one * wildcard is possible. */
534 if (tmpstr1[i] == '*')
535 if (!strncmp(tmpstr1, tmpstr2, i)) {
536 memset(tmpstr2, 0, slen2);
537 strncpy(tmpstr2, tmpstr1, i);
542 if (tmpstr1[i] == '?') {
543 if (!strncmp(tmpstr1, tmpstr2, i)) {
544 if (!(slen1 < i + 1))
545 if (tmpstr1[i + 1] != '?' &&
546 tmpstr1[i + 1] != tmpstr2[i + 1])
549 if (!(slen1 < slen2))
555 /* if using *, remove it */
556 if (strchr(tmpstr1, '*'))
557 *strchr(tmpstr1, '*') = 0;
559 if (!strcmp(tmpstr1, tmpstr2)) {
560 memset(tmpstr1, 0, slen1);
561 memset(tmpstr2, 0, slen2);
567 memset(tmpstr1, 0, slen1);
568 memset(tmpstr2, 0, slen2);
574 /* Basic has function to hash strings. May be used with the SilcHashTable.
575 Note that this lowers the characters of the string (with tolower()) so
576 this is used usually with nicknames, channel and server names to provide
577 case insensitive keys. */
579 SilcUInt32 silc_hash_string(void *key, void *user_context)
581 char *s = (char *)key;
585 h = (h << 4) + tolower(*s);
586 if ((g = h & 0xf0000000)) {
596 /* Basic hash function to hash integers. May be used with the SilcHashTable. */
598 SilcUInt32 silc_hash_uint(void *key, void *user_context)
600 return *(SilcUInt32 *)key;
603 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
605 SilcUInt32 silc_hash_ptr(void *key, void *user_context)
607 return (SilcUInt32)key;
610 /* Hash a ID. The `user_context' is the ID type. */
612 SilcUInt32 silc_hash_id(void *key, void *user_context)
614 SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
621 SilcClientID *id = (SilcClientID *)key;
624 /* The client ID is hashed by hashing the hash of the ID
625 (which is a truncated MD5 hash of the nickname) so that we
626 can access the entry from the cache with both Client ID but
627 with just a hash from the ID as well. */
629 for (i = 0; i < sizeof(id->hash); i++) {
630 h = (h << 4) + id->hash[i];
631 if ((g = h & 0xf0000000)) {
642 SilcServerID *id = (SilcServerID *)key;
644 h = id->port * id->rnd;
645 for (i = 0; i < id->ip.data_len; i++)
651 case SILC_ID_CHANNEL:
653 SilcChannelID *id = (SilcChannelID *)key;
655 h = id->port * id->rnd;
656 for (i = 0; i < id->ip.data_len; i++)
669 /* Hash binary data. The `user_context' is the data length. */
671 SilcUInt32 silc_hash_data(void *key, void *user_context)
673 SilcUInt32 len = (SilcUInt32)user_context, h = 0;
674 unsigned char *data = (unsigned char *)key;
677 h = (data[0] * data[len - 1] + 1) * len;
678 for (i = 0; i < len; i++)
684 /* Hashed SILC Public key. */
686 SilcUInt32 silc_hash_public_key(void *key, void *user_context)
688 SilcPublicKey pk = (SilcPublicKey)key;
689 return (pk->len + silc_hash_string(pk->name, NULL) +
690 silc_hash_string(pk->identifier, NULL) +
691 silc_hash_data(pk->pk, (void *)pk->pk_len));
694 /* Compares two strings. It may be used as SilcHashTable comparison
697 bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
699 return !strcasecmp((char *)key1, (char *)key2);
702 /* Compares two ID's. May be used as SilcHashTable comparison function.
703 The Client ID's compares only the hash of the Client ID not any other
704 part of the Client ID. Other ID's are fully compared. */
706 bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
708 SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
709 return (id_type == SILC_ID_CLIENT ?
710 SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
711 SILC_ID_COMPARE_TYPE(key1, key2, id_type));
714 /* Compare two Client ID's entirely and not just the hash from the ID. */
716 bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
718 return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
721 /* Compares binary data. May be used as SilcHashTable comparison function. */
723 bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
725 SilcUInt32 len = (SilcUInt32)user_context;
726 return !memcmp(key1, key2, len);
729 /* Compares two SILC Public keys. It may be used as SilcHashTable
730 comparison function. */
732 bool silc_hash_public_key_compare(void *key1, void *key2, void *user_context)
734 return silc_pkcs_public_key_compare(key1, key2);
737 /* Parses mode mask and returns the mode as string. */
739 char *silc_client_chmode(SilcUInt32 mode, const char *cipher, const char *hmac)
746 memset(string, 0, sizeof(string));
748 if (mode & SILC_CHANNEL_MODE_PRIVATE)
749 strncat(string, "p", 1);
751 if (mode & SILC_CHANNEL_MODE_SECRET)
752 strncat(string, "s", 1);
754 if (mode & SILC_CHANNEL_MODE_PRIVKEY)
755 strncat(string, "k", 1);
757 if (mode & SILC_CHANNEL_MODE_INVITE)
758 strncat(string, "i", 1);
760 if (mode & SILC_CHANNEL_MODE_TOPIC)
761 strncat(string, "t", 1);
763 if (mode & SILC_CHANNEL_MODE_ULIMIT)
764 strncat(string, "l", 1);
766 if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
767 strncat(string, "a", 1);
769 if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
770 strncat(string, "f", 1);
772 if (mode & SILC_CHANNEL_MODE_SILENCE_USERS)
773 strncat(string, "m", 1);
775 if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS)
776 strncat(string, "M", 1);
778 if (mode & SILC_CHANNEL_MODE_CIPHER)
779 strncat(string, cipher, strlen(cipher));
781 if (mode & SILC_CHANNEL_MODE_HMAC)
782 strncat(string, hmac, strlen(hmac));
784 /* Rest of mode is ignored */
786 return strdup(string);
789 /* Parses channel user mode mask and returns te mode as string */
791 char *silc_client_chumode(SilcUInt32 mode)
798 memset(string, 0, sizeof(string));
800 if (mode & SILC_CHANNEL_UMODE_CHANFO)
801 strncat(string, "f", 1);
803 if (mode & SILC_CHANNEL_UMODE_CHANOP)
804 strncat(string, "o", 1);
806 return strdup(string);
809 /* Parses channel user mode and returns it as special mode character. */
811 char *silc_client_chumode_char(SilcUInt32 mode)
818 memset(string, 0, sizeof(string));
820 if (mode & SILC_CHANNEL_UMODE_CHANFO)
821 strncat(string, "*", 1);
823 if (mode & SILC_CHANNEL_UMODE_CHANOP)
824 strncat(string, "@", 1);
826 return strdup(string);
829 /* Creates fingerprint from data, usually used with SHA1 digests */
831 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
833 char fingerprint[64], *cp;
836 memset(fingerprint, 0, sizeof(fingerprint));
838 for (i = 0; i < data_len; i++) {
839 snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
842 if ((i + 1) % 2 == 0)
843 snprintf(cp++, sizeof(fingerprint), " ");
845 if ((i + 1) % 10 == 0)
846 snprintf(cp++, sizeof(fingerprint), " ");
849 if ((i + 1) % 2 == 0)
851 if ((i + 1) % 10 == 0)
854 return strdup(fingerprint);
857 /* Return TRUE if the `data' is ASCII string. */
859 bool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
863 for (i = 0; i < data_len; i++) {
864 if (!isascii(data[i]))
871 /* Parses SILC protocol style version string. */
873 bool silc_parse_version_string(const char *version,
874 SilcUInt32 *protocol_version,
875 char **protocol_version_string,
876 SilcUInt32 *software_version,
877 char **software_version_string,
878 char **vendor_version)
881 int maj = 0, min = 0;
883 if (!strstr(version, "SILC-"))
886 cp = (char *)version + 5;
890 /* Take protocol version */
893 cp = strchr(cp, '.');
899 memset(buf, 0, sizeof(buf));
900 snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
901 if (protocol_version)
902 *protocol_version = atoi(buf);
903 memset(buf, 0, sizeof(buf));
904 snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
905 if (protocol_version_string)
906 *protocol_version_string = strdup(buf);
908 /* Take software version */
912 cp = strchr(cp, '-');
917 cp = strchr(cp, '.');
921 memset(buf, 0, sizeof(buf));
922 snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
923 if (software_version)
924 *software_version = atoi(buf);
925 memset(buf, 0, sizeof(buf));
926 snprintf(buf, sizeof(buf) - 1, "%d.%d", maj, min);
927 if (software_version_string)
928 *software_version_string = strdup(buf);
930 /* Take vendor string */
934 cp = strchr(cp, '.');
935 if (cp && cp + 1 && vendor_version)
936 *vendor_version = strdup(cp + 1);
942 /* Converts version string x.x into number representation. */
944 SilcUInt32 silc_version_to_num(const char *version)
946 int maj = 0, min = 0;
952 cp = (char *)version;
954 cp = strchr(cp, '.');
958 memset(buf, 0, sizeof(buf));
959 snprintf(buf, sizeof(buf) - 1, "%d%d", maj, min);
960 return (SilcUInt32)atoi(buf);
963 /* Displays input prompt on command line and takes input data from user */
965 char *silc_get_input(const char *prompt, bool echo_off)
970 #ifdef HAVE_TERMIOS_H
974 struct termios to_old;
976 fd = open("/dev/tty", O_RDONLY);
978 fprintf(stderr, "silc: %s\n", strerror(errno));
982 signal(SIGINT, SIG_IGN);
984 /* Get terminal info */
989 to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
990 tcsetattr(fd, TCSANOW, &to);
992 memset(input, 0, sizeof(input));
994 printf("%s", prompt);
997 if ((read(fd, input, sizeof(input))) < 0) {
998 fprintf(stderr, "silc: %s\n", strerror(errno));
1002 if (strlen(input) <= 1) {
1003 tcsetattr(fd, TCSANOW, &to_old);
1007 if (strchr(input, '\n'))
1008 *strchr(input, '\n') = '\0';
1010 /* Restore old terminfo */
1011 tcsetattr(fd, TCSANOW, &to_old);
1012 signal(SIGINT, SIG_DFL);
1014 ret = silc_calloc(strlen(input), sizeof(char));
1015 memcpy(ret, input, strlen(input));
1016 memset(input, 0, sizeof(input));
1017 #endif /* HAVE_TERMIOS_H */
1023 fd = open("/dev/tty", O_RDONLY);
1025 fprintf(stderr, "silc: %s\n", strerror(errno));
1029 memset(input, 0, sizeof(input));
1031 printf("%s", prompt);
1034 if ((read(fd, input, sizeof(input))) < 0) {
1035 fprintf(stderr, "silc: %s\n", strerror(errno));
1039 if (strlen(input) <= 1)
1042 if (strchr(input, '\n'))
1043 *strchr(input, '\n') = '\0';
1045 return strdup(input);
1049 #endif /* SILC_UNIX */