Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2003 Pekka Riikonen
+ Copyright (C) 1997 - 2004 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
SILC_TASK_CALLBACK(silc_server_timeout_remote);
SILC_TASK_CALLBACK(silc_server_channel_key_rekey);
SILC_TASK_CALLBACK(silc_server_get_stats);
+SILC_TASK_CALLBACK(silc_server_connect_router);
/* Allocates a new SILC server object. This has to be done before the server
can be used. After allocation one must call silc_server_init to initialize
if (list)
silc_idcache_list_free(list);
+ if (server->pk_hash)
+ silc_hash_table_free(server->pk_hash);
+
/* Delete all clients */
list = NULL;
if (silc_idcache_get_all(server->local_list->clients, &list) &&
if (list)
silc_idcache_list_free(list);
+
/* Delete all servers */
list = NULL;
if (silc_idcache_get_all(server->local_list->servers, &list) &&
silc_idcache_free(server->global_list->servers);
silc_idcache_free(server->global_list->channels);
silc_hash_table_free(server->watcher_list);
+ silc_hash_table_free(server->watcher_list_pk);
silc_hash_free(server->md5hash);
silc_hash_free(server->sha1hash);
server->global_list->servers = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
server->global_list->channels = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
- /* Init watcher list */
+ /* Init watcher lists */
server->watcher_list =
silc_hash_table_alloc(1, silc_hash_client_id_hash, NULL,
silc_hash_data_compare, (void *)CLIENTID_HASH_LEN,
NULL, NULL, TRUE);
if (!server->watcher_list)
goto err;
+ server->watcher_list_pk =
+ silc_hash_table_alloc(1, silc_hash_public_key, NULL,
+ silc_hash_public_key_compare, NULL,
+ NULL, NULL, TRUE);
+ if (!server->watcher_list_pk)
+ goto err;
+
+ /* Init public key list */
+ server->pk_hash =
+ silc_hash_table_alloc(0, silc_hash_public_key, NULL,
+ silc_hash_public_key_compare, NULL,
+ NULL, NULL, TRUE);
+
+ if (!server->pk_hash)
+ goto err;
/* Create a listening server */
if (!silc_server_listen(server,
if (server->config->debug_string) {
silc_debug = TRUE;
silc_log_set_debug_string(server->config->debug_string);
- } else {
- silc_debug = FALSE;
}
#endif /* SILC_DEBUG */
SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
}
+/* callback for async connection to remote router */
+
+SILC_TASK_CALLBACK(silc_server_connection_established)
+{
+ SilcServer server = app_context;
+ SilcServerConnection sconn = (SilcServerConnection)context;
+ int sock = fd;
+ int opt = EINVAL, optlen = sizeof(opt);
+
+ silc_schedule_task_del_by_fd(server->schedule, sock);
+ silc_schedule_unset_listen_fd(server->schedule, sock);
+
+ if (silc_net_get_socket_opt(sock, SOL_SOCKET, SO_ERROR, &opt, &optlen) ||
+ (opt != 0)) {
+ SILC_LOG_ERROR(("Could not connect to router %s:%d: %s",
+ sconn->remote_host, sconn->remote_port,
+ strerror(opt)));
+ if (!sconn->no_reconnect)
+ silc_schedule_task_add(server->schedule, 0,
+ silc_server_connect_to_router_retry,
+ context, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
+ else {
+ silc_server_config_unref(&sconn->conn);
+ silc_free(sconn->remote_host);
+ silc_free(sconn->backup_replace_ip);
+ silc_free(sconn);
+ }
+ return;
+ }
+
+ SILC_LOG_DEBUG(("Connection to router %s:%d established", sconn->remote_host,
+ sconn->remote_port));
+
+ /* Continue with key exchange protocol */
+ silc_server_start_key_exchange(server, sconn, sock);
+}
/* Generic routine to use connect to a router. */
SILC_TASK_CALLBACK(silc_server_connect_router)
silc_server_config_ref(&sconn->conn, server->config, (void *)rconn);
/* Connect to remote host */
- sock = silc_net_create_connection(
+ sock = silc_net_create_connection_async(
(!server->config->server_info->primary ? NULL :
server->config->server_info->primary->server_ip),
sconn->remote_port, sconn->remote_host);
return;
}
- /* Continue with key exchange protocol */
- silc_server_start_key_exchange(server, sconn, sock);
+ /* wait for the connection to be established */
+ silc_schedule_task_add(server->schedule, sock,
+ silc_server_connection_established,
+ context, 0, 0, SILC_TASK_FD,
+ SILC_TASK_PRI_NORMAL);
+ silc_schedule_set_listen_fd(server->schedule, sock,
+ SILC_TASK_WRITE, FALSE);
}
/* This function connects to our primary router or if we are a router this
sock->ip));
/* Listenning port */
- if (!server->sockets[(SilcUInt32)proto_ctx->context]) {
+ if (!server->sockets[SILC_PTR_TO_32(proto_ctx->context)]) {
silc_server_disconnect_remote(server, sock,
SILC_STATUS_ERR_RESOURCE_LIMIT,
"Connection refused");
silc_free(proto_ctx);
return;
}
- port = server->sockets[(SilcUInt32)proto_ctx->context]->port;
+ port = server->sockets[SILC_PTR_TO_32(proto_ctx->context)]->port;
/* Check whether this connection is denied to connect to us. */
deny = silc_server_config_find_denied(server, sock->ip);
is accepted further. */
proto_ctx = silc_calloc(1, sizeof(*proto_ctx));
proto_ctx->server = server;
- proto_ctx->context = (void *)fd;
+ proto_ctx->context = SILC_32_TO_PTR(fd);
silc_socket_host_lookup(newsocket, TRUE,
silc_server_accept_new_connection_lookup,
(void *)proto_ctx, server->schedule);
silc_free(sock->user_data);
server->stat.auth_failures++;
- /* From here on, wait 10 seconds for the backup router to appear. */
+ /* From here on, wait 20 seconds for the backup router to appear. */
silc_schedule_task_add(server->schedule, 0,
silc_server_backup_router_wait,
- (void *)server, 10, 0,
+ (void *)server, 20, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
goto out;
}
client->mode |= SILC_UMODE_ANONYMOUS;
}
+ /* Add public key to hash list (for whois using attributes) */
+ silc_hash_table_add(server->pk_hash,
+ entry->data.public_key, client);
+
id_entry = (void *)client;
break;
}
silc_free(sock->user_data);
server->stat.auth_failures++;
- /* From here on, wait 10 seconds for the backup router to appear. */
+ /* From here on, wait 20 seconds for the backup router to appear. */
silc_schedule_task_add(server->schedule, 0,
silc_server_backup_router_wait,
- (void *)server, 10, 0,
+ (void *)server, 20, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
goto out;
}
/* Remove the backup waiting with timeout */
silc_schedule_task_add(server->schedule, 0,
silc_server_backup_router_wait,
- (void *)server, 5, 0,
+ (void *)server, 10, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
}
/* Do not send data to disconnected connection */
if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
SILC_LOG_DEBUG(("Disconnected socket connection, cannot send"));
+ SILC_SET_CONNECTION_FOR_INPUT(server->schedule, fd);
+ SILC_UNSET_OUTBUF_PENDING(sock);
+ silc_buffer_clear(sock->outbuf);
return;
}
sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
"Router")));
- if (sock->user_data)
+ if (sock->user_data) {
+ /* If backup then mark that resuming will not be allowed */
+ if (server->server_type == SILC_ROUTER && !server->backup_router &&
+ sock->type == SILC_SOCKET_TYPE_SERVER) {
+ SilcServerEntry server_entry = sock->user_data;
+ if (server_entry->server_type == SILC_BACKUP_ROUTER)
+ server->backup_closed = TRUE;
+ }
+
silc_server_free_sock_user_data(server, sock, NULL);
+ }
SILC_SET_DISCONNECTING(sock);
silc_server_close_connection(server, sock);
}
sock->type == SILC_SOCKET_TYPE_SERVER ? "Server" :
"Router"), strerror(errno)));
- if (sock->user_data)
+ if (sock->user_data) {
+ /* If backup then mark that resuming will not be allowed */
+ if (server->server_type == SILC_ROUTER && !server->backup_router &&
+ sock->type == SILC_SOCKET_TYPE_SERVER) {
+ SilcServerEntry server_entry = sock->user_data;
+ if (server_entry->server_type == SILC_BACKUP_ROUTER)
+ server->backup_closed = TRUE;
+ }
+
silc_server_free_sock_user_data(server, sock, NULL);
+ }
SILC_SET_DISCONNECTING(sock);
silc_server_close_connection(server, sock);
}
if (sock->user_data) {
char tmp[128];
- /* If backup disconnected then mark that resuming willl not be allowed */
+ /* If backup disconnected then mark that resuming will not be allowed */
if (server->server_type == SILC_ROUTER && !server->backup_router &&
sock->type == SILC_SOCKET_TYPE_SERVER && sock->user_data) {
SilcServerEntry server_entry = sock->user_data;
if (SILC_PRIMARY_ROUTE(server) == sock && server->backup_router)
server->backup_noswitch = TRUE;
- if (sock->user_data)
+ if (sock->user_data) {
+ /* If we are router and backup errorred then mark that resuming
+ will not be allowed */
+ if (server->server_type == SILC_ROUTER && !server->backup_router &&
+ sock->type == SILC_SOCKET_TYPE_SERVER) {
+ SilcServerEntry server_entry = sock->user_data;
+ if (server_entry->server_type == SILC_BACKUP_ROUTER)
+ server->backup_closed = TRUE;
+ }
+
silc_server_free_sock_user_data(server, sock, NULL);
+ }
SILC_SET_DISCONNECTING(sock);
silc_server_close_connection(server, sock);
}
SilcIDListData idata = (SilcIDListData)sock->user_data;
int ret;
+ if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+ SILC_LOG_DEBUG(("Connection is disconnected"));
+ goto out;
+ }
+
server->stat.packets_received++;
/* Parse the packet */
/* If entry is disabled ignore what we got. */
if (idata && idata->status & SILC_IDLIST_STATUS_DISABLED &&
ret != SILC_PACKET_HEARTBEAT && ret != SILC_PACKET_RESUME_ROUTER &&
- ret != SILC_PACKET_REKEY && ret != SILC_PACKET_REKEY_DONE) {
- SILC_LOG_DEBUG(("Connection is disabled"));
+ ret != SILC_PACKET_REKEY && ret != SILC_PACKET_REKEY_DONE &&
+ ret != SILC_PACKET_KEY_EXCHANGE_1 && ret != SILC_PACKET_KEY_EXCHANGE_2) {
+ SILC_LOG_DEBUG(("Connection is disabled (packet %s dropped)",
+ silc_get_packet_name(ret)));
goto out;
}
silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
parser_context);
+ if (SILC_IS_DISCONNECTING(sock) || SILC_IS_DISCONNECTED(sock)) {
+ SILC_LOG_DEBUG(("Connection is disconnected"));
+ return FALSE;
+ }
+
/* Reprocess data since we'll return FALSE here. This is because
the idata->receive_key might have become valid in the last packet
and we want to call this processor with valid cipher. */
silc_server_packet_parse_real(server->schedule, server, 0, sock->sock,
parser_context);
break;
- default:
- return TRUE;
}
return TRUE;
/* Do not switch to backup in case of error */
server->backup_noswitch = (status == SILC_STATUS_OK ? FALSE : TRUE);
- /* If backup disconnected then mark that resuming willl not be allowed */
+ /* If backup disconnected then mark that resuming will not be allowed */
if (server->server_type == SILC_ROUTER && !server->backup_router &&
sock->type == SILC_SOCKET_TYPE_SERVER && sock->user_data) {
SilcServerEntry server_entry = sock->user_data;
"Router"), tmp[0] ? tmp : ""));
SILC_SET_DISCONNECTED(sock);
+ silc_socket_set_qos(sock, 0, 0, 0, 0, NULL);
silc_schedule_task_add(server->schedule, sock->sock,
silc_server_close_connection_final,
(void *)sock, 0, 1, SILC_TASK_TIMEOUT,
/* Remove this client from watcher list if it is */
silc_server_del_from_watcher_list(server, client);
+ /* Remove this client from the public key hash list */
+ if (client->data.public_key)
+ silc_hash_table_del_by_context(server->pk_hash,
+ client->data.public_key, client);
+
/* Update statistics */
server->stat.my_clients--;
server->stat.clients--;
sock->protocol->state = SILC_PROTOCOL_STATE_ERROR;
silc_protocol_execute_final(sock->protocol, server->schedule);
sock->protocol = NULL;
+ if (!sock->user_data)
+ return;
}
switch (sock->type) {
type = silc_hash_table_count(channel->invite_list);
SILC_PUT16_MSB(type, list->data);
silc_hash_table_list(channel->invite_list, &htl);
- while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2))
+ while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2))
list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len,
type);
silc_hash_table_list_reset(&htl);
type = silc_hash_table_count(channel->ban_list);
SILC_PUT16_MSB(type, list->data);
silc_hash_table_list(channel->ban_list, &htl);
- while (silc_hash_table_get(&htl, (void **)&type, (void **)&tmp2))
+ while (silc_hash_table_get(&htl, (void *)&type, (void *)&tmp2))
list = silc_argument_payload_encode_one(list, tmp2->data, tmp2->len,
type);
silc_hash_table_list_reset(&htl);
SilcBuffer chidp, clidp, csidp;
SilcBuffer tmp, fkey = NULL, chpklist;
int len;
- unsigned char mode[4];
+ unsigned char mode[4], ulimit[4];
char *hmac;
SILC_LOG_DEBUG(("Start"));
/* CMODE notify */
SILC_PUT32_MSB(channel->mode, mode);
+ if (channel->mode & SILC_CHANNEL_MODE_ULIMIT)
+ SILC_PUT32_MSB(channel->user_limit, ulimit);
hmac = channel->hmac ? (char *)silc_hmac_get_name(channel->hmac) : NULL;
if (channel->founder_key)
fkey = silc_pkcs_public_key_payload_encode(channel->founder_key);
tmp =
silc_server_announce_encode_notify(SILC_NOTIFY_TYPE_CMODE_CHANGE,
- 7, csidp->data, csidp->len,
+ 8, csidp->data, csidp->len,
mode, sizeof(mode),
NULL, 0,
hmac, hmac ? strlen(hmac) : 0,
fkey ? fkey->data : NULL,
fkey ? fkey->len : 0,
chpklist ? chpklist->data : NULL,
- chpklist ? chpklist->len : 0);
+ chpklist ? chpklist->len : 0,
+ (channel->mode &
+ SILC_CHANNEL_MODE_ULIMIT ?
+ ulimit : NULL),
+ (channel->mode &
+ SILC_CHANNEL_MODE_ULIMIT ?
+ sizeof(ulimit) : 0));
len = tmp->len;
*channel_modes =
silc_buffer_realloc(*channel_modes,
are no part of the list. */
if (ht) {
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)) {
if (!silc_hash_table_find(ht, chl->channel, NULL, NULL)) {
silc_hash_table_del(chl->channel->user_list, chl->client);
silc_hash_table_del(chl->client->channels, chl->channel);
silc_hash_table_free(ht);
} else {
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)) {
silc_hash_table_del(chl->channel->user_list, chl->client);
silc_hash_table_del(chl->client->channels, chl->channel);
silc_free(chl);
silc_ske_free(ctx->ske);
silc_socket_free(sock);
silc_free(ctx);
+
+ /* Disconnect since we failed to rekey, the keys are probably wrong. */
+ silc_server_disconnect_remote(server, sock,
+ SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
+
+ /* Reconnect */
+ if (sock->type != SILC_SOCKET_TYPE_CLIENT)
+ silc_server_create_connections(server);
}
/* A timeout callback for the re-key. We will be the initiator of the
silc_schedule_task_add(server->schedule, sock->sock,
silc_server_rekey_timeout,
proto_ctx,
- server->config->key_exchange_timeout, 0,
+ (idata->rekey->timeout >
+ server->config->key_exchange_timeout ?
+ idata->rekey->timeout :
+ server->config->key_exchange_timeout * 4), 0,
SILC_TASK_TIMEOUT,
SILC_TASK_PRI_LOW);
silc_free(ctx);
silc_server_disconnect_remote(server, sock,
SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL);
+ if (sock->user_data)
+ silc_server_free_sock_user_data(server, sock, NULL);
/* Reconnect */
if (sock->type != SILC_SOCKET_TYPE_CLIENT)