updates.
[silc.git] / apps / silcd / server.c
index ea75773096a11ea98029697a732eaf5c0ac15713..38f645e49338ac19068edc549ff44c093e289f82 100644 (file)
@@ -931,87 +931,58 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_final)
   sock->protocol = NULL;
 }
 
-/* Accepts new connections to the server. Accepting new connections are
-   done in three parts to make it async. */
-
-SILC_TASK_CALLBACK(silc_server_accept_new_connection)
+/* Host lookup callbcak that is called after the incoming connection's
+   IP and FQDN lookup is performed. This will actually check the acceptance
+   of the incoming connection and will register the key exchange protocol
+   for this connection. */
+
+static void 
+silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
+                                        void *context)
 {
   SilcServer server = (SilcServer)context;
-  SilcSocketConnection newsocket;
   SilcServerKEInternalContext *proto_ctx;
-  int sock, port;
   void *cconfig, *sconfig, *rconfig;
   SilcServerConfigSectionDenyConnection *deny;
+  int port;
 
-  SILC_LOG_DEBUG(("Accepting new connection"));
-
-  server->stat.conn_attempts++;
-
-  sock = silc_net_accept_connection(server->sock);
-  if (sock < 0) {
-    SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
-    server->stat.conn_failures++;
-    return;
-  }
+  SILC_LOG_DEBUG(("Start"));
 
-  /* Check max connections */
-  if (sock > SILC_SERVER_MAX_CONNECTIONS) {
-    SILC_LOG_ERROR(("Refusing connection, server is full"));
+  /* Check whether we could resolve both IP and FQDN. */
+  if (!sock->ip || (!strcmp(sock->ip, sock->hostname) &&
+                   server->params->require_reverse_mapping)) {
+    SILC_LOG_ERROR(("IP/DNS lookup failed %s",
+                   sock->hostname ? sock->hostname :
+                   sock->ip ? sock->ip : ""));
     server->stat.conn_failures++;
+    silc_server_disconnect_remote(server, sock,
+                                 "Server closed connection: Unknown host");
     return;
   }
 
-  /* Set socket options */
-  silc_net_set_socket_nonblock(sock);
-  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
-
-  /* We don't create a ID yet, since we don't know what type of connection
-     this is yet. But, we do add the connection to the socket table. */
-  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
-  server->sockets[sock] = newsocket;
-
-  /* XXX This MUST be done async as this will block the entire server
-     process. Either we have to do our own resolver stuff or in the future
-     we can use threads. */
-  /* Perform name and address lookups for the remote host. */
-  if (!silc_net_check_host_by_sock(sock, &newsocket->hostname, 
-                                  &newsocket->ip)) {
-    if ((server->params->require_reverse_mapping && !newsocket->hostname) ||
-       !newsocket->ip) {
-      SILC_LOG_ERROR(("IP/DNS lookup failed %s",
-                     newsocket->hostname ? newsocket->hostname :
-                     newsocket->ip ? newsocket->ip : ""));
-      server->stat.conn_failures++;
-      return;
-    }
-    if (!newsocket->hostname)
-      newsocket->hostname = strdup(newsocket->ip);
-  }
-  newsocket->port = silc_net_get_remote_port(sock);
-
   /* Register the connection for network input and output. This sets
      that scheduler will listen for incoming packets for this connection 
      and sets that outgoing packets may be sent to this connection as well.
      However, this doesn't set the scheduler for outgoing traffic, it
      will be set separately by calling SILC_SET_CONNECTION_FOR_OUTPUT,
      later when outgoing data is available. */
-  SILC_REGISTER_CONNECTION_FOR_IO(sock);
+  SILC_REGISTER_CONNECTION_FOR_IO(sock->sock);
 
-  SILC_LOG_INFO(("Incoming connection from %s (%s)", newsocket->hostname,
-                newsocket->ip));
+  SILC_LOG_INFO(("Incoming connection from %s (%s)", sock->hostname,
+                sock->ip));
 
-  port = server->sockets[fd]->port; /* Listenning port */
+  port = server->sockets[server->sock]->port; /* Listenning port */
 
   /* Check whether this connection is denied to connect to us. */
-  deny = silc_server_config_denied_conn(server->config, newsocket->ip, port);
+  deny = silc_server_config_denied_conn(server->config, sock->ip, port);
   if (!deny)
-    deny = silc_server_config_denied_conn(server->config, newsocket->hostname,
+    deny = silc_server_config_denied_conn(server->config, sock->hostname,
                                          port);
   if (deny) {
     /* The connection is denied */
     SILC_LOG_INFO(("Connection %s (%s) is denied", 
-                   newsocket->hostname, newsocket->ip));
-    silc_server_disconnect_remote(server, newsocket, deny->comment ?
+                   sock->hostname, sock->ip));
+    silc_server_disconnect_remote(server, sock, deny->comment ?
                                  deny->comment :
                                  "Server closed connection: "
                                  "Connection refused");
@@ -1023,23 +994,23 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      have to check all configurations since we don't know what type of
      connection this is. */
   if (!(cconfig = silc_server_config_find_client_conn(server->config,
-                                                     newsocket->ip, port)))
+                                                     sock->ip, port)))
     cconfig = silc_server_config_find_client_conn(server->config,
-                                                 newsocket->hostname, 
+                                                 sock->hostname, 
                                                  port);
   if (!(sconfig = silc_server_config_find_server_conn(server->config,
-                                                    newsocket->ip, 
+                                                    sock->ip, 
                                                     port)))
     sconfig = silc_server_config_find_server_conn(server->config,
-                                                 newsocket->hostname,
+                                                 sock->hostname,
                                                  port);
   if (!(rconfig = silc_server_config_find_router_conn(server->config,
-                                                    newsocket->ip, port)))
+                                                    sock->ip, port)))
     rconfig = silc_server_config_find_router_conn(server->config,
-                                                 newsocket->hostname, 
+                                                 sock->hostname, 
                                                  port);
   if (!cconfig && !sconfig && !rconfig) {
-    silc_server_disconnect_remote(server, newsocket
+    silc_server_disconnect_remote(server, sock
                                  "Server closed connection: "
                                  "Connection refused");
     server->stat.conn_failures++;
@@ -1052,7 +1023,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      sent as context for the protocol. */
   proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
   proto_ctx->server = context;
-  proto_ctx->sock = newsocket;
+  proto_ctx->sock = sock;
   proto_ctx->rng = server->rng;
   proto_ctx->responder = TRUE;
   proto_ctx->cconfig = cconfig;
@@ -1065,7 +1036,7 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      there before we start the protocol. */
   server->stat.auth_attempts++;
   silc_protocol_alloc(SILC_PROTOCOL_SERVER_KEY_EXCHANGE, 
-                     &newsocket->protocol, proto_ctx, 
+                     &sock->protocol, proto_ctx, 
                      silc_server_accept_new_connection_second);
 
   /* Register a timeout task that will be executed if the connector
@@ -1073,13 +1044,57 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
      now, this is a hard coded limit. After 60 secs the connection will
      be closed if the key exchange protocol has not been started. */
   proto_ctx->timeout_task = 
-    silc_task_register(server->timeout_queue, newsocket->sock, 
+    silc_task_register(server->timeout_queue, sock->sock, 
                       silc_server_timeout_remote,
                       context, 60, 0,
                       SILC_TASK_TIMEOUT,
                       SILC_TASK_PRI_LOW);
 }
 
+/* Accepts new connections to the server. Accepting new connections are
+   done in three parts to make it async. */
+
+SILC_TASK_CALLBACK(silc_server_accept_new_connection)
+{
+  SilcServer server = (SilcServer)context;
+  SilcSocketConnection newsocket;
+  int sock;
+
+  SILC_LOG_DEBUG(("Accepting new connection"));
+
+  server->stat.conn_attempts++;
+
+  sock = silc_net_accept_connection(server->sock);
+  if (sock < 0) {
+    SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
+    server->stat.conn_failures++;
+    return;
+  }
+
+  /* Check max connections */
+  if (sock > SILC_SERVER_MAX_CONNECTIONS) {
+    SILC_LOG_ERROR(("Refusing connection, server is full"));
+    server->stat.conn_failures++;
+    return;
+  }
+
+  /* Set socket options */
+  silc_net_set_socket_nonblock(sock);
+  silc_net_set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
+
+  /* We don't create a ID yet, since we don't know what type of connection
+     this is yet. But, we do add the connection to the socket table. */
+  silc_socket_alloc(sock, SILC_SOCKET_TYPE_UNKNOWN, NULL, &newsocket);
+  server->sockets[sock] = newsocket;
+
+  /* Perform asynchronous host lookup. This will lookup the IP and the
+     FQDN of the remote connection. After the lookup is done the connection
+     is accepted further. */
+  silc_socket_host_lookup(newsocket, TRUE, 
+                         silc_server_accept_new_connection_lookup, context, 
+                         server->timeout_queue);
+}
+
 /* Second part of accepting new connection. Key exchange protocol has been
    performed and now it is time to do little connection authentication
    protocol to figure out whether this connection is client or server
@@ -1395,8 +1410,15 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
     if (ret == -2)
       return;
 
-    if (ret == -1)
+    if (ret == -1) {
+      SILC_LOG_ERROR(("Error sending packet to connection "
+                     "%s:%d [%s]", sock->hostname, sock->port,  
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
       return;
+    }
     
     /* The packet has been sent and now it is time to set the connection
        back to only for input. When there is again some outgoing data 
@@ -1413,9 +1435,18 @@ SILC_TASK_CALLBACK(silc_server_packet_process)
 
   /* Read some data from connection */
   ret = silc_packet_receive(sock);
-  if (ret < 0)
+  if (ret < 0) {
+
+    if (ret == -1)
+      SILC_LOG_ERROR(("Error receiving packet from connection "
+                     "%s:%d [%s]", sock->hostname, sock->port,  
+                     (sock->type == SILC_SOCKET_TYPE_UNKNOWN ? "Unknown" :
+                      sock->type == SILC_SOCKET_TYPE_CLIENT ? "Client" :
+                      sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
+                      "Router")));
     return;
-    
+  }    
+
   /* EOF */
   if (ret == 0) {
     SILC_LOG_DEBUG(("Read EOF"));
@@ -1803,8 +1834,6 @@ void silc_server_packet_parse_type(SilcServer server,
     } else {
       SILC_LOG_ERROR(("Received Key Exchange packet but no key exchange "
                      "protocol active, packet dropped."));
-
-      /* XXX Trigger KE protocol?? Rekey actually, maybe. */
     }
     break;
 
@@ -2292,16 +2321,27 @@ int silc_server_remove_clients_by_server(SilcServer server,
   uint32 clients_c = 0;
   unsigned char **argv = NULL;
   uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
+  SilcHashTable channels;
+  SilcChannelClientEntry chl;
+  SilcChannelEntry channel;
+  SilcHashTableList htl;
   int i;
 
   SILC_LOG_DEBUG(("Start"));
 
+  /* Allocate the hash table that holds the channels that require
+     channel key re-generation after we've removed this server's clients
+     from the channels. */
+  channels = silc_hash_table_alloc(0, silc_hash_ptr, NULL, NULL, NULL,
+                                  NULL, NULL, TRUE);
+
   if (server_signoff) {
     idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
     argv = silc_realloc(argv, sizeof(*argv) * (argc + 1));
     argv_lens = silc_realloc(argv_lens,  sizeof(*argv_lens) * (argc + 1));
     argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1));
-    argv[argc] = idp->data;
+    argv[argc] = silc_calloc(idp->len, sizeof(*argv[0]));
+    memcpy(argv[argc], idp->data, idp->len);
     argv_lens[argc] = idp->len;
     argv_types[argc] = argc + 1;
     argc++;
@@ -2349,6 +2389,13 @@ int silc_server_remove_clients_by_server(SilcServer server,
          silc_buffer_free(idp);
        }
 
+       silc_hash_table_list(client->channels, &htl);
+       while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+         if (silc_hash_table_find(channels, chl->channel, NULL, NULL))
+           continue;
+         silc_hash_table_add(channels, chl->channel, chl->channel);
+       }
+
        /* Remove the client entry */
        silc_server_remove_from_channels(server, NULL, client, FALSE, 
                                         NULL, FALSE);
@@ -2402,6 +2449,13 @@ int silc_server_remove_clients_by_server(SilcServer server,
          silc_buffer_free(idp);
        }
 
+       silc_hash_table_list(client->channels, &htl);
+       while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+         if (silc_hash_table_find(channels, chl->channel, NULL, NULL))
+           continue;
+         silc_hash_table_add(channels, chl->channel, chl->channel);
+       }
+
        /* Remove the client entry */
        silc_server_remove_from_channels(server, NULL, client, FALSE,
                                         NULL, FALSE);
@@ -2443,11 +2497,25 @@ int silc_server_remove_clients_by_server(SilcServer server,
 
     silc_free(clients);
     silc_buffer_free(args);
+    for (i = 0; i < argc; i++)
+      silc_free(argv[i]);
     silc_free(argv);
     silc_free(argv_lens);
     silc_free(argv_types);
   }
 
+  /* We must now re-generate the channel key for all channels that had
+     this server's client(s) on the channel. As they left the channel we
+     must re-generate the channel key. */
+  silc_hash_table_list(channels, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&channel)) {
+    silc_server_create_channel_key(server, channel, 0);
+    silc_server_send_channel_key(server, NULL, channel, 
+                                server->server_type == SILC_ROUTER ? 
+                                FALSE : !server->standalone);
+  }
+  silc_hash_table_free(channels);
+
   return TRUE;
 }
 
@@ -2534,9 +2602,9 @@ void silc_server_remove_from_channels(SilcServer server,
     server->stat.my_chanclients--;
 
     /* If there is no global users on the channel anymore mark the channel
-       as local channel. */
-    if (server->server_type == SILC_SERVER &&
-       !silc_server_channel_has_global(channel))
+       as local channel. Do not check if the removed client is local client. */
+    if (server->server_type == SILC_SERVER && channel->global_users && 
+       chl->client->router && !silc_server_channel_has_global(channel))
       channel->global_users = FALSE;
 
     /* If there is not at least one local user on the channel then we don't
@@ -2651,9 +2719,9 @@ int silc_server_remove_from_one_channel(SilcServer server,
   server->stat.my_chanclients--;
   
   /* If there is no global users on the channel anymore mark the channel
-     as local channel. */
-  if (server->server_type == SILC_SERVER &&
-      !silc_server_channel_has_global(channel))
+     as local channel. Do not check if the client is local client. */
+  if (server->server_type == SILC_SERVER && channel->global_users &&
+      chl->client->router && !silc_server_channel_has_global(channel))
     channel->global_users = FALSE;
 
   /* If there is not at least one local user on the channel then we don't
@@ -3234,9 +3302,13 @@ static SilcBuffer
 silc_server_announce_encode_notify(SilcNotifyType notify, uint32 argc, ...)
 {
   va_list ap;
+  SilcBuffer p;
 
   va_start(ap, argc);
-  return silc_notify_payload_encode(notify, argc, ap);
+  p = silc_notify_payload_encode(notify, argc, ap);
+  va_end(ap);
+  return p;
 }
 
 /* Returns assembled packets for channel users of the `channel'. */
@@ -3483,17 +3555,18 @@ void silc_server_get_users_on_channel(SilcServer server,
   SilcBuffer client_id_list;
   SilcBuffer client_mode_list;
   SilcBuffer idp;
-  uint32 list_count = 0;
+  uint32 list_count = 0, len = 0;
 
-  /* XXX rewrite - this does not support IPv6 based Client ID's. */
+  silc_hash_table_list(channel->user_list, &htl);
+  while (silc_hash_table_get(&htl, NULL, (void *)&chl))
+    len += (silc_id_get_len(chl->client->id, SILC_ID_CLIENT) + 4);
 
-  client_id_list = 
-    silc_buffer_alloc((SILC_ID_CLIENT_LEN + 4) * 
-                     silc_hash_table_count(channel->user_list));
+  client_id_list = silc_buffer_alloc(len);
   client_mode_list = 
     silc_buffer_alloc(4 * silc_hash_table_count(channel->user_list));
   silc_buffer_pull_tail(client_id_list, SILC_BUFFER_END(client_id_list));
   silc_buffer_pull_tail(client_mode_list, SILC_BUFFER_END(client_mode_list));
+
   silc_hash_table_list(channel->user_list, &htl);
   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     /* Client ID */
@@ -3631,15 +3704,15 @@ SilcSocketConnection silc_server_get_client_route(SilcServer server,
   if (client) {
     silc_free(id);
 
-    if (client && client->data.registered == FALSE)
+    if (client->data.registered == FALSE)
       return NULL;
 
     /* If we are router and the client has router then the client is in
        our cell but not directly connected to us. */
     if (server->server_type == SILC_ROUTER && client->router) {
-      /* We are of course in this case the client's router thus the real
-        "router" of the client is the server who owns the client. Thus
-        we will send the packet to that server. */
+      /* We are of course in this case the client's router thus the route
+        to the client is the server who owns the client. So, we will send
+        the packet to that server. */
       if (idata)
        *idata = (SilcIDListData)client->router;
       return client->router->connection;
@@ -3761,6 +3834,7 @@ SilcClientEntry silc_server_get_client_resolve(SilcServer server,
                            buffer->data, buffer->len, FALSE);
     silc_buffer_free(idp);
     silc_buffer_free(buffer);
+    return NULL;
   }
 
   return client;