From 0a6ebeedacee8f43f604cddbf7dc6bc113c6515e Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Fri, 8 Jun 2007 15:47:21 +0000 Subject: [PATCH] Added dynamic server connection support with WHOIS and IDENTIFY queries. Added support checking server name in nick@serv query. Fixed various connecting and disconnecting bugs. --- apps/silcd/command.c | 65 +++++++-- apps/silcd/command_reply.c | 12 +- apps/silcd/idlist.c | 40 ++--- apps/silcd/idlist.h | 4 +- apps/silcd/server.c | 290 +++++++++++++++++++++++++++---------- apps/silcd/server.h | 1 + apps/silcd/server_backup.c | 6 +- apps/silcd/server_query.c | 165 +++++++++++++++++++-- apps/silcd/server_query.h | 3 +- apps/silcd/server_util.c | 44 +++--- apps/silcd/serverconfig.c | 18 +++ apps/silcd/serverconfig.h | 4 + 12 files changed, 488 insertions(+), 164 deletions(-) diff --git a/apps/silcd/command.c b/apps/silcd/command.c index f5c3824f..f2211f50 100644 --- a/apps/silcd/command.c +++ b/apps/silcd/command.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005, 2007 Pekka Riikonen + Copyright (C) 1997 - 2007 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 @@ -599,7 +599,7 @@ SILC_SERVER_CMD_FUNC(whois) { SilcServerCommandContext cmd = (SilcServerCommandContext)context; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOIS, cmd, 1, 256); - silc_server_query_command(cmd->server, SILC_COMMAND_WHOIS, cmd); + silc_server_query_command(cmd->server, SILC_COMMAND_WHOIS, cmd, NULL); silc_server_command_free(cmd); } @@ -609,7 +609,7 @@ SILC_SERVER_CMD_FUNC(whowas) { SilcServerCommandContext cmd = (SilcServerCommandContext)context; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_WHOWAS, cmd, 1, 2); - silc_server_query_command(cmd->server, SILC_COMMAND_WHOWAS, cmd); + silc_server_query_command(cmd->server, SILC_COMMAND_WHOWAS, cmd, NULL); silc_server_command_free(cmd); } @@ -619,7 +619,7 @@ SILC_SERVER_CMD_FUNC(identify) { SilcServerCommandContext cmd = (SilcServerCommandContext)context; SILC_SERVER_COMMAND_CHECK(SILC_COMMAND_IDENTIFY, cmd, 1, 256); - silc_server_query_command(cmd->server, SILC_COMMAND_IDENTIFY, cmd); + silc_server_query_command(cmd->server, SILC_COMMAND_IDENTIFY, cmd, NULL); silc_server_command_free(cmd); } @@ -1325,9 +1325,8 @@ SILC_TASK_CALLBACK(silc_server_command_quit_cb) if (client) { /* Free all client specific data, such as client entry and entires on channels this client may be on. */ - silc_server_free_client_data(server, q->sock, client, - TRUE, q->signoff); - silc_packet_set_context(q->sock, NULL); + silc_server_free_sock_user_data(server, q->sock, q->signoff); + silc_server_close_connection(server, q->sock); } silc_packet_stream_unref(q->sock); @@ -1438,7 +1437,7 @@ SILC_SERVER_CMD_FUNC(kill) comment = silc_argument_get_arg_type(cmd->args, 2, &tmp_len2); if (comment && tmp_len2 > 128) { tmp_len2 = 128; - comment[127] = '\0'; + comment[tmp_len2 - 1] = '\0'; } /* If authentication data is provided then verify that killing is @@ -2295,6 +2294,37 @@ static void silc_server_command_join_channel(SilcServer server, /* Server side of command JOIN. Joins client into requested channel. If the channel does not exist it will be created. */ +/* Ways of creating channel with dynamic connections: + + 1. If channels are not local (no local_channels in silcd.conf) then + /join silc, will create connection to default router if it is + specified in the silcd.conf. If it isn't, it creates local channel. + + 2. If channels are not local then /join silc@silcnet.org, will create + connection to default if it is specified in the silcd.conf and if it + isn't or join fails it creates connection to silcnet.org and sends + the JOIN command to that server/router. + + 3. If channels are local (local_channels set in silcd.conf) then + /join silc, will create local channel. No connections are created + to anywhere. + + 4. If channels are local then /join silc@silcnet.org will create + connection to default router if it is specified in the silcd.conf and + if it isn't, or join fails it creates connection to silcnet.org and + send the JOIN command to that server/router. + + 5. If we create connection to a remote that already has a channel that + we also have as a local channel, should we merge those channels? + Should I announce my local channels when I connect to router? Should + I keep local channels local, unless I say /join localch@silcnet.org + in which case the local channel 'localch' becomes global? + + 6. After we have connection established to router, depending on the + local_channels setting /join silc will join locally or globally. + /join silc@silcnet.org would always join globally. +*/ + SILC_SERVER_CMD_FUNC(join) { SilcServerCommandContext cmd = (SilcServerCommandContext)context; @@ -2303,6 +2333,7 @@ SILC_SERVER_CMD_FUNC(join) unsigned char *auth, *cauth; SilcUInt32 tmp_len, auth_len, cauth_len; char *tmp, *channel_name, *channel_namec = NULL, *cipher, *hmac; + char parsed[256 + 1], serv[256 + 1]; SilcChannelEntry channel; SilcUInt32 umode = 0; SilcBool created = FALSE, create_key = TRUE; @@ -2321,15 +2352,19 @@ SILC_SERVER_CMD_FUNC(join) /* Truncate over long channel names */ if (tmp_len > 256) { - tmp[tmp_len - 1] = '\0'; tmp_len = 256; + tmp[tmp_len - 1] = '\0'; } - channel_name = tmp; + + /* Parse server name from the channel name */ + silc_parse_userfqdn(channel_name, parsed, sizeof(parsed), serv, + sizeof(serv)); + channel_name = parsed; /* Check for valid channel name. This is cached, the original is saved in the channel context. */ - channel_namec = silc_channel_name_check(tmp, tmp_len, SILC_STRING_UTF8, 256, - NULL); + channel_namec = silc_channel_name_check(channel_name, strlen(channel_name), + SILC_STRING_UTF8, 256, NULL); if (!channel_namec) { silc_server_command_send_status_reply(cmd, SILC_COMMAND_JOIN, SILC_STATUS_ERR_BAD_CHANNEL, 0); @@ -4152,12 +4187,12 @@ SILC_SERVER_CMD_FUNC(watch) } if (add_nick && add_nick_len > 128) { - add_nick[128] = '\0'; add_nick_len = 128; + add_nick[add_nick_len - 1] = '\0'; } if (del_nick && del_nick_len > 128) { - del_nick[128] = '\0'; del_nick_len = 128; + del_nick[del_nick_len - 1] = '\0'; } /* Add new nickname to be watched in our cell */ @@ -5138,7 +5173,7 @@ SILC_SERVER_CMD_FUNC(connect) SILC_GET32_MSB(port, tmp); /* Create the connection. It is done with timeout and is async. */ - silc_server_create_connection(server, FALSE, host, port, NULL, NULL); + silc_server_create_connection(server, FALSE, FALSE, host, port, NULL, NULL); /* Send reply to the sender */ silc_server_command_send_status_reply(cmd, SILC_COMMAND_PRIV_CONNECT, diff --git a/apps/silcd/command_reply.c b/apps/silcd/command_reply.c index e6858ae4..acf1e2dd 100644 --- a/apps/silcd/command_reply.c +++ b/apps/silcd/command_reply.c @@ -261,9 +261,10 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) return FALSE; } - /* Remove the old cache entry */ - silc_idcache_del_by_context(global ? server->global_list->clients : - server->local_list->clients, client, NULL); + /* Update entry */ + silc_idcache_update_by_context(global ? server->global_list->clients : + server->local_list->clients, client, NULL, + nickname, TRUE); silc_free(client->nickname); silc_free(client->username); @@ -277,11 +278,6 @@ silc_server_command_reply_whois_save(SilcServerCommandReplyContext cmd) client->mode = mode; client->data.status |= SILC_IDLIST_STATUS_RESOLVED; client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING; - - /* Create new cache entry */ - silc_idcache_add(global ? server->global_list->clients : - server->local_list->clients, nickname, client->id, - client); } /* Save channel list if it was sent to us */ diff --git a/apps/silcd/idlist.c b/apps/silcd/idlist.c index 8088150e..a29ba9fb 100644 --- a/apps/silcd/idlist.c +++ b/apps/silcd/idlist.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005, 2007 Pekka Riikonen + Copyright (C) 1997 - 2007 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 @@ -62,23 +62,6 @@ void silc_idlist_del_data(void *entry) idata->public_key = NULL; } -/* Purges ID cache */ - -SILC_TASK_CALLBACK(silc_idlist_purge) -{ - SilcServer server = app_context; - SilcIDListPurge i = (SilcIDListPurge)context; - - SILC_LOG_DEBUG(("Purging cache")); - -#if 0 - /* XXX */ - silc_idcache_purge(i->cache); - silc_schedule_task_add_timeout(server->schedule, silc_idlist_purge, i, - i->timeout, 0); -#endif -} - /****************************************************************************** Server entry functions @@ -380,7 +363,7 @@ silc_idlist_add_client(SilcIDList id_list, char *nickname, char *username, int silc_idlist_del_client(SilcIDList id_list, SilcClientEntry entry) { - SILC_LOG_DEBUG(("Start")); + SILC_LOG_DEBUG(("Delete client %p", entry)); if (entry) { /* Delete client, destructor will free data */ @@ -459,7 +442,8 @@ int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname, is returned to `clients_count'. Caller must free the returned table. The 'nickname' must be normalized already. */ -int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname, +int silc_idlist_get_clients_by_hash(SilcIDList id_list, + char *nickname, char *server, SilcHash md5hash, SilcClientEntry **clients, SilcUInt32 *clients_count) @@ -468,6 +452,7 @@ int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname, SilcIDCacheEntry id_cache = NULL; unsigned char hash[SILC_HASH_MAXLEN]; SilcClientID client_id; + SilcClientEntry client_entry; SILC_LOG_DEBUG(("Start")); @@ -482,6 +467,21 @@ int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname, if (!silc_idcache_find_by_id(id_list->clients, &client_id, &list)) return FALSE; + /* If server is specified, narrow the search with it. */ + if (server) { + silc_list_start(list); + while ((id_cache = silc_list_get(list))) { + client_entry = id_cache->context; + if (!client_entry->servername) + continue; + if (!silc_utf8_strcasecmp(client_entry->servername, server)) + silc_list_del(list, id_cache); + } + } + + if (!silc_list_count(list)) + return FALSE; + *clients = silc_realloc(*clients, (silc_list_count(list) + *clients_count) * sizeof(**clients)); diff --git a/apps/silcd/idlist.h b/apps/silcd/idlist.h index fe9f6ced..6af4a9eb 100644 --- a/apps/silcd/idlist.h +++ b/apps/silcd/idlist.h @@ -543,6 +543,7 @@ struct SilcIDListStruct { typedef struct { /* Generic data structure. DO NOT add anything before this! */ SilcIDListDataStruct data; + SilcAsyncOperation op; SilcServerConfigRef cconfig; SilcServerConfigRef sconfig; SilcServerConfigRef rconfig; @@ -588,7 +589,8 @@ int silc_idlist_get_clients_by_nickname(SilcIDList id_list, char *nickname, char *server, SilcClientEntry **clients, SilcUInt32 *clients_count); -int silc_idlist_get_clients_by_hash(SilcIDList id_list, char *nickname, +int silc_idlist_get_clients_by_hash(SilcIDList id_list, + char *nickname, char *server, SilcHash md5hash, SilcClientEntry **clients, SilcUInt32 *clients_count); diff --git a/apps/silcd/server.c b/apps/silcd/server.c index 16d22988..d4770c1f 100644 --- a/apps/silcd/server.c +++ b/apps/silcd/server.c @@ -4,7 +4,7 @@ Author: Pekka Riikonen - Copyright (C) 1997 - 2005, 2007 Pekka Riikonen + Copyright (C) 1997 - 2007 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 @@ -219,6 +219,7 @@ static void silc_server_packet_error(SilcPacketEngine engine, void *callback_context, void *stream_context) { + SilcServer server = callback_context; SilcIDListData idata = silc_packet_get_context(stream); SilcStream sock = silc_packet_stream_get_stream(stream); const char *ip; @@ -230,9 +231,26 @@ static void silc_server_packet_error(SilcPacketEngine engine, if (!silc_socket_stream_get_info(sock, NULL, NULL, &ip, &port)) return; - SILC_LOG_ERROR(("Connection %s:%d [%s]: %s", - SILC_CONNTYPE_STRING(idata->conn_type), ip, port, + SILC_LOG_ERROR(("Connection %s:%d [%s]: %s", ip, port, + SILC_CONNTYPE_STRING(idata->conn_type), silc_packet_error_string(error))); + + if (server->router_conn && server->router_conn->sock == stream && + !server->router && server->standalone) { + silc_server_create_connections(server); + } else { + /* If backup disconnected then mark that resuming will not be allowed */ + if (server->server_type == SILC_ROUTER && !server->backup_router && + idata->conn_type == SILC_CONN_SERVER) { + SilcServerEntry server_entry = (SilcServerEntry)idata; + if (server_entry->server_type == SILC_BACKUP_ROUTER) + server->backup_closed = TRUE; + } + + silc_server_free_sock_user_data(server, stream, NULL); + } + + silc_server_close_connection(server, stream); } /* Packet stream callbacks */ @@ -337,11 +355,13 @@ static void silc_server_packet_parse_type(SilcServer server, status = (SilcStatus)packet->buffer.data[0]; if (silc_buffer_len(&packet->buffer) > 1 && - silc_utf8_valid(packet->buffer.data + 1, silc_buffer_len(&packet->buffer) - 1)) + silc_utf8_valid(packet->buffer.data + 1, + silc_buffer_len(&packet->buffer) - 1)) message = silc_memdup(packet->buffer.data + 1, silc_buffer_len(&packet->buffer) - 1); - if (!silc_socket_stream_get_info(sock, NULL, &hostname, &ip, NULL)) + if (!silc_socket_stream_get_info(silc_packet_stream_get_stream(sock), + NULL, &hostname, &ip, NULL)) break; SILC_LOG_INFO(("Disconnected by %s (%s): %s (%d) %s", ip, hostname, @@ -560,6 +580,9 @@ void silc_server_free(SilcServer server) { SilcList list; SilcIDCacheEntry cache; + SilcIDListData idata; + + SILC_LOG_DEBUG(("Free server %p", server)); if (!server) return; @@ -574,8 +597,12 @@ void silc_server_free(SilcServer server) silc_pkcs_private_key_free(server->private_key); if (server->pending_commands) silc_dlist_uninit(server->pending_commands); - if (server->id_entry) + if (server->id_entry) { + if (server->id_entry->data.sconn) + silc_schedule_task_del_by_context(server->schedule, + server->id_entry->data.sconn->sock); silc_idlist_del_server(server->local_list, server->id_entry); + } /* Delete all channels */ if (silc_idcache_get_all(server->local_list->channels, &list)) { @@ -592,24 +619,38 @@ void silc_server_free(SilcServer server) /* Delete all clients */ if (silc_idcache_get_all(server->local_list->clients, &list)) { silc_list_start(list); - while ((cache = silc_list_get(list))) + while ((cache = silc_list_get(list))) { + silc_schedule_task_del_by_context(server->schedule, cache->context); silc_idlist_del_client(server->local_list, cache->context); + } } if (silc_idcache_get_all(server->global_list->clients, &list)) { silc_list_start(list); - while ((cache = silc_list_get(list))) + while ((cache = silc_list_get(list))) { + silc_schedule_task_del_by_context(server->schedule, cache->context); silc_idlist_del_client(server->global_list, cache->context); + } } /* Delete all servers */ if (silc_idcache_get_all(server->local_list->servers, &list)) { silc_list_start(list); - while ((cache = silc_list_get(list))) + while ((cache = silc_list_get(list))) { + idata = (SilcIDListData)cache->context; + if (idata->sconn) + silc_schedule_task_del_by_context(server->schedule, + idata->sconn->sock); silc_idlist_del_server(server->local_list, cache->context); + } } if (silc_idcache_get_all(server->global_list->servers, &list)) { - while ((cache = silc_list_get(list))) + while ((cache = silc_list_get(list))) { + idata = (SilcIDListData)cache->context; + if (idata->sconn) + silc_schedule_task_del_by_context(server->schedule, + idata->sconn->sock); silc_idlist_del_server(server->global_list, cache->context); + } } silc_idcache_free(server->local_list->clients); @@ -629,11 +670,13 @@ void silc_server_free(SilcServer server) silc_skr_free(server->repository); silc_packet_engine_stop(server->packet_engine); + silc_schedule_task_del_by_context(server->schedule, server); + silc_schedule_uninit(server->schedule); + server->schedule = NULL; + silc_free(server->local_list); silc_free(server->global_list); silc_free(server->server_name); - silc_free(server->purge_i); - silc_free(server->purge_g); silc_free(server); silc_hmac_unregister_all(); @@ -734,7 +777,6 @@ SilcBool silc_server_init(SilcServer server) { SilcServerID *id; SilcServerEntry id_entry; - SilcIDListPurge purge; SilcNetListener listener; SilcUInt16 *port; char **ip; @@ -842,7 +884,9 @@ SilcBool silc_server_init(SilcServer server) /* Create a Server ID for the server. */ port = silc_net_listener_get_port(listener, NULL); ip = silc_net_listener_get_ip(listener, NULL); - silc_id_create_server_id(ip[0], port[0], server->rng, &id); + silc_id_create_server_id(server->config->server_info->primary->public_ip ? + server->config->server_info->primary->public_ip : + ip[0], port[0], server->rng, &id); if (!id) goto err; @@ -896,6 +940,7 @@ SilcBool silc_server_init(SilcServer server) } } +#if 0 /* Register the ID Cache purge task. This periodically purges the ID cache and removes the expired cache entries. */ @@ -912,6 +957,7 @@ SilcBool silc_server_init(SilcServer server) purge->timeout = 300; silc_schedule_task_add_timeout(server->schedule, silc_idlist_purge, (void *)purge, purge->timeout, 0); +#endif /Ã* 0 */ /* If we are normal server we'll retrieve network statisticial information once in a while from the router. */ @@ -932,7 +978,7 @@ SilcBool silc_server_init(SilcServer server) /* Register client entry expiration timeout */ silc_schedule_task_add_timeout(server->schedule, silc_server_purge_expired_clients, server, - 600, 0); + 120, 0); /* Initialize HTTP server */ silc_server_http_init(server); @@ -1246,8 +1292,6 @@ void silc_server_stop(SilcServer server) silc_server_http_uninit(server); silc_schedule_stop(server->schedule); - silc_schedule_uninit(server->schedule); - server->schedule = NULL; SILC_LOG_DEBUG(("Server stopped")); } @@ -1259,6 +1303,7 @@ SILC_TASK_CALLBACK(silc_server_purge_expired_clients) SilcServer server = context; SilcClientEntry client; SilcIDList id_list; + SilcUInt64 curtime = silc_time(); SILC_LOG_DEBUG(("Expire timeout")); @@ -1267,6 +1312,12 @@ SILC_TASK_CALLBACK(silc_server_purge_expired_clients) if (client->data.status & SILC_IDLIST_STATUS_REGISTERED) continue; + /* For unregistered clients the created timestamp is actually + unregistered timestamp. Make sure client remains in history + at least 500 seconds. */ + if (curtime - client->data.created < 500) + continue; + id_list = (client->data.status & SILC_IDLIST_STATUS_LOCAL ? server->local_list : server->global_list); @@ -1277,7 +1328,7 @@ SILC_TASK_CALLBACK(silc_server_purge_expired_clients) silc_schedule_task_add_timeout(server->schedule, silc_server_purge_expired_clients, server, - 600, 0); + 120, 0); } @@ -1285,8 +1336,9 @@ SILC_TASK_CALLBACK(silc_server_purge_expired_clients) /* Free connection context */ -static void silc_server_connection_free(SilcServerConnection sconn) +void silc_server_connection_free(SilcServerConnection sconn) { + SILC_LOG_DEBUG(("Free connection %p", sconn)); silc_dlist_del(sconn->server->conns, sconn); silc_server_config_unref(&sconn->conn); silc_free(sconn->remote_host); @@ -1298,6 +1350,7 @@ static void silc_server_connection_free(SilcServerConnection sconn) void silc_server_create_connection(SilcServer server, SilcBool reconnect, + SilcBool dynamic, const char *remote_host, SilcUInt32 port, SilcServerConnectCallback callback, void *context) @@ -1313,6 +1366,10 @@ void silc_server_create_connection(SilcServer server, sconn->no_reconnect = reconnect == FALSE; sconn->callback = callback; sconn->callback_context = context; + sconn->no_conf = dynamic; + sconn->server = server; + + SILC_LOG_DEBUG(("Created connection %p", sconn)); silc_schedule_task_add_timeout(server->schedule, silc_server_connect_router, sconn, 0, 0); @@ -1330,22 +1387,29 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success, SilcServerConfigServer *conn; SilcServerConfigConnParams *param; SilcIDListData idata; - SilcServerEntry id_entry; + SilcServerEntry id_entry = NULL; unsigned char id[32]; SilcUInt32 id_len; SilcID remote_id; SILC_LOG_DEBUG(("Connection authentication completed")); + sconn->op = NULL; + if (success == FALSE) { /* Authentication failed */ /* XXX retry connecting */ silc_server_disconnect_remote(server, sconn->sock, SILC_STATUS_ERR_AUTH_FAILED, NULL); + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); return; } + /* XXX For now remote is router always */ + entry->data.conn_type = SILC_CONN_ROUTER; + SILC_LOG_INFO(("Connected to %s %s", SILC_CONNTYPE_STRING(entry->data.conn_type), sconn->remote_host)); @@ -1363,6 +1427,8 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success, if (!id_entry) { silc_server_disconnect_remote(server, sconn->sock, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL); + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); silc_free(entry); return; @@ -1385,6 +1451,8 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success, SILC_STR_END)) { silc_server_disconnect_remote(server, sconn->sock, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL); + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); silc_free(entry); return; @@ -1415,11 +1483,15 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success, as NULL since it's local to us. */ id_entry = silc_idlist_add_server(server->global_list, strdup(sconn->remote_host), - SILC_ROUTER, &remote_id.u.server_id, + SILC_ROUTER, + silc_id_dup(&remote_id.u.server_id, + SILC_ID_SERVER), NULL, sconn->sock); if (!id_entry) { silc_server_disconnect_remote(server, sconn->sock, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL); + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); silc_free(entry); return; @@ -1427,9 +1499,10 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success, /* Registered */ silc_idlist_add_data(id_entry, (SilcIDListData)entry); - idata = (SilcIDListData)entry; + idata = (SilcIDListData)id_entry; idata->status |= (SILC_IDLIST_STATUS_REGISTERED | SILC_IDLIST_STATUS_LOCAL); + idata->sconn = sconn; if (!sconn->backup) { /* Mark this router our primary router if we're still standalone */ @@ -1479,11 +1552,15 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success, default: silc_server_disconnect_remote(server, sconn->sock, SILC_STATUS_ERR_AUTH_FAILED, NULL); + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); silc_free(entry); return; } + SILC_LOG_DEBUG(("Connection established, sock %p", sconn->sock)); + conn = sconn->conn.ref_ptr; param = &server->config->param; if (conn && conn->param) @@ -1499,6 +1576,10 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success, silc_socket_set_heartbeat(sock, param->keepalive_secs, server, silc_server_perform_heartbeat, server->schedule); +#endif /* 0 */ + + /* Set the entry as packet stream context */ + silc_packet_set_context(sconn->sock, id_entry); out: /* Call the completion callback to indicate that we've connected to @@ -1506,6 +1587,7 @@ silc_server_ke_auth_compl(SilcConnAuth connauth, SilcBool success, if (sconn && sconn->callback) (*sconn->callback)(server, id_entry, sconn->callback_context); +#if 0 /* Free the temporary connection data context */ if (sconn) { silc_server_config_unref(&sconn->conn); @@ -1528,8 +1610,9 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status, SilcSKERekeyMaterial rekey, void *context) { - SilcServerConnection sconn = context; - SilcUnknownEntry entry = silc_packet_get_context(sconn->sock); + SilcPacketStream sock = context; + SilcUnknownEntry entry = silc_packet_get_context(sock); + SilcServerConnection sconn = silc_ske_get_context(ske); SilcServer server = entry->server; SilcServerConfigRouter *conn = sconn->conn.ref_ptr; SilcAuthMethod auth_meth = SILC_AUTH_NONE; @@ -1540,6 +1623,8 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status, SilcHmac hmac_send, hmac_receive; SilcHash hash; + sconn->op = NULL; + if (status != SILC_SKE_STATUS_OK) { /* SKE failed */ SILC_LOG_ERROR(("Error (%s) during Key Exchange protocol with %s (%s)", @@ -1549,6 +1634,8 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status, silc_ske_free(ske); silc_server_disconnect_remote(server, sconn->sock, SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL); + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); return; } @@ -1565,6 +1652,8 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status, silc_ske_free(ske); silc_server_disconnect_remote(server, sconn->sock, SILC_STATUS_ERR_KEY_EXCHANGE_FAILED, NULL); + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); return; } @@ -1582,6 +1671,8 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status, silc_ske_free(ske); silc_server_disconnect_remote(server, sconn->sock, SILC_STATUS_ERR_RESOURCE_LIMIT, NULL); + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); return; } @@ -1604,10 +1695,11 @@ static void silc_server_ke_completed(SilcSKE ske, SilcSKEStatus status, } /* Start connection authentication */ - silc_connauth_initiator(connauth, server->server_type == SILC_ROUTER ? - SILC_CONN_ROUTER : SILC_CONN_SERVER, auth_meth, - auth_data, auth_data_len, - silc_server_ke_auth_compl, sconn); + sconn->op = + silc_connauth_initiator(connauth, server->server_type == SILC_ROUTER ? + SILC_CONN_ROUTER : SILC_CONN_SERVER, auth_meth, + auth_data, auth_data_len, + silc_server_ke_auth_compl, sconn); } /* Function that is called when the network connection to a router has @@ -1631,6 +1723,8 @@ void silc_server_start_key_exchange(SilcServerConnection sconn) if (!sconn->sock) { SILC_LOG_ERROR(("Cannot connect: cannot create packet stream")); silc_stream_destroy(sconn->stream); + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); return; } @@ -1640,6 +1734,8 @@ void silc_server_start_key_exchange(SilcServerConnection sconn) if (!silc_packet_set_ids(sconn->sock, SILC_ID_SERVER, server->id, 0, NULL)) { silc_packet_stream_destroy(sconn->sock); + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); return; } @@ -1664,10 +1760,12 @@ void silc_server_start_key_exchange(SilcServerConnection sconn) /* Start SILC Key Exchange protocol */ SILC_LOG_DEBUG(("Starting key exchange protocol")); ske = silc_ske_alloc(server->rng, server->schedule, server->repository, - server->public_key, server->private_key, sconn->sock); + server->public_key, server->private_key, sconn); if (!ske) { silc_free(entry); silc_packet_stream_destroy(sconn->sock); + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); return; } @@ -1677,7 +1775,7 @@ void silc_server_start_key_exchange(SilcServerConnection sconn) /* Start key exchange protocol */ params.version = silc_version_string; params.timeout_secs = server->config->key_exchange_timeout; - silc_ske_initiator(ske, sconn->sock, ¶ms, NULL); + sconn->op = silc_ske_initiator(ske, sconn->sock, ¶ms, NULL); } /* Timeout callback that will be called to retry connecting to remote @@ -1712,6 +1810,9 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router_retry) if ((sconn->retry_count > param->reconnect_count) && !param->reconnect_keep_trying) { SILC_LOG_ERROR(("Could not connect, giving up")); + + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); return; } @@ -1754,6 +1855,9 @@ static void silc_server_connection_established(SilcNetStatus status, 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); break; @@ -1766,6 +1870,8 @@ static void silc_server_connection_established(SilcNetStatus status, silc_server_connect_to_router_retry, sconn, 1, 0); } else { + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); } break; @@ -1784,6 +1890,8 @@ SILC_TASK_CALLBACK(silc_server_connect_router) /* Don't connect if we are shutting down. */ if (server->server_shutdown) { + if (sconn->callback) + (*sconn->callback)(server, NULL, sconn->callback_context); silc_server_connection_free(sconn); return; } @@ -1792,7 +1900,7 @@ SILC_TASK_CALLBACK(silc_server_connect_router) (sconn->backup ? "backup router" : "router"), sconn->remote_host, sconn->remote_port)); - if (!server->no_conf) { + if (!sconn->no_conf) { /* Find connection configuration */ rconn = silc_server_config_find_router_conn(server, sconn->remote_host, sconn->remote_port); @@ -1868,6 +1976,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router) if (!ptr->initiator) continue; + if (ptr->dynamic_connection) + continue; /* Check whether we are connecting or connected to this host already */ if (silc_server_num_sockets_by_remote(server, @@ -1916,6 +2026,8 @@ SILC_TASK_CALLBACK(silc_server_connect_to_router) sconn->backup_replace_port = ptr->backup_replace_port; } + SILC_LOG_DEBUG(("Created connection %p", sconn)); + /* XXX */ if (!server->router_conn && !sconn->backup) server->router_conn = sconn; @@ -2019,6 +2131,7 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success, const char *hostname; SilcUInt16 port; + entry->op = NULL; silc_socket_stream_get_info(silc_packet_stream_get_stream(sock), NULL, &hostname, NULL, &port); @@ -2140,7 +2253,7 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success, SilcBool backup_router = FALSE; char *backup_replace_ip = NULL; SilcUInt16 backup_replace_port = 0; - SilcServerConfigServer *sconn = entry->sconfig.ref_ptr; + SilcServerConfigServer *srvconn = entry->sconfig.ref_ptr; SilcServerConfigRouter *rconn = entry->rconfig.ref_ptr; /* If we are backup router and this is incoming server connection @@ -2199,14 +2312,14 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success, if (!silc_server_connection_allowed(server, sock, entry->data.conn_type, &server->config->param, - sconn ? sconn->param : NULL, + srvconn ? srvconn->param : NULL, silc_connauth_get_ske(connauth))) { server->stat.auth_failures++; goto out; } - if (sconn) { - if (sconn->param) { - param = sconn->param; + if (srvconn) { + if (srvconn->param) { + param = srvconn->param; if (!param->keepalive_secs) param->keepalive_secs = server->config->param.keepalive_secs; @@ -2220,7 +2333,7 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success, } } - backup_router = sconn->backup_router; + backup_router = srvconn->backup_router; } } @@ -2357,7 +2470,7 @@ silc_server_accept_auth_compl(SilcConnAuth connauth, SilcBool success, silc_packet_set_context(sock, id_entry); /* Connection has been fully established now. Everything is ok. */ - SILC_LOG_DEBUG(("New connection authenticated")); + SILC_LOG_DEBUG(("New connection %p authenticated", sconn)); #if 0 /* Perform keepalive. */ @@ -2400,6 +2513,10 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status, SilcCipher send_key, receive_key; SilcHmac hmac_send, hmac_receive; SilcHash hash; + unsigned char *pk; + SilcUInt32 pk_len; + + entry->op = NULL; if (status != SILC_SKE_STATUS_OK) { /* SKE failed */ @@ -2427,6 +2544,8 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status, 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_LOG_DEBUG(("Starting connection authentication")); server->stat.auth_attempts++; @@ -2442,8 +2561,9 @@ silc_server_accept_completed(SilcSKE ske, SilcSKEStatus status, } /* Start connection authentication */ - silc_connauth_responder(connauth, silc_server_accept_get_auth, - silc_server_accept_auth_compl, sock); + entry->op = + silc_connauth_responder(connauth, silc_server_accept_get_auth, + silc_server_accept_auth_compl, sock); } /* Accept new TCP connection */ @@ -2548,6 +2668,7 @@ static void silc_server_accept_new_connection(SilcNetStatus status, entry->ip = ip; entry->port = port; entry->server = server; + entry->data.conn_type = SILC_CONN_UNKNOWN; silc_packet_set_context(packet_stream, entry); silc_server_config_ref(&entry->cconfig, server->config, cconfig); @@ -2584,7 +2705,7 @@ static void silc_server_accept_new_connection(SilcNetStatus status, /* Start key exchange protocol */ params.version = silc_version_string; params.timeout_secs = server->config->key_exchange_timeout; - silc_ske_responder(ske, packet_stream, ¶ms); + entry->op = silc_ske_responder(ske, packet_stream, ¶ms); } @@ -2631,6 +2752,8 @@ SILC_TASK_CALLBACK(silc_server_do_rekey) SilcIDListData idata = silc_packet_get_context(sock); SilcSKE ske; + SILC_LOG_DEBUG(("Perform rekey, sock %p", sock)); + /* Do not execute rekey with disabled connections */ if (idata->status & SILC_IDLIST_STATUS_DISABLED) return; @@ -2736,20 +2859,6 @@ void silc_server_close_connection(SilcServer server, const char *hostname; SilcUInt16 port; -#if 0 - /* If any protocol is active cancel its execution. It will call - the final callback which will finalize the disconnection. */ - if (sock->protocol && sock->protocol->protocol && - sock->protocol->protocol->type != SILC_PROTOCOL_SERVER_BACKUP) { - SILC_LOG_DEBUG(("Cancelling protocol, calling final callback")); - silc_protocol_cancel(sock->protocol, server->schedule); - sock->protocol->state = SILC_PROTOCOL_STATE_ERROR; - silc_protocol_execute_final(sock->protocol, server->schedule); - sock->protocol = NULL; - return; - } -#endif - memset(tmp, 0, sizeof(tmp)); // silc_socket_get_error(sock, tmp, sizeof(tmp)); silc_socket_stream_get_info(silc_packet_stream_get_stream(sock), @@ -2760,8 +2869,15 @@ void silc_server_close_connection(SilcServer server, // silc_socket_set_qos(sock, 0, 0, 0, 0, NULL); + if (idata && idata->sconn) { + silc_server_connection_free(idata->sconn); + idata->sconn = NULL; + } + /* Close connection with timeout */ server->stat.conn_num--; + silc_schedule_task_del_by_all(server->schedule, 0, + silc_server_close_connection_final, sock); silc_schedule_task_add_timeout(server->schedule, silc_server_close_connection_final, sock, 0, 1); @@ -2799,16 +2915,6 @@ void silc_server_disconnect_remote(SilcServer server, silc_server_close_connection(server, sock); } -SILC_TASK_CALLBACK(silc_server_free_client_data_timeout) -{ - SilcClientEntry client = context; - - assert(!silc_hash_table_count(client->channels)); - - silc_idlist_del_data(client); - // silc_idcache_purge_by_context(server->local_list->clients, client); -} - /* Frees client data and notifies about client's signoff. */ void silc_server_free_client_data(SilcServer server, @@ -2817,7 +2923,7 @@ void silc_server_free_client_data(SilcServer server, int notify, const char *signoff) { - SILC_LOG_DEBUG(("Freeing client data")); + SILC_LOG_DEBUG(("Freeing client %p data", client)); if (client->id) { /* Check if anyone is watching this nickname */ @@ -2863,16 +2969,15 @@ void silc_server_free_client_data(SilcServer server, into history (for WHOWAS command) for 5 minutes, unless we're shutting down server. */ if (!server->server_shutdown) { - silc_schedule_task_add_timeout(server->schedule, - silc_server_free_client_data_timeout, - client, 600, 0); client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED; - client->data.status &= ~SILC_IDLIST_STATUS_LOCAL; client->mode = 0; client->router = NULL; client->connection = NULL; + client->data.created = silc_time(); + silc_dlist_add(server->expired_clients, client); } else { /* Delete directly since we're shutting down server */ + SILC_LOG_DEBUG(("Delete client directly")); silc_idlist_del_data(client); silc_idlist_del_client(server->local_list, client); } @@ -2888,15 +2993,34 @@ void silc_server_free_sock_user_data(SilcServer server, { SilcIDListData idata = silc_packet_get_context(sock); + SILC_LOG_DEBUG(("Start")); + if (!idata) return; + silc_schedule_task_del_by_context(server->schedule, sock); + + /* Cancel active protocols */ + if (idata) { + if (idata->sconn && idata->sconn->op) { + SILC_LOG_DEBUG(("Abort active protocol")); + silc_async_abort(idata->sconn->op, NULL, 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); + } + } + switch (idata->conn_type) { case SILC_CONN_CLIENT: { SilcClientEntry client_entry = (SilcClientEntry)idata; silc_server_free_client_data(server, sock, client_entry, TRUE, signoff_message); + if (idata->sconn) + silc_server_connection_free(idata->sconn); silc_packet_set_context(sock, NULL); break; } @@ -2907,7 +3031,7 @@ void silc_server_free_sock_user_data(SilcServer server, SilcServerEntry user_data = (SilcServerEntry)idata; SilcServerEntry backup_router = NULL; - SILC_LOG_DEBUG(("Freeing server data")); + SILC_LOG_DEBUG(("Freeing server %p data", user_data)); if (user_data->id) backup_router = silc_server_backup_get(server, user_data->id); @@ -3088,6 +3212,8 @@ void silc_server_free_sock_user_data(SilcServer server, backup_router->connection); } + if (idata->sconn) + silc_server_connection_free(idata->sconn); silc_packet_set_context(sock, NULL); break; } @@ -3098,6 +3224,8 @@ void silc_server_free_sock_user_data(SilcServer server, SILC_LOG_DEBUG(("Freeing unknown connection data")); + if (idata->sconn) + silc_server_connection_free(idata->sconn); silc_idlist_del_data(idata); silc_free(entry); silc_packet_set_context(sock, NULL); @@ -4919,15 +5047,17 @@ SILC_TASK_CALLBACK(silc_server_get_stats) SILC_LOG_DEBUG(("Retrieving stats from router")); server->stat.commands_sent++; idp = silc_id_payload_encode(server->router->id, SILC_ID_SERVER); - packet = silc_command_payload_encode_va(SILC_COMMAND_STATS, - ++server->cmd_ident, 1, - 1, idp->data, - silc_buffer_len(idp)); - silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server), - SILC_PACKET_COMMAND, 0, packet->data, - silc_buffer_len(packet)); - silc_buffer_free(packet); - silc_buffer_free(idp); + if (idp) { + packet = silc_command_payload_encode_va(SILC_COMMAND_STATS, + ++server->cmd_ident, 1, + 1, idp->data, + silc_buffer_len(idp)); + silc_server_packet_send(server, SILC_PRIMARY_ROUTE(server), + SILC_PACKET_COMMAND, 0, packet->data, + silc_buffer_len(packet)); + silc_buffer_free(packet); + silc_buffer_free(idp); + } } silc_schedule_task_add_timeout(server->schedule, silc_server_get_stats, diff --git a/apps/silcd/server.h b/apps/silcd/server.h index b3021e78..f249a376 100644 --- a/apps/silcd/server.h +++ b/apps/silcd/server.h @@ -146,6 +146,7 @@ void silc_server_stop(SilcServer server); void silc_server_start_key_exchange(SilcServerConnection sconn); void silc_server_create_connection(SilcServer server, SilcBool reconnect, + SilcBool dynamic, const char *remote_host, SilcUInt32 port, SilcServerConnectCallback callback, void *context); diff --git a/apps/silcd/server_backup.c b/apps/silcd/server_backup.c index c52b3e35..1ef5fb0f 100644 --- a/apps/silcd/server_backup.c +++ b/apps/silcd/server_backup.c @@ -791,7 +791,7 @@ SILC_TASK_CALLBACK(silc_server_backup_connected_again) if (primary) { if (!silc_server_find_socket_by_host(server, SILC_CONN_ROUTER, primary->host, primary->port)) - silc_server_create_connection(server, FALSE, + silc_server_create_connection(server, FALSE, FALSE, primary->host, primary->port, silc_server_backup_connected, context); @@ -841,7 +841,7 @@ SILC_TASK_CALLBACK(silc_server_backup_connect_primary_again) if (primary) { if (!silc_server_find_socket_by_host(server, SILC_CONN_ROUTER, primary->host, primary->port)) - silc_server_create_connection(server, FALSE, + silc_server_create_connection(server, FALSE, FALSE, primary->host, primary->port, silc_server_backup_connect_primary, context); @@ -1034,7 +1034,7 @@ SILC_TASK_CALLBACK(silc_server_protocol_backup) SILC_CONN_ROUTER)) { SILC_LOG_DEBUG(("Received START (session %d), reconnect to router", ctx->session)); - silc_server_create_connection(server, FALSE; + silc_server_create_connection(server, FALSE, FALSE, primary->host, primary->port, silc_server_backup_connect_primary, silc_socket_dup(ctx->sock)); diff --git a/apps/silcd/server_query.c b/apps/silcd/server_query.c index 166db3af..634a15fa 100644 --- a/apps/silcd/server_query.c +++ b/apps/silcd/server_query.c @@ -61,6 +61,7 @@ typedef struct { SilcDList attrs; /* Requested Attributes in WHOIS */ /* Query session data */ + SilcPacketStream router; /* Router to send our query */ SilcServerCommandContext cmd; /* Command context for query */ SilcServerQueryList querylist; /* Temporary query list context */ SilcServerQueryID queries; /* Ongoing queries */ @@ -69,11 +70,15 @@ typedef struct { SilcUInt16 queries_count; /* Number of ongoing queries */ SilcUInt16 queries_left; /* Number of ongoing queries left */ SilcUInt16 errors_count; /* number of errors */ - unsigned int querycmd : 7; /* Query command (SilcCommand) */ - unsigned int resolved : 1; /* TRUE if normal server has resolved + unsigned int querycmd : 7; /* Query command (SilcCommand) */ + unsigned int resolved : 1; /* TRUE if normal server has resolved information from router */ + unsigned int dynamic_prim : 1; /* Dynamic connection attempt to primary */ + unsigned int dynamic_retry : 1; /* Primary returned error, send to + nick@serv server. */ } *SilcServerQuery; + void silc_server_query_free(SilcServerQuery query); void silc_server_query_send_error(SilcServer server, SilcServerQuery query, @@ -220,16 +225,22 @@ void silc_server_query_add_error_id(SilcServer server, to the entity who sent this query to us automatically. Returns TRUE if the query is being processed or FALSE on error. */ -SilcBool silc_server_query_command(SilcServer server, SilcCommand querycmd, - SilcServerCommandContext cmd) +SilcBool silc_server_query_command(SilcServer server, + SilcCommand querycmd, + SilcServerCommandContext cmd, + void *old_query) { SilcServerQuery query; SILC_LOG_DEBUG(("Query %s command", silc_get_command_name(querycmd))); - query = silc_calloc(1, sizeof(*query)); - query->querycmd = querycmd; - query->cmd = silc_server_command_dup(cmd); + if (!old_query) { + query = silc_calloc(1, sizeof(*query)); + query->querycmd = querycmd; + query->cmd = silc_server_command_dup(cmd); + query->router = SILC_PRIMARY_ROUTE(server); + } else + query = old_query; switch (querycmd) { @@ -281,6 +292,43 @@ SilcBool silc_server_query_command(SilcServer server, SilcCommand querycmd, return TRUE; } +/* Remote server connected callback. */ + +void silc_server_query_connected(SilcServer server, + SilcServerEntry server_entry, + void *context) +{ + SilcServerQuery query = context; + + if (!server_entry) { + /* Connecting failed */ + + if (query->dynamic_prim /* && @serv != prim.host.name */ && + !silc_server_num_sockets_by_remote(server, query->nick_server, + query->nick_server, 706)) { + /* Connection attempt to primary router failed, now try to the one + specified in nick@server. */ + silc_server_create_connection(server, FALSE, TRUE, query->nick_server, + 706, silc_server_query_connected, + query); + query->dynamic_prim = FALSE; + return; + } + + /* Process the query after failed connect. This will send error back + because such nick was not found. */ + SILC_LOG_DEBUG(("Process query, connecting failed")); + silc_server_query_process(server, query, TRUE); + return; + } + + /* Reprocess the query */ + SILC_LOG_DEBUG(("Reprocess query after creating connection to %s", + server_entry->server_name)); + query->router = server_entry->data.sconn->sock; + silc_server_query_command(server, query->querycmd, query->cmd, query); +} + /* Send the received query to our primary router since we could not handle the query directly. We will reprocess the query after our router replies back. */ @@ -290,7 +338,8 @@ void silc_server_query_send_router(SilcServer server, SilcServerQuery query) SilcBuffer tmpbuf; SilcUInt16 old_ident; - SILC_LOG_DEBUG(("Forwarding the query to router for processing")); + SILC_LOG_DEBUG(("Forwarding the query to router %p for processing", + query->router)); /* Statistics */ server->stat.commands_sent++; @@ -299,8 +348,7 @@ void silc_server_query_send_router(SilcServer server, SilcServerQuery query) old_ident = silc_command_get_ident(query->cmd->payload); silc_command_set_ident(query->cmd->payload, ++server->cmd_ident); tmpbuf = silc_command_payload_encode_payload(query->cmd->payload); - silc_server_packet_send(server, - SILC_PRIMARY_ROUTE(server), + silc_server_packet_send(server, query->router, SILC_PACKET_COMMAND, 0, tmpbuf->data, silc_buffer_len(tmpbuf)); silc_command_set_ident(query->cmd->payload, old_ident); @@ -335,6 +383,21 @@ void silc_server_query_send_router_reply(void *context, void *reply) if (cmdr && !silc_command_get_status(cmdr->payload, NULL, NULL)) { SilcBuffer buffer; + /* If this was nick@server query, retry to @serv if the primary router + returned error. */ + if (query->nick_server[0] && !query->dynamic_retry && + !silc_server_num_sockets_by_remote(server, query->nick_server, + query->nick_server, 706)) { + SILC_LOG_DEBUG(("Retry query by connecting to %s:%d", + query->nick_server, 706)); + silc_server_create_connection(server, FALSE, TRUE, query->nick_server, + 706, silc_server_query_connected, + query); + query->dynamic_retry = TRUE; + query->resolved = FALSE; + return; + } + SILC_LOG_DEBUG(("Sending error to original query")); /* Statistics */ @@ -427,6 +490,38 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) silc_free(tmp); } + /* Check server name. If we are open server and don't yet have + connection to remote router, create it now. */ + if (query->nick_server[0] && server->config->open_server && + !query->resolved) { + /* If primary router is specified, use that. Otherwise connect + to the server in nick@server string. */ + SilcServerConfigRouter *router; + + router = silc_server_config_get_primary_router(server); + if (router && server->standalone) { + /* Create connection to primary router */ + SILC_LOG_DEBUG(("Create dynamic connection to primary router %s:%d", + router->host, router->port)); + query->dynamic_prim = TRUE; + silc_server_create_connection(server, FALSE, TRUE, + router->host, router->port, + silc_server_query_connected, query); + return; + } else if (!silc_server_num_sockets_by_remote(server, + query->nick_server, + query->nick_server, + 706)) { + /* Create connection and handle the query after connection */ + SILC_LOG_DEBUG(("Create dynamic connection to %s:%d", + query->nick_server, 706)); + silc_server_create_connection(server, FALSE, TRUE, + query->nick_server, 706, + silc_server_query_connected, query); + return; + } + } + } else { /* Parse the IDs included in the query */ query->ids = silc_calloc(argc, sizeof(*query->ids)); @@ -582,6 +677,38 @@ void silc_server_query_parse(SilcServer server, SilcServerQuery query) return; } + /* Check server name. If we are open server and don't yet have + connection to remote router, create it now. */ + if (query->nick_server[0] && server->config->open_server && + !query->resolved) { + /* If primary router is specified, use that. Otherwise connect + to the server in nick@server string. */ + SilcServerConfigRouter *router; + + router = silc_server_config_get_primary_router(server); + if (router && server->standalone) { + /* Create connection to primary router */ + SILC_LOG_DEBUG(("Create dynamic connection to primary router %s:%d", + router->host, router->port)); + query->dynamic_prim = TRUE; + silc_server_create_connection(server, FALSE, TRUE, + router->host, router->port, + silc_server_query_connected, query); + return; + } else if (!silc_server_num_sockets_by_remote(server, + query->nick_server, + query->nick_server, + 706)) { + /* Create connection and handle the query after connection */ + SILC_LOG_DEBUG(("Create dynamic connection to %s:%d", + query->nick_server, 706)); + silc_server_create_connection(server, FALSE, TRUE, + query->nick_server, 706, + silc_server_query_connected, query); + return; + } + } + } else { /* Parse the IDs included in the query */ query->ids = silc_calloc(argc, sizeof(*query->ids)); @@ -827,23 +954,29 @@ void silc_server_query_process(SilcServer server, SilcServerQuery query, if (query->nickname[0]) { /* Get all clients matching nickname from local list */ if (!silc_idlist_get_clients_by_hash(server->local_list, - query->nickname, server->md5hash, + query->nickname, + query->nick_server[0] ? + query->nick_server : NULL, + server->md5hash, &clients, &clients_count)) silc_idlist_get_clients_by_nickname(server->local_list, query->nickname, - query->nick_server, - /* XXX nick_server may not be set */ + query->nick_server[0] ? + query->nick_server : NULL, &clients, &clients_count); /* Check global list as well */ if (check_global) { if (!silc_idlist_get_clients_by_hash(server->global_list, - query->nickname, server->md5hash, + query->nickname, + query->nick_server[0] ? + query->nick_server : NULL, + server->md5hash, &clients, &clients_count)) silc_idlist_get_clients_by_nickname(server->global_list, query->nickname, - query->nick_server, - /* XXX nick_server may not be set */ + query->nick_server[0] ? + query->nick_server : NULL, &clients, &clients_count); } diff --git a/apps/silcd/server_query.h b/apps/silcd/server_query.h index 53a11ee2..a9a0e02c 100644 --- a/apps/silcd/server_query.h +++ b/apps/silcd/server_query.h @@ -27,7 +27,8 @@ to the entity who sent this query to us automatically. Returns TRUE if the query is being processed or FALSE on error. */ SilcBool silc_server_query_command(SilcServer server, SilcCommand querycmd, - SilcServerCommandContext cmd); + SilcServerCommandContext cmd, + void *old_query); /* Find client by the Client ID indicated by the `client_id', and if not found then query it by using WHOIS command. The client information diff --git a/apps/silcd/server_util.c b/apps/silcd/server_util.c index 3086aef4..545ec504 100644 --- a/apps/silcd/server_util.c +++ b/apps/silcd/server_util.c @@ -162,15 +162,17 @@ SilcBool silc_server_remove_clients_by_server(SilcServer server, if (server_signoff) { idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER); - argv = silc_realloc(argv, sizeof(*argv) * (argc + 1)); - argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1)); - argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1)); - argv[argc] = silc_calloc(silc_buffer_len(idp), sizeof(*argv[0])); - memcpy(argv[argc], idp->data, silc_buffer_len(idp)); - argv_lens[argc] = silc_buffer_len(idp); - argv_types[argc] = argc + 1; - argc++; - silc_buffer_free(idp); + if (idp) { + argv = silc_realloc(argv, sizeof(*argv) * (argc + 1)); + argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * (argc + 1)); + argv_types = silc_realloc(argv_types, sizeof(*argv_types) * (argc + 1)); + argv[argc] = silc_calloc(silc_buffer_len(idp), sizeof(*argv[0])); + memcpy(argv[argc], idp->data, silc_buffer_len(idp)); + argv_lens[argc] = silc_buffer_len(idp); + argv_types[argc] = argc + 1; + argc++; + silc_buffer_free(idp); + } } if (silc_idcache_get_all(server->local_list->clients, &list)) { @@ -189,17 +191,19 @@ SilcBool silc_server_remove_clients_by_server(SilcServer server, if (server_signoff) { idp = silc_id_payload_encode(client->id, SILC_ID_CLIENT); - argv = silc_realloc(argv, sizeof(*argv) * (argc + 1)); - argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * - (argc + 1)); - argv_types = silc_realloc(argv_types, sizeof(*argv_types) * - (argc + 1)); - argv[argc] = silc_calloc(silc_buffer_len(idp), sizeof(*argv[0])); - memcpy(argv[argc], idp->data, silc_buffer_len(idp)); - argv_lens[argc] = silc_buffer_len(idp); - argv_types[argc] = argc + 1; - argc++; - silc_buffer_free(idp); + if (idp) { + argv = silc_realloc(argv, sizeof(*argv) * (argc + 1)); + argv_lens = silc_realloc(argv_lens, sizeof(*argv_lens) * + (argc + 1)); + argv_types = silc_realloc(argv_types, sizeof(*argv_types) * + (argc + 1)); + argv[argc] = silc_calloc(silc_buffer_len(idp), sizeof(*argv[0])); + memcpy(argv[argc], idp->data, silc_buffer_len(idp)); + argv_lens[argc] = silc_buffer_len(idp); + argv_types[argc] = argc + 1; + argc++; + silc_buffer_free(idp); + } } /* Update statistics */ diff --git a/apps/silcd/serverconfig.c b/apps/silcd/serverconfig.c index 7d00e4de..d2b67d2b 100644 --- a/apps/silcd/serverconfig.c +++ b/apps/silcd/serverconfig.c @@ -338,6 +338,12 @@ SILC_CONFIG_CALLBACK(fetch_generic) } config->httpd_port = (SilcUInt16)port; } + else if (!strcmp(name, "open_server")) { + config->open_server = *(SilcBool *)val; + } + else if (!strcmp(name, "local_channels")) { + config->local_channels = *(SilcBool *)val; + } else return SILC_CONFIG_EINTERNAL; @@ -582,6 +588,11 @@ SILC_CONFIG_CALLBACK(fetch_serverinfo) CONFIG_IS_DOUBLE(tmp->server_ip); tmp->server_ip = strdup((char *) val); } + else if (!strcmp(name, "public_ip")) { + SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigServerInfoInterface); + CONFIG_IS_DOUBLE(tmp->public_ip); + tmp->public_ip = strdup((char *) val); + } else if (!strcmp(name, "port")) { int port = *(int *)val; SILC_SERVER_CONFIG_ALLOCTMP(SilcServerConfigServerInfoInterface); @@ -1157,6 +1168,9 @@ SILC_CONFIG_CALLBACK(fetch_router) else if (!strcmp(name, "backuplocal")) { tmp->backup_local = *(SilcBool *)val; } + else if (!strcmp(name, "dynamic_connection")) { + tmp->dynamic_connection = *(SilcBool *)val; + } else return SILC_CONFIG_EINTERNAL; @@ -1203,6 +1217,8 @@ static const SilcConfigTable table_general[] = { { "http_server", SILC_CONFIG_ARG_TOGGLE, fetch_generic, NULL }, { "http_server_ip", SILC_CONFIG_ARG_STRE, fetch_generic, NULL }, { "http_server_port", SILC_CONFIG_ARG_INT, fetch_generic, NULL }, + { "open_server", SILC_CONFIG_ARG_TOGGLE, fetch_generic, NULL }, + { "local_channels", SILC_CONFIG_ARG_TOGGLE, fetch_generic, NULL }, { 0, 0, 0, 0 } }; @@ -1236,6 +1252,7 @@ static const SilcConfigTable table_pkcs[] = { static const SilcConfigTable table_serverinfo_c[] = { { "ip", SILC_CONFIG_ARG_STR, fetch_serverinfo, NULL}, + { "public_ip", SILC_CONFIG_ARG_STR, fetch_serverinfo, NULL}, { "port", SILC_CONFIG_ARG_INT, fetch_serverinfo, NULL}, { 0, 0, 0, 0 } }; @@ -1344,6 +1361,7 @@ static const SilcConfigTable table_routerconn[] = { { "backuphost", SILC_CONFIG_ARG_STRE, fetch_router, NULL }, { "backupport", SILC_CONFIG_ARG_INT, fetch_router, NULL }, { "backuplocal", SILC_CONFIG_ARG_TOGGLE, fetch_router, NULL }, + { "dynamic_connection", SILC_CONFIG_ARG_TOGGLE, fetch_router, NULL }, { 0, 0, 0, 0 } }; diff --git a/apps/silcd/serverconfig.h b/apps/silcd/serverconfig.h index 7ef873e0..b5e91f65 100644 --- a/apps/silcd/serverconfig.h +++ b/apps/silcd/serverconfig.h @@ -50,6 +50,7 @@ typedef struct SilcServerConfigPkcsStruct { typedef struct SilcServerConfigServerInfoInterfaceStruct { char *server_ip; + char *public_ip; SilcUInt16 port; struct SilcServerConfigServerInfoInterfaceStruct *next; } SilcServerConfigServerInfoInterface; @@ -149,6 +150,7 @@ typedef struct SilcServerConfigRouterStruct { SilcServerConfigConnParams *param; SilcBool initiator; SilcBool backup_router; + SilcBool dynamic_connection; char *backup_replace_ip; SilcUInt16 backup_replace_port; SilcBool backup_local; @@ -180,6 +182,8 @@ typedef struct { SilcBool httpd; char *httpd_ip; SilcUInt16 httpd_port; + SilcBool open_server; + SilcBool local_channels; /* Other configuration sections */ SilcServerConfigCipher *cipher; -- 2.24.0