Added silc_server_send_opers[_notify] to send packets to operators.
authorPekka Riikonen <priikone@silcnet.org>
Mon, 24 Jun 2002 18:55:39 +0000 (18:55 +0000)
committerPekka Riikonen <priikone@silcnet.org>
Mon, 24 Jun 2002 18:55:39 +0000 (18:55 +0000)
Fixed UMODE_CHANGE notify handling.
Fixed backup router issues after resuming protocol is completed.
Connections that were unconfigured in rehash are now closed
automatically.

CHANGES
TODO
apps/silcd/packet_receive.c
apps/silcd/packet_send.c
apps/silcd/packet_send.h
apps/silcd/server.c
apps/silcd/server.h
apps/silcd/server_backup.c
apps/silcd/server_util.c
apps/silcd/server_util.h

diff --git a/CHANGES b/CHANGES
index 837f2525820846c0ee77abcd3088f0c7b1808271..b7f4ad81f237c33b7a9f244eaa148dc29da25be8 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,29 @@
+Mon Jun 24 17:47:52 EEST 2002 Pekka Riikonen <priikone@silcnet.org>
+
+       * Added functions silc_server_send_opers and
+         silc_server_send_opers_notify to send packets stricly
+         to operators.  Added macro SILC_SERVER_SEND_OPERS macro
+         to send variable argument notify to operators.
+         Affected files silcd/packet_send.[ch] and silcd/server.h.
+
+       * Removed UMODE rights checking with UMODE_CHANGE notify.
+         Affected file silcd/packet_receive.c.
+
+       * Server/router operator now receives notify when network
+         switches to backup router and when it resumes the use of
+         primary router.  Affected file silcd/server.c and
+         silcd/server_backup.c.
+
+       * Fixed the updating of client information after backup
+         resuming protocol is over; update all except local clients
+         to the new primary router.  The affected file is
+         silcd/server_util.c.
+
+       * Added support for closing active connections in rehash
+         that were unconfigured by the user.  Supports currently
+         closing server and router connections.  Affected file
+         silcd/server.c.
+
 Sun Jun 23 17:32:31 EEST 2002 Pekka Riikonen <priikone@silcnet.org>
 
        * Don't do SILC_STRING_LANGUAGE encoding if the outbuffer
diff --git a/TODO b/TODO
index 71863c8ace06b839eaf42d2ebdb2023ffdbe3493..0c4fa20d5d9ddbeedc082c72c29a655c106685d9 100644 (file)
--- a/TODO
+++ b/TODO
@@ -35,10 +35,6 @@ TODO/bugs In SILC Server
          cell use it.  When primary goes down, also those that are
          not using it (are not connected locally) are signoffed.
 
-       o Configure use of backup router on normal server using HUP.
-
-       o Configure use of backup router on router using HUP.
-
        o Rewrite SILC_LOG_DEBUG's in silcd/server_backup.c.
 
        o Testing
index a9f3f267f9300271910401ba816adc04f05c9224..4abfcfb86ba5a70f822fd47e9c96b9b3fd5e65cc 100644 (file)
@@ -1551,12 +1551,6 @@ void silc_server_notify(SilcServer server,
       goto out;
     SILC_GET32_MSB(mode, tmp);
 
-    /* Check that mode changing is allowed. */
-    if (!silc_server_check_umode_rights(server, client, mode)) {
-      SILC_LOG_DEBUG(("UMODE change is not allowed"));
-      goto out;
-    }
-
     /* Remove internal resumed flag if client is marked detached now */
     if (mode & SILC_UMODE_DETACHED)
       client->data.status &= ~SILC_IDLIST_STATUS_RESUMED;
index 818ec0f015a3209a108a4fb571c705535f1842f2..5a60a9f9c00d7fb648876c2808f16e84143cf31a 100644 (file)
@@ -1911,3 +1911,183 @@ void silc_server_packet_queue_purge(SilcServer server,
     silc_buffer_clear(sock->outbuf);
   }
 }
+
+/* Send packet to clients that are known to be operators.  If server
+   is router and `route' is TRUE then the packet would go to all operators
+   in the SILC network.  If `route' is FALSE then only local operators
+   (local for server and cell wide for router).  If `local' is TRUE then
+   only locally connected operators receive the packet.  If `local' is
+   TRUE then `route' is ignored.  If server is normal server and `route'
+   is FALSE it is equivalent to `local' being TRUE. */
+
+void silc_server_send_opers(SilcServer server,
+                           SilcPacketType type,
+                           SilcPacketFlags flags,
+                           bool route, bool local,
+                           unsigned char *data, 
+                           SilcUInt32 data_len,
+                           bool force_send)
+{
+  SilcIDCacheList list = NULL;
+  SilcIDCacheEntry id_cache = NULL;
+  SilcClientEntry client = NULL;
+  SilcSocketConnection sock;
+  SilcServerEntry *routed = NULL;
+  SilcUInt32 routed_count = 0;
+  bool gone = FALSE;
+  int k;
+
+  SILC_LOG_DEBUG(("Sending %s packet to operators",
+                 silc_get_packet_name(type)));
+
+  /* If local was requested send only locally connected operators. */
+  if (local || (server->server_type == SILC_SERVER && !route)) {
+    if (!silc_idcache_get_all(server->local_list->clients, &list) ||
+       !silc_idcache_list_first(list, &id_cache))
+      return;
+    while (id_cache) {
+      client = (SilcClientEntry)id_cache->context;
+      if (!client->router && SILC_IS_LOCAL(client) &&
+         (client->mode & SILC_UMODE_SERVER_OPERATOR ||
+          client->mode & SILC_UMODE_ROUTER_OPERATOR)) {
+
+       /* Send the packet to locally connected operator */
+       silc_server_packet_send_dest(server, client->connection, type, flags,
+                                    client->id, SILC_ID_CLIENT,
+                                    data, data_len, force_send);
+      }
+
+      if (!silc_idcache_list_next(list, &id_cache))
+       break;
+    }
+    silc_idcache_list_free(list);
+    return;
+  }
+
+  if (!silc_idcache_get_all(server->local_list->clients, &list) ||
+      !silc_idcache_list_first(list, &id_cache))
+    return;
+  while (id_cache) {
+    client = (SilcClientEntry)id_cache->context;
+    if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+       !(client->mode & SILC_UMODE_ROUTER_OPERATOR))
+      goto next;
+
+    if (server->server_type != SILC_SERVER && client->router && 
+       ((!route && client->router->router == server->id_entry) || route)) {
+
+      /* Check if we have sent the packet to this route already */
+      for (k = 0; k < routed_count; k++)
+       if (routed[k] == client->router)
+         break;
+      if (k < routed_count)
+       goto next;
+
+      /* Route only once to router */
+      sock = (SilcSocketConnection)client->router->connection;
+      if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       if (gone)
+         goto next;
+       gone = TRUE;
+      }
+
+      /* Send the packet */
+      silc_server_packet_send_dest(server, sock, type, flags,
+                                  client->id, SILC_ID_CLIENT,
+                                  data, data_len, force_send);
+
+      /* Mark this route routed already */
+      routed = silc_realloc(routed, sizeof(*routed) * (routed_count + 1));
+      routed[routed_count++] = client->router;
+      goto next;
+    }
+
+    if (client->router || !client->connection)
+      goto next;
+
+    /* Send to locally connected client */
+    sock = (SilcSocketConnection)client->connection;
+    silc_server_packet_send_dest(server, sock, type, flags,
+                                client->id, SILC_ID_CLIENT,
+                                data, data_len, force_send);
+
+  next:
+    if (!silc_idcache_list_next(list, &id_cache))
+      break;
+  }
+  silc_idcache_list_free(list);
+
+  if (!silc_idcache_get_all(server->global_list->clients, &list) ||
+      !silc_idcache_list_first(list, &id_cache))
+    return;
+  while (id_cache) {
+    client = (SilcClientEntry)id_cache->context;
+    if (!(client->mode & SILC_UMODE_SERVER_OPERATOR) &&
+       !(client->mode & SILC_UMODE_ROUTER_OPERATOR))
+      goto nextg;
+
+    if (server->server_type != SILC_SERVER && client->router && 
+       ((!route && client->router->router == server->id_entry) || route)) {
+
+      /* Check if we have sent the packet to this route already */
+      for (k = 0; k < routed_count; k++)
+       if (routed[k] == client->router)
+         break;
+      if (k < routed_count)
+       goto nextg;
+
+      /* Route only once to router */
+      sock = (SilcSocketConnection)client->router->connection;
+      if (sock->type == SILC_SOCKET_TYPE_ROUTER) {
+       if (gone)
+         goto nextg;
+       gone = TRUE;
+      }
+
+      /* Send the packet */
+      silc_server_packet_send_dest(server, sock, type, flags,
+                                  client->id, SILC_ID_CLIENT,
+                                  data, data_len, force_send);
+
+      /* Mark this route routed already */
+      routed = silc_realloc(routed, sizeof(*routed) * (routed_count + 1));
+      routed[routed_count++] = client->router;
+      goto nextg;
+    }
+
+    if (client->router || !client->connection)
+      goto nextg;
+
+    /* Send to locally connected client */
+    sock = (SilcSocketConnection)client->connection;
+    silc_server_packet_send_dest(server, sock, type, flags,
+                                client->id, SILC_ID_CLIENT,
+                                data, data_len, force_send);
+
+  nextg:
+    if (!silc_idcache_list_next(list, &id_cache))
+      break;
+  }
+  silc_idcache_list_free(list);
+  silc_free(routed);
+}
+
+/* Send a notify packet to operators */
+
+void silc_server_send_opers_notify(SilcServer server,
+                                  bool route,
+                                  bool local,
+                                  SilcNotifyType type,
+                                  SilcUInt32 argc, ...)
+{
+  va_list ap;
+  SilcBuffer packet;
+
+  va_start(ap, argc);
+  packet = silc_notify_payload_encode(type, argc, ap);
+  silc_server_send_opers(server, SILC_PACKET_NOTIFY, 0,
+                        route, local, packet->data, packet->len,
+                        FALSE);
+  silc_buffer_free(packet);
+  va_end(ap);
+}
index ca0779e922741ec7144f700ad753010ccac90c6a..ec3d1f460a4696665d48d50048f53142039ef94b 100644 (file)
@@ -61,7 +61,7 @@ void silc_server_packet_route(SilcServer server,
                              SilcPacketContext *packet);
 void silc_server_packet_send_clients(SilcServer server,
                                     SilcHashTable clients,
-                                    SilcPacketType type, 
+                                    SilcPacketType type,
                                     SilcPacketFlags flags,
                                     bool route,
                                     unsigned char *data, 
@@ -267,5 +267,17 @@ void silc_server_send_connection_auth_request(SilcServer server,
                                              SilcAuthMethod auth_meth);
 void silc_server_packet_queue_purge(SilcServer server,
                                    SilcSocketConnection sock);
+void silc_server_send_opers(SilcServer server,
+                           SilcPacketType type,
+                           SilcPacketFlags flags,
+                           bool route, bool local,
+                           unsigned char *data, 
+                           SilcUInt32 data_len,
+                           bool force_send);
+void silc_server_send_opers_notify(SilcServer server,
+                                  bool route,
+                                  bool local,
+                                  SilcNotifyType type,
+                                  SilcUInt32 argc, ...);
 
 #endif
index fb135c4cfc66765513e938bd0d7894ef65837298..0a8921c382f1a7698a5d3df86a1572f471128037 100644 (file)
@@ -28,6 +28,7 @@
 #include "server_internal.h"
 
 /* Static prototypes */
+SILC_TASK_CALLBACK(silc_server_rehash_close_connection);
 SILC_TASK_CALLBACK(silc_server_connect_to_router_retry);
 SILC_TASK_CALLBACK(silc_server_connect_router);
 SILC_TASK_CALLBACK(silc_server_connect_to_router);
@@ -448,6 +449,31 @@ bool silc_server_init(SilcServer server)
   return FALSE;
 }
 
+/* Task callback to close a socket connection after rehash */
+
+SILC_TASK_CALLBACK(silc_server_rehash_close_connection)
+{
+  SilcServer server = context;
+  SilcSocketConnection sock = server->sockets[fd];
+
+  if (!sock)
+    return;
+
+  SILC_LOG_INFO(("Closing connection %s:%d [%s]: connection is unconfigured",
+                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")));
+  silc_schedule_task_del_by_context(server->schedule, sock);
+  silc_server_disconnect_remote(server, sock,
+                               SILC_STATUS_ERR_BANNED_FROM_SERVER,
+                               "This connection is removed from "
+                               "configuration");
+  if (sock->user_data)
+    silc_server_free_sock_user_data(server, sock, NULL);
+}
+
 /* This function basically reads the config file again and switches the config
    object pointed by the server object. After that, we have to fix various
    things such as the server_name and the listening ports.
@@ -516,6 +542,70 @@ bool silc_server_rehash(SilcServer server)
     silc_pkcs_private_key_set(server->pkcs, server->private_key);
   }
 
+  /* Check for unconfigured server and router connections and close
+     connections that were unconfigured. */
+
+  if (server->config->routers) {
+    SilcServerConfigRouter *ptr;
+    SilcServerConfigRouter *newptr;
+    bool found;
+
+    for (ptr = server->config->routers; ptr; ptr = ptr->next) {
+      found = FALSE;
+
+      /* Check whether new config has this one too */
+      for (newptr = newconfig->routers; newptr; newptr = newptr->next) {
+       if (!strcmp(newptr->host, ptr->host) && newptr->port == ptr->port &&
+           newptr->initiator == ptr->initiator) {
+         found = TRUE;
+         break;
+       }
+      }
+
+      if (!found) {
+       /* Remove this connection */
+       SilcSocketConnection sock;
+       sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_ROUTER,
+                                              ptr->host, ptr->port);
+       if (sock && !SILC_IS_LISTENER(sock))
+         silc_schedule_task_add(server->schedule, sock->sock,
+                                silc_server_rehash_close_connection,
+                                server, 0, 1, SILC_TASK_TIMEOUT,
+                                SILC_TASK_PRI_NORMAL);
+      }
+    }
+  }
+
+  if (server->config->servers) {
+    SilcServerConfigServer *ptr;
+    SilcServerConfigServer *newptr;
+    bool found;
+
+    for (ptr = server->config->servers; ptr; ptr = ptr->next) {
+      found = FALSE;
+
+      /* Check whether new config has this one too */
+      for (newptr = newconfig->servers; newptr; newptr = newptr->next) {
+       if (!strcmp(newptr->host, ptr->host)) {
+         found = TRUE;
+         break;
+       }
+      }
+
+      if (!found) {
+       /* Remove this connection */
+       SilcSocketConnection sock;
+       sock = silc_server_find_socket_by_host(server, SILC_SOCKET_TYPE_SERVER,
+                                              ptr->host, 0);
+       if (sock && !SILC_IS_LISTENER(sock))
+         silc_schedule_task_add(server->schedule, sock->sock,
+                                silc_server_rehash_close_connection,
+                                server, 0, 1, SILC_TASK_TIMEOUT,
+                                SILC_TASK_PRI_NORMAL);
+      }
+    }
+  }
+
   /* Go through all configured routers after rehash */
   silc_schedule_task_add(server->schedule, 0,
                         silc_server_connect_to_router,
@@ -2865,6 +2955,20 @@ void silc_server_free_sock_user_data(SilcServer server,
        if (server->server_type == SILC_SERVER)
          silc_server_update_channels_by_server(server, user_data,
                                                backup_router);
+
+       /* Send notify about primary router going down to local operators */
+       if (server->backup_router)
+         SILC_SERVER_SEND_OPERS(server, FALSE, TRUE,
+                                SILC_NOTIFY_TYPE_NONE,
+                                ("%s switched to backup router %s "
+                                 "(we are primary router now)",
+                                 server->server_name, server->server_name));
+       else
+         SILC_SERVER_SEND_OPERS(server, FALSE, TRUE,
+                                SILC_NOTIFY_TYPE_NONE,
+                                ("%s switched to backup router %s",
+                                 server->server_name,
+                                 server->router->server_name));
       }
 
       /* Free the server entry */
index d318233ec65e9fa69bd0fc6a94fdcc00723028ed..5ba6a0bacb417627446b13b14cd52b18a2a5c247 100644 (file)
@@ -98,7 +98,16 @@ do {                                                         \
   silc_server_send_notify(server, sock, FALSE,                         \
                          type, 1, __fmt__, strlen(__fmt__));   \
   silc_free(__fmt__);                                          \
-} while(0);
+} while(0)
+
+/* Send notify to operators */
+#define SILC_SERVER_SEND_OPERS(server, route, local, type, fmt)                \
+do {                                                                   \
+  char *__fmt__ = silc_format fmt;                                     \
+  silc_server_send_opers_notify(server, route, local,                  \
+                               type, 1, __fmt__, strlen(__fmt__));     \
+  silc_free(__fmt__);                                                  \
+} while(0)
 
 /* Check whether rekey protocol is active */
 #define SILC_SERVER_IS_REKEY(sock)                                     \
index 299f1eee1b681e8700a27aaf2d3338f0d6b64786..4c262c7bdab72f0e8a0fcefd9db8b53745a3030f 100644 (file)
@@ -1144,8 +1144,8 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
           router */
        silc_server_update_servers_by_server(server, backup_router, router,
                                             FALSE);
-       silc_server_update_clients_by_server(server, backup_router,
-                                            router, TRUE, FALSE);
+       silc_server_update_clients_by_server(server, NULL, router, 
+                                            FALSE, FALSE);
        if (server->server_type == SILC_SERVER)
          silc_server_update_channels_by_server(server, backup_router, router);
        silc_server_backup_replaced_del(server, backup_router);
@@ -1163,6 +1163,13 @@ SILC_TASK_CALLBACK_GLOBAL(silc_server_protocol_backup)
        silc_server_announce_channels(server, 0, router->connection);
       }
 
+      /* Send notify about primary router going down to local operators */
+      SILC_SERVER_SEND_OPERS(server, FALSE, TRUE,
+                            SILC_NOTIFY_TYPE_NONE,
+                            ("%s resumed the use of primary router %s",
+                             server->server_name,
+                             server->router->server_name));
+
       /* Protocol has ended, call the final callback */
       if (protocol->final_callback)
        silc_protocol_execute_final(protocol, server->schedule);
index a09a9d90e120013d1010c3eda7775b31dd686854..121bb8d88034f9f361ee54ffbc8846957f0ef76f 100644 (file)
@@ -462,7 +462,8 @@ silc_server_update_clients_by_real_server(SilcServer server,
    be the new source. This function also removes the clients that are
    *really* originated from `from' if `remove_from' is TRUE. These are
    clients that the `from' owns, and not just clients that are behind
-   the `from'. */
+   the `from'. If `from' is NULL then all non-local clients are switched
+   to `to'. */
 
 void silc_server_update_clients_by_server(SilcServer server, 
                                          SilcServerEntry from,
@@ -506,26 +507,31 @@ void silc_server_update_clients_by_server(SilcServer server,
          SILC_LOG_DEBUG(("Client->router (global) %s", 
                          silc_id_render(client->router->id, SILC_ID_SERVER)));
 
-       if (client->router == from) {
-         /* Skip clients that are *really* owned by the `from' */
-         if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
-                                            client->id->ip.data_len)) {
-           SILC_LOG_DEBUG(("Found really owned client, skip it"));
-           if (!silc_idcache_list_next(list, &id_cache))
-             break;
-           else
-             continue;
-         }
-
-         if (resolve_real_server) {
-           client->router = 
-             silc_server_update_clients_by_real_server(server, from, client,
-                                                       local, id_cache);
-           if (!client->router)
+       if (from) {
+         if (client->router == from) {
+           /* Skip clients that are *really* owned by the `from' */
+           if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
+                                              client->id->ip.data_len)) {
+             SILC_LOG_DEBUG(("Found really owned client, skip it"));
+             if (!silc_idcache_list_next(list, &id_cache))
+               break;
+             else
+               continue;
+           }
+
+           if (resolve_real_server) {
+             client->router = 
+               silc_server_update_clients_by_real_server(server, from, client,
+                                                         local, id_cache);
+             if (!client->router)
+               client->router = to;
+           } else {
              client->router = to;
-         } else {
-           client->router = to;
+           }
          }
+       } else {
+         /* All are changed */
+         client->router = to;
        }
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -557,26 +563,31 @@ void silc_server_update_clients_by_server(SilcServer server,
          SILC_LOG_DEBUG(("Client->router (local) %s", 
                          silc_id_render(client->router->id, SILC_ID_SERVER)));
 
-       if (client->router == from) {
-         /* Skip clients that are *really* owned by the `from' */
-         if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
-                                            client->id->ip.data_len)) {
-           SILC_LOG_DEBUG(("Found really owned client, skip it"));
-           if (!silc_idcache_list_next(list, &id_cache))
-             break;
-           else
-             continue;
-         }
-
-         if (resolve_real_server) {
-           client->router = 
-             silc_server_update_clients_by_real_server(server, from, client,
-                                                       local, id_cache);
-           if (!client->router)
-             client->router = from; /* on local list put old from */
-         } else {
-           client->router = to;
+       if (from) {
+         if (client->router == from) {
+           /* Skip clients that are *really* owned by the `from' */
+           if (remove_from && SILC_ID_COMPARE(from->id, client->id, 
+                                              client->id->ip.data_len)) {
+             SILC_LOG_DEBUG(("Found really owned client, skip it"));
+             if (!silc_idcache_list_next(list, &id_cache))
+               break;
+             else
+               continue;
+           }
+
+           if (resolve_real_server) {
+             client->router = 
+               silc_server_update_clients_by_real_server(server, from, client,
+                                                         local, id_cache);
+             if (!client->router)
+               client->router = from; /* on local list put old from */
+           } else {
+             client->router = to;
+           }
          }
+       } else {
+         /* All are changed */
+         client->router = to;
        }
 
        if (!silc_idcache_list_next(list, &id_cache))
@@ -1527,3 +1538,25 @@ bool silc_server_force_cumode_change(SilcServer server,
 
   return TRUE;
 }
+
+/* Find active socket connection by the IP address and port indicated by
+   `ip' and `port', and socket connection type of `type'. */
+
+SilcSocketConnection
+silc_server_find_socket_by_host(SilcServer server,
+                               SilcSocketType type,
+                               const char *ip, SilcUInt16 port)
+{
+  int i;
+
+  for (i = 0; i < server->config->param.connections_max; i++) {
+    if (!server->sockets[i])
+      continue;
+    if (!strcmp(server->sockets[i]->ip, ip) &&
+       (!port || server->sockets[i]->port == port) &&
+       server->sockets[i]->type == type)
+      return server->sockets[i];
+  }
+
+  return NULL;
+}
index 6f67e25c5669fc32fba37e7cd11e5f5b6e88c498..ec434bc2ef99bb9b64da9e9cbb27809ac4818772 100644 (file)
@@ -37,7 +37,8 @@ bool silc_server_remove_clients_by_server(SilcServer server,
    be the new source. This function also removes the clients that are
    *really* originated from `from' if `remove_from' is TRUE. These are
    clients that the `from' owns, and not just clients that are behind
-   the `from'. */
+   the `from'. If `from' is NULL then all non-local clients are switched
+   to `to'. */
 void silc_server_update_clients_by_server(SilcServer server, 
                                          SilcServerEntry from,
                                          SilcServerEntry to,
@@ -97,7 +98,7 @@ char *silc_server_name_modify_bad(const char *name, SilcUInt32 name_len);
 SilcUInt32 silc_server_num_sockets_by_ip(SilcServer server, const char *ip,
                                         SilcSocketType type);
 
-/* Find number of sockets by IP address indicated by remote host, indicatd
+/* Find number of sockets by IP address indicated by remote host, indicated
    by `ip' or `hostname', `port', and `type'.  Returns 0 if socket connections
    does not exist. If `ip' is provided then `hostname' is ignored. */
 SilcUInt32 silc_server_num_sockets_by_remote(SilcServer server, 
@@ -180,4 +181,11 @@ bool silc_server_force_cumode_change(SilcServer server,
                                     SilcChannelClientEntry chl,
                                     SilcUInt32 forced_mode);
 
+/* Find active socket connection by the IP address and port indicated by
+   `ip' and `port', and socket connection type of `type'. */
+SilcSocketConnection
+silc_server_find_socket_by_host(SilcServer server,
+                               SilcSocketType type,
+                               const char *ip, SilcUInt16 port);
+
 #endif /* SERVER_UTIL_H */