updates.
[silc.git] / apps / silcd / server.c
index beecd96a0d522c8b1d8e9187d163e2cd97e528d7..3d7b9686234f1514a10e60c4efc867cbdb61af9a 100644 (file)
@@ -119,20 +119,76 @@ void silc_server_free(SilcServer server)
    XXX This function will become more general and will support multiple
    listening ports */
 
-static bool silc_server_listen(SilcServer server, int *sock)
+static bool silc_server_listen(SilcServer server, const char *server_ip,
+                               SilcUInt16 port, int *sock)
 {
-
-  *sock = silc_net_create_server(server->config->server_info->port,
-                               server->config->server_info->server_ip);
+  *sock = silc_net_create_server(port, server_ip);
   if (*sock < 0) {
     SILC_LOG_ERROR(("Could not create server listener: %s on %hu",
-                   server->config->server_info->server_ip,
-                   server->config->server_info->port));
+                       server_ip, port));
     return FALSE;
   }
   return TRUE;
 }
 
+/* Adds a secondary listener. */
+bool silc_server_init_secondary(SilcServer server)
+{
+  int sock=0, sock_list[server->config->param.connections_max];
+  SilcSocketConnection newsocket = NULL;
+  SilcServerConfigServerInfoInterface *interface;
+
+  for (interface = server->config->server_info->secondary; interface; 
+       interface = interface->next, sock++) {
+         
+    if (!silc_server_listen(server,
+       interface->server_ip, interface->port, &sock_list[sock]))
+      goto err;
+
+    /* Set socket to non-blocking mode */
+    silc_net_set_socket_nonblock(sock_list[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_list[sock], 
+               SILC_SOCKET_TYPE_SERVER, NULL, &newsocket);
+    server->sockets[sock_list[sock]] = newsocket;
+
+    /* Perform name and address lookups to resolve the listenning address
+       and port. */
+    if (!silc_net_check_local_by_sock(sock_list[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);
+    
+    newsocket->user_data = (void *)server->id_entry;
+    silc_schedule_task_add(server->schedule, sock_list[sock],
+                        silc_server_accept_new_connection,
+                        (void *)server, 0, 0,
+                        SILC_TASK_FD,
+                        SILC_TASK_PRI_NORMAL);
+
+  }
+
+  return TRUE;
+  
+err:
+
+  do silc_net_close_server(sock_list[sock--]); while (sock >= 0);
+  return FALSE;
+
+}
+
 /* Initializes the entire SILC server. This is called always before running
    the server. This is called only once at the initialization of the program.
    This binds the server to its listenning port. After this function returns
@@ -218,7 +274,12 @@ bool silc_server_init(SilcServer server)
     goto err;
 
   /* Create a listening server */
-  if (!silc_server_listen(server, &sock))
+  if (!silc_server_listen(server,
+               server->config->server_info->primary == NULL ? NULL :
+                       server->config->server_info->primary->server_ip,
+               server->config->server_info->primary == NULL ? 0 :
+                       server->config->server_info->primary->port,
+               &sock))
     goto err;
 
   /* Set socket to non-blocking mode */
@@ -308,6 +369,10 @@ bool silc_server_init(SilcServer server)
                         (void *)server, 0, 0,
                         SILC_TASK_FD,
                         SILC_TASK_PRI_NORMAL);
+
+  if (silc_server_init_secondary(server) == FALSE)
+    goto err;
+  
   server->listenning = TRUE;
 
   /* If server connections has been configured then we must be router as
@@ -659,9 +724,10 @@ SILC_TASK_CALLBACK(silc_server_connect_router)
   silc_server_config_ref(&sconn->conn, server->config, (void *)rconn);
 
   /* Connect to remote host */
-  sock = silc_net_create_connection(server->config->server_info->server_ip,
-                                   sconn->remote_port,
-                                   sconn->remote_host);
+  sock = silc_net_create_connection(server->config->server_info->primary == NULL ? NULL :
+                server->config->server_info->primary->server_ip,
+                sconn->remote_port,
+                sconn->remote_host);
   if (sock < 0) {
     SILC_LOG_ERROR(("Could not connect to router %s:%d",
                    sconn->remote_host, sconn->remote_port));
@@ -1094,14 +1160,17 @@ static void
 silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
                                         void *context)
 {
-  SilcServer server = (SilcServer)context;
-  SilcServerKEInternalContext *proto_ctx;
+  SilcServerKEInternalContext *proto_ctx = 
+    (SilcServerKEInternalContext *)context;
+  SilcServer server = (SilcServer)proto_ctx->server;
   SilcServerConfigClient *cconfig = NULL;
   SilcServerConfigServer *sconfig = NULL;
   SilcServerConfigRouter *rconfig = NULL;
   SilcServerConfigDeny *deny;
   int port;
 
+  context = (void *)server;
+
   SILC_LOG_DEBUG(("Start"));
 
   /* Check whether we could resolve both IP and FQDN. */
@@ -1114,6 +1183,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
     silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_INCOMPLETE_INFORMATION,
                                  "Unknown host or IP");
+    silc_free(proto_ctx);
     return;
   }
 
@@ -1128,7 +1198,8 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
   SILC_LOG_INFO(("Incoming connection %s (%s)", sock->hostname,
                 sock->ip));
 
-  port = server->sockets[server->sock]->port; /* Listenning port */
+  /* Listenning port */
+  port = server->sockets[(SilcUInt32)proto_ctx->context]->port;
 
   /* Check whether this connection is denied to connect to us. */
   deny = silc_server_config_find_denied(server, sock->ip);
@@ -1142,6 +1213,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
                                  SILC_STATUS_ERR_BANNED_FROM_SERVER,
                                  deny->reason);
     server->stat.conn_failures++;
+    silc_free(proto_ctx);
     return;
   }
 
@@ -1154,7 +1226,7 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
     sconfig = silc_server_config_find_server_conn(server, sock->hostname);
   if (server->server_type == SILC_ROUTER) {
     if (!(rconfig = silc_server_config_find_router_conn(server,
-                                                       sock->ip, port)))
+                                                       sock->ip, sock->port)))
       rconfig = silc_server_config_find_router_conn(server, sock->hostname,
                                                    sock->port);
   }
@@ -1164,15 +1236,14 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
     silc_server_disconnect_remote(server, sock,
                                  SILC_STATUS_ERR_BANNED_FROM_SERVER);
     server->stat.conn_failures++;
+    silc_free(proto_ctx);
     return;
   }
 
   /* The connection is allowed */
 
-  /* Allocate internal context for key exchange protocol. This is
+  /* Set internal context for key exchange protocol. This is
      sent as context for the protocol. */
-  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
-  proto_ctx->server = context;
   proto_ctx->sock = sock;
   proto_ctx->rng = server->rng;
   proto_ctx->responder = TRUE;
@@ -1204,7 +1275,8 @@ silc_server_accept_new_connection_lookup(SilcSocketConnection sock,
   proto_ctx->timeout_task =
     silc_schedule_task_add(server->schedule, sock->sock,
                           silc_server_timeout_remote,
-                          context, server->config->key_exchange_timeout, 0,
+                          (void *)server,
+                          server->config->key_exchange_timeout, 0,
                           SILC_TASK_TIMEOUT,
                           SILC_TASK_PRI_LOW);
 }
@@ -1216,13 +1288,14 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
 {
   SilcServer server = (SilcServer)context;
   SilcSocketConnection newsocket;
+  SilcServerKEInternalContext *proto_ctx;
   int sock;
 
   SILC_LOG_DEBUG(("Accepting new connection"));
 
   server->stat.conn_attempts++;
 
-  sock = silc_net_accept_connection(server->sock);
+  sock = silc_net_accept_connection(fd);
   if (sock < 0) {
     SILC_LOG_ERROR(("Could not accept new connection: %s", strerror(errno)));
     server->stat.conn_failures++;
@@ -1249,9 +1322,12 @@ SILC_TASK_CALLBACK(silc_server_accept_new_connection)
   /* 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. */
+  proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
+  proto_ctx->server = server;
+  proto_ctx->context = (void *)fd;
   silc_socket_host_lookup(newsocket, TRUE,
-                         silc_server_accept_new_connection_lookup, context,
-                         server->schedule);
+                         silc_server_accept_new_connection_lookup,
+                         (void *)proto_ctx, server->schedule);
 }
 
 /* Second part of accepting new connection. Key exchange protocol has been
@@ -2532,30 +2608,29 @@ void silc_server_free_client_data(SilcServer server,
      to the network before removing the client entry. */
   silc_server_packet_queue_purge(server, sock);
 
-  if (!client->id)
-    return;
-
-  /* Send SIGNOFF notify to routers. */
-  if (notify && !server->standalone && server->router)
-    silc_server_send_notify_signoff(server, server->router->connection,
-                                   server->server_type == SILC_SERVER ?
-                                   FALSE : TRUE, client->id, signoff);
+  if (client->id) {
+    /* Check if anyone is watching this nickname */
+    if (server->server_type == SILC_ROUTER)
+      silc_server_check_watcher_list(server, client, NULL,
+                                    SILC_NOTIFY_TYPE_SIGNOFF);
 
-  /* Remove client from all channels */
-  if (notify)
-    silc_server_remove_from_channels(server, NULL, client,
-                                    TRUE, (char *)signoff, TRUE);
-  else
-    silc_server_remove_from_channels(server, NULL, client,
-                                    FALSE, NULL, FALSE);
+    /* Send SIGNOFF notify to routers. */
+    if (notify && !server->standalone && server->router)
+      silc_server_send_notify_signoff(server, server->router->connection,
+                                     server->server_type == SILC_SERVER ?
+                                     FALSE : TRUE, client->id, signoff);
 
-  /* Check if anyone is watching this nickname */
-  if (server->server_type == SILC_ROUTER)
-    silc_server_check_watcher_list(server, client, NULL,
-                                  SILC_NOTIFY_TYPE_SIGNOFF);
+    /* Remove client from all channels */
+    if (notify)
+      silc_server_remove_from_channels(server, NULL, client,
+                                      TRUE, (char *)signoff, TRUE);
+    else
+      silc_server_remove_from_channels(server, NULL, client,
+                                      FALSE, NULL, FALSE);
 
-  /* Remove this client from watcher list if it is */
-  silc_server_del_from_watcher_list(server, client);
+    /* Remove this client from watcher list if it is */
+    silc_server_del_from_watcher_list(server, client);
+  }
 
   /* Update statistics */
   server->stat.my_clients--;
@@ -2790,7 +2865,6 @@ void silc_server_remove_from_channels(SilcServer server,
 
       silc_schedule_task_del_by_context(server->schedule, channel->rekey);
       silc_server_channel_delete(server, channel);
-      silc_buffer_free(clidp);
       continue;
     }
 
@@ -3543,20 +3617,53 @@ void silc_server_announce_get_channel_topic(SilcServer server,
 
 void silc_server_announce_get_channel_users(SilcServer server,
                                            SilcChannelEntry channel,
+                                           SilcBuffer *channel_modes,
                                            SilcBuffer *channel_users,
                                            SilcBuffer *channel_users_modes)
 {
   SilcChannelClientEntry chl;
   SilcHashTableList htl;
-  SilcBuffer chidp, clidp;
+  SilcBuffer chidp, clidp, csidp;
   SilcBuffer tmp;
   int len;
-  unsigned char mode[4];
+  unsigned char mode[4], *fkey = NULL;
+  SilcUInt32 fkey_len = 0;
+  char *hmac;
 
   SILC_LOG_DEBUG(("Start"));
 
-  /* Now find all users on the channel */
   chidp = silc_id_payload_encode(channel->id, SILC_ID_CHANNEL);
+  csidp = silc_id_payload_encode(server->id, SILC_ID_SERVER);
+
+  /* CMODE notify */
+  SILC_PUT32_MSB(channel->mode, mode);
+  hmac = channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) : NULL;
+  if (channel->founder_key)
+    fkey = silc_pkcs_public_key_encode(channel->founder_key, &fkey_len);
+  tmp = 
+    silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CMODE_CHANGE,
+                                      6, csidp->data, csidp->len,
+                                      mode, sizeof(mode),
+                                      NULL, 0,
+                                      hmac, hmac ? strlen(hmac) : 0,
+                                      channel->passphrase,
+                                      channel->passphrase ?
+                                      strlen(channel->passphrase) : 0,
+                                      fkey, fkey_len);
+  len = tmp->len;
+  *channel_modes =
+    silc_buffer_realloc(*channel_modes,
+                       (*channel_modes ?
+                        (*channel_modes)->truelen + len : len));
+  silc_buffer_pull_tail(*channel_modes,
+                       ((*channel_modes)->end -
+                        (*channel_modes)->data));
+  silc_buffer_put(*channel_modes, tmp->data, tmp->len);
+  silc_buffer_pull(*channel_modes, len);
+  silc_buffer_free(tmp);
+  silc_free(fkey);
+
+  /* Now find all users on the channel */
   silc_hash_table_list(channel->user_list, &htl);
   while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
     clidp = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT);
@@ -3580,10 +3687,13 @@ void silc_server_announce_get_channel_users(SilcServer server,
 
     /* CUMODE notify for mode change on the channel */
     SILC_PUT32_MSB(chl->mode, mode);
+    if (chl->mode & SILC_CHANNEL_UMODE_CHANFO && channel->founder_key)
+      fkey = silc_pkcs_public_key_encode(channel->founder_key, &fkey_len);
     tmp = silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CUMODE_CHANGE,
-                                            3, clidp->data, clidp->len,
-                                            mode, 4,
-                                            clidp->data, clidp->len);
+                                            4, csidp->data, csidp->len,
+                                            mode, sizeof(mode),
+                                            clidp->data, clidp->len,
+                                            fkey, fkey_len);
     len = tmp->len;
     *channel_users_modes =
       silc_buffer_realloc(*channel_users_modes,
@@ -3596,11 +3706,12 @@ void silc_server_announce_get_channel_users(SilcServer server,
     silc_buffer_put(*channel_users_modes, tmp->data, tmp->len);
     silc_buffer_pull(*channel_users_modes, len);
     silc_buffer_free(tmp);
-
+    silc_free(fkey);
     silc_buffer_free(clidp);
   }
   silc_hash_table_list_reset(&htl);
   silc_buffer_free(chidp);
+  silc_buffer_free(csidp);
 }
 
 /* Returns assembled packets for all channels and users on those channels
@@ -3610,6 +3721,7 @@ void silc_server_announce_get_channel_users(SilcServer server,
 void silc_server_announce_get_channels(SilcServer server,
                                       SilcIDList id_list,
                                       SilcBuffer *channels,
+                                      SilcBuffer **channel_modes,
                                       SilcBuffer *channel_users,
                                       SilcBuffer **channel_users_modes,
                                       SilcUInt32 *channel_users_modes_c,
@@ -3668,10 +3780,14 @@ void silc_server_announce_get_channels(SilcServer server,
                                            sizeof(**channel_users_modes) *
                                            (i + 1));
        (*channel_users_modes)[i] = NULL;
+       *channel_modes = silc_realloc(*channel_modes,
+                                     sizeof(**channel_modes) * (i + 1));
+       (*channel_modes)[i] = NULL;
        *channel_ids = silc_realloc(*channel_ids,
                                    sizeof(**channel_ids) * (i + 1));
        (*channel_ids)[i] = NULL;
        silc_server_announce_get_channel_users(server, channel,
+                                              &(*channel_modes)[i], 
                                               channel_users,
                                               &(*channel_users_modes)[i]);
        (*channel_ids)[i] = channel->id;
@@ -3706,7 +3822,7 @@ void silc_server_announce_channels(SilcServer server,
                                   unsigned long creation_time,
                                   SilcSocketConnection remote)
 {
-  SilcBuffer channels = NULL, channel_users = NULL;
+  SilcBuffer channels = NULL, *channel_modes = NULL, channel_users = NULL;
   SilcBuffer *channel_users_modes = NULL;
   SilcBuffer *channel_topics = NULL;
   SilcUInt32 channel_users_modes_c = 0;
@@ -3716,7 +3832,8 @@ void silc_server_announce_channels(SilcServer server,
 
   /* Get channels and channel users in local list */
   silc_server_announce_get_channels(server, server->local_list,
-                                   &channels, &channel_users,
+                                   &channels, &channel_modes,
+                                   &channel_users,
                                    &channel_users_modes,
                                    &channel_users_modes_c,
                                    &channel_topics,
@@ -3725,7 +3842,8 @@ void silc_server_announce_channels(SilcServer server,
   /* Get channels and channel users in global list */
   if (server->server_type != SILC_SERVER)
     silc_server_announce_get_channels(server, server->global_list,
-                                     &channels, &channel_users,
+                                     &channels, &channel_modes,
+                                     &channel_users,
                                      &channel_users_modes,
                                      &channel_users_modes_c,
                                      &channel_topics,
@@ -3744,6 +3862,28 @@ void silc_server_announce_channels(SilcServer server,
     silc_buffer_free(channels);
   }
 
+  if (channel_modes) {
+    int i;
+
+    for (i = 0; i < channel_users_modes_c; i++) {
+      if (!channel_modes[i])
+        continue;
+      silc_buffer_push(channel_modes[i],
+                      channel_modes[i]->data -
+                      channel_modes[i]->head);
+      SILC_LOG_HEXDUMP(("channel modes"), channel_modes[i]->data,
+                      channel_modes[i]->len);
+      silc_server_packet_send_dest(server, remote,
+                                  SILC_PACKET_NOTIFY, SILC_PACKET_FLAG_LIST,
+                                  channel_ids[i], SILC_ID_CHANNEL,
+                                  channel_modes[i]->data,
+                                  channel_modes[i]->len,
+                                  FALSE);
+      silc_buffer_free(channel_modes[i]);
+    }
+    silc_free(channel_modes);
+  }
+
   if (channel_users) {
     silc_buffer_push(channel_users, channel_users->data - channel_users->head);
     SILC_LOG_HEXDUMP(("channel users"), channel_users->data,
@@ -3827,7 +3967,7 @@ SILC_TASK_CALLBACK(silc_server_failure_callback)
 
 /* Assembles user list and users mode list from the `channel'. */
 
-void silc_server_get_users_on_channel(SilcServer server,
+bool silc_server_get_users_on_channel(SilcServer server,
                                      SilcChannelEntry channel,
                                      SilcBuffer *user_list,
                                      SilcBuffer *mode_list,
@@ -3840,6 +3980,9 @@ void silc_server_get_users_on_channel(SilcServer server,
   SilcBuffer idp;
   SilcUInt32 list_count = 0, len = 0;
 
+  if (!silc_hash_table_count(channel->user_list))
+    return FALSE;
+
   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);
@@ -3874,6 +4017,7 @@ void silc_server_get_users_on_channel(SilcServer server,
   *user_list = client_id_list;
   *mode_list = client_mode_list;
   *user_count = list_count;
+  return TRUE;
 }
 
 /* Saves users and their modes to the `channel'. */