Accept 0600 and 0640 for server private key permissions.
[silc.git] / apps / silcd / serverconfig.c
index 4e575c2eb1c6d69109b8f83f4b22529b6dc75bc3..30d76af3516cae4c81cbb60a718e938cc66fadf3 100644 (file)
@@ -2,15 +2,14 @@
 
   serverconfig.c
 
-  Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+  Author: Giovanni Giacobbi <giovanni@giacobbi.net>
 
-  Copyright (C) 1997 - 2000 Pekka Riikonen
+  Copyright (C) 1997 - 2007 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
 
 #include "serverincludes.h"
 #include "server_internal.h"
+#include <dirent.h>
 
-SilcServerConfigSection silc_server_config_sections[] = {
-  { "[Cipher]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_CIPHER, 4 },
-  { "[PKCS]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_PKCS, 1 },
-  { "[Hash]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION, 4 },
-  { "[hmac]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_HMAC, 3 },
-  { "[ServerKeys]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_KEYS, 2 },
-  { "[ServerInfo]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO, 4 },
-  { "[AdminInfo]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO, 4 },
-  { "[ListenPort]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT, 3 },
-  { "[Identity]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_IDENTITY, 2 },
-  { "[Logging]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING, 3 },
-  { "[ConnectionClass]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS, 4 },
-  { "[ClientConnection]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION, 5 },
-  { "[ServerConnection]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION, 6 },
-  { "[RouterConnection]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION, 7 },
-  { "[AdminConnection]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION, 5 },
-  { "[DenyConnection]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION, 3 },
-  { "[motd]", 
-    SILC_CONFIG_SERVER_SECTION_TYPE_MOTD, 1 },
-  { "[pid]",
-    SILC_CONFIG_SERVER_SECTION_TYPE_PID, 1},
-  
-  { NULL, SILC_CONFIG_SERVER_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. */
+#if 0
+#define SERVER_CONFIG_DEBUG(fmt) SILC_LOG_DEBUG(fmt)
+#else
+#define SERVER_CONFIG_DEBUG(fmt)
+#endif
 
-SilcServerConfig silc_server_config_alloc(char *filename)
-{
-  SilcServerConfig new;
-  SilcBuffer buffer;
-  SilcServerConfigParse config_parse;
+/* auto-declare needed variables for the common list parsing */
+#define SILC_SERVER_CONFIG_SECTION_INIT(__type__)                      \
+  SilcServerConfig config = (SilcServerConfig) context;                        \
+  __type__ *findtmp, *tmp = (__type__ *) config->tmp;                  \
+  int got_errno = 0
+
+/* allocate the tmp field for fetching data */
+#define SILC_SERVER_CONFIG_ALLOCTMP(__type__)                          \
+  if (!tmp) {                                                          \
+    config->tmp = silc_calloc(1, sizeof(*findtmp));                    \
+    tmp = (__type__ *) config->tmp;                                    \
+  }
 
-  SILC_LOG_DEBUG(("Allocating new configuration object"));
+/* append the tmp field to the specified list */
+#define SILC_SERVER_CONFIG_LIST_APPENDTMP(__list__)                    \
+  if (!__list__) {                                                     \
+    __list__ = tmp;                                                    \
+  } else {                                                             \
+    for (findtmp = __list__; findtmp->next; findtmp = findtmp->next);  \
+    findtmp->next = tmp;                                               \
+  }
 
-  new = silc_calloc(1, sizeof(*new));
-  if (!new) {
-    fprintf(stderr, "Could not allocate new configuration object");
-    return NULL;
+/* loops all elements in a list and provides a di struct pointer of the
+ * specified type containing the current element */
+#define SILC_SERVER_CONFIG_LIST_DESTROY(__type__, __list__)            \
+  for (tmp = (void *) __list__; tmp;) {                                        \
+    __type__ *di = (__type__ *) tmp;                                   \
+    tmp = (void *) di->next;
+
+/* Set EDOUBLE error value and bail out if necessary */
+#define CONFIG_IS_DOUBLE(__x__)                                                \
+  if ((__x__)) {                                                       \
+    got_errno = SILC_CONFIG_EDOUBLE;                                   \
+    goto got_err;                                                      \
   }
 
-  new->filename = filename;
+/* Free the authentication fields in the specified struct
+ * Expands to two instructions */
+#define CONFIG_FREE_AUTH(__section__)                  \
+  silc_free(__section__->passphrase);
 
-  SILC_LOG_DEBUG(("Loading config data from `%s'", filename));
+/* Set default values to those parameters that have not been defined */
+static void
+my_set_param_defaults(SilcServerConfigConnParams *params,
+                     SilcServerConfigConnParams *defaults)
+{
+#define SET_PARAM_DEFAULT(p, d)        params->p =                             \
+  (params->p ? params->p : (defaults && defaults->p ? defaults->p : d))
+
+  SET_PARAM_DEFAULT(connections_max, SILC_SERVER_MAX_CONNECTIONS);
+  SET_PARAM_DEFAULT(connections_max_per_host,
+                   SILC_SERVER_MAX_CONNECTIONS_SINGLE);
+  SET_PARAM_DEFAULT(keepalive_secs, SILC_SERVER_KEEPALIVE);
+  SET_PARAM_DEFAULT(reconnect_count, SILC_SERVER_RETRY_COUNT);
+  SET_PARAM_DEFAULT(reconnect_interval, SILC_SERVER_RETRY_INTERVAL_MIN);
+  SET_PARAM_DEFAULT(reconnect_interval_max, SILC_SERVER_RETRY_INTERVAL_MAX);
+  SET_PARAM_DEFAULT(key_exchange_rekey, SILC_SERVER_REKEY);
+  SET_PARAM_DEFAULT(qos_rate_limit, SILC_SERVER_QOS_RATE_LIMIT);
+  SET_PARAM_DEFAULT(qos_bytes_limit, SILC_SERVER_QOS_BYTES_LIMIT);
+  SET_PARAM_DEFAULT(qos_limit_sec, SILC_SERVER_QOS_LIMIT_SEC);
+  SET_PARAM_DEFAULT(qos_limit_usec, SILC_SERVER_QOS_LIMIT_USEC);
+  SET_PARAM_DEFAULT(chlimit, SILC_SERVER_CH_JOIN_LIMIT);
+
+#undef SET_PARAM_DEFAULT
+}
 
-  /* Open configuration file and parse it */
-  config_parse = NULL;
-  buffer = NULL;
-  silc_config_open(filename, &buffer);
-  if (!buffer)
-    goto fail;
-  if ((silc_server_config_parse(new, buffer, &config_parse)) == FALSE)
-    goto fail;
-  if ((silc_server_config_parse_lines(new, config_parse)) == FALSE)
-    goto fail;
+/* Find connection parameters by the parameter block name. */
+static SilcServerConfigConnParams *
+my_find_param(SilcServerConfig config, const char *name)
+{
+  SilcServerConfigConnParams *param;
 
-  silc_buffer_free(buffer);
+  for (param = config->conn_params; param; param = param->next) {
+    if (!strcasecmp(param->name, name))
+      return param;
+  }
 
-  return new;
+  SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                        "Cannot find Params \"%s\".", name));
 
- fail:
-  silc_buffer_free(buffer);
-  silc_free(new);
   return NULL;
 }
 
-/* Free's a configuration object. */
+/* SKR find callbcak */
 
-void silc_server_config_free(SilcServerConfig config)
+static void my_find_callback(SilcSKR skr, SilcSKRFind find,
+                            SilcSKRStatus status, SilcDList keys,
+                            void *context)
 {
-  if (config) {
-    silc_free(config->filename);
-    silc_free(config->server_keys);
-    silc_free(config->server_info);
-    silc_free(config->admin_info);
-    silc_free(config->listen_port);
-    silc_free(config->identity);
-    silc_free(config->conn_class);
-    silc_free(config->clients);
-    silc_free(config->admins);
-    silc_free(config->servers);
-    silc_free(config->routers);
-    silc_free(config->denied);
-    silc_free(config->motd);
-    silc_free(config->pidfile);
-    silc_free(config);
-  }
-}
+  SilcSKRStatus *s = context;
 
-/* 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. */
+  *s = status;
+  if (keys)
+    silc_dlist_uninit(keys);
 
-int silc_server_config_parse(SilcServerConfig config, SilcBuffer buffer, 
-                            SilcServerConfigParse *return_config)
-{
-  int i, begin, linenum;
-  char line[1024], *cp;
-  SilcServerConfigSection *cptr = NULL;
-  SilcServerConfigParse 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;
+  silc_skr_find_free(find);
+}
 
-    /* Remove tabs and whitespaces from the line */
-    if (strchr(cp, '\t')) {
-      i = 0;
-      while(strchr(cp + i, '\t')) {
-       *strchr(cp + i, '\t') = ' ';
-       i++;
+/* parse an authdata according to its auth method */
+static SilcBool my_parse_authdata(SilcAuthMethod auth_meth, const char *p,
+                                 void **auth_data, SilcUInt32 *auth_data_len,
+                                 SilcSKRKeyUsage usage, void *key_context)
+{
+  if (auth_meth == SILC_AUTH_PASSWORD) {
+    /* p is a plain text password */
+    if (auth_data && auth_data_len) {
+      if (!silc_utf8_valid(p, strlen(p))) {
+       *auth_data_len = silc_utf8_encoded_len(p, strlen(p),
+                                              SILC_STRING_LOCALE);
+       *auth_data = silc_calloc(*auth_data_len, sizeof(unsigned char));
+       silc_utf8_encode(p, strlen(p), SILC_STRING_LOCALE, *auth_data,
+                        *auth_data_len);
+      } else {
+       *auth_data = (void *) strdup(p);
+       *auth_data_len = (SilcUInt32) strlen(p);
       }
     }
-    for (i = 0; i < strlen(cp); i++) {
-      if (cp[i] != ' ') {
-       if (i)
-         cp++;
-       break;
-      }
-      cp++;
+  } else if (auth_meth == SILC_AUTH_PUBLIC_KEY) {
+    /* p is a public key file name */
+    SilcPublicKey public_key;
+    SilcSKR skr = *auth_data;
+    SilcSKRFind find;
+    SilcSKRStatus status = SILC_SKR_NOT_FOUND;
+
+    if (!silc_pkcs_load_public_key(p, &public_key)) {
+      SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                            "Could not load public key file!"));
+      return FALSE;
     }
 
-    /* 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_server_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;
-      }
+    find = silc_skr_find_alloc();
+    silc_skr_find_set_public_key(find, public_key);
+    silc_skr_find_set_usage(find, usage);
+    if (!key_context)
+      silc_skr_find_set_context(find, SILC_32_TO_PTR(usage));
+    silc_skr_find(skr, NULL, find, my_find_callback, &status);
+    if (status == SILC_SKR_OK) {
+      /* Already added, ignore error */
+      silc_pkcs_public_key_free(public_key);
+      return TRUE;
+    }
 
-      break;
-    default:
-      /*
-       * Start of a configuration line
-       */
-
-      if (cptr->type != SILC_CONFIG_SERVER_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;
+    /* Add the public key to repository */
+    status = silc_skr_add_public_key(skr, public_key, usage,
+                                    key_context ? key_context :
+                                    (void *)usage, NULL);
+    if (status != SILC_SKR_OK) {
+      SILC_SERVER_LOG_ERROR(("Error while adding public key \"%s\"", p));
+      return FALSE;
     }
   }
-  
-  /* 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_server_config_parse_lines(SilcServerConfig config, 
-                                  SilcServerConfigParse parse_config)
+static int my_parse_publickeydir(const char *dirname, void **auth_data,
+                                SilcSKRKeyUsage usage)
 {
-  int ret, check = FALSE;
-  uint32 checkmask;
-  char *tmp;
-  SilcServerConfigParse pc = parse_config;
-  SilcBuffer line;
-
-  SILC_LOG_DEBUG(("Parsing configuration lines"));
-  
-  if (!config)
-    return FALSE;
-  
-  checkmask = 0;
-  while(pc) {
-    check = FALSE;
-    line = pc->line;
-
-    /* Get number of tokens in line */
-    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_CONFIG_SERVER_SECTION_TYPE_CIPHER:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->cipher);
-
-      /* 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;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_PKCS:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->pkcs);
-
-      /* 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;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_HASH_FUNCTION:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->hash_func);
-
-      /* 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;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_HMAC:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->hmac);
-
-      /* 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 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;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_KEYS:
-
-      if (!config->server_keys)
-       config->server_keys = silc_calloc(1, sizeof(*config->server_keys));
-
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: Public key name not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-      
-      if (!silc_pkcs_load_public_key(tmp, &config->server_keys->public_key, 
-                                    SILC_PKCS_FILE_PEM))
-       if (!silc_pkcs_load_public_key(tmp, &config->server_keys->public_key, 
-                                      SILC_PKCS_FILE_BIN)) {
-         fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
-      silc_free(tmp);
-
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: Private key name not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-      
-      if (!silc_pkcs_load_private_key(tmp, &config->server_keys->private_key, 
-                                    SILC_PKCS_FILE_BIN))
-       if (!silc_pkcs_load_private_key(tmp, 
-                                       &config->server_keys->private_key, 
-                                       SILC_PKCS_FILE_PEM)) {
-         fprintf(stderr, "%s:%d: Could not load private key file `%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
-      silc_free(tmp);
-
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO:
-
-      if (!config->server_info)
-       config->server_info = silc_calloc(1, sizeof(*config->server_info));
-
-      /* Get server name */
-      ret = silc_config_get_token(line, &config->server_info->server_name);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       /* Server name not defined */
-
-      }
-      
-      /* Get server IP */
-      ret = silc_config_get_token(line, &config->server_info->server_ip);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       /* Server IP not defined */
-
-      }
-
-      /* Get server location */
-      ret = silc_config_get_token(line, &config->server_info->location);
-      if (ret < 0)
-       break;
-
-      /* Get server port */
-      /* XXX: Need port here??? */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       /* Port not defined */
+  int total = 0;
+  struct dirent *get_file;
+  DIR *dp;
+
+  if (!(dp = opendir(dirname))) {
+    SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                          "Could not open directory \"%s\"", dirname));
+    return -1;
+  }
 
-      }
-      config->server_info->port = atoi(tmp);
-      silc_free(tmp);
+  /* errors are not considered fatal */
+  while ((get_file = readdir(dp))) {
+    const char *filename = get_file->d_name;
+    char buf[1024];
+    int dirname_len = strlen(dirname), filename_len = strlen(filename);
+    struct stat check_file;
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
+    /* Ignore "." and "..", and take files only with ".pub" suffix. */
+    if (!strcmp(filename, ".") || !strcmp(filename, "..") ||
+       (filename_len < 5) || strcmp(filename + filename_len - 4, ".pub"))
+      continue;
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO:
+    memset(buf, 0, sizeof(buf));
+    snprintf(buf, sizeof(buf) - 1, "%s%s%s", dirname,
+            (dirname[dirname_len - 1] == '/' ? "" : "/"), filename);
+
+    if (stat(buf, &check_file) < 0) {
+      SILC_SERVER_LOG_ERROR(("Error stating file %s: %s", buf,
+                            strerror(errno)));
+    } else if (S_ISREG(check_file.st_mode)) {
+      if (my_parse_authdata(SILC_AUTH_PUBLIC_KEY, buf, auth_data, NULL,
+                           usage, NULL))
+       total++;
+    }
+  }
 
-      if (!config->admin_info)
-       config->admin_info = silc_calloc(1, sizeof(*config->admin_info));
+  SILC_LOG_DEBUG(("Tried to load %d public keys in \"%s\"", total, dirname));
+  return total;
+}
 
-      /* Get location */
-      ret = silc_config_get_token(line, &config->admin_info->location);
-      if (ret < 0)
-       break;
+/* Callbacks */
 
-      /* Get server type */
-      ret = silc_config_get_token(line, &config->admin_info->server_type);
-      if (ret < 0)
-       break;
-
-      /* Get admins name */
-      ret = silc_config_get_token(line, &config->admin_info->admin_name);
-      if (ret < 0)
-       break;
+SILC_CONFIG_CALLBACK(fetch_generic)
+{
+  SilcServerConfig config = (SilcServerConfig) context;
+  int got_errno = 0;
 
-      /* Get admins email address */
-      ret = silc_config_get_token(line, &config->admin_info->admin_email);
-      if (ret < 0)
-       break;
+  if (!strcmp(name, "prefer_passphrase_auth")) {
+    config->prefer_passphrase_auth = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "require_reverse_lookup")) {
+    config->require_reverse_lookup = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "connections_max")) {
+    config->param.connections_max = (SilcUInt32) *(int *)val;
+  }
+  else if (!strcmp(name, "connections_max_per_host")) {
+    config->param.connections_max_per_host = (SilcUInt32) *(int *)val;
+  }
+  else if (!strcmp(name, "keepalive_secs")) {
+    config->param.keepalive_secs = (SilcUInt32) *(int *)val;
+  }
+  else if (!strcmp(name, "reconnect_count")) {
+    config->param.reconnect_count = (SilcUInt32) *(int *)val;
+  }
+  else if (!strcmp(name, "reconnect_interval")) {
+    config->param.reconnect_interval = (SilcUInt32) *(int *)val;
+  }
+  else if (!strcmp(name, "reconnect_interval_max")) {
+    config->param.reconnect_interval_max = (SilcUInt32) *(int *)val;
+  }
+  else if (!strcmp(name, "reconnect_keep_trying")) {
+    config->param.reconnect_keep_trying = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "key_exchange_rekey")) {
+    config->param.key_exchange_rekey = (SilcUInt32) *(int *)val;
+  }
+  else if (!strcmp(name, "key_exchange_pfs")) {
+    config->param.key_exchange_pfs = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "channel_rekey_secs")) {
+    config->channel_rekey_secs = (SilcUInt32) *(int *)val;
+  }
+  else if (!strcmp(name, "key_exchange_timeout")) {
+    config->key_exchange_timeout = (SilcUInt32) *(int *)val;
+  }
+  else if (!strcmp(name, "conn_auth_timeout")) {
+    config->conn_auth_timeout = (SilcUInt32) *(int *)val;
+  }
+  else if (!strcmp(name, "version_protocol")) {
+    CONFIG_IS_DOUBLE(config->param.version_protocol);
+    config->param.version_protocol =
+      (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "version_software")) {
+    CONFIG_IS_DOUBLE(config->param.version_software);
+    config->param.version_software =
+      (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "version_software_vendor")) {
+    CONFIG_IS_DOUBLE(config->param.version_software_vendor);;
+    config->param.version_software_vendor =
+      (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "detach_disabled")) {
+    config->detach_disabled = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "detach_timeout")) {
+    config->detach_timeout = (SilcUInt32) *(int *)val;
+  }
+  else if (!strcmp(name, "qos")) {
+    config->param.qos = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "qos_rate_limit")) {
+    config->param.qos_rate_limit = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "qos_bytes_limit")) {
+    config->param.qos_bytes_limit = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "qos_limit_sec")) {
+    config->param.qos_limit_sec = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "qos_limit_usec")) {
+    config->param.qos_limit_usec = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "channel_join_limit")) {
+    config->param.chlimit = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "debug_string")) {
+    CONFIG_IS_DOUBLE(config->debug_string);
+    config->debug_string = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "http_server")) {
+    config->httpd = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "http_server_ip")) {
+    CONFIG_IS_DOUBLE(config->httpd_ip);
+    config->httpd_ip = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "http_server_port")) {
+    int port = *(int *)val;
+    if ((port <= 0) || (port > 65535)) {
+      SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                            "Invalid port number!"));
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+    config->httpd_port = (SilcUInt16)port;
+  }
+  else if (!strcmp(name, "dynamic_server")) {
+    config->dynamic_server = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "local_channels")) {
+    config->local_channels = *(SilcBool *)val;
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
+  return SILC_CONFIG_OK;
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT:
+ got_err:
+  return got_errno;
+}
 
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->listen_port);
+SILC_CONFIG_CALLBACK(fetch_cipher)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigCipher);
+
+  SERVER_CONFIG_DEBUG(("Received CIPHER type=%d name=\"%s\" (val=%x)",
+                      type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* discard empty sub-blocks */
+      return SILC_CONFIG_OK;
+    if (!tmp->name) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
 
-      /* Get local IP */
-      ret = silc_config_get_token(line, &config->listen_port->local_ip);
-      if (ret < 0)
-       break;
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->cipher);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigCipher);
 
-      /* Get listener IP */
-      ret = silc_config_get_token(line, &config->listen_port->listener_ip);
-      if (ret < 0)
-       break;
+  /* Identify and save this value */
+  if (!strcmp(name, "name")) {
+    CONFIG_IS_DOUBLE(tmp->name);
+    tmp->name = strdup((char *) val);
+  }
+  else if (!strcmp(name, "keylength")) {
+    tmp->key_length = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "blocklength")) {
+    tmp->block_length = *(SilcUInt32 *)val;
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->name);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-      /* Get port */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       /* Any port */
-       config->listen_port->port = 0;
-      } else {
-       config->listen_port->port = atoi(tmp);
-       silc_free(tmp);
-      }
+SILC_CONFIG_CALLBACK(fetch_hash)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigHash);
+
+  SERVER_CONFIG_DEBUG(("Received HASH type=%d name=%s (val=%x)",
+                      type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* discard empty sub-blocks */
+      return SILC_CONFIG_OK;
+    if (!tmp->name || (tmp->block_length == 0) || (tmp->digest_length == 0)) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->hash);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigHash);
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_IDENTITY:
+  /* Identify and save this value */
+  if (!strcmp(name, "name")) {
+    CONFIG_IS_DOUBLE(tmp->name);
+    tmp->name = strdup((char *) val);
+  }
+  else if (!strcmp(name, "blocklength")) {
+    tmp->block_length = *(int *)val;
+  }
+  else if (!strcmp(name, "digestlength")) {
+    tmp->digest_length = *(int *)val;
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->name);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-      if (!config->identity)
-        config->identity = silc_calloc(1, sizeof(*config->identity));
+SILC_CONFIG_CALLBACK(fetch_hmac)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigHmac);
+
+  SERVER_CONFIG_DEBUG(("Received HMAC type=%d name=\"%s\" (val=%x)",
+                      type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* discard empty sub-blocks */
+      return SILC_CONFIG_OK;
+    if (!tmp->name || !tmp->hash || (tmp->mac_length == 0)) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
 
-      /* Get user */
-      ret = silc_config_get_token(line, &config->identity->user);
-      if (ret < 0)
-        break;
-      /* Get group */
-      ret = silc_config_get_token(line, &config->identity->group);
-      if (ret < 0)
-        break;
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->hmac);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigHmac);
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
+  /* Identify and save this value */
+  if (!strcmp(name, "name")) {
+    CONFIG_IS_DOUBLE(tmp->name);
+    tmp->name = strdup((char *) val);
+  }
+  else if (!strcmp(name, "hash")) {
+    CONFIG_IS_DOUBLE(tmp->hash);
+    tmp->hash = strdup((char *) val);
+  }
+  else if (!strcmp(name, "maclength")) {
+    tmp->mac_length = *(int *)val;
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->name);
+  silc_free(tmp->hash);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_CONNECTION_CLASS:
+SILC_CONFIG_CALLBACK(fetch_pkcs)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigPkcs);
+
+  SERVER_CONFIG_DEBUG(("Received PKCS type=%d name=\"%s\" (val=%x)",
+                      type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* Check the temporary struct's fields */
+    if (!tmp) /* discard empty sub-blocks */
+      return SILC_CONFIG_OK;
+    if (!tmp->name) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
 
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->conn_class);
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->pkcs);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigPkcs);
 
-      /* Get class number */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       /* Class number not defined */
+  /* Identify and save this value */
+  if (!strcmp(name, "name")) {
+    CONFIG_IS_DOUBLE(tmp->name);
+    tmp->name = strdup((char *) val);
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->name);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
+SILC_CONFIG_CALLBACK(fetch_serverinfo)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigServerInfoInterface);
+  SilcServerConfigServerInfo *server_info = config->server_info;
+
+  SERVER_CONFIG_DEBUG(("Received SERVERINFO type=%d name=\"%s\" (val=%x)",
+                      type, name, context));
+
+  /* If there isn't the main struct alloc it */
+  if (!server_info)
+    config->server_info = server_info = (SilcServerConfigServerInfo *)
+               silc_calloc(1, sizeof(*server_info));
+
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    if (!strcmp(name, "primary")) {
+      if (server_info->primary) {
+       SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                              "Double primary specification."));
+       got_errno = SILC_CONFIG_EPRINTLINE;
+       goto got_err;
       }
-      config->conn_class->class = atoi(tmp);
-      silc_free(tmp);
-
-      /* Get ping frequency */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      config->conn_class->ping_freq = atoi(tmp);
-      silc_free(tmp);
-
-      /* Get connect frequency */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      config->conn_class->connect_freq = atoi(tmp);
-      silc_free(tmp);
-
-      /* Get max links */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      config->conn_class->max_links = atoi(tmp);
-      silc_free(tmp);
-
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_LOGGING:
-
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->logging);
+      CONFIG_IS_DOUBLE(server_info->primary);
 
-      /* Get log section type and check it */
-      ret = silc_config_get_token(line, &config->logging->logtype);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: Log file section not defined\n", 
-               config->filename, pc->linenum);
-       break;
+      /* now check the temporary struct, don't accept empty block and
+         make sure all fields are there */
+      if (!tmp || !tmp->server_ip || !tmp->port) {
+       got_errno = SILC_CONFIG_EMISSFIELDS;
+       goto got_err;
       }
-      if (strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_INFO)
-         && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_WARNING)
-         && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_ERROR)
-         && strcmp(config->logging->logtype, SILC_CONFIG_SERVER_LF_FATAL)) {
-       fprintf(stderr, "%s:%d: Unknown log file section '%s'\n",
-               config->filename, pc->linenum, config->logging->logtype);
-       break;
+      server_info->primary = tmp;
+      config->tmp = NULL;
+      return SILC_CONFIG_OK;
+    } else if (!strcmp(name, "secondary")) {
+      if (!tmp)
+       return SILC_CONFIG_OK;
+      if (!tmp || !tmp->server_ip || !tmp->port) {
+       got_errno = SILC_CONFIG_EMISSFIELDS;
+       goto got_err;
       }
-
-      /* Get log filename */
-      ret = silc_config_get_token(line, &config->logging->filename);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       fprintf(stderr, "%s:%d: Log file name not defined\n",
-               config->filename, pc->linenum);
-       break;
-      }
-
-      /* Get max byte size */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->logging->maxsize = atoi(tmp);
-       silc_free(tmp);
+      SILC_SERVER_CONFIG_LIST_APPENDTMP(server_info->secondary);
+      config->tmp = NULL;
+      return SILC_CONFIG_OK;
+    } else if (!server_info->public_key || !server_info->private_key) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
+    return SILC_CONFIG_OK;
+  }
+  if (!strcmp(name, "hostname")) {
+    CONFIG_IS_DOUBLE(server_info->server_name);
+    server_info->server_name = strdup((char *) val);
+  }
+  else if (!strcmp(name, "ip")) {
+    SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigServerInfoInterface);
+    CONFIG_IS_DOUBLE(tmp->server_ip);
+    tmp->server_ip = strdup((char *) val);
+  }
+  else if (!strcmp(name, "public_ip")) {
+    SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigServerInfoInterface);
+    CONFIG_IS_DOUBLE(tmp->public_ip);
+    tmp->public_ip = strdup((char *) val);
+  }
+  else if (!strcmp(name, "port")) {
+    int port = *(int *)val;
+    SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigServerInfoInterface);
+    if ((port <= 0) || (port > 65535)) {
+      SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                            "Invalid port number!"));
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+    tmp->port = (SilcUInt16) port;
+  }
+  else if (!strcmp(name, "servertype")) {
+    CONFIG_IS_DOUBLE(server_info->server_type);
+    server_info->server_type = strdup((char *) val);
+  }
+  else if (!strcmp(name, "admin")) {
+    CONFIG_IS_DOUBLE(server_info->admin);
+    server_info->admin = strdup((char *) val);
+  }
+  else if (!strcmp(name, "adminemail")) {
+    CONFIG_IS_DOUBLE(server_info->email);
+    server_info->email = strdup((char *) val);
+  }
+  else if (!strcmp(name, "location")) {
+    CONFIG_IS_DOUBLE(server_info->location);
+    server_info->location = strdup((char *) val);
+  }
+  else if (!strcmp(name, "user")) {
+    CONFIG_IS_DOUBLE(server_info->user);
+    server_info->user = strdup((char *) val);
+  }
+  else if (!strcmp(name, "group")) {
+    CONFIG_IS_DOUBLE(server_info->group);
+    server_info->group = strdup((char *) val);
+  }
+  else if (!strcmp(name, "motdfile")) {
+    CONFIG_IS_DOUBLE(server_info->motd_file);
+    server_info->motd_file = strdup((char *) val);
+  }
+  else if (!strcmp(name, "pidfile")) {
+    CONFIG_IS_DOUBLE(server_info->pid_file);
+    server_info->pid_file = strdup((char *) val);
+  }
+  else if (!strcmp(name, "publickey")) {
+    char *file_tmp = (char *) val;
+    CONFIG_IS_DOUBLE(server_info->public_key);
+
+    /* Try to load specified file, if fail stop config parsing */
+    if (!silc_pkcs_load_public_key(file_tmp, &server_info->public_key)) {
+      SILC_SERVER_LOG_ERROR(("Error: Could not load public key file."));
+      return SILC_CONFIG_EPRINTLINE;
+    }
+  }
+  else if (!strcmp(name, "privatekey")) {
+    struct stat st;
+    char *file_tmp = (char *) val;
+    CONFIG_IS_DOUBLE(server_info->private_key);
+
+    /* Check the private key file permissions. */
+    if ((stat(file_tmp, &st)) != -1) {
+      if (((st.st_mode & 0777) != 0600) &&
+         ((st.st_mode & 0777) != 0640)) {
+       SILC_SERVER_LOG_ERROR(("Wrong permissions in private key "
+                             "file \"%s\".  The permissions must be "
+                             "0600 or 0640.", file_tmp));
+        return SILC_CONFIG_ESILENT;
       }
+    }
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
+    /* Try to load specified file, if fail stop config parsing */
+    if (!silc_pkcs_load_private_key(file_tmp, "", 0,
+                                   &server_info->private_key)) {
+      SILC_SERVER_LOG_ERROR(("Error: Could not load private key file."));
+      return SILC_CONFIG_EPRINTLINE;
+    }
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  /* Here we need to check if tmp exists because this function handles
+   * misc data (multiple fields and single-only fields) */
+  if (tmp) {
+    silc_free(tmp->server_ip);
+    silc_free(tmp);
+    config->tmp = NULL;
+  }
+  return got_errno;
+}
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION:
+SILC_CONFIG_CALLBACK(fetch_logging)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigLogging);
 
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->clients);
+  if (!strcmp(name, "timestamp")) {
+    config->logging_timestamp = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "quicklogs")) {
+    config->logging_quick = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "flushdelay")) {
+    int flushdelay = *(int *)val;
+    if (flushdelay < 2) { /* this value was taken from silclog.h (min delay) */
+      SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                           "Invalid flushdelay value, use quicklogs if you "
+                           "want real-time logging."));
+      return SILC_CONFIG_EPRINTLINE;
+    }
+    config->logging_flushdelay = (long) flushdelay;
+  }
 
-      /* Get host */
-      ret = silc_config_get_token(line, &config->clients->host);
-      if (ret < 0)
-       break;
-      if (ret == 0)
-       /* Any host */
-       config->clients->host = strdup("*");
+  /* The following istances happens only in Logging's sub-blocks, a match
+     for the sub-block name means that you should store the filename/maxsize
+     temporary struct to the proper logging channel.
+     If we get a match for "file" or "maxsize" this means that we are inside
+     a sub-sub-block and it is safe to alloc a new tmp. */
+#define FETCH_LOGGING_CHAN(__chan__, __member__)               \
+  else if (!strcmp(name, __chan__)) {                          \
+    if (!tmp) return SILC_CONFIG_OK;                           \
+    if (!tmp->file) {                                          \
+      got_errno = SILC_CONFIG_EMISSFIELDS; goto got_err;       \
+    }                                                          \
+    config->__member__ = tmp;                                  \
+    config->tmp = NULL;                                                \
+  }
+  FETCH_LOGGING_CHAN("info", logging_info)
+  FETCH_LOGGING_CHAN("warnings", logging_warnings)
+  FETCH_LOGGING_CHAN("errors", logging_errors)
+  FETCH_LOGGING_CHAN("fatals", logging_fatals)
+#undef FETCH_LOGGING_CHAN
+  else if (!strcmp(name, "file")) {
+    SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigLogging);
+    CONFIG_IS_DOUBLE(tmp->file);
+    tmp->file = strdup((char *) val);
+  }
+  else if (!strcmp(name, "size")) {
+    if (!tmp) {
+      config->tmp = silc_calloc(1, sizeof(*tmp));
+      tmp = (SilcServerConfigLogging *) config->tmp;
+    }
+    tmp->maxsize = *(SilcUInt32 *) val;
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->file);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-      /* Get authentication method */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
-           strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
-         fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
+SILC_CONFIG_CALLBACK(fetch_connparam)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigConnParams);
+
+  SERVER_CONFIG_DEBUG(("Received CONNPARAM type=%d name=\"%s\" (val=%x)",
+                      type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* discard empty sub-blocks */
+      return SILC_CONFIG_OK;
+    if (!tmp->name) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
+    /* Set defaults */
+    my_set_param_defaults(tmp, &config->param);
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->clients->auth_meth = SILC_AUTH_PASSWORD;
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->conn_params);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  if (!tmp) {
+    SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigConnParams);
+    tmp->reconnect_keep_trying = TRUE;
+  }
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->clients->auth_meth = SILC_AUTH_PUBLIC_KEY;
+  if (!strcmp(name, "name")) {
+    CONFIG_IS_DOUBLE(tmp->name);
+    tmp->name = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "connections_max")) {
+    tmp->connections_max = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "connections_max_per_host")) {
+    tmp->connections_max_per_host = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "keepalive_secs")) {
+    tmp->keepalive_secs = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "reconnect_count")) {
+    tmp->reconnect_count = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "reconnect_interval")) {
+    tmp->reconnect_interval = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "reconnect_interval_max")) {
+    tmp->reconnect_interval_max = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "reconnect_keep_trying")) {
+    tmp->reconnect_keep_trying = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "key_exchange_rekey")) {
+    tmp->key_exchange_rekey = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "key_exchange_pfs")) {
+    tmp->key_exchange_pfs = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "version_protocol")) {
+    CONFIG_IS_DOUBLE(tmp->version_protocol);
+    tmp->version_protocol = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "version_software")) {
+    CONFIG_IS_DOUBLE(tmp->version_software);
+    tmp->version_software = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "version_software_vendor")) {
+    CONFIG_IS_DOUBLE(tmp->version_software_vendor);;
+    tmp->version_software_vendor =
+      (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "anonymous")) {
+    tmp->anonymous = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "qos")) {
+    tmp->qos = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "qos_rate_limit")) {
+    tmp->qos_rate_limit = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "qos_bytes_limit")) {
+    tmp->qos_bytes_limit = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "qos_limit_sec")) {
+    tmp->qos_limit_sec = *(SilcUInt32 *)val;
+  }
+  else if (!strcmp(name, "qos_limit_usec")) {
+    tmp->qos_limit_usec = *(SilcUInt32 *)val;
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
 
-       silc_free(tmp);
-      }
+  return SILC_CONFIG_OK;
 
-      /* Get authentication data */
-      ret = silc_config_get_token(line, (char **)&config->clients->auth_data);
-      if (ret < 0)
-       break;
+ got_err:
+  silc_free(tmp->name);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-      if (config->clients->auth_meth == SILC_AUTH_PASSWORD) {
-       config->clients->auth_data_len = strlen(config->clients->auth_data);
-      } else if (config->clients->auth_meth == SILC_AUTH_PUBLIC_KEY) {
-       /* Get the public key */
-       SilcPublicKey public_key;
-
-       if (!silc_pkcs_load_public_key(config->clients->auth_data,
-                                      &public_key, SILC_PKCS_FILE_PEM))
-         if (!silc_pkcs_load_public_key(config->clients->auth_data,
-                                        &public_key, SILC_PKCS_FILE_BIN)) {
-           fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
-                   config->filename, pc->linenum, 
-                   (char *)config->clients->auth_data);
-           break;
-         }
-
-       silc_free(config->clients->auth_data);
-       config->clients->auth_data = (void *)public_key;
-       config->clients->auth_data_len = 0;
-      }
+SILC_CONFIG_CALLBACK(fetch_client)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigClient);
 
-      /* Get port */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->clients->port = atoi(tmp);
-       silc_free(tmp);
-      }
+  SERVER_CONFIG_DEBUG(("Received CLIENT type=%d name=\"%s\" (val=%x)",
+                      type, name, context));
 
-      /* Get class number */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->clients->class = atoi(tmp);
-       silc_free(tmp);
-      }
+  /* Alloc before block checking, because empty sub-blocks are welcome here */
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigClient);
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* empty sub-blocks are welcome */
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->clients);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION:
+  /* Identify and save this value */
+  if (!strcmp(name, "host")) {
+    CONFIG_IS_DOUBLE(tmp->host);
+    tmp->host = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "passphrase")) {
+    CONFIG_IS_DOUBLE(tmp->passphrase);
+    if (!my_parse_authdata(SILC_AUTH_PASSWORD, (char *) val,
+                          (void *)&tmp->passphrase,
+                          &tmp->passphrase_len, 0, NULL)) {
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+  }
+  else if (!strcmp(name, "publickey")) {
+    if (!my_parse_authdata(SILC_AUTH_PUBLIC_KEY, (char *) val,
+                          (void *)&config->server->repository, NULL,
+                          SILC_SKR_USAGE_AUTH |
+                          SILC_SKR_USAGE_KEY_AGREEMENT, NULL)) {
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+    tmp->publickeys = TRUE;
+  }
+  else if (!strcmp(name, "publickeydir")) {
+    if (my_parse_publickeydir((char *) val,
+                             (void *)&config->server->repository,
+                             SILC_SKR_USAGE_AUTH |
+                             SILC_SKR_USAGE_KEY_AGREEMENT) < 0) {
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+    tmp->publickeys = TRUE;
+  }
+  else if (!strcmp(name, "params")) {
+    CONFIG_IS_DOUBLE(tmp->param);
+    tmp->param = my_find_param(config, (char *) val);
+    if (!tmp->param) { /* error message already output */
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->host);
+  CONFIG_FREE_AUTH(tmp);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->servers);
+SILC_CONFIG_CALLBACK(fetch_admin)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigAdmin);
+
+  SERVER_CONFIG_DEBUG(("Received CLIENT type=%d name=\"%s\" (val=%x)",
+                      type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* discard empty sub-blocks */
+      return SILC_CONFIG_OK;
+
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->admins);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigAdmin);
 
-      /* Get host */
-      ret = silc_config_get_token(line, &config->servers->host);
-      if (ret < 0)
-       break;
-      if (ret == 0)
-       /* Any host */
-       config->servers->host = strdup("*");
+  /* Identify and save this value */
+  if (!strcmp(name, "host")) {
+    CONFIG_IS_DOUBLE(tmp->host);
+    tmp->host = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "user")) {
+    CONFIG_IS_DOUBLE(tmp->user);
+    tmp->user = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "nick")) {
+    CONFIG_IS_DOUBLE(tmp->nick);
+    tmp->nick = (*(char *)val ? strdup((char *) val) : NULL);
+  }
+  else if (!strcmp(name, "passphrase")) {
+    CONFIG_IS_DOUBLE(tmp->passphrase);
+    if (!my_parse_authdata(SILC_AUTH_PASSWORD, (char *) val,
+                          (void *)&tmp->passphrase,
+                          &tmp->passphrase_len, 0, NULL)) {
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+  }
+  else if (!strcmp(name, "publickey")) {
+    if (!my_parse_authdata(SILC_AUTH_PUBLIC_KEY, (char *) val,
+                          (void *)&config->server->repository, NULL,
+                          SILC_SKR_USAGE_SERVICE_AUTHORIZATION, tmp)) {
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+    tmp->publickeys = TRUE;
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->host);
+  silc_free(tmp->user);
+  silc_free(tmp->nick);
+  CONFIG_FREE_AUTH(tmp);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-      /* Get authentication method */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
-           strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
-         fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
+SILC_CONFIG_CALLBACK(fetch_deny)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigDeny);
+
+  SERVER_CONFIG_DEBUG(("Received DENY type=%d name=\"%s\" (val=%x)",
+                      type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* discard empty sub-blocks */
+      return SILC_CONFIG_OK;
+    if (!tmp->reason) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->servers->auth_meth = SILC_AUTH_PASSWORD;
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->denied);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigDeny);
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->servers->auth_meth = SILC_AUTH_PUBLIC_KEY;
+  /* Identify and save this value */
+  if (!strcmp(name, "host")) {
+    CONFIG_IS_DOUBLE(tmp->host);
+    tmp->host = (*(char *)val ? strdup((char *) val) : strdup("*"));
+  }
+  else if (!strcmp(name, "reason")) {
+    CONFIG_IS_DOUBLE(tmp->reason);
+    tmp->reason = strdup((char *) val);
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->host);
+  silc_free(tmp->reason);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-       silc_free(tmp);
-      }
+SILC_CONFIG_CALLBACK(fetch_server)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigServer);
+
+  SERVER_CONFIG_DEBUG(("Received SERVER type=%d name=\"%s\" (val=%x)",
+                      type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    /* check the temporary struct's fields */
+    if (!tmp) /* discard empty sub-blocks */
+      return SILC_CONFIG_OK;
+    if (!tmp->host) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
 
-      /* Get authentication data */
-      ret = silc_config_get_token(line, (char **)&config->servers->auth_data);
-      if (ret < 0)
-       break;
+    /* the temporary struct is ok, append it to the list */
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->servers);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigServer);
 
-      if (config->servers->auth_meth == SILC_AUTH_PASSWORD) {
-       config->servers->auth_data_len = strlen(config->servers->auth_data);
-      } else if (config->servers->auth_meth == SILC_AUTH_PUBLIC_KEY) {
-       /* Get the public key */
-       SilcPublicKey public_key;
-
-       if (!silc_pkcs_load_public_key(config->servers->auth_data,
-                                      &public_key, SILC_PKCS_FILE_PEM))
-         if (!silc_pkcs_load_public_key(config->servers->auth_data,
-                                        &public_key, SILC_PKCS_FILE_BIN)) {
-           fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
-                   config->filename, pc->linenum, 
-                   (char *)config->servers->auth_data);
-           break;
-         }
-
-       silc_free(config->servers->auth_data);
-       config->servers->auth_data = (void *)public_key;
-       config->servers->auth_data_len = 0;
-      }
+  /* Identify and save this value */
+  if (!strcmp(name, "host")) {
+    CONFIG_IS_DOUBLE(tmp->host);
+    tmp->host = (*(char *)val ? strdup((char *) val) : strdup("*"));
+  }
+  else if (!strcmp(name, "passphrase")) {
+    CONFIG_IS_DOUBLE(tmp->passphrase);
+    if (!my_parse_authdata(SILC_AUTH_PASSWORD, (char *) val,
+                          (void *)&tmp->passphrase,
+                          &tmp->passphrase_len, 0, NULL)) {
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+  }
+  else if (!strcmp(name, "publickey")) {
+    CONFIG_IS_DOUBLE(tmp->publickeys);
+    if (!my_parse_authdata(SILC_AUTH_PUBLIC_KEY, (char *) val,
+                          (void *)&config->server->repository, NULL,
+                          SILC_SKR_USAGE_AUTH |
+                          SILC_SKR_USAGE_KEY_AGREEMENT, NULL)) {
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+    tmp->publickeys = TRUE;
+  }
+  else if (!strcmp(name, "params")) {
+    CONFIG_IS_DOUBLE(tmp->param);
+    tmp->param = my_find_param(config, (char *) val);
+    if (!tmp->param) { /* error message already output */
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+  }
+  else if (!strcmp(name, "backup")) {
+    tmp->backup_router = *(SilcBool *)val;
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
 
-      /* Get port */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->servers->port = atoi(tmp);
-       silc_free(tmp);
-      }
+  return SILC_CONFIG_OK;
 
-      /* Get version */
-      ret = silc_config_get_token(line, &config->servers->version);
-      if (ret < 0)
-       break;
+ got_err:
+  silc_free(tmp->host);
+  CONFIG_FREE_AUTH(tmp);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-      /* Get class number */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->servers->class = atoi(tmp);
-       silc_free(tmp);
-      }
+SILC_CONFIG_CALLBACK(fetch_router)
+{
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigRouter);
+
+  SERVER_CONFIG_DEBUG(("Received ROUTER type=%d name=\"%s\" (val=%x)",
+                      type, name, context));
+  if (type == SILC_CONFIG_ARG_BLOCK) {
+    if (!tmp) /* discard empty sub-blocks */
+      return SILC_CONFIG_OK;
+    if (!tmp->host) {
+      got_errno = SILC_CONFIG_EMISSFIELDS;
+      goto got_err;
+    }
 
-      /* Check whether this connection is backup router connection */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret != -1) {
-       config->servers->backup_router = atoi(tmp);
-       if (config->servers->backup_router != 0)
-         config->servers->backup_router = TRUE;
-       silc_free(tmp);
-      }
+    SILC_SERVER_CONFIG_LIST_APPENDTMP(config->routers);
+    config->tmp = NULL;
+    return SILC_CONFIG_OK;
+  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigRouter);
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
+  /* Identify and save this value */
+  if (!strcmp(name, "host")) {
+    CONFIG_IS_DOUBLE(tmp->host);
+    tmp->host = strdup((char *) val);
+  }
+  else if (!strcmp(name, "port")) {
+    int port = *(int *)val;
+    if ((port <= 0) || (port > 65535)) {
+      SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                            "Invalid port number!"));
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+    tmp->port = (SilcUInt16) port;
+  }
+  else if (!strcmp(name, "passphrase")) {
+    CONFIG_IS_DOUBLE(tmp->passphrase);
+    if (!my_parse_authdata(SILC_AUTH_PASSWORD, (char *) val,
+                          (void *)&tmp->passphrase,
+                          &tmp->passphrase_len, 0, NULL)) {
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+  }
+  else if (!strcmp(name, "publickey")) {
+    CONFIG_IS_DOUBLE(tmp->publickeys);
+    if (!my_parse_authdata(SILC_AUTH_PUBLIC_KEY, (char *) val,
+                          (void *)&config->server->repository, NULL,
+                          SILC_SKR_USAGE_AUTH |
+                          SILC_SKR_USAGE_KEY_AGREEMENT, NULL)) {
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+    tmp->publickeys = TRUE;
+  }
+  else if (!strcmp(name, "params")) {
+    CONFIG_IS_DOUBLE(tmp->param);
+    tmp->param = my_find_param(config, (char *) val);
+    if (!tmp->param) { /* error message already output */
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+  }
+  else if (!strcmp(name, "initiator")) {
+    tmp->initiator = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "backuphost")) {
+    CONFIG_IS_DOUBLE(tmp->backup_replace_ip);
+    tmp->backup_replace_ip = (*(char *)val ? strdup((char *) val) :
+                             strdup("*"));
+    tmp->backup_router = TRUE;
+  }
+  else if (!strcmp(name, "backupport")) {
+    int port = *(int *)val;
+    if ((port <= 0) || (port > 65535)) {
+      SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                            "Invalid port number!"));
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
+    }
+    tmp->backup_replace_port = (SilcUInt16) port;
+  }
+  else if (!strcmp(name, "backuplocal")) {
+    tmp->backup_local = *(SilcBool *)val;
+  }
+  else if (!strcmp(name, "dynamic_connection")) {
+    tmp->dynamic_connection = *(SilcBool *)val;
+  }
+  else
+    return SILC_CONFIG_EINTERNAL;
+
+  return SILC_CONFIG_OK;
+
+ got_err:
+  silc_free(tmp->host);
+  silc_free(tmp->backup_replace_ip);
+  CONFIG_FREE_AUTH(tmp);
+  silc_free(tmp);
+  config->tmp = NULL;
+  return got_errno;
+}
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION:
+/* known config options tables */
+static const SilcConfigTable table_general[] = {
+  { "prefer_passphrase_auth",  SILC_CONFIG_ARG_TOGGLE, fetch_generic,  NULL },
+  { "require_reverse_lookup",  SILC_CONFIG_ARG_TOGGLE, fetch_generic,  NULL },
+  { "connections_max",         SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "connections_max_per_host", SILC_CONFIG_ARG_INT,    fetch_generic, NULL },
+  { "keepalive_secs",          SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "reconnect_count",         SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "reconnect_interval",              SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "reconnect_interval_max",   SILC_CONFIG_ARG_INT,   fetch_generic,  NULL },
+  { "reconnect_keep_trying",   SILC_CONFIG_ARG_TOGGLE, fetch_generic,  NULL },
+  { "key_exchange_rekey",      SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "key_exchange_pfs",                SILC_CONFIG_ARG_TOGGLE, fetch_generic,  NULL },
+  { "channel_rekey_secs",      SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "key_exchange_timeout",    SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "conn_auth_timeout",       SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "version_protocol",                SILC_CONFIG_ARG_STR,    fetch_generic,  NULL },
+  { "version_software",                SILC_CONFIG_ARG_STR,    fetch_generic,  NULL },
+  { "version_software_vendor", SILC_CONFIG_ARG_STR,    fetch_generic,  NULL },
+  { "detach_disabled",         SILC_CONFIG_ARG_TOGGLE, fetch_generic,  NULL },
+  { "detach_timeout",          SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "qos",                     SILC_CONFIG_ARG_TOGGLE, fetch_generic,  NULL },
+  { "qos_rate_limit",          SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "qos_bytes_limit",         SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "qos_limit_sec",           SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "qos_limit_usec",          SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "channel_join_limit",      SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "debug_string",                    SILC_CONFIG_ARG_STR,    fetch_generic,  NULL },
+  { "http_server",             SILC_CONFIG_ARG_TOGGLE, fetch_generic,  NULL },
+  { "http_server_ip",                  SILC_CONFIG_ARG_STRE,   fetch_generic,  NULL },
+  { "http_server_port",                SILC_CONFIG_ARG_INT,    fetch_generic,  NULL },
+  { "dynamic_server",                  SILC_CONFIG_ARG_TOGGLE, fetch_generic,  NULL },
+  { "local_channels",          SILC_CONFIG_ARG_TOGGLE, fetch_generic,  NULL },
+  { 0, 0, 0, 0 }
+};
 
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->routers);
+static const SilcConfigTable table_cipher[] = {
+  { "name",            SILC_CONFIG_ARG_STR,    fetch_cipher,   NULL },
+  { "keylength",       SILC_CONFIG_ARG_INT,    fetch_cipher,   NULL },
+  { "blocklength",     SILC_CONFIG_ARG_INT,    fetch_cipher,   NULL },
+  { 0, 0, 0, 0 }
+};
 
-      /* Get host */
-      ret = silc_config_get_token(line, &config->routers->host);
-      if (ret < 0)
-       break;
+static const SilcConfigTable table_hash[] = {
+  { "name",            SILC_CONFIG_ARG_STR,    fetch_hash,     NULL },
+  { "blocklength",     SILC_CONFIG_ARG_INT,    fetch_hash,     NULL },
+  { "digestlength",    SILC_CONFIG_ARG_INT,    fetch_hash,     NULL },
+  { 0, 0, 0, 0 }
+};
 
-      /* Get authentication method */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
-           strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
-         fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
+static const SilcConfigTable table_hmac[] = {
+  { "name",            SILC_CONFIG_ARG_STR,    fetch_hmac,     NULL },
+  { "hash",            SILC_CONFIG_ARG_STR,    fetch_hmac,     NULL },
+  { "maclength",       SILC_CONFIG_ARG_INT,    fetch_hmac,     NULL },
+  { 0, 0, 0, 0 }
+};
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->routers->auth_meth = SILC_AUTH_PASSWORD;
+static const SilcConfigTable table_pkcs[] = {
+  { "name",            SILC_CONFIG_ARG_STR,    fetch_pkcs,     NULL },
+  { 0, 0, 0, 0 }
+};
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->routers->auth_meth = SILC_AUTH_PUBLIC_KEY;
+static const SilcConfigTable table_serverinfo_c[] = {
+  { "ip",              SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "public_ip",       SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "port",            SILC_CONFIG_ARG_INT,    fetch_serverinfo, NULL},
+  { 0, 0, 0, 0 }
+};
 
-       silc_free(tmp);
-      }
+static const SilcConfigTable table_serverinfo[] = {
+  { "hostname",                SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "primary",         SILC_CONFIG_ARG_BLOCK,  fetch_serverinfo, table_serverinfo_c},
+  { "secondary",       SILC_CONFIG_ARG_BLOCK,  fetch_serverinfo, table_serverinfo_c},
+  { "servertype",      SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "location",                SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "admin",           SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "adminemail",      SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "user",            SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "group",           SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "publickey",       SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "privatekey",      SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+  { "motdfile",                SILC_CONFIG_ARG_STRE,   fetch_serverinfo, NULL},
+  { "pidfile",         SILC_CONFIG_ARG_STRE,   fetch_serverinfo, NULL},
+  { 0, 0, 0, 0 }
+};
 
-      /* Get authentication data */
-      ret = silc_config_get_token(line, (char **)&config->routers->auth_data);
-      if (ret < 0)
-       break;
+static const SilcConfigTable table_logging_c[] = {
+  { "file",            SILC_CONFIG_ARG_STR,    fetch_logging,  NULL },
+  { "size",            SILC_CONFIG_ARG_SIZE,   fetch_logging,  NULL },
+/*{ "quicklog",                SILC_CONFIG_ARG_NONE,   fetch_logging,  NULL }, */
+  { 0, 0, 0, 0 }
+};
 
-      if (config->routers->auth_meth == SILC_AUTH_PASSWORD) {
-       config->routers->auth_data_len = strlen(config->routers->auth_data);
-      } else if (config->routers->auth_meth == SILC_AUTH_PUBLIC_KEY) {
-       /* Get the public key */
-       SilcPublicKey public_key;
-
-       if (!silc_pkcs_load_public_key(config->routers->auth_data,
-                                      &public_key, SILC_PKCS_FILE_PEM))
-         if (!silc_pkcs_load_public_key(config->routers->auth_data,
-                                        &public_key, SILC_PKCS_FILE_BIN)) {
-           fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
-                   config->filename, pc->linenum, 
-                   (char *)config->routers->auth_data);
-           break;
-         }
-
-       silc_free(config->routers->auth_data);
-       config->routers->auth_data = (void *)public_key;
-       config->routers->auth_data_len = 0;
-      }
+static const SilcConfigTable table_logging[] = {
+  { "timestamp",       SILC_CONFIG_ARG_TOGGLE, fetch_logging,  NULL },
+  { "quicklogs",       SILC_CONFIG_ARG_TOGGLE, fetch_logging,  NULL },
+  { "flushdelay",      SILC_CONFIG_ARG_INT,    fetch_logging,  NULL },
+  { "info",            SILC_CONFIG_ARG_BLOCK,  fetch_logging,  table_logging_c },
+  { "warnings",                SILC_CONFIG_ARG_BLOCK,  fetch_logging,  table_logging_c },
+  { "errors",          SILC_CONFIG_ARG_BLOCK,  fetch_logging,  table_logging_c },
+  { "fatals",          SILC_CONFIG_ARG_BLOCK,  fetch_logging,  table_logging_c },
+  { 0, 0, 0, 0 }
+};
 
-      /* Get port */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->routers->port = atoi(tmp);
-       silc_free(tmp);
-      }
+static const SilcConfigTable table_connparam[] = {
+  { "name",                   SILC_CONFIG_ARG_STR,    fetch_connparam, NULL },
+  { "require_reverse_lookup",  SILC_CONFIG_ARG_TOGGLE, fetch_connparam,        NULL },
+  { "connections_max",        SILC_CONFIG_ARG_INT,    fetch_connparam, NULL },
+  { "connections_max_per_host",SILC_CONFIG_ARG_INT,    fetch_connparam, NULL },
+  { "keepalive_secs",         SILC_CONFIG_ARG_INT,    fetch_connparam, NULL },
+  { "reconnect_count",        SILC_CONFIG_ARG_INT,    fetch_connparam, NULL },
+  { "reconnect_interval",      SILC_CONFIG_ARG_INT,    fetch_connparam,        NULL },
+  { "reconnect_interval_max",  SILC_CONFIG_ARG_INT,    fetch_connparam,        NULL },
+  { "reconnect_keep_trying",   SILC_CONFIG_ARG_TOGGLE, fetch_connparam,        NULL },
+  { "key_exchange_rekey",      SILC_CONFIG_ARG_INT,    fetch_connparam,        NULL },
+  { "key_exchange_pfs",               SILC_CONFIG_ARG_TOGGLE, fetch_connparam, NULL },
+  { "version_protocol",               SILC_CONFIG_ARG_STR,    fetch_connparam, NULL },
+  { "version_software",               SILC_CONFIG_ARG_STR,    fetch_connparam, NULL },
+  { "version_software_vendor", SILC_CONFIG_ARG_STR,    fetch_connparam,        NULL },
+  { "anonymous",               SILC_CONFIG_ARG_TOGGLE, fetch_connparam,        NULL },
+  { "qos",                    SILC_CONFIG_ARG_TOGGLE, fetch_connparam, NULL },
+  { "qos_rate_limit",          SILC_CONFIG_ARG_INT,    fetch_connparam,        NULL },
+  { "qos_bytes_limit",         SILC_CONFIG_ARG_INT,    fetch_connparam,        NULL },
+  { "qos_limit_sec",           SILC_CONFIG_ARG_INT,    fetch_connparam,        NULL },
+  { "qos_limit_usec",          SILC_CONFIG_ARG_INT,    fetch_connparam,        NULL },
+  { 0, 0, 0, 0 }
+};
 
-      /* Get version */
-      ret = silc_config_get_token(line, &config->routers->version);
-      if (ret < 0)
-       break;
+static const SilcConfigTable table_client[] = {
+  { "host",            SILC_CONFIG_ARG_STRE,   fetch_client,   NULL },
+  { "passphrase",      SILC_CONFIG_ARG_STR,    fetch_client,   NULL },
+  { "publickey",       SILC_CONFIG_ARG_STR,    fetch_client,   NULL },
+  { "publickeydir",    SILC_CONFIG_ARG_STR,    fetch_client,   NULL },
+  { "params",          SILC_CONFIG_ARG_STR,    fetch_client,   NULL },
+  { 0, 0, 0, 0 }
+};
 
-      /* Get class number */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->routers->class = atoi(tmp);
-       silc_free(tmp);
-      }
+static const SilcConfigTable table_admin[] = {
+  { "host",            SILC_CONFIG_ARG_STRE,   fetch_admin,    NULL },
+  { "user",            SILC_CONFIG_ARG_STRE,   fetch_admin,    NULL },
+  { "nick",            SILC_CONFIG_ARG_STRE,   fetch_admin,    NULL },
+  { "passphrase",      SILC_CONFIG_ARG_STR,    fetch_admin,    NULL },
+  { "publickey",       SILC_CONFIG_ARG_STR,    fetch_admin,    NULL },
+  { "port",            SILC_CONFIG_ARG_INT,    fetch_admin,    NULL },
+  { "params",          SILC_CONFIG_ARG_STR,    fetch_admin,    NULL },
+  { 0, 0, 0, 0 }
+};
 
-      /* Get whether we are initiator or not */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       config->routers->initiator = atoi(tmp);
-       if (config->routers->initiator != 0)
-         config->routers->initiator = TRUE;
-       silc_free(tmp);
-      }
+static const SilcConfigTable table_deny[] = {
+  { "host",            SILC_CONFIG_ARG_STRE,   fetch_deny,     NULL },
+  { "reason",          SILC_CONFIG_ARG_STR,    fetch_deny,     NULL },
+  { 0, 0, 0, 0 }
+};
 
-      /* Get backup replace IP */
-      ret = silc_config_get_token(line, &config->routers->backup_replace_ip);
-      if (ret != -1)
-       config->routers->backup_router = TRUE;
-
-      if (config->routers->backup_router) {
-       /* Get backup replace port */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret != -1) {
-         config->routers->backup_replace_port = atoi(tmp);
-         silc_free(tmp);
-       }
-       
-       /* Check whether the backup connection is local */
-       ret = silc_config_get_token(line, &tmp);
-       if (ret != -1) {
-         config->routers->backup_local = atoi(tmp);
-         if (config->routers->backup_local != 0)
-           config->routers->backup_local = TRUE;
-         silc_free(tmp);
-       }
-      }
+static const SilcConfigTable table_serverconn[] = {
+  { "host",            SILC_CONFIG_ARG_STRE,   fetch_server,   NULL },
+  { "passphrase",      SILC_CONFIG_ARG_STR,    fetch_server,   NULL },
+  { "publickey",       SILC_CONFIG_ARG_STR,    fetch_server,   NULL },
+  { "params",          SILC_CONFIG_ARG_STR,    fetch_server,   NULL },
+  { "backup",          SILC_CONFIG_ARG_TOGGLE, fetch_server,   NULL },
+  { 0, 0, 0, 0 }
+};
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
+static const SilcConfigTable table_routerconn[] = {
+  { "host",            SILC_CONFIG_ARG_STRE,   fetch_router,   NULL },
+  { "port",            SILC_CONFIG_ARG_INT,    fetch_router,   NULL },
+  { "passphrase",      SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
+  { "publickey",       SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
+  { "params",          SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
+  { "initiator",       SILC_CONFIG_ARG_TOGGLE, fetch_router,   NULL },
+  { "backuphost",      SILC_CONFIG_ARG_STRE,   fetch_router,   NULL },
+  { "backupport",      SILC_CONFIG_ARG_INT,    fetch_router,   NULL },
+  { "backuplocal",     SILC_CONFIG_ARG_TOGGLE, fetch_router,   NULL },
+  { "dynamic_connection",      SILC_CONFIG_ARG_TOGGLE, fetch_router,   NULL },
+  { 0, 0, 0, 0 }
+};
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_CONNECTION:
+static const SilcConfigTable table_main[] = {
+  { "cipher",          SILC_CONFIG_ARG_BLOCK,  fetch_cipher,  table_cipher },
+  { "hash",            SILC_CONFIG_ARG_BLOCK,  fetch_hash,    table_hash },
+  { "hmac",            SILC_CONFIG_ARG_BLOCK,  fetch_hmac,    table_hmac },
+  { "pkcs",            SILC_CONFIG_ARG_BLOCK,  fetch_pkcs,    table_pkcs },
+  { "general",         SILC_CONFIG_ARG_BLOCK,  NULL,          table_general },
+  { "serverinfo",      SILC_CONFIG_ARG_BLOCK,  fetch_serverinfo, table_serverinfo },
+  { "logging",         SILC_CONFIG_ARG_BLOCK,  NULL,          table_logging },
+  { "connectionparams",        SILC_CONFIG_ARG_BLOCK,  fetch_connparam, table_connparam },
+  { "client",          SILC_CONFIG_ARG_BLOCK,  fetch_client,  table_client },
+  { "admin",           SILC_CONFIG_ARG_BLOCK,  fetch_admin,   table_admin },
+  { "deny",            SILC_CONFIG_ARG_BLOCK,  fetch_deny,    table_deny },
+  { "serverconnection",        SILC_CONFIG_ARG_BLOCK,  fetch_server,  table_serverconn },
+  { "routerconnection",        SILC_CONFIG_ARG_BLOCK,  fetch_router,  table_routerconn },
+  { 0, 0, 0, 0 }
+};
 
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->admins);
+/* Set default values to stuff that was not configured. */
 
-      /* Get host */
-      ret = silc_config_get_token(line, &config->admins->host);
-      if (ret < 0)
-       break;
-      if (ret == 0)
-       /* Any host */
-       config->admins->host = strdup("*");
+static void silc_server_config_set_defaults(SilcServerConfig config)
+{
+  my_set_param_defaults(&config->param, NULL);
+
+  config->channel_rekey_secs = (config->channel_rekey_secs ?
+                               config->channel_rekey_secs :
+                               SILC_SERVER_CHANNEL_REKEY);
+  config->key_exchange_timeout = (config->key_exchange_timeout ?
+                                 config->key_exchange_timeout :
+                                 SILC_SERVER_SKE_TIMEOUT);
+  config->conn_auth_timeout = (config->conn_auth_timeout ?
+                              config->conn_auth_timeout :
+                              SILC_SERVER_CONNAUTH_TIMEOUT);
+}
 
-      /* Get username */
-      ret = silc_config_get_token(line, &config->admins->username);
-      if (ret < 0)
-       break;
-      if (ret == 0)
-       /* Any username */
-       config->admins->username = strdup("*");
+/* Check for correctness of the configuration */
 
-      /* Get nickname */
-      ret = silc_config_get_token(line, &config->admins->nickname);
-      if (ret < 0)
-       break;
-      if (ret == 0)
-       /* Any nickname */
-       config->admins->nickname = strdup("*");
+static SilcBool silc_server_config_check(SilcServerConfig config)
+{
+  SilcBool ret = TRUE;
+  SilcServerConfigServer *s;
+  SilcServerConfigRouter *r;
+  SilcBool b = FALSE;
+
+  /* ServerConfig is mandatory */
+  if (!config->server_info) {
+    SILC_SERVER_LOG_ERROR(("\nError: Missing mandatory block `ServerInfo'"));
+    ret = FALSE;
+  }
 
-      /* Get authentication method */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret) {
-       if (strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD) &&
-           strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY)) {
-         fprintf(stderr, "%s:%d: Unknown authentication method '%s'\n",
-                 config->filename, pc->linenum, tmp);
-         break;
-       }
+  if (!config->server_info->public_key ||
+      !config->server_info->private_key) {
+    SILC_SERVER_LOG_ERROR(("\nError: Server keypair is missing"));
+    ret = FALSE;
+  }
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PASSWD))
-         config->admins->auth_meth = SILC_AUTH_PASSWORD;
+  if (!config->server_info->primary) {
+    SILC_SERVER_LOG_ERROR(("\nError: Missing mandatory block `Primary' "
+                          "in `ServerInfo'"));
+    ret = FALSE;
+  }
 
-       if (!strcmp(tmp, SILC_CONFIG_SERVER_AUTH_METH_PUBKEY))
-         config->admins->auth_meth = SILC_AUTH_PUBLIC_KEY;
+  if (!config->server_info->primary->server_ip) {
+    SILC_SERVER_LOG_ERROR(("\nError: Missing mandatory field `Ip' "
+                          "in `Primary' in `ServerInfo'"));
+    ret = FALSE;
+  }
 
-       silc_free(tmp);
-      }
+  /* RouterConnection sanity checks */
 
-      /* Get authentication data */
-      ret = silc_config_get_token(line, (char **)&config->admins->auth_data);
-      if (ret < 0)
-       break;
+  if (config->routers && config->routers->backup_router == TRUE &&
+      !config->servers) {
+    SILC_SERVER_LOG_ERROR((
+         "\nError: First RouterConnection block must be primary router "
+        "connection. You have marked it incorrectly as backup router."));
+    ret = FALSE;
+  }
+  if (config->routers && config->routers->backup_router == TRUE &&
+      !config->servers && !config->routers->next) {
+    SILC_SERVER_LOG_ERROR((
+         "\nError: You have configured backup router but not primary router. "
+        "If backup router is configured also primary router must be "
+        "configured."));
+    ret = FALSE;
+  }
 
-      if (config->admins->auth_meth == SILC_AUTH_PASSWORD) {
-       config->admins->auth_data_len = strlen(config->admins->auth_data);
-      } else if (config->admins->auth_meth == SILC_AUTH_PUBLIC_KEY) {
-       /* Get the public key */
-       SilcPublicKey public_key;
-
-       if (!silc_pkcs_load_public_key(config->admins->auth_data,
-                                      &public_key, SILC_PKCS_FILE_PEM))
-         if (!silc_pkcs_load_public_key(config->admins->auth_data,
-                                        &public_key, SILC_PKCS_FILE_BIN)) {
-           fprintf(stderr, "%s:%d: Could not load public key file `%s'\n",
-                   config->filename, pc->linenum, 
-                   (char *)config->admins->auth_data);
-           break;
-         }
-
-       silc_free(config->admins->auth_data);
-       config->admins->auth_data = (void *)public_key;
-       config->admins->auth_data_len = 0;
-      }
+  /* Backup router sanity checks */
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
+  for (r = config->routers; r; r = r->next) {
+    if (r->backup_router && !strcmp(r->host, r->backup_replace_ip)) {
+      SILC_SERVER_LOG_ERROR((
+          "\nError: Backup router connection incorrectly configured to use "
+         "primary and backup router as same host `%s'. They must not be "
+         "same host.", r->host));
+      ret = FALSE;
+    }
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_DENY_CONNECTION:
+    if (r->initiator == FALSE && r->port != 0) {
+      SILC_SERVER_LOG_WARNING(("\nWarning: Initiator is FALSE and Port is "
+                               "specified. Ignoring Port value."));
+      r->port = 0;
+    }
+  }
 
-      SILC_SERVER_CONFIG_LIST_ALLOC(config->denied);
+  /* ServerConnection sanity checks */
 
-      /* Get host */
-      ret = silc_config_get_token(line, &config->denied->host);
-      if (ret < 0)
+  for (s = config->servers; s; s = s->next) {
+    if (s->backup_router) {
+      b = TRUE;
+      break;
+    }
+  }
+  if (b) {
+    for (s = config->servers; s; s = s->next) {
+      if (!s->backup_router) {
+       SILC_SERVER_LOG_ERROR((
+          "\nError: Your server is backup router but not all ServerConnection "
+         "blocks were marked as backup connections. They all must be "
+         "marked as backup connections."));
+       ret = FALSE;
        break;
-      if (ret == 0) {
-       /* Any host */
-       config->denied->host = strdup("*");
-       fprintf(stderr, "warning: %s:%d: Denying all connections",
-               config->filename, pc->linenum);
       }
+    }
+  }
 
-      /* Get port */
-      ret = silc_config_get_token(line, &tmp);
-      if (ret < 0)
-       break;
-      if (ret == 0) {
-       /* Any port */
-       config->denied->port = 0;
-      } else {
-       config->denied->port = atoi(tmp);
-       silc_free(tmp);
-      }
+  return ret;
+}
 
-      /* Get comment */
-      ret = silc_config_get_token(line, &config->denied->comment);
-      if (ret < 0)
-       break;
+/* Allocates a new configuration object, opens configuration file and
+   parses it. The parsed data is returned to the newly allocated
+   configuration object. The SilcServerConfig must be freed by calling
+   the silc_server_config_destroy function. */
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
+SilcServerConfig silc_server_config_alloc(const char *filename,
+                                         SilcServer server)
+{
+  SilcServerConfig config_new;
+  SilcConfigEntity ent;
+  SilcConfigFile *file;
+  int ret;
+  SILC_LOG_DEBUG(("Loading config data from `%s'", filename));
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_MOTD:
+  /* alloc a config object */
+  config_new = silc_calloc(1, sizeof(*config_new));
+  if (!config_new)
+    return NULL;
 
-      if (!config->motd)
-       config->motd = silc_calloc(1, sizeof(*config->motd));
+  /* general config defaults */
+  config_new->refcount = 1;
+  config_new->logging_timestamp = TRUE;
+  config_new->param.reconnect_keep_trying = TRUE;
+  config_new->server = server;
+
+  /* obtain a config file object */
+  file = silc_config_open(filename);
+  if (!file) {
+    SILC_SERVER_LOG_ERROR(("\nError: can't open config file `%s'",
+                          filename));
+    return NULL;
+  }
 
-      /* Get motd file */
-      ret = silc_config_get_token(line, &config->motd->motd_file);
-      if (ret < 0)
-       break;
+  /* obtain a SilcConfig entity, we can use it to start the parsing */
+  ent = silc_config_init(file);
+
+  /* load the known configuration options, give our empty object as context */
+  silc_config_register_table(ent, table_main, (void *) config_new);
+
+  /* enter the main parsing loop.  When this returns, we have the parsing
+   * result and the object filled (or partially, in case of errors). */
+  ret = silc_config_main(ent);
+  SILC_LOG_DEBUG(("Parser returned [ret=%d]: %s", ret,
+                 silc_config_strerror(ret)));
+
+  /* Check if the parser returned errors */
+  if (ret) {
+    /* handle this special error return which asks to quietly return */
+    if (ret != SILC_CONFIG_ESILENT) {
+      char *linebuf, *filename = silc_config_get_filename(file);
+      SilcUInt32 line = silc_config_get_line(file);
+      if (ret != SILC_CONFIG_EPRINTLINE)
+        SILC_SERVER_LOG_ERROR(("Error while parsing config file: %s.",
+                              silc_config_strerror(ret)));
+      linebuf = silc_config_read_line(file, line);
+      if (linebuf) {
+       SILC_SERVER_LOG_ERROR(("  file %s line %lu:  %s\n", filename,
+                              line, linebuf));
+       silc_free(linebuf);
+      }
+    }
+    silc_server_config_destroy(config_new);
+    silc_config_close(file);
+    return NULL;
+  }
 
-      check = TRUE;
-      checkmask |= (1L << pc->section->type);
-      break;
+  /* close (destroy) the file object */
+  silc_config_close(file);
 
-    case SILC_CONFIG_SERVER_SECTION_TYPE_PID:
-
-       if (!config->pidfile)
-          config->pidfile = silc_calloc(1, sizeof(*config->pidfile));
-          
-       ret = silc_config_get_token(line, &config->pidfile->pid_file);
-       if (ret < 0)
-          break;
-          
-       check = TRUE;
-       checkmask |= (1L << pc->section->type);
-       break;
-
-    case SILC_CONFIG_SERVER_SECTION_TYPE_NONE:
-    default:
-      /* Error */
-      break;
-    }
+  /* Check the configuration */
+  if (!silc_server_config_check(config_new)) {
+    silc_server_config_destroy(config_new);
+    return NULL;
+  }
 
-    /* Check for error */
-    if (check == FALSE) {
-      /* Line could not be parsed */
-      fprintf(stderr, "%s:%d: Parse error\n", config->filename, pc->linenum);
-      break;
-    }
+  /* Set default to configuration parameters */
+  silc_server_config_set_defaults(config_new);
+
+  return config_new;
+}
+
+/* Increments the reference counter of a config object */
 
-    pc = pc->next;
+void silc_server_config_ref(SilcServerConfigRef *ref, SilcServerConfig config,
+                           void *ref_ptr)
+{
+  if (ref_ptr) {
+    config->refcount++;
+    ref->config = config;
+    ref->ref_ptr = ref_ptr;
+    SILC_LOG_DEBUG(("Referencing config [%p] refcnt %d->%d", config,
+                   config->refcount - 1, config->refcount));
   }
+}
 
-  if (check == FALSE)
-    return FALSE;
+/* Decrements the reference counter of a config object.  If the counter
+   reaches 0, the config object is destroyed. */
 
-  /* Check that all mandatory sections really were found. If not, the server
-     cannot function and we return error. */
-  ret = silc_server_config_check_sections(checkmask);
-  if (ret == FALSE) {
-    /* XXX */
-
-  }
-  
-  /* 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->listen_port && config->listen_port->prev)
-    config->listen_port = config->listen_port->prev;
-  while (config->logging && config->logging->prev)
-    config->logging = config->logging->prev;
-  while (config->conn_class && config->conn_class->prev)
-    config->conn_class = config->conn_class->prev;
-  while (config->clients && config->clients->prev)
-    config->clients = config->clients->prev;
-  while (config->servers && config->servers->prev)
-    config->servers = config->servers->prev;
-  while (config->admins && config->admins->prev)
-    config->admins = config->admins->prev;
-  while (config->routers && config->routers->prev)
-    config->routers = config->routers->prev;
-  
-  SILC_LOG_DEBUG(("Done"));
-  
-  return TRUE;
+void silc_server_config_unref(SilcServerConfigRef *ref)
+{
+  if (ref->ref_ptr)
+    silc_server_config_destroy(ref->config);
 }
 
-/* This function checks that the mask sent as argument includes all the 
-   sections that are mandatory in SILC server. */
+/* Destroy a config object with all his children lists */
 
-int silc_server_config_check_sections(uint32 checkmask)
+void silc_server_config_destroy(SilcServerConfig config)
 {
-  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_INFO))) {
-    
-    return FALSE;
+  void *tmp;
+
+  config->refcount--;
+  SILC_LOG_DEBUG(("Unreferencing config [%p] refcnt %d->%d", config,
+                 config->refcount + 1, config->refcount));
+  if (config->refcount > 0)
+    return;
+
+  SILC_LOG_DEBUG(("Freeing config context"));
+
+  /* Destroy general config stuff */
+  silc_free(config->debug_string);
+  silc_free(config->param.version_protocol);
+  silc_free(config->param.version_software);
+  silc_free(config->param.version_software_vendor);
+  silc_free(config->httpd_ip);
+
+  /* Destroy Logging channels */
+  if (config->logging_info)
+    silc_free(config->logging_info->file);
+  if (config->logging_warnings)
+    silc_free(config->logging_warnings->file);
+  if (config->logging_errors)
+    silc_free(config->logging_errors->file);
+  if (config->logging_fatals)
+    silc_free(config->logging_fatals->file);
+  silc_free(config->logging_info);
+  silc_free(config->logging_warnings);
+  silc_free(config->logging_errors);
+  silc_free(config->logging_fatals);
+
+  /* Destroy the ServerInfo struct */
+  if (config->server_info) {
+    register SilcServerConfigServerInfo *si = config->server_info;
+    silc_free(si->server_name);
+    if (si->primary) {
+      silc_free(si->primary->server_ip);
+      silc_free(si->primary);
+    }
+    SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigServerInfoInterface,
+                                 si->secondary)
+      silc_free(di->server_ip);
+      silc_free(di);
+    }
+    silc_free(si->server_type);
+    silc_free(si->location);
+    silc_free(si->admin);
+    silc_free(si->email);
+    silc_free(si->user);
+    silc_free(si->group);
+    silc_free(si->motd_file);
+    silc_free(si->pid_file);
+    if (si->public_key)
+      silc_pkcs_public_key_free(si->public_key);
+    if (si->private_key)
+      silc_pkcs_private_key_free(si->private_key);
+    silc_free(si);
   }
-  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_ADMIN_INFO))) {
-    
-    return FALSE;
+
+  /* Now let's destroy the lists */
+
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigCipher,
+                                 config->cipher)
+    silc_free(di->name);
+    silc_free(di);
   }
-  if (!(checkmask & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_LISTEN_PORT))) {
-    
-    return FALSE;
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigHash, config->hash)
+    silc_free(di->name);
+    silc_free(di);
   }
-  if (!(checkmask & 
-       (1L << SILC_CONFIG_SERVER_SECTION_TYPE_CLIENT_CONNECTION))) {
-    
-    return FALSE;
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigHmac, config->hmac)
+    silc_free(di->name);
+    silc_free(di->hash);
+    silc_free(di);
   }
-  if (!(checkmask 
-       & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_SERVER_CONNECTION))) {
-    
-    return FALSE;
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigPkcs, config->pkcs)
+    silc_free(di->name);
+    silc_free(di);
   }
-  if (!(checkmask 
-       & (1L << SILC_CONFIG_SERVER_SECTION_TYPE_ROUTER_CONNECTION))) {
-    
-    return FALSE;
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigConnParams,
+                                  config->conn_params)
+    silc_free(di->name);
+    silc_free(di->version_protocol);
+    silc_free(di->version_software);
+    silc_free(di->version_software_vendor);
+    silc_free(di);
   }
-
-  return TRUE;
-}
-
-/* Sets log files where log messages is saved by the server. */
-
-void silc_server_config_setlogfiles(SilcServerConfig config, SilcSchedule sked)
-{
-  SilcServerConfigSectionLogging *log;
-
-  SILC_LOG_DEBUG(("Setting configured log file names"));
-  log = config->logging;
-  while (log) {
-    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_INFO))
-      silc_log_set_file(SILC_LOG_INFO, log->filename, log->maxsize, sked);
-    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_WARNING))
-      silc_log_set_file(SILC_LOG_WARNING, log->filename, log->maxsize, sked);
-    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_ERROR))
-      silc_log_set_file(SILC_LOG_ERROR, log->filename, log->maxsize, sked);
-    if (!strcmp(log->logtype, SILC_CONFIG_SERVER_LF_FATAL))
-      silc_log_set_file(SILC_LOG_FATAL, log->filename, log->maxsize, sked);
-
-    log = log->next;
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigClient, config->clients)
+    silc_free(di->host);
+    CONFIG_FREE_AUTH(di);
+    silc_free(di);
+  }
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigAdmin, config->admins)
+    silc_free(di->host);
+    silc_free(di->user);
+    silc_free(di->nick);
+    CONFIG_FREE_AUTH(di);
+    silc_free(di);
   }
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigDeny, config->denied)
+    silc_free(di->host);
+    silc_free(di->reason);
+    silc_free(di);
+  }
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigServer,
+                                 config->servers)
+    silc_free(di->host);
+    CONFIG_FREE_AUTH(di);
+    silc_free(di);
+  }
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigRouter,
+                                 config->routers)
+    silc_free(di->host);
+    silc_free(di->backup_replace_ip);
+    CONFIG_FREE_AUTH(di);
+    silc_free(di);
+  }
+
+  memset(config, 'F', sizeof(*config));
+  silc_free(config);
 }
 
 /* Registers configured ciphers. These can then be allocated by the
    server when needed. */
 
-bool silc_server_config_register_ciphers(SilcServerConfig config)
+SilcBool silc_server_config_register_ciphers(SilcServer server)
 {
-  SilcServerConfigSectionAlg *alg;
-  SilcServer server = (SilcServer)config->server;
+  SilcServerConfig config = server->config;
+  SilcServerConfigCipher *cipher = config->cipher;
+  char *module_path = config->module_path;
 
   SILC_LOG_DEBUG(("Registering configured ciphers"));
 
-  if (!config->cipher)
+  if (!cipher) /* any cipher in the config file? */
     return FALSE;
 
-  alg = config->cipher;
-  while(alg) {
-
-    if (!alg->sim_name) {
+  while (cipher) {
+    /* if there isn't a module_path OR there isn't a module sim name try to
+     * use buil-in functions */
+    if (!module_path || !cipher->module) {
       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]);
+       if (!strcmp(silc_default_ciphers[i].name, cipher->name)) {
+         silc_cipher_register((SilcCipherObject *)&silc_default_ciphers[i]);
          break;
        }
-      
-      if (!silc_cipher_is_supported(alg->alg_name)) {
-       SILC_LOG_ERROR(("Unknown cipher `%s'", alg->alg_name));
-       silc_server_stop(server);
-       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 list of all SIM's in server */
-       silc_dlist_add(server->sim, sim);
-
-       silc_free(alg_name);
-      } else {
-       SILC_LOG_ERROR(("Error configuring ciphers"));
+      if (!silc_cipher_is_supported(cipher->name)) {
+       SILC_LOG_ERROR(("Unknown cipher `%s'", cipher->name));
        silc_server_stop(server);
        exit(1);
       }
-
-      /* Register the cipher */
-      silc_cipher_register(&cipher);
-#endif
-    }
-
-    alg = alg->next;
-  }
-
-  return TRUE;
-}
-
-/* Registers configured PKCS's. */
-
-bool silc_server_config_register_pkcs(SilcServerConfig config)
-{
-  SilcServerConfigSectionAlg *alg = config->pkcs;
-  SilcServer server = (SilcServer)config->server;
-
-  SILC_LOG_DEBUG(("Registering configured PKCS"));
-
-  if (!config->pkcs)
-    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_server_stop(server);
-      exit(1);
     }
-
-    alg = alg->next;
-  }
+    cipher = cipher->next;
+  } /* while */
 
   return TRUE;
 }
@@ -1368,82 +1755,35 @@ bool silc_server_config_register_pkcs(SilcServerConfig config)
 /* Registers configured hash functions. These can then be allocated by the
    server when needed. */
 
-bool silc_server_config_register_hashfuncs(SilcServerConfig config)
+SilcBool silc_server_config_register_hashfuncs(SilcServer server)
 {
-  SilcServerConfigSectionAlg *alg;
-  SilcServer server = (SilcServer)config->server;
+  SilcServerConfig config = server->config;
+  SilcServerConfigHash *hash = config->hash;
+  char *module_path = config->module_path;
 
   SILC_LOG_DEBUG(("Registering configured hash functions"));
 
-  if (!config->hash_func)
+  if (!hash) /* any hash func in the config file? */
     return FALSE;
 
-  alg = config->hash_func;
-  while(alg) {
-
-    if (!alg->sim_name) {
+  while (hash) {
+    /* if there isn't a module_path OR there isn't a module sim name try to
+     * use buil-in functions */
+    if (!module_path || !hash->module) {
       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]);
+       if (!strcmp(silc_default_hash[i].name, hash->name)) {
+         silc_hash_register((SilcHashObject *)&silc_default_hash[i]);
          break;
        }
-      
-      if (!silc_hash_is_supported(alg->alg_name)) {
-       SILC_LOG_ERROR(("Unknown hash funtion `%s'", alg->alg_name));
+      if (!silc_hash_is_supported(hash->name)) {
+       SILC_LOG_ERROR(("Unknown hash funtion `%s'", hash->name));
        silc_server_stop(server);
        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 server */
-       silc_dlist_add(server->sim, sim);
-      } else {
-       SILC_LOG_ERROR(("Error configuring hash functions"));
-       silc_server_stop(server);
-       exit(1);
-      }
-
-      /* Register the hash function */
-      silc_hash_register(&hash);
-#endif
     }
-
-    alg = alg->next;
-  }
+    hash = hash->next;
+  } /* while */
 
   return TRUE;
 }
@@ -1451,88 +1791,157 @@ bool silc_server_config_register_hashfuncs(SilcServerConfig config)
 /* Registers configure HMACs. These can then be allocated by the server
    when needed. */
 
-bool silc_server_config_register_hmacs(SilcServerConfig config)
+SilcBool silc_server_config_register_hmacs(SilcServer server)
 {
-  SilcServerConfigSectionAlg *alg;
-  SilcServer server = (SilcServer)config->server;
+  SilcServerConfig config = server->config;
+  SilcServerConfigHmac *hmac = config->hmac;
 
   SILC_LOG_DEBUG(("Registering configured HMACs"));
 
-  if (!config->hmac)
+  if (!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'", alg->sim_name));
+  while (hmac) {
+    SilcHmacObject hmac_obj;
+    if (!silc_hash_is_supported(hmac->hash)) {
+      SILC_LOG_ERROR(("Unknown hash function `%s'", hmac->hash));
       silc_server_stop(server);
       exit(1);
     }
-    
+
     /* Register the HMAC */
-    memset(&hmac, 0, sizeof(hmac));
-    hmac.name = alg->alg_name;
-    hmac.len = alg->key_len;
-    silc_hmac_register(&hmac);
+    memset(&hmac_obj, 0, sizeof(hmac_obj));
+    hmac_obj.name = hmac->name;
+    hmac_obj.len = hmac->mac_length;
+    silc_hmac_register(&hmac_obj);
 
-    alg = alg->next;
-  }
+    hmac = hmac->next;
+  } /* while */
 
   return TRUE;
 }
 
-/* Returns client authentication information from server configuration
-   by host (name or ip). If `port' is non-null then both name or IP and 
-   the port must match. */
+/* Registers configured PKCS's. */
 
-SilcServerConfigSectionClientConnection *
-silc_server_config_find_client_conn(SilcServerConfig config, 
-                                   char *host, int port)
+SilcBool silc_server_config_register_pkcs(SilcServer server)
 {
-  int i;
-  SilcServerConfigSectionClientConnection *client = NULL;
-  bool match = FALSE;
+  return FALSE;
+}
 
-  if (!host)
-    return NULL;
+/* Sets log files where log messages are saved by the server logger. */
 
-  if (!config->clients)
+void silc_server_config_setlogfiles(SilcServer server)
+{
+  SilcServerConfig config = server->config;
+  SilcServerConfigLogging *this;
+
+  SILC_LOG_DEBUG(("Setting configured log file names and options"));
+
+  silc_log_timestamp(config->logging_timestamp);
+  silc_log_quick(config->logging_quick);
+  silc_log_flushdelay(config->logging_flushdelay ?
+                     config->logging_flushdelay :
+                     SILC_SERVER_LOG_FLUSH_DELAY);
+
+  if ((this = config->logging_fatals))
+    silc_log_set_file(SILC_LOG_FATAL, this->file, this->maxsize,
+                     server->schedule);
+  if ((this = config->logging_errors))
+    silc_log_set_file(SILC_LOG_ERROR, this->file, this->maxsize,
+                     server->schedule);
+  if ((this = config->logging_warnings))
+    silc_log_set_file(SILC_LOG_WARNING, this->file, this->maxsize,
+                     server->schedule);
+  if ((this = config->logging_info))
+    silc_log_set_file(SILC_LOG_INFO, this->file, this->maxsize,
+                     server->schedule);
+}
+
+/* Returns client authentication information from configuration file by host
+   (name or ip) */
+
+SilcServerConfigClient *
+silc_server_config_find_client(SilcServer server, char *host)
+{
+  SilcServerConfig config = server->config;
+  SilcServerConfigClient *client;
+
+  if (!config || !host)
     return NULL;
 
-  client = config->clients;
+  for (client = config->clients; client; client = client->next) {
+    if (client->host && !silc_string_compare(client->host, host))
+      continue;
+    break;
+  }
+
+  /* if none matched, then client is already NULL */
+  return client;
+}
 
-  for (i = 0; client; i++) {
-    if (silc_string_compare(client->host, host))
-      match = TRUE;
+/* Returns admin connection configuration by host, username and/or
+   nickname. */
 
-    if (port && client->port && client->port != port)
-      match = FALSE;
+SilcServerConfigAdmin *
+silc_server_config_find_admin(SilcServer server, char *host, char *user,
+                             char *nick)
+{
+  SilcServerConfig config = server->config;
+  SilcServerConfigAdmin *admin;
 
-    if (match)
-      break;
+  /* make sure we have a value for the matching parameters */
+  if (!host)
+    host = "*";
+  if (!user)
+    user = "*";
+  if (!nick)
+    nick = "*";
 
-    client = client->next;
+  for (admin = config->admins; admin; admin = admin->next) {
+    if (admin->host && !silc_string_compare(admin->host, host))
+      continue;
+    if (admin->user && !silc_string_compare(admin->user, user))
+      continue;
+    if (admin->nick && !silc_string_compare(admin->nick, nick))
+      continue;
+    /* no checks failed -> this entry matches */
+    break;
   }
 
-  if (!client)
+  /* if none matched, then admin is already NULL */
+  return admin;
+}
+
+/* Returns the denied connection configuration entry by host. */
+
+SilcServerConfigDeny *
+silc_server_config_find_denied(SilcServer server, char *host)
+{
+  SilcServerConfig config = server->config;
+  SilcServerConfigDeny *deny;
+
+  /* make sure we have a value for the matching parameters */
+  if (!config || !host)
     return NULL;
 
-  return client;
+  for (deny = config->denied; deny; deny = deny->next) {
+    if (deny->host && !silc_string_compare(deny->host, host))
+      continue;
+    break;
+  }
+
+  /* if none matched, then deny is already NULL */
+  return deny;
 }
 
-/* Returns server connection info from server configuartion by host 
-   (name or ip). If `port' is non-null then both name or IP and the port
-   must match. */
+/* Returns server connection info from server configuartion by host
+   (name or ip). */
 
-SilcServerConfigSectionServerConnection *
-silc_server_config_find_server_conn(SilcServerConfig config, 
-                                   char *host, int port)
+SilcServerConfigServer *
+silc_server_config_find_server_conn(SilcServer server, char *host)
 {
-  int i;
-  SilcServerConfigSectionServerConnection *serv = NULL;
-  bool match = FALSE;
+  SilcServerConfig config = server->config;
+  SilcServerConfigServer *serv = NULL;
 
   if (!host)
     return NULL;
@@ -1540,36 +1949,23 @@ silc_server_config_find_server_conn(SilcServerConfig config,
   if (!config->servers)
     return NULL;
 
-  serv = config->servers;
-  for (i = 0; serv; i++) {
-    if (silc_string_compare(serv->host, host))
-      match = TRUE;
-
-    if (port && serv->port && serv->port != port)
-      match = FALSE;
-
-    if (match)
-      break;
-
-    serv = serv->next;
+  for (serv = config->servers; serv; serv = serv->next) {
+    if (!silc_string_compare(serv->host, host))
+      continue;
+    break;
   }
 
-  if (!serv)
-    return NULL;
-
   return serv;
 }
 
-/* Returns router connection info from server configuartion by
+/* Returns router connection info from server configuration by
    host (name or ip). */
 
-SilcServerConfigSectionServerConnection *
-silc_server_config_find_router_conn(SilcServerConfig config, 
-                                   char *host, int port)
+SilcServerConfigRouter *
+silc_server_config_find_router_conn(SilcServer server, char *host, int port)
 {
-  int i;
-  SilcServerConfigSectionServerConnection *serv = NULL;
-  bool match = FALSE;
+  SilcServerConfig config = server->config;
+  SilcServerConfigRouter *serv = NULL;
 
   if (!host)
     return NULL;
@@ -1577,34 +1973,51 @@ silc_server_config_find_router_conn(SilcServerConfig config,
   if (!config->routers)
     return NULL;
 
-  serv = config->routers;
-  for (i = 0; serv; i++) {
-    if (silc_string_compare(serv->host, host))
-      match = TRUE;
-
+  for (serv = config->routers; serv; serv = serv->next) {
+    if (!silc_string_compare(serv->host, host))
+      continue;
     if (port && serv->port && serv->port != port)
-      match = FALSE;
+      continue;
+    break;
+  }
 
-    if (match)
-      break;
+  return serv;
+}
 
-    serv = serv->next;
-  }
+/* Find backup router connection by host (name or ip) */
+
+SilcServerConfigRouter *
+silc_server_config_find_backup_conn(SilcServer server, char *host)
+{
+  SilcServerConfig config = server->config;
+  SilcServerConfigRouter *serv = NULL;
+
+  if (!host)
+    return NULL;
 
-  if (!serv)
+  if (!config->routers)
     return NULL;
 
+  for (serv = config->routers; serv; serv = serv->next) {
+    if (!serv->backup_router)
+      continue;
+    if (!silc_string_compare(serv->host, host))
+      continue;
+    break;
+  }
+
   return serv;
 }
 
-/* Returns TRUE if configuartion for a router connection that we are 
+/* Returns TRUE if configuration for a router connection that we are
    initiating exists. */
 
-bool silc_server_config_is_primary_route(SilcServerConfig config)
+SilcBool silc_server_config_is_primary_route(SilcServer server)
 {
+  SilcServerConfig config = server->config;
+  SilcServerConfigRouter *serv = NULL;
   int i;
-  SilcServerConfigSectionServerConnection *serv = NULL;
-  bool found = FALSE;
+  SilcBool found = FALSE;
 
   serv = config->routers;
   for (i = 0; serv; i++) {
@@ -1622,11 +2035,12 @@ bool silc_server_config_is_primary_route(SilcServerConfig config)
 /* Returns our primary connection configuration or NULL if we do not
    have primary router configured. */
 
-SilcServerConfigSectionServerConnection *
-silc_server_config_get_primary_router(SilcServerConfig config)
+SilcServerConfigRouter *
+silc_server_config_get_primary_router(SilcServer server)
 {
+  SilcServerConfig config = server->config;
+  SilcServerConfigRouter *serv = NULL;
   int i;
-  SilcServerConfigSectionServerConnection *serv = NULL;
 
   serv = config->routers;
   for (i = 0; serv; i++) {
@@ -1638,74 +2052,30 @@ silc_server_config_get_primary_router(SilcServerConfig config)
   return NULL;
 }
 
-/* Returns Admin connection configuration by host, username and/or 
-   nickname. */
-
-SilcServerConfigSectionAdminConnection *
-silc_server_config_find_admin(SilcServerConfig config,
-                             char *host, char *username, char *nickname)
-{
-  SilcServerConfigSectionAdminConnection *admin = NULL;
-  int i;
-
-  if (!config->admins)
-    return NULL;
-
-  if (!host)
-    host = "*";
-  if (!username)
-    username = "*";
-  if (!nickname)
-    nickname = "*";
-
-  admin = config->admins;
-  for (i = 0; admin; i++) {
-    if (silc_string_compare(admin->host, host) &&
-       silc_string_compare(admin->username, username) &&
-       silc_string_compare(admin->nickname, nickname))
-      break;
-
-    admin = admin->next;
-  }
-
-  if (!admin)
-    return NULL;
-
-  return admin;
-}
-
-/* Returns the Denied connection configuration by host and port. */
+/* If we have backup router configured that is going to replace us this
+   function returns it. */
 
-SilcServerConfigSectionDenyConnection *
-silc_server_config_denied_conn(SilcServerConfig config, char *host,
-                              int port)
+SilcServerConfigRouter *
+silc_server_config_get_backup_router(SilcServer server)
 {
+  SilcServerConfig config = server->config;
+  SilcServerConfigRouter *serv = NULL;
   int i;
-  SilcServerConfigSectionDenyConnection *deny = NULL;
-  bool match = FALSE;
-
-  if (!host)
-    return NULL;
 
-  if (!config->denied)
+  if (server->server_type != SILC_ROUTER)
     return NULL;
 
-  deny = config->denied;
-  for (i = 0; deny; i++) {
-    if (silc_string_compare(deny->host, host))
-      match = TRUE;
-
-    if (port && deny->port && deny->port != port)
-      match = FALSE;
-
-    if (match)
-      break;
-
-    deny = deny->next;
+  serv = config->routers;
+  for (i = 0; serv; i++) {
+    if (serv->initiator == FALSE && serv->backup_router == TRUE &&
+       serv->backup_local == TRUE &&
+       !strcmp(server->config->server_info->primary->server_ip,
+               serv->backup_replace_ip) &&
+       server->config->server_info->primary->port ==
+       serv->backup_replace_port)
+      return serv;
+    serv = serv->next;
   }
 
-  if (!deny)
-    return NULL;
-
-  return deny;
+  return NULL;
 }