X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcutil%2Fsilcutil.c;h=c3af592539a49d72b430f572c80cea7b40e4b307;hb=a818c5b5411bbc4436d1c5f011236985c96bb787;hp=ea3733de119edf221bf08c2d9acbe93bbb44aaa3;hpb=489c2a0760de411cf0efb99befc412fe587de9d1;p=silc.git diff --git a/lib/silcutil/silcutil.c b/lib/silcutil/silcutil.c index ea3733de..c3af5925 100644 --- a/lib/silcutil/silcutil.c +++ b/lib/silcutil/silcutil.c @@ -1,16 +1,15 @@ /* - silcutil.c + silcutil.c - Author: Pekka Riikonen + Author: Pekka Riikonen - Copyright (C) 1997 - 2000 Pekka Riikonen + Copyright (C) 1997 - 2002 Pekka Riikonen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - + the Free Software Foundation; version 2 of the License. + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -25,95 +24,6 @@ #include "silcincludes.h" -/* Reads a file to a buffer. The allocated buffer is returned. Length of - the file read is returned to the return_len argument. */ - -char *silc_file_read(const char *filename, int *return_len) -{ - int fd; - char *buffer; - int filelen; - - fd = open(filename, O_RDONLY); - if (fd < 0) { - SILC_LOG_ERROR(("Cannot open file %s: %s", filename, strerror(errno))); - return NULL; - } - - filelen = lseek(fd, (off_t)0L, SEEK_END); - if (filelen < 0) - return NULL; - if (lseek(fd, (off_t)0L, SEEK_SET) < 0) - return NULL; - - if (filelen < 0) { - SILC_LOG_ERROR(("Cannot open file %s: %s", filename, strerror(errno))); - return NULL; - } - - buffer = silc_calloc(filelen + 1, sizeof(char)); - - if ((read(fd, buffer, filelen)) == -1) { - memset(buffer, 0, sizeof(buffer)); - close(fd); - SILC_LOG_ERROR(("Cannot read from file %s: %s", filename, - strerror(errno))); - return NULL; - } - - close(fd); - buffer[filelen] = EOF; - - if (return_len) - *return_len = filelen; - - return buffer; -} - -/* Writes a buffer to the file. */ - -int silc_file_write(const char *filename, const char *buffer, int len) -{ - int fd; - - if ((fd = creat(filename, 0644)) == -1) { - SILC_LOG_ERROR(("Cannot open file %s for writing: %s", strerror(errno))); - return -1; - } - - if ((write(fd, buffer, len)) == -1) { - SILC_LOG_ERROR(("Cannot write to file %s: %s", strerror(errno))); - return -1; - } - - close(fd); - - return 0; -} - -/* Writes a buffer to the file. If the file is created specific mode is - set to the file. */ - -int silc_file_write_mode(const char *filename, const char *buffer, - int len, int mode) -{ - int fd; - - if ((fd = creat(filename, mode)) == -1) { - SILC_LOG_ERROR(("Cannot open file %s for writing: %s", strerror(errno))); - return -1; - } - - if ((write(fd, buffer, len)) == -1) { - SILC_LOG_ERROR(("Cannot write to file %s: %s", strerror(errno))); - return -1; - } - - close(fd); - - return 0; -} - /* Gets line from a buffer. Stops reading when a newline or EOF occurs. This doesn't remove the newline sign from the destination buffer. The argument begin is returned and should be passed again for the function. */ @@ -195,92 +105,17 @@ char *silc_to_upper(char *string) return ret; } -/* Compares two strings. Strings may include wildcards * and ?. - Returns TRUE if strings match. */ - -int silc_string_compare(char *string1, char *string2) -{ - int i; - int slen1 = strlen(string1); - int slen2 = strlen(string2); - char *tmpstr1, *tmpstr2; - - if (!string1 || !string2) - return FALSE; - - /* See if they are same already */ - if (!strncmp(string1, string2, strlen(string2))) - return TRUE; - - if (slen2 < slen1) - if (!strchr(string1, '*')) - return FALSE; - - /* Take copies of the original strings as we will change them */ - tmpstr1 = silc_calloc(slen1 + 1, sizeof(char)); - memcpy(tmpstr1, string1, slen1); - tmpstr2 = silc_calloc(slen2 + 1, sizeof(char)); - memcpy(tmpstr2, string2, slen2); - - for (i = 0; i < slen2; i++) { - - /* * wildcard. Only one * wildcard is possible. */ - if (tmpstr1[i] == '*') - if (!strncmp(tmpstr1, tmpstr2, i)) { - memset(tmpstr2, 0, slen2); - strncpy(tmpstr2, tmpstr1, i); - break; - } - - /* ? wildcard */ - if (tmpstr1[i] == '?') { - if (!strncmp(tmpstr1, tmpstr2, i)) { - if (!(slen1 < i + 1)) - if (tmpstr1[i + 1] != '?' && - tmpstr1[i + 1] != tmpstr2[i + 1]) - continue; - - if (!(slen1 < slen2)) - tmpstr2[i] = '?'; - } -#if 0 - } else { - if (strncmp(tmpstr1, tmpstr2, i)) - strncpy(tmpstr2, string2, slen2); -#endif - } - } - - /* if using *, remove it */ - if (strchr(tmpstr1, '*')) - *strchr(tmpstr1, '*') = 0; - - if (!strcmp(tmpstr1, tmpstr2)) { - memset(tmpstr1, 0, slen1); - memset(tmpstr2, 0, slen2); - silc_free(tmpstr1); - silc_free(tmpstr2); - return TRUE; - } - - memset(tmpstr1, 0, slen1); - memset(tmpstr2, 0, slen2); - silc_free(tmpstr1); - silc_free(tmpstr2); - return FALSE; -} - -unsigned char pem_enc[64] = +static unsigned char pem_enc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /* Encodes data into PEM encoding. Returns NULL terminated PEM encoded data string. Note: This is originally public domain code and is still PD. */ -char *silc_encode_pem(unsigned char *data, unsigned int len) +char *silc_encode_pem(unsigned char *data, SilcUInt32 len) { int i, j; - unsigned int bits, c, char_count; + SilcUInt32 bits, c, char_count; char *pem; char_count = 0; @@ -325,10 +160,10 @@ char *silc_encode_pem(unsigned char *data, unsigned int len) /* Same as above but puts newline ('\n') every 72 characters. */ -char *silc_encode_pem_file(unsigned char *data, unsigned int data_len) +char *silc_encode_pem_file(unsigned char *data, SilcUInt32 data_len) { int i, j; - unsigned int len, cols; + SilcUInt32 len, cols; char *pem, *pem2; pem = silc_encode_pem(data, data_len); @@ -347,17 +182,18 @@ char *silc_encode_pem_file(unsigned char *data, unsigned int data_len) pem2[i] = pem[j++]; } + silc_free(pem); return pem2; } /* Decodes PEM into data. Returns the decoded data. Note: This is originally public domain code and is still PD. */ -unsigned char *silc_decode_pem(unsigned char *pem, unsigned int pem_len, - unsigned int *ret_len) +unsigned char *silc_decode_pem(unsigned char *pem, SilcUInt32 pem_len, + SilcUInt32 *ret_len) { int i, j; - unsigned int len, c, char_count, bits; + SilcUInt32 len, c, char_count, bits; unsigned char *data; static char ialpha[256], decoder[256]; @@ -420,49 +256,36 @@ unsigned char *silc_decode_pem(unsigned char *pem, unsigned int pem_len, return data; } -/* Parse nickname string. The format may be !@ to - support multiple same nicknames. The is the final unifier if same - nickname is on same server. Note, this is only local format and server - does not know anything about these. */ +/* Parse userfqdn string which is in user@fqdn format */ -int silc_parse_nickname(char *string, char **nickname, char **server, - unsigned int *num) +bool silc_parse_userfqdn(const char *string, char **left, char **right) { - unsigned int tlen; - char tmp[256]; + SilcUInt32 tlen; if (!string) return FALSE; - if (strchr(string, '!')) { - tlen = strcspn(string, "!"); - memset(tmp, 0, sizeof(tmp)); - memcpy(tmp, string, tlen); - - if (num) - *num = atoi(tmp); - - if (tlen >= strlen(string)) - return FALSE; - - string += tlen + 1; + if (string[0] == '@') { + if (left) + *left = strdup(string); + return TRUE; } if (strchr(string, '@')) { tlen = strcspn(string, "@"); - if (nickname) { - *nickname = silc_calloc(tlen + 1, sizeof(char)); - memcpy(*nickname, string, tlen); + if (left) { + *left = silc_calloc(tlen + 1, sizeof(char)); + memcpy(*left, string, tlen); } - if (server) { - *server = silc_calloc(strlen(string) - tlen, sizeof(char)); - memcpy(*server, string + tlen + 1, strlen(string) - tlen - 1); + if (right) { + *right = silc_calloc((strlen(string) - tlen) + 1, sizeof(char)); + memcpy(*right, string + tlen + 1, strlen(string) - tlen - 1); } } else { - if (nickname) - *nickname = strdup(string); + if (left) + *left = strdup(string); } return TRUE; @@ -475,23 +298,29 @@ int silc_parse_nickname(char *string, char **nickname, char **server, void silc_parse_command_line(unsigned char *buffer, unsigned char ***parsed, - unsigned int **parsed_lens, - unsigned int **parsed_types, - unsigned int *parsed_num, - unsigned int max_args) + SilcUInt32 **parsed_lens, + SilcUInt32 **parsed_types, + SilcUInt32 *parsed_num, + SilcUInt32 max_args) { int i, len = 0; int argc = 0; const char *cp = buffer; + char *tmp; *parsed = silc_calloc(1, sizeof(**parsed)); *parsed_lens = silc_calloc(1, sizeof(**parsed_lens)); /* Get the command first */ len = strcspn(cp, " "); - (*parsed)[0] = silc_to_upper((char *)cp); + tmp = silc_to_upper((char *)cp); + (*parsed)[0] = silc_calloc(len + 1, sizeof(char)); + memcpy((*parsed)[0], tmp, len); + silc_free(tmp); (*parsed_lens)[0] = len; - cp += len + 1; + cp += len; + while (*cp == ' ') + cp++; argc++; /* Parse arguments */ @@ -502,6 +331,10 @@ void silc_parse_command_line(unsigned char *buffer, len = strcspn(cp, " "); else len = strlen(cp); + while (len && cp[len - 1] == ' ') + len--; + if (!len) + break; *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1)); *parsed_lens = silc_realloc(*parsed_lens, @@ -515,7 +348,8 @@ void silc_parse_command_line(unsigned char *buffer, if (strlen(cp) == 0) break; else - cp++; + while (*cp == ' ') + cp++; } } @@ -549,7 +383,7 @@ char *silc_format(char *fmt, ...) static char rid[256]; -char *silc_id_render(void *id, unsigned short type) +char *silc_id_render(void *id, SilcUInt16 type) { char tmp[100]; unsigned char tmps[2]; @@ -559,7 +393,19 @@ char *silc_id_render(void *id, unsigned short type) case SILC_ID_SERVER: { SilcServerID *server_id = (SilcServerID *)id; - strcat(rid, inet_ntoa(server_id->ip)); + if (server_id->ip.data_len > 4) { +#ifdef HAVE_IPV6 + struct in6_addr ipv6; + memmove(&ipv6, server_id->ip.data, sizeof(ipv6)); + if (!inet_ntop(AF_INET6, &ipv6, tmp, sizeof(tmp))) + strcat(rid, tmp); +#endif + } else { + struct in_addr ipv4; + memmove(&ipv4.s_addr, server_id->ip.data, 4); + strcat(rid, inet_ntoa(ipv4)); + } + memset(tmp, 0, sizeof(tmp)); snprintf(tmp, sizeof(tmp), ",%d,", ntohs(server_id->port)); strcat(rid, tmp); @@ -572,7 +418,19 @@ char *silc_id_render(void *id, unsigned short type) case SILC_ID_CLIENT: { SilcClientID *client_id = (SilcClientID *)id; - strcat(rid, inet_ntoa(client_id->ip)); + if (client_id->ip.data_len > 4) { +#ifdef HAVE_IPV6 + struct in6_addr ipv6; + memmove(&ipv6, client_id->ip.data, sizeof(ipv6)); + if (!inet_ntop(AF_INET6, &ipv6, tmp, sizeof(tmp))) + strcat(rid, tmp); +#endif + } else { + struct in_addr ipv4; + memmove(&ipv4.s_addr, client_id->ip.data, 4); + strcat(rid, inet_ntoa(ipv4)); + } + memset(tmp, 0, sizeof(tmp)); snprintf(tmp, sizeof(tmp), ",%02x,", client_id->rnd); strcat(rid, tmp); @@ -586,7 +444,19 @@ char *silc_id_render(void *id, unsigned short type) case SILC_ID_CHANNEL: { SilcChannelID *channel_id = (SilcChannelID *)id; - strcat(rid, inet_ntoa(channel_id->ip)); + if (channel_id->ip.data_len > 4) { +#ifdef HAVE_IPV6 + struct in6_addr ipv6; + memmove(&ipv6, channel_id->ip.data, sizeof(ipv6)); + if (!inet_ntop(AF_INET6, &ipv6, tmp, sizeof(tmp))) + strcat(rid, tmp); +#endif + } else { + struct in_addr ipv4; + memmove(&ipv4.s_addr, channel_id->ip.data, 4); + strcat(rid, inet_ntoa(ipv4)); + } + memset(tmp, 0, sizeof(tmp)); snprintf(tmp, sizeof(tmp), ",%d,", ntohs(channel_id->port)); strcat(rid, tmp); @@ -600,3 +470,334 @@ char *silc_id_render(void *id, unsigned short type) return rid; } + +/* Compares two strings. Strings may include wildcards * and ?. + Returns TRUE if strings match. */ + +int silc_string_compare(char *string1, char *string2) +{ + int i; + int slen1; + int slen2; + char *tmpstr1, *tmpstr2; + + if (!string1 || !string2) + return FALSE; + + slen1 = strlen(string1); + slen2 = strlen(string2); + + /* See if they are same already */ + if (!strncmp(string1, string2, strlen(string2))) + return TRUE; + + if (slen2 < slen1) + if (!strchr(string1, '*')) + return FALSE; + + /* Take copies of the original strings as we will change them */ + tmpstr1 = silc_calloc(slen1 + 1, sizeof(char)); + memcpy(tmpstr1, string1, slen1); + tmpstr2 = silc_calloc(slen2 + 1, sizeof(char)); + memcpy(tmpstr2, string2, slen2); + + for (i = 0; i < slen1; i++) { + + /* * wildcard. Only one * wildcard is possible. */ + if (tmpstr1[i] == '*') + if (!strncmp(tmpstr1, tmpstr2, i)) { + memset(tmpstr2, 0, slen2); + strncpy(tmpstr2, tmpstr1, i); + break; + } + + /* ? wildcard */ + if (tmpstr1[i] == '?') { + if (!strncmp(tmpstr1, tmpstr2, i)) { + if (!(slen1 < i + 1)) + if (tmpstr1[i + 1] != '?' && + tmpstr1[i + 1] != tmpstr2[i + 1]) + continue; + + if (!(slen1 < slen2)) + tmpstr2[i] = '?'; + } + } + } + + /* if using *, remove it */ + if (strchr(tmpstr1, '*')) + *strchr(tmpstr1, '*') = 0; + + if (!strcmp(tmpstr1, tmpstr2)) { + memset(tmpstr1, 0, slen1); + memset(tmpstr2, 0, slen2); + silc_free(tmpstr1); + silc_free(tmpstr2); + return TRUE; + } + + memset(tmpstr1, 0, slen1); + memset(tmpstr2, 0, slen2); + silc_free(tmpstr1); + silc_free(tmpstr2); + return FALSE; +} + +/* Basic has function to hash strings. May be used with the SilcHashTable. + Note that this lowers the characters of the string (with tolower()) so + this is used usually with nicknames, channel and server names to provide + case insensitive keys. */ + +SilcUInt32 silc_hash_string(void *key, void *user_context) +{ + char *s = (char *)key; + SilcUInt32 h = 0, g; + + while (*s != '\0') { + h = (h << 4) + tolower(*s); + if ((g = h & 0xf0000000)) { + h = h ^ (g >> 24); + h = h ^ g; + } + s++; + } + + return h; +} + +/* Basic hash function to hash integers. May be used with the SilcHashTable. */ + +SilcUInt32 silc_hash_uint(void *key, void *user_context) +{ + return *(SilcUInt32 *)key; +} + +/* Basic hash funtion to hash pointers. May be used with the SilcHashTable. */ + +SilcUInt32 silc_hash_ptr(void *key, void *user_context) +{ + return (SilcUInt32)key; +} + +/* Hash a ID. The `user_context' is the ID type. */ + +SilcUInt32 silc_hash_id(void *key, void *user_context) +{ + SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context; + SilcUInt32 h = 0; + int i; + + switch (id_type) { + case SILC_ID_CLIENT: + { + SilcClientID *id = (SilcClientID *)key; + SilcUInt32 g; + + /* The client ID is hashed by hashing the hash of the ID + (which is a truncated MD5 hash of the nickname) so that we + can access the entry from the cache with both Client ID but + with just a hash from the ID as well. */ + + for (i = 0; i < sizeof(id->hash); i++) { + h = (h << 4) + id->hash[i]; + if ((g = h & 0xf0000000)) { + h = h ^ (g >> 24); + h = h ^ g; + } + } + + return h; + } + break; + case SILC_ID_SERVER: + { + SilcServerID *id = (SilcServerID *)key; + + h = id->port * id->rnd; + for (i = 0; i < id->ip.data_len; i++) + h ^= id->ip.data[i]; + + return h; + } + break; + case SILC_ID_CHANNEL: + { + SilcChannelID *id = (SilcChannelID *)key; + + h = id->port * id->rnd; + for (i = 0; i < id->ip.data_len; i++) + h ^= id->ip.data[i]; + + return h; + } + break; + default: + break; + } + + return h; +} + +/* Hash binary data. The `user_context' is the data length. */ + +SilcUInt32 silc_hash_data(void *key, void *user_context) +{ + SilcUInt32 len = (SilcUInt32)user_context, h = 0; + unsigned char *data = (unsigned char *)key; + int i; + + h = (data[0] * data[len - 1] + 1) * len; + for (i = 0; i < len; i++) + h ^= data[i]; + + return h; +} + +/* Compares two strings. May be used as SilcHashTable comparison function. */ + +bool silc_hash_string_compare(void *key1, void *key2, void *user_context) +{ + return !strcasecmp((char *)key1, (char *)key2); +} + +/* Compares two ID's. May be used as SilcHashTable comparison function. + The Client ID's compares only the hash of the Client ID not any other + part of the Client ID. Other ID's are fully compared. */ + +bool silc_hash_id_compare(void *key1, void *key2, void *user_context) +{ + SilcIdType id_type = (SilcIdType)(SilcUInt32)user_context; + return (id_type == SILC_ID_CLIENT ? + SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) : + SILC_ID_COMPARE_TYPE(key1, key2, id_type)); +} + +/* Compare two Client ID's entirely and not just the hash from the ID. */ + +bool silc_hash_client_id_compare(void *key1, void *key2, void *user_context) +{ + return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT); +} + +/* Compares binary data. May be used as SilcHashTable comparison function. */ + +bool silc_hash_data_compare(void *key1, void *key2, void *user_context) +{ + SilcUInt32 len = (SilcUInt32)user_context; + return !memcmp(key1, key2, len); +} + +/* Parses mode mask and returns the mode as string. */ + +char *silc_client_chmode(SilcUInt32 mode, const char *cipher, const char *hmac) +{ + char string[100]; + + if (!mode) + return NULL; + + memset(string, 0, sizeof(string)); + + if (mode & SILC_CHANNEL_MODE_PRIVATE) + strncat(string, "p", 1); + + if (mode & SILC_CHANNEL_MODE_SECRET) + strncat(string, "s", 1); + + if (mode & SILC_CHANNEL_MODE_PRIVKEY) + strncat(string, "k", 1); + + if (mode & SILC_CHANNEL_MODE_INVITE) + strncat(string, "i", 1); + + if (mode & SILC_CHANNEL_MODE_TOPIC) + strncat(string, "t", 1); + + if (mode & SILC_CHANNEL_MODE_ULIMIT) + strncat(string, "l", 1); + + if (mode & SILC_CHANNEL_MODE_PASSPHRASE) + strncat(string, "a", 1); + + if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) + strncat(string, "f", 1); + + if (mode & SILC_CHANNEL_MODE_CIPHER) + strncat(string, cipher, strlen(cipher)); + + if (mode & SILC_CHANNEL_MODE_HMAC) + strncat(string, hmac, strlen(hmac)); + + /* Rest of mode is ignored */ + + return strdup(string); +} + +/* Parses channel user mode mask and returns te mode as string */ + +char *silc_client_chumode(SilcUInt32 mode) +{ + char string[4]; + + if (!mode) + return NULL; + + memset(string, 0, sizeof(string)); + + if (mode & SILC_CHANNEL_UMODE_CHANFO) + strncat(string, "f", 1); + + if (mode & SILC_CHANNEL_UMODE_CHANOP) + strncat(string, "o", 1); + + return strdup(string); +} + +/* Parses channel user mode and returns it as special mode character. */ + +char *silc_client_chumode_char(SilcUInt32 mode) +{ + char string[4]; + + if (!mode) + return NULL; + + memset(string, 0, sizeof(string)); + + if (mode & SILC_CHANNEL_UMODE_CHANFO) + strncat(string, "*", 1); + + if (mode & SILC_CHANNEL_UMODE_CHANOP) + strncat(string, "@", 1); + + return strdup(string); +} + +/* Creates fingerprint from data, usually used with SHA1 digests */ + +char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len) +{ + char fingerprint[64], *cp; + int i; + + memset(fingerprint, 0, sizeof(fingerprint)); + cp = fingerprint; + for (i = 0; i < data_len; i++) { + snprintf(cp, sizeof(fingerprint), "%02X", data[i]); + cp += 2; + + if ((i + 1) % 2 == 0) + snprintf(cp++, sizeof(fingerprint), " "); + + if ((i + 1) % 10 == 0) + snprintf(cp++, sizeof(fingerprint), " "); + } + i--; + if ((i + 1) % 2 == 0) + cp[-2] = 0; + if ((i + 1) % 10 == 0) + cp[-1] = 0; + + return strdup(fingerprint); +}