Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2001 Pekka Riikonen
+ Copyright (C) 1997 - 2002 Pekka Riikonen
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/* Remove the client from all channels. The client is removed from
the channels' user list. */
silc_hash_table_list(client->channels, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
channel = chl->channel;
/* Remove channel from client's channel list */
if (channel->rekey)
silc_schedule_task_del_by_context(server->schedule, channel->rekey);
- if (!silc_idlist_del_channel(server->local_list, channel))
- silc_idlist_del_channel(server->global_list, channel);
- server->stat.my_channels--;
+ if (silc_idlist_del_channel(server->local_list, channel))
+ server->stat.my_channels--;
+ else
+ silc_idlist_del_channel(server->global_list, channel);
continue;
}
/* Remove client from channel's client list */
silc_hash_table_del(channel->user_list, chl->client);
+ channel->user_count--;
/* If there is no global users on the channel anymore mark the channel
as local channel. Do not check if the removed client is local client. */
SilcChannelClientEntry chl2;
SilcHashTableList htl2;
- channel->id = NULL;
+ channel->disabled = TRUE;
silc_hash_table_list(channel->user_list, &htl2);
- while (silc_hash_table_get(&htl2, NULL, (void *)&chl2)) {
+ while (silc_hash_table_get(&htl2, NULL, (void **)&chl2)) {
silc_hash_table_del(chl2->client->channels, channel);
silc_hash_table_del(channel->user_list, chl2->client);
+ channel->user_count--;
silc_free(chl2);
}
+ silc_hash_table_list_reset(&htl2);
continue;
}
/* Remove the channel entry */
- if (!silc_idlist_del_channel(server->local_list, channel))
- silc_idlist_del_channel(server->global_list, channel);
- server->stat.my_channels--;
+ if (silc_idlist_del_channel(server->local_list, channel))
+ server->stat.my_channels--;
+ else
+ silc_idlist_del_channel(server->global_list, channel);
continue;
}
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);
}
SilcClientEntry client = NULL;
SilcBuffer idp;
SilcClientEntry *clients = NULL;
- uint32 clients_c = 0;
+ SilcUInt32 clients_c = 0;
unsigned char **argv = NULL;
- uint32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
+ SilcUInt32 *argv_lens = NULL, *argv_types = NULL, argc = 0;
SilcHashTableList htl;
SilcChannelEntry channel;
SilcHashTable channels;
}
if (client->router != entry) {
- if (server_signoff && client->connection) {
+ if (server_signoff) {
clients = silc_realloc(clients,
sizeof(*clients) * (clients_c + 1));
clients[clients_c] = client;
silc_buffer_free(idp);
}
+ /* Update statistics */
+ server->stat.clients--;
+ if (server->stat.cell_clients)
+ server->stat.cell_clients--;
+ SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+ SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
/* Remove the client entry */
silc_server_remove_clients_channels(server, NULL, client, channels);
- silc_idlist_del_client(server->local_list, client);
+ if (!server_signoff) {
+ client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+ id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+ } else {
+ silc_idlist_del_client(server->local_list, client);
+ }
if (!silc_idcache_list_next(list, &id_cache))
break;
silc_buffer_free(idp);
}
+ /* Update statistics */
+ server->stat.clients--;
+ if (server->stat.cell_clients)
+ server->stat.cell_clients--;
+ SILC_OPER_STATS_UPDATE(client, server, SILC_UMODE_SERVER_OPERATOR);
+ SILC_OPER_STATS_UPDATE(client, router, SILC_UMODE_ROUTER_OPERATOR);
+
/* Remove the client entry */
silc_server_remove_clients_channels(server, NULL, client, channels);
- silc_idlist_del_client(server->global_list, client);
+ if (!server_signoff) {
+ client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
+ id_cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
+ } else {
+ silc_idlist_del_client(server->global_list, client);
+ }
if (!silc_idcache_list_next(list, &id_cache))
break;
/* Send the SERVER_SIGNOFF notify */
if (server_signoff) {
- SilcBuffer args;
+ SilcBuffer args, not;
/* Send SERVER_SIGNOFF notify to our primary router */
if (!server->standalone && server->router &&
silc_buffer_free(args);
}
+ /* Send to local clients. We also send the list of client ID's that
+ is to be removed for those servers that would like to use that list. */
args = silc_argument_payload_encode(argc, argv, argv_lens,
argv_types);
- /* Send to local clients */
- for (i = 0; i < clients_c; i++) {
- silc_server_send_notify_args(server, clients[i]->connection,
- FALSE, SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
- argc, args);
- }
+ not = silc_notify_payload_encode_args(SILC_NOTIFY_TYPE_SERVER_SIGNOFF,
+ argc, args);
+ silc_server_packet_send_clients(server, clients, clients_c,
+ SILC_PACKET_NOTIFY, 0, FALSE,
+ not->data, not->len, FALSE);
silc_free(clients);
silc_buffer_free(args);
+ silc_buffer_free(not);
for (i = 0; i < argc; i++)
silc_free(argv[i]);
silc_free(argv);
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)) {
- if (!silc_server_create_channel_key(server, channel, 0))
+ while (silc_hash_table_get(&htl, NULL, (void **)&channel)) {
+ if (!silc_server_create_channel_key(server, channel, 0)) {
+ silc_hash_table_list_reset(&htl);
+ silc_hash_table_free(channels);
return FALSE;
+ }
/* Do not send the channel key if private channel key mode is set */
if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY)
server->server_type == SILC_ROUTER ?
FALSE : !server->standalone);
}
+ silc_hash_table_list_reset(&htl);
silc_hash_table_free(channels);
return TRUE;
static SilcServerEntry
silc_server_update_clients_by_real_server(SilcServer server,
- SilcClientEntry client)
+ SilcServerEntry from,
+ SilcClientEntry client,
+ bool local,
+ SilcIDCacheEntry client_cache)
{
SilcServerEntry server_entry;
SilcIDCacheEntry id_cache = NULL;
if (silc_idcache_list_first(list, &id_cache)) {
while (id_cache) {
server_entry = (SilcServerEntry)id_cache->context;
- if (SILC_ID_COMPARE(server_entry->id, client->id,
+ if (server_entry != from &&
+ SILC_ID_COMPARE(server_entry->id, client->id,
+ client->id->ip.data_len)) {
+ SILC_LOG_DEBUG(("Found (local) %s",
+ silc_id_render(server_entry->id, SILC_ID_SERVER)));
+
+ if (!server_entry->data.send_key && server_entry->router) {
+ SILC_LOG_DEBUG(("Server not locally connected, use its router"));
+ /* If the client is not marked as local then move it to local list
+ since the server is local. */
+ if (!local) {
+ SILC_LOG_DEBUG(("Moving client to local list"));
+ silc_idcache_add(server->local_list->clients, client_cache->name,
+ client_cache->id, client_cache->context,
+ client_cache->expire, NULL);
+ silc_idcache_del_by_context(server->global_list->clients, client);
+ }
+ server_entry = server_entry->router;
+ } else {
+ /* If the client is not marked as local then move it to local list
+ since the server is local. */
+ if (server_entry->server_type != SILC_BACKUP_ROUTER && !local) {
+ SILC_LOG_DEBUG(("Moving client to local list"));
+ silc_idcache_add(server->local_list->clients, client_cache->name,
+ client_cache->id, client_cache->context,
+ client_cache->expire, NULL);
+ silc_idcache_del_by_context(server->global_list->clients, client);
+ }
+ }
+
+ silc_idcache_list_free(list);
+ return 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))
+ return NULL;
+
+ if (silc_idcache_list_first(list, &id_cache)) {
+ while (id_cache) {
+ server_entry = (SilcServerEntry)id_cache->context;
+ if (server_entry != from &&
+ SILC_ID_COMPARE(server_entry->id, client->id,
client->id->ip.data_len)) {
- SILC_LOG_DEBUG(("Found"));
+ SILC_LOG_DEBUG(("Found (global) %s",
+ silc_id_render(server_entry->id, SILC_ID_SERVER)));
+
+ if (!server_entry->data.send_key && server_entry->router) {
+ SILC_LOG_DEBUG(("Server not locally connected, use its router"));
+ /* If the client is marked as local then move it to global list
+ since the server is global. */
+ 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);
+ silc_idcache_del_by_context(server->local_list->clients, client);
+ }
+ server_entry = server_entry->router;
+ } else {
+ /* If the client is marked as local then move it to global list
+ since the server is global. */
+ 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);
+ silc_idcache_del_by_context(server->local_list->clients, client);
+ }
+ }
+
silc_idcache_list_free(list);
return server_entry;
}
SilcIDCacheList list = NULL;
SilcIDCacheEntry id_cache = NULL;
SilcClientEntry client = NULL;
+ bool local;
SILC_LOG_DEBUG(("Start"));
- if (silc_idcache_get_all(server->local_list->clients, &list)) {
+ 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;
continue;
}
+ SILC_LOG_DEBUG(("Client (global) %s",
+ silc_id_render(client->id, SILC_ID_CLIENT)));
+ if (client->router)
+ 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 (SILC_ID_COMPARE(from->id, client->id,
- client->id->ip.data_len)) {
+ 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;
if (resolve_real_server) {
client->router =
- silc_server_update_clients_by_real_server(server, client);
+ silc_server_update_clients_by_real_server(server, from, client,
+ local, id_cache);
if (!client->router)
client->router = to;
} else {
silc_idcache_list_free(list);
}
- if (silc_idcache_get_all(server->global_list->clients, &list)) {
+ local = TRUE;
+ 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;
continue;
}
+ SILC_LOG_DEBUG(("Client (local) %s",
+ silc_id_render(client->id, SILC_ID_CLIENT)));
+ if (client->router)
+ 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 (SILC_ID_COMPARE(from->id, client->id,
- client->id->ip.data_len)) {
+ 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;
if (resolve_real_server) {
client->router =
- silc_server_update_clients_by_real_server(server, client);
+ silc_server_update_clients_by_real_server(server, from, client,
+ local, id_cache);
if (!client->router)
- client->router = to;
+ client->router = from; /* on local list put old from */
} else {
client->router = to;
}
silc_server_remove_clients_by_server(server, from, TRUE);
}
+/* Updates servers that are from `from' to be originated from `to'. This
+ will also update the server's connection to `to's connection. */
+
+void silc_server_update_servers_by_server(SilcServer server,
+ SilcServerEntry from,
+ SilcServerEntry to)
+{
+ SilcIDCacheList list = NULL;
+ SilcIDCacheEntry id_cache = NULL;
+ SilcServerEntry server_entry = NULL;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ 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_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 (server_entry->router == from) {
+ server_entry->router = to;
+ server_entry->connection = to->connection;
+ }
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ }
+ }
+ silc_idcache_list_free(list);
+ }
+}
+
+/* Removes channels that are from `from. */
+
+void silc_server_remove_channels_by_server(SilcServer server,
+ SilcServerEntry from)
+{
+ SilcIDCacheList list = NULL;
+ SilcIDCacheEntry id_cache = NULL;
+ SilcChannelEntry channel = NULL;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (silc_idcache_get_all(server->global_list->channels, &list)) {
+ if (silc_idcache_list_first(list, &id_cache)) {
+ while (id_cache) {
+ channel = (SilcChannelEntry)id_cache->context;
+ if (channel->router == from)
+ silc_idlist_del_channel(server->global_list, channel);
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ }
+ }
+ silc_idcache_list_free(list);
+ }
+}
+
+/* Updates channels that are from `from' to be originated from `to'. */
+
+void silc_server_update_channels_by_server(SilcServer server,
+ SilcServerEntry from,
+ SilcServerEntry to)
+{
+ SilcIDCacheList list = NULL;
+ SilcIDCacheEntry id_cache = NULL;
+ SilcChannelEntry channel = NULL;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ if (silc_idcache_get_all(server->global_list->channels, &list)) {
+ if (silc_idcache_list_first(list, &id_cache)) {
+ while (id_cache) {
+ channel = (SilcChannelEntry)id_cache->context;
+ if (channel->router == from)
+ channel->router = to;
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ }
+ }
+ silc_idcache_list_free(list);
+ }
+}
+
/* Checks whether given channel has global users. If it does this returns
TRUE and FALSE if there is only locally connected clients on the channel. */
SilcHashTableList htl;
silc_hash_table_list(channel->user_list, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
- if (chl->client->router)
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+ if (chl->client->router) {
+ silc_hash_table_list_reset(&htl);
return TRUE;
+ }
}
+ silc_hash_table_list_reset(&htl);
return FALSE;
}
SilcHashTableList htl;
silc_hash_table_list(channel->user_list, &htl);
- while (silc_hash_table_get(&htl, NULL, (void *)&chl)) {
- if (!chl->client->router)
+ while (silc_hash_table_get(&htl, NULL, (void **)&chl)) {
+ if (!chl->client->router) {
+ silc_hash_table_list_reset(&htl);
return TRUE;
+ }
}
+ silc_hash_table_list_reset(&htl);
return FALSE;
}
`client' which is faster than checking the user list from `channel'. */
bool silc_server_client_on_channel(SilcClientEntry client,
- SilcChannelEntry channel)
+ SilcChannelEntry channel,
+ SilcChannelClientEntry *chl)
{
if (!client || !channel)
return FALSE;
- if (silc_hash_table_find(client->channels, channel, NULL, NULL))
- return TRUE;
+ return silc_hash_table_find(client->channels, channel, NULL,
+ (void **)chl);
+}
+
+/* Checks string for bad characters and returns TRUE if they are found. */
+
+bool silc_server_name_bad_chars(const char *name, SilcUInt32 name_len)
+{
+ int i;
+
+ for (i = 0; i < name_len; i++) {
+ if (!isascii(name[i]))
+ return TRUE;
+ if (name[i] <= 32) return TRUE;
+ if (name[i] == ' ') return TRUE;
+ if (name[i] == '*') return TRUE;
+ if (name[i] == '?') return TRUE;
+ if (name[i] == ',') return TRUE;
+ }
return FALSE;
}
+
+/* Modifies the `name' if it includes bad characters and returns new
+ allocated name that does not include bad characters. */
+
+char *silc_server_name_modify_bad(const char *name, SilcUInt32 name_len)
+{
+ int i;
+ char *newname = strdup(name);
+
+ for (i = 0; i < name_len; i++) {
+ if (!isascii(newname[i])) newname[i] = '_';
+ if (newname[i] <= 32) newname[i] = '_';
+ if (newname[i] == ' ') newname[i] = '_';
+ if (newname[i] == '*') newname[i] = '_';
+ if (newname[i] == '?') newname[i] = '_';
+ if (newname[i] == ',') newname[i] = '_';
+ }
+
+ return newname;
+}
+
+/* Find number of sockets by IP address indicated by `ip'. Returns 0 if
+ socket connections with the IP address does not exist. */
+
+SilcUInt32 silc_server_num_sockets_by_ip(SilcServer server, const char *ip,
+ SilcSocketType type)
+{
+ 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) &&
+ server->sockets[i]->type == type)
+ count++;
+ }
+
+ return count;
+}
+
+/* Find number of sockets by IP address indicated by remote host, indicatd
+ 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,
+ const char *ip,
+ const char *hostname,
+ SilcUInt16 port,
+ SilcSocketType type)
+{
+ int i, count;
+
+ if (!ip && !hostname)
+ return 0;
+
+ for (i = 0, count = 0; i < server->config->param.connections_max; i++) {
+ if (server->sockets[i] &&
+ ((ip && !strcmp(server->sockets[i]->ip, ip)) ||
+ (hostname && !strcmp(server->sockets[i]->hostname, hostname))) &&
+ server->sockets[i]->port == port &&
+ server->sockets[i]->type == type)
+ count++;
+ }
+
+ return count;
+}
+
+/* Finds locally cached public key by the public key received in the SKE.
+ If we have it locally cached then we trust it and will use it in the
+ authentication protocol. Returns the locally cached public key or NULL
+ if we do not find the public key. */
+
+SilcPublicKey silc_server_find_public_key(SilcServer server,
+ SilcHashTable local_public_keys,
+ SilcPublicKey remote_public_key)
+{
+ SilcPublicKey cached_key;
+
+ SILC_LOG_DEBUG(("Find remote public key (%d keys in local cache)",
+ silc_hash_table_count(local_public_keys)));
+
+ if (!silc_hash_table_find_ext(local_public_keys, remote_public_key,
+ (void **)&cached_key, NULL,
+ silc_hash_public_key, NULL,
+ silc_hash_public_key_compare, NULL)) {
+ SILC_LOG_ERROR(("Public key not found"));
+ return NULL;
+ }
+
+ SILC_LOG_DEBUG(("Found public key"));
+
+ return cached_key;
+}
+
+/* This returns the first public key from the table of public keys. This
+ is used only in cases where single public key exists in the table and
+ we want to get a pointer to it. For public key tables that has multiple
+ keys in it the silc_server_find_public_key must be used. */
+
+SilcPublicKey silc_server_get_public_key(SilcServer server,
+ SilcHashTable local_public_keys)
+{
+ SilcPublicKey cached_key;
+ SilcHashTableList htl;
+
+ SILC_LOG_DEBUG(("Start"));
+
+ assert(silc_hash_table_count(local_public_keys) < 2);
+
+ silc_hash_table_list(local_public_keys, &htl);
+ if (!silc_hash_table_get(&htl, NULL, (void **)&cached_key))
+ return NULL;
+ silc_hash_table_list_reset(&htl);
+
+ return cached_key;
+}
+
+/* Check whether the connection `sock' is allowed to connect to us. This
+ checks for example whether there is too much connections for this host,
+ and required version for the host etc. */
+
+bool silc_server_connection_allowed(SilcServer server,
+ SilcSocketConnection sock,
+ SilcSocketType type,
+ SilcServerConfigConnParams *global,
+ SilcServerConfigConnParams *params,
+ SilcSKE ske)
+{
+ SilcUInt32 conn_number = (type == SILC_SOCKET_TYPE_CLIENT ?
+ server->stat.my_clients :
+ type == SILC_SOCKET_TYPE_SERVER ?
+ server->stat.my_servers :
+ server->stat.my_routers);
+ SilcUInt32 num_sockets, max_hosts, max_per_host;
+ SilcUInt32 r_protocol_version, l_protocol_version;
+ SilcUInt32 r_software_version, l_software_version;
+ char *r_vendor_version = NULL, *l_vendor_version;
+
+ /* Check version */
+
+ l_protocol_version =
+ silc_version_to_num(params && params->version_protocol ?
+ params->version_protocol :
+ global->version_protocol);
+ l_software_version =
+ silc_version_to_num(params && params->version_software ?
+ params->version_software :
+ global->version_software);
+ l_vendor_version = (params && params->version_software_vendor ?
+ params->version_software_vendor :
+ global->version_software_vendor);
+
+ if (ske && silc_ske_parse_version(ske, &r_protocol_version, NULL,
+ &r_software_version, NULL,
+ &r_vendor_version)) {
+ sock->version = r_protocol_version;
+
+ /* Match protocol version */
+ if (l_protocol_version && r_protocol_version &&
+ r_protocol_version < l_protocol_version) {
+ SILC_LOG_INFO(("Connection %s (%s) is too old version",
+ sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "You support too old protocol version");
+ return FALSE;
+ }
+
+ /* Math software version */
+ if (l_software_version && r_software_version &&
+ r_software_version < l_software_version) {
+ SILC_LOG_INFO(("Connection %s (%s) is too old version",
+ sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "You support too old software version");
+ return FALSE;
+ }
+
+ /* Regex match vendor version */
+ if (l_vendor_version && r_vendor_version &&
+ !silc_string_match(l_vendor_version, r_vendor_version)) {
+ SILC_LOG_INFO(("Connection %s (%s) is unsupported version",
+ sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Your software is not supported");
+ return FALSE;
+ }
+ }
+ silc_free(r_vendor_version);
+
+ /* Check for maximum connections limit */
+
+ num_sockets = silc_server_num_sockets_by_ip(server, sock->ip, type);
+ max_hosts = (params ? params->connections_max : global->connections_max);
+ max_per_host = (params ? params->connections_max_per_host :
+ global->connections_max_per_host);
+
+ if (max_hosts && conn_number >= max_hosts) {
+ SILC_LOG_INFO(("Server is full, closing %s (%s) connection",
+ sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Server is full, try again later");
+ return FALSE;
+ }
+
+ if (num_sockets >= max_per_host) {
+ SILC_LOG_INFO(("Too many connections from %s (%s), closing connection",
+ sock->hostname, sock->ip));
+ silc_server_disconnect_remote(server, sock,
+ "Server closed connection: "
+ "Too many connections from your host");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Checks that client has rights to add or remove channel modes. If any
+ of the checks fails FALSE is returned. */
+
+bool silc_server_check_cmode_rights(SilcServer server,
+ SilcChannelEntry channel,
+ SilcChannelClientEntry client,
+ SilcUInt32 mode)
+{
+ bool is_op = client->mode & SILC_CHANNEL_UMODE_CHANOP;
+ bool is_fo = client->mode & SILC_CHANNEL_UMODE_CHANFO;
+
+ /* Check whether has rights to change anything */
+ if (!is_op && !is_fo)
+ return FALSE;
+
+ /* Check whether has rights to change everything */
+ if (is_op && is_fo)
+ return 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;
+ } else {
+ if (channel->mode & SILC_CHANNEL_MODE_PRIVKEY) {
+ if (is_op && !is_fo)
+ return FALSE;
+ }
+ }
+
+ if (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)
+ return FALSE;
+ }
+ }
+
+ if (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)
+ return FALSE;
+ }
+ }
+
+ if (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)
+ return FALSE;
+ }
+ }
+
+ if (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)
+ return FALSE;
+ }
+ }
+
+ if (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)
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Check that the client has rights to change its user mode. Returns
+ FALSE if setting some mode is not allowed. */
+
+bool silc_server_check_umode_rights(SilcServer server,
+ SilcClientEntry client,
+ SilcUInt32 mode)
+{
+ bool server_op = FALSE, router_op = FALSE;
+
+ if (mode & SILC_UMODE_SERVER_OPERATOR) {
+ /* Cannot set server operator mode (must use OPER command) */
+ if (!(client->mode & SILC_UMODE_SERVER_OPERATOR))
+ return FALSE;
+ } else {
+ /* Remove the server operator rights */
+ if (client->mode & SILC_UMODE_SERVER_OPERATOR)
+ server_op = TRUE;
+ }
+
+ if (mode & SILC_UMODE_ROUTER_OPERATOR) {
+ /* Cannot set router operator mode (must use SILCOPER command) */
+ if (!(client->mode & SILC_UMODE_ROUTER_OPERATOR))
+ return FALSE;
+ } else {
+ /* Remove the router operator rights */
+ if (client->mode & SILC_UMODE_ROUTER_OPERATOR)
+ router_op = TRUE;
+ }
+
+ if (server_op)
+ SILC_UMODE_STATS_UPDATE(server, SILC_UMODE_SERVER_OPERATOR);
+ if (router_op)
+ SILC_UMODE_STATS_UPDATE(router, SILC_UMODE_ROUTER_OPERATOR);
+
+ return TRUE;
+}