+/*
+
+ serverconfig.c
+
+ Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+
+ Copyright (C) 1997 - 2001 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.
+
+ 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
+ GNU General Public License for more details.
+
+*/
+/* $Id$ */
+
+#include "module.h"
+
+#include "net-nonblock.h"
+#include "net-sendbuffer.h"
+#include "signals.h"
+#include "servers.h"
+#include "commands.h"
+#include "levels.h"
+#include "modules.h"
+#include "rawlog.h"
+#include "misc.h"
+#include "settings.h"
+
+#include "servers-setup.h"
+
+#include "silc-servers.h"
+#include "silc-channels.h"
+#include "silc-queries.h"
+#include "window-item-def.h"
+
+#include "fe-common/core/printtext.h"
+
+/*
+ All possible configuration sections for SILC client.
+*/
+SilcClientConfigSection silc_client_config_sections[] = {
+ { "[cipher]",
+ SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER, 4 },
+ { "[pkcs]",
+ SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS, 1 },
+ { "[hash]",
+ SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION, 4 },
+ { "[hmac]",
+ SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC, 3 },
+ { "[connection]",
+ SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION, 4 },
+
+ { NULL, SILC_CLIENT_CONFIG_SECTION_TYPE_NONE, 0 }
+};
+
+/* Allocates a new configuration object, opens configuration file and
+ parses the file. The parsed data is returned to the newly allocated
+ configuration object. */
+
+SilcClientConfig silc_client_config_alloc(char *filename)
+{
+ SilcClientConfig new;
+ SilcBuffer buffer;
+ SilcClientConfigParse config_parse;
+
+ SILC_LOG_DEBUG(("Allocating new configuration object"));
+
+ new = silc_calloc(1, sizeof(*new));
+ new->filename = filename;
+
+ /* Open configuration file and parse it */
+ config_parse = NULL;
+ buffer = NULL;
+ silc_config_open(filename, &buffer);
+ if (!buffer)
+ goto fail;
+ if ((silc_client_config_parse(new, buffer, &config_parse)) == FALSE)
+ goto fail;
+ if ((silc_client_config_parse_lines(new, config_parse)) == FALSE)
+ goto fail;
+
+ silc_free(buffer);
+
+ return new;
+
+ fail:
+ silc_free(new);
+ return NULL;
+}
+
+/* Free's a configuration object. */
+
+void silc_client_config_free(SilcClientConfig config)
+{
+ if (config) {
+
+ silc_free(config);
+ }
+}
+
+/* Parses the the buffer and returns the parsed lines into return_config
+ argument. The return_config argument doesn't have to be initialized
+ before calling this. It will be initialized during the parsing. The
+ buffer sent as argument can be safely free'd after this function has
+ succesfully returned. */
+
+int silc_client_config_parse(SilcClientConfig config, SilcBuffer buffer,
+ SilcClientConfigParse *return_config)
+{
+ int i, begin;
+ int linenum;
+ char line[1024], *cp;
+ SilcClientConfigSection *cptr = NULL;
+ SilcClientConfigParse parse = *return_config, first = NULL;
+
+ SILC_LOG_DEBUG(("Parsing configuration file"));
+
+ begin = 0;
+ linenum = 0;
+ while((begin = silc_gets(line, sizeof(line),
+ buffer->data, buffer->len, begin)) != EOF) {
+ cp = line;
+ linenum++;
+
+ /* Check for bad line */
+ if (silc_check_line(cp))
+ continue;
+
+ /* Remove tabs and whitespaces from the line */
+ if (strchr(cp, '\t')) {
+ i = 0;
+ while(strchr(cp + i, '\t')) {
+ *strchr(cp + i, '\t') = ' ';
+ i++;
+ }
+ }
+ for (i = 0; i < strlen(cp); i++) {
+ if (cp[i] != ' ') {
+ if (i)
+ cp++;
+ break;
+ }
+ cp++;
+ }
+
+ /* Parse line */
+ switch(cp[0]) {
+ case '[':
+ /*
+ * Start of a section
+ */
+
+ /* Remove new line sign */
+ if (strchr(cp, '\n'))
+ *strchr(cp, '\n') = '\0';
+
+ /* Check for matching sections */
+ for (cptr = silc_client_config_sections; cptr->section; cptr++)
+ if (!strncasecmp(cp, cptr->section, strlen(cptr->section)))
+ break;
+
+ if (!cptr->section) {
+ fprintf(stderr, "%s:%d: Unknown section `%s'\n",
+ config->filename, linenum, cp);
+ return FALSE;
+ }
+
+ break;
+ default:
+ /*
+ * Start of a configuration line
+ */
+
+ /* Handle config section */
+ if (cptr->type != SILC_CLIENT_CONFIG_SECTION_TYPE_NONE) {
+
+ if (strchr(cp, '\n'))
+ *strchr(cp, '\n') = ':';
+
+ if (parse == NULL) {
+ parse = silc_calloc(1, sizeof(*parse));
+ parse->line = NULL;
+ parse->section = NULL;
+ parse->next = NULL;
+ parse->prev = NULL;
+ } else {
+ if (parse->next == NULL) {
+ parse->next = silc_calloc(1, sizeof(*parse->next));
+ parse->next->line = NULL;
+ parse->next->section = NULL;
+ parse->next->next = NULL;
+ parse->next->prev = parse;
+ parse = parse->next;
+ }
+ }
+
+ if (first == NULL)
+ first = parse;
+
+ /* Add the line to parsing structure for further parsing. */
+ if (parse) {
+ parse->section = cptr;
+ parse->line = silc_buffer_alloc(strlen(cp) + 1);
+ parse->linenum = linenum;
+ silc_buffer_pull_tail(parse->line, strlen(cp));
+ silc_buffer_put(parse->line, cp, strlen(cp));
+ }
+ }
+ break;
+ }
+ }
+
+ /* Set the return_config argument to its first value so that further
+ parsing can be started from the first line. */
+ *return_config = first;
+
+ return TRUE;
+}
+
+/* Parses the lines earlier read from configuration file. The config object
+ must not be initialized, it will be initialized in this function. The
+ parse_config argument is uninitialized automatically during this
+ function. */
+
+int silc_client_config_parse_lines(SilcClientConfig config,
+ SilcClientConfigParse parse_config)
+{
+ int ret, check = FALSE;
+ char *tmp;
+ SilcClientConfigParse pc = parse_config;
+ SilcBuffer line;
+
+ SILC_LOG_DEBUG(("Parsing configuration lines"));
+
+ if (!config)
+ return FALSE;
+
+ while(pc) {
+ check = FALSE;
+ line = pc->line;
+
+ /* Get number of tokens in line (command section is handeled
+ specially and has no tokens at all). */
+ ret = silc_config_check_num_token(line);
+ if (ret != pc->section->maxfields) {
+ /* Bad line */
+ fprintf(stderr, "%s:%d: Missing tokens, %d tokens (should be %d)\n",
+ config->filename, pc->linenum, ret,
+ pc->section->maxfields);
+ break;
+ }
+
+ /* Parse the line */
+ switch(pc->section->type) {
+ case SILC_CLIENT_CONFIG_SECTION_TYPE_CIPHER:
+
+ if (!config->cipher) {
+ config->cipher = silc_calloc(1, sizeof(*config->cipher));
+ config->cipher->next = NULL;
+ config->cipher->prev = NULL;
+ } else {
+ if (!config->cipher->next) {
+ config->cipher->next =
+ silc_calloc(1, sizeof(*config->cipher->next));
+ config->cipher->next->next = NULL;
+ config->cipher->next->prev = config->cipher;
+ config->cipher = config->cipher->next;
+ }
+ }
+
+ /* Get cipher name */
+ ret = silc_config_get_token(line, &config->cipher->alg_name);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: Cipher name not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+
+ /* Get module name */
+ config->cipher->sim_name = NULL;
+ ret = silc_config_get_token(line, &config->cipher->sim_name);
+ if (ret < 0)
+ break;
+
+ /* Get key length */
+ ret = silc_config_get_token(line, &tmp);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: Cipher key length not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+ config->cipher->key_len = atoi(tmp);
+ silc_free(tmp);
+
+ /* Get block length */
+ ret = silc_config_get_token(line, &tmp);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: Cipher block length not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+ config->cipher->block_len = atoi(tmp);
+ silc_free(tmp);
+
+ check = TRUE;
+ break;
+
+ case SILC_CLIENT_CONFIG_SECTION_TYPE_PKCS:
+
+ if (!config->pkcs) {
+ config->pkcs = silc_calloc(1, sizeof(*config->pkcs));
+ config->pkcs->next = NULL;
+ config->pkcs->prev = NULL;
+ } else {
+ if (!config->pkcs->next) {
+ config->pkcs->next =
+ silc_calloc(1, sizeof(*config->pkcs->next));
+ config->pkcs->next->next = NULL;
+ config->pkcs->next->prev = config->pkcs;
+ config->pkcs = config->pkcs->next;
+ }
+ }
+
+ /* Get PKCS name */
+ ret = silc_config_get_token(line, &config->pkcs->alg_name);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: PKCS name not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+
+ check = TRUE;
+ break;
+
+ case SILC_CLIENT_CONFIG_SECTION_TYPE_HASH_FUNCTION:
+
+ if (!config->hash_func) {
+ config->hash_func = silc_calloc(1, sizeof(*config->hash_func));
+ config->hash_func->next = NULL;
+ config->hash_func->prev = NULL;
+ } else {
+ if (!config->hash_func->next) {
+ config->hash_func->next =
+ silc_calloc(1, sizeof(*config->hash_func->next));
+ config->hash_func->next->next = NULL;
+ config->hash_func->next->prev = config->hash_func;
+ config->hash_func = config->hash_func->next;
+ }
+ }
+
+ /* Get Hash function name */
+ ret = silc_config_get_token(line, &config->hash_func->alg_name);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: Hash function name not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+
+ /* Get Hash function module name */
+ config->hash_func->sim_name = NULL;
+ ret = silc_config_get_token(line, &config->hash_func->sim_name);
+ if (ret < 0)
+ break;
+
+ /* Get block length */
+ ret = silc_config_get_token(line, &tmp);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: Hash function block length not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+ config->hash_func->block_len = atoi(tmp);
+ silc_free(tmp);
+
+ /* Get hash length */
+ ret = silc_config_get_token(line, &tmp);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: Hash function hash length not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+ config->hash_func->key_len = atoi(tmp);
+ silc_free(tmp);
+
+ check = TRUE;
+ break;
+
+ case SILC_CLIENT_CONFIG_SECTION_TYPE_HMAC:
+
+ if (!config->hmac) {
+ config->hmac = silc_calloc(1, sizeof(*config->hmac));
+ config->hmac->next = NULL;
+ config->hmac->prev = NULL;
+ } else {
+ if (!config->hmac->next) {
+ config->hmac->next =
+ silc_calloc(1, sizeof(*config->hmac->next));
+ config->hmac->next->next = NULL;
+ config->hmac->next->prev = config->hmac;
+ config->hmac = config->hmac->next;
+ }
+ }
+
+ /* Get HMAC name */
+ ret = silc_config_get_token(line, &config->hmac->alg_name);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: HMAC name not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+
+ /* Get Hash function name */
+ ret = silc_config_get_token(line, &config->hmac->sim_name);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: Hash function name not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+
+ /* Get MAC length */
+ ret = silc_config_get_token(line, &tmp);
+ if (ret < 0)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "%s:%d: HMAC's MAC length not defined\n",
+ config->filename, pc->linenum);
+ break;
+ }
+ config->hmac->key_len = atoi(tmp);
+ silc_free(tmp);
+
+ check = TRUE;
+ break;
+
+ case SILC_CLIENT_CONFIG_SECTION_TYPE_CONNECTION:
+
+ if (!config->conns) {
+ config->conns = silc_calloc(1, sizeof(*config->conns));
+ config->conns->next = NULL;
+ config->conns->prev = NULL;
+ } else {
+ if (!config->conns->next) {
+ config->conns->next = silc_calloc(1, sizeof(*config->conns));
+ config->conns->next->next = NULL;
+ config->conns->next->prev = config->conns;
+ config->conns = config->conns->next;
+ }
+ }
+
+ /* Get host */
+ ret = silc_config_get_token(line, &config->conns->host);
+ if (ret < 0)
+ break;
+ if (ret == 0)
+ /* Any host */
+ config->conns->host = strdup("*");
+
+ /* Get authentication method */
+ ret = silc_config_get_token(line, &tmp);
+ if (ret < 0)
+ break;
+ if (ret) {
+ if (strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD) &&
+ strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY)) {
+ fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
+ config->filename, pc->linenum, tmp);
+ break;
+ }
+
+ if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PASSWD))
+ config->conns->auth_meth = SILC_AUTH_PASSWORD;
+
+ if (!strcmp(tmp, SILC_CLIENT_CONFIG_AUTH_METH_PUBKEY))
+ config->conns->auth_meth = SILC_AUTH_PUBLIC_KEY;
+
+ silc_free(tmp);
+ }
+
+ /* Get authentication data */
+ ret = silc_config_get_token(line, &config->conns->auth_data);
+ if (ret < 0)
+ break;
+
+ /* Get port */
+ ret = silc_config_get_token(line, &tmp);
+ if (ret < 0)
+ break;
+ if (ret) {
+ config->conns->port = atoi(tmp);
+ silc_free(tmp);
+ }
+
+ check = TRUE;
+ break;
+
+ case SILC_CLIENT_CONFIG_SECTION_TYPE_NONE:
+ default:
+ break;
+ }
+
+ /* Check for error */
+ if (check == FALSE) {
+ /* Line could not be parsed */
+ fprintf(stderr, "%s:%d: Parse error\n", config->filename, pc->linenum);
+ break;
+ }
+
+ pc = pc->next;
+ }
+
+ if (check == FALSE)
+ return FALSE;;
+
+ /* Before returning all the lists in the config object must be set
+ to their first values (the last value is first here). */
+ while (config->cipher && config->cipher->prev)
+ config->cipher = config->cipher->prev;
+ while (config->pkcs && config->pkcs->prev)
+ config->pkcs = config->pkcs->prev;
+ while (config->hash_func && config->hash_func->prev)
+ config->hash_func = config->hash_func->prev;
+ while (config->hmac && config->hmac->prev)
+ config->hmac = config->hmac->prev;
+ while (config->conns && config->conns->prev)
+ config->conns = config->conns->prev;
+
+ SILC_LOG_DEBUG(("Done"));
+
+ return TRUE;
+}
+
+/* Registers configured ciphers. These can then be allocated by the
+ client when needed. */
+
+bool silc_client_config_register_ciphers(SilcClientConfig config)
+{
+ SilcClientConfigSectionAlg *alg;
+ SilcClient client = config->client;
+
+ SILC_LOG_DEBUG(("Registering configured ciphers"));
+
+ if (!config->cipher)
+ return FALSE;
+
+ alg = config->cipher;
+ while(alg) {
+
+ if (!alg->sim_name) {
+ /* Crypto module is supposed to be built in. Get the pointer to the
+ built in cipher and register it. */
+ int i;
+
+ for (i = 0; silc_default_ciphers[i].name; i++)
+ if (!strcmp(silc_default_ciphers[i].name, alg->alg_name)) {
+ silc_cipher_register(&silc_default_ciphers[i]);
+ break;
+ }
+
+ if (!silc_cipher_is_supported(alg->alg_name)) {
+ SILC_LOG_ERROR(("Unknown cipher `%s'", alg->alg_name));
+ silc_client_stop(client);
+ exit(1);
+ }
+
+#ifdef SILC_SIM
+ } else {
+ /* Load (try at least) the crypto SIM module */
+ SilcCipherObject cipher;
+ SilcSimContext *sim;
+ char *alg_name;
+
+ memset(&cipher, 0, sizeof(cipher));
+ cipher.name = alg->alg_name;
+ cipher.block_len = alg->block_len;
+ cipher.key_len = alg->key_len * 8;
+
+ sim = silc_sim_alloc();
+ sim->type = SILC_SIM_CIPHER;
+ sim->libname = alg->sim_name;
+
+ alg_name = strdup(alg->alg_name);
+ if (strchr(alg_name, '-'))
+ *strchr(alg_name, '-') = '\0';
+
+ if ((silc_sim_load(sim))) {
+ cipher.set_key =
+ silc_sim_getsym(sim, silc_sim_symname(alg_name,
+ SILC_CIPHER_SIM_SET_KEY));
+ SILC_LOG_DEBUG(("set_key=%p", cipher.set_key));
+ cipher.set_key_with_string =
+ silc_sim_getsym(sim, silc_sim_symname(alg_name,
+ SILC_CIPHER_SIM_SET_KEY_WITH_STRING));
+ SILC_LOG_DEBUG(("set_key_with_string=%p", cipher.set_key_with_string));
+ cipher.encrypt =
+ silc_sim_getsym(sim, silc_sim_symname(alg_name,
+ SILC_CIPHER_SIM_ENCRYPT_CBC));
+ SILC_LOG_DEBUG(("encrypt_cbc=%p", cipher.encrypt));
+ cipher.decrypt =
+ silc_sim_getsym(sim, silc_sim_symname(alg_name,
+ SILC_CIPHER_SIM_DECRYPT_CBC));
+ SILC_LOG_DEBUG(("decrypt_cbc=%p", cipher.decrypt));
+ cipher.context_len =
+ silc_sim_getsym(sim, silc_sim_symname(alg_name,
+ SILC_CIPHER_SIM_CONTEXT_LEN));
+ SILC_LOG_DEBUG(("context_len=%p", cipher.context_len));
+
+ /* Put the SIM to the table of all SIM's in client */
+ sims = silc_realloc(sims,
+ sizeof(*sims) *
+ (sims_count + 1));
+ sims[sims_count] = sim;
+ sims_count++;
+
+ silc_free(alg_name);
+ } else {
+ SILC_LOG_ERROR(("Error configuring ciphers"));
+ silc_client_stop(client);
+ exit(1);
+ }
+
+ /* Register the cipher */
+ silc_cipher_register(&cipher);
+#endif
+ }
+
+ alg = alg->next;
+ }
+
+ return TRUE;
+}
+
+/* Registers configured PKCS's. */
+
+bool silc_client_config_register_pkcs(SilcClientConfig config)
+{
+ SilcClientConfigSectionAlg *alg = config->pkcs;
+ SilcClient client = config->client;
+
+ SILC_LOG_DEBUG(("Registering configured PKCS"));
+
+ if (!alg)
+ return FALSE;
+
+ while(alg) {
+ int i;
+
+ for (i = 0; silc_default_pkcs[i].name; i++)
+ if (!strcmp(silc_default_pkcs[i].name, alg->alg_name)) {
+ silc_pkcs_register(&silc_default_pkcs[i]);
+ break;
+ }
+
+ if (!silc_pkcs_is_supported(alg->alg_name)) {
+ SILC_LOG_ERROR(("Unknown PKCS `%s'", alg->alg_name));
+ silc_client_stop(client);
+ exit(1);
+ }
+
+ alg = alg->next;
+ }
+
+ return TRUE;
+}
+
+/* Registers configured hash funtions. These can then be allocated by the
+ client when needed. */
+
+bool silc_client_config_register_hashfuncs(SilcClientConfig config)
+{
+ SilcClientConfigSectionAlg *alg;
+ SilcClient client = config->client;
+
+ SILC_LOG_DEBUG(("Registering configured hash functions"));
+
+ if (!config->hash_func)
+ return FALSE;
+
+ alg = config->hash_func;
+ while(alg) {
+ if (!alg->sim_name) {
+ int i;
+
+ for (i = 0; silc_default_hash[i].name; i++)
+ if (!strcmp(silc_default_hash[i].name, alg->alg_name)) {
+ silc_hash_register(&silc_default_hash[i]);
+ break;
+ }
+
+ if (!silc_hash_is_supported(alg->alg_name)) {
+ SILC_LOG_ERROR(("Unknown hash function `%s'", alg->alg_name));
+ silc_client_stop(client);
+ exit(1);
+ }
+#ifdef SILC_SIM
+ } else {
+ /* Load (try at least) the hash SIM module */
+ SilcHashObject hash;
+ SilcSimContext *sim;
+
+ memset(&hash, 0, sizeof(hash));
+ hash.name = alg->alg_name;
+ hash.block_len = alg->block_len;
+ hash.hash_len = alg->key_len;
+
+ sim = silc_sim_alloc();
+ sim->type = SILC_SIM_HASH;
+ sim->libname = alg->sim_name;
+
+ if ((silc_sim_load(sim))) {
+ hash.init =
+ silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+ SILC_HASH_SIM_INIT));
+ SILC_LOG_DEBUG(("init=%p", hash.init));
+ hash.update =
+ silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+ SILC_HASH_SIM_UPDATE));
+ SILC_LOG_DEBUG(("update=%p", hash.update));
+ hash.final =
+ silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+ SILC_HASH_SIM_FINAL));
+ SILC_LOG_DEBUG(("final=%p", hash.final));
+ hash.context_len =
+ silc_sim_getsym(sim, silc_sim_symname(alg->alg_name,
+ SILC_HASH_SIM_CONTEXT_LEN));
+ SILC_LOG_DEBUG(("context_len=%p", hash.context_len));
+
+ /* Put the SIM to the table of all SIM's in client */
+ sims = silc_realloc(sims,
+ sizeof(*sims) *
+ (sims_count + 1));
+ sims[sims_count] = sim;
+ sims_count++;
+ } else {
+ SILC_LOG_ERROR(("Error configuring hash functions"));
+ silc_client_stop(client);
+ exit(1);
+ }
+
+ /* Register the hash function */
+ silc_hash_register(&hash);
+#endif
+ }
+ alg = alg->next;
+ }
+
+ return TRUE;
+}
+
+/* Registers configured HMACs. These can then be allocated by the
+ client when needed. */
+
+bool silc_client_config_register_hmacs(SilcClientConfig config)
+{
+ SilcClientConfigSectionAlg *alg;
+ SilcClient client = config->client;
+
+ SILC_LOG_DEBUG(("Registering configured HMACs"));
+
+ if (!config->hmac)
+ return FALSE;
+
+ alg = config->hmac;
+ while(alg) {
+ SilcHmacObject hmac;
+
+ if (!silc_hash_is_supported(alg->sim_name)) {
+ SILC_LOG_ERROR(("Unknown hash function `%s' for HMAC `%s'",
+ alg->sim_name, alg->alg_name));
+ silc_client_stop(client);
+ exit(1);
+ }
+
+ /* Register the HMAC */
+ memset(&hmac, 0, sizeof(hmac));
+ hmac.name = alg->alg_name;
+ hmac.len = alg->key_len;
+ silc_hmac_register(&hmac);
+
+ alg = alg->next;
+ }
+
+ return TRUE;
+}
+
+SilcClientConfigSectionConnection *
+silc_client_config_find_connection(SilcClientConfig config,
+ char *host, int port)
+{
+ int i;
+ SilcClientConfigSectionConnection *conn = NULL;
+
+ SILC_LOG_DEBUG(("Finding connection"));
+
+ if (!host)
+ return NULL;
+
+ if (!config->conns)
+ return NULL;
+
+ conn = config->conns;
+ for (i = 0; conn; i++) {
+ if (silc_string_compare(conn->host, host))
+ break;
+ conn = conn->next;
+ }
+
+ if (!conn)
+ return NULL;
+
+ SILC_LOG_DEBUG(("Found match"));
+
+ return conn;
+}