X-Git-Url: http://git.silcnet.org/gitweb/?p=silc.git;a=blobdiff_plain;f=apps%2Fsilcd%2Fserver_util.c;h=dead873a6d250037bff3b3c9a91a585e9a4acc0a;hp=2a7cb7a74a36de7c7b6912b76c96000ce5df82e6;hb=382d15d447b7a95390decfa783836ae4fe255b3d;hpb=f82024f04b73e3b80ddd6c590699206d3dc5c1eb diff --git a/apps/silcd/server_util.c b/apps/silcd/server_util.c index 2a7cb7a7..dead873a 100644 --- a/apps/silcd/server_util.c +++ b/apps/silcd/server_util.c @@ -28,22 +28,26 @@ extern char *server_version; keys are regnerated. This is called only by the function silc_server_remove_clients_by_server. */ -static void silc_server_remove_clients_channels(SilcServer server, - SilcSocketConnection sock, - SilcClientEntry client, - SilcHashTable channels) +static void +silc_server_remove_clients_channels(SilcServer server, + SilcServerEntry server_entry, + SilcHashTable clients, + SilcClientEntry client, + SilcHashTable channels) { SilcChannelEntry channel; - SilcChannelClientEntry chl; - SilcHashTableList htl; - SilcBuffer clidp; - - SILC_LOG_DEBUG(("Start")); + SilcChannelClientEntry chl, chl2; + SilcHashTableList htl, htl2; - if (!client || !client->id) + if (!client) return; - clidp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); + SILC_LOG_DEBUG(("Remove client %s from all channels", + client->nickname ? client->nickname : + (unsigned char *)"")); + + if (silc_hash_table_find(clients, client, NULL, NULL)) + silc_hash_table_del(clients, client); /* Remove the client from all channels. The client is removed from the channels' user list. */ @@ -53,7 +57,7 @@ static void silc_server_remove_clients_channels(SilcServer server, /* Remove channel if this is last client leaving the channel, unless the channel is permanent. */ - if (server->server_type == SILC_ROUTER && + if (server->server_type != SILC_SERVER && silc_hash_table_count(channel->user_list) < 2) { if (silc_hash_table_find(channels, channel, NULL, NULL)) silc_hash_table_del(channels, channel); @@ -73,12 +77,19 @@ static void silc_server_remove_clients_channels(SilcServer server, channel->global_users = FALSE; silc_free(chl); - server->stat.my_chanclients--; + + /* Update statistics */ + if (SILC_IS_LOCAL(client)) + server->stat.my_chanclients--; + if (server->server_type == SILC_ROUTER) { + server->stat.cell_chanclients--; + server->stat.chanclients--; + } /* If there is not at least one local user on the channel then we don't need the channel entry anymore, we can remove it safely, unless the channel is permanent channel */ - if (server->server_type != SILC_ROUTER && + if (server->server_type == SILC_SERVER && !silc_server_channel_has_local(channel)) { if (silc_hash_table_find(channels, channel, NULL, NULL)) silc_hash_table_del(channels, channel); @@ -87,22 +98,36 @@ static void silc_server_remove_clients_channels(SilcServer server, continue; } + /* Mark other local clients to the table of clients whom will receive + the SERVER_SIGNOFF notify. */ + silc_hash_table_list(channel->user_list, &htl2); + while (silc_hash_table_get(&htl2, NULL, (void **)&chl2)) { + SilcClientEntry c = chl2->client; + if (!c) + continue; + + /* Add client to table, if it's not from the signoff server */ + if (c->router != server_entry && + !silc_hash_table_find(clients, c, NULL, NULL)) + silc_hash_table_add(clients, c, c); + } + silc_hash_table_list_reset(&htl2); + /* Add the channel to the the channels list to regenerate the channel key */ if (!silc_hash_table_find(channels, channel, NULL, NULL)) silc_hash_table_add(channels, channel, channel); } silc_hash_table_list_reset(&htl); - silc_buffer_free(clidp); } -/* This function is used to remove all client entries by the server `entry'. - This is called when the connection is lost to the server. In this case - we must invalidate all the client entries owned by the server `entry'. - If the `server_signoff' is TRUE then the SERVER_SIGNOFF notify is +/* This function removes all client entries that are originated from + `router' and are owned by `entry'. `router' and `entry' can be same + too. If `server_signoff' is TRUE then SERVER_SIGNOFF notify is distributed to our local clients. */ -bool silc_server_remove_clients_by_server(SilcServer server, +bool silc_server_remove_clients_by_server(SilcServer server, + SilcServerEntry router, SilcServerEntry entry, bool server_signoff) { @@ -110,22 +135,29 @@ bool silc_server_remove_clients_by_server(SilcServer server, SilcIDCacheEntry id_cache = NULL; SilcClientEntry client = NULL; SilcBuffer idp; - SilcClientEntry *clients = NULL; - SilcUInt32 clients_c = 0; unsigned char **argv = NULL; SilcUInt32 *argv_lens = NULL, *argv_types = NULL, argc = 0; SilcHashTableList htl; SilcChannelEntry channel; - SilcHashTable channels; + SilcHashTable channels, clients; int i; - SILC_LOG_DEBUG(("Start")); + if (!(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) + return FALSE; + + SILC_LOG_DEBUG(("Removing clients by %s", + entry->server_name ? entry->server_name : "server")); + + if (!router) + router = entry; /* 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); + clients = 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); @@ -141,25 +173,16 @@ bool silc_server_remove_clients_by_server(SilcServer server, } if (silc_idcache_get_all(server->local_list->clients, &list)) { - if (silc_idcache_list_first(list, &id_cache)) { while (id_cache) { client = (SilcClientEntry)id_cache->context; - if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) { - if (!silc_idcache_list_next(list, &id_cache)) - break; - else - continue; - } - - if (client->router != entry) { - if (server_signoff) { - clients = silc_realloc(clients, - sizeof(*clients) * (clients_c + 1)); - clients[clients_c] = client; - clients_c++; - } + /* If client is not registered, is not originated from `router' + and is not owned by `entry', skip it. */ + if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED) || + client->router != router || + (router != entry && !SILC_ID_COMPARE(client->id, entry->id, + client->id->ip.data_len))) { if (!silc_idcache_list_next(list, &id_cache)) break; else @@ -188,16 +211,17 @@ bool silc_server_remove_clients_by_server(SilcServer server, SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR); SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR); + silc_server_remove_clients_channels(server, entry, clients, + client, channels); + silc_server_del_from_watcher_list(server, client); + /* Remove the client entry */ - silc_server_remove_clients_channels(server, NULL, client, channels); if (!server_signoff) { client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF; } else { + silc_idlist_del_data(client); silc_idlist_del_client(server->local_list, client); - - /* Remove this client from watcher list if it is */ - silc_server_del_from_watcher_list(server, client); } if (!silc_idcache_list_next(list, &id_cache)) @@ -212,21 +236,13 @@ bool silc_server_remove_clients_by_server(SilcServer server, if (silc_idcache_list_first(list, &id_cache)) { while (id_cache) { client = (SilcClientEntry)id_cache->context; - if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) { - if (!silc_idcache_list_next(list, &id_cache)) - break; - else - continue; - } - - if (client->router != entry) { - if (server_signoff && client->connection) { - clients = silc_realloc(clients, - sizeof(*clients) * (clients_c + 1)); - clients[clients_c] = client; - clients_c++; - } + /* If client is not registered, is not originated from `router' + and is not owned by `entry', skip it. */ + if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED) || + client->router != router || + (router != entry && !SILC_ID_COMPARE(client->id, entry->id, + client->id->ip.data_len))) { if (!silc_idcache_list_next(list, &id_cache)) break; else @@ -255,12 +271,16 @@ bool silc_server_remove_clients_by_server(SilcServer server, SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR); SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR); + silc_server_remove_clients_channels(server, entry, clients, + client, channels); + silc_server_del_from_watcher_list(server, client); + /* Remove the client entry */ - silc_server_remove_clients_channels(server, NULL, client, channels); if (!server_signoff) { client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF; } else { + silc_idlist_del_data(client); silc_idlist_del_client(server->global_list, client); } @@ -271,19 +291,34 @@ bool silc_server_remove_clients_by_server(SilcServer server, silc_idcache_list_free(list); } + /* Return now if we are shutting down */ + if (server->server_shutdown) { + silc_hash_table_free(channels); + + if (server_signoff) { + for (i = 0; i < argc; i++) + silc_free(argv[i]); + silc_free(argv); + silc_free(argv_lens); + silc_free(argv_types); + silc_hash_table_free(clients); + } + return TRUE; + } + /* Send the SERVER_SIGNOFF notify */ if (server_signoff) { SilcBuffer args, not; + SILC_LOG_DEBUG(("Sending SERVER_SIGNOFF for %s with %d clients", + silc_id_render(entry->id, SILC_ID_SERVER), argc - 1)); + /* Send SERVER_SIGNOFF notify to our primary router */ - if (!server->standalone && server->router && - server->router != entry) { + if (server->router != entry) { args = silc_argument_payload_encode(1, argv, argv_lens, argv_types); - silc_server_send_notify_args(server, - server->router->connection, - server->server_type == SILC_SERVER ? - FALSE : TRUE, + silc_server_send_notify_args(server, SILC_PRIMARY_ROUTE(server), + SILC_BROADCAST(server), SILC_NOTIFY_TYPE_SERVER_SIGNOFF, argc, args); silc_buffer_free(args); @@ -295,11 +330,14 @@ bool silc_server_remove_clients_by_server(SilcServer server, argv_types); not = silc_notify_payload_encode_args(SILC_NOTIFY_TYPE_SERVER_SIGNOFF, argc, args); - silc_server_packet_send_clients(server, clients, clients_c, + silc_server_packet_send_clients(server, clients, SILC_PACKET_NOTIFY, 0, FALSE, not->data, not->len, FALSE); - silc_free(clients); + /* Send notify also to local backup routers */ + silc_server_backup_send(server, NULL, SILC_PACKET_NOTIFY, 0, + not->data, not->len, FALSE, TRUE); + silc_buffer_free(args); silc_buffer_free(not); for (i = 0; i < argc; i++) @@ -307,6 +345,7 @@ bool silc_server_remove_clients_by_server(SilcServer server, silc_free(argv); silc_free(argv_lens); silc_free(argv_types); + silc_hash_table_free(clients); } /* We must now re-generate the channel key for all channels that had @@ -321,7 +360,7 @@ bool silc_server_remove_clients_by_server(SilcServer server, } /* Do not send the channel key if private channel key mode is set */ - if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) + if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY || !channel->channel_key) continue; silc_server_send_channel_key(server, NULL, channel, @@ -337,6 +376,7 @@ bool silc_server_remove_clients_by_server(SilcServer server, static SilcServerEntry silc_server_update_clients_by_real_server(SilcServer server, SilcServerEntry from, + SilcServerEntry to, SilcClientEntry client, bool local, SilcIDCacheEntry client_cache) @@ -344,6 +384,7 @@ silc_server_update_clients_by_real_server(SilcServer server, SilcServerEntry server_entry; SilcIDCacheEntry id_cache = NULL; SilcIDCacheList list; + bool tolocal = (to == server->id_entry); if (!silc_idcache_get_all(server->local_list->servers, &list)) return NULL; @@ -352,6 +393,7 @@ silc_server_update_clients_by_real_server(SilcServer server, while (id_cache) { server_entry = (SilcServerEntry)id_cache->context; if (server_entry != from && + (tolocal || server_entry != server->id_entry) && SILC_ID_COMPARE(server_entry->id, client->id, client->id->ip.data_len)) { SILC_LOG_DEBUG(("Found (local) %s", @@ -398,7 +440,8 @@ silc_server_update_clients_by_real_server(SilcServer server, if (silc_idcache_list_first(list, &id_cache)) { while (id_cache) { server_entry = (SilcServerEntry)id_cache->context; - if (server_entry != from && + if (server_entry != from && server_entry != server->id_entry && + (tolocal || server_entry != server->id_entry) && SILC_ID_COMPARE(server_entry->id, client->id, client->id->ip.data_len)) { SILC_LOG_DEBUG(("Found (global) %s", @@ -411,8 +454,7 @@ silc_server_update_clients_by_real_server(SilcServer server, if (local) { SILC_LOG_DEBUG(("Moving client to global list")); silc_idcache_add(server->global_list->clients, client_cache->name, - client_cache->id, client_cache->context, - client_cache->expire, NULL); + client_cache->id, client_cache->context, 0, NULL); silc_idcache_del_by_context(server->local_list->clients, client); } server_entry = server_entry->router; @@ -422,8 +464,7 @@ silc_server_update_clients_by_real_server(SilcServer server, if (server_entry->server_type != SILC_BACKUP_ROUTER && local) { SILC_LOG_DEBUG(("Moving client to global list")); silc_idcache_add(server->global_list->clients, client_cache->name, - client_cache->id, client_cache->context, - client_cache->expire, NULL); + client_cache->id, client_cache->context, 0, NULL); silc_idcache_del_by_context(server->local_list->clients, client); } } @@ -447,70 +488,66 @@ silc_server_update_clients_by_real_server(SilcServer server, attempt to figure out which clients really are originated from the `from' and which are originated from a server that we have connection to, when we've acting as backup router. If it is FALSE the `to' will - 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'. */ + be the new source. */ void silc_server_update_clients_by_server(SilcServer server, SilcServerEntry from, SilcServerEntry to, - bool resolve_real_server, - bool remove_from) + bool resolve_real_server) { SilcIDCacheList list = NULL; SilcIDCacheEntry id_cache = NULL; SilcClientEntry client = NULL; bool local; - SILC_LOG_DEBUG(("Start")); - - SILC_LOG_DEBUG(("Updating %s", silc_id_render(from->id, - SILC_ID_SERVER))); - SILC_LOG_DEBUG(("to %s", silc_id_render(to->id, - SILC_ID_SERVER))); - - local = FALSE; if (silc_idcache_get_all(server->global_list->clients, &list)) { if (silc_idcache_list_first(list, &id_cache)) { while (id_cache) { client = (SilcClientEntry)id_cache->context; - if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) { + + /* If entry is disabled skip it. If entry is local to us, do not + switch it to anyone else, it is ours so skip it. */ + if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED) || + SILC_IS_LOCAL(client)) { if (!silc_idcache_list_next(list, &id_cache)) break; else continue; } - SILC_LOG_DEBUG(("Client (global) %s", + SILC_LOG_DEBUG(("Client %s", silc_id_render(client->id, SILC_ID_CLIENT))); if (client->router) - SILC_LOG_DEBUG(("Client->router (global) %s", + SILC_LOG_DEBUG(("Client->router %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) { + if (resolve_real_server) { + client->router = + silc_server_update_clients_by_real_server(server, from, to, + client, local, + id_cache); + if (!client->router) { + if (server->server_type == SILC_ROUTER) + client->router = from; + else + client->router = to; + } + } else { client->router = to; - } else { - client->router = to; + } } + } else { + /* All are changed */ + client->router = to; } + if (client->router) + SILC_LOG_DEBUG(("Client changed to %s", + silc_id_render(client->router->id, SILC_ID_SERVER))); + if (!silc_idcache_list_next(list, &id_cache)) break; } @@ -523,53 +560,51 @@ void silc_server_update_clients_by_server(SilcServer server, if (silc_idcache_list_first(list, &id_cache)) { while (id_cache) { client = (SilcClientEntry)id_cache->context; - if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) { + + /* If entry is disabled skip it. If entry is local to us, do not + switch it to anyone else, it is ours so skip it. */ + if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED) || + SILC_IS_LOCAL(client)) { if (!silc_idcache_list_next(list, &id_cache)) break; else continue; } - SILC_LOG_DEBUG(("Client (local) %s", + SILC_LOG_DEBUG(("Client %s", silc_id_render(client->id, SILC_ID_CLIENT))); if (client->router) - SILC_LOG_DEBUG(("Client->router (local) %s", + SILC_LOG_DEBUG(("Client->router %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) { + if (resolve_real_server) { + client->router = + silc_server_update_clients_by_real_server(server, from, to, + client, local, + id_cache); + if (!client->router) + client->router = from; + } else { + client->router = to; + } } + } else { + /* All are changed */ + client->router = to; } + if (client->router) + SILC_LOG_DEBUG(("Client changed to %s", + silc_id_render(client->router->id, SILC_ID_SERVER))); + if (!silc_idcache_list_next(list, &id_cache)) break; } } silc_idcache_list_free(list); } - - if (remove_from) - /* Now remove the clients that are still marked as orignated from the - `from'. These are the clients that really was owned by the `from' and - not just exist behind the `from'. */ - silc_server_remove_clients_by_server(server, from, TRUE); } /* Updates servers that are from `from' to be originated from `to'. This @@ -583,16 +618,129 @@ void silc_server_update_servers_by_server(SilcServer server, SilcIDCacheEntry id_cache = NULL; SilcServerEntry server_entry = NULL; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Updating servers")); + + if (silc_idcache_get_all(server->local_list->servers, &list)) { + if (silc_idcache_list_first(list, &id_cache)) { + while (id_cache) { + server_entry = (SilcServerEntry)id_cache->context; + + /* If entry is local to us, do not switch it to any anyone else, + it is ours. */ + if (SILC_IS_LOCAL(server_entry) || server_entry == server->id_entry || + server_entry == from) { + if (!silc_idcache_list_next(list, &id_cache)) + break; + else + continue; + } + + /* If we are standalone router, any server that is not directly + connected to cannot exist anymore. If we are not standalone + we update it correctly. */ + if (server->server_type == SILC_ROUTER && server->standalone) { + silc_server_backup_del(server, server_entry); + silc_server_backup_replaced_del(server, server_entry); + silc_idlist_del_data(server_entry); + silc_idlist_del_server(server->local_list, server_entry); + server->stat.servers--; + server->stat.cell_servers--; + } else { + /* XXX if we are not standalone, do a check from local config + whether this server is in our cell, but not connected to + us (in which case we must remove it). */ + + if (server_entry->router == from) { + SILC_LOG_DEBUG(("Updating server (local) %s", + server_entry->server_name ? + server_entry->server_name : "")); + server_entry->router = to; + server_entry->connection = to->connection; + } + } + + if (!silc_idcache_list_next(list, &id_cache)) + break; + } + } + silc_idcache_list_free(list); + } + + if (silc_idcache_get_all(server->global_list->servers, &list)) { + if (silc_idcache_list_first(list, &id_cache)) { + while (id_cache) { + server_entry = (SilcServerEntry)id_cache->context; + + /* If entry is local to us, do not switch it to anyone else, + it is ours. */ + if (SILC_IS_LOCAL(server_entry) || server_entry == server->id_entry || + server_entry == from) { + if (!silc_idcache_list_next(list, &id_cache)) + break; + else + continue; + } + + /* If we are standalone router, any server that is not directly + connected to cannot exist anymore. If we are not standalone + we update it correctly. */ + if (server->server_type == SILC_ROUTER && server->standalone) { + silc_server_backup_del(server, server_entry); + silc_server_backup_replaced_del(server, server_entry); + silc_idlist_del_data(server_entry); + silc_idlist_del_server(server->global_list, server_entry); + server->stat.servers--; + server->stat.cell_servers--; + } else { + /* XXX if we are not standalone, do a check from local config + whether this server is in our cell, but not connected to + us (in which case we must remove it). */ + + if (server_entry->router == from) { + SILC_LOG_DEBUG(("Updating server (global) %s", + server_entry->server_name ? + server_entry->server_name : "")); + server_entry->router = to; + server_entry->connection = to->connection; + } + } + + if (!silc_idcache_list_next(list, &id_cache)) + break; + } + } + silc_idcache_list_free(list); + } +} + + +/* Toggles the enabled/disabled status of local server connections. Packets + can be sent to the servers when `toggle_enabled' is TRUE and will be + dropped if `toggle_enabled' is FALSE, after this function is called. */ + +void silc_server_local_servers_toggle_enabled(SilcServer server, + bool toggle_enabled) +{ + SilcIDCacheList list = NULL; + SilcIDCacheEntry id_cache = NULL; + SilcServerEntry server_entry = NULL; if (silc_idcache_get_all(server->local_list->servers, &list)) { if (silc_idcache_list_first(list, &id_cache)) { while (id_cache) { server_entry = (SilcServerEntry)id_cache->context; - if (server_entry->router == from) { - server_entry->router = to; - server_entry->connection = to->connection; + if (!SILC_IS_LOCAL(server_entry) || server_entry == server->id_entry) { + if (!silc_idcache_list_next(list, &id_cache)) + break; + else + continue; } + + if (toggle_enabled) + server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED; + else + server_entry->data.status |= SILC_IDLIST_STATUS_DISABLED; + if (!silc_idcache_list_next(list, &id_cache)) break; } @@ -604,10 +752,90 @@ void silc_server_update_servers_by_server(SilcServer server, if (silc_idcache_list_first(list, &id_cache)) { while (id_cache) { server_entry = (SilcServerEntry)id_cache->context; - if (server_entry->router == from) { - server_entry->router = to; - server_entry->connection = to->connection; + if (!SILC_IS_LOCAL(server_entry) || server_entry == server->id_entry) { + if (!silc_idcache_list_next(list, &id_cache)) + break; + else + continue; } + + if (toggle_enabled) + server_entry->data.status &= ~SILC_IDLIST_STATUS_DISABLED; + else + server_entry->data.status |= SILC_IDLIST_STATUS_DISABLED; + + if (!silc_idcache_list_next(list, &id_cache)) + break; + } + } + silc_idcache_list_free(list); + } +} + +/* Removes servers that are originated from the `from'. The server + entry is deleted in this function. If `remove_clients' is TRUE then + all clients originated from the server are removed too, and server + signoff is sent. Note that this does not remove the `from'. This + also does not remove locally connected servers. */ + +void silc_server_remove_servers_by_server(SilcServer server, + SilcServerEntry from, + bool remove_clients) +{ + SilcIDCacheList list = NULL; + SilcIDCacheEntry id_cache = NULL; + SilcServerEntry server_entry = NULL; + + SILC_LOG_DEBUG(("Removing servers by %s", + from->server_name ? from->server_name : "server")); + + if (silc_idcache_get_all(server->local_list->servers, &list)) { + if (silc_idcache_list_first(list, &id_cache)) { + while (id_cache) { + server_entry = (SilcServerEntry)id_cache->context; + if (SILC_IS_LOCAL(server_entry) || server_entry == server->id_entry || + server_entry->router != from || server_entry == from) { + if (!silc_idcache_list_next(list, &id_cache)) + break; + else + continue; + } + + /* Remove clients owned by this server */ + if (remove_clients) + silc_server_remove_clients_by_server(server, from, server_entry, + TRUE); + + /* Remove the server */ + silc_idlist_del_server(server->local_list, server_entry); + + if (!silc_idcache_list_next(list, &id_cache)) + break; + } + } + silc_idcache_list_free(list); + } + + if (silc_idcache_get_all(server->global_list->servers, &list)) { + if (silc_idcache_list_first(list, &id_cache)) { + while (id_cache) { + server_entry = (SilcServerEntry)id_cache->context; + if (SILC_IS_LOCAL(server_entry) || server_entry == server->id_entry || + server_entry->router != from || server_entry == from) { + if (!silc_idcache_list_next(list, &id_cache)) + break; + else + continue; + } + + /* Remove clients owned by this server */ + if (remove_clients) + silc_server_remove_clients_by_server(server, from, server_entry, + TRUE); + + /* Remove the server */ + silc_idlist_del_server(server->global_list, server_entry); + if (!silc_idcache_list_next(list, &id_cache)) break; } @@ -625,7 +853,7 @@ void silc_server_remove_channels_by_server(SilcServer server, SilcIDCacheEntry id_cache = NULL; SilcChannelEntry channel = NULL; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Removing channels by server")); if (silc_idcache_get_all(server->global_list->channels, &list)) { if (silc_idcache_list_first(list, &id_cache)) { @@ -651,7 +879,7 @@ void silc_server_update_channels_by_server(SilcServer server, SilcIDCacheEntry id_cache = NULL; SilcChannelEntry channel = NULL; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Updating channels by server")); if (silc_idcache_get_all(server->global_list->channels, &list)) { if (silc_idcache_list_first(list, &id_cache)) { @@ -697,7 +925,7 @@ bool silc_server_channel_has_local(SilcChannelEntry channel) silc_hash_table_list(channel->user_list, &htl); while (silc_hash_table_get(&htl, NULL, (void **)&chl)) { - if (!chl->client->router) { + if (SILC_IS_LOCAL(chl->client)) { silc_hash_table_list_reset(&htl); return TRUE; } @@ -720,15 +948,25 @@ bool silc_server_channel_delete(SilcServer server, bool delchan = !(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH); if (delchan) { - SILC_LOG_DEBUG(("Deleting %s channel", channel->channel_name)); + /* Update statistics */ + if (server->server_type == SILC_ROUTER) + server->stat.chanclients -= channel->user_count; /* Totally delete the channel and all users on the channel. The users are deleted automatically in silc_idlist_del_channel. */ silc_schedule_task_del_by_context(server->schedule, channel->rekey); - if (silc_idlist_del_channel(server->local_list, channel)) + if (silc_idlist_del_channel(server->local_list, channel)) { server->stat.my_channels--; - else - silc_idlist_del_channel(server->global_list, channel); + if (server->server_type == SILC_ROUTER) { + server->stat.channels--; + server->stat.cell_channels--; + } + } else { + if (silc_idlist_del_channel(server->global_list, channel)) + if (server->server_type == SILC_ROUTER) + server->stat.channels--; + } + return FALSE; } @@ -739,6 +977,15 @@ bool silc_server_channel_delete(SilcServer server, silc_hash_table_del(chl->client->channels, channel); silc_hash_table_del(channel->user_list, chl->client); channel->user_count--; + + /* Update statistics */ + if (SILC_IS_LOCAL(chl->client)) + server->stat.my_chanclients--; + if (server->server_type == SILC_ROUTER) { + server->stat.cell_chanclients--; + server->stat.chanclients--; + } + silc_free(chl); } silc_hash_table_list_reset(&htl); @@ -812,7 +1059,8 @@ SilcUInt32 silc_server_num_sockets_by_ip(SilcServer server, const char *ip, int i, count; for (i = 0, count = 0; i < server->config->param.connections_max; i++) { - if (server->sockets[i] && !strcmp(server->sockets[i]->ip, ip) && + if (server->sockets[i] && !SILC_IS_LISTENER(server->sockets[i]) && + !strcmp(server->sockets[i]->ip, ip) && server->sockets[i]->type == type) count++; } @@ -836,7 +1084,7 @@ SilcUInt32 silc_server_num_sockets_by_remote(SilcServer server, return 0; for (i = 0, count = 0; i < server->config->param.connections_max; i++) { - if (server->sockets[i] && + if (server->sockets[i] && !SILC_IS_LISTENER(server->sockets[i]) && ((ip && !strcmp(server->sockets[i]->ip, ip)) || (hostname && !strcmp(server->sockets[i]->hostname, hostname))) && server->sockets[i]->port == port && @@ -918,6 +1166,8 @@ bool silc_server_connection_allowed(SilcServer server, SilcUInt32 r_software_version, l_software_version; char *r_vendor_version = NULL, *l_vendor_version; + SILC_LOG_DEBUG(("Checking whether connection is allowed")); + /* Check version */ l_protocol_version = @@ -1019,14 +1269,17 @@ bool silc_server_check_cmode_rights(SilcServer server, if (is_op && is_fo) return TRUE; + /* Founder implies operator */ + if (is_fo) + is_op = TRUE; + /* We know that client is channel operator, check that they are not changing anything that requires channel founder rights. Rest of the modes are available automatically for channel operator. */ if (mode & SILC_CHANNEL_MODE_PRIVKEY) { - if (!(channel->mode & SILC_CHANNEL_MODE_PRIVKEY)) - if (is_op && !is_fo) - return FALSE; + if (is_op && !is_fo) + return FALSE; } else { if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) { if (is_op && !is_fo) @@ -1035,9 +1288,10 @@ bool silc_server_check_cmode_rights(SilcServer server, } if (mode & SILC_CHANNEL_MODE_PASSPHRASE) { - if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE)) + if (!(channel->mode & SILC_CHANNEL_MODE_PASSPHRASE)) { if (is_op && !is_fo) return FALSE; + } } else { if (channel->mode & SILC_CHANNEL_MODE_PASSPHRASE) { if (is_op && !is_fo) @@ -1046,9 +1300,10 @@ bool silc_server_check_cmode_rights(SilcServer server, } if (mode & SILC_CHANNEL_MODE_CIPHER) { - if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) + if (!(channel->mode & SILC_CHANNEL_MODE_CIPHER)) { if (is_op && !is_fo) return FALSE; + } } else { if (channel->mode & SILC_CHANNEL_MODE_CIPHER) { if (is_op && !is_fo) @@ -1057,9 +1312,10 @@ bool silc_server_check_cmode_rights(SilcServer server, } if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) { - if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) + if (!(channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH)) { if (is_op && !is_fo) return FALSE; + } } else { if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) { if (is_op && !is_fo) @@ -1068,9 +1324,10 @@ bool silc_server_check_cmode_rights(SilcServer server, } if (mode & SILC_CHANNEL_MODE_SILENCE_USERS) { - if (!(channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS)) + if (!(channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS)) { if (is_op && !is_fo) return FALSE; + } } else { if (channel->mode & SILC_CHANNEL_MODE_SILENCE_USERS) { if (is_op && !is_fo) @@ -1079,9 +1336,10 @@ bool silc_server_check_cmode_rights(SilcServer server, } if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS) { - if (!(channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS)) + if (!(channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS)) { if (is_op && !is_fo) return FALSE; + } } else { if (channel->mode & SILC_CHANNEL_MODE_SILENCE_OPERS) { if (is_op && !is_fo) @@ -1138,6 +1396,8 @@ void silc_server_send_connect_notifys(SilcServer server, { SilcIDListData idata = (SilcIDListData)client; + SILC_LOG_DEBUG(("Send welcome notifys")); + /* Send some nice info to the client */ SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, ("Welcome to the SILC Network %s", @@ -1146,11 +1406,23 @@ void silc_server_send_connect_notifys(SilcServer server, ("Your host is %s, running version %s", server->server_name, server_version)); - if (server->stat.clients && server->stat.servers + 1) + if (server->server_type == SILC_ROUTER) { SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, - ("There are %d clients on %d servers in SILC " - "Network", server->stat.clients, - server->stat.servers + 1)); + ("There are %d clients, %d servers and %d " + "routers in SILC Network", + server->stat.clients, server->stat.servers + 1, + server->stat.routers)); + } else { + if (server->stat.clients && server->stat.servers + 1) + SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, + ("There are %d clients, %d servers and %d " + "routers in SILC Network", + server->stat.clients, server->stat.servers + 1, + (server->standalone ? 0 : + !server->stat.routers ? 1 : + server->stat.routers))); + } + if (server->stat.cell_clients && server->stat.cell_servers + 1) SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, ("There are %d clients on %d server in our cell", @@ -1186,8 +1458,8 @@ void silc_server_send_connect_notifys(SilcServer server, SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, ("Your connection is secured with %s cipher, " "key length %d bits", - idata->send_key->cipher->name, - idata->send_key->cipher->key_len)); + silc_cipher_get_name(idata->send_key), + silc_cipher_get_key_len(idata->send_key))); SILC_SERVER_SEND_NOTIFY(server, sock, SILC_NOTIFY_TYPE_NONE, ("Your current nickname is %s", client->nickname)); @@ -1208,6 +1480,9 @@ void silc_server_kill_client(SilcServer server, { SilcBuffer killed, killer; + SILC_LOG_DEBUG(("Killing client %s", + silc_id_render(remote_client->id, SILC_ID_CLIENT))); + /* Send the KILL notify packets. First send it to the channel, then to our primary router and then directly to the client who is being killed right now. */ @@ -1225,10 +1500,9 @@ void silc_server_kill_client(SilcServer server, killer->data, killer->len); /* Send KILLED notify to primary route */ - if (!server->standalone) - silc_server_send_notify_killed(server, server->router->connection, TRUE, - remote_client->id, comment, - killer_id, killer_id_type); + silc_server_send_notify_killed(server, SILC_PRIMARY_ROUTE(server), + SILC_BROADCAST(server), remote_client->id, + comment, killer_id, killer_id_type); /* Send KILLED notify to the client directly */ if (remote_client->connection || remote_client->router) @@ -1241,7 +1515,7 @@ void silc_server_kill_client(SilcServer server, /* Remove the client from all channels. This generates new keys to the channels as well. */ silc_server_remove_from_channels(server, NULL, remote_client, FALSE, - NULL, TRUE); + NULL, TRUE, TRUE); /* Remove the client entry, If it is locally connected then we will also disconnect the client here */ @@ -1253,17 +1527,22 @@ void silc_server_kill_client(SilcServer server, } else { /* Update statistics */ server->stat.clients--; - server->stat.my_clients--; if (server->stat.cell_clients) server->stat.cell_clients--; SILC_OPER_STATS_UPDATE(remote_client, server, SILC_UMODE_SERVER_OPERATOR); SILC_OPER_STATS_UPDATE(remote_client, router, SILC_UMODE_ROUTER_OPERATOR); + if (SILC_IS_LOCAL(remote_client)) { + server->stat.my_clients--; + silc_schedule_task_del_by_context(server->schedule, remote_client); + silc_idlist_del_data(remote_client); + } + /* Remove remote client */ + silc_idlist_del_data(remote_client); if (!silc_idlist_del_client(server->global_list, remote_client)) { /* Remove this client from watcher list if it is */ silc_server_del_from_watcher_list(server, remote_client); - silc_idlist_del_client(server->local_list, remote_client); } } @@ -1287,6 +1566,9 @@ silc_server_check_watcher_list_foreach(void *key, void *context, SilcClientEntry entry = context; SilcSocketConnection sock; + if (!context) + return; + if (entry == notify->client) return; @@ -1317,7 +1599,8 @@ bool silc_server_check_watcher_list(SilcServer server, unsigned char hash[16]; WatcherNotifyContext n; - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Checking watcher list %s", + client->nickname ? client->nickname : (unsigned char *)"")); /* If the watching is rejected by the client do nothing */ if (client->mode & SILC_UMODE_REJECT_WATCHING) @@ -1362,8 +1645,9 @@ bool silc_server_del_from_watcher_list(SilcServer server, if (entry == client) { silc_hash_table_del_by_context(server->watcher_list, key, client); - SILC_LOG_DEBUG(("Removing %s from WATCH list", - silc_id_render(client->id, SILC_ID_CLIENT))); + if (client->id) + SILC_LOG_DEBUG(("Removing %s from WATCH list", + silc_id_render(client->id, SILC_ID_CLIENT))); /* Now check whether there still exists entries with this key, if not then free the key to not leak memory. */ @@ -1390,9 +1674,12 @@ bool silc_server_force_cumode_change(SilcServer server, SilcBuffer idp1, idp2; unsigned char cumode[4]; - silc_server_send_notify_cumode(server, sock, FALSE, channel, forced_mode, - server->id, SILC_ID_SERVER, - chl->client->id); + SILC_LOG_DEBUG(("Enforcing sender to change mode")); + + if (sock) + silc_server_send_notify_cumode(server, sock, FALSE, channel, forced_mode, + server->id, SILC_ID_SERVER, + chl->client->id, NULL); idp1 = silc_id_payload_encode(server->id, SILC_ID_SERVER); idp2 = silc_id_payload_encode(chl->client->id, SILC_ID_CLIENT); @@ -1404,4 +1691,259 @@ bool silc_server_force_cumode_change(SilcServer server, idp2->data, idp2->len); silc_buffer_free(idp1); silc_buffer_free(idp2); + + 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; +} + +/* This function can be used to match the invite and ban lists. */ + +bool silc_server_inviteban_match(SilcServer server, SilcHashTable list, + SilcUInt8 type, void *check) +{ + unsigned char *tmp = NULL; + SilcUInt32 len = 0, t; + SilcHashTableList htl; + SilcBuffer entry, idp = NULL; + bool ret = FALSE; + + if (type < 1 || type > 3 || !check) + return FALSE; + + if (type == 1) { + tmp = strdup((char *)check); + if (!tmp) + return FALSE; + } + if (type == 2) { + tmp = silc_pkcs_public_key_encode(check, &len); + if (!tmp) + return FALSE; + } + if (type == 3) { + idp = silc_id_payload_encode(check, SILC_ID_CLIENT); + if (!idp) + return FALSE; + tmp = idp->data; + len = idp->len; + } + + /* Compare the list */ + silc_hash_table_list(list, &htl); + while (silc_hash_table_get(&htl, (void **)&t, (void **)&entry)) { + if (type == t) { + if (type == 1) { + if (silc_string_match((char *)entry, tmp)) { + ret = TRUE; + break; + } + } else if (!memcmp(entry->data, tmp, len)) { + ret = TRUE; + break; + } + } + } + silc_hash_table_list_reset(&htl); + + if (!idp) + silc_free(tmp); + silc_buffer_free(idp); + return ret; +} + +static void silc_server_inviteban_dummy_dest(void *key, void *context, + void *user_context) +{ + /* Nothing */ +} + +/* Process invite or ban information */ + +void silc_server_inviteban_process(SilcServer server, SilcHashTable list, + SilcUInt8 action, SilcArgumentPayload args) +{ + unsigned char *tmp; + SilcUInt32 type, len; + SilcBuffer tmp2; + SilcHashTableList htl; + + SILC_LOG_DEBUG(("Processing invite/ban for %s action", + action == 0x00 ? "ADD" : "DEL")); + + /* Add the information to invite list */ + if (action == 0x00) { + /* Traverse all arguments and add to the hash table according to + their type. */ + tmp = silc_argument_get_first_arg(args, &type, &len); + while (tmp) { + if (type == 1) { + /* Invite string. Get the old invite string from hash table + and append this at the end of the existing one. */ + char *string = NULL; + silc_hash_table_find(list, (void *)1, + NULL, (void **)&string); + silc_hash_table_del_ext(list, (void *)1, NULL, NULL, NULL, NULL, + silc_server_inviteban_dummy_dest, NULL); + if (!string) + string = silc_calloc(len + 2, sizeof(*string)); + else + string = silc_realloc(string, sizeof(*string) * + (strlen(string) + len + 2)); + memset(string + strlen(string), 0, len + 2); + if (tmp[len - 1] == ',') + tmp[len - 1] = '\0'; + strncat(string, tmp, len); + strncat(string, ",", 1); + + /* Add new invite string to invite list */ + silc_hash_table_add(list, (void *)1, string); + + } else if (type == 2) { + /* Public key. Check first if the public key is already on the + list and ignore it if it is, otherwise, add it to hash table. */ + + /* Check if the public key is in the list already */ + silc_hash_table_list(list, &htl); + while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) { + if (type == 2 && !memcmp(tmp2->data, tmp, len)) { + tmp = NULL; + break; + } + } + silc_hash_table_list_reset(&htl); + + /* Add new public key to invite list */ + if (tmp) { + tmp2 = silc_buffer_alloc_size(len); + silc_buffer_put(tmp2, tmp, len); + silc_hash_table_add(list, (void *)2, tmp2); + } + + } else if (type == 3) { + /* Client ID */ + + /* Check if the ID is in the list already */ + silc_hash_table_list(list, &htl); + while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) { + if (type == 3 && !memcmp(tmp2->data, tmp, len)) { + tmp = NULL; + break; + } + } + silc_hash_table_list_reset(&htl); + + /* Add new Client ID to invite list */ + if (tmp) { + tmp2 = silc_buffer_alloc_size(len); + silc_buffer_put(tmp2, tmp, len); + silc_hash_table_add(list, (void *)3, tmp2); + } + } + + tmp = silc_argument_get_next_arg(args, &type, &len); + } + } + + /* Delete information to invite list */ + if (action == 0x01 && list) { + /* Now delete the arguments from invite list */ + tmp = silc_argument_get_first_arg(args, &type, &len); + while (tmp) { + if (type == 1) { + /* Invite string. Get the old string from hash table and delete + the requested string. */ + char *string = NULL, *start, *end, *n; + + if (silc_hash_table_find(list, (void *)1, NULL, (void **)&string)) { + if (!strncmp(string, tmp, strlen(string) - 1)) { + silc_hash_table_del(list, (void *)1); + string = NULL; + } else { + start = strstr(string, tmp); + if (start && strlen(start) >= len) { + end = start + len; + n = silc_calloc(strlen(string) - len, sizeof(*n)); + strncat(n, string, start - string); + strncat(n, end + 1, ((string + strlen(string)) - end) - 1); + silc_hash_table_del(list, (void *)1); + string = n; + } + } + + /* Add new invite string to invite list */ + if (string) + silc_hash_table_add(list, (void *)1, string); + } + + } else if (type == 2) { + /* Public key. */ + + /* Delete from the invite list */ + silc_hash_table_list(list, &htl); + while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) { + if (type == 2 && !memcmp(tmp2->data, tmp, len)) { + silc_hash_table_del_by_context(list, (void *)2, tmp2); + break; + } + } + silc_hash_table_list_reset(&htl); + + } else if (type == 3) { + /* Client ID */ + + /* Delete from the invite list */ + silc_hash_table_list(list, &htl); + while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2)) { + if (type == 3 && !memcmp(tmp2->data, tmp, len)) { + silc_hash_table_del_by_context(list, (void *)3, tmp2); + break; + } + } + silc_hash_table_list_reset(&htl); + } + + tmp = silc_argument_get_next_arg(args, &type, &len); + } + } +} + +/* Destructor for invite or ban list entrys */ + +void silc_server_inviteban_destruct(void *key, void *context, + void *user_context) +{ + switch ((SilcUInt32)key) { + case 1: + /* Invite string */ + silc_free(context); + break; + case 2: + case 3: + /* Public key/Channel ID SilcBuffer */ + silc_buffer_free(context); + break; + default: + break; + } }