Merged c0ffee's patch.
[silc.git] / apps / silcd / serverconfig.c
index 2e4773c77ffdb465725a5140570cfa873b5816f6..3a8005324a44bce23b9e62630df7343475d0005c 100644 (file)
@@ -4,7 +4,7 @@
 
   Author: Johnny Mnemonic <johnny@themnemonic.org>
 
-  Copyright (C) 1997 - 2002 Johnny Mnemonic
+  Copyright (C) 1997 - 2002 Pekka Riikonen
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   __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;                                    \
+  }
+
 /* append the tmp field to the specified list */
 #define SILC_SERVER_CONFIG_LIST_APPENDTMP(__list__)                    \
   if (!__list__) {                                                     \
@@ -70,33 +77,28 @@ static void my_free_public_key(void *key, void *context, void *user_data)
 }
 
 /* Set default values to those parameters that have not been defined */
-static void 
+static void
 my_set_param_defaults(SilcServerConfigConnParams *params,
                      SilcServerConfigConnParams *defaults)
 {
-#define SET_PARAM_DEFAULT(p, d)                                                \
+#define SET_PARAM_DEFAULT(p, d)        params->p =                             \
   (params->p ? params->p : (defaults && defaults->p ? defaults->p : d))
 
-  params->connections_max = 
-    SET_PARAM_DEFAULT(connections_max, SILC_SERVER_MAX_CONNECTIONS);
-  params->connections_max_per_host = 
-    SET_PARAM_DEFAULT(connections_max_per_host, 
-                     SILC_SERVER_MAX_CONNECTIONS_SINGLE);
-  params->keepalive_secs = 
-    SET_PARAM_DEFAULT(keepalive_secs, SILC_SERVER_KEEPALIVE);
-  params->reconnect_count = 
-    SET_PARAM_DEFAULT(reconnect_count, SILC_SERVER_RETRY_COUNT);
-  params->reconnect_interval = 
-    SET_PARAM_DEFAULT(reconnect_interval, SILC_SERVER_RETRY_INTERVAL_MIN);
-  params->reconnect_interval_max = 
-    SET_PARAM_DEFAULT(reconnect_interval_max, SILC_SERVER_RETRY_INTERVAL_MAX);
-  params->key_exchange_rekey = 
-    SET_PARAM_DEFAULT(key_exchange_rekey, SILC_SERVER_REKEY);
+  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);
+
+#undef SET_PARAM_DEFAULT
 }
 
 /* Find connection parameters by the parameter block name. */
 static SilcServerConfigConnParams *
-my_find_param(SilcServerConfig config, const char *name, SilcUInt32 line)
+my_find_param(SilcServerConfig config, const char *name)
 {
   SilcServerConfigConnParams *param;
 
@@ -105,46 +107,54 @@ my_find_param(SilcServerConfig config, const char *name, SilcUInt32 line)
       return param;
   }
 
-  fprintf(stderr, "\nError while parsing config file at line %lu: "
-         "Cannot find Param \"%s\".\n", line, name);
+  SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                        "Cannot find Params \"%s\".\n", name));
 
   return NULL;
 }
 
 /* parse an authdata according to its auth method */
-static bool my_parse_authdata(SilcAuthMethod auth_meth, char *p, 
-                             SilcUInt32 line, void **auth_data, 
+static bool my_parse_authdata(SilcAuthMethod auth_meth, char *p,
+                             SilcUInt32 line, void **auth_data,
                              SilcUInt32 *auth_data_len)
 {
   if (auth_meth == SILC_AUTH_PASSWORD) {
     /* p is a plain text password */
-    if (auth_data)
-      *auth_data = (void *) strdup(p);
-    if (auth_data_len)
-      *auth_data_len = (SilcUInt32) strlen(p);
+    if (auth_data && auth_data_len) {
+      if (!silc_utf8_valid(p, strlen(p))) {
+       *auth_data_len = silc_utf8_encoded_len(p, strlen(p), 0);
+       *auth_data = silc_calloc(*auth_data_len, sizeof(unsigned char));
+       silc_utf8_encode(p, strlen(p), SILC_STRING_ASCII, *auth_data,
+                        *auth_data_len);
+      } else {
+       *auth_data = (void *) strdup(p);
+       *auth_data_len = (SilcUInt32) strlen(p);
+      }
+    }
   } else if (auth_meth == SILC_AUTH_PUBLIC_KEY) {
     /* p is a public key file name */
     SilcPublicKey public_key;
 
     if (!silc_pkcs_load_public_key(p, &public_key, SILC_PKCS_FILE_PEM))
       if (!silc_pkcs_load_public_key(p, &public_key, SILC_PKCS_FILE_BIN)) {
-       fprintf(stderr, "\nError while parsing config file at line %lu: "
-               "Could not load public key file!\n", line);
+       SILC_SERVER_LOG_ERROR(("\nError while parsing config file at line "
+                              "%lu: Could not load public key file!\n",
+                              line));
        return FALSE;
       }
 
     /* The auth_data is a pointer to the hash table of public keys. */
     if (auth_data) {
       if (*auth_data == NULL)
-       *auth_data = silc_hash_table_alloc(1, silc_hash_public_key, NULL, 
-                                          NULL, NULL, 
-                                          my_free_public_key, NULL, 
+       *auth_data = silc_hash_table_alloc(1, silc_hash_public_key, NULL,
+                                          NULL, NULL,
+                                          my_free_public_key, NULL,
                                           TRUE);
       silc_hash_table_add(*auth_data, public_key, public_key);
     }
   } else {
-    fprintf(stderr, "\nError while parsing config file at line %lu: "
-           "Unknown authentication method.\n", line);
+    SILC_SERVER_LOG_ERROR(("\nError while parsing config file at line %lu: "
+                          "Unknown authentication method.\n", line));
     return FALSE;
   }
   return TRUE;
@@ -203,6 +213,27 @@ SILC_CONFIG_CALLBACK(fetch_generic)
   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 = *(bool *)val;
+  }
+  else if (!strcmp(name, "detach_timeout")) {
+    config->detach_timeout = (SilcUInt32) *(int *)val;
+  }
   else
     return SILC_CONFIG_EINTERNAL;
 
@@ -220,22 +251,18 @@ SILC_CONFIG_CALLBACK(fetch_cipher)
                       type, name, context));
   if (type == SILC_CONFIG_ARG_BLOCK) {
     /* check the temporary struct's fields */
-    if (!tmp) /* empty sub-block? */
+    if (!tmp) /* discard empty sub-blocks */
       return SILC_CONFIG_OK;
     if (!tmp->name) {
       got_errno = SILC_CONFIG_EMISSFIELDS;
       goto got_err;
     }
-    /* the temporary struct is ok, append it to the list */
+
     SILC_SERVER_CONFIG_LIST_APPENDTMP(config->cipher);
     config->tmp = NULL;
     return SILC_CONFIG_OK;
   }
-  /* if there isn't a temporary struct alloc one */
-  if (!tmp) {
-    config->tmp = silc_calloc(1, sizeof(*findtmp));
-    tmp = (SilcServerConfigCipher *) config->tmp;
-  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigCipher);
 
   /* Identify and save this value */
   if (!strcmp(name, "name")) {
@@ -272,23 +299,18 @@ SILC_CONFIG_CALLBACK(fetch_hash)
                       type, name, context));
   if (type == SILC_CONFIG_ARG_BLOCK) {
     /* check the temporary struct's fields */
-    if (!tmp) /* empty sub-block? */
+    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;
     }
-    /* the temporary struct in tmp is ok */
+
     SILC_SERVER_CONFIG_LIST_APPENDTMP(config->hash);
     config->tmp = NULL;
     return SILC_CONFIG_OK;
   }
-
-  /* if there isn't a temporary struct alloc one */
-  if (!tmp) {
-    config->tmp = silc_calloc(1, sizeof(*findtmp));
-    tmp = (SilcServerConfigHash *) config->tmp;
-  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigHash);
 
   /* Identify and save this value */
   if (!strcmp(name, "name")) {
@@ -325,22 +347,18 @@ SILC_CONFIG_CALLBACK(fetch_hmac)
                       type, name, context));
   if (type == SILC_CONFIG_ARG_BLOCK) {
     /* check the temporary struct's fields */
-    if (!tmp) /* empty sub-block? */
+    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;
     }
-    /* the temporary struct is ok, append it to the list */
+
     SILC_SERVER_CONFIG_LIST_APPENDTMP(config->hmac);
     config->tmp = NULL;
     return SILC_CONFIG_OK;
   }
-  /* if there isn't a temporary struct alloc one */
-  if (!tmp) {
-    config->tmp = silc_calloc(1, sizeof(*findtmp));
-    tmp = (SilcServerConfigHmac *) config->tmp;
-  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigHmac);
 
   /* Identify and save this value */
   if (!strcmp(name, "name")) {
@@ -370,26 +388,22 @@ SILC_CONFIG_CALLBACK(fetch_pkcs)
 {
   SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigPkcs);
 
-  SERVER_CONFIG_DEBUG(("Received PKCS type=%d name=\"%s\" (val=%x)", 
+  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) /* empty sub-block? */
+    if (!tmp) /* discard empty sub-blocks */
       return SILC_CONFIG_OK;
     if (!tmp->name) {
       got_errno = SILC_CONFIG_EMISSFIELDS;
       goto got_err;
     }
-    /* the temporary struct is ok, append it to the list */
+
     SILC_SERVER_CONFIG_LIST_APPENDTMP(config->pkcs);
     config->tmp = NULL;
     return SILC_CONFIG_OK;
   }
-  /* if there isn't a temporary struct alloc one */
-  if (!tmp) {
-    config->tmp = silc_calloc(1, sizeof(*findtmp));
-    tmp = (SilcServerConfigPkcs *) config->tmp;
-  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigPkcs);
 
   /* Identify and save this value */
   if (!strcmp(name, "name")) {
@@ -409,9 +423,8 @@ SILC_CONFIG_CALLBACK(fetch_pkcs)
 
 SILC_CONFIG_CALLBACK(fetch_serverinfo)
 {
-  SilcServerConfig config = (SilcServerConfig) context;
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigServerInfoInterface);
   SilcServerConfigServerInfo *server_info = config->server_info;
-  int got_errno = 0;
 
   /* if there isn't the struct alloc it */
   if (!server_info)
@@ -419,7 +432,23 @@ SILC_CONFIG_CALLBACK(fetch_serverinfo)
                silc_calloc(1, sizeof(*server_info));
 
   if (type == SILC_CONFIG_ARG_BLOCK) {
-    /* check for mandatory inputs */
+    if (!strcmp(name, "primary")) {
+      CONFIG_IS_DOUBLE(server_info->primary);
+      if (!tmp)
+       return SILC_CONFIG_OK;
+      server_info->primary = tmp;
+      config->tmp = NULL;
+      return SILC_CONFIG_OK;
+    } else if (!strcmp(name, "secondary")) {
+      if (!tmp)
+       return SILC_CONFIG_OK;
+      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")) {
@@ -427,16 +456,20 @@ SILC_CONFIG_CALLBACK(fetch_serverinfo)
     server_info->server_name = strdup((char *) val);
   }
   else if (!strcmp(name, "ip")) {
-    CONFIG_IS_DOUBLE(server_info->server_ip);
-    server_info->server_ip = strdup((char *) val);
+    SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigServerInfoInterface);
+    CONFIG_IS_DOUBLE(tmp->server_ip);
+    tmp->server_ip = strdup((char *) val);
   }
   else if (!strcmp(name, "port")) {
     int port = *(int *)val;
+    SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigServerInfoInterface);
     if ((port <= 0) || (port > 65535)) {
-      fprintf(stderr, "Invalid port number!\n");
-      return SILC_CONFIG_ESILENT;
+      SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                            "Invalid port number!\n"));
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
     }
-    server_info->port = (SilcUInt16) port;
+    tmp->port = (SilcUInt16) port;
   }
   else if (!strcmp(name, "servertype")) {
     CONFIG_IS_DOUBLE(server_info->server_type);
@@ -471,28 +504,28 @@ SILC_CONFIG_CALLBACK(fetch_serverinfo)
     server_info->pid_file = strdup((char *) val);
   }
   else if (!strcmp(name, "publickey")) {
-    char *tmp = (char *) val;
+    char *file_tmp = (char *) val;
 
     /* try to load specified file, if fail stop config parsing */
-    if (!silc_pkcs_load_public_key(tmp, &server_info->public_key,
+    if (!silc_pkcs_load_public_key(file_tmp, &server_info->public_key,
                                   SILC_PKCS_FILE_PEM))
-      if (!silc_pkcs_load_public_key(tmp, &server_info->public_key,
+      if (!silc_pkcs_load_public_key(file_tmp, &server_info->public_key,
                                     SILC_PKCS_FILE_BIN)) {
-       fprintf(stderr, "\nError: Could not load public key file.");
-       fprintf(stderr, "\n  line %lu: file \"%s\"\n", line, tmp);
+       SILC_SERVER_LOG_ERROR(("Error: Could not load public key file.\n"));
+       SILC_SERVER_LOG_ERROR(("   line %lu, file \"%s\"\n", line, file_tmp));
        return SILC_CONFIG_ESILENT;
       }
   }
   else if (!strcmp(name, "privatekey")) {
-    char *tmp = (char *) val;
+    char *file_tmp = (char *) val;
 
     /* try to load specified file, if fail stop config parsing */
-    if (!silc_pkcs_load_private_key(tmp, &server_info->private_key,
+    if (!silc_pkcs_load_private_key(file_tmp, &server_info->private_key,
                                    SILC_PKCS_FILE_BIN))
-      if (!silc_pkcs_load_private_key(tmp, &server_info->private_key,
+      if (!silc_pkcs_load_private_key(file_tmp, &server_info->private_key,
                                      SILC_PKCS_FILE_PEM)) {
-       fprintf(stderr, "\nError: Could not load private key file.");
-       fprintf(stderr, "\n  line %lu: file \"%s\"\n", line, tmp);
+       SILC_SERVER_LOG_ERROR(("Error: Could not load private key file.\n"));
+       SILC_SERVER_LOG_ERROR(("   line %lu, file \"%s\"\n", line, file_tmp));
        return SILC_CONFIG_ESILENT;
       }
   }
@@ -501,28 +534,35 @@ SILC_CONFIG_CALLBACK(fetch_serverinfo)
   return SILC_CONFIG_OK;
 
  got_err:
+  silc_free(tmp);
+  silc_free(config->tmp);
+  config->tmp = NULL;
   return got_errno;
 }
 
 SILC_CONFIG_CALLBACK(fetch_logging)
 {
-  SilcServerConfig config = (SilcServerConfig) context;
-  SilcServerConfigLogging *tmp =
-       (SilcServerConfigLogging *) config->tmp;
-  int got_errno;
+  SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigLogging);
 
   if (!strcmp(name, "quicklogs")) {
-    silc_log_quick = *(bool *)val;
+    config->logging_quick = *(bool *)val;
   }
   else if (!strcmp(name, "flushdelay")) {
     int flushdelay = *(int *)val;
     if (flushdelay < 2) { /* this value was taken from silclog.h (min delay) */
-      fprintf(stderr, "Error: line %lu: invalid flushdelay value, use "
-               "quicklogs if you want real-time logging.\n", line);
-      return SILC_CONFIG_ESILENT;
+      SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                           "Invalid flushdelay value, use quicklogs if you "
+                           "want real-time logging.\n"));
+      return SILC_CONFIG_EPRINTLINE;
     }
-    silc_log_flushdelay = (long) flushdelay;
+    config->logging_flushdelay = (long) flushdelay;
   }
+
+  /* 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;                           \
@@ -538,13 +578,8 @@ SILC_CONFIG_CALLBACK(fetch_logging)
   FETCH_LOGGING_CHAN("fatals", logging_fatals)
 #undef FETCH_LOGGING_CHAN
   else if (!strcmp(name, "file")) {
-    if (!tmp) { /* FIXME: what the fuck is this? */
-      config->tmp = silc_calloc(1, sizeof(*tmp));
-      tmp = (SilcServerConfigLogging *) config->tmp;
-    }
-    if (tmp->file) {
-      got_errno = SILC_CONFIG_EMISSFIELDS; goto got_err;
-    }
+    SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigLogging);
+    CONFIG_IS_DOUBLE(tmp->file);
     tmp->file = strdup((char *) val);
   }
   else if (!strcmp(name, "size")) {
@@ -569,18 +604,16 @@ SILC_CONFIG_CALLBACK(fetch_connparam)
 {
   SILC_SERVER_CONFIG_SECTION_INIT(SilcServerConfigConnParams);
 
-  SERVER_CONFIG_DEBUG(("Received CONNPARAM type=%d name=\"%s\" (val=%x)", 
+  SERVER_CONFIG_DEBUG(("Received CONNPARAM type=%d name=\"%s\" (val=%x)",
                       type, name, context));
-
   if (type == SILC_CONFIG_ARG_BLOCK) {
-    if (!tmp)
+    /* 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);
 
@@ -588,12 +621,7 @@ SILC_CONFIG_CALLBACK(fetch_connparam)
     config->tmp = NULL;
     return SILC_CONFIG_OK;
   }
-
-  /* if there isn't a temporary struct alloc one */
-  if (!tmp) {
-    config->tmp = silc_calloc(1, sizeof(*findtmp));
-    tmp = (SilcServerConfigConnParams *) config->tmp;
-  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigConnParams);
 
   if (!strcmp(name, "name")) {
     CONFIG_IS_DOUBLE(tmp->name);
@@ -626,6 +654,19 @@ SILC_CONFIG_CALLBACK(fetch_connparam)
   else if (!strcmp(name, "key_exchange_pfs")) {
     tmp->key_exchange_pfs = *(bool *)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
     return SILC_CONFIG_EINTERNAL;
 
@@ -645,14 +686,11 @@ SILC_CONFIG_CALLBACK(fetch_client)
   SERVER_CONFIG_DEBUG(("Received CLIENT type=%d name=\"%s\" (val=%x)",
                       type, name, context));
 
-  /* alloc tmp before block checking (empty sub-blocks are welcome here) */
-  if (!tmp) {
-    config->tmp = silc_calloc(1, sizeof(*findtmp));
-    tmp = (SilcServerConfigClient *) config->tmp;
-  }
+  /* Alloc before block checking, because empty sub-blocks are welcome here */
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigClient);
 
   if (type == SILC_CONFIG_ARG_BLOCK) {
-    /* closing the block */
+    /* empty sub-blocks are welcome */
     SILC_SERVER_CONFIG_LIST_APPENDTMP(config->clients);
     config->tmp = NULL;
     return SILC_CONFIG_OK;
@@ -681,9 +719,9 @@ SILC_CONFIG_CALLBACK(fetch_client)
   }
   else if (!strcmp(name, "params")) {
     CONFIG_IS_DOUBLE(tmp->param);
-    tmp->param = my_find_param(config, (char *) val, line);
-    if (!tmp->param) { /* error already output */
-      got_errno = SILC_CONFIG_ESILENT;
+    tmp->param = my_find_param(config, (char *) val);
+    if (!tmp->param) { /* error message already output */
+      got_errno = SILC_CONFIG_EPRINTLINE;
       goto got_err;
     }
   }
@@ -705,22 +743,16 @@ SILC_CONFIG_CALLBACK(fetch_admin)
 
   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) /* empty sub-block? */
+    if (!tmp) /* discard empty sub-blocks */
       return SILC_CONFIG_OK;
 
     SILC_SERVER_CONFIG_LIST_APPENDTMP(config->admins);
     config->tmp = NULL;
     return SILC_CONFIG_OK;
   }
-
-  /* if there isn't a temporary struct alloc one */
-  if (!tmp) {
-    config->tmp = silc_calloc(1, sizeof(*findtmp));
-    tmp = (SilcServerConfigAdmin *) config->tmp;
-  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigAdmin);
 
   /* Identify and save this value */
   if (!strcmp(name, "host")) {
@@ -774,21 +806,18 @@ SILC_CONFIG_CALLBACK(fetch_deny)
                       type, name, context));
   if (type == SILC_CONFIG_ARG_BLOCK) {
     /* check the temporary struct's fields */
-    if (!tmp) /* empty sub-block? */
+    if (!tmp) /* discard empty sub-blocks */
       return SILC_CONFIG_OK;
     if (!tmp->reason) {
       got_errno = SILC_CONFIG_EMISSFIELDS;
       goto got_err;
     }
+
     SILC_SERVER_CONFIG_LIST_APPENDTMP(config->denied);
     config->tmp = NULL;
     return SILC_CONFIG_OK;
   }
-  /* if there isn't a temporary struct alloc one */
-  if (!tmp) {
-    config->tmp = silc_calloc(1, sizeof(*findtmp));
-    tmp = (SilcServerConfigDeny *) config->tmp;
-  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigDeny);
 
   /* Identify and save this value */
   if (!strcmp(name, "host")) {
@@ -817,10 +846,9 @@ SILC_CONFIG_CALLBACK(fetch_server)
 
   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) /* empty sub-block? */
+    if (!tmp) /* discard empty sub-blocks */
       return SILC_CONFIG_OK;
 
     /* the temporary struct is ok, append it to the list */
@@ -828,12 +856,7 @@ SILC_CONFIG_CALLBACK(fetch_server)
     config->tmp = NULL;
     return SILC_CONFIG_OK;
   }
-
-  /* if there isn't a temporary struct alloc one */
-  if (!tmp) {
-    config->tmp = silc_calloc(1, sizeof(*findtmp));
-    tmp = (SilcServerConfigServer *) config->tmp;
-  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigServer);
 
   /* Identify and save this value */
   if (!strcmp(name, "host")) {
@@ -857,15 +880,11 @@ SILC_CONFIG_CALLBACK(fetch_server)
       goto got_err;
     }
   }
-  else if (!strcmp(name, "versionid")) {
-    CONFIG_IS_DOUBLE(tmp->version);
-    tmp->version = strdup((char *) val);
-  }
   else if (!strcmp(name, "params")) {
     CONFIG_IS_DOUBLE(tmp->param);
-    tmp->param = my_find_param(config, (char *) val, line);
-    if (!tmp->param) { /* error already output */
-      got_errno = SILC_CONFIG_ESILENT;
+    tmp->param = my_find_param(config, (char *) val);
+    if (!tmp->param) { /* error message already output */
+      got_errno = SILC_CONFIG_EPRINTLINE;
       goto got_err;
     }
   }
@@ -879,7 +898,6 @@ SILC_CONFIG_CALLBACK(fetch_server)
 
  got_err:
   silc_free(tmp->host);
-  silc_free(tmp->version);
   CONFIG_FREE_AUTH(tmp);
   silc_free(tmp);
   config->tmp = NULL;
@@ -892,22 +910,15 @@ SILC_CONFIG_CALLBACK(fetch_router)
 
   SERVER_CONFIG_DEBUG(("Received ROUTER type=%d name=\"%s\" (val=%x)",
                       type, name, context));
-
   if (type == SILC_CONFIG_ARG_BLOCK) {
-    if (!tmp) /* empty sub-block? */
+    if (!tmp) /* discard empty sub-blocks */
       return SILC_CONFIG_OK;
 
-    /* the temporary struct is ok, append it to the list */
     SILC_SERVER_CONFIG_LIST_APPENDTMP(config->routers);
     config->tmp = NULL;
     return SILC_CONFIG_OK;
   }
-
-  /* if there isn't a temporary struct alloc one */
-  if (!tmp) {
-    config->tmp = silc_calloc(1, sizeof(*findtmp));
-    tmp = (SilcServerConfigRouter *) config->tmp;
-  }
+  SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigRouter);
 
   /* Identify and save this value */
   if (!strcmp(name, "host")) {
@@ -917,8 +928,10 @@ SILC_CONFIG_CALLBACK(fetch_router)
   else if (!strcmp(name, "port")) {
     int port = *(int *)val;
     if ((port <= 0) || (port > 65535)) {
-      fprintf(stderr, "Invalid port number!\n");
-      return SILC_CONFIG_ESILENT;
+      SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                            "Invalid port number!\n"));
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
     }
     tmp->port = (SilcUInt16) port;
   }
@@ -939,15 +952,11 @@ SILC_CONFIG_CALLBACK(fetch_router)
       goto got_err;
     }
   }
-  else if (!strcmp(name, "versionid")) {
-    CONFIG_IS_DOUBLE(tmp->version);
-    tmp->version = strdup((char *) val);
-  }
   else if (!strcmp(name, "params")) {
     CONFIG_IS_DOUBLE(tmp->param);
-    tmp->param = my_find_param(config, (char *) val, line);
-    if (!tmp->param) { /* error already output */
-      got_errno = SILC_CONFIG_ESILENT;
+    tmp->param = my_find_param(config, (char *) val);
+    if (!tmp->param) { /* error message already output */
+      got_errno = SILC_CONFIG_EPRINTLINE;
       goto got_err;
     }
   }
@@ -962,8 +971,10 @@ SILC_CONFIG_CALLBACK(fetch_router)
   else if (!strcmp(name, "backupport")) {
     int port = *(int *)val;
     if ((port <= 0) || (port > 65535)) {
-      fprintf(stderr, "Invalid port number!\n");
-      return SILC_CONFIG_ESILENT;
+      SILC_SERVER_LOG_ERROR(("Error while parsing config file: "
+                            "Invalid port number!\n"));
+      got_errno = SILC_CONFIG_EPRINTLINE;
+      goto got_err;
     }
     tmp->backup_replace_port = (SilcUInt16) port;
   }
@@ -977,7 +988,6 @@ SILC_CONFIG_CALLBACK(fetch_router)
 
  got_err:
   silc_free(tmp->host);
-  silc_free(tmp->version);
   silc_free(tmp->backup_replace_ip);
   CONFIG_FREE_AUTH(tmp);
   silc_free(tmp);
@@ -1002,6 +1012,11 @@ static const SilcConfigTable table_general[] = {
   { "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 },
   { 0, 0, 0, 0 }
 };
 
@@ -1033,10 +1048,16 @@ static const SilcConfigTable table_pkcs[] = {
   { 0, 0, 0, 0 }
 };
 
-static const SilcConfigTable table_serverinfo[] = {
-  { "hostname",                SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
+static const SilcConfigTable table_serverinfo_c[] = {
   { "ip",              SILC_CONFIG_ARG_STR,    fetch_serverinfo, NULL},
   { "port",            SILC_CONFIG_ARG_INT,    fetch_serverinfo, NULL},
+  { 0, 0, 0, 0 }
+};
+
+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},
@@ -1079,6 +1100,9 @@ static const SilcConfigTable table_connparam[] = {
   { "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 },
   { 0, 0, 0, 0 }
 };
 
@@ -1111,7 +1135,6 @@ 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 },
-  { "versionid",       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 }
@@ -1122,7 +1145,6 @@ static const SilcConfigTable table_routerconn[] = {
   { "port",            SILC_CONFIG_ARG_INT,    fetch_router,   NULL },
   { "passphrase",      SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
   { "publickey",       SILC_CONFIG_ARG_STR,    fetch_router,   NULL },
-  { "versionid",       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 },
@@ -1148,34 +1170,60 @@ static const SilcConfigTable table_main[] = {
   { 0, 0, 0, 0 }
 };
 
+/* Set default values to stuff that was not configured. */
+
+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);
+}
+
 /* Allocates a new configuration object, opens configuration file and
- * parses it. The parsed data is returned to the newly allocated
- * configuration object. */
+   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. */
 
-SilcServerConfig silc_server_config_alloc(char *filename)
+SilcServerConfig silc_server_config_alloc(const char *filename)
 {
-  SilcServerConfig config;
+  SilcServerConfig config_new;
   SilcConfigEntity ent;
   SilcConfigFile *file;
   int ret;
   SILC_LOG_DEBUG(("Loading config data from `%s'", filename));
 
   /* alloc a config object */
-  config = (SilcServerConfig) silc_calloc(1, sizeof(*config));
+  config_new = silc_calloc(1, sizeof(*config_new));
+  config_new->refcount = 1;
+  if (!config_new)
+    return NULL;
+
   /* obtain a config file object */
   file = silc_config_open(filename);
   if (!file) {
-    fprintf(stderr, "\nError: can't open config file `%s'\n", filename);
+    SILC_SERVER_LOG_ERROR(("\nError: can't open config file `%s'\n",
+                          filename));
     return NULL;
   }
+
   /* 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);
+  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_LOG_DEBUG(("Parser returned [ret=%d]: %s", ret,
                  silc_config_strerror(ret)));
 
   /* Check if the parser returned errors */
@@ -1184,31 +1232,79 @@ SilcServerConfig silc_server_config_alloc(char *filename)
     if (ret != SILC_CONFIG_ESILENT) {
       char *linebuf, *filename = silc_config_get_filename(file);
       SilcUInt32 line = silc_config_get_line(file);
-      fprintf(stderr, "\nError while parsing config file: %s.\n",
-               silc_config_strerror(ret));
+      if (ret != SILC_CONFIG_EPRINTLINE)
+        SILC_SERVER_LOG_ERROR(("Error while parsing config file: %s.\n",
+                              silc_config_strerror(ret)));
       linebuf = silc_config_read_line(file, line);
-      fprintf(stderr, "  file %s line %lu:  %s\n\n", filename, line, linebuf);
+      SILC_SERVER_LOG_ERROR(("  file %s line %lu:  %s\n\n", filename,
+                            line, linebuf));
       silc_free(linebuf);
     }
+    silc_server_config_destroy(config_new);
     return NULL;
   }
+
   /* close (destroy) the file object */
   silc_config_close(file);
 
-  /* XXX FIXME: check for missing mandatory fields */
-  if (!config->server_info) {
-    fprintf(stderr, "\nError: Missing mandatory block `server_info'\n");
+  /* If config_new is incomplete, abort the object and return NULL */
+  if (!config_new->server_info) {
+    SILC_SERVER_LOG_ERROR(("\nError: Missing mandatory block "
+                          "`server_info'\n"));
+    silc_server_config_destroy(config_new);
     return NULL;
   }
-  return config;
+
+  /* XXX are there any other mandatory sections in the config file? */
+
+  /* Set default to configuration parameters */
+  silc_server_config_set_defaults(config_new);
+
+  return config_new;
+}
+
+/* Increments the reference counter of a config object */
+
+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));
+  }
+}
+
+/* Decrements the reference counter of a config object.  If the counter
+   reaches 0, the config object is destroyed. */
+
+void silc_server_config_unref(SilcServerConfigRef *ref)
+{
+  if (ref->ref_ptr)
+    silc_server_config_destroy(ref->config);
 }
 
-/* ... */
+/* Destroy a config object with all his children lists */
 
 void silc_server_config_destroy(SilcServerConfig config)
 {
   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->module_path);
+  silc_free(config->param.version_protocol);
+  silc_free(config->param.version_software);
+  silc_free(config->param.version_software_vendor);
 
   /* Destroy Logging channels */
   if (config->logging_info)
@@ -1224,7 +1320,15 @@ void silc_server_config_destroy(SilcServerConfig config)
   if (config->server_info) {
     register SilcServerConfigServerInfo *si = config->server_info;
     silc_free(si->server_name);
-    silc_free(si->server_ip);
+    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);
@@ -1233,6 +1337,8 @@ void silc_server_config_destroy(SilcServerConfig config)
     silc_free(si->group);
     silc_free(si->motd_file);
     silc_free(si->pid_file);
+    silc_pkcs_public_key_free(si->public_key);
+    silc_pkcs_private_key_free(si->private_key);
   }
 
   /* Now let's destroy the lists */
@@ -1257,8 +1363,15 @@ void silc_server_config_destroy(SilcServerConfig config)
     silc_free(di->name);
     silc_free(di);
   }
-  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigClient,
-                                 config->clients)
+  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);
+  }
+  SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigClient, config->clients)
     silc_free(di->host);
     CONFIG_FREE_AUTH(di);
     silc_free(di);
@@ -1278,18 +1391,19 @@ void silc_server_config_destroy(SilcServerConfig config)
   SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigServer,
                                  config->servers)
     silc_free(di->host);
-    silc_free(di->version);
     CONFIG_FREE_AUTH(di);
     silc_free(di);
   }
   SILC_SERVER_CONFIG_LIST_DESTROY(SilcServerConfigRouter,
                                  config->routers)
     silc_free(di->host);
-    silc_free(di->version);
     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
@@ -1348,10 +1462,10 @@ bool silc_server_config_register_ciphers(SilcServer server)
                                                SILC_CIPHER_SIM_SET_KEY));
        SILC_LOG_DEBUG(("set_key=%p", cipher_obj.set_key));
        cipher_obj.set_key_with_string =
-         silc_sim_getsym(sim, 
+         silc_sim_getsym(sim,
            silc_sim_symname(alg_name,
              SILC_CIPHER_SIM_SET_KEY_WITH_STRING));
-       SILC_LOG_DEBUG(("set_key_with_string=%p", 
+       SILC_LOG_DEBUG(("set_key_with_string=%p",
          cipher_obj.set_key_with_string));
        cipher_obj.encrypt =
          silc_sim_getsym(sim, silc_sim_symname(alg_name,
@@ -1544,19 +1658,24 @@ void silc_server_config_setlogfiles(SilcServer server)
   SilcServerConfig config = server->config;
   SilcServerConfigLogging *this;
 
-  SILC_LOG_DEBUG(("Setting configured log file names"));
+  SILC_LOG_DEBUG(("Setting configured log file names and options"));
 
-  if ((this = config->logging_info))
-    silc_log_set_file(SILC_LOG_INFO, this->file, this->maxsize, 
-                     server->schedule);
-  if ((this = config->logging_warnings))
-    silc_log_set_file(SILC_LOG_WARNING, this->file, this->maxsize, 
+  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_fatals))
-    silc_log_set_file(SILC_LOG_FATAL, this->file, this->maxsize, 
+  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);
 }
 
@@ -1586,7 +1705,7 @@ silc_server_config_find_client(SilcServer server, char *host)
    nickname. */
 
 SilcServerConfigAdmin *
-silc_server_config_find_admin(SilcServer server, char *host, char *user, 
+silc_server_config_find_admin(SilcServer server, char *host, char *user,
                              char *nick)
 {
   SilcServerConfig config = server->config;
@@ -1729,24 +1848,3 @@ silc_server_config_get_primary_router(SilcServer server)
 
   return NULL;
 }
-
-/* Set default values to stuff that was not configured. */
-
-bool silc_server_config_set_defaults(SilcServer server)
-{
-  SilcServerConfig config = server->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);
-
-  return TRUE;
-}