Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2008 Pekka Riikonen
+ Copyright (C) 1997 - 2009 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
if (server->router_conn && server->router_conn->sock == stream &&
!server->router && server->standalone) {
+ if (idata->sconn && idata->sconn->callback)
+ (*idata->sconn->callback)(server, NULL, idata->sconn->callback_context);
silc_server_create_connections(server);
silc_server_free_sock_user_data(server, stream, NULL);
} else {
server->backup_closed = TRUE;
}
+ if (idata->sconn && idata->sconn->callback)
+ (*idata->sconn->callback)(server, NULL, idata->sconn->callback_context);
silc_server_free_sock_user_data(server, stream, NULL);
}
SilcPacketStream stream = context;
SilcIDListData idata = silc_packet_get_context(stream);
- if (!idata)
+ if (!idata || !silc_packet_stream_is_valid(stream)) {
+ silc_packet_stream_unref(stream);
return;
+ }
if (server->router_conn && server->router_conn->sock == stream &&
!server->router && server->standalone) {
+ if (idata->sconn && idata->sconn->callback)
+ (*idata->sconn->callback)(server, NULL, idata->sconn->callback_context);
silc_server_create_connections(server);
+ silc_server_free_sock_user_data(server, stream, NULL);
} else {
/* If backup disconnected then mark that resuming will not be allowed */
if (server->server_type == SILC_ROUTER && !server->backup_router &&
server->backup_closed = TRUE;
}
+ if (idata->sconn && idata->sconn->callback)
+ (*idata->sconn->callback)(server, NULL, idata->sconn->callback_context);
silc_server_free_sock_user_data(server, stream, NULL);
}
silc_server_close_connection(server, stream);
+
+ /* Release our stream reference */
+ silc_packet_stream_unref(stream);
}
/* Packet engine callback to indicate error */
const char *ip;
SilcUInt16 port;
- SILC_LOG_DEBUG(("Packet error, sock %p", stream));
+ SILC_LOG_DEBUG(("Packet error %d, sock %p", error, stream));
if (!idata || !sock)
return;
if (!silc_packet_stream_is_valid(stream))
return;
+ /* We must take reference of the stream */
+ silc_packet_stream_ref(stream);
+
+ /* In case we get here many times, register only one timeout */
+ silc_schedule_task_del_by_all(server->schedule, 0,
+ silc_server_packet_error_timeout, stream);
+
+ /* Close connection with random timeout */
silc_schedule_task_add_timeout(server->schedule,
- silc_server_packet_error_timeout,
- stream, 0, 0);
+ silc_server_packet_error_timeout, stream,
+ silc_rng_get_byte(server->rng) % 10, 0);
}
/* Packet stream callbacks */
{
SilcPacketType type = packet->type;
SilcIDListData idata = silc_packet_get_context(sock);
+#ifdef SILC_DEBUG
+ const char *ip;
+ SilcUInt16 port;
- SILC_LOG_DEBUG(("Received %s packet [flags %d]",
- silc_get_packet_name(type), packet->flags));
+ silc_socket_stream_get_info(silc_packet_stream_get_stream(sock),
+ NULL, NULL, &ip, &port);
+#endif /* SILC_DEBUG */
+
+ SILC_LOG_DEBUG(("Received %s packet [flags %d] from %s:%d",
+ silc_get_packet_name(type), packet->flags, ip, port));
/* Parse the packet type */
switch (type) {
silc_free(server->local_list);
silc_free(server->global_list);
silc_free(server->server_name);
+ silc_free(server->id);
silc_free(server);
silc_hmac_unregister_all();
sconn->no_conf = dynamic;
sconn->server = server;
- SILC_LOG_DEBUG(("Created connection %p", sconn));
+ SILC_LOG_DEBUG(("Created connection %p to %s:%d", sconn,
+ remote_host, port));
silc_schedule_task_add_timeout(server->schedule, silc_server_connect_router,
sconn, 0, 0);
if (success == FALSE) {
/* Authentication failed */
- /* XXX retry connecting */
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
if (sconn->callback)
(*sconn->callback)(server, NULL, sconn->callback_context);
SILC_ID_SERVER),
NULL, sconn->sock);
if (!id_entry) {
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
if (sconn->callback)
(*sconn->callback)(server, NULL, sconn->callback_context);
silc_server_free_sock_user_data(server, sconn->sock, NULL);
idata->status |= (SILC_IDLIST_STATUS_REGISTERED |
SILC_IDLIST_STATUS_LOCAL);
idata->sconn = sconn;
+ idata->sconn->callback = NULL;
/* Statistics */
server->stat.my_routers++;
silc_server_backup_add(server, server->id_entry, ip,
sconn->remote_port, TRUE);
}
+ }
#if 0
- } else {
+ else {
/* We already have primary router. Disconnect this connection */
SILC_LOG_DEBUG(("We already have primary router, disconnect"));
silc_idlist_del_server(server->global_list, id_entry);
silc_server_disconnect_remote(server, sconn->sock,
SILC_STATUS_ERR_RESOURCE_LIMIT, NULL);
return;
-#endif /* 0 */
}
+#endif /* 0 */
} else {
/* Add this server to be our backup router */
id_entry->server_type = SILC_BACKUP_ROUTER;
{
SilcPacketStream sock = context;
SilcUnknownEntry entry = silc_packet_get_context(sock);
- SilcServerConnection sconn = entry->data.sconn;
- SilcServer server = entry->server;
- SilcServerConfigRouter *conn = sconn->conn.ref_ptr;
+ SilcServerConnection sconn;
+ SilcServer server;
+ SilcServerConfigRouter *conn;
SilcAuthMethod auth_meth = SILC_AUTH_NONE;
void *auth_data = NULL;
SilcUInt32 auth_data_len = 0;
SilcConnAuth connauth;
SilcCipher send_key, receive_key;
SilcHmac hmac_send, hmac_receive;
- SilcHash hash;
-
- SILC_LOG_DEBUG(("Connection %p, SKE completed, entry %p", sconn, entry));
+ server = entry->server;
+ sconn = entry->data.sconn;
+ conn = sconn->conn.ref_ptr;
entry->op = NULL;
+ SILC_LOG_DEBUG(("Connection %p, SKE completed, entry %p", sconn, entry));
+
if (status != SILC_SKE_STATUS_OK) {
/* SKE failed */
SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)",
silc_ske_map_status(status), entry->hostname, entry->ip));
-
- /* XXX retry connecting */
silc_ske_free(ske);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
if (sconn->callback)
(*sconn->callback)(server, NULL, sconn->callback_context);
silc_server_free_sock_user_data(server, sconn->sock, NULL);
/* Set the keys into use. The data will be encrypted after this. */
if (!silc_ske_set_keys(ske, keymat, prop, &send_key, &receive_key,
- &hmac_send, &hmac_receive, &hash)) {
+ &hmac_send, &hmac_receive, NULL)) {
+ silc_ske_free(ske);
- /* XXX retry connecting */
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
/* Error setting keys */
- silc_ske_free(ske);
if (sconn->callback)
(*sconn->callback)(server, NULL, sconn->callback_context);
silc_server_free_sock_user_data(server, sconn->sock, NULL);
connauth = silc_connauth_alloc(server->schedule, ske,
server->config->conn_auth_timeout);
if (!connauth) {
- /* XXX retry connecting */
+ silc_ske_free(ske);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
/** Error allocating auth protocol */
- silc_ske_free(ske);
if (sconn->callback)
(*sconn->callback)(server, NULL, sconn->callback_context);
silc_server_free_sock_user_data(server, sconn->sock, NULL);
if (!sconn->sock) {
SILC_LOG_ERROR(("Cannot connect: cannot create packet stream"));
silc_stream_destroy(sconn->stream);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
if (sconn->callback)
(*sconn->callback)(server, NULL, sconn->callback_context);
silc_server_connection_free(sconn);
/* Set source ID to packet stream */
if (!silc_packet_set_ids(sconn->sock, SILC_ID_SERVER, server->id,
0, NULL)) {
+ silc_packet_stream_destroy(sconn->sock);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
if (sconn->callback)
(*sconn->callback)(server, NULL, sconn->callback_context);
- silc_packet_stream_destroy(sconn->sock);
silc_server_connection_free(sconn);
return;
}
entry = silc_calloc(1, sizeof(*entry));
if (!entry) {
silc_packet_stream_destroy(sconn->sock);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
silc_server_connection_free(sconn);
return;
}
entry->server = server;
entry->data.sconn = sconn;
+ entry->data.conn_type = SILC_CONN_UNKNOWN;
+ entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
silc_packet_set_context(sconn->sock, entry);
SILC_LOG_DEBUG(("Created unknown connection %p", entry));
server->public_key, server->private_key, sconn);
if (!ske) {
silc_free(entry);
+ silc_packet_stream_destroy(sconn->sock);
+
+ /* Try reconnecting if configuration wants it */
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ return;
+ }
+
if (sconn->callback)
(*sconn->callback)(server, NULL, sconn->callback_context);
- silc_packet_stream_destroy(sconn->sock);
silc_server_connection_free(sconn);
return;
}
/* If we've reached max retry count, give up. */
if ((sconn->retry_count > param->reconnect_count) &&
- !param->reconnect_keep_trying) {
+ sconn->no_reconnect) {
SILC_LOG_ERROR(("Could not connect, giving up"));
if (sconn->callback)
SILC_LOG_ERROR(("Could not connect to %s:%d: %s",
sconn->remote_host, sconn->remote_port,
silc_net_get_error_string(status)));
-
- if (sconn->callback)
- (*sconn->callback)(server, NULL, sconn->callback_context);
- silc_server_connection_free(sconn);
+ if (!sconn->no_reconnect) {
+ silc_schedule_task_add_timeout(sconn->server->schedule,
+ silc_server_connect_to_router_retry,
+ sconn, 1, 0);
+ silc_dlist_del(server->conns, sconn);
+ } else {
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
+ silc_server_connection_free(sconn);
+ }
break;
default:
SILC_LOG_INFO(("Unconfigured %s connection %s:%d, cannot connect",
(sconn->backup ? "backup router" : "router"),
sconn->remote_host, sconn->remote_port));
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
silc_server_connection_free(sconn);
return;
}
if (!sconn->op) {
SILC_LOG_ERROR(("Could not connect to router %s:%d",
sconn->remote_host, sconn->remote_port));
+ if (sconn->callback)
+ (*sconn->callback)(server, NULL, sconn->callback_context);
silc_server_connection_free(sconn);
return;
}
SilcServer server = context;
SilcServerConnection sconn;
SilcServerConfigRouter *ptr;
+ SilcServerConfigConnParams *param;
/* Don't connect if we are shutting down. */
if (server->server_shutdown)
}
}
+ param = (ptr->param ? ptr->param : &server->config->param);
+
/* Allocate connection object for hold connection specific stuff. */
sconn = silc_calloc(1, sizeof(*sconn));
if (!sconn)
sconn->backup_replace_ip = strdup(ptr->backup_replace_ip);
sconn->backup_replace_port = ptr->backup_replace_port;
}
+ sconn->no_reconnect = param->reconnect_keep_trying == FALSE;
SILC_LOG_DEBUG(("Created connection %p", sconn));
entry->data.conn_type = SILC_CONN_CLIENT;
/* Statistics */
+ SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+ server->stat.clients + 1));
server->stat.my_clients++;
server->stat.clients++;
server->stat.cell_clients++;
/* Add connection to server->conns so that we know we have connection
to this peer. */
sconn = silc_calloc(1, sizeof(*sconn));
+ if (!sconn)
+ goto out;
sconn->server = server;
sconn->sock = sock;
sconn->remote_host = strdup(hostname);
sconn->remote_port = port;
silc_dlist_add(server->conns, sconn);
idata->sconn = sconn;
+ idata->sconn->callback = NULL;
idata->last_receive = time(NULL);
/* Add the common data structure to the ID entry. */
idata->rekey = rekey;
idata->public_key = silc_pkcs_public_key_copy(prop->public_key);
pk = silc_pkcs_public_key_encode(idata->public_key, &pk_len);
- silc_hash_make(server->sha1hash, pk, pk_len, idata->fingerprint);
-
- silc_hash_alloc(silc_hash_get_name(prop->hash), &idata->hash);
+ if (pk) {
+ silc_hash_make(server->sha1hash, pk, pk_len, idata->fingerprint);
+ silc_free(pk);
+ }
+ idata->hash = hash;
SILC_LOG_DEBUG(("Starting connection authentication"));
server->stat.auth_attempts++;
entry->port = port;
entry->server = server;
entry->data.conn_type = SILC_CONN_UNKNOWN;
+ entry->data.status |= SILC_IDLIST_STATUS_LOCAL;
silc_packet_set_context(packet_stream, entry);
SILC_LOG_DEBUG(("Created unknown connection %p", entry));
sock, idata->sconn->rekey_timeout, 0);
}
+/* Helper to stop future rekeys on a link. */
+void silc_server_stop_rekey(SilcServer server, SilcClientEntry client)
+{
+ if (!client->connection)
+ return;
+
+ SILC_LOG_DEBUG(("Stopping rekey for client %p", client));
+
+ silc_schedule_task_del_by_all(server->schedule, 0, silc_server_do_rekey,
+ client->connection);
+}
+
/* Rekey callback. Start rekey as initiator */
SILC_TASK_CALLBACK(silc_server_do_rekey)
SILC_LOG_DEBUG(("Perform rekey, sock %p", sock));
/* Do not execute rekey with disabled connections */
- if (idata->status & SILC_IDLIST_STATUS_DISABLED)
+ if (idata->status & SILC_IDLIST_STATUS_DISABLED || !idata->rekey)
return;
/* If another protocol is active do not start rekey */
SilcIDListData idata = silc_packet_get_context(sock);
SilcSKE ske;
+ if (!idata->rekey) {
+ silc_packet_free(packet);
+ return;
+ }
+ if (idata->conn_type == SILC_CONN_UNKNOWN) {
+ silc_packet_free(packet);
+ return;
+ }
+
SILC_LOG_DEBUG(("Executing rekey protocol with %s:%d [%s], sock %p",
idata->sconn->remote_host, idata->sconn->remote_port,
SILC_CONNTYPE_STRING(idata->conn_type), sock));
if (!sock)
return;
- SILC_LOG_DEBUG(("Disconnecting remote host, sock %p", sock));
+ SILC_LOG_DEBUG(("Disconnecting remote host, sock %p, status %d", sock,
+ status));
va_start(ap, status);
cp = va_arg(ap, char *);
}
/* Update statistics */
- server->stat.my_clients--;
+
+ /* Local detached clients aren't counted. */
+ if (!client->local_detached)
+ server->stat.my_clients--;
+ SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+ server->stat.clients - 1));
+ SILC_VERIFY(server->stat.clients > 0);
server->stat.clients--;
if (server->stat.cell_clients)
server->stat.cell_clients--;
if (idata->sconn && idata->sconn->op) {
SILC_LOG_DEBUG(("Abort active protocol"));
silc_async_abort(idata->sconn->op, NULL, NULL);
+ idata->sconn->op = NULL;
}
if (idata->conn_type == SILC_CONN_UNKNOWN &&
((SilcUnknownEntry)idata)->op) {
SILC_LOG_DEBUG(("Abort active protocol"));
silc_async_abort(((SilcUnknownEntry)idata)->op, NULL, NULL);
+ ((SilcUnknownEntry)idata)->op = NULL;
}
}
/* We'll need to constantly try to reconnect to the primary
router so that we'll see when it comes back online. */
- silc_server_create_connection(server, FALSE, FALSE, ip, port,
+ silc_server_create_connection(server, TRUE, FALSE, ip, port,
silc_server_backup_connected,
NULL);
}
}
client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+
+ SILC_LOG_DEBUG(("stat.clients %d->%d", server->stat.clients,
+ server->stat.clients + 1));
+ server->stat.clients++;
}
if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
- SILC_LOG_ERROR(("Attempting to add unregistered client to channel ",
+ SILC_LOG_ERROR(("Attempting to add unregistered client to channel "
"%s", channel->channel_name));
continue;
}