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];
386 char *silc_id_render(void *id, SilcUInt16 type)
389 unsigned char tmps[2];
391 memset(rid, 0, sizeof(rid));
395 SilcServerID *server_id = (SilcServerID *)id;
396 if (server_id->ip.data_len > 4) {
398 struct sockaddr_in6 ipv6;
399 memset(&ipv6, 0, sizeof(ipv6));
400 ipv6.sin6_family = AF_INET6;
401 memmove(&ipv6.sin6_addr, server_id->ip.data, sizeof(ipv6.sin6_addr));
402 if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
403 tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST))
408 memmove(&ipv4.s_addr, server_id->ip.data, 4);
409 strcat(rid, inet_ntoa(ipv4));
412 memset(tmp, 0, sizeof(tmp));
413 snprintf(tmp, sizeof(tmp), ",%d,", ntohs(server_id->port));
415 SILC_PUT16_MSB(server_id->rnd, tmps);
416 memset(tmp, 0, sizeof(tmp));
417 snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
423 SilcClientID *client_id = (SilcClientID *)id;
424 if (client_id->ip.data_len > 4) {
426 struct sockaddr_in6 ipv6;
427 memset(&ipv6, 0, sizeof(ipv6));
428 ipv6.sin6_family = AF_INET6;
429 memmove(&ipv6.sin6_addr, client_id->ip.data, sizeof(ipv6.sin6_addr));
430 if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
431 tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST))
436 memmove(&ipv4.s_addr, client_id->ip.data, 4);
437 strcat(rid, inet_ntoa(ipv4));
440 memset(tmp, 0, sizeof(tmp));
441 snprintf(tmp, sizeof(tmp), ",%02x,", client_id->rnd);
443 memset(tmp, 0, sizeof(tmp));
444 snprintf(tmp, sizeof(tmp), "[%02x %02x %02x %02x...]",
445 client_id->hash[0], client_id->hash[1],
446 client_id->hash[2], client_id->hash[3]);
450 case SILC_ID_CHANNEL:
452 SilcChannelID *channel_id = (SilcChannelID *)id;
453 if (channel_id->ip.data_len > 4) {
455 struct sockaddr_in6 ipv6;
456 memset(&ipv6, 0, sizeof(ipv6));
457 ipv6.sin6_family = AF_INET6;
458 memmove(&ipv6.sin6_addr, channel_id->ip.data, sizeof(ipv6.sin6_addr));
459 if (!getnameinfo((struct sockaddr *)&ipv6, sizeof(ipv6),
460 tmp, sizeof(tmp), NULL, 0, NI_NUMERICHOST))
465 memmove(&ipv4.s_addr, channel_id->ip.data, 4);
466 strcat(rid, inet_ntoa(ipv4));
469 memset(tmp, 0, sizeof(tmp));
470 snprintf(tmp, sizeof(tmp), ",%d,", ntohs(channel_id->port));
472 SILC_PUT16_MSB(channel_id->rnd, tmps);
473 memset(tmp, 0, sizeof(tmp));
474 snprintf(tmp, sizeof(tmp), "[%02x %02x]", tmps[0], tmps[1]);
483 /* Compares two strings. Strings may include wildcards * and ?.
484 Returns TRUE if strings match. */
486 int silc_string_compare(char *string1, char *string2)
491 char *tmpstr1, *tmpstr2;
493 if (!string1 || !string2)
496 slen1 = strlen(string1);
497 slen2 = strlen(string2);
499 /* See if they are same already */
500 if (!strncmp(string1, string2, strlen(string2)))
504 if (!strchr(string1, '*'))
507 /* Take copies of the original strings as we will change them */
508 tmpstr1 = silc_calloc(slen1 + 1, sizeof(char));
509 memcpy(tmpstr1, string1, slen1);
510 tmpstr2 = silc_calloc(slen2 + 1, sizeof(char));
511 memcpy(tmpstr2, string2, slen2);
513 for (i = 0; i < slen1; i++) {
515 /* * wildcard. Only one * wildcard is possible. */
516 if (tmpstr1[i] == '*')
517 if (!strncmp(tmpstr1, tmpstr2, i)) {
518 memset(tmpstr2, 0, slen2);
519 strncpy(tmpstr2, tmpstr1, i);
524 if (tmpstr1[i] == '?') {
525 if (!strncmp(tmpstr1, tmpstr2, i)) {
526 if (!(slen1 < i + 1))
527 if (tmpstr1[i + 1] != '?' &&
528 tmpstr1[i + 1] != tmpstr2[i + 1])
531 if (!(slen1 < slen2))
537 /* if using *, remove it */
538 if (strchr(tmpstr1, '*'))
539 *strchr(tmpstr1, '*') = 0;
541 if (!strcmp(tmpstr1, tmpstr2)) {
542 memset(tmpstr1, 0, slen1);
543 memset(tmpstr2, 0, slen2);
549 memset(tmpstr1, 0, slen1);
550 memset(tmpstr2, 0, slen2);
556 /* Basic has function to hash strings. May be used with the SilcHashTable.
557 Note that this lowers the characters of the string (with tolower()) so
558 this is used usually with nicknames, channel and server names to provide
559 case insensitive keys. */
561 SilcUInt32 silc_hash_string(void *key, void *user_context)
563 char *s = (char *)key;
567 h = (h << 4) + tolower(*s);
568 if ((g = h & 0xf0000000)) {
578 /* Basic hash function to hash integers. May be used with the SilcHashTable. */
580 SilcUInt32 silc_hash_uint(void *key, void *user_context)
582 return *(SilcUInt32 *)key;
585 /* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */
587 SilcUInt32 silc_hash_ptr(void *key, void *user_context)
589 return (SilcUInt32)key;
592 /* Hash a ID. The `user_context' is the ID type. */
594 SilcUInt32 silc_hash_id(void *key, void *user_context)
596 SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
603 SilcClientID *id = (SilcClientID *)key;
606 /* The client ID is hashed by hashing the hash of the ID
607 (which is a truncated MD5 hash of the nickname) so that we
608 can access the entry from the cache with both Client ID but
609 with just a hash from the ID as well. */
611 for (i = 0; i < sizeof(id->hash); i++) {
612 h = (h << 4) + id->hash[i];
613 if ((g = h & 0xf0000000)) {
624 SilcServerID *id = (SilcServerID *)key;
626 h = id->port * id->rnd;
627 for (i = 0; i < id->ip.data_len; i++)
633 case SILC_ID_CHANNEL:
635 SilcChannelID *id = (SilcChannelID *)key;
637 h = id->port * id->rnd;
638 for (i = 0; i < id->ip.data_len; i++)
651 /* Hash binary data. The `user_context' is the data length. */
653 SilcUInt32 silc_hash_data(void *key, void *user_context)
655 SilcUInt32 len = (SilcUInt32)user_context, h = 0;
656 unsigned char *data = (unsigned char *)key;
659 h = (data[0] * data[len - 1] + 1) * len;
660 for (i = 0; i < len; i++)
666 /* Hashed SILC Public key. */
668 SilcUInt32 silc_hash_public_key(void *key, void *user_context)
670 SilcPublicKey pk = (SilcPublicKey)key;
671 return (pk->len + silc_hash_string(pk->name, NULL) +
672 silc_hash_string(pk->identifier, NULL) +
673 silc_hash_data(pk->pk, (void *)pk->pk_len));
676 /* Compares two strings. May be used as SilcHashTable comparison function. */
678 bool silc_hash_string_compare(void *key1, void *key2, void *user_context)
680 return !strcasecmp((char *)key1, (char *)key2);
683 /* Compares two ID's. May be used as SilcHashTable comparison function.
684 The Client ID's compares only the hash of the Client ID not any other
685 part of the Client ID. Other ID's are fully compared. */
687 bool silc_hash_id_compare(void *key1, void *key2, void *user_context)
689 SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context;
690 return (id_type == SILC_ID_CLIENT ?
691 SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
692 SILC_ID_COMPARE_TYPE(key1, key2, id_type));
695 /* Compare two Client ID's entirely and not just the hash from the ID. */
697 bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context)
699 return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
702 /* Compares binary data. May be used as SilcHashTable comparison function. */
704 bool silc_hash_data_compare(void *key1, void *key2, void *user_context)
706 SilcUInt32 len = (SilcUInt32)user_context;
707 return !memcmp(key1, key2, len);
710 /* Compares two SILC Public keys. May be used as SilcHashTable comparison
713 bool silc_hash_public_key_compare(void *key1, void *key2, void *user_context)
715 return silc_pkcs_public_key_compare(key1, key2);
718 /* Parses mode mask and returns the mode as string. */
720 char *silc_client_chmode(SilcUInt32 mode, const char *cipher, const char *hmac)
727 memset(string, 0, sizeof(string));
729 if (mode & SILC_CHANNEL_MODE_PRIVATE)
730 strncat(string, "p", 1);
732 if (mode & SILC_CHANNEL_MODE_SECRET)
733 strncat(string, "s", 1);
735 if (mode & SILC_CHANNEL_MODE_PRIVKEY)
736 strncat(string, "k", 1);
738 if (mode & SILC_CHANNEL_MODE_INVITE)
739 strncat(string, "i", 1);
741 if (mode & SILC_CHANNEL_MODE_TOPIC)
742 strncat(string, "t", 1);
744 if (mode & SILC_CHANNEL_MODE_ULIMIT)
745 strncat(string, "l", 1);
747 if (mode & SILC_CHANNEL_MODE_PASSPHRASE)
748 strncat(string, "a", 1);
750 if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)
751 strncat(string, "f", 1);
753 if (mode & SILC_CHANNEL_MODE_CIPHER)
754 strncat(string, cipher, strlen(cipher));
756 if (mode & SILC_CHANNEL_MODE_HMAC)
757 strncat(string, hmac, strlen(hmac));
759 /* Rest of mode is ignored */
761 return strdup(string);
764 /* Parses channel user mode mask and returns te mode as string */
766 char *silc_client_chumode(SilcUInt32 mode)
773 memset(string, 0, sizeof(string));
775 if (mode & SILC_CHANNEL_UMODE_CHANFO)
776 strncat(string, "f", 1);
778 if (mode & SILC_CHANNEL_UMODE_CHANOP)
779 strncat(string, "o", 1);
781 return strdup(string);
784 /* Parses channel user mode and returns it as special mode character. */
786 char *silc_client_chumode_char(SilcUInt32 mode)
793 memset(string, 0, sizeof(string));
795 if (mode & SILC_CHANNEL_UMODE_CHANFO)
796 strncat(string, "*", 1);
798 if (mode & SILC_CHANNEL_UMODE_CHANOP)
799 strncat(string, "@", 1);
801 return strdup(string);
804 /* Creates fingerprint from data, usually used with SHA1 digests */
806 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
808 char fingerprint[64], *cp;
811 memset(fingerprint, 0, sizeof(fingerprint));
813 for (i = 0; i < data_len; i++) {
814 snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
817 if ((i + 1) % 2 == 0)
818 snprintf(cp++, sizeof(fingerprint), " ");
820 if ((i + 1) % 10 == 0)
821 snprintf(cp++, sizeof(fingerprint), " ");
824 if ((i + 1) % 2 == 0)
826 if ((i + 1) % 10 == 0)
829 return strdup(fingerprint);