+Sun Sep 9 15:49:16 EEST 2001 Pekka Riikonen <priikone@silcnet.org>
+
+ * Added check to the silc_server_new_id_real to not accept
+ new ID if it is the sender's own ID. Affected file is
+ silcd/packet_receive.c.
+
+ * Assure that we do not announce ourself or the one we've
+ sending our announcements when we're router and are announcing
+ servers to our primary router. Affected file silcd/server.c.
+
+ * Fixed silc_server_command_identify_check_client to assemble
+ correct WHOIS packet. It send corrupted WHOIS packet and
+ caused problem with router to router connections. Affected
+ file silcd/command.c.
+
+ Fixed also silc_server_command_whois_check the same way
+ as for the IDENTIFY command.
+
+ * Added new SilcIDListStatus to the server in the SilcIDListData
+ structure. The status now includes the current status of
+ the entry (like registered, resolved etc.). Affected file
+ silcd/idlist.[ch]. Defined a bunch of different status types
+ as well. This replaced the old boolean registered field as well.
+
+ Added resolve_cmd_ident field to the SilcClientEntry structure
+ too so that if the entry is for example being resolved so
+ another command may attach to the same pending command reply
+ without requiring to resolve the same entry again. This concept
+ should optimize the WHOIS and the IDENTIFY resolving under
+ heavy load by taking away unnecessary resolving for entries
+ that are being resolved already.
+
+ Added support for adding multiple pending commands for one
+ command idenfier. Affected file silcd/command[_reply].[ch].
+
+ * Fixed WHOIS and IDENTIFY save to remove the cache entry
+ before deleting the data. Otherwise the hash table will have
+ freed data in comparison functions. Affected file is
+ silcd/command_reply.c.
+
+ * Fixed silc_idlist_replace_client_id to add the new entry to
+ the cache with NULL nickname. Otherwise there will be invalid
+ memory as the nickname after the nickname is freed. Affected
+ file silcd/packet_receive.c.
+
+ * Fixed the silc_idlist_get_clients_by_hash. The entries was
+ saved into wrong slots because the previous number of entries
+ was not taken into account. Affected file silcd/idlist.c.
+ Fixed same thing in silc_idlist_get_clients_by_nickname too.
+
+ * If we are router and we receive JOIN notify to a channel that
+ does not have any users then notified client is marked as the
+ channel founder, as it is it. The affected file is
+ silcd/packet_receive.c
+
+ * Added to the extended hash table API's table_del_*ext functions
+ the destructor as argument too, so that the caller can decide
+ which destructor to use or whether to use destructor at all.
+ Affected file lib/silcutil/silchashtable.[ch].
+
+ * Fixed ID Cache purging. It actually deleted the entries from
+ the hash table after the data was freed. The hash table ended
+ up comparing freed memory. The affected file is
+ lib/silccore/silcidcache.c.
+
Sat Sep 8 10:22:10 EEST 2001 Pekka Riikonen <priikone@silcnet.org>
* Fixed Irssi SILC client's KILL command's HELP syntax.
TODO/bugs In SILC Client Library
================================
+ o Someone changing nickname on the channel from nick to Nick will cause
+ that the new nick becomes Nick@host, because the old nick is not
+ removed from the cache before adding the new one. This is because
+ of the way NICK_CHANGE notify is handled. This should be fixed so
+ that if the old nick is to be removed the new nick will *replace*
+ the old one.
+
o JOIN command's argument handling is buggy. See the XXX in the code.
SilcCommand command)
{
SilcIDListData idata = (SilcIDListData)sock->user_data;
- if (idata->registered)
+ if (idata->status & SILC_IDLIST_STATUS_REGISTERED)
return TRUE;
silc_server_command_send_status_reply(cmd, command,
/* Add new pending command to be executed when reply to a command has been
received. The `reply_cmd' is the command that will call the `callback'
- with `context' when reply has been received. If `ident' is non-zero
+ with `context' when reply has been received. It can be SILC_COMMAND_NONE
+ to match any command with the `ident'. If `ident' is non-zero
the `callback' will be executed when received reply with command
identifier `ident'. */
/* Checks for pending commands and marks callbacks to be called from
the command reply function. Returns TRUE if there were pending command. */
-int silc_server_command_pending_check(SilcServer server,
- SilcServerCommandReplyContext ctx,
- SilcCommand command,
- uint16 ident)
+SilcServerCommandPendingCallbacks
+silc_server_command_pending_check(SilcServer server,
+ SilcServerCommandReplyContext ctx,
+ SilcCommand command,
+ uint16 ident,
+ uint32 *callbacks_count)
{
SilcServerCommandPending *r;
+ SilcServerCommandPendingCallbacks callbacks = NULL;
+ int i = 0;
silc_dlist_start(server->pending_commands);
while ((r = silc_dlist_get(server->pending_commands)) != SILC_LIST_END) {
- if (r->reply_cmd == command && r->ident == ident) {
- ctx->context = r->context;
- ctx->callback = r->callback;
- ctx->destructor = r->destructor;
+ if ((r->reply_cmd == command || r->reply_cmd == SILC_COMMAND_NONE)
+ && r->ident == ident) {
+ callbacks = silc_realloc(callbacks, sizeof(*callbacks) * (i + 1));
+ callbacks[i].context = r->context;
+ callbacks[i].callback = r->callback;
+ callbacks[i].destructor = r->destructor;
ctx->ident = ident;
- return TRUE;
+ i++;
}
}
- return FALSE;
+ *callbacks_count = i;
+ return callbacks;
}
/* Destructor function for pending callbacks. This is called when using
/* No ID, get the nickname@server string and parse it. */
tmp = silc_argument_get_arg_type(cmd->args, 1, NULL);
if (tmp) {
- if (strchr(tmp, '@')) {
- len = strcspn(tmp, "@");
- *nickname = silc_calloc(len + 1, sizeof(char));
- memcpy(*nickname, tmp, len);
- *server_name = silc_calloc(strlen(tmp) - len, sizeof(char));
- memcpy(*server_name, tmp + len + 1, strlen(tmp) - len - 1);
- } else {
- *nickname = strdup(tmp);
- }
+ silc_parse_userfqdn(tmp, nickname, server_name);
} else {
silc_server_command_send_status_reply(cmd, command,
SILC_STATUS_ERR_NOT_ENOUGH_PARAMS);
return TRUE;
}
+/* Resolve context used by both WHOIS and IDENTIFY commands */
+typedef struct {
+ SilcServerEntry router;
+ uint16 ident;
+ unsigned char **res_argv;
+ uint32 *res_argv_lens;
+ uint32 *res_argv_types;
+ uint32 res_argc;
+} *SilcServerResolveContext;
+
static char
silc_server_command_whois_check(SilcServerCommandContext cmd,
SilcClientEntry *clients,
uint32 clients_count)
{
SilcServer server = cmd->server;
- int i;
SilcClientEntry entry;
+ SilcServerResolveContext resolve = NULL, r = NULL;
+ uint32 resolve_count = 0;
+ int i, k;
for (i = 0; i < clients_count; i++) {
entry = clients[i];
- if (!entry || entry->data.registered == FALSE)
+ if (!entry || (entry->nickname && entry->username && entry->userinfo) ||
+ !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED) ||
+ !entry->router)
continue;
- if (!entry->nickname || !entry->username || !entry->userinfo) {
- SilcBuffer tmpbuf;
- uint16 old_ident;
-
- if (!entry->router)
- continue;
-
- old_ident = silc_command_get_ident(cmd->payload);
- silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
- tmpbuf = silc_command_payload_encode_payload(cmd->payload);
+ /* We need to resolve this entry since it is not complete */
- /* Send WHOIS command */
- silc_server_packet_send(server, entry->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
-
- /* Reprocess this packet after received reply */
- silc_server_command_pending(server, SILC_COMMAND_WHOIS,
- silc_command_get_ident(cmd->payload),
+ if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
+ /* The entry is being resolved (and we are not the resolver) so attach
+ to the command reply and we're done with this one. */
+ silc_server_command_pending(server, SILC_COMMAND_NONE,
+ entry->resolve_cmd_ident,
silc_server_command_destructor,
- silc_server_command_whois,
+ silc_server_command_whois,
silc_server_command_dup(cmd));
- cmd->pending = TRUE;
-
- silc_command_set_ident(cmd->payload, old_ident);
+ } else {
+ if (entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
+ /* We've resolved this and it still is not ready. We'll return
+ and are that this will be handled again after it is resolved. */
+ for (i = 0; i < resolve_count; i++) {
+ for (k = 0; k < r->res_argc; k++)
+ silc_free(r->res_argv[k]);
+ silc_free(r->res_argv);
+ silc_free(r->res_argv_lens);
+ silc_free(r->res_argv_types);
+ }
+ silc_free(resolve);
+ return FALSE;
+ } else {
+ /* We'll resolve this client */
+ SilcBuffer idp;
+
+ r = NULL;
+ for (k = 0; k < resolve_count; k++) {
+ if (resolve[k].router == entry->router) {
+ r = &resolve[k];
+ break;
+ }
+ }
- silc_buffer_free(tmpbuf);
- return FALSE;
+ if (!r) {
+ resolve = silc_realloc(resolve, sizeof(*resolve) *
+ (resolve_count + 1));
+ r = &resolve[resolve_count];
+ memset(r, 0, sizeof(*r));
+ r->router = entry->router;
+ r->ident = ++server->cmd_ident;
+ resolve_count++;
+ }
+
+ r->res_argv = silc_realloc(r->res_argv, sizeof(*r->res_argv) *
+ (r->res_argc + 1));
+ r->res_argv_lens = silc_realloc(r->res_argv_lens,
+ sizeof(*r->res_argv_lens) *
+ (r->res_argc + 1));
+ r->res_argv_types = silc_realloc(r->res_argv_types,
+ sizeof(*r->res_argv_types) *
+ (r->res_argc + 1));
+ idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+ r->res_argv[r->res_argc] = silc_calloc(idp->len,
+ sizeof(**r->res_argv));
+ memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
+ r->res_argv_lens[r->res_argc] = idp->len;
+ r->res_argv_types[r->res_argc] = r->res_argc + 3;
+ r->res_argc++;
+ silc_buffer_free(idp);
+
+ entry->resolve_cmd_ident = r->ident;
+ entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+ entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
+ }
}
}
+ /* Do the resolving */
+ for (i = 0; i < resolve_count; i++) {
+ SilcBuffer res_cmd;
+
+ r = &resolve[i];
+
+ /* Send WHOIS request. We send WHOIS since we're doing the requesting
+ now anyway so make it a good one. */
+ res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
+ r->res_argc, r->res_argv,
+ r->res_argv_lens,
+ r->res_argv_types,
+ r->ident);
+ silc_server_packet_send(server, r->router->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ res_cmd->data, res_cmd->len, TRUE);
+
+ /* Reprocess this packet after received reply */
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ r->ident,
+ silc_server_command_destructor,
+ silc_server_command_whois,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+
+ silc_buffer_free(res_cmd);
+ for (k = 0; k < r->res_argc; k++)
+ silc_free(r->res_argv[k]);
+ silc_free(r->res_argv);
+ silc_free(r->res_argv_lens);
+ silc_free(r->res_argv_types);
+ }
+ silc_free(resolve);
+
+ if (resolve_count)
+ return FALSE;
+
return TRUE;
}
len = 0;
for (i = 0; i < clients_count; i++)
- if (clients[i]->data.registered)
+ if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
len++;
status = SILC_STATUS_OK;
for (i = 0, k = 0; i < clients_count; i++) {
entry = clients[i];
- if (entry->data.registered == FALSE) {
+ if (!(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
if (clients_count == 1) {
if (entry->nickname) {
silc_server_command_send_status_data(cmd, SILC_COMMAND_WHOIS,
}
/* Get the nickname@server string and parse it. */
- if (strchr(tmp, '@')) {
- len = strcspn(tmp, "@");
- *nickname = silc_calloc(len + 1, sizeof(char));
- memcpy(*nickname, tmp, len);
- *server_name = silc_calloc(strlen(tmp) - len, sizeof(char));
- memcpy(*server_name, tmp + len + 1, strlen(tmp) - len - 1);
- } else {
- *nickname = strdup(tmp);
- }
+ silc_parse_userfqdn(tmp, nickname, server_name);
+
/* Get the max count of reply messages allowed */
tmp = silc_argument_get_arg_type(cmd->args, 2, NULL);
if (tmp)
/* We will take only clients that are not valid anymore. They are the
ones that are not registered anymore but still have a ID. They
have disconnected us, and thus valid for WHOWAS. */
- if (entry->data.registered == TRUE)
+ if (entry->data.status & SILC_IDLIST_STATUS_REGISTERED)
continue;
if (entry->id == NULL)
continue;
silc_server_command_whowas_send_reply(cmd, clients, clients_count);
out:
- if (clients)
- silc_free(clients);
- if (nick)
- silc_free(nick);
- if (server_name)
- silc_free(server_name);
+ silc_free(clients);
+ silc_free(nick);
+ silc_free(server_name);
return ret;
}
char *nick = NULL;
char *nick_server = NULL;
- if (strchr(tmp, '@')) {
- len = strcspn(tmp, "@");
- nick = silc_calloc(len + 1, sizeof(char));
- memcpy(nick, tmp, len);
- nick_server = silc_calloc(strlen(tmp) - len, sizeof(char));
- memcpy(nick_server, tmp + len + 1, strlen(tmp) - len - 1);
- } else {
- nick = strdup(tmp);
- }
+ silc_parse_userfqdn(tmp, &nick, &nick_server);
if (!silc_idlist_get_clients_by_hash(server->local_list,
nick, server->md5hash,
uint32 clients_count)
{
SilcServer server = cmd->server;
- int i;
SilcClientEntry entry;
+ SilcServerResolveContext resolve = NULL, r = NULL;
+ uint32 resolve_count = 0;
+ int i, k;
for (i = 0; i < clients_count; i++) {
entry = clients[i];
- if (!entry || entry->data.registered == FALSE)
+ if (!entry || entry->nickname ||
+ !(entry->data.status & SILC_IDLIST_STATUS_REGISTERED) ||
+ !entry->router)
continue;
- if (!entry->nickname) {
- SilcBuffer tmpbuf;
- uint16 old_ident;
-
- if (!entry->router)
- continue;
-
- old_ident = silc_command_get_ident(cmd->payload);
- silc_command_set_ident(cmd->payload, silc_rng_get_rn16(server->rng));
- silc_command_set_command(cmd->payload, SILC_COMMAND_WHOIS);
- tmpbuf = silc_command_payload_encode_payload(cmd->payload);
-
- /* Send WHOIS request. We send WHOIS since we're doing the requesting
- now anyway so make it a good one. */
- silc_server_packet_send(server, entry->router->connection,
- SILC_PACKET_COMMAND, cmd->packet->flags,
- tmpbuf->data, tmpbuf->len, TRUE);
-
- /* Reprocess this packet after received reply */
- silc_server_command_pending(server, SILC_COMMAND_WHOIS,
- silc_command_get_ident(cmd->payload),
+ /* We need to resolve this entry since it is not complete */
+
+ if (!cmd->pending && entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
+ /* The entry is being resolved (and we are not the resolver) so attach
+ to the command reply and we're done with this one. */
+ silc_server_command_pending(server, SILC_COMMAND_NONE,
+ entry->resolve_cmd_ident,
silc_server_command_destructor,
silc_server_command_identify,
silc_server_command_dup(cmd));
+ } else {
+ if (entry->data.status & SILC_IDLIST_STATUS_RESOLVING) {
+ /* We've resolved this and it still is not ready. We'll return
+ and are that this will be handled again after it is resolved. */
+ for (i = 0; i < resolve_count; i++) {
+ for (k = 0; k < r->res_argc; k++)
+ silc_free(r->res_argv[k]);
+ silc_free(r->res_argv);
+ silc_free(r->res_argv_lens);
+ silc_free(r->res_argv_types);
+ }
+ silc_free(resolve);
+ return FALSE;
+ } else {
+ /* We'll resolve this client */
+ SilcBuffer idp;
+
+ r = NULL;
+ for (k = 0; k < resolve_count; k++) {
+ if (resolve[k].router == entry->router) {
+ r = &resolve[k];
+ break;
+ }
+ }
- cmd->pending = TRUE;
-
- /* Put old data back to the Command Payload we just changed */
- silc_command_set_ident(cmd->payload, old_ident);
- silc_command_set_command(cmd->payload, SILC_COMMAND_IDENTIFY);
+ if (!r) {
+ resolve = silc_realloc(resolve, sizeof(*resolve) *
+ (resolve_count + 1));
+ r = &resolve[resolve_count];
+ memset(r, 0, sizeof(*r));
+ r->router = entry->router;
+ r->ident = ++server->cmd_ident;
+ resolve_count++;
+ }
- silc_buffer_free(tmpbuf);
- return FALSE;
+ r->res_argv = silc_realloc(r->res_argv, sizeof(*r->res_argv) *
+ (r->res_argc + 1));
+ r->res_argv_lens = silc_realloc(r->res_argv_lens,
+ sizeof(*r->res_argv_lens) *
+ (r->res_argc + 1));
+ r->res_argv_types = silc_realloc(r->res_argv_types,
+ sizeof(*r->res_argv_types) *
+ (r->res_argc + 1));
+ idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
+ r->res_argv[r->res_argc] = silc_calloc(idp->len,
+ sizeof(**r->res_argv));
+ memcpy(r->res_argv[r->res_argc], idp->data, idp->len);
+ r->res_argv_lens[r->res_argc] = idp->len;
+ r->res_argv_types[r->res_argc] = r->res_argc + 3;
+ r->res_argc++;
+ silc_buffer_free(idp);
+
+ entry->resolve_cmd_ident = r->ident;
+ entry->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+ entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
+ }
}
}
+ /* Do the resolving */
+ for (i = 0; i < resolve_count; i++) {
+ SilcBuffer res_cmd;
+
+ r = &resolve[i];
+
+ /* Send WHOIS request. We send WHOIS since we're doing the requesting
+ now anyway so make it a good one. */
+ res_cmd = silc_command_payload_encode(SILC_COMMAND_WHOIS,
+ r->res_argc, r->res_argv,
+ r->res_argv_lens,
+ r->res_argv_types,
+ r->ident);
+ silc_server_packet_send(server, r->router->connection,
+ SILC_PACKET_COMMAND, cmd->packet->flags,
+ res_cmd->data, res_cmd->len, TRUE);
+
+ /* Reprocess this packet after received reply */
+ silc_server_command_pending(server, SILC_COMMAND_WHOIS,
+ r->ident,
+ silc_server_command_destructor,
+ silc_server_command_identify,
+ silc_server_command_dup(cmd));
+ cmd->pending = TRUE;
+
+ silc_buffer_free(res_cmd);
+ for (k = 0; k < r->res_argc; k++)
+ silc_free(r->res_argv[k]);
+ silc_free(r->res_argv);
+ silc_free(r->res_argv_lens);
+ silc_free(r->res_argv_types);
+ }
+ silc_free(resolve);
+
+ if (resolve_count)
+ return FALSE;
+
return TRUE;
}
len = 0;
for (i = 0; i < clients_count; i++)
- if (clients[i]->data.registered)
+ if (clients[i]->data.status & SILC_IDLIST_STATUS_REGISTERED)
len++;
if (len > 1)
for (i = 0, k = 0; i < clients_count; i++) {
entry = clients[i];
- if (entry->data.registered == FALSE) {
+ if (!(entry->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
if (clients_count == 1) {
SilcBuffer idp = silc_id_payload_encode(entry->id, SILC_ID_CLIENT);
silc_server_command_send_status_data(cmd, SILC_COMMAND_IDENTIFY,
/* Executed pending command. The first argument to the callback function
is the user specified context. The second argument is always the
SilcServerCommandReply context. */
-#define SILC_SERVER_PENDING_EXEC(ctx, cmd) \
-do { \
- if (ctx->callback) \
- (*ctx->callback)(ctx->context, ctx); \
+#define SILC_SERVER_PENDING_EXEC(ctx, cmd) \
+do { \
+ int _i; \
+ for (_i = 0; _i < ctx->callbacks_count; _i++) \
+ if (ctx->callbacks[_i].callback) \
+ (*ctx->callbacks[_i].callback)(ctx->callbacks[_i].context, ctx); \
} while(0)
/* Execute destructor for pending command */
-#define SILC_SERVER_PENDING_DESTRUCTOR(ctx, cmd) \
-do { \
- silc_server_command_pending_del(ctx->server, cmd, ctx->ident); \
- if (ctx->destructor) \
- (*ctx->destructor)(ctx->context); \
+#define SILC_SERVER_PENDING_DESTRUCTOR(ctx, cmd) \
+do { \
+ int _i; \
+ silc_server_command_pending_del(ctx->server, cmd, ctx->ident); \
+ for (_i = 0; _i < ctx->callbacks_count; _i++) \
+ if (ctx->callbacks[_i].destructor) \
+ (*ctx->callbacks[_i].destructor)(ctx->callbacks[_i].context); \
} while(0)
/* Prototypes */
void silc_server_command_pending_del(SilcServer server,
SilcCommand reply_cmd,
uint16 ident);
-int silc_server_command_pending_check(SilcServer server,
- SilcServerCommandReplyContext ctx,
- SilcCommand command,
- uint16 ident);
+SilcServerCommandPendingCallbacks
+silc_server_command_pending_check(SilcServer server,
+ SilcServerCommandReplyContext ctx,
+ SilcCommand command,
+ uint16 ident,
+ uint32 *callbacks_count);
SILC_SERVER_CMD_FUNC(whois);
SILC_SERVER_CMD_FUNC(whowas);
SILC_SERVER_CMD_FUNC(identify);
ident = silc_command_get_ident(ctx->payload);
/* Check for pending commands and mark to be exeucted */
- silc_server_command_pending_check(server, ctx,
- silc_command_get(ctx->payload), ident);
+ ctx->callbacks =
+ silc_server_command_pending_check(server, ctx,
+ silc_command_get(ctx->payload),
+ ident, &ctx->callbacks_count);
/* Execute command reply */
command = silc_command_get(ctx->payload);
silc_command_payload_free(cmd->payload);
if (cmd->sock)
silc_socket_free(cmd->sock); /* Decrease the reference counter */
+ silc_free(cmd->callbacks);
silc_free(cmd);
}
}
return FALSE;
/* Take hostname out of nick string if it includes it. */
- if (strchr(nickname, '@')) {
- int len = strcspn(nickname, "@");
- nick = silc_calloc(len + 1, sizeof(char));
- servername = silc_calloc((strlen(nickname) - len) + 1, sizeof(char));
- memcpy(nick, nickname, len);
- memcpy(servername, nickname + len + 1, strlen(nickname) - len);
- } else {
- nick = strdup(nickname);
- }
+ silc_parse_userfqdn(nickname, &nick, &servername);
/* We don't have that client anywhere, add it. The client is added
to global list since server didn't have it in the lists so it must be
return FALSE;
}
- client->data.registered = TRUE;
+ client->data.status |=
+ (SILC_IDLIST_STATUS_REGISTERED | SILC_IDLIST_STATUS_RESOLVED);
+ client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
client->mode = mode;
client->servername = servername;
} else {
SILC_LOG_DEBUG(("Updating client data"));
/* Take hostname out of nick string if it includes it. */
- if (strchr(nickname, '@')) {
- int len = strcspn(nickname, "@");
- nick = silc_calloc(len + 1, sizeof(char));
- servername = silc_calloc((strlen(nickname) - len) + 1, sizeof(char));
- memcpy(nick, nickname, len);
- memcpy(servername, nickname + len + 1, strlen(nickname) - len);
- } else {
- nick = strdup(nickname);
- }
+ silc_parse_userfqdn(nickname, &nick, &servername);
- if (client->nickname)
- silc_free(client->nickname);
- if (client->username)
- silc_free(client->username);
- if (client->userinfo)
- silc_free(client->userinfo);
+ /* Remove the old cache entry */
+ silc_idcache_del_by_context(global ? server->global_list->clients :
+ server->local_list->clients, client);
+
+ silc_free(client->nickname);
+ silc_free(client->username);
+ silc_free(client->userinfo);
+ silc_free(client->servername);
client->nickname = nick;
client->username = strdup(username);
client->userinfo = strdup(realname);
- client->mode = mode;
client->servername = servername;
+ client->mode = mode;
+ client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
+ client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
- /* Remove the old cache entry and create a new one */
- silc_idcache_del_by_context(global ? server->global_list->clients :
- server->local_list->clients, client);
+ /* Create new cache entry */
silc_idcache_add(global ? server->global_list->clients :
server->local_list->clients, nick, client->id,
client, FALSE);
return FALSE;
/* Take hostname out of nick string if it includes it. */
- if (strchr(nickname, '@')) {
- int len = strcspn(nickname, "@");
- nick = silc_calloc(len + 1, sizeof(char));
- servername = silc_calloc((strlen(nickname) - len) + 1, sizeof(char));
- memcpy(nick, nickname, len);
- memcpy(servername, nickname + len + 1, strlen(nickname) - len);
- } else {
- nick = strdup(nickname);
- }
+ silc_parse_userfqdn(nickname, &nick, &servername);
/* We don't have that client anywhere, add it. The client is added
to global list since server didn't have it in the lists so it must be
return FALSE;
}
- client->data.registered = FALSE;
+ client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
client = silc_idlist_find_client_by_id(server->global_list,
client_id, TRUE, &cache);
cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
/* We have the client already, update the data */
/* Take hostname out of nick string if it includes it. */
- if (strchr(nickname, '@')) {
- int len = strcspn(nickname, "@");
- nick = silc_calloc(len + 1, sizeof(char));
- servername = silc_calloc((strlen(nickname) - len) + 1, sizeof(char));
- memcpy(nick, nickname, len);
- memcpy(servername, nickname + len + 1, strlen(nickname) - len);
- } else {
- nick = strdup(nickname);
- }
+ silc_parse_userfqdn(nickname, &nick, &servername);
- if (client->nickname)
- silc_free(client->nickname);
- if (client->username)
- silc_free(client->username);
+ silc_free(client->nickname);
+ silc_free(client->username);
client->nickname = nick;
client->username = strdup(username);
be bogus client or some router in the net is buggy. */
if (server->server_type == SILC_ROUTER)
goto error;
-
- /* Take hostname out of nick string if it includes it. */
- if (name) {
- if (strchr(name, '@')) {
- int len = strcspn(name, "@");
- nick = silc_calloc(len + 1, sizeof(char));
- memcpy(nick, name, len);
- } else {
- nick = strdup(name);
- }
- }
+
+ /* Take nickname */
+ if (name)
+ silc_parse_userfqdn(name, &nick, NULL);
/* We don't have that client anywhere, add it. The client is added
to global list since server didn't have it in the lists so it must be
SILC_LOG_ERROR(("Could not add new client to the ID Cache"));
goto error;
}
- client->data.registered = TRUE;
+ client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+ client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
+ client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
} else {
/* We have the client already, update the data */
SILC_LOG_DEBUG(("Updating client data"));
- /* Take hostname out of nick string if it includes it. */
+ /* Take nickname */
if (name) {
- if (strchr(name, '@')) {
- int len = strcspn(name, "@");
- nick = silc_calloc(len + 1, sizeof(char));
- memcpy(nick, name, len);
- } else {
- nick = strdup(name);
- }
- }
-
- if (name && client->nickname)
+ silc_parse_userfqdn(name, &nick, NULL);
+
+ /* Remove the old cache entry */
+ silc_idcache_del_by_context(global ? server->global_list->clients :
+ server->local_list->clients, client);
+
silc_free(client->nickname);
-
- if (nick)
client->nickname = nick;
+ }
- if (info && client->username) {
+ if (info) {
silc_free(client->username);
client->username = strdup(info);
}
+
+ client->data.status |= SILC_IDLIST_STATUS_RESOLVED;
+ client->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
- /* Remove the old cache entry and create a new one */
if (name) {
- silc_idcache_del_by_context(global ? server->global_list->clients :
- server->local_list->clients, client);
+ /* Add new cache entry */
silc_idcache_add(global ? server->global_list->clients :
server->local_list->clients, nick, client->id,
client, FALSE);
silc_free(server_id);
goto error;
}
- server_entry->data.registered = TRUE;
+ server_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
+ server_entry->data.status |= SILC_IDLIST_STATUS_RESOLVED;
+ server_entry->data.status &= ~SILC_IDLIST_STATUS_RESOLVING;
server_id = NULL;
}
silc_free(server_id);
goto out;
}
- entry->data.registered = TRUE;
+ entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
}
}
/* All server command replys */
extern SilcServerCommandReply silc_command_reply_list[];
+/* Context holding pending command callbacks. */
+typedef struct {
+ SilcServerPendingDestructor destructor;
+ SilcCommandCb callback;
+ void *context;
+} *SilcServerCommandPendingCallbacks;
+
/* Context sent as argument to all command reply functions */
typedef struct {
SilcServer server;
SilcArgumentPayload args;
/* If defined this executes the pending command. */
- SilcServerPendingDestructor destructor;
- SilcCommandCb callback;
- void *context;
+ SilcServerCommandPendingCallbacks callbacks;
+ uint32 callbacks_count;
uint16 ident;
} *SilcServerCommandReplyContext;
data->public_key = idata->public_key;
data->last_receive = idata->last_receive;
data->last_sent = idata->last_sent;
- data->registered = idata->registered;
+ data->status = idata->status;
}
/* Free's all data in the common ID entry data structure. */
if (ret_entry)
*ret_entry = id_cache;
- if (server && registered && !server->data.registered)
+ if (server && registered &&
+ !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
return NULL;
SILC_LOG_DEBUG(("Found"));
if (ret_entry)
*ret_entry = id_cache;
- if (server && registered && !server->data.registered)
+ if (server && registered &&
+ !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
return NULL;
SILC_LOG_DEBUG(("Found"));
if (ret_entry)
*ret_entry = id_cache;
- if (server && registered && !server->data.registered)
+ if (server && registered &&
+ !(server->data.status & SILC_IDLIST_STATUS_REGISTERED))
return NULL;
SILC_LOG_DEBUG(("Found"));
{
SilcIDCacheList list = NULL;
SilcIDCacheEntry id_cache = NULL;
- int i;
SILC_LOG_DEBUG(("Start"));
(silc_idcache_list_count(list) + *clients_count) *
sizeof(**clients));
- i = 0;
silc_idcache_list_first(list, &id_cache);
- (*clients)[i++] = (SilcClientEntry)id_cache->context;
+ (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
while (silc_idcache_list_next(list, &id_cache))
- (*clients)[i++] = (SilcClientEntry)id_cache->context;
+ (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
silc_idcache_list_free(list);
- *clients_count += i;
-
- SILC_LOG_DEBUG(("Found %d clients", *clients_count));
+ SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
return TRUE;
}
SilcIDCacheList list = NULL;
SilcIDCacheEntry id_cache = NULL;
unsigned char hash[32];
- int i;
SilcClientID client_id;
SILC_LOG_DEBUG(("Start"));
(silc_idcache_list_count(list) + *clients_count) *
sizeof(**clients));
- i = 0;
silc_idcache_list_first(list, &id_cache);
- (*clients)[i++] = (SilcClientEntry)id_cache->context;
+ (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
while (silc_idcache_list_next(list, &id_cache))
- (*clients)[i++] = (SilcClientEntry)id_cache->context;
+ (*clients)[(*clients_count)++] = (SilcClientEntry)id_cache->context;
silc_idcache_list_free(list);
- *clients_count += i;
-
- SILC_LOG_DEBUG(("Found %d clients", *clients_count));
+ SILC_LOG_DEBUG(("Found total %d clients", *clients_count));
return TRUE;
}
if (ret_entry)
*ret_entry = id_cache;
- if (client && registered && !client->data.registered)
+ if (client && registered &&
+ !(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
return NULL;
SILC_LOG_DEBUG(("Found"));
silc_free(client->id);
client->id = new_id;
- silc_idcache_add(id_list->clients, client->nickname, client->id,
- client, FALSE);
+ silc_idcache_add(id_list->clients, NULL, client->id, client, FALSE);
SILC_LOG_DEBUG(("Replaced"));
void *context;
} *SilcServerRekey;
+/* ID List Entry status type and all the types. */
+typedef uint8 SilcIDListStatus;
+#define SILC_IDLIST_STATUS_NONE 0x00 /* No status */
+#define SILC_IDLIST_STATUS_REGISTERED 0x01 /* Entry is registered */
+#define SILC_IDLIST_STATUS_RESOLVED 0x02 /* Entry info is resolved */
+#define SILC_IDLIST_STATUS_RESOLVING 0x04 /* Entry is being resolved
+ with WHOIS or IDENTIFY */
+
/*
Generic ID list data structure.
This structure is included in all ID list entries and it includes data
pointers that are common to all ID entries. This structure is always
defined to the first field in the ID entries and is used to explicitly
- cast to this type without first explicitly casting to correct ID entry
- type. Hence, the ID list entry is casted to this type to get this data
- from the ID entry (which is usually opaque pointer).
+ type cast to this type without first explicitly casting to correct ID
+ entry type. Hence, the ID list entry is type casted to this type to
+ get this data from the ID entry (which is usually opaque pointer).
Note that some of the fields may be NULL.
/* Public key */
SilcPublicKey public_key;
- long last_receive; /* Time last received data */
- long last_sent; /* Time last sent data */
- bool registered; /* Boolean whether connection is registered */
+ long last_receive; /* Time last received data */
+ long last_sent; /* Time last sent data */
+
+ SilcIDListStatus status; /* Status mask of the entry */
} *SilcIDListData, SilcIDListDataStruct;
/*
but as just said, this is usually pointer to the socket connection
list.
+ uint16 resolve_cmd_ident
+
+ Command identifier for the entry when the entry's data.status
+ is SILC_IDLIST_STATUS_RESOLVING. If this entry is asked to be
+ resolved when the status is set then the resolver may attach to
+ this command identifier and handle the process after the resolving
+ is over.
+
*/
struct SilcClientEntryStruct {
/* Generic data structure. DO NOT add anything before this! */
/* Connection data */
void *connection;
+
+ /* data.status is RESOLVING and this includes the resolving command
+ reply identifier. */
+ uint16 resolve_cmd_ident;
};
/*
goto out;
}
- client->data.registered = TRUE;
+ client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
}
}
/* Do not process the notify if the client is not registered */
- if (client->data.registered == FALSE)
+ if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED))
break;
/* Do not add client to channel if it is there already */
chl = silc_calloc(1, sizeof(*chl));
chl->client = client;
chl->channel = channel;
+
+ /* If this is the first one on the channel then it is the founder off
+ the channel. */
+ if (!silc_hash_table_count(channel->user_list))
+ chl->mode = (SILC_CHANNEL_UMODE_CHANOP | SILC_CHANNEL_UMODE_CHANFO);
+
silc_hash_table_add(channel->user_list, client, chl);
silc_hash_table_add(client->channels, channel, chl);
silc_free(client_id);
/* Remove the client from all channels. */
silc_server_remove_from_channels(server, NULL, client, TRUE, tmp, FALSE);
- client->data.registered = FALSE;
+ client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
cache->expire = SILC_ID_CACHE_EXPIRE_DEF;
server->stat.clients--;
if (server->server_type == SILC_ROUTER)
}
/* Update client entry */
- idata->registered = TRUE;
+ idata->status |= SILC_IDLIST_STATUS_REGISTERED;
client->nickname = nickname;
client->username = username;
client->userinfo = realname ? realname : strdup(" ");
silc_free(id_string);
/* Update server entry */
- idata->registered = TRUE;
+ idata->status |= SILC_IDLIST_STATUS_REGISTERED;
new_server->server_name = server_name;
new_server->id = server_id;
goto out;
}
entry->nickname = NULL;
- entry->data.registered = TRUE;
+ entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
if (sock->type == SILC_SOCKET_TYPE_SERVER)
server->stat.cell_clients++;
SILC_LOG_DEBUG(("Ignoring my own ID as new ID"));
break;
}
+
+ /* If the ID is the sender's ID, ignore it (we have it already) */
+ if (SILC_ID_SERVER_COMPARE(id, router->id)) {
+ SILC_LOG_DEBUG(("Ignoring sender's own ID"));
+ break;
+ }
SILC_LOG_DEBUG(("New server id(%s) from [%s] %s",
silc_id_render(id, SILC_ID_SERVER),
SILC_LOG_ERROR(("Could not add new server to the ID Cache"));
goto out;
}
- entry->data.registered = TRUE;
+ entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
if (sock->type == SILC_SOCKET_TYPE_SERVER)
server->stat.cell_servers++;
SILC_LOG_ERROR(("Could not add ourselves to cache"));
goto err0;
}
- id_entry->data.registered = TRUE;
+ id_entry->data.status |= SILC_IDLIST_STATUS_REGISTERED;
/* Add ourselves also to the socket table. The entry allocated above
is sent as argument for fast referencing in the future. */
server->id_entry->router = id_entry;
server->router = id_entry;
idata = (SilcIDListData)sock->user_data;
- idata->registered = TRUE;
+ idata->status |= SILC_IDLIST_STATUS_REGISTERED;
/* Perform keepalive. The `hb_context' will be freed automatically
when finally calling the silc_socket_free function. XXX hardcoded
silc_server_free_client_data_timeout,
(void *)i, 300, 0,
SILC_TASK_TIMEOUT, SILC_TASK_PRI_LOW);
- client->data.registered = FALSE;
+ client->data.status &= ~SILC_IDLIST_STATUS_REGISTERED;
client->router = NULL;
client->connection = NULL;
if (silc_idcache_list_first(list, &id_cache)) {
while (id_cache) {
client = (SilcClientEntry)id_cache->context;
- if (client->data.registered == FALSE) {
+ if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
if (!silc_idcache_list_next(list, &id_cache))
break;
else
if (silc_idcache_list_first(list, &id_cache)) {
while (id_cache) {
client = (SilcClientEntry)id_cache->context;
- if (client->data.registered == FALSE) {
+ if (!(client->data.status & SILC_IDLIST_STATUS_REGISTERED)) {
if (!silc_idcache_list_next(list, &id_cache))
break;
else
form is dictated by the New ID payload. */
static void silc_server_announce_get_servers(SilcServer server,
+ SilcServerEntry remote,
SilcIDList id_list,
SilcBuffer *servers)
{
while (id_cache) {
entry = (SilcServerEntry)id_cache->context;
+ /* Do not announce the one we've sending our announcments and
+ do not announce ourself. */
+ if (entry == remote || entry == server->id_entry) {
+ if (!silc_idcache_list_next(list, &id_cache))
+ break;
+ continue;
+ }
+
idp = silc_id_payload_encode(entry->id, SILC_ID_SERVER);
*servers = silc_buffer_realloc(*servers,
SILC_LOG_DEBUG(("Announcing servers"));
/* Get servers in local list */
- silc_server_announce_get_servers(server, server->local_list, &servers);
+ silc_server_announce_get_servers(server, server->router,
+ server->local_list, &servers);
/* Get servers in global list */
- silc_server_announce_get_servers(server, server->global_list, &servers);
+ silc_server_announce_get_servers(server, server->router,
+ server->global_list, &servers);
if (servers) {
silc_buffer_push(servers, servers->data - servers->head);
continue;
}
- client->data.registered = TRUE;
+ client->data.status |= SILC_IDLIST_STATUS_REGISTERED;
}
silc_free(client_id);
if (!client || !client->nickname || !client->username) {
SilcBuffer buffer, idp;
+ client->data.status |= SILC_IDLIST_STATUS_RESOLVING;
+ client->data.status &= ~SILC_IDLIST_STATUS_RESOLVED;
+ client->resolve_cmd_ident = ++server->cmd_ident;
+
idp = silc_id_payload_encode(client_id, SILC_ID_CLIENT);
buffer = silc_command_payload_encode_va(SILC_COMMAND_WHOIS,
- ++server->cmd_ident, 1,
+ server->cmd_ident, 1,
3, idp->data, idp->len);
silc_server_packet_send(server, client ? client->router->connection :
server->router->connection,
ret = silc_hash_table_del(cache->context_table, c->context);
if (c->id)
ret = silc_hash_table_del_ext(cache->id_table, c->id, hash,
- hash_context, compare, compare_context);
+ hash_context, compare, compare_context,
+ NULL, NULL);
return ret;
}
return TRUE;
}
+static void silc_idcache_destructor_dummy(void *key, void *context,
+ void *user_context)
+{
+ /* Dummy - nothing */
+}
+
/* Foreach callback fro silc_idcache_purge. */
static void silc_idcache_purge_foreach(void *key, void *context,
SilcIDCacheEntry c = (SilcIDCacheEntry)context;
if (c->expire && c->expire < curtime) {
+ /* Remove the entry from the hash tables */
+ if (c->name)
+ silc_hash_table_del_by_context(cache->name_table, c->name, c);
+ if (c->context)
+ silc_hash_table_del(cache->context_table, c->context);
+ if (c->id)
+ silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
+ NULL, NULL, NULL, NULL,
+ silc_idcache_destructor_dummy, NULL);
+
/* Call the destructor */
if (cache->destructor)
cache->destructor(cache, c);
- /* Delete the entry */
- silc_idcache_del(cache, c);
+ /* Free the entry, it has been deleted from the hash tables */
+ silc_free(c);
}
}
bool silc_idcache_purge_by_context(SilcIDCache cache, void *context)
{
- SilcIDCacheEntry entry;
+ SilcIDCacheEntry c;
bool ret = FALSE;
if (!silc_hash_table_find(cache->context_table, context, NULL,
- (void *)&entry))
+ (void *)&c))
return FALSE;
+ /* Remove the entry from the hash tables */
+ if (c->name)
+ ret = silc_hash_table_del_by_context(cache->name_table, c->name, c);
+ if (c->context)
+ ret = silc_hash_table_del(cache->context_table, c->context);
+ if (c->id)
+ ret =
+ silc_hash_table_del_by_context_ext(cache->id_table, c->id, c,
+ NULL, NULL, NULL, NULL,
+ silc_idcache_destructor_dummy, NULL);
+
/* Call the destructor */
if (cache->destructor)
- cache->destructor(cache, entry);
-
- if (entry->name)
- ret = silc_hash_table_del_by_context(cache->name_table, entry->name,
- entry);
- if (entry->context)
- ret = silc_hash_table_del(cache->context_table, entry->context);
- if (entry->id)
- ret = silc_hash_table_del_by_context(cache->id_table, entry->id, entry);
+ cache->destructor(cache, c);
+
+ /* Free the entry, it has been deleted from the hash tables */
+ silc_free(c);
return ret;
}
silchashtable.c
- Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+ Author: Pekka Riikonen <priikone@silcnet.org>
Copyright (C) 2001 Pekka Riikonen
SilcHashFunction hash,
void *hash_user_context,
SilcHashCompare compare,
- void *compare_user_context)
+ void *compare_user_context,
+ SilcHashDestructor destructor,
+ void *destructor_user_context)
{
SilcHashTableEntry *entry, prev, e;
if (prev && e->next)
prev->next = e->next;
- if (ht->destructor)
- ht->destructor(e->key, e->context, ht->destructor_user_context);
+ if (destructor) {
+ destructor(e->key, e->context, destructor_user_context);
+ } else {
+ if (ht->destructor)
+ ht->destructor(e->key, e->context, ht->destructor_user_context);
+ }
silc_free(e);
ht->entry_count--;
SilcHashFunction hash,
void *hash_user_context,
SilcHashCompare compare,
- void *compare_user_context)
+ void *compare_user_context,
+ SilcHashDestructor destructor,
+ void *destructor_user_context)
{
SilcHashTableEntry *entry, prev, e;
if (prev && e->next)
prev->next = e->next;
- if (ht->destructor)
- ht->destructor(e->key, e->context, ht->destructor_user_context);
+ if (destructor) {
+ destructor(e->key, e->context, destructor_user_context);
+ } else {
+ if (ht->destructor)
+ ht->destructor(e->key, e->context, ht->destructor_user_context);
+ }
silc_free(e);
ht->entry_count--;
* SilcHashFunction hash,
* void *hash_user_context,
* SilcHashCompare compare,
- * void *compare_user_context);
+ * void *compare_user_context,
+ * SilcHashDestructor destructor,
+ * void *destructor_user_context);
*
* DESCRIPTION
*
* function. If not provided the hash table's default is used.
* The `compare' and `compare_user_context' are application specified
* comparing function. If not provided the hash table's default is used.
+ * The `destructor' and `destructor_user_context' are application
+ * specific destructor function.
*
***/
bool silc_hash_table_del_ext(SilcHashTable ht, void *key,
SilcHashFunction hash,
void *hash_user_context,
SilcHashCompare compare,
- void *compare_user_context);
+ void *compare_user_context,
+ SilcHashDestructor destructor,
+ void *destructor_user_context);
/****f* silcutil/SilcHashTableAPI/silc_hash_table_del_by_context_ext
*
* SilcHashFunction hash,
* void *hash_user_context,
* SilcHashCompare compare,
- * void *compare_user_context);
+ * void *compare_user_context,
+ * SilcHashDestructor destructor,
+ * void *destructor_user_context);
*
* DESCRIPTION
*
* function. If not provided the hash table's default is used.
* The `compare' and `compare_user_context' are application specified
* comparing function. If not provided the hash table's default is used.
+ * The `destructor' and `destructor_user_context' are application
+ * specific destructor function.
*
***/
bool silc_hash_table_del_by_context_ext(SilcHashTable ht, void *key,
SilcHashFunction hash,
void *hash_user_context,
SilcHashCompare compare,
- void *compare_user_context);
+ void *compare_user_context,
+ SilcHashDestructor destructor,
+ void *destructor_user_context);
/****f* silcutil/SilcHashTableAPI/silc_hash_table_find_ext
*