updates.
[silc.git] / apps / silcd / server.c
index 927040744a2a2a7afcdcb9930b9301f3b529c6f8..306cb07f78f34a9fd2b619bb32fbcc5472c68fd6 100644 (file)
@@ -44,6 +44,7 @@ SILC_TASK_CALLBACK(silc_server_timeout_remote);
 SILC_TASK_CALLBACK(silc_server_channel_key_rekey);
 SILC_TASK_CALLBACK(silc_server_failure_callback);
 SILC_TASK_CALLBACK(silc_server_rekey_callback);
+SILC_TASK_CALLBACK(silc_server_get_stats);
 
 /* Allocates a new SILC server object. This has to be done before the server
    can be used. After allocation one must call silc_server_init to initialize
@@ -87,10 +88,15 @@ void silc_server_free(SilcServer server)
     silc_dlist_uninit(server->sim);
 #endif
 
+    silc_server_config_unref(&server->config_ref);
     if (server->rng)
       silc_rng_free(server->rng);
     if (server->pkcs)
       silc_pkcs_free(server->pkcs);
+    if (server->public_key)
+      silc_pkcs_public_key_free(server->public_key);
+    if (server->private_key)
+      silc_pkcs_private_key_free(server->private_key);
     if (server->pending_commands)
       silc_dlist_uninit(server->pending_commands);
     if (server->id_entry)
@@ -139,18 +145,15 @@ bool silc_server_init(SilcServer server)
   SilcServerID *id;
   SilcServerEntry id_entry;
   SilcIDListPurge purge;
+  SilcSocketConnection newsocket = NULL;
 
   SILC_LOG_DEBUG(("Initializing server"));
-  assert(server);
-  assert(server->config);
-
-  /* Set public and private keys */
-  if (!server->config->server_info ||
-      !server->config->server_info->public_key ||
-      !server->config->server_info->private_key) {
-    SILC_LOG_ERROR(("Server public key and/or private key does not exist"));
-    return FALSE;
-  }
+
+  server->starttime = time(NULL);
+
+  /* Take config object for us */
+  silc_server_config_ref(&server->config_ref, server->config, 
+                        server->config);
 
   /* Steal public and private key from the config object */
   server->public_key = server->config->server_info->public_key;
@@ -178,7 +181,8 @@ bool silc_server_init(SilcServer server)
   silc_hash_alloc("sha1", &server->sha1hash);
 
   /* Allocate PKCS context for local public and private keys */
-  silc_pkcs_alloc(server->public_key->name, &server->pkcs);
+  if (!silc_pkcs_alloc(server->public_key->name, &server->pkcs))
+    goto err;
   silc_pkcs_public_key_set(server->pkcs, server->public_key);
   silc_pkcs_private_key_set(server->pkcs, server->private_key);
 
@@ -208,73 +212,71 @@ bool silc_server_init(SilcServer server)
   if (!silc_server_listen(server, &sock))
     goto err;
 
+  /* Set socket to non-blocking mode */
+  silc_net_set_socket_nonblock(sock);
+  server->sock = sock;
+
   /* Allocate the entire socket list that is used in server. Eventually
      all connections will have entry in this table (it is a table of
      pointers to the actual object that is allocated individually
      later). */
   server->sockets = silc_calloc(server->config->param.connections_max,
                                sizeof(*server->sockets));
+  if (!server->sockets)
+    goto err;
 
-  do {
-    SilcSocketConnection newsocket = NULL;
-
-    /* Set socket to non-blocking mode */
-    silc_net_set_socket_nonblock(sock);
-    server->sock = sock;
-
-    /* Add ourselves also to the socket table. The entry allocated above
-       is sent as argument for fast referencing in the future. */
-    silc_socket_alloc(sock, SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
-    server->sockets[sock] = newsocket;
-
-    /* Perform name and address lookups to resolve the listenning address
-       and port. */
-    if (!silc_net_check_local_by_sock(sock, &newsocket->hostname,
-                                     &newsocket->ip)) {
-      if ((server->config->require_reverse_lookup && !newsocket->hostname) ||
-         !newsocket->ip) {
-       SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
-                       newsocket->hostname ? newsocket->hostname :
-                       newsocket->ip ? newsocket->ip : ""));
-       server->stat.conn_failures++;
-       goto err;
-      }
-      if (!newsocket->hostname)
-       newsocket->hostname = strdup(newsocket->ip);
-    }
-    newsocket->port = silc_net_get_local_port(sock);
-
-    /* Create a Server ID for the server. */
-    silc_id_create_server_id(newsocket->ip, newsocket->port, server->rng, &id);
-    if (!id)
-      goto err;
+  /* Add ourselves also to the socket table. The entry allocated above
+     is sent as argument for fast referencing in the future. */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
+  server->sockets[sock] = newsocket;
 
-    server->id = id;
-    server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
-    server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER);
-    server->id_type = SILC_ID_SERVER;
-    server->server_name = server->config->server_info->server_name;
-    server->config->server_info->server_name = NULL;
-
-    /* Add ourselves to the server list. We don't have a router yet
-       beacuse we haven't established a route yet. It will be done later.
-       For now, NULL is sent as router. This allocates new entry to
-       the ID list. */
-    id_entry =
-      silc_idlist_add_server(server->local_list, strdup(server->server_name),
-                            server->server_type, server->id, NULL, NULL);
-    if (!id_entry) {
-      SILC_LOG_ERROR(("Could not add ourselves to cache"));
+  /* Perform name and address lookups to resolve the listenning address
+     and port. */
+  if (!silc_net_check_local_by_sock(sock, &newsocket->hostname,
+                                   &newsocket->ip)) {
+    if ((server->config->require_reverse_lookup && !newsocket->hostname) ||
+       !newsocket->ip) {
+      SILC_LOG_ERROR(("IP/DNS lookup failed for local host %s",
+                     newsocket->hostname ? newsocket->hostname :
+                     newsocket->ip ? newsocket->ip : ""));
+      server->stat.conn_failures++;
       goto err;
     }
-    id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+    if (!newsocket->hostname)
+      newsocket->hostname = strdup(newsocket->ip);
+  }
+  newsocket->port = silc_net_get_local_port(sock);
+
+  /* Create a Server ID for the server. */
+  silc_id_create_server_id(newsocket->ip, newsocket->port, server->rng, &id);
+  if (!id)
+    goto err;
+
+  server->id = id;
+  server->id_string = silc_id_id2str(id, SILC_ID_SERVER);
+  server->id_string_len = silc_id_get_len(id, SILC_ID_SERVER);
+  server->id_type = SILC_ID_SERVER;
+  server->server_name = server->config->server_info->server_name;
+  server->config->server_info->server_name = NULL;
+
+  /* Add ourselves to the server list. We don't have a router yet
+     beacuse we haven't established a route yet. It will be done later.
+     For now, NULL is sent as router. This allocates new entry to
+     the ID list. */
+  id_entry =
+    silc_idlist_add_server(server->local_list, strdup(server->server_name),
+                          server->server_type, server->id, NULL, NULL);
+  if (!id_entry) {
+    SILC_LOG_ERROR(("Could not add ourselves to cache"));
+    goto err;
+  }
+  id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
 
-    /* Put the allocated socket pointer also to the entry allocated above
-       for fast back-referencing to the socket list. */
-    newsocket->user_data = (void *)id_entry;
-    id_entry->connection = (void *)newsocket;
-    server->id_entry = id_entry;
-  } while (0);
+  /* Put the allocated socket pointer also to the entry allocated above
+     for fast back-referencing to the socket list. */
+  newsocket->user_data = (void *)id_entry;
+  id_entry->connection = (void *)newsocket;
+  server->id_entry = id_entry;
 
   /* Register protocols */
   silc_server_protocols_register();
@@ -283,7 +285,7 @@ bool silc_server_init(SilcServer server)
      timeout. It expires as soon as the caller calls silc_server_run. This
      task performs authentication protocol and key exchange with our
      primary router. */
-  silc_schedule_task_add(server->schedule, sock,
+  silc_schedule_task_add(server->schedule, 0,
                         silc_server_connect_to_router,
                         (void *)server, 0, 1,
                         SILC_TASK_TIMEOUT,
@@ -339,12 +341,20 @@ bool silc_server_init(SilcServer server)
                         (void *)purge, purge->timeout, 0,
                         SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
 
+  /* If we are normal server we'll retrieve network statisticial information
+     once in a while from the router. */
+  if (server->server_type == SILC_SERVER)
+    silc_schedule_task_add(purge->schedule, 0, silc_server_get_stats,
+                          server, 10, 0, SILC_TASK_TIMEOUT,
+                          SILC_TASK_PRI_LOW);
+
   SILC_LOG_DEBUG(("Server initialized"));
 
   /* We are done here, return succesfully */
   return TRUE;
 
  err:
+  silc_server_config_unref(&server->config_ref);
   silc_net_close_server(sock);
   return FALSE;
 }
@@ -358,28 +368,27 @@ bool silc_server_rehash(SilcServer server)
 {
   SilcServerConfig newconfig;
 
+  SILC_LOG_INFO(("Rehashing server"));
+
   /* Reset the logging system */
   silc_log_quick = TRUE;
   silc_log_flush_all();
 
   /* Start the main rehash phase (read again the config file) */
-  SILC_LOG_INFO(("Rehashing server"));
   newconfig = silc_server_config_alloc(server->config_file);
-
   if (!newconfig) {
     SILC_LOG_ERROR(("Rehash FAILED."));
     return FALSE;
   }
-  silc_server_config_unref(&server->config_ref);
-  server->config = newconfig;
-  silc_server_config_ref(&server->config_ref, newconfig, (void *) newconfig);
+
+  /* Reinit scheduler if necessary */
+  if (newconfig->param.connections_max > server->config->param.connections_max)
+    if (!silc_schedule_reinit(server->schedule, 
+                             newconfig->param.connections_max))
+      return FALSE;
 
   /* Fix the server_name field */
-  if (!strcmp(server->server_name, newconfig->server_info->server_name)) {
-    /* We don't need any update */
-    silc_free(newconfig->server_info->server_name);
-    newconfig->server_info->server_name = NULL;
-  } else {
+  if (strcmp(server->server_name, newconfig->server_info->server_name)) {
     silc_free(server->server_name);
     server->server_name = newconfig->server_info->server_name;
     newconfig->server_info->server_name = NULL;
@@ -387,125 +396,73 @@ bool silc_server_rehash(SilcServer server)
     /* Update the idcache list with a fresh pointer */
     silc_free(server->id_entry->server_name);
     server->id_entry->server_name = strdup(server->server_name);
-    silc_idcache_del_by_context(server->local_list->servers, server->id_entry);
-    silc_idcache_add(server->local_list->servers, server->id_entry->server_name,
-                    server->id_entry->id, server->id_entry, 0, NULL);
+    if (!silc_idcache_del_by_context(server->local_list->servers, 
+                                    server->id_entry))
+      return FALSE;
+    if (!silc_idcache_add(server->local_list->servers,
+                         server->id_entry->server_name,
+                         server->id_entry->id, server->id_entry, 0, NULL))
+      return FALSE;
   }
 
+  /* Set logging */
   silc_server_config_setlogfiles(server);
 
-  /* XXX There is still to implement the publickey change and modules
-     adding (we can't allow modules removing since we can't know which
-     one are actually in use */
-
-  return TRUE;
-}
-
-/* Drop root privileges. If some system call fails, die. */
-
-void silc_server_drop(SilcServer server)
-{
-  /* Are we executing silcd as root or a regular user? */
-  if (geteuid()) {
-    SILC_LOG_DEBUG(("Server started as user"));
+  /* Change new key pair if necessary */
+  if (newconfig->server_info->public_key &&
+      !silc_pkcs_public_key_compare(server->public_key,
+                                   newconfig->server_info->public_key)) {
+    silc_pkcs_public_key_free(server->public_key);
+    silc_pkcs_private_key_free(server->private_key);
+    server->public_key = newconfig->server_info->public_key;
+    server->private_key = newconfig->server_info->private_key;
+    newconfig->server_info->public_key = NULL;
+    newconfig->server_info->private_key = NULL;
+
+    /* Allocate PKCS context for local public and private keys */
+    silc_pkcs_free(server->pkcs);
+    if (!silc_pkcs_alloc(server->public_key->name, &server->pkcs))
+      return FALSE;
+    silc_pkcs_public_key_set(server->pkcs, server->public_key);
+    silc_pkcs_private_key_set(server->pkcs, server->private_key);
   }
-  else {
-    struct passwd *pw;
-    struct group *gr;
-    char *user, *group;
-
-    SILC_LOG_DEBUG(("Server started as root. Dropping privileges."));
-
-    /* Get the values given for user and group in configuration file */
-    user = server->config->server_info->user;
-    group = server->config->server_info->group;
-
-    if (!user || !group) {
-      fprintf(stderr, "Error:"
-       "\tSILC server must not be run as root.  For the security of your\n"
-       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
-       "\tuser account.  Modify the ServerInfo configuration section to run\n"
-       "\tthe server as non-root user.\n");
-      exit(1);
-    }
 
-    /* Check whether the user/group does not begin with a number */
-    if (isdigit(user[0]) || isdigit(group[0])) {
-      SILC_LOG_DEBUG(("User and/or group starts with a number"));
-      fprintf(stderr, "Invalid user and/or group information\n");
-      fprintf(stderr, "Please assign them as names, not numbers\n");
-      exit(1);
-    }
-
-    if (!(pw = getpwnam(user))) {
-      fprintf(stderr, "Error: No such user %s found.\n", user);
-      exit(1);
-    }
-    if (!(gr = getgrnam(group))) {
-      fprintf(stderr, "Error: No such group %s found.\n", group);
-      exit(1);
-    }
+  /* Go through all configured routers after rehash */
+  silc_schedule_task_add(server->schedule, 0,
+                        silc_server_connect_to_router,
+                        (void *)server, 0, 1,
+                        SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_NORMAL);
 
-    /* Check whether user and/or group is set to root. If yes, exit
-       immediately. Otherwise, setgid and setuid server to user.group */
-    if ((gr->gr_gid == 0) || (pw->pw_uid == 0)) {
-      fprintf(stderr, "Error:"
-       "\tSILC server must not be run as root.  For the security of your\n"
-       "\tsystem it is strongly suggested that you run SILC under dedicated\n"
-       "\tuser account.  Modify the ServerInfo configuration section to run\n"
-       "\tthe server as non-root user.\n");
-      exit(1);
-    }
+  /* Check whether our router status has changed */
+  if (newconfig->servers) {
+    SilcServerConfigServer *ptr = newconfig->servers;
 
-    SILC_LOG_DEBUG(("Changing to group %s (gid=%u)", group, gr->gr_gid));
-    if (setgid(gr->gr_gid) != 0) {
-      fprintf(stderr, "Error: Failed setgid() to %s (gid=%u). Exiting.\n",
-             group, gr->gr_gid);
-      exit(1);
-    }
-#if defined HAVE_SETGROUPS && defined HAVE_INITGROUPS
-    SILC_LOG_DEBUG(("Removing supplementary groups"));
-    if (setgroups(0, NULL) != 0) {
-      fprintf(stderr, "Error: Failed setgroups() to NULL. Exiting.\n");
-      exit(1);
-    }
-    SILC_LOG_DEBUG(("Setting supplementary groups for user %s", user));
-    if (initgroups(user, gr->gr_gid) != 0) {
-      fprintf(stderr, "Error: Failed initgroups() for user %s (gid=%u). "
-             "Exiting.\n", user, gr->gr_gid);
-      exit(1);
-    }
-#endif
-    SILC_LOG_DEBUG(("Changing to user %s (uid=%u)", user, pw->pw_uid));
-    if (setuid(pw->pw_uid) != 0) {
-      fprintf(stderr, "Error: Failed to setuid() to %s (gid=%u). Exiting.\n",
-              user, pw->pw_uid);
-      exit(1);
+    server->server_type = SILC_ROUTER;
+    while (ptr) {
+      if (ptr->backup_router) {
+       server->server_type = SILC_BACKUP_ROUTER;
+       server->backup_router = TRUE;
+       server->id_entry->server_type = SILC_BACKUP_ROUTER;
+       break;
+      }
+      ptr = ptr->next;
     }
   }
-}
 
-/* Fork server to background */
-
-void silc_server_daemonise(SilcServer server)
-{
-  int i;
-
-  SILC_LOG_DEBUG(("Forking SILC server to background"));
-
-  if ((i = fork()) < 0) {
-    fprintf(stderr, "Error: fork() failed: %s\n", strerror(errno));
-    exit(1);
-  }
+  /* Our old config is gone now. We'll unreference our reference made in
+     silc_server_init and then destroy it since we are destroying it
+     underneath the application (layer which called silc_server_init). */
+  silc_server_config_unref(&server->config_ref);
+  silc_server_config_destroy(server->config);
 
-  if (i) /* Kill the parent */
-    exit(0);
+  /* Take new config context */
+  server->config = newconfig;
+  silc_server_config_ref(&server->config_ref, server->config, server->config);
 
-  server->background = TRUE;
-  setsid();
+  SILC_LOG_DEBUG(("Server rehashed"));
 
-  /* XXX close stdin, stdout, stderr -- before this, check that all writes
-     to stderr are changed to SILC_SERVER_LOG_ERROR() */
+  return TRUE;
 }
 
 /* The heart of the server. This runs the scheduler thus runs the server.
@@ -656,7 +613,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry)
     return;
   }
 
-  /* we will lookup a fresh pointer later */
+  /* We will lookup a fresh pointer later */
   silc_server_config_unref(&sconn->conn);
 
   /* Wait one before retrying */
@@ -682,7 +639,9 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   rconn = silc_server_config_find_router_conn(server, sconn->remote_host,
                                              sconn->remote_port);
   if (!rconn) {
-    SILC_LOG_INFO(("Unconfigured server, giving up"));
+    SILC_LOG_INFO(("Unconfigured %s connection %s:%d, cannot connect",
+                  (sconn->backup ? "backup router" : "router"),
+                  sconn->remote_host, sconn->remote_port));
     silc_free(sconn->remote_host);
     silc_free(sconn->backup_replace_ip);
     silc_free(sconn);
@@ -740,6 +699,12 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
     return;
   }
 
+  /* Cancel any possible retry timeouts */
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_router);
+  silc_schedule_task_del_by_callback(server->schedule,
+                                    silc_server_connect_to_router_retry);
+
   /* Create the connections to all our routes */
   for (ptr = server->config->routers; ptr; ptr = ptr->next) {
 
@@ -749,6 +714,17 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
                    ptr->host, ptr->port));
 
     if (ptr->initiator) {
+      /* Check whether we are connected to this host already */
+      if (silc_server_num_sockets_by_remote(server, 
+                                           silc_net_is_ip(ptr->host) ?
+                                           ptr->host : NULL,
+                                           silc_net_is_ip(ptr->host) ?
+                                           NULL : ptr->host, ptr->port,
+                                           SILC_SOCKET_TYPE_ROUTER)) {
+       SILC_LOG_DEBUG(("We are already connected to this router"));
+       continue;
+      }
+
       /* Allocate connection object for hold connection specific stuff. */
       sconn = silc_calloc(1, sizeof(*sconn));
       sconn->server = server;
@@ -763,7 +739,7 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router)
       if (!server->router_conn && !sconn->backup)
        server->router_conn = sconn;
 
-      silc_schedule_task_add(server->schedule, fd,
+      silc_schedule_task_add(server->schedule, 0,
                             silc_server_connect_router,
                             (void *)sconn, 0, 1, SILC_TASK_TIMEOUT,
                             SILC_TASK_PRI_NORMAL);
@@ -798,9 +774,6 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
     if (ctx->ske)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
-    silc_server_config_unref(&ctx->cconfig);
-    silc_server_config_unref(&ctx->sconfig);
-    silc_server_config_unref(&ctx->rconfig);
     silc_free(ctx);
     silc_server_config_unref(&sconn->conn);
     silc_free(sconn->remote_host);
@@ -891,6 +864,10 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_second)
       silc_ske_free(ctx->ske);
     silc_free(ctx->dest_id);
     silc_free(ctx);
+    silc_server_config_unref(&sconn->conn);
+    silc_free(sconn->remote_host);
+    silc_free(sconn->backup_replace_ip);
+    silc_free(sconn);
     silc_schedule_task_del_by_callback(server->schedule,
                                       silc_server_failure_callback);
     silc_server_disconnect_remote(server, sock, "Server closed connection: "
@@ -1246,7 +1223,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 
   /* Check for maximum allowed connections */
   if (sock > server->config->param.connections_max) {
-    SILC_LOG_ERROR(("Refusing connection, server is full, try again later"));
+    SILC_LOG_ERROR(("Refusing connection, server is full"));
     server->stat.conn_failures++;
     silc_net_close_connection(sock);
     return;
@@ -1458,8 +1435,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection_final)
       /* Statistics */
       server->stat.my_clients++;
       server->stat.clients++;
-      if (server->server_type == SILC_ROUTER)
-       server->stat.cell_clients++;
+      server->stat.cell_clients++;
 
       /* Get connection parameters */
       if (conn->param) {
@@ -2525,7 +2501,7 @@ void silc_server_free_client_data(SilcServer server,
   /* Update statistics */
   server->stat.my_clients--;
   server->stat.clients--;
-  if (server->server_type == SILC_ROUTER)
+  if (server->stat.cell_clients)
     server->stat.cell_clients--;
   SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
   SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
@@ -4240,3 +4216,30 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_rekey_final)
     silc_ske_free(ctx->ske);
   silc_free(ctx);
 }
+
+/* Task callback used to retrieve network statistical information from
+   router server once in a while. */
+
+SILC_TASK_CALLBACK(silc_server_get_stats)
+{
+  SilcServer server = (SilcServer)context;
+  SilcBuffer idp, packet;
+
+  SILC_LOG_DEBUG(("Retrieving stats from router"));
+
+  if (!server->standalone) {
+    idp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER);
+    packet = silc_command_payload_encode_va(SILC_COMMAND_STATS, 
+                                           ++server->cmd_ident, 1,
+                                           1, idp->data, idp->len);
+    silc_server_packet_send(server, server->router->connection,
+                           SILC_PACKET_COMMAND, 0, packet->data,
+                           packet->len, FALSE);
+    silc_buffer_free(packet);
+    silc_buffer_free(idp);
+  }
+
+  silc_schedule_task_add(server->schedule, 0, silc_server_get_stats,
+                        server, 120, 0, SILC_TASK_TIMEOUT,
+                        SILC_TASK_PRI_LOW);
+}